@gjsify/node-gi 0.58.0 → 0.59.1
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.
- package/binding.gyp +2 -0
- package/gettext.js +2 -2
- package/gi.js +15 -15
- package/host.nativescript.js +186 -0
- package/host.node.js +224 -0
- package/index.js +6 -190
- package/nativescript.webpack.js +16 -0
- package/overrides/byte-array.js +24 -2
- package/package.json +12 -1
- package/prebuilds/darwin-arm64/node_gi.node +0 -0
- package/prebuilds/darwin-x64/node_gi.node +0 -0
- package/prebuilds/linux-arm64/node_gi.node +0 -0
- package/prebuilds/linux-x64/node_gi.node +0 -0
- package/prebuilds/win32-arm64/node_gi.node +0 -0
- package/prebuilds/win32-x64/node_gi.node +0 -0
- package/src/addon.cc +7 -0
- package/src/android-gdk.cc +291 -0
- package/src/android-log.cc +187 -0
- package/src/common.h +23 -0
- package/src/loop.cc +478 -141
|
@@ -0,0 +1,187 @@
|
|
|
1
|
+
// SPDX-License-Identifier: MIT
|
|
2
|
+
// Android only: route GLib's messages into logcat.
|
|
3
|
+
//
|
|
4
|
+
// On Android an app's stdout/stderr go to /dev/null unless something redirects them,
|
|
5
|
+
// so GLib's default handlers — which write to stderr — lose every g_warning,
|
|
6
|
+
// g_critical and g_message a GI call produces. The failure mode is the expensive one:
|
|
7
|
+
// GTK reports precisely why it refused to start, and the report never leaves the
|
|
8
|
+
// process. GTK's own Android runtime installs these four handlers in startRuntime()
|
|
9
|
+
// (refs/gtk/gdk/android/gdkandroidruntime.c), but startRuntime also loads an
|
|
10
|
+
// application library and spawns GTK's own thread, so a node-gi host must not call
|
|
11
|
+
// it — this is the piece of it that a host still needs.
|
|
12
|
+
//
|
|
13
|
+
// Installed from Init, not on request: a host that has not asked for logging still
|
|
14
|
+
// wants to be told why GI failed, and there is no other sink on this platform.
|
|
15
|
+
// g_log_set_default_handler / g_log_set_writer_func are process-global and
|
|
16
|
+
// last-call-wins, so a later logSetWriterFunc (private.cc) still overrides this.
|
|
17
|
+
//
|
|
18
|
+
// GLib is not the only writer: a foreign C library that does fprintf(stderr, ...) and
|
|
19
|
+
// abort() (libepoxy's "eglQueryDisplayAttribEXT() not found" was the case) never reaches
|
|
20
|
+
// a GLib handler. So fd 1 and fd 2 themselves are pointed at pipes that a reader thread
|
|
21
|
+
// forwards to logcat line by line.
|
|
22
|
+
|
|
23
|
+
#include "common.h"
|
|
24
|
+
|
|
25
|
+
#ifdef __ANDROID__
|
|
26
|
+
|
|
27
|
+
#include <android/log.h>
|
|
28
|
+
#include <errno.h>
|
|
29
|
+
#include <fcntl.h>
|
|
30
|
+
#include <sys/stat.h>
|
|
31
|
+
#include <unistd.h>
|
|
32
|
+
|
|
33
|
+
#include <cstdio>
|
|
34
|
+
#include <mutex>
|
|
35
|
+
#include <string>
|
|
36
|
+
#include <thread>
|
|
37
|
+
|
|
38
|
+
namespace nodegi {
|
|
39
|
+
|
|
40
|
+
// Whether `fd` is the /dev/null an app process starts with. Anything else (a terminal
|
|
41
|
+
// under `adb shell`, a file or pipe a host or the launcher set up) is a sink somebody
|
|
42
|
+
// chose, and taking it over would hide output from them.
|
|
43
|
+
static bool IsDevNull(int fd) {
|
|
44
|
+
struct stat fd_st, null_st;
|
|
45
|
+
return fstat(fd, &fd_st) == 0 && stat("/dev/null", &null_st) == 0 && S_ISCHR(fd_st.st_mode) &&
|
|
46
|
+
fd_st.st_rdev == null_st.st_rdev;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
// Longest line forwarded whole; logcat truncates an entry near 4 KiB anyway, and a
|
|
50
|
+
// writer that never sends '\n' must not grow the buffer without bound.
|
|
51
|
+
static const size_t kMaxLine = 4000;
|
|
52
|
+
|
|
53
|
+
struct StdioPipe {
|
|
54
|
+
int read_fd;
|
|
55
|
+
int priority;
|
|
56
|
+
const char* tag;
|
|
57
|
+
};
|
|
58
|
+
|
|
59
|
+
static void ForwardLoop(StdioPipe pipe_info) {
|
|
60
|
+
std::string line;
|
|
61
|
+
char buf[1024];
|
|
62
|
+
for (;;) {
|
|
63
|
+
ssize_t n = read(pipe_info.read_fd, buf, sizeof buf);
|
|
64
|
+
if (n < 0 && errno == EINTR) continue;
|
|
65
|
+
if (n <= 0) break;
|
|
66
|
+
for (ssize_t i = 0; i < n; i++) {
|
|
67
|
+
if (buf[i] == '\n') {
|
|
68
|
+
__android_log_write(pipe_info.priority, pipe_info.tag, line.c_str());
|
|
69
|
+
line.clear();
|
|
70
|
+
} else {
|
|
71
|
+
line.push_back(buf[i]);
|
|
72
|
+
if (line.size() >= kMaxLine) {
|
|
73
|
+
__android_log_write(pipe_info.priority, pipe_info.tag, line.c_str());
|
|
74
|
+
line.clear();
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
if (!line.empty()) __android_log_write(pipe_info.priority, pipe_info.tag, line.c_str());
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
static void RedirectFd(int fd, int priority, const char* tag) {
|
|
83
|
+
if (!IsDevNull(fd)) return;
|
|
84
|
+
int fds[2];
|
|
85
|
+
if (pipe2(fds, O_CLOEXEC) != 0) return;
|
|
86
|
+
if (dup2(fds[1], fd) < 0) {
|
|
87
|
+
close(fds[0]);
|
|
88
|
+
close(fds[1]);
|
|
89
|
+
return;
|
|
90
|
+
}
|
|
91
|
+
close(fds[1]);
|
|
92
|
+
// Two pipes rather than one: priority is per entry, and stderr must stay WARN so a
|
|
93
|
+
// logcat filter on `*:W` still shows it.
|
|
94
|
+
std::thread(ForwardLoop, StdioPipe{fds[0], priority, tag}).detach();
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
// Once per process: Init runs once per env, but fd 1/2 are process-wide.
|
|
98
|
+
//
|
|
99
|
+
// Limit: abort() ends the process, and the reader thread with it. stderr is made
|
|
100
|
+
// unbuffered so the message at least reaches the pipe before the abort, but whether the
|
|
101
|
+
// reader has forwarded it by then is a race the tombstone still backs up.
|
|
102
|
+
static void InstallStdioRedirect() {
|
|
103
|
+
static std::once_flag once;
|
|
104
|
+
std::call_once(once, [] {
|
|
105
|
+
RedirectFd(STDOUT_FILENO, ANDROID_LOG_INFO, "stdout");
|
|
106
|
+
RedirectFd(STDERR_FILENO, ANDROID_LOG_WARN, "stderr");
|
|
107
|
+
setvbuf(stdout, nullptr, _IOLBF, 0);
|
|
108
|
+
setvbuf(stderr, nullptr, _IONBF, 0);
|
|
109
|
+
});
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
static void AndroidPrintHandler(const char* message) {
|
|
113
|
+
__android_log_print(ANDROID_LOG_INFO, "print", "%s", message);
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
static void AndroidPrinterrHandler(const char* message) {
|
|
117
|
+
__android_log_print(ANDROID_LOG_WARN, "print", "%s", message);
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
static int AndroidPriority(GLogLevelFlags log_level) {
|
|
121
|
+
if (log_level & G_LOG_LEVEL_ERROR) return ANDROID_LOG_FATAL;
|
|
122
|
+
if (log_level & G_LOG_LEVEL_CRITICAL) return ANDROID_LOG_ERROR;
|
|
123
|
+
if (log_level & G_LOG_LEVEL_WARNING) return ANDROID_LOG_WARN;
|
|
124
|
+
if (log_level & G_LOG_LEVEL_MESSAGE) return ANDROID_LOG_INFO;
|
|
125
|
+
if (log_level & G_LOG_LEVEL_INFO) return ANDROID_LOG_INFO;
|
|
126
|
+
if (log_level & G_LOG_LEVEL_DEBUG) return ANDROID_LOG_DEBUG;
|
|
127
|
+
return ANDROID_LOG_WARN;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
static void AndroidLogHandler(const gchar* log_domain, GLogLevelFlags log_level,
|
|
131
|
+
const gchar* message, gpointer) {
|
|
132
|
+
__android_log_print(AndroidPriority(log_level),
|
|
133
|
+
log_domain != nullptr ? log_domain : "**", "%s", message);
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
static GLogWriterOutput AndroidLogWriter(GLogLevelFlags log_level, const GLogField* fields,
|
|
137
|
+
gsize n_fields, gpointer) {
|
|
138
|
+
const gchar* domain = nullptr;
|
|
139
|
+
const gchar* message = nullptr;
|
|
140
|
+
for (gsize i = 0; (domain == nullptr || message == nullptr) && i < n_fields; i++) {
|
|
141
|
+
if (g_strcmp0(fields[i].key, "GLIB_DOMAIN") == 0)
|
|
142
|
+
domain = static_cast<const gchar*>(fields[i].value);
|
|
143
|
+
else if (g_strcmp0(fields[i].key, "MESSAGE") == 0)
|
|
144
|
+
message = static_cast<const gchar*>(fields[i].value);
|
|
145
|
+
}
|
|
146
|
+
// liblog answers with the bytes it wrote, or a negative errno: anything above zero is
|
|
147
|
+
// a delivery. Reporting UNHANDLED on one sends GLib to its default writer, i.e. to the
|
|
148
|
+
// stderr this whole file exists because of.
|
|
149
|
+
int rc = __android_log_print(AndroidPriority(log_level),
|
|
150
|
+
domain != nullptr ? domain : "**", "%s",
|
|
151
|
+
message != nullptr ? message : "(empty)");
|
|
152
|
+
return rc > 0 ? G_LOG_WRITER_HANDLED : G_LOG_WRITER_UNHANDLED;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
// androidLogBridge() -> boolean — whether GLib's messages reach logcat.
|
|
156
|
+
static Napi::Value AndroidLogBridge(const Napi::CallbackInfo& info) {
|
|
157
|
+
return Napi::Boolean::New(info.Env(), true);
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
void InitAndroidLog(Napi::Env env, Napi::Object exports) {
|
|
161
|
+
g_set_print_handler(AndroidPrintHandler);
|
|
162
|
+
g_set_printerr_handler(AndroidPrinterrHandler);
|
|
163
|
+
g_log_set_default_handler(AndroidLogHandler, nullptr);
|
|
164
|
+
g_log_set_writer_func(AndroidLogWriter, nullptr, nullptr);
|
|
165
|
+
InstallStdioRedirect();
|
|
166
|
+
exports.Set("androidLogBridge", Napi::Function::New(env, AndroidLogBridge));
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
} // namespace nodegi
|
|
170
|
+
|
|
171
|
+
#else
|
|
172
|
+
|
|
173
|
+
namespace nodegi {
|
|
174
|
+
|
|
175
|
+
static Napi::Value AndroidLogBridge(const Napi::CallbackInfo& info) {
|
|
176
|
+
return Napi::Boolean::New(info.Env(), false);
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
// Off Android stderr is a real sink and GLib's own handlers are the right ones; the
|
|
180
|
+
// export stays so a caller can ask without branching on the platform first.
|
|
181
|
+
void InitAndroidLog(Napi::Env env, Napi::Object exports) {
|
|
182
|
+
exports.Set("androidLogBridge", Napi::Function::New(env, AndroidLogBridge));
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
} // namespace nodegi
|
|
186
|
+
|
|
187
|
+
#endif
|
package/src/common.h
CHANGED
|
@@ -19,11 +19,28 @@
|
|
|
19
19
|
#ifndef NODE_GI_SRC_COMMON_H_
|
|
20
20
|
#define NODE_GI_SRC_COMMON_H_
|
|
21
21
|
|
|
22
|
+
// NODE_GI_HAS_LIBUV — may this build REFERENCE libuv at all? Not "is there an
|
|
23
|
+
// event loop": bionic resolves every symbol at dlopen, so a single
|
|
24
|
+
// uv_timer_start left in the object file makes the whole addon fail to load on
|
|
25
|
+
// Android, whatever the call site does at runtime. The Android host is
|
|
26
|
+
// NativeScript's V8 runtime, a Node-API host with no libuv of its own
|
|
27
|
+
// (napi_get_uv_event_loop always fails there), so the uv bridge in loop.cc is
|
|
28
|
+
// compiled out and the ALooper pump beside it takes over.
|
|
29
|
+
#ifndef NODE_GI_HAS_LIBUV
|
|
30
|
+
#ifdef __ANDROID__
|
|
31
|
+
#define NODE_GI_HAS_LIBUV 0
|
|
32
|
+
#else
|
|
33
|
+
#define NODE_GI_HAS_LIBUV 1
|
|
34
|
+
#endif
|
|
35
|
+
#endif
|
|
36
|
+
|
|
22
37
|
#ifndef _WIN32
|
|
23
38
|
#include <dlfcn.h> // dlopen/dlsym the GtkWidgetClass template API (no GTK link)
|
|
24
39
|
#endif
|
|
25
40
|
#include <napi.h>
|
|
41
|
+
#if NODE_GI_HAS_LIBUV
|
|
26
42
|
#include <uv.h>
|
|
43
|
+
#endif
|
|
27
44
|
|
|
28
45
|
#include <girepository/girepository.h>
|
|
29
46
|
#include <girepository/girffi.h> // gi_callable_info_create_closure + ffi_cif
|
|
@@ -178,6 +195,12 @@ const ForeignStructOps* ForeignOpsForInfo(GIBaseInfo* iface);
|
|
|
178
195
|
void InitCairo(Napi::Env env, Napi::Object exports);
|
|
179
196
|
// win32: probeHostOpenGL / preloadOpenGL / currentGLStrings (opengl-win32.cc); a no-op elsewhere.
|
|
180
197
|
void InitOpenGLWin32(Napi::Env env, Napi::Object exports);
|
|
198
|
+
// Android: install GLib's print/log/writer handlers onto logcat (android-log.cc), where
|
|
199
|
+
// stderr goes nowhere. Elsewhere only the `androidLogBridge` probe, answering false.
|
|
200
|
+
void InitAndroidLog(Napi::Env env, Napi::Object exports);
|
|
201
|
+
// Android: androidInitGdk(), the opt-in GDK bring-up that replaces GTK's
|
|
202
|
+
// RuntimeApplication (android-gdk.cc). Elsewhere the export answers false.
|
|
203
|
+
void InitAndroidGdk(Napi::Env env, Napi::Object exports);
|
|
181
204
|
|
|
182
205
|
// ---- GJS-exact 64-bit integer marshalling helpers (shared) -------------------
|
|
183
206
|
//
|