node-gtk 2.2.0 → 4.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (51) hide show
  1. package/README.md +45 -161
  2. package/bin/node-gtk.js +12 -1
  3. package/binding.gyp +21 -0
  4. package/lib/binding/node-v127-linux-x64/node_gtk.node +0 -0
  5. package/lib/bootstrap.js +43 -11
  6. package/lib/esm/hooks.mjs +49 -0
  7. package/lib/esm/register.mjs +17 -0
  8. package/lib/index.js +1 -2
  9. package/lib/index.mjs +25 -0
  10. package/lib/inspect.js +1 -1
  11. package/lib/loop.js +5 -0
  12. package/lib/module.js +8 -2
  13. package/lib/overrides/Gtk-4.0.js +6 -27
  14. package/lib/register-class.js +86 -3
  15. package/lib/styles.d.ts +81 -0
  16. package/lib/styles.js +428 -0
  17. package/package.json +15 -2
  18. package/src/boxed.cc +13 -5
  19. package/src/closure.cc +19 -6
  20. package/src/closure.h +8 -4
  21. package/src/function.cc +59 -5
  22. package/src/fundamental.cc +451 -0
  23. package/src/fundamental.h +71 -0
  24. package/src/gi.cc +21 -1
  25. package/src/gobject.cc +268 -9
  26. package/src/gobject.h +5 -0
  27. package/src/modules/cairo/context.cc +103 -103
  28. package/src/modules/cairo/font-extents.cc +6 -2
  29. package/src/modules/cairo/generator.js +1 -1
  30. package/src/modules/cairo/glyph.cc +6 -2
  31. package/src/modules/cairo/path.cc +6 -2
  32. package/src/modules/cairo/rectangle-int.cc +6 -2
  33. package/src/modules/cairo/rectangle.cc +6 -2
  34. package/src/modules/cairo/text-cluster.cc +6 -2
  35. package/src/modules/cairo/text-extents.cc +6 -2
  36. package/src/modules/system.cc +4 -4
  37. package/src/util.h +3 -3
  38. package/src/value.cc +44 -8
  39. package/src/value.h +2 -2
  40. package/tools/README.md +52 -2
  41. package/tools/create-app.js +246 -0
  42. package/tools/generate-types.js +80 -3
  43. package/tools/list-libraries.js +125 -0
  44. package/tools/templates/app/README.md.tmpl +97 -0
  45. package/tools/templates/app/gitignore.tmpl +10 -0
  46. package/tools/templates/app/package.json.tmpl +26 -0
  47. package/tools/templates/app/src/main.ts.tmpl +110 -0
  48. package/tools/templates/app/src/welcome.ts.tmpl +41 -0
  49. package/tools/templates/app/style.css.tmpl +19 -0
  50. package/tools/templates/app/tsconfig.json.tmpl +19 -0
  51. /package/{COPYING → LICENSE} +0 -0
package/src/boxed.cc CHANGED
@@ -6,6 +6,7 @@
6
6
  #include "debug.h"
7
7
  #include "error.h"
8
8
  #include "function.h"
9
+ #include "fundamental.h"
9
10
  #include "gi.h"
10
11
  #include "gobject.h"
11
12
  #include "macros.h"
