@next2d/builder 0.1.5 → 0.1.7
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/README.md +15 -0
- package/dist/index.js +315 -3
- package/package.json +10 -8
- package/templates/xbox/CMakeLists.txt +124 -0
- package/templates/xbox/MicrosoftGame.config +36 -0
- package/templates/xbox/README.md +372 -0
- package/templates/xbox/cmake/FetchDawn.cmake +42 -0
- package/templates/xbox/cmake/FindV8.cmake +68 -0
- package/templates/xbox/js/bootstrap.js +143 -0
- package/templates/xbox/js/selftest.js +515 -0
- package/templates/xbox/src/AssetLoader.cpp +106 -0
- package/templates/xbox/src/AssetLoader.h +35 -0
- package/templates/xbox/src/EventLoop.cpp +162 -0
- package/templates/xbox/src/EventLoop.h +73 -0
- package/templates/xbox/src/HostContext.h +58 -0
- package/templates/xbox/src/bindings/Audio.cpp +320 -0
- package/templates/xbox/src/bindings/Bindings.h +75 -0
- package/templates/xbox/src/bindings/Canvas.cpp +171 -0
- package/templates/xbox/src/bindings/Canvas2D.cpp +462 -0
- package/templates/xbox/src/bindings/Console.cpp +52 -0
- package/templates/xbox/src/bindings/DomShims.cpp +221 -0
- package/templates/xbox/src/bindings/EventTarget.cpp +127 -0
- package/templates/xbox/src/bindings/EventTarget.h +17 -0
- package/templates/xbox/src/bindings/Fetch.cpp +149 -0
- package/templates/xbox/src/bindings/Gamepad.cpp +79 -0
- package/templates/xbox/src/bindings/Image.cpp +160 -0
- package/templates/xbox/src/bindings/ImageSource.h +26 -0
- package/templates/xbox/src/bindings/Network.cpp +303 -0
- package/templates/xbox/src/bindings/RasterCore.h +265 -0
- package/templates/xbox/src/bindings/Timers.cpp +87 -0
- package/templates/xbox/src/bindings/Video.cpp +235 -0
- package/templates/xbox/src/bindings/webgpu/WebGPU.cpp +1318 -0
- package/templates/xbox/src/bindings/webgpu/WebGPUCommon.h +119 -0
- package/templates/xbox/src/bindings/webgpu/WebGPUEnums.h +245 -0
- package/templates/xbox/src/gpu/DawnContext.cpp +170 -0
- package/templates/xbox/src/gpu/DawnContext.h +64 -0
- package/templates/xbox/src/main.cpp +368 -0
- package/templates/xbox/src/platform/AudioEngine.cpp +184 -0
- package/templates/xbox/src/platform/AudioEngine.h +65 -0
- package/templates/xbox/src/platform/GamepadManager.cpp +83 -0
- package/templates/xbox/src/platform/GamepadManager.h +40 -0
- package/templates/xbox/src/platform/ImageTypes.h +17 -0
- package/templates/xbox/src/platform/TextRasterizer.cpp +183 -0
- package/templates/xbox/src/platform/TextRasterizer.h +35 -0
- package/templates/xbox/src/platform/WicDecoder.cpp +63 -0
- package/templates/xbox/src/platform/WicDecoder.h +16 -0
- package/templates/xbox/src/v8/V8Runtime.cpp +285 -0
- package/templates/xbox/src/v8/V8Runtime.h +68 -0
- package/templates/xbox/src/v8/V8Util.h +56 -0
- package/templates/xbox/src/worker/WorkerRuntime.cpp +453 -0
- package/templates/xbox/src/worker/WorkerRuntime.h +108 -0
- package/templates/xbox/tests/check_local.sh +56 -0
- package/templates/xbox/tests/raster_test.cpp +279 -0
- package/templates/xbox/tests/windows_platform_test.cpp +187 -0
|
@@ -0,0 +1,221 @@
|
|
|
1
|
+
#include "Bindings.h"
|
|
2
|
+
|
|
3
|
+
#include "HostContext.h"
|
|
4
|
+
#include "EventTarget.h"
|
|
5
|
+
#include "v8/V8Util.h"
|
|
6
|
+
|
|
7
|
+
#include <Windows.h>
|
|
8
|
+
|
|
9
|
+
namespace next2d {
|
|
10
|
+
|
|
11
|
+
using v8util::Str;
|
|
12
|
+
using v8util::SetMethod;
|
|
13
|
+
using v8util::SetValue;
|
|
14
|
+
|
|
15
|
+
namespace {
|
|
16
|
+
|
|
17
|
+
// document.createElement(tag)
|
|
18
|
+
void CreateElement(const v8::FunctionCallbackInfo<v8::Value>& args)
|
|
19
|
+
{
|
|
20
|
+
v8::Isolate* isolate = args.GetIsolate();
|
|
21
|
+
HostContext* host = HostContext::From(isolate);
|
|
22
|
+
const std::string tag = args.Length() > 0 ? v8util::ToStdString(isolate, args[0]) : "";
|
|
23
|
+
|
|
24
|
+
if (tag == "canvas") {
|
|
25
|
+
args.GetReturnValue().Set(CreateCanvasElement(isolate, host, 0, 0));
|
|
26
|
+
return;
|
|
27
|
+
}
|
|
28
|
+
if (tag == "video") {
|
|
29
|
+
args.GetReturnValue().Set(CreateVideoElement(isolate, host));
|
|
30
|
+
return;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
// その他の要素は最小スタブ (style/子要素操作/イベントを no-op で備える)
|
|
34
|
+
v8::Local<v8::Object> el = v8::Object::New(isolate);
|
|
35
|
+
SetValue(isolate, el, "style", v8::Object::New(isolate));
|
|
36
|
+
SetValue(isolate, el, "tagName", Str(isolate, tag));
|
|
37
|
+
SetMethod(isolate, el, "appendChild", [](const v8::FunctionCallbackInfo<v8::Value>& a) {
|
|
38
|
+
if (a.Length() > 0) a.GetReturnValue().Set(a[0]);
|
|
39
|
+
});
|
|
40
|
+
SetMethod(isolate, el, "removeChild", [](const v8::FunctionCallbackInfo<v8::Value>&) {});
|
|
41
|
+
SetMethod(isolate, el, "setAttribute", [](const v8::FunctionCallbackInfo<v8::Value>&) {});
|
|
42
|
+
SetMethod(isolate, el, "addEventListener", [](const v8::FunctionCallbackInfo<v8::Value>&) {});
|
|
43
|
+
SetMethod(isolate, el, "removeEventListener", [](const v8::FunctionCallbackInfo<v8::Value>&) {});
|
|
44
|
+
args.GetReturnValue().Set(el);
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
// window/document/body 用の EventTarget を設置する (実ディスパッチ対応)。
|
|
48
|
+
void NoopEventTarget(v8::Isolate* isolate, v8::Local<v8::Object> obj)
|
|
49
|
+
{
|
|
50
|
+
InstallEventTarget(isolate, obj);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
} // namespace
|
|
54
|
+
|
|
55
|
+
namespace {
|
|
56
|
+
|
|
57
|
+
// navigator.clipboard.readText() -> Promise<string> (Win32 クリップボード)
|
|
58
|
+
void ClipboardReadText(const v8::FunctionCallbackInfo<v8::Value>& args)
|
|
59
|
+
{
|
|
60
|
+
v8::Isolate* isolate = args.GetIsolate();
|
|
61
|
+
v8::Local<v8::Context> ctx = isolate->GetCurrentContext();
|
|
62
|
+
auto resolver = v8::Promise::Resolver::New(ctx).ToLocalChecked();
|
|
63
|
+
args.GetReturnValue().Set(resolver->GetPromise());
|
|
64
|
+
|
|
65
|
+
std::string text;
|
|
66
|
+
if (OpenClipboard(nullptr)) {
|
|
67
|
+
HANDLE h = GetClipboardData(CF_UNICODETEXT);
|
|
68
|
+
if (h) {
|
|
69
|
+
auto* w = static_cast<const wchar_t*>(GlobalLock(h));
|
|
70
|
+
if (w) {
|
|
71
|
+
int n = WideCharToMultiByte(CP_UTF8, 0, w, -1, nullptr, 0, nullptr, nullptr);
|
|
72
|
+
if (n > 0) {
|
|
73
|
+
text.resize(n - 1);
|
|
74
|
+
WideCharToMultiByte(CP_UTF8, 0, w, -1, text.data(), n, nullptr, nullptr);
|
|
75
|
+
}
|
|
76
|
+
GlobalUnlock(h);
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
CloseClipboard();
|
|
80
|
+
}
|
|
81
|
+
resolver->Resolve(ctx, Str(isolate, text)).Check();
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
// navigator.clipboard.writeText(text) -> Promise
|
|
85
|
+
void ClipboardWriteText(const v8::FunctionCallbackInfo<v8::Value>& args)
|
|
86
|
+
{
|
|
87
|
+
v8::Isolate* isolate = args.GetIsolate();
|
|
88
|
+
v8::Local<v8::Context> ctx = isolate->GetCurrentContext();
|
|
89
|
+
auto resolver = v8::Promise::Resolver::New(ctx).ToLocalChecked();
|
|
90
|
+
args.GetReturnValue().Set(resolver->GetPromise());
|
|
91
|
+
|
|
92
|
+
if (args.Length() > 0 && OpenClipboard(nullptr)) {
|
|
93
|
+
const std::string s = v8util::ToStdString(isolate, args[0]);
|
|
94
|
+
int n = MultiByteToWideChar(CP_UTF8, 0, s.c_str(), -1, nullptr, 0);
|
|
95
|
+
if (n > 0) {
|
|
96
|
+
HGLOBAL h = GlobalAlloc(GMEM_MOVEABLE, static_cast<size_t>(n) * sizeof(wchar_t));
|
|
97
|
+
if (h) {
|
|
98
|
+
auto* w = static_cast<wchar_t*>(GlobalLock(h));
|
|
99
|
+
MultiByteToWideChar(CP_UTF8, 0, s.c_str(), -1, w, n);
|
|
100
|
+
GlobalUnlock(h);
|
|
101
|
+
EmptyClipboard();
|
|
102
|
+
SetClipboardData(CF_UNICODETEXT, h);
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
CloseClipboard();
|
|
106
|
+
}
|
|
107
|
+
resolver->Resolve(ctx, v8::Undefined(isolate)).Check();
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
// new OffscreenCanvas(width, height)
|
|
111
|
+
void OffscreenCanvasConstructor(const v8::FunctionCallbackInfo<v8::Value>& args)
|
|
112
|
+
{
|
|
113
|
+
v8::Isolate* isolate = args.GetIsolate();
|
|
114
|
+
if (!args.IsConstructCall()) {
|
|
115
|
+
v8util::ThrowTypeError(isolate, "OffscreenCanvas must be called with new");
|
|
116
|
+
return;
|
|
117
|
+
}
|
|
118
|
+
HostContext* host = HostContext::From(isolate);
|
|
119
|
+
int w = args.Length() > 0 && args[0]->IsNumber()
|
|
120
|
+
? static_cast<int>(args[0].As<v8::Number>()->Value()) : host->viewport_width;
|
|
121
|
+
int h = args.Length() > 1 && args[1]->IsNumber()
|
|
122
|
+
? static_cast<int>(args[1].As<v8::Number>()->Value()) : host->viewport_height;
|
|
123
|
+
args.GetReturnValue().Set(CreateOffscreenCanvas(isolate, host, w, h));
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
} // namespace
|
|
127
|
+
|
|
128
|
+
void InstallDomShims(v8::Isolate* isolate, v8::Local<v8::Object> global, HostContext* host)
|
|
129
|
+
{
|
|
130
|
+
v8::Local<v8::Context> ctx = isolate->GetCurrentContext();
|
|
131
|
+
|
|
132
|
+
// OffscreenCanvas コンストラクタ (メッシュ生成/テキスト/点内包判定で使用)
|
|
133
|
+
{
|
|
134
|
+
v8::Local<v8::FunctionTemplate> oc =
|
|
135
|
+
v8::FunctionTemplate::New(isolate, OffscreenCanvasConstructor);
|
|
136
|
+
oc->SetClassName(Str(isolate, "OffscreenCanvas"));
|
|
137
|
+
global->Set(ctx, Str(isolate, "OffscreenCanvas"),
|
|
138
|
+
oc->GetFunction(ctx).ToLocalChecked()).Check();
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
// globalThis / window / self は同一オブジェクトを指す
|
|
142
|
+
SetValue(isolate, global, "globalThis", global);
|
|
143
|
+
SetValue(isolate, global, "window", global);
|
|
144
|
+
SetValue(isolate, global, "self", global);
|
|
145
|
+
NoopEventTarget(isolate, global);
|
|
146
|
+
|
|
147
|
+
// devicePixelRatio
|
|
148
|
+
SetValue(isolate, global, "devicePixelRatio",
|
|
149
|
+
v8::Number::New(isolate, host->device_pixel_ratio));
|
|
150
|
+
|
|
151
|
+
// screen
|
|
152
|
+
v8::Local<v8::Object> screen = v8::Object::New(isolate);
|
|
153
|
+
SetValue(isolate, screen, "width", v8::Integer::New(isolate, host->viewport_width));
|
|
154
|
+
SetValue(isolate, screen, "height", v8::Integer::New(isolate, host->viewport_height));
|
|
155
|
+
SetValue(isolate, global, "screen", screen);
|
|
156
|
+
|
|
157
|
+
// innerWidth / innerHeight
|
|
158
|
+
SetValue(isolate, global, "innerWidth", v8::Integer::New(isolate, host->viewport_width));
|
|
159
|
+
SetValue(isolate, global, "innerHeight", v8::Integer::New(isolate, host->viewport_height));
|
|
160
|
+
|
|
161
|
+
// navigator (gpu/getGamepads は後段のバインディングが付与)
|
|
162
|
+
v8::Local<v8::Object> navigator = v8::Object::New(isolate);
|
|
163
|
+
SetValue(isolate, navigator, "userAgent",
|
|
164
|
+
Str(isolate, "Next2D/Xbox (GDK; V8; Dawn/WebGPU)"));
|
|
165
|
+
SetValue(isolate, navigator, "language", Str(isolate, "en-US"));
|
|
166
|
+
SetValue(isolate, navigator, "platform", Str(isolate, "Xbox"));
|
|
167
|
+
|
|
168
|
+
// navigator.clipboard (テキスト貼付/コピー、Win32 クリップボード)
|
|
169
|
+
v8::Local<v8::Object> clipboard = v8::Object::New(isolate);
|
|
170
|
+
SetMethod(isolate, clipboard, "readText", ClipboardReadText);
|
|
171
|
+
SetMethod(isolate, clipboard, "writeText", ClipboardWriteText);
|
|
172
|
+
SetValue(isolate, navigator, "clipboard", clipboard);
|
|
173
|
+
|
|
174
|
+
SetValue(isolate, global, "navigator", navigator);
|
|
175
|
+
|
|
176
|
+
// document
|
|
177
|
+
v8::Local<v8::Object> document = v8::Object::New(isolate);
|
|
178
|
+
SetMethod(isolate, document, "createElement", CreateElement);
|
|
179
|
+
SetMethod(isolate, document, "getElementById",
|
|
180
|
+
[](const v8::FunctionCallbackInfo<v8::Value>& a) { a.GetReturnValue().SetNull(); });
|
|
181
|
+
SetMethod(isolate, document, "querySelector",
|
|
182
|
+
[](const v8::FunctionCallbackInfo<v8::Value>& a) { a.GetReturnValue().SetNull(); });
|
|
183
|
+
SetMethod(isolate, document, "getElementsByTagName",
|
|
184
|
+
[](const v8::FunctionCallbackInfo<v8::Value>& a) {
|
|
185
|
+
a.GetReturnValue().Set(v8::Array::New(a.GetIsolate(), 0));
|
|
186
|
+
});
|
|
187
|
+
NoopEventTarget(isolate, document);
|
|
188
|
+
|
|
189
|
+
// document.body / documentElement は canvas を格納できる最小要素
|
|
190
|
+
v8::Local<v8::Object> body = v8::Object::New(isolate);
|
|
191
|
+
SetValue(isolate, body, "style", v8::Object::New(isolate));
|
|
192
|
+
SetMethod(isolate, body, "appendChild", [](const v8::FunctionCallbackInfo<v8::Value>& a) {
|
|
193
|
+
if (a.Length() > 0) a.GetReturnValue().Set(a[0]);
|
|
194
|
+
});
|
|
195
|
+
NoopEventTarget(isolate, body);
|
|
196
|
+
SetValue(isolate, document, "body", body);
|
|
197
|
+
SetValue(isolate, document, "documentElement", body);
|
|
198
|
+
|
|
199
|
+
// メインの canvas をあらかじめ 1 枚用意し document.__mainCanvas として公開する。
|
|
200
|
+
// bootstrap.js はこれを既定キャンバスとして Next2D に渡す。
|
|
201
|
+
v8::Local<v8::Object> canvas = CreateCanvasElement(isolate, host, 0, 0);
|
|
202
|
+
SetValue(isolate, document, "__mainCanvas", canvas);
|
|
203
|
+
|
|
204
|
+
global->Set(ctx, Str(isolate, "document"), document).Check();
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
void InstallGlobalBindings(v8::Isolate* isolate, v8::Local<v8::Object> global, HostContext* host)
|
|
208
|
+
{
|
|
209
|
+
// 依存順: console -> DOM -> timers -> fetch -> image -> audio -> webgpu -> gamepad
|
|
210
|
+
InstallConsole(isolate, global);
|
|
211
|
+
InstallDomShims(isolate, global, host);
|
|
212
|
+
InstallTimers(isolate, global, host);
|
|
213
|
+
InstallFetch(isolate, global, host);
|
|
214
|
+
InstallNetwork(isolate, global, host); // XMLHttpRequest/Blob/URL/ImageData
|
|
215
|
+
InstallImage(isolate, global, host);
|
|
216
|
+
InstallAudio(isolate, global, host);
|
|
217
|
+
InstallWebGPU(isolate, global, host); // navigator.gpu
|
|
218
|
+
InstallGamepad(isolate, global, host); // navigator.getGamepads
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
} // namespace next2d
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
#include "EventTarget.h"
|
|
2
|
+
|
|
3
|
+
#include "v8/V8Util.h"
|
|
4
|
+
|
|
5
|
+
#include <string>
|
|
6
|
+
|
|
7
|
+
namespace next2d {
|
|
8
|
+
|
|
9
|
+
using v8util::Str;
|
|
10
|
+
using v8util::SetMethod;
|
|
11
|
+
using v8util::SetValue;
|
|
12
|
+
using v8util::ToStdString;
|
|
13
|
+
|
|
14
|
+
namespace {
|
|
15
|
+
|
|
16
|
+
void AddEventListener(const v8::FunctionCallbackInfo<v8::Value>& args)
|
|
17
|
+
{
|
|
18
|
+
v8::Isolate* isolate = args.GetIsolate();
|
|
19
|
+
v8::Local<v8::Context> ctx = isolate->GetCurrentContext();
|
|
20
|
+
if (args.Length() < 2 || !args[1]->IsFunction()) {
|
|
21
|
+
return;
|
|
22
|
+
}
|
|
23
|
+
const std::string type = ToStdString(isolate, args[0]);
|
|
24
|
+
v8::Local<v8::Object> self = args.This();
|
|
25
|
+
|
|
26
|
+
v8::Local<v8::Value> lv;
|
|
27
|
+
v8::Local<v8::Object> listeners;
|
|
28
|
+
if (self->Get(ctx, Str(isolate, "__listeners")).ToLocal(&lv) && lv->IsObject()) {
|
|
29
|
+
listeners = lv.As<v8::Object>();
|
|
30
|
+
} else {
|
|
31
|
+
listeners = v8::Object::New(isolate);
|
|
32
|
+
SetValue(isolate, self, "__listeners", listeners);
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
v8::Local<v8::Value> arrv;
|
|
36
|
+
v8::Local<v8::Array> arr;
|
|
37
|
+
if (listeners->Get(ctx, Str(isolate, type.c_str())).ToLocal(&arrv) && arrv->IsArray()) {
|
|
38
|
+
arr = arrv.As<v8::Array>();
|
|
39
|
+
} else {
|
|
40
|
+
arr = v8::Array::New(isolate, 0);
|
|
41
|
+
listeners->Set(ctx, Str(isolate, type.c_str()), arr).Check();
|
|
42
|
+
}
|
|
43
|
+
arr->Set(ctx, arr->Length(), args[1]).Check();
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
void RemoveEventListener(const v8::FunctionCallbackInfo<v8::Value>& args)
|
|
47
|
+
{
|
|
48
|
+
v8::Isolate* isolate = args.GetIsolate();
|
|
49
|
+
v8::Local<v8::Context> ctx = isolate->GetCurrentContext();
|
|
50
|
+
if (args.Length() < 2) {
|
|
51
|
+
return;
|
|
52
|
+
}
|
|
53
|
+
const std::string type = ToStdString(isolate, args[0]);
|
|
54
|
+
v8::Local<v8::Value> lv;
|
|
55
|
+
if (!args.This()->Get(ctx, Str(isolate, "__listeners")).ToLocal(&lv) || !lv->IsObject()) {
|
|
56
|
+
return;
|
|
57
|
+
}
|
|
58
|
+
v8::Local<v8::Value> arrv;
|
|
59
|
+
if (!lv.As<v8::Object>()->Get(ctx, Str(isolate, type.c_str())).ToLocal(&arrv) || !arrv->IsArray()) {
|
|
60
|
+
return;
|
|
61
|
+
}
|
|
62
|
+
// 一致する関数を null 化 (簡易)
|
|
63
|
+
auto arr = arrv.As<v8::Array>();
|
|
64
|
+
for (uint32_t i = 0; i < arr->Length(); ++i) {
|
|
65
|
+
v8::Local<v8::Value> fn;
|
|
66
|
+
if (arr->Get(ctx, i).ToLocal(&fn) && fn->StrictEquals(args[1])) {
|
|
67
|
+
arr->Set(ctx, i, v8::Null(isolate)).Check();
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
void DispatchEventJS(const v8::FunctionCallbackInfo<v8::Value>& args)
|
|
73
|
+
{
|
|
74
|
+
v8::Isolate* isolate = args.GetIsolate();
|
|
75
|
+
if (args.Length() >= 1 && args[0]->IsObject()) {
|
|
76
|
+
DispatchEvent(isolate, args.This(), args[0].As<v8::Object>());
|
|
77
|
+
}
|
|
78
|
+
args.GetReturnValue().Set(true);
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
} // namespace
|
|
82
|
+
|
|
83
|
+
void InstallEventTarget(v8::Isolate* isolate, v8::Local<v8::Object> target)
|
|
84
|
+
{
|
|
85
|
+
SetMethod(isolate, target, "addEventListener", AddEventListener);
|
|
86
|
+
SetMethod(isolate, target, "removeEventListener", RemoveEventListener);
|
|
87
|
+
SetMethod(isolate, target, "dispatchEvent", DispatchEventJS);
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
void DispatchEvent(v8::Isolate* isolate, v8::Local<v8::Object> target,
|
|
91
|
+
v8::Local<v8::Object> event)
|
|
92
|
+
{
|
|
93
|
+
v8::Local<v8::Context> ctx = isolate->GetCurrentContext();
|
|
94
|
+
v8::Local<v8::Value> type_val;
|
|
95
|
+
if (!event->Get(ctx, Str(isolate, "type")).ToLocal(&type_val)) {
|
|
96
|
+
return;
|
|
97
|
+
}
|
|
98
|
+
const std::string type = ToStdString(isolate, type_val);
|
|
99
|
+
v8::Local<v8::Value> args1[1] = { event };
|
|
100
|
+
|
|
101
|
+
// on<type> プロパティ
|
|
102
|
+
v8::Local<v8::Value> on;
|
|
103
|
+
const std::string onname = "on" + type;
|
|
104
|
+
if (target->Get(ctx, Str(isolate, onname.c_str())).ToLocal(&on) && on->IsFunction()) {
|
|
105
|
+
v8::TryCatch tc(isolate);
|
|
106
|
+
(void) on.As<v8::Function>()->Call(ctx, target, 1, args1);
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
// __listeners[type]
|
|
110
|
+
v8::Local<v8::Value> lv;
|
|
111
|
+
if (target->Get(ctx, Str(isolate, "__listeners")).ToLocal(&lv) && lv->IsObject()) {
|
|
112
|
+
v8::Local<v8::Value> arrv;
|
|
113
|
+
if (lv.As<v8::Object>()->Get(ctx, Str(isolate, type.c_str())).ToLocal(&arrv) &&
|
|
114
|
+
arrv->IsArray()) {
|
|
115
|
+
auto arr = arrv.As<v8::Array>();
|
|
116
|
+
for (uint32_t i = 0; i < arr->Length(); ++i) {
|
|
117
|
+
v8::Local<v8::Value> fn;
|
|
118
|
+
if (arr->Get(ctx, i).ToLocal(&fn) && fn->IsFunction()) {
|
|
119
|
+
v8::TryCatch tc(isolate);
|
|
120
|
+
(void) fn.As<v8::Function>()->Call(ctx, target, 1, args1);
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
} // namespace next2d
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
// DOM EventTarget 相当の最小実装。
|
|
2
|
+
// addEventListener で登録した関数を __listeners[type] に保持し、C++ から DispatchEvent で発火する。
|
|
3
|
+
// main.cpp の WndProc が Win32 入力を PointerEvent/KeyboardEvent/WheelEvent へ変換して使う。
|
|
4
|
+
#pragma once
|
|
5
|
+
|
|
6
|
+
#include <v8.h>
|
|
7
|
+
|
|
8
|
+
namespace next2d {
|
|
9
|
+
|
|
10
|
+
// target に addEventListener / removeEventListener / dispatchEvent を設置する。
|
|
11
|
+
void InstallEventTarget(v8::Isolate* isolate, v8::Local<v8::Object> target);
|
|
12
|
+
|
|
13
|
+
// target の on<type> と __listeners[type] を発火する。event は {type, ...} を持つ JS オブジェクト。
|
|
14
|
+
void DispatchEvent(v8::Isolate* isolate, v8::Local<v8::Object> target,
|
|
15
|
+
v8::Local<v8::Object> event);
|
|
16
|
+
|
|
17
|
+
} // namespace next2d
|
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
#include "Bindings.h"
|
|
2
|
+
|
|
3
|
+
#include "HostContext.h"
|
|
4
|
+
#include "AssetLoader.h"
|
|
5
|
+
#include "v8/V8Util.h"
|
|
6
|
+
|
|
7
|
+
#include <cstring>
|
|
8
|
+
#include <vector>
|
|
9
|
+
|
|
10
|
+
namespace next2d {
|
|
11
|
+
|
|
12
|
+
using v8util::Str;
|
|
13
|
+
using v8util::ToStdString;
|
|
14
|
+
|
|
15
|
+
namespace {
|
|
16
|
+
|
|
17
|
+
// Response.arrayBuffer(): 内部データ(External の std::vector) から ArrayBuffer を作る。
|
|
18
|
+
void ResponseArrayBuffer(const v8::FunctionCallbackInfo<v8::Value>& args)
|
|
19
|
+
{
|
|
20
|
+
v8::Isolate* isolate = args.GetIsolate();
|
|
21
|
+
v8::Local<v8::Context> ctx = isolate->GetCurrentContext();
|
|
22
|
+
|
|
23
|
+
v8::Local<v8::Object> self = args.This();
|
|
24
|
+
v8::Local<v8::Value> ext =
|
|
25
|
+
self->GetInternalField(0).As<v8::Value>();
|
|
26
|
+
auto* data = static_cast<std::vector<uint8_t>*>(ext.As<v8::External>()->Value());
|
|
27
|
+
|
|
28
|
+
std::unique_ptr<v8::BackingStore> store =
|
|
29
|
+
v8::ArrayBuffer::NewBackingStore(isolate, data->size());
|
|
30
|
+
if (!data->empty()) {
|
|
31
|
+
std::memcpy(store->Data(), data->data(), data->size());
|
|
32
|
+
}
|
|
33
|
+
v8::Local<v8::ArrayBuffer> buffer =
|
|
34
|
+
v8::ArrayBuffer::New(isolate, std::move(store));
|
|
35
|
+
|
|
36
|
+
v8::Local<v8::Promise::Resolver> resolver =
|
|
37
|
+
v8::Promise::Resolver::New(ctx).ToLocalChecked();
|
|
38
|
+
resolver->Resolve(ctx, buffer).Check();
|
|
39
|
+
args.GetReturnValue().Set(resolver->GetPromise());
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
void ResponseText(const v8::FunctionCallbackInfo<v8::Value>& args)
|
|
43
|
+
{
|
|
44
|
+
v8::Isolate* isolate = args.GetIsolate();
|
|
45
|
+
v8::Local<v8::Context> ctx = isolate->GetCurrentContext();
|
|
46
|
+
|
|
47
|
+
v8::Local<v8::Object> self = args.This();
|
|
48
|
+
auto* data = static_cast<std::vector<uint8_t>*>(
|
|
49
|
+
self->GetInternalField(0).As<v8::External>()->Value());
|
|
50
|
+
|
|
51
|
+
v8::Local<v8::String> text = v8::String::NewFromUtf8(
|
|
52
|
+
isolate, reinterpret_cast<const char*>(data->data()),
|
|
53
|
+
v8::NewStringType::kNormal, static_cast<int>(data->size())
|
|
54
|
+
).ToLocalChecked();
|
|
55
|
+
|
|
56
|
+
v8::Local<v8::Promise::Resolver> resolver =
|
|
57
|
+
v8::Promise::Resolver::New(ctx).ToLocalChecked();
|
|
58
|
+
resolver->Resolve(ctx, text).Check();
|
|
59
|
+
args.GetReturnValue().Set(resolver->GetPromise());
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
void ResponseJson(const v8::FunctionCallbackInfo<v8::Value>& args)
|
|
63
|
+
{
|
|
64
|
+
v8::Isolate* isolate = args.GetIsolate();
|
|
65
|
+
v8::Local<v8::Context> ctx = isolate->GetCurrentContext();
|
|
66
|
+
|
|
67
|
+
v8::Local<v8::Object> self = args.This();
|
|
68
|
+
auto* data = static_cast<std::vector<uint8_t>*>(
|
|
69
|
+
self->GetInternalField(0).As<v8::External>()->Value());
|
|
70
|
+
|
|
71
|
+
v8::Local<v8::String> text = v8::String::NewFromUtf8(
|
|
72
|
+
isolate, reinterpret_cast<const char*>(data->data()),
|
|
73
|
+
v8::NewStringType::kNormal, static_cast<int>(data->size())
|
|
74
|
+
).ToLocalChecked();
|
|
75
|
+
|
|
76
|
+
v8::Local<v8::Promise::Resolver> resolver =
|
|
77
|
+
v8::Promise::Resolver::New(ctx).ToLocalChecked();
|
|
78
|
+
v8::Local<v8::Value> parsed;
|
|
79
|
+
if (v8::JSON::Parse(ctx, text).ToLocal(&parsed)) {
|
|
80
|
+
resolver->Resolve(ctx, parsed).Check();
|
|
81
|
+
} else {
|
|
82
|
+
resolver->Reject(ctx, v8::Exception::SyntaxError(Str(isolate, "Invalid JSON"))).Check();
|
|
83
|
+
}
|
|
84
|
+
args.GetReturnValue().Set(resolver->GetPromise());
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
// External に持たせた vector を解放するための弱参照コールバック
|
|
88
|
+
void ReleaseResponseData(const v8::WeakCallbackInfo<std::vector<uint8_t>>& info)
|
|
89
|
+
{
|
|
90
|
+
delete info.GetParameter();
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
v8::Local<v8::Object> MakeResponse(v8::Isolate* isolate, v8::Local<v8::Context> ctx,
|
|
94
|
+
std::vector<uint8_t>&& bytes, bool ok, int status)
|
|
95
|
+
{
|
|
96
|
+
v8::Local<v8::ObjectTemplate> tmpl = v8::ObjectTemplate::New(isolate);
|
|
97
|
+
tmpl->SetInternalFieldCount(1);
|
|
98
|
+
v8::Local<v8::Object> response = tmpl->NewInstance(ctx).ToLocalChecked();
|
|
99
|
+
|
|
100
|
+
auto* data = new std::vector<uint8_t>(std::move(bytes));
|
|
101
|
+
response->SetInternalField(0, v8::External::New(isolate, data));
|
|
102
|
+
|
|
103
|
+
// GC 時に解放
|
|
104
|
+
auto* handle = new v8::Global<v8::Object>(isolate, response);
|
|
105
|
+
handle->SetWeak(data, ReleaseResponseData, v8::WeakCallbackType::kParameter);
|
|
106
|
+
|
|
107
|
+
v8util::SetValue(isolate, response, "ok", v8::Boolean::New(isolate, ok));
|
|
108
|
+
v8util::SetValue(isolate, response, "status", v8::Integer::New(isolate, status));
|
|
109
|
+
v8util::SetMethod(isolate, response, "arrayBuffer", ResponseArrayBuffer);
|
|
110
|
+
v8util::SetMethod(isolate, response, "text", ResponseText);
|
|
111
|
+
v8util::SetMethod(isolate, response, "json", ResponseJson);
|
|
112
|
+
return response;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
// fetch(url) -> Promise<Response>
|
|
116
|
+
void Fetch(const v8::FunctionCallbackInfo<v8::Value>& args)
|
|
117
|
+
{
|
|
118
|
+
v8::Isolate* isolate = args.GetIsolate();
|
|
119
|
+
v8::Local<v8::Context> ctx = isolate->GetCurrentContext();
|
|
120
|
+
AssetLoader* assets = HostContext::From(isolate)->assets;
|
|
121
|
+
|
|
122
|
+
v8::Local<v8::Promise::Resolver> resolver =
|
|
123
|
+
v8::Promise::Resolver::New(ctx).ToLocalChecked();
|
|
124
|
+
args.GetReturnValue().Set(resolver->GetPromise());
|
|
125
|
+
|
|
126
|
+
if (args.Length() < 1) {
|
|
127
|
+
resolver->Reject(ctx, v8::Exception::TypeError(Str(isolate, "url required"))).Check();
|
|
128
|
+
return;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
const std::string url = ToStdString(isolate, args[0]);
|
|
132
|
+
auto bytes = assets->ReadBinary(url);
|
|
133
|
+
if (!bytes) {
|
|
134
|
+
// 404 相当の Response を返す (fetch は存在しないリソースでも reject しない)
|
|
135
|
+
resolver->Resolve(ctx, MakeResponse(isolate, ctx, {}, false, 404)).Check();
|
|
136
|
+
return;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
resolver->Resolve(ctx, MakeResponse(isolate, ctx, std::move(*bytes), true, 200)).Check();
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
} // namespace
|
|
143
|
+
|
|
144
|
+
void InstallFetch(v8::Isolate* isolate, v8::Local<v8::Object> global, HostContext* /*host*/)
|
|
145
|
+
{
|
|
146
|
+
v8util::SetMethod(isolate, global, "fetch", Fetch);
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
} // namespace next2d
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
#include "Bindings.h"
|
|
2
|
+
|
|
3
|
+
#include "HostContext.h"
|
|
4
|
+
#include "platform/GamepadManager.h"
|
|
5
|
+
#include "v8/V8Util.h"
|
|
6
|
+
|
|
7
|
+
namespace next2d {
|
|
8
|
+
|
|
9
|
+
using v8util::Str;
|
|
10
|
+
|
|
11
|
+
namespace {
|
|
12
|
+
|
|
13
|
+
v8::Local<v8::Object> BuildGamepad(v8::Isolate* isolate, v8::Local<v8::Context> ctx,
|
|
14
|
+
const GamepadSnapshot& snap, int index)
|
|
15
|
+
{
|
|
16
|
+
v8::Local<v8::Object> pad = v8::Object::New(isolate);
|
|
17
|
+
|
|
18
|
+
v8util::SetValue(isolate, pad, "index", v8::Integer::New(isolate, index));
|
|
19
|
+
v8util::SetValue(isolate, pad, "id", Str(isolate, "Xbox Wireless Controller (STANDARD GAMEPAD)"));
|
|
20
|
+
v8util::SetValue(isolate, pad, "mapping", Str(isolate, "standard"));
|
|
21
|
+
v8util::SetValue(isolate, pad, "connected", v8::Boolean::New(isolate, snap.connected));
|
|
22
|
+
v8util::SetValue(isolate, pad, "timestamp", v8::Number::New(isolate, snap.timestamp));
|
|
23
|
+
|
|
24
|
+
// axes
|
|
25
|
+
v8::Local<v8::Array> axes = v8::Array::New(isolate, static_cast<int>(snap.axes.size()));
|
|
26
|
+
for (size_t i = 0; i < snap.axes.size(); ++i) {
|
|
27
|
+
axes->Set(ctx, static_cast<uint32_t>(i), v8::Number::New(isolate, snap.axes[i])).Check();
|
|
28
|
+
}
|
|
29
|
+
v8util::SetValue(isolate, pad, "axes", axes);
|
|
30
|
+
|
|
31
|
+
// buttons: { pressed, touched, value }
|
|
32
|
+
v8::Local<v8::Array> buttons = v8::Array::New(isolate, static_cast<int>(snap.buttons.size()));
|
|
33
|
+
for (size_t i = 0; i < snap.buttons.size(); ++i) {
|
|
34
|
+
v8::Local<v8::Object> b = v8::Object::New(isolate);
|
|
35
|
+
v8util::SetValue(isolate, b, "pressed", v8::Boolean::New(isolate, snap.pressed[i]));
|
|
36
|
+
v8util::SetValue(isolate, b, "touched", v8::Boolean::New(isolate, snap.pressed[i]));
|
|
37
|
+
v8util::SetValue(isolate, b, "value", v8::Number::New(isolate, snap.buttons[i]));
|
|
38
|
+
buttons->Set(ctx, static_cast<uint32_t>(i), b).Check();
|
|
39
|
+
}
|
|
40
|
+
v8util::SetValue(isolate, pad, "buttons", buttons);
|
|
41
|
+
|
|
42
|
+
return pad;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
// navigator.getGamepads()
|
|
46
|
+
void GetGamepads(const v8::FunctionCallbackInfo<v8::Value>& args)
|
|
47
|
+
{
|
|
48
|
+
v8::Isolate* isolate = args.GetIsolate();
|
|
49
|
+
v8::Local<v8::Context> ctx = isolate->GetCurrentContext();
|
|
50
|
+
GamepadManager* mgr = HostContext::From(isolate)->gamepad;
|
|
51
|
+
|
|
52
|
+
const auto& snaps = mgr ? mgr->snapshots() : std::vector<GamepadSnapshot>{};
|
|
53
|
+
v8::Local<v8::Array> result = v8::Array::New(isolate, static_cast<int>(snaps.size()));
|
|
54
|
+
for (size_t i = 0; i < snaps.size(); ++i) {
|
|
55
|
+
if (snaps[i].connected) {
|
|
56
|
+
result->Set(ctx, static_cast<uint32_t>(i),
|
|
57
|
+
BuildGamepad(isolate, ctx, snaps[i], static_cast<int>(i))).Check();
|
|
58
|
+
} else {
|
|
59
|
+
result->Set(ctx, static_cast<uint32_t>(i), v8::Null(isolate)).Check();
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
args.GetReturnValue().Set(result);
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
} // namespace
|
|
66
|
+
|
|
67
|
+
// navigator は DomShims が生成するため、ここでは navigator.getGamepads を後付けする。
|
|
68
|
+
void InstallGamepad(v8::Isolate* isolate, v8::Local<v8::Object> global, HostContext* /*host*/)
|
|
69
|
+
{
|
|
70
|
+
v8::Local<v8::Context> ctx = isolate->GetCurrentContext();
|
|
71
|
+
v8::Local<v8::Value> nav_val;
|
|
72
|
+
if (!global->Get(ctx, Str(isolate, "navigator")).ToLocal(&nav_val) || !nav_val->IsObject()) {
|
|
73
|
+
return;
|
|
74
|
+
}
|
|
75
|
+
v8::Local<v8::Object> navigator = nav_val.As<v8::Object>();
|
|
76
|
+
v8util::SetMethod(isolate, navigator, "getGamepads", GetGamepads);
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
} // namespace next2d
|