@geastack/apple 0.2.8
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/LICENSE +202 -0
- package/README.md +54 -0
- package/dist/index.d.ts +127 -0
- package/dist/index.js +3076 -0
- package/generated/AVFoundation.d.ts +112 -0
- package/generated/ActivityKit.d.ts +7 -0
- package/generated/AppKit.d.ts +268 -0
- package/generated/CoreGraphics.d.ts +18 -0
- package/generated/CoreLocation.d.ts +28 -0
- package/generated/CoreMedia.d.ts +8 -0
- package/generated/Dispatch.d.ts +2 -0
- package/generated/Foundation.d.ts +13 -0
- package/generated/MapKit.d.ts +28 -0
- package/generated/Metal.d.ts +91 -0
- package/generated/MetalKit.d.ts +22 -0
- package/generated/Photos.d.ts +5 -0
- package/generated/QuartzCore.d.ts +12 -0
- package/generated/SwiftData.d.ts +11 -0
- package/generated/UIKit.d.ts +187 -0
- package/package.json +123 -0
- package/runtime/AVFoundation.js +136 -0
- package/runtime/ActivityKit.js +18 -0
- package/runtime/AppKit.js +313 -0
- package/runtime/CoreGraphics.js +15 -0
- package/runtime/CoreLocation.js +30 -0
- package/runtime/CoreMedia.js +7 -0
- package/runtime/Dispatch.js +11 -0
- package/runtime/Foundation.js +21 -0
- package/runtime/MapKit.js +22 -0
- package/runtime/Metal.js +126 -0
- package/runtime/MetalKit.js +15 -0
- package/runtime/Photos.js +9 -0
- package/runtime/QuartzCore.js +9 -0
- package/runtime/SwiftData.js +29 -0
- package/runtime/UIKit.js +242 -0
- package/runtime/empty.js +1 -0
- package/targets/ios/Info.plist.in +25 -0
- package/targets/ios/README.md +46 -0
- package/targets/ios/build-ios.sh +395 -0
- package/targets/ios/detect-development-team.mjs +109 -0
- package/targets/ios/generate-resident-entry.mjs +67 -0
- package/targets/ios/generate-resident-registry.mjs +162 -0
- package/targets/ios/generate-xcode-project.mjs +520 -0
- package/targets/ios/main/canvas_view.h +7 -0
- package/targets/ios/main/canvas_view.mm +94 -0
- package/targets/ios/main/font_registry.h +17 -0
- package/targets/ios/main/font_registry.mm +129 -0
- package/targets/ios/main/image_bridge.h +13 -0
- package/targets/ios/main/image_bridge.mm +53 -0
- package/targets/ios/main/ios_apps.c +28 -0
- package/targets/ios/main/ios_camera.mm +446 -0
- package/targets/ios/main/ios_display.mm +344 -0
- package/targets/ios/main/ios_main.mm +811 -0
- package/targets/ios/main/ios_memory.cpp +55 -0
- package/targets/ios/main/ios_renderer.h +20 -0
- package/targets/ios/main/ios_renderer.mm +27 -0
- package/targets/ios/main/ios_root_background.cpp +20 -0
- package/targets/ios/main/ios_root_background.h +11 -0
- package/targets/ios/main/ios_sensors.mm +244 -0
- package/targets/ios/main/ios_timers.mm +53 -0
- package/targets/ios/main/renderer/ios_renderer_internal.h +65 -0
- package/targets/ios/main/renderer/ios_renderer_support.mm +111 -0
- package/targets/ios/main/renderer/native_button.mm +179 -0
- package/targets/ios/main/renderer/native_label.mm +161 -0
- package/targets/ios/main/renderer/native_scroll_container.mm +279 -0
- package/targets/ios/main/renderer/view_reconciler.mm +274 -0
- package/targets/macos/Info.plist.in +26 -0
- package/targets/macos/README.md +131 -0
- package/targets/macos/build-child-lifecycle.sh +119 -0
- package/targets/macos/build-macos.sh +2156 -0
- package/targets/macos/check-module-graph-freshness.mjs +82 -0
- package/targets/macos/compiler-input-fingerprint.mjs +32 -0
- package/targets/macos/finalize-apple-native-output.mjs +77 -0
- package/targets/macos/generate-resident-entry.mjs +80 -0
- package/targets/macos/generate-resident-registry.mjs +141 -0
- package/targets/macos/heavy-build-lock.sh +176 -0
- package/targets/macos/main/canvas_view.h +9 -0
- package/targets/macos/main/canvas_view.mm +176 -0
- package/targets/macos/main/color_convert.h +18 -0
- package/targets/macos/main/color_convert.mm +39 -0
- package/targets/macos/main/font_registry.h +35 -0
- package/targets/macos/main/font_registry.mm +350 -0
- package/targets/macos/main/image_bridge.h +16 -0
- package/targets/macos/main/image_bridge.mm +52 -0
- package/targets/macos/main/macos_app_platform.mm +95 -0
- package/targets/macos/main/macos_apps.c +31 -0
- package/targets/macos/main/macos_device_control.mm +56 -0
- package/targets/macos/main/macos_display.mm +484 -0
- package/targets/macos/main/macos_host_device_control.mm +95 -0
- package/targets/macos/main/macos_main.mm +839 -0
- package/targets/macos/main/macos_memory.cpp +31 -0
- package/targets/macos/main/macos_native_shell.h +31 -0
- package/targets/macos/main/macos_native_shell.mm +396 -0
- package/targets/macos/main/macos_network.mm +253 -0
- package/targets/macos/main/macos_renderer.h +39 -0
- package/targets/macos/main/macos_renderer.mm +1969 -0
- package/targets/macos/main/macos_sensors.cpp +148 -0
- package/targets/macos/main/macos_smoke_app.cpp +65 -0
- package/targets/macos/main/macos_timers.mm +52 -0
- package/targets/macos/main/press_bridge.h +21 -0
- package/targets/macos/main/press_bridge.mm +72 -0
- package/targets/macos/main/thermalright_hid_display.h +14 -0
- package/targets/macos/main/thermalright_hid_display.mm +995 -0
- package/targets/macos/native-artifact-provenance.mjs +421 -0
- package/targets/macos/order-compile-edges.mjs +64 -0
- package/targets/macos/preflight-hard-muted-artifact.mjs +93 -0
- package/targets/macos/prune-generated-modules.mjs +147 -0
- package/targets/macos/sync-resources.mjs +153 -0
- package/targets/shared/analyze-audio-capture.mjs +131 -0
- package/targets/shared/apple_audio.mm +1194 -0
- package/targets/shared/apple_audio_selftest.mm +602 -0
- package/targets/shared/apple_audio_testing.h +34 -0
- package/targets/shared/build-audio-selftest.sh +50 -0
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
#include "imu.h"
|
|
2
|
+
#include "memory.h"
|
|
3
|
+
#include "power.h"
|
|
4
|
+
#include "touch.h"
|
|
5
|
+
|
|
6
|
+
#include <CoreFoundation/CoreFoundation.h>
|
|
7
|
+
#include <IOKit/ps/IOPSKeys.h>
|
|
8
|
+
#include <IOKit/ps/IOPowerSources.h>
|
|
9
|
+
|
|
10
|
+
#include <cstdint>
|
|
11
|
+
|
|
12
|
+
// macOS doesn't have an IMU; stubs let host/imu.cpp link. Tilt is fixed at 0.
|
|
13
|
+
namespace gea::platform::sensors {
|
|
14
|
+
|
|
15
|
+
void Accelerometer::init() {}
|
|
16
|
+
void Accelerometer::close() {}
|
|
17
|
+
void Accelerometer::calibrateBias() {}
|
|
18
|
+
int Accelerometer::tiltX() { return 0; }
|
|
19
|
+
int Accelerometer::tiltY() { return 0; }
|
|
20
|
+
double Accelerometer::accelerationX() { return 0.0; }
|
|
21
|
+
double Accelerometer::accelerationY() { return 0.0; }
|
|
22
|
+
double Accelerometer::accelerationZ() { return 9.80665; } // gravity, plate flat
|
|
23
|
+
double Accelerometer::gyroscopeX() { return 0.0; }
|
|
24
|
+
double Accelerometer::gyroscopeY() { return 0.0; }
|
|
25
|
+
double Accelerometer::gyroscopeZ() { return 0.0; }
|
|
26
|
+
void Accelerometer::setWebTilt(int, int) {}
|
|
27
|
+
|
|
28
|
+
} // namespace gea::platform::sensors
|
|
29
|
+
|
|
30
|
+
// Memory diagnostics — stub everything to zeros. macOS has plenty of RAM
|
|
31
|
+
// and the diagnostics views just show "0" for the embedded-only metrics.
|
|
32
|
+
namespace gea::platform::memory {
|
|
33
|
+
|
|
34
|
+
std::uint32_t Memory::internalFree() { return 0; }
|
|
35
|
+
std::uint32_t Memory::internalLargestFreeBlock() { return 0; }
|
|
36
|
+
std::uint32_t Memory::internalMinimumFree() { return 0; }
|
|
37
|
+
std::uint32_t Memory::psramFree() { return 0; }
|
|
38
|
+
std::uint32_t Memory::currentTaskStackHighWaterMark() { return 0; }
|
|
39
|
+
std::uint32_t Memory::geaMainStackBytes() { return 0; }
|
|
40
|
+
std::uint32_t Memory::geaInitStackBytes() { return 0; }
|
|
41
|
+
std::uint32_t Memory::appFrameStackWords() { return 0; }
|
|
42
|
+
std::uint32_t Memory::appFrameStackBytes() { return 0; }
|
|
43
|
+
std::uint32_t Memory::displayFlushConfiguredRows() { return 0; }
|
|
44
|
+
std::uint32_t Memory::displayFlushConfiguredDepth() { return 0; }
|
|
45
|
+
std::uint32_t Memory::displayFlushBufferMaxBytes() { return 0; }
|
|
46
|
+
std::uint32_t Memory::displayFlushRows() { return 0; }
|
|
47
|
+
std::uint32_t Memory::displayFlushDepth() { return 0; }
|
|
48
|
+
std::uint32_t Memory::displayFlushBufferBytes() { return 0; }
|
|
49
|
+
|
|
50
|
+
// No internal/external split here, so there is nothing to hold back for the
|
|
51
|
+
// display; the caller treats nullptr as "no reserve" and carries on.
|
|
52
|
+
void *Memory::reserveInternalDma(std::size_t) { return nullptr; }
|
|
53
|
+
void Memory::releaseInternalDma(void *) {}
|
|
54
|
+
} // namespace gea::platform::memory
|
|
55
|
+
|
|
56
|
+
// Touch is driven by AppKit mouse events (GeaCanvasView dispatches them as
|
|
57
|
+
// touch events, mirroring the iOS renderer's gea_ios_touch_set_state flow).
|
|
58
|
+
// The mouse handler keeps this cache current so the frame loop's Move
|
|
59
|
+
// dispatch — which re-reads coordinates via consumeLatestMove — and
|
|
60
|
+
// immediate-mode apps polling read()/readCached() see the real pointer.
|
|
61
|
+
// Coordinates are PANEL-NATIVE, matching the hardware touch controllers
|
|
62
|
+
// (TouchRuntime::transformTouchToLogical maps them back to logical space).
|
|
63
|
+
namespace gea::platform::touch {
|
|
64
|
+
|
|
65
|
+
namespace {
|
|
66
|
+
bool g_touching = false;
|
|
67
|
+
int g_touch_x = 0;
|
|
68
|
+
int g_touch_y = 0;
|
|
69
|
+
} // namespace
|
|
70
|
+
|
|
71
|
+
extern "C" void gea_macos_touch_set_state(int touching, int x, int y)
|
|
72
|
+
{
|
|
73
|
+
g_touching = touching != 0;
|
|
74
|
+
g_touch_x = x;
|
|
75
|
+
g_touch_y = y;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
void Touchscreen::setObserver(Observer) {}
|
|
79
|
+
bool Touchscreen::init() { return true; }
|
|
80
|
+
int Touchscreen::read(int *x, int *y)
|
|
81
|
+
{
|
|
82
|
+
if (x) *x = g_touch_x;
|
|
83
|
+
if (y) *y = g_touch_y;
|
|
84
|
+
return g_touching ? 1 : 0;
|
|
85
|
+
}
|
|
86
|
+
int Touchscreen::readCached(int *x, int *y)
|
|
87
|
+
{
|
|
88
|
+
if (x) *x = g_touch_x;
|
|
89
|
+
if (y) *y = g_touch_y;
|
|
90
|
+
return g_touching ? 1 : 0;
|
|
91
|
+
}
|
|
92
|
+
void Touchscreen::consumeLatestMove(int *x, int *y)
|
|
93
|
+
{
|
|
94
|
+
if (x) *x = g_touch_x;
|
|
95
|
+
if (y) *y = g_touch_y;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
} // namespace gea::platform::touch
|
|
99
|
+
|
|
100
|
+
// Battery. `gea::platform::power::Power` had no macOS implementation at all,
|
|
101
|
+
// so `host/battery.h`'s inline `batteryPercent()` left an undefined symbol and
|
|
102
|
+
// every app that shows a battery level failed to LINK (`watch`, `watch-date`:
|
|
103
|
+
// "gea::platform::power::Power::batteryPercent()", referenced from ...).
|
|
104
|
+
//
|
|
105
|
+
// A stub returning a constant would link and lie. macOS publishes the real
|
|
106
|
+
// figure through IOKit's power-sources API, and the target already links
|
|
107
|
+
// -framework IOKit (targets/macos/build-macos.sh LINK_FRAMEWORKS), so the
|
|
108
|
+
// honest implementation costs nothing the build was not already paying.
|
|
109
|
+
// A desktop with no battery reports 100: "not running on battery" is the
|
|
110
|
+
// truthful answer there, and it is what the embedded API's callers expect
|
|
111
|
+
// when the device is on external power.
|
|
112
|
+
namespace gea::platform::power {
|
|
113
|
+
|
|
114
|
+
bool Power::init() { return true; }
|
|
115
|
+
|
|
116
|
+
int Power::batteryPercent()
|
|
117
|
+
{
|
|
118
|
+
CFTypeRef blob = IOPSCopyPowerSourcesInfo();
|
|
119
|
+
if (blob == nullptr) return 100;
|
|
120
|
+
CFArrayRef sources = IOPSCopyPowerSourcesList(blob);
|
|
121
|
+
if (sources == nullptr) {
|
|
122
|
+
CFRelease(blob);
|
|
123
|
+
return 100;
|
|
124
|
+
}
|
|
125
|
+
int percent = 100;
|
|
126
|
+
const CFIndex count = CFArrayGetCount(sources);
|
|
127
|
+
for (CFIndex i = 0; i < count; ++i) {
|
|
128
|
+
CFDictionaryRef description = IOPSGetPowerSourceDescription(blob, CFArrayGetValueAtIndex(sources, i));
|
|
129
|
+
if (description == nullptr) continue;
|
|
130
|
+
CFNumberRef current = static_cast<CFNumberRef>(CFDictionaryGetValue(description, CFSTR(kIOPSCurrentCapacityKey)));
|
|
131
|
+
CFNumberRef maximum = static_cast<CFNumberRef>(CFDictionaryGetValue(description, CFSTR(kIOPSMaxCapacityKey)));
|
|
132
|
+
int currentValue = 0;
|
|
133
|
+
int maximumValue = 0;
|
|
134
|
+
if (current == nullptr || maximum == nullptr) continue;
|
|
135
|
+
if (!CFNumberGetValue(current, kCFNumberIntType, ¤tValue)) continue;
|
|
136
|
+
if (!CFNumberGetValue(maximum, kCFNumberIntType, &maximumValue)) continue;
|
|
137
|
+
if (maximumValue <= 0) continue;
|
|
138
|
+
percent = static_cast<int>((static_cast<double>(currentValue) / maximumValue) * 100.0 + 0.5);
|
|
139
|
+
break;
|
|
140
|
+
}
|
|
141
|
+
CFRelease(sources);
|
|
142
|
+
CFRelease(blob);
|
|
143
|
+
if (percent < 0) return 0;
|
|
144
|
+
if (percent > 100) return 100;
|
|
145
|
+
return percent;
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
} // namespace gea::platform::power
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
// Phase B smoke app: exercises View / Text / Button / event dispatch
|
|
2
|
+
// without the JSX → C++ pipeline. Real apps generated by geatsc will
|
|
3
|
+
// drop into targets/macos/generated/<app>/ and supersede this.
|
|
4
|
+
|
|
5
|
+
#include "ui/document.h"
|
|
6
|
+
#include "events.h"
|
|
7
|
+
#include <cstdio>
|
|
8
|
+
|
|
9
|
+
namespace {
|
|
10
|
+
gea::embedded::ui::TextElement g_counterLabel;
|
|
11
|
+
int g_counterValue = 0;
|
|
12
|
+
|
|
13
|
+
void refreshCounterLabel()
|
|
14
|
+
{
|
|
15
|
+
char buf[32];
|
|
16
|
+
std::snprintf(buf, sizeof(buf), "Clicked %d", g_counterValue);
|
|
17
|
+
g_counterLabel.setText(buf);
|
|
18
|
+
}
|
|
19
|
+
} // namespace
|
|
20
|
+
|
|
21
|
+
extern "C" {
|
|
22
|
+
|
|
23
|
+
void gea_embedded_app_init(int width, int height)
|
|
24
|
+
{
|
|
25
|
+
using namespace gea::embedded::ui;
|
|
26
|
+
using gea::framework::events::PointerEvent;
|
|
27
|
+
|
|
28
|
+
auto &doc = Document::instance();
|
|
29
|
+
doc.clear();
|
|
30
|
+
auto root = doc.createView();
|
|
31
|
+
root.style().backgroundColor(0x33DE); // RGB565 ~ #3478F6
|
|
32
|
+
root.style().set(Property::FlexDirection, 0); // column
|
|
33
|
+
root.style().set(Property::JustifyContent, 1); // center
|
|
34
|
+
root.style().set(Property::AlignItems, 1); // center
|
|
35
|
+
root.style().set(Property::Gap, 16);
|
|
36
|
+
|
|
37
|
+
auto title = doc.createText("Hello, native Mac!");
|
|
38
|
+
title.style().color(0xFFFF); // white
|
|
39
|
+
title.style().set(Property::FontSize, 24);
|
|
40
|
+
root.appendChild(title);
|
|
41
|
+
|
|
42
|
+
g_counterLabel = doc.createText("Clicked 0");
|
|
43
|
+
g_counterLabel.style().color(0xFFFF);
|
|
44
|
+
g_counterLabel.style().set(Property::FontSize, 16);
|
|
45
|
+
root.appendChild(g_counterLabel);
|
|
46
|
+
g_counterValue = 0;
|
|
47
|
+
|
|
48
|
+
auto button = doc.createButton();
|
|
49
|
+
auto buttonLabel = doc.createText("Click me");
|
|
50
|
+
button.appendChild(buttonLabel);
|
|
51
|
+
button.addEventListener("click", [](PointerEvent &) {
|
|
52
|
+
g_counterValue++;
|
|
53
|
+
refreshCounterLabel();
|
|
54
|
+
});
|
|
55
|
+
root.appendChild(button);
|
|
56
|
+
|
|
57
|
+
doc.mount(root, width, height);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
void gea_embedded_app_frame(int timestampMs)
|
|
61
|
+
{
|
|
62
|
+
(void)timestampMs;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
} // extern "C"
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
#import <Foundation/Foundation.h>
|
|
2
|
+
#include "services/frame_scheduler.h"
|
|
3
|
+
|
|
4
|
+
#include <mach/mach_time.h>
|
|
5
|
+
|
|
6
|
+
namespace {
|
|
7
|
+
|
|
8
|
+
int monotonicMillis()
|
|
9
|
+
{
|
|
10
|
+
static mach_timebase_info_data_t tb = {0, 0};
|
|
11
|
+
if (tb.denom == 0) mach_timebase_info(&tb);
|
|
12
|
+
static const uint64_t start = mach_absolute_time();
|
|
13
|
+
uint64_t ns = (mach_absolute_time() - start) * tb.numer / tb.denom;
|
|
14
|
+
return static_cast<int>(ns / 1000000ULL);
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
int g_frame_interval_ms = gea::framework::services::FrameScheduler::kDefaultFrameIntervalMs;
|
|
18
|
+
|
|
19
|
+
} // namespace
|
|
20
|
+
|
|
21
|
+
extern "C" int gea_embedded_now_ms(void)
|
|
22
|
+
{
|
|
23
|
+
return monotonicMillis();
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
namespace gea::framework::services {
|
|
27
|
+
|
|
28
|
+
EventQueue FrameScheduler::createEventQueue() { return EventQueue{}; }
|
|
29
|
+
EventQueue FrameScheduler::eventQueue() { return EventQueue{}; }
|
|
30
|
+
bool FrameScheduler::sendEvent(const gea::framework::events::Event &, int) { return true; }
|
|
31
|
+
bool FrameScheduler::receiveEvent(gea::framework::events::Event *) { return false; }
|
|
32
|
+
void FrameScheduler::start(EventQueue) {}
|
|
33
|
+
void FrameScheduler::runFrame(const FrameCallbacks &callbacks)
|
|
34
|
+
{
|
|
35
|
+
if (callbacks.frame) callbacks.frame(monotonicMillis(), callbacks.context);
|
|
36
|
+
}
|
|
37
|
+
void FrameScheduler::setFrameIntervalMs(int intervalMs)
|
|
38
|
+
{
|
|
39
|
+
if (intervalMs < kMinFrameIntervalMs) intervalMs = kMinFrameIntervalMs;
|
|
40
|
+
if (intervalMs > kMaxFrameIntervalMs) intervalMs = kMaxFrameIntervalMs;
|
|
41
|
+
g_frame_interval_ms = intervalMs;
|
|
42
|
+
}
|
|
43
|
+
int FrameScheduler::frameIntervalMs() { return g_frame_interval_ms; }
|
|
44
|
+
void FrameScheduler::setFrameRate(double fps)
|
|
45
|
+
{
|
|
46
|
+
if (fps <= 0.0) return;
|
|
47
|
+
setFrameIntervalMs(static_cast<int>(1000.0 / fps + 0.5));
|
|
48
|
+
}
|
|
49
|
+
double FrameScheduler::frameRate() { return 1000.0 / static_cast<double>(g_frame_interval_ms); }
|
|
50
|
+
int FrameScheduler::nowMs() { return monotonicMillis(); }
|
|
51
|
+
|
|
52
|
+
} // namespace gea::framework::services
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
#pragma once
|
|
2
|
+
|
|
3
|
+
#ifdef __OBJC__
|
|
4
|
+
#import <Foundation/Foundation.h>
|
|
5
|
+
|
|
6
|
+
@interface PressBridge : NSObject
|
|
7
|
+
@property(nonatomic, assign) int nodeId;
|
|
8
|
+
- (void)fire:(id)sender;
|
|
9
|
+
@end
|
|
10
|
+
#endif
|
|
11
|
+
|
|
12
|
+
#ifdef __cplusplus
|
|
13
|
+
extern "C" {
|
|
14
|
+
#endif
|
|
15
|
+
|
|
16
|
+
// Dispatches a Click + Press event to the given node id via Tree::dispatchEvent.
|
|
17
|
+
void gea_macos_fire_press_for_node(int nodeId);
|
|
18
|
+
|
|
19
|
+
#ifdef __cplusplus
|
|
20
|
+
}
|
|
21
|
+
#endif
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
#import "press_bridge.h"
|
|
2
|
+
|
|
3
|
+
#include "ui/tree_internal.h"
|
|
4
|
+
#include "events.h"
|
|
5
|
+
|
|
6
|
+
#include <cstring>
|
|
7
|
+
|
|
8
|
+
extern "C" void gea_macos_fire_press_for_node(int nodeId)
|
|
9
|
+
{
|
|
10
|
+
using gea::framework::events::PointerEvent;
|
|
11
|
+
using gea::framework::events::PointerEventType;
|
|
12
|
+
if (nodeId < 0) return;
|
|
13
|
+
auto &tree = gea::embedded::ui::Tree::instance();
|
|
14
|
+
if (nodeId >= tree.nodeCount()) return;
|
|
15
|
+
|
|
16
|
+
// A native click on macOS corresponds to a full press-and-release cycle.
|
|
17
|
+
// Apps may listen for any of these — the browser equivalent fires the
|
|
18
|
+
// whole sequence too — so dispatch them in browser order:
|
|
19
|
+
// touchstart → touchend → click
|
|
20
|
+
// This way apps written for the ESP32 touchscreen (which only sees
|
|
21
|
+
// touchstart/touchend) still respond to mouse clicks, and JSX onClick
|
|
22
|
+
// handlers fire consistently with the web target.
|
|
23
|
+
auto fire = [&](PointerEventType type) {
|
|
24
|
+
PointerEvent ev;
|
|
25
|
+
ev.type = type;
|
|
26
|
+
ev.targetId = nodeId;
|
|
27
|
+
tree.dispatchEvent(ev);
|
|
28
|
+
};
|
|
29
|
+
fire(PointerEventType::TouchStart);
|
|
30
|
+
fire(PointerEventType::TouchEnd);
|
|
31
|
+
fire(PointerEventType::Click);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
// Headless equivalent of a keystroke into the first `<input>` / `<textarea>`:
|
|
35
|
+
// write the text into the node's `value` attribute and dispatch
|
|
36
|
+
// `PointerEventType::Input`, which is exactly what `GeaTextAreaView
|
|
37
|
+
// textDidChange:` / `GeaTextFieldView controlTextDidChange:` do for a real key
|
|
38
|
+
// event (macos_renderer.mm). Driven by GEA_MACOS_SYNTH_INPUT so the text-input
|
|
39
|
+
// path can be exercised in a shell with no Accessibility permission — posting
|
|
40
|
+
// synthetic key events through CGEvent needs one, so without this there is no
|
|
41
|
+
// way to regression-test "typing does not crash" from a script.
|
|
42
|
+
// Returns the node it targeted, or -1 when the tree has no text input.
|
|
43
|
+
extern "C" int gea_macos_fire_input_for_node(int nodeId, const char *text)
|
|
44
|
+
{
|
|
45
|
+
using gea::framework::events::PointerEvent;
|
|
46
|
+
using gea::framework::events::PointerEventType;
|
|
47
|
+
auto &tree = gea::embedded::ui::Tree::instance();
|
|
48
|
+
int target = nodeId;
|
|
49
|
+
if (target < 0) {
|
|
50
|
+
for (int i = 0; i < tree.nodeCount(); i++) {
|
|
51
|
+
const char *tag = tree.tagName(i);
|
|
52
|
+
if (!tag) continue;
|
|
53
|
+
if (std::strcmp(tag, "input") == 0 || std::strcmp(tag, "textarea") == 0) { target = i; break; }
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
if (target < 0 || target >= tree.nodeCount()) return -1;
|
|
57
|
+
tree.setAttribute(target, "value", text ? text : "");
|
|
58
|
+
PointerEvent ev;
|
|
59
|
+
ev.type = PointerEventType::Input;
|
|
60
|
+
ev.targetId = target;
|
|
61
|
+
ev.keyCode = 0;
|
|
62
|
+
tree.dispatchEvent(ev);
|
|
63
|
+
return target;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
@implementation PressBridge
|
|
67
|
+
- (void)fire:(id)sender
|
|
68
|
+
{
|
|
69
|
+
(void)sender;
|
|
70
|
+
gea_macos_fire_press_for_node(self.nodeId);
|
|
71
|
+
}
|
|
72
|
+
@end
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
#pragma once
|
|
2
|
+
|
|
3
|
+
#include <cstdint>
|
|
4
|
+
|
|
5
|
+
namespace gea::macos::thermalright {
|
|
6
|
+
|
|
7
|
+
bool enabled();
|
|
8
|
+
int width();
|
|
9
|
+
int height();
|
|
10
|
+
int fps();
|
|
11
|
+
bool submitRgb565(const std::uint16_t *pixels, int width, int height);
|
|
12
|
+
void shutdown();
|
|
13
|
+
|
|
14
|
+
} // namespace gea::macos::thermalright
|