@@ -258,8 +259,8 @@ static void BoxedConstructor(const Nan::FunctionCallbackInfo<Value> &info) {
258
259
  box->persistent = new Nan::Persistent<Object>(self);
259
260
  box->persistent->SetWeak(box, BoxedDestroyed, Nan::WeakCallbackType::kParameter);
260
261
 
261
- self->SetAlignedPointerInInternalField (0, boxed);
262
- self->SetAlignedPointerInInternalField (1, box);
262
+ Nan::SetInternalFieldPointer(self, 0, boxed);
263
+ Nan::SetInternalFieldPointer(self, 1, box);
263
264
 
264
265
  SET_OBJECT_GTYPE (self, gtype);
265
266
 
@@ -414,6 +415,13 @@ Local<Function> GetBoxedFunction(GIBaseInfo *info, GType gtype) {
414
415
  Local<Function> MakeBoxedClass(GIBaseInfo *info) {
415
416
  GType gtype = g_registered_type_info_get_g_type ((GIRegisteredTypeInfo *) info);
416
417
 
418
+ // GVariant is a StructInfo but a refcounted fundamental (G_TYPE_VARIANT),
419
+ // not a boxed value: give it the fundamental ref/unref wrapper instead of
420
+ // the boxed copy/free one. JS still attaches its struct methods to this
421
+ // class via makeBoxed() (#465).
422
+ if (gtype == G_TYPE_VARIANT)
423
+ return MakeVariantClass (info);
424
+
417
425
  if (gtype == G_TYPE_NONE) {
418
426
  auto moduleCache = GNodeJS::GetModuleCache();
419
427
  auto ns = UTF8 (g_base_info_get_namespace (info));
@@ -454,7 +462,7 @@ Local<Value> WrapperFromBoxed(GIBaseInfo *info, void *data, ResourceOwnership ow
454
462
  void* PointerFromWrapper(Local<Value> value) {
455
463
  Local<Object> object = TO_OBJECT (value);
456
464
  g_assert(object->InternalFieldCount() > 0);
457
- void *boxed = object->GetAlignedPointerFromInternalField(0);
465
+ void *boxed = Nan::GetInternalFieldPointer(object, 0);
458
466
  return boxed;
459
467
  }
460
468
 
@@ -467,12 +475,12 @@ void DisownBoxed(Local<Value> value) {
467
475
  if (object->InternalFieldCount() < 2)
468
476
  return;
469
477
 
470
- Boxed *box = static_cast<Boxed *>(object->GetAlignedPointerFromInternalField(1));
478
+ Boxed *box = static_cast<Boxed *>(Nan::GetInternalFieldPointer(object, 1));
471
479
  if (box != NULL) {
472
480
  box->owns_memory = false;
473
481
  box->data = NULL;
474
482
  }
475
- object->SetAlignedPointerInInternalField(0, NULL);
483
+ Nan::SetInternalFieldPointer(object, 0, NULL);
476
484
  }
477
485
 
478
486
  };
package/src/closure.cc CHANGED
@@ -3,6 +3,7 @@
3
3
 
4
4
  #include "closure.h"
5
5
  #include "error.h"
6
+ #include "gobject.h"
6
7
  #include "macros.h"
7
8
  #include "loop.h"
8
9
  #include "type.h"
@@ -54,9 +55,9 @@ static void LoadGIArgumentFromPointer (GITypeInfo *type_info, GIArgument *arg) {
54
55
  }
55
56
  }
56
57
 
57
- GClosure *Closure::New (Local<Function> function, GICallableInfo* info, guint signalId) {
58
+ GClosure *Closure::New (guint handlerIndex, GICallableInfo* info, guint signalId) {
58
59
  Closure *closure = (Closure *) g_closure_new_simple (sizeof (*closure), GUINT_TO_POINTER(signalId));
59
- closure->persistent.Reset(function);
60
+ closure->handlerIndex = handlerIndex;
60
61
  if (info) {
61
62
  closure->info = g_base_info_ref(info);
62
63
  } else {
@@ -69,11 +70,18 @@ GClosure *Closure::New (Local<Function> function, GICallableInfo* info, guint si
69
70
  }
70
71
 
71
72
  void Closure::Execute(GICallableInfo *info, guint signal_id,
72
- const Nan::Persistent<v8::Function> &persFn,
73
+ GObject *instance, guint handlerIndex,
73
74
  GValue *g_return_value, guint n_param_values,
74
75
  const GValue *param_values) {
75
76
  Nan::HandleScope scope;
76
- auto func = Nan::New<Function>(persFn);
77
+
78
+ /* The handler lives in a JS array on the instance's wrapper (#375). If the
79
+ * wrapper has been collected (the object was dropped from JS) there is
80
+ * nothing to call. */
81
+ Local<Value> handlerValue = GetSignalHandler(instance, handlerIndex);
82
+ if (handlerValue.IsEmpty() || !handlerValue->IsFunction())
83
+ return;
84
+ auto func = handlerValue.As<Function>();
77
85
 
78
86
  GSignalQuery signal_query = { 0, };
79
87
 
@@ -242,16 +250,21 @@ void Closure::Marshal(GClosure *base,
242
250
 
243
251
  auto closure = (Closure *) base;
244
252
  auto signal_id = GPOINTER_TO_UINT(marshal_data);
253
+ auto handlerIndex = closure->handlerIndex;
254
+
255
+ /* param_values[0] is always the instance the signal was emitted on, which
256
+ * is the object whose wrapper holds this handler (#375). */
257
+ GObject *instance = (GObject *) g_value_get_object(&param_values[0]);
245
258
 
246
259
  AsyncCallEnvironment* env = reinterpret_cast<AsyncCallEnvironment *>(AsyncCallEnvironment::asyncHandle.data);
247
260
 
248
261
  if (env->IsSameThread()) {
249
262
  /* Case 1: same thread */
250
- Closure::Execute(closure->info, signal_id, closure->persistent, g_return_value, n_param_values, param_values);
263
+ Closure::Execute(closure->info, signal_id, instance, handlerIndex, g_return_value, n_param_values, param_values);
251
264
  } else {
252
265
  /* Case 2: different thread */
253
266
  env->Call([&]() {
254
- Closure::Execute(closure->info, signal_id, closure->persistent, g_return_value, n_param_values, param_values);
267
+ Closure::Execute(closure->info, signal_id, instance, handlerIndex, g_return_value, n_param_values, param_values);
255
268
  });
256
269
  }
257
270
  }
package/src/closure.h CHANGED
@@ -16,20 +16,24 @@ namespace GNodeJS {
16
16
 
17
17
  struct Closure {
18
18
  GClosure base;
19
- Nan::Persistent<v8::Function> persistent;
19
+ /* The handler function is NOT held here. It lives in a JS array on the
20
+ * wrapper object (reachable only through the wrapper), and we keep just its
21
+ * index. This breaks the wrapper <-> handler reference loop that a strong
22
+ * Nan::Persistent used to create and leak (#375); see
23
+ * doc/signal-handler-gc.md. */
24
+ guint handlerIndex;
20
25
  GICallableInfo* info;
21
26
 
22
27
  ~Closure() {
23
- persistent.Reset();
24
28
  if (info) g_base_info_unref (info);
25
29
  }
26
30
 
27
- static GClosure *New(Local<Function> function,
31
+ static GClosure *New(guint handlerIndex,
28
32
  GICallableInfo* info,
29
33
  guint signalId);
30
34
 
31
35
  static void Execute(GICallableInfo *info, guint signal_id,
32
- const Nan::Persistent<v8::Function> &persFn,
36
+ GObject *instance, guint handlerIndex,
33
37
  GValue *g_return_value, guint n_param_values,
34
38
  const GValue *param_values);
35
39
 
package/src/function.cc CHANGED
@@ -5,6 +5,7 @@
5
5
  #include "callback.h"
6
6
  #include "debug.h"
7
7
  #include "error.h"
8
+ #include "fundamental.h"
8
9
  #include "function.h"
9
10
  #include "gobject.h"
10
11
  #include "macros.h"
@@ -134,6 +135,43 @@ bool IsDestroyNotify (GIBaseInfo *info) {
134
135
  && strcmp(g_base_info_get_namespace(info), "GLib") == 0;
135
136
  }
136
137
 
138
+ /*
139
+ * A transfer-full GObject return value hands node-gtk an *owning* reference. The
140
+ * JS wrapper only needs node-gtk's toggle reference, so that extra reference has
141
+ * to be released — otherwise the refcount never falls back to 1, ToggleNotify
142
+ * never flips the V8 handle to weak, and the GObject (plus its wrapper) leaks for
143
+ * the lifetime of the process. This is the leak reported in #446: objects from
144
+ * GI function returns (`Type.new()`, transfer-full getters, …) were never GC'd,
145
+ * while `new Type()` — which drops its construction ref in GObjectConstructor —
146
+ * was. It is the return-value counterpart of OUT GObject args, which
147
+ * FreeGIArgument already balances; the return value is otherwise never freed on
148
+ * the success path. (The IN counterpart is RefObjectForTransferFullIn, #439.)
149
+ *
150
+ * Must be sampled *before* the value is wrapped: associating the wrapper sinks a
151
+ * floating reference (consuming it), after which a floating incoming ref (nothing
152
+ * extra to drop) is indistinguishable from a real owned one. G_IS_OBJECT excludes
153
+ * GParamSpec (wrapped via ParamSpec::FromGParamSpec) and boxed/fundamental
154
+ * interface types.
155
+ */
156
+ static bool OwnsExtraGObjectReturnRef(GITypeInfo *return_type, GITransfer transfer, gpointer ptr) {
157
+ if (transfer != GI_TRANSFER_EVERYTHING || ptr == NULL)
158
+ return false;
159
+
160
+ if (g_type_info_get_tag(return_type) != GI_TYPE_TAG_INTERFACE)
161
+ return false;
162
+
163
+ GIBaseInfo *iface = g_type_info_get_interface(return_type);
164
+ GIInfoType itype = g_base_info_get_type(iface);
165
+
166
+ bool result =
167
+ (itype == GI_INFO_TYPE_OBJECT || itype == GI_INFO_TYPE_INTERFACE)
168
+ && G_IS_OBJECT(ptr)
169
+ && !g_object_is_floating(ptr);
170
+
171
+ g_base_info_unref(iface);
172
+ return result;
173
+ }
174
+
137
175
 
138
176
  /**
139
177
  * The constructor just stores the GIBaseInfo ref. The rest of the
@@ -485,11 +523,14 @@ Local<Value> FunctionCall (
485
523
  // Boxed: hand it a copy so the JS wrapper's own memory isn't
486
524
  // double-freed when finalized (#409). GObject: add the reference
487
525
  // the callee will own, so it isn't finalized out from under the
488
- // callee once the wrapper is GC'd (#439).
526
+ // callee once the wrapper is GC'd (#439). Fundamental (e.g.
527
+ // GskRenderNode): add the reference the callee will own (#468).
489
528
  else if (direction == GI_DIRECTION_IN
490
529
  && g_arg_info_get_ownership_transfer(&arg_info) == GI_TRANSFER_EVERYTHING) {
491
530
  CopyBoxedForTransferFullIn(&type_info, &callable_arg_values[i], param.length);
492
531
  RefObjectForTransferFullIn(&type_info, &callable_arg_values[i]);
532
+ RefFundamentalForTransferFullIn(&type_info, &callable_arg_values[i]);
533
+ RefVariantForTransferFullIn(&type_info, &callable_arg_values[i]);
493
534
  }
494
535
  }
495
536
 
@@ -549,6 +590,13 @@ Local<Value> FunctionCall (
549
590
 
550
591
  } else if (!use_return_value) {
551
592
 
593
+ // A transfer-full GObject return hands us an extra owning reference on
594
+ // top of the wrapper's toggle ref; drop it once wrapped so the object
595
+ // isn't pinned alive forever (#446). Sampled now, before wrapping sinks
596
+ // any floating reference.
597
+ bool release_return_ref =
598
+ OwnsExtraGObjectReturnRef(&return_type, return_transfer, return_value_stack.v_pointer);
599
+
552
600
  // Value transferred to jsReturnValue
553
601
  jsReturnValue = func->JsReturnValue (
554
602
  info.This(),
@@ -556,6 +604,9 @@ Local<Value> FunctionCall (
556
604
  &return_value_stack,
557
605
  callable_arg_values,
558
606
  return_transfer);
607
+
608
+ if (release_return_ref)
609
+ g_object_unref (return_value_stack.v_pointer);
559
610
  } else {
560
611
 
561
612
  // Value returned in return_value
@@ -661,7 +712,8 @@ Local<Value> FunctionInfo::JsReturnValue (
661
712
  ADD_RETURN (isReturningSelf ? self :
662
713
  GIArgumentToV8 (
663
714
  return_type, return_value, length,
664
- return_transfer == GI_TRANSFER_EVERYTHING ? kTransfer : kNone))
715
+ return_transfer == GI_TRANSFER_EVERYTHING ? kTransfer : kNone,
716
+ g_callable_info_may_return_null (info)))
665
717
  }
666
718
 
667
719
  for (int i = 0; i < n_callable_args; i++) {
@@ -695,20 +747,22 @@ Local<Value> FunctionInfo::JsReturnValue (
695
747
  &callable_arg_values[length_i],
696
748
  IsDirectionOut(length_direction));
697
749
 
698
- Local<Value> result = ArrayToV8(&arg_type, *(void**)arg_value.v_pointer, param.length);
750
+ bool may_be_null = g_arg_info_may_be_null(&arg_info);
751
+ Local<Value> result = ArrayToV8(&arg_type, *(void**)arg_value.v_pointer, param.length, may_be_null);
699
752
 
700
753
  ADD_RETURN (result)
701
754
 
702
755
  } else if (param.type == ParameterType::kNORMAL) {
703
756
  GITransfer transfer = g_arg_info_get_ownership_transfer(&arg_info);
704
757
  ResourceOwnership ownership = transfer == GI_TRANSFER_EVERYTHING ? kTransfer : kNone;
758
+ bool may_be_null = g_arg_info_may_be_null(&arg_info);
705
759
 
706
760
  if (IsPointerType(&arg_type) && g_arg_info_is_caller_allocates(&arg_info)) {
707
761
  void *pointer = &arg_value.v_pointer;
708
- ADD_RETURN (GIArgumentToV8(&arg_type, (GIArgument*) pointer, -1, ownership))
762
+ ADD_RETURN (GIArgumentToV8(&arg_type, (GIArgument*) pointer, -1, ownership, may_be_null))
709
763
  }
710
764
  else {
711
- ADD_RETURN (GIArgumentToV8(&arg_type, (GIArgument*) arg_value.v_pointer, -1, ownership))
765
+ ADD_RETURN (GIArgumentToV8(&arg_type, (GIArgument*) arg_value.v_pointer, -1, ownership, may_be_null))
712
766
  }
713
767
  }
714
768
  }