@next2d/builder 0.1.7 → 0.1.10
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/dist/cli.d.ts +59 -0
- package/dist/cli.js +141 -0
- package/dist/constants.d.ts +53 -0
- package/dist/constants.js +64 -0
- package/dist/context.d.ts +29 -0
- package/dist/context.js +19 -0
- package/dist/electron.d.ts +10 -0
- package/dist/electron.js +142 -0
- package/dist/index.js +18 -754
- package/dist/native.d.ts +27 -0
- package/dist/native.js +123 -0
- package/dist/utils.d.ts +27 -0
- package/dist/utils.js +40 -0
- package/dist/web.d.ts +18 -0
- package/dist/web.js +70 -0
- package/dist/xbox.d.ts +62 -0
- package/dist/xbox.js +426 -0
- package/package.json +12 -9
- package/templates/xbox/CMakeLists.txt +116 -11
- package/templates/xbox/README.md +34 -8
- package/templates/xbox/cmake/FetchDawn.cmake +47 -6
- package/templates/xbox/cmake/FindV8.cmake +15 -0
- package/templates/xbox/js/bootstrap.js +151 -28
- package/templates/xbox/js/selftest.js +740 -90
- package/templates/xbox/src/AssetLoader.cpp +115 -12
- package/templates/xbox/src/AssetLoader.h +4 -0
- package/templates/xbox/src/EmbeddedAssets.cpp +149 -0
- package/templates/xbox/src/EmbeddedAssets.h +60 -0
- package/templates/xbox/src/EventLoop.cpp +13 -1
- package/templates/xbox/src/EventLoop.h +12 -0
- package/templates/xbox/src/bindings/Audio.cpp +211 -31
- package/templates/xbox/src/bindings/Bindings.h +10 -0
- package/templates/xbox/src/bindings/Canvas.cpp +77 -2
- package/templates/xbox/src/bindings/Canvas2D.cpp +496 -43
- package/templates/xbox/src/bindings/Console.cpp +6 -1
- package/templates/xbox/src/bindings/DomShims.cpp +143 -0
- package/templates/xbox/src/bindings/EventTarget.cpp +11 -0
- package/templates/xbox/src/bindings/Fetch.cpp +2 -8
- package/templates/xbox/src/bindings/Image.cpp +243 -15
- package/templates/xbox/src/bindings/Polyfills.cpp +1226 -0
- package/templates/xbox/src/bindings/RasterCore.h +105 -1
- package/templates/xbox/src/bindings/Video.cpp +153 -10
- package/templates/xbox/src/bindings/webgpu/WebGPU.cpp +844 -18
- package/templates/xbox/src/bindings/webgpu/WebGPUCommon.h +7 -9
- package/templates/xbox/src/bindings/webgpu/WebGPUEnums.h +32 -0
- package/templates/xbox/src/gpu/DawnContext.cpp +245 -7
- package/templates/xbox/src/gpu/DawnContext.h +18 -0
- package/templates/xbox/src/main.cpp +459 -8
- package/templates/xbox/src/platform/AudioDecoder.cpp +121 -0
- package/templates/xbox/src/platform/AudioDecoder.h +18 -0
- package/templates/xbox/src/platform/AudioEngine.cpp +37 -6
- package/templates/xbox/src/platform/DecodeQueue.cpp +136 -0
- package/templates/xbox/src/platform/DecodeQueue.h +27 -0
- package/templates/xbox/src/platform/ImageDecoder.cpp +44 -0
- package/templates/xbox/src/platform/ImageDecoder.h +18 -0
- package/templates/xbox/src/platform/StbTextRasterizer.cpp +324 -0
- package/templates/xbox/src/platform/StbTextRasterizer.h +37 -0
- package/templates/xbox/src/platform/TextRasterizer.cpp +32 -0
- package/templates/xbox/src/platform/WicDecoder.cpp +19 -0
- package/templates/xbox/src/v8/CodeCache.h +223 -0
- package/templates/xbox/src/v8/V8Runtime.cpp +76 -11
- package/templates/xbox/src/v8/V8Util.h +79 -0
- package/templates/xbox/src/v8/WeakHandle.h +45 -0
- package/templates/xbox/src/worker/WorkerRuntime.cpp +432 -48
- package/templates/xbox/src/worker/WorkerRuntime.h +43 -13
- package/templates/xbox/tests/check_local.sh +7 -2
- package/templates/xbox/tests/gamecore_api_probe.cpp +172 -0
- package/templates/xbox/tests/raster_test.cpp +50 -0
- package/templates/xbox/tests/windows_platform_test.cpp +75 -3
- package/templates/xbox/third_party/LICENSES.md +14 -0
- package/templates/xbox/third_party/dr_mp3.h +5412 -0
- package/templates/xbox/third_party/dr_wav.h +9131 -0
- package/templates/xbox/third_party/stb_image.h +7988 -0
- package/templates/xbox/third_party/stb_truetype.h +5079 -0
- package/templates/xbox/third_party/stb_vorbis.c +5584 -0
|
@@ -4,9 +4,12 @@
|
|
|
4
4
|
#include "AssetLoader.h"
|
|
5
5
|
#include "EventTarget.h"
|
|
6
6
|
#include "platform/AudioEngine.h"
|
|
7
|
+
#include "platform/DecodeQueue.h"
|
|
7
8
|
#include "v8/V8Util.h"
|
|
9
|
+
#include "v8/WeakHandle.h"
|
|
8
10
|
|
|
9
11
|
#include <memory>
|
|
12
|
+
#include <utility>
|
|
10
13
|
#include <vector>
|
|
11
14
|
|
|
12
15
|
namespace next2d {
|
|
@@ -22,11 +25,6 @@ struct GainParam {
|
|
|
22
25
|
std::shared_ptr<AudioVoice> voice; // source.connect(gain) 時にリンクされる
|
|
23
26
|
};
|
|
24
27
|
|
|
25
|
-
void ReleaseGainParam(const v8::WeakCallbackInfo<GainParam>& info)
|
|
26
|
-
{
|
|
27
|
-
delete info.GetParameter();
|
|
28
|
-
}
|
|
29
|
-
|
|
30
28
|
// 再生中(非ループ)の source ノード。毎フレーム IsFinished を見て "ended" を発火する。
|
|
31
29
|
std::vector<v8::Global<v8::Object>>& PlayingSources()
|
|
32
30
|
{
|
|
@@ -40,19 +38,12 @@ struct Holder {
|
|
|
40
38
|
std::shared_ptr<T> ptr;
|
|
41
39
|
};
|
|
42
40
|
|
|
43
|
-
template <typename T>
|
|
44
|
-
void ReleaseHolder(const v8::WeakCallbackInfo<Holder<T>>& info)
|
|
45
|
-
{
|
|
46
|
-
delete info.GetParameter();
|
|
47
|
-
}
|
|
48
|
-
|
|
49
41
|
template <typename T>
|
|
50
42
|
void Attach(v8::Isolate* isolate, v8::Local<v8::Object> obj, std::shared_ptr<T> ptr)
|
|
51
43
|
{
|
|
52
44
|
auto* holder = new Holder<T>{std::move(ptr)};
|
|
53
45
|
obj->SetInternalField(0, v8::External::New(isolate, holder));
|
|
54
|
-
|
|
55
|
-
handle->SetWeak(holder, ReleaseHolder<T>, v8::WeakCallbackType::kParameter);
|
|
46
|
+
v8util::AttachWeak(isolate, obj, holder);
|
|
56
47
|
}
|
|
57
48
|
|
|
58
49
|
template <typename T>
|
|
@@ -198,8 +189,7 @@ void CreateGain(const v8::FunctionCallbackInfo<v8::Value>& args)
|
|
|
198
189
|
v8::Local<v8::Object> gain = InternalTemplate(isolate)->NewInstance(ctx).ToLocalChecked();
|
|
199
190
|
auto* param = new GainParam();
|
|
200
191
|
gain->SetInternalField(0, v8::External::New(isolate, param));
|
|
201
|
-
|
|
202
|
-
handle->SetWeak(param, ReleaseGainParam, v8::WeakCallbackType::kParameter);
|
|
192
|
+
v8util::AttachWeak(isolate, gain, param);
|
|
203
193
|
|
|
204
194
|
gain->SetNativeDataProperty(ctx, Str(isolate, "value"),
|
|
205
195
|
[](v8::Local<v8::Name>, const v8::PropertyCallbackInfo<v8::Value>& info) {
|
|
@@ -226,26 +216,53 @@ void CreateGain(const v8::FunctionCallbackInfo<v8::Value>& args)
|
|
|
226
216
|
// 毎フレーム呼ばれ、再生終了した source ノードへ "ended" を発火する (main.cpp のループから)。
|
|
227
217
|
} // namespace
|
|
228
218
|
|
|
219
|
+
// V8 破棄前に呼ぶ。再生追跡リストの v8::Global を解放する。
|
|
220
|
+
// (放置すると static デストラクタが V8 破棄後に走りアクセス違反になる)
|
|
221
|
+
void ShutdownAudioEvents()
|
|
222
|
+
{
|
|
223
|
+
for (auto& g : PlayingSources()) {
|
|
224
|
+
g.Reset();
|
|
225
|
+
}
|
|
226
|
+
PlayingSources().clear();
|
|
227
|
+
}
|
|
228
|
+
|
|
229
229
|
void PumpAudioEvents(v8::Isolate* isolate)
|
|
230
230
|
{
|
|
231
231
|
auto& sources = PlayingSources();
|
|
232
232
|
if (sources.empty()) return;
|
|
233
233
|
v8::HandleScope scope(isolate);
|
|
234
234
|
v8::Local<v8::Context> ctx = isolate->GetCurrentContext();
|
|
235
|
+
|
|
236
|
+
// 1) 終了した source を集めて先に登録リストから外す。ここでは JS を呼ばない。
|
|
237
|
+
// (この時点の erase による v8::Global の move は他の再入が無いので安全)
|
|
238
|
+
std::vector<v8::Global<v8::Object>> finished;
|
|
235
239
|
for (size_t i = 0; i < sources.size();) {
|
|
236
240
|
v8::Local<v8::Object> node = sources[i].Get(isolate);
|
|
237
241
|
auto voice = Get<AudioVoice>(node);
|
|
238
242
|
if (!voice || voice->IsFinished()) {
|
|
239
|
-
|
|
240
|
-
v8::Local<v8::Object> ev = v8::Object::New(isolate);
|
|
241
|
-
ev->Set(ctx, Str(isolate, "type"), Str(isolate, "ended")).Check();
|
|
242
|
-
DispatchEvent(isolate, node, ev);
|
|
243
|
-
}
|
|
243
|
+
finished.push_back(std::move(sources[i]));
|
|
244
244
|
sources.erase(sources.begin() + i);
|
|
245
245
|
} else {
|
|
246
246
|
++i;
|
|
247
247
|
}
|
|
248
248
|
}
|
|
249
|
+
|
|
250
|
+
// 2) リストが安定した状態で "ended" を配送する。
|
|
251
|
+
// "ended" ハンドラは source.disconnect() や新規再生で PlayingSources を
|
|
252
|
+
// 再入的に変更しうる。以前は配送と erase を同一ループで行っていたため、
|
|
253
|
+
// 配送中にベクタが変更されると直後の erase 中の v8::Global move が壊れ
|
|
254
|
+
// GlobalHandles::MoveGlobal でアクセス違反していた。独立した finished を
|
|
255
|
+
// 走査することで再入変更から隔離する。
|
|
256
|
+
for (auto& g : finished) {
|
|
257
|
+
v8::Local<v8::Object> node = g.Get(isolate);
|
|
258
|
+
auto voice = Get<AudioVoice>(node);
|
|
259
|
+
if (voice) {
|
|
260
|
+
v8::Local<v8::Object> ev = v8::Object::New(isolate);
|
|
261
|
+
ev->Set(ctx, Str(isolate, "type"), Str(isolate, "ended")).Check();
|
|
262
|
+
DispatchEvent(isolate, node, ev);
|
|
263
|
+
}
|
|
264
|
+
g.Reset();
|
|
265
|
+
}
|
|
249
266
|
}
|
|
250
267
|
|
|
251
268
|
namespace {
|
|
@@ -268,21 +285,158 @@ void DecodeAudioData(const v8::FunctionCallbackInfo<v8::Value>& args)
|
|
|
268
285
|
static_cast<uint8_t*>(store->Data()) + store->ByteLength());
|
|
269
286
|
}
|
|
270
287
|
|
|
288
|
+
// Media Foundation デコードはバックグラウンドスレッドへ (Promise なので元々
|
|
289
|
+
// 非同期契約)。BGM の同期デコードは画面遷移時の数百 ms ヒッチになっていた。
|
|
290
|
+
auto resolver_ref =
|
|
291
|
+
std::make_shared<v8::Global<v8::Promise::Resolver>>(isolate, resolver);
|
|
271
292
|
auto pcm = std::make_shared<PcmBuffer>();
|
|
272
|
-
|
|
273
|
-
|
|
293
|
+
auto decoded = std::make_shared<bool>(false);
|
|
294
|
+
|
|
295
|
+
decodequeue::Submit(
|
|
296
|
+
[input = std::move(input), pcm, decoded]() {
|
|
297
|
+
*decoded = !input.empty() && AudioEngine::Decode(input, *pcm);
|
|
298
|
+
},
|
|
299
|
+
[isolate, resolver_ref, pcm, decoded]() {
|
|
300
|
+
v8::HandleScope hs(isolate);
|
|
301
|
+
v8::Local<v8::Promise::Resolver> r = resolver_ref->Get(isolate);
|
|
302
|
+
v8::Local<v8::Context> c = r->GetCreationContextChecked();
|
|
303
|
+
v8::Context::Scope cs(c);
|
|
304
|
+
|
|
305
|
+
// 重要: デコード失敗でも Reject しない。@next2d/media の SoundDecodeService は
|
|
306
|
+
// decodeAudioData の reject を catch して先頭バイトをスキップし再帰リトライするが、
|
|
307
|
+
// そのループ条件 (`idx > byteLength`) が常に false のため同一バッファで無限再帰し、
|
|
308
|
+
// マイクロタスクが枯渇してフレームワークのローディング画面ごとフリーズする。
|
|
309
|
+
// ここで無音の空バッファを resolve すれば、そのサウンドは無音になるだけでアプリは
|
|
310
|
+
// 進行できる (graceful degradation)。実データが壊れているか MF 非対応かは上のログで判別。
|
|
311
|
+
if (!*decoded) {
|
|
312
|
+
pcm->samples.clear(); // 0 サンプル = 無音
|
|
313
|
+
pcm->channels = pcm->channels ? pcm->channels : 2;
|
|
314
|
+
pcm->sample_rate = pcm->sample_rate ? pcm->sample_rate : 48000;
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
v8::Local<v8::Object> buffer =
|
|
318
|
+
InternalTemplate(isolate)->NewInstance(c).ToLocalChecked();
|
|
319
|
+
Attach<PcmBuffer>(isolate, buffer, pcm);
|
|
320
|
+
v8util::SetValue(isolate, buffer, "sampleRate",
|
|
321
|
+
v8::Number::New(isolate, pcm->sample_rate));
|
|
322
|
+
v8util::SetValue(isolate, buffer, "numberOfChannels",
|
|
323
|
+
v8::Integer::New(isolate, pcm->channels));
|
|
324
|
+
v8util::SetValue(isolate, buffer, "duration",
|
|
325
|
+
v8::Number::New(isolate, pcm->channels
|
|
326
|
+
? static_cast<double>(pcm->samples.size()) / pcm->channels / pcm->sample_rate
|
|
327
|
+
: 0.0));
|
|
328
|
+
r->Resolve(c, buffer).Check();
|
|
329
|
+
resolver_ref->Reset();
|
|
330
|
+
});
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
// --- Audio (HTMLAudioElement 最小実装) -------------------------------------
|
|
334
|
+
// ゲームコードが `new Audio(url)` で効果音/BGM を再生する経路。
|
|
335
|
+
// play/pause/paused/loop/volume/currentTime/preload をサポートする。
|
|
336
|
+
// pause→play は先頭からの再生になる (位置レジュームは未対応の近似)。
|
|
337
|
+
|
|
338
|
+
void AudioElementPlay(const v8::FunctionCallbackInfo<v8::Value>& args)
|
|
339
|
+
{
|
|
340
|
+
v8::Isolate* isolate = args.GetIsolate();
|
|
341
|
+
v8::Local<v8::Context> ctx = isolate->GetCurrentContext();
|
|
342
|
+
v8::Local<v8::Object> self = args.This();
|
|
343
|
+
|
|
344
|
+
auto resolver = v8::Promise::Resolver::New(ctx).ToLocalChecked();
|
|
345
|
+
args.GetReturnValue().Set(resolver->GetPromise());
|
|
346
|
+
|
|
347
|
+
auto voice = Get<AudioVoice>(self);
|
|
348
|
+
if (voice) {
|
|
349
|
+
bool loop = false;
|
|
350
|
+
double volume = 1.0;
|
|
351
|
+
v8::Local<v8::Value> v;
|
|
352
|
+
if (self->Get(ctx, Str(isolate, "loop")).ToLocal(&v)) {
|
|
353
|
+
loop = v->BooleanValue(isolate);
|
|
354
|
+
}
|
|
355
|
+
if (self->Get(ctx, Str(isolate, "volume")).ToLocal(&v) && v->IsNumber()) {
|
|
356
|
+
volume = v.As<v8::Number>()->Value();
|
|
357
|
+
}
|
|
358
|
+
voice->SetVolume(static_cast<float>(volume));
|
|
359
|
+
voice->Start(loop);
|
|
360
|
+
}
|
|
361
|
+
v8util::SetValue(isolate, self, "paused", v8::Boolean::New(isolate, false));
|
|
362
|
+
resolver->Resolve(ctx, v8::Undefined(isolate)).Check();
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
void AudioElementPause(const v8::FunctionCallbackInfo<v8::Value>& args)
|
|
366
|
+
{
|
|
367
|
+
v8::Isolate* isolate = args.GetIsolate();
|
|
368
|
+
auto voice = Get<AudioVoice>(args.This());
|
|
369
|
+
if (voice) {
|
|
370
|
+
voice->Stop();
|
|
371
|
+
}
|
|
372
|
+
v8util::SetValue(isolate, args.This(), "paused", v8::Boolean::New(isolate, true));
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
void AudioElementConstructor(const v8::FunctionCallbackInfo<v8::Value>& args)
|
|
376
|
+
{
|
|
377
|
+
v8::Isolate* isolate = args.GetIsolate();
|
|
378
|
+
if (!args.IsConstructCall()) {
|
|
379
|
+
v8util::ThrowTypeError(isolate, "Audio must be called with new");
|
|
274
380
|
return;
|
|
275
381
|
}
|
|
382
|
+
v8::Local<v8::Object> self = args.This();
|
|
383
|
+
Attach<AudioVoice>(isolate, self, nullptr);
|
|
384
|
+
|
|
385
|
+
// URL 指定があればロードし、デコードはバックグラウンドスレッドへ (失敗時は
|
|
386
|
+
// 無音の no-op)。デコード完了前に play() が呼ばれていた場合 (paused=false) は
|
|
387
|
+
// 完了時に再生を開始する (同期デコード時代の「即時再生」挙動を維持)。
|
|
388
|
+
if (args.Length() > 0 && args[0]->IsString()) {
|
|
389
|
+
const std::string src = ToStdString(isolate, args[0]);
|
|
390
|
+
v8util::SetValue(isolate, self, "src", args[0]);
|
|
391
|
+
HostContext* host = HostContext::From(isolate);
|
|
392
|
+
auto bytes = host->assets->ReadBinary(src);
|
|
393
|
+
if (bytes) {
|
|
394
|
+
auto self_ref = std::make_shared<v8::Global<v8::Object>>(isolate, self);
|
|
395
|
+
auto pcm = std::make_shared<PcmBuffer>();
|
|
396
|
+
auto decoded = std::make_shared<bool>(false);
|
|
397
|
+
decodequeue::Submit(
|
|
398
|
+
[input = std::move(*bytes), pcm, decoded]() {
|
|
399
|
+
*decoded = AudioEngine::Decode(input, *pcm);
|
|
400
|
+
},
|
|
401
|
+
[isolate, self_ref, pcm, decoded, host]() {
|
|
402
|
+
v8::HandleScope hs(isolate);
|
|
403
|
+
v8::Local<v8::Object> el = self_ref->Get(isolate);
|
|
404
|
+
v8::Local<v8::Context> c = el->GetCreationContextChecked();
|
|
405
|
+
v8::Context::Scope cs(c);
|
|
406
|
+
if (*decoded) {
|
|
407
|
+
auto voice = host->audio->CreateVoice(pcm);
|
|
408
|
+
Attach<AudioVoice>(isolate, el, voice);
|
|
409
|
+
// デコード中に play() 済みなら開始する
|
|
410
|
+
v8::Local<v8::Value> paused_v, loop_v, vol_v;
|
|
411
|
+
const bool paused =
|
|
412
|
+
!el->Get(c, Str(isolate, "paused")).ToLocal(&paused_v) ||
|
|
413
|
+
paused_v->BooleanValue(isolate);
|
|
414
|
+
if (!paused && voice) {
|
|
415
|
+
if (el->Get(c, Str(isolate, "volume")).ToLocal(&vol_v) &&
|
|
416
|
+
vol_v->IsNumber()) {
|
|
417
|
+
voice->SetVolume(static_cast<float>(
|
|
418
|
+
vol_v.As<v8::Number>()->Value()));
|
|
419
|
+
}
|
|
420
|
+
const bool loop =
|
|
421
|
+
el->Get(c, Str(isolate, "loop")).ToLocal(&loop_v) &&
|
|
422
|
+
loop_v->BooleanValue(isolate);
|
|
423
|
+
voice->Start(loop);
|
|
424
|
+
}
|
|
425
|
+
}
|
|
426
|
+
self_ref->Reset();
|
|
427
|
+
});
|
|
428
|
+
}
|
|
429
|
+
}
|
|
276
430
|
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
v8util::SetValue(isolate,
|
|
280
|
-
v8util::SetValue(isolate,
|
|
281
|
-
v8util::SetValue(isolate,
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
431
|
+
v8util::SetValue(isolate, self, "loop", v8::Boolean::New(isolate, false));
|
|
432
|
+
v8util::SetValue(isolate, self, "volume", v8::Number::New(isolate, 1.0));
|
|
433
|
+
v8util::SetValue(isolate, self, "paused", v8::Boolean::New(isolate, true));
|
|
434
|
+
v8util::SetValue(isolate, self, "preload", Str(isolate, "auto"));
|
|
435
|
+
v8util::SetValue(isolate, self, "currentTime", v8::Number::New(isolate, 0));
|
|
436
|
+
v8util::SetMethod(isolate, self, "play", AudioElementPlay);
|
|
437
|
+
v8util::SetMethod(isolate, self, "pause", AudioElementPause);
|
|
438
|
+
InstallEventTarget(isolate, self);
|
|
439
|
+
args.GetReturnValue().Set(self);
|
|
286
440
|
}
|
|
287
441
|
|
|
288
442
|
// AudioContext コンストラクタ
|
|
@@ -299,6 +453,23 @@ void AudioContextConstructor(const v8::FunctionCallbackInfo<v8::Value>& args)
|
|
|
299
453
|
v8util::SetMethod(isolate, self, "createBufferSource", CreateBufferSource);
|
|
300
454
|
v8util::SetMethod(isolate, self, "createGain", CreateGain);
|
|
301
455
|
v8util::SetMethod(isolate, self, "decodeAudioData", DecodeAudioData);
|
|
456
|
+
|
|
457
|
+
// state / resume / suspend / close: ブラウザの AudioContext はユーザージェスチャまで
|
|
458
|
+
// suspended だが、ホストの XAudio2 は常に有効なので "running" 固定。player の音声
|
|
459
|
+
// アンロック (初回 pointerup で ctx.resume()) が未実装だと TypeError で throw し、
|
|
460
|
+
// そのハンドラ(ミュート解除等)以降が中断する。解決済み Promise を返す no-op にする。
|
|
461
|
+
v8util::SetValue(isolate, self, "state", v8util::Str(isolate, "running"));
|
|
462
|
+
auto resolvedPromise = [](const v8::FunctionCallbackInfo<v8::Value>& a) {
|
|
463
|
+
v8::Isolate* iso = a.GetIsolate();
|
|
464
|
+
v8::Local<v8::Context> c = iso->GetCurrentContext();
|
|
465
|
+
auto r = v8::Promise::Resolver::New(c).ToLocalChecked();
|
|
466
|
+
(void) r->Resolve(c, v8::Undefined(iso));
|
|
467
|
+
a.GetReturnValue().Set(r->GetPromise());
|
|
468
|
+
};
|
|
469
|
+
v8util::SetMethod(isolate, self, "resume", resolvedPromise);
|
|
470
|
+
v8util::SetMethod(isolate, self, "suspend", resolvedPromise);
|
|
471
|
+
v8util::SetMethod(isolate, self, "close", resolvedPromise);
|
|
472
|
+
|
|
302
473
|
args.GetReturnValue().Set(self);
|
|
303
474
|
}
|
|
304
475
|
|
|
@@ -315,6 +486,15 @@ void InstallAudio(v8::Isolate* isolate, v8::Local<v8::Object> global, HostContex
|
|
|
315
486
|
|
|
316
487
|
global->Set(ctx, Str(isolate, "AudioContext"), fn).Check();
|
|
317
488
|
global->Set(ctx, Str(isolate, "webkitAudioContext"), fn).Check();
|
|
489
|
+
|
|
490
|
+
// Audio (HTMLAudioElement 最小): ゲームコードの `new Audio(url)` 用。
|
|
491
|
+
// インスタンスは内部フィールドに AudioVoice ホルダを持つ。
|
|
492
|
+
v8::Local<v8::FunctionTemplate> audio_tmpl =
|
|
493
|
+
v8::FunctionTemplate::New(isolate, AudioElementConstructor);
|
|
494
|
+
audio_tmpl->SetClassName(Str(isolate, "Audio"));
|
|
495
|
+
audio_tmpl->InstanceTemplate()->SetInternalFieldCount(1);
|
|
496
|
+
global->Set(ctx, Str(isolate, "Audio"),
|
|
497
|
+
audio_tmpl->GetFunction(ctx).ToLocalChecked()).Check();
|
|
318
498
|
}
|
|
319
499
|
|
|
320
500
|
} // namespace next2d
|
|
@@ -41,6 +41,12 @@ void InstallAudio(v8::Isolate* isolate, v8::Local<v8::Object> global, HostContex
|
|
|
41
41
|
// 再生終了した AudioBufferSourceNode へ "ended" を発火する。main.cpp のループから毎フレーム呼ぶ。
|
|
42
42
|
void PumpAudioEvents(v8::Isolate* isolate);
|
|
43
43
|
|
|
44
|
+
// V8 破棄前に呼ぶ。音声の再生追跡リスト (v8::Global) を解放する。
|
|
45
|
+
void ShutdownAudioEvents();
|
|
46
|
+
|
|
47
|
+
// V8 破棄前に呼ぶ。WebGPU テンプレートの v8::Global (static) を解放する。
|
|
48
|
+
void ShutdownWebGPU();
|
|
49
|
+
|
|
44
50
|
// window / document / navigator / screen 等の DOM 相当スタブと canvas 生成
|
|
45
51
|
void InstallDomShims(v8::Isolate* isolate, v8::Local<v8::Object> global, HostContext* host);
|
|
46
52
|
|
|
@@ -69,6 +75,10 @@ bool GetVideoFramePixels(v8::Isolate* isolate, v8::Local<v8::Object> obj,
|
|
|
69
75
|
// blob: URL のバイト列をテキストとして取得する (Network.cpp)。
|
|
70
76
|
bool ResolveObjectURL(const std::string& url, std::string* out_text);
|
|
71
77
|
|
|
78
|
+
// TextEncoder/TextDecoder/queueMicrotask 等の JS ポリフィルを評価する (Polyfills.cpp)。
|
|
79
|
+
// bootstrap.js はメインのみのため、worker を含む全コンテキストにはこちらで注入する。
|
|
80
|
+
void InstallPolyfills(v8::Isolate* isolate, v8::Local<v8::Context> context);
|
|
81
|
+
|
|
72
82
|
// 上記をまとめてインストールする
|
|
73
83
|
void InstallGlobalBindings(v8::Isolate* isolate, v8::Local<v8::Object> global, HostContext* host);
|
|
74
84
|
|
|
@@ -105,6 +105,16 @@ void TransferControlToOffscreen(const v8::FunctionCallbackInfo<v8::Value>& args)
|
|
|
105
105
|
if (args.This()->Get(ctx, Str(isolate, "height")).ToLocal(&hv) && hv->IsNumber()) {
|
|
106
106
|
height = static_cast<int>(hv.As<v8::Number>()->Value());
|
|
107
107
|
}
|
|
108
|
+
|
|
109
|
+
// このソース canvas (args.This()) こそがプレイヤーの主表示 canvas。
|
|
110
|
+
// player は「document.createElement('canvas') → pointer リスナ登録 → その canvas を
|
|
111
|
+
// transferControlToOffscreen()」の順で主 canvas を作る (input と描画で同一要素)。
|
|
112
|
+
// 一方 createElement は毎回 main_canvas を上書きするため、後続のヒットテスト用
|
|
113
|
+
// Canvas2D 生成 (getContext('2d') 用に 7 個も作る) で main_canvas が別要素に化け、
|
|
114
|
+
// WndProc の入力配送先とリスナ登録先がズレて「ボタンが全く反応しない」状態になる。
|
|
115
|
+
// transferControlToOffscreen が呼ばれる唯一の canvas = 正しい入力要素なので確定させる。
|
|
116
|
+
host->main_canvas.Reset(isolate, args.This());
|
|
117
|
+
|
|
108
118
|
args.GetReturnValue().Set(CreateOffscreenCanvas(isolate, host, width, height));
|
|
109
119
|
}
|
|
110
120
|
|
|
@@ -135,6 +145,9 @@ v8::Local<v8::Object> CreateCanvasElement(v8::Isolate* isolate, HostContext* hos
|
|
|
135
145
|
int width, int height)
|
|
136
146
|
{
|
|
137
147
|
v8::Local<v8::Object> canvas = v8::Object::New(isolate);
|
|
148
|
+
// player の canvasSetPositionService は children[0].localName === "canvas" を確認する
|
|
149
|
+
SetValue(isolate, canvas, "tagName", Str(isolate, "CANVAS"));
|
|
150
|
+
SetValue(isolate, canvas, "localName", Str(isolate, "canvas"));
|
|
138
151
|
SetValue(isolate, canvas, "width",
|
|
139
152
|
v8::Integer::New(isolate, width ? width : host->viewport_width));
|
|
140
153
|
SetValue(isolate, canvas, "height",
|
|
@@ -144,10 +157,72 @@ v8::Local<v8::Object> CreateCanvasElement(v8::Isolate* isolate, HostContext* hos
|
|
|
144
157
|
SetValue(isolate, canvas, "style", v8::Object::New(isolate));
|
|
145
158
|
SetMethod(isolate, canvas, "getContext", Canvas_GetContext);
|
|
146
159
|
SetMethod(isolate, canvas, "transferControlToOffscreen", TransferControlToOffscreen);
|
|
160
|
+
// Pointer capture: player の pointerdown ハンドラは t.setPointerCapture(pointerId) を
|
|
161
|
+
// 呼ぶ。未実装だと TypeError で DispatchEvent の TryCatch に飲まれ、以降の座標解決
|
|
162
|
+
// (dl) に到達せずボタンが反応しない。no-op として実装する (単一ウィンドウでは
|
|
163
|
+
// capture の有無で挙動は変わらない)。hasPointerCapture は false を返す。
|
|
164
|
+
SetMethod(isolate, canvas, "setPointerCapture",
|
|
165
|
+
[](const v8::FunctionCallbackInfo<v8::Value>&) {});
|
|
166
|
+
SetMethod(isolate, canvas, "releasePointerCapture",
|
|
167
|
+
[](const v8::FunctionCallbackInfo<v8::Value>&) {});
|
|
168
|
+
SetMethod(isolate, canvas, "hasPointerCapture",
|
|
169
|
+
[](const v8::FunctionCallbackInfo<v8::Value>& a) {
|
|
170
|
+
a.GetReturnValue().Set(false);
|
|
171
|
+
});
|
|
147
172
|
InstallEventTarget(isolate, canvas); // pointer/wheel リスナを保持
|
|
148
173
|
SetMethod(isolate, canvas, "getBoundingClientRect", GetBoundingClientRect);
|
|
149
|
-
|
|
150
|
-
|
|
174
|
+
|
|
175
|
+
// player の CanvasInitializeService が canvas.setAttribute("style", ...) を呼ぶ。
|
|
176
|
+
// 属性は __attrs に保存する (見た目の CSS はネイティブ描画では不要)。
|
|
177
|
+
SetMethod(isolate, canvas, "setAttribute",
|
|
178
|
+
[](const v8::FunctionCallbackInfo<v8::Value>& a) {
|
|
179
|
+
if (a.Length() < 2) return;
|
|
180
|
+
v8::Isolate* iso = a.GetIsolate();
|
|
181
|
+
v8::Local<v8::Context> c = iso->GetCurrentContext();
|
|
182
|
+
v8::Local<v8::Value> attrs;
|
|
183
|
+
v8::Local<v8::Object> store;
|
|
184
|
+
if (a.This()->Get(c, v8util::Str(iso, "__attrs")).ToLocal(&attrs) && attrs->IsObject()) {
|
|
185
|
+
store = attrs.As<v8::Object>();
|
|
186
|
+
} else {
|
|
187
|
+
store = v8::Object::New(iso);
|
|
188
|
+
v8util::SetValue(iso, a.This(), "__attrs", store);
|
|
189
|
+
}
|
|
190
|
+
store->Set(c, a[0], a[1]).Check();
|
|
191
|
+
});
|
|
192
|
+
SetMethod(isolate, canvas, "getAttribute",
|
|
193
|
+
[](const v8::FunctionCallbackInfo<v8::Value>& a) {
|
|
194
|
+
a.GetReturnValue().SetNull();
|
|
195
|
+
if (a.Length() < 1) return;
|
|
196
|
+
v8::Isolate* iso = a.GetIsolate();
|
|
197
|
+
v8::Local<v8::Context> c = iso->GetCurrentContext();
|
|
198
|
+
v8::Local<v8::Value> attrs;
|
|
199
|
+
if (a.This()->Get(c, v8util::Str(iso, "__attrs")).ToLocal(&attrs) && attrs->IsObject()) {
|
|
200
|
+
v8::Local<v8::Value> v;
|
|
201
|
+
if (attrs.As<v8::Object>()->Get(c, a[0]).ToLocal(&v) && !v->IsUndefined()) {
|
|
202
|
+
a.GetReturnValue().Set(v);
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
});
|
|
206
|
+
SetMethod(isolate, canvas, "removeAttribute",
|
|
207
|
+
[](const v8::FunctionCallbackInfo<v8::Value>& a) {
|
|
208
|
+
if (a.Length() < 1) return;
|
|
209
|
+
v8::Isolate* iso = a.GetIsolate();
|
|
210
|
+
v8::Local<v8::Context> c = iso->GetCurrentContext();
|
|
211
|
+
v8::Local<v8::Value> attrs;
|
|
212
|
+
if (a.This()->Get(c, v8util::Str(iso, "__attrs")).ToLocal(&attrs) && attrs->IsObject()) {
|
|
213
|
+
(void) attrs.As<v8::Object>()->Delete(c, a[0]);
|
|
214
|
+
}
|
|
215
|
+
});
|
|
216
|
+
// 主要 canvas の記録 (WndProc の入力配送先)。
|
|
217
|
+
// 確定は transferControlToOffscreen 側で行う (player が input を張る主 canvas はそちら)。
|
|
218
|
+
// ここでは「まだ未設定なら暫定で最初の canvas を採用」するフォールバックに留め、
|
|
219
|
+
// 後続の Canvas2D 生成で上書きしない (上書きすると入力配送先がヒットテスト用 canvas に化ける)。
|
|
220
|
+
{
|
|
221
|
+
HostContext* host = HostContext::From(isolate);
|
|
222
|
+
if (host->main_canvas.IsEmpty()) {
|
|
223
|
+
host->main_canvas.Reset(isolate, canvas);
|
|
224
|
+
}
|
|
225
|
+
}
|
|
151
226
|
return canvas;
|
|
152
227
|
}
|
|
153
228
|
|