@owear/linux-x64-gnu 0.1.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.
- package/LICENSE +201 -0
- package/README.md +37 -0
- package/bin/modules/capturer.so +0 -0
- package/bin/modules/clipboard.so +0 -0
- package/bin/modules/dialog.so +0 -0
- package/bin/modules/fs.so +0 -0
- package/bin/modules/globalshortcut.so +0 -0
- package/bin/modules/menu.so +0 -0
- package/bin/modules/net.so +0 -0
- package/bin/modules/notification.so +0 -0
- package/bin/modules/path.so +0 -0
- package/bin/modules/power.so +0 -0
- package/bin/modules/process.so +0 -0
- package/bin/modules/screen.so +0 -0
- package/bin/modules/shell.so +0 -0
- package/bin/modules/updater.so +0 -0
- package/bin/owear +0 -0
- package/include/ow/App.h +40 -0
- package/include/ow/Base64.h +89 -0
- package/include/ow/Bridge/Codec.h +50 -0
- package/include/ow/Common.h +70 -0
- package/include/ow/Json.h +15 -0
- package/include/ow/Module.h +78 -0
- package/include/ow/Shm.h +46 -0
- package/include/ow/Window.h +112 -0
- package/include/ow/detail/minjson.hpp +419 -0
- package/include/ow_api.h +103 -0
- package/package.json +24 -0
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
// Copyright 2026 Owear Contributors
|
|
2
|
+
// SPDX-License-Identifier: Apache-2.0
|
|
3
|
+
#pragma once
|
|
4
|
+
//
|
|
5
|
+
// ow/Window.h — ventanas multiplataforma con titlebar custom/overlay.
|
|
6
|
+
// Contrato compartido: window_win.cpp / window_mac.mm / window_linux.cpp
|
|
7
|
+
//
|
|
8
|
+
|
|
9
|
+
#include "ow/Common.h"
|
|
10
|
+
|
|
11
|
+
namespace ow {
|
|
12
|
+
|
|
13
|
+
enum class TitleBarStyle {
|
|
14
|
+
Default, // decoraciones del sistema tal cual
|
|
15
|
+
Hidden, // sin titlebar; la ventana completa es contenido (frameless)
|
|
16
|
+
Custom // titlebar custom + botones nativos overlay (donde el SO lo permite)
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
struct TitleBarOverlay {
|
|
20
|
+
bool enabled = false;
|
|
21
|
+
std::string color = "#00000000"; // fondo bajo los botones nativos
|
|
22
|
+
std::string symbolColor = "#ffffff"; // color de glifos (min/max/close)
|
|
23
|
+
int height = 36; // alto en px lógicos
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
struct WindowOptions {
|
|
27
|
+
std::string title = "Owear";
|
|
28
|
+
int width = 1024;
|
|
29
|
+
int height = 768;
|
|
30
|
+
int minWidth = 0, minHeight = 0;
|
|
31
|
+
int maxWidth = 0, maxHeight = 0;
|
|
32
|
+
bool resizable = true;
|
|
33
|
+
bool center = true;
|
|
34
|
+
bool show = true;
|
|
35
|
+
bool frameless = false;
|
|
36
|
+
|
|
37
|
+
TitleBarStyle titleBarStyle = TitleBarStyle::Default;
|
|
38
|
+
TitleBarOverlay titleBarOverlay;
|
|
39
|
+
|
|
40
|
+
/// URL inicial: http(s):// (dev server), app://<bundle>/... o file://
|
|
41
|
+
std::string url = "app://index.html";
|
|
42
|
+
|
|
43
|
+
/// Argumentos extra para el proceso del WebView (debug flags, etc).
|
|
44
|
+
std::vector<std::string> webviewArgs;
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
/// Payload de evento como JSON serializado ("null" si el evento no lleva datos).
|
|
48
|
+
using EventPayload = std::string_view;
|
|
49
|
+
using ListenerId = uint64_t;
|
|
50
|
+
|
|
51
|
+
class Window : NonCopyable {
|
|
52
|
+
public:
|
|
53
|
+
explicit Window(const WindowOptions& options);
|
|
54
|
+
~Window();
|
|
55
|
+
|
|
56
|
+
WindowId Id() const;
|
|
57
|
+
|
|
58
|
+
// ── ciclo de vida ──────────────────────────────────────────────
|
|
59
|
+
void Show();
|
|
60
|
+
void Hide();
|
|
61
|
+
void Close(); // dispara closeRequested; puede ser vetado por listeners
|
|
62
|
+
void Destroy(); // cierre inmediato sin veto
|
|
63
|
+
void Focus();
|
|
64
|
+
|
|
65
|
+
// ── estado ─────────────────────────────────────────────────────
|
|
66
|
+
void Minimize();
|
|
67
|
+
void Maximize();
|
|
68
|
+
void Unmaximize();
|
|
69
|
+
void Restore();
|
|
70
|
+
void SetFullScreen(bool enabled);
|
|
71
|
+
bool IsMaximized() const;
|
|
72
|
+
bool IsMinimized() const;
|
|
73
|
+
bool IsFullScreen() const;
|
|
74
|
+
|
|
75
|
+
// ── geometría ──────────────────────────────────────────────────
|
|
76
|
+
struct Bounds { int x = 0, y = 0, w = 0, h = 0; };
|
|
77
|
+
Bounds GetBounds() const;
|
|
78
|
+
void SetBounds(const Bounds&);
|
|
79
|
+
void Center();
|
|
80
|
+
|
|
81
|
+
// ── apariencia / titlebar ───────────────────────────────────────
|
|
82
|
+
void SetTitle(const std::string& title);
|
|
83
|
+
std::string Title() const;
|
|
84
|
+
void SetTitleBarStyle(TitleBarStyle style);
|
|
85
|
+
void SetTitleBarOverlay(const TitleBarOverlay& overlay);
|
|
86
|
+
|
|
87
|
+
// ── webview ────────────────────────────────────────────────────
|
|
88
|
+
void LoadURL(const std::string& url);
|
|
89
|
+
/// Evalúa JS en la página. callback recibe el resultado JSON o null.
|
|
90
|
+
void EvalJS(const std::string& js,
|
|
91
|
+
std::function<void(std::string_view resultJson)> callback = nullptr);
|
|
92
|
+
|
|
93
|
+
// ── eventos hacia JS (bridge) y C++ ─────────────────────────────
|
|
94
|
+
/// Emite un evento a todos los suscriptores JS de esta ventana.
|
|
95
|
+
void EmitToJS(const std::string& name, std::string_view jsonPayload);
|
|
96
|
+
/// Suscriptor nativo. Los eventos nativos se reenvían a JS automáticamente.
|
|
97
|
+
ListenerId On(const std::string& name, std::function<void(EventPayload)> cb);
|
|
98
|
+
void Off(ListenerId id);
|
|
99
|
+
|
|
100
|
+
/// Referencia cruda al backend de plataforma (solo para src/ interno).
|
|
101
|
+
void* NativeHandle() const;
|
|
102
|
+
|
|
103
|
+
/// Detrás de este puntero vive el estado de plataforma.
|
|
104
|
+
class Impl; // definido en src/Window/Window_p.hpp
|
|
105
|
+
std::unique_ptr<Impl> impl_;
|
|
106
|
+
|
|
107
|
+
public:
|
|
108
|
+
/// Interno (ControlServer): respuesta del SDK al closeRequested.
|
|
109
|
+
class Impl* impl() { return impl_.get(); }
|
|
110
|
+
};
|
|
111
|
+
|
|
112
|
+
} // namespace ow
|
|
@@ -0,0 +1,419 @@
|
|
|
1
|
+
// Copyright 2026 Owear Contributors
|
|
2
|
+
// SPDX-License-Identifier: Apache-2.0
|
|
3
|
+
//
|
|
4
|
+
// ow/detail/minjson.hpp — JSON minimalista, header-only, sin excepciones.
|
|
5
|
+
// Usado por el kernel (bridge/control) y disponible para módulos vía ow/Json.h.
|
|
6
|
+
//
|
|
7
|
+
// Diseño: Value inmutable tras parse; objetos preservan orden de inserción.
|
|
8
|
+
//
|
|
9
|
+
|
|
10
|
+
#pragma once
|
|
11
|
+
|
|
12
|
+
#include <cstdint>
|
|
13
|
+
#include <cmath>
|
|
14
|
+
#include <string>
|
|
15
|
+
#include <string_view>
|
|
16
|
+
#include <vector>
|
|
17
|
+
#include <utility>
|
|
18
|
+
#include <optional>
|
|
19
|
+
|
|
20
|
+
namespace ow::json {
|
|
21
|
+
|
|
22
|
+
class Value;
|
|
23
|
+
using Array = std::vector<Value>;
|
|
24
|
+
using Member = std::pair<std::string, Value>;
|
|
25
|
+
using Object = std::vector<Member>;
|
|
26
|
+
|
|
27
|
+
enum class Type : uint8_t { Null, Bool, Int, Double, String, ArrayT, ObjectT };
|
|
28
|
+
|
|
29
|
+
class Value {
|
|
30
|
+
public:
|
|
31
|
+
Value() : type_(Type::Null) {}
|
|
32
|
+
Value(std::nullptr_t) : type_(Type::Null) {}
|
|
33
|
+
Value(bool b) : type_(Type::Bool), bool_(b) {}
|
|
34
|
+
Value(int v) : type_(Type::Int), int_(v) {}
|
|
35
|
+
Value(int64_t v) : type_(Type::Int), int_(v) {}
|
|
36
|
+
Value(uint32_t v) : type_(Type::Int), int_(v) {}
|
|
37
|
+
Value(double v) : type_(IsIntegral(v) ? Type::Int : Type::Double),
|
|
38
|
+
int_(static_cast<int64_t>(v)) {}
|
|
39
|
+
Value(const char* s) : type_(Type::String), str_(s) {}
|
|
40
|
+
Value(std::string s) : type_(Type::String), str_(std::move(s)) {}
|
|
41
|
+
Value(Array a) : type_(Type::ArrayT), arr_(std::move(a)) {}
|
|
42
|
+
Value(Object o) : type_(Type::ObjectT), obj_(std::move(o)) {}
|
|
43
|
+
|
|
44
|
+
Type GetType() const { return type_; }
|
|
45
|
+
bool IsNull() const { return type_ == Type::Null; }
|
|
46
|
+
bool IsBool() const { return type_ == Type::Bool; }
|
|
47
|
+
bool IsNumber() const { return type_ == Type::Int || type_ == Type::Double; }
|
|
48
|
+
bool IsString() const { return type_ == Type::String; }
|
|
49
|
+
bool IsArray() const { return type_ == Type::ArrayT; }
|
|
50
|
+
bool IsObject() const { return type_ == Type::ObjectT; }
|
|
51
|
+
|
|
52
|
+
bool AsBool(bool def = false) const { return IsBool() ? bool_ : def; }
|
|
53
|
+
int64_t AsInt(int64_t def = 0) const {
|
|
54
|
+
if (type_ == Type::Int) return int_;
|
|
55
|
+
if (type_ == Type::Double) return static_cast<int64_t>(dbl_);
|
|
56
|
+
return def;
|
|
57
|
+
}
|
|
58
|
+
double AsDouble(double def = 0) const {
|
|
59
|
+
if (type_ == Type::Double) return dbl_;
|
|
60
|
+
if (type_ == Type::Int) return static_cast<double>(int_);
|
|
61
|
+
return def;
|
|
62
|
+
}
|
|
63
|
+
const std::string& AsString() const { return str_; }
|
|
64
|
+
|
|
65
|
+
const Array& AsArray() const { return arr_; }
|
|
66
|
+
const Object& AsObject() const { return obj_; }
|
|
67
|
+
|
|
68
|
+
/// Acceso a miembro de objeto. nullptr si no existe o no es objeto.
|
|
69
|
+
const Value* Find(std::string_view key) const {
|
|
70
|
+
if (type_ != Type::ObjectT) return nullptr;
|
|
71
|
+
for (const auto& m : obj_)
|
|
72
|
+
if (m.first == key) return &m.second;
|
|
73
|
+
return nullptr;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
std::string Serialize() const {
|
|
77
|
+
std::string out;
|
|
78
|
+
out.reserve(64);
|
|
79
|
+
Write(out);
|
|
80
|
+
return out;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
private:
|
|
84
|
+
static bool IsIntegral(double v) {
|
|
85
|
+
return std::isfinite(v) && v >= -9.007199254740992e15 && v <= 9.007199254740992e15
|
|
86
|
+
&& v == static_cast<double>(static_cast<int64_t>(v));
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
void Write(std::string& out) const {
|
|
90
|
+
switch (type_) {
|
|
91
|
+
case Type::Null: out += "null"; break;
|
|
92
|
+
case Type::Bool: out += bool_ ? "true" : "false"; break;
|
|
93
|
+
case Type::Int: {
|
|
94
|
+
char tmp[24];
|
|
95
|
+
int n = std::snprintf(tmp, sizeof(tmp), "%lld",
|
|
96
|
+
static_cast<long long>(int_));
|
|
97
|
+
out.append(tmp, static_cast<size_t>(n));
|
|
98
|
+
break;
|
|
99
|
+
}
|
|
100
|
+
case Type::Double: {
|
|
101
|
+
char tmp[36];
|
|
102
|
+
int n = std::snprintf(tmp, sizeof(tmp), "%.17g", dbl_);
|
|
103
|
+
// recorta precisión redundante con %g corto cuando es exacto
|
|
104
|
+
char tmp2[36];
|
|
105
|
+
int n2 = std::snprintf(tmp2, sizeof(tmp2), "%.15g", dbl_);
|
|
106
|
+
if (n > 0 && n2 > 0 && std::strtod(tmp2, nullptr) == dbl_)
|
|
107
|
+
out.append(tmp2, static_cast<size_t>(n2));
|
|
108
|
+
else
|
|
109
|
+
out.append(tmp, static_cast<size_t>(n));
|
|
110
|
+
break;
|
|
111
|
+
}
|
|
112
|
+
case Type::String:
|
|
113
|
+
WriteEscaped(out, str_);
|
|
114
|
+
break;
|
|
115
|
+
case Type::ArrayT: {
|
|
116
|
+
out += '[';
|
|
117
|
+
bool first = true;
|
|
118
|
+
for (const auto& v : arr_) {
|
|
119
|
+
if (!first) out += ',';
|
|
120
|
+
first = false;
|
|
121
|
+
v.Write(out);
|
|
122
|
+
}
|
|
123
|
+
out += ']';
|
|
124
|
+
break;
|
|
125
|
+
}
|
|
126
|
+
case Type::ObjectT: {
|
|
127
|
+
out += '{';
|
|
128
|
+
bool first = true;
|
|
129
|
+
for (const auto& m : obj_) {
|
|
130
|
+
if (!first) out += ',';
|
|
131
|
+
first = false;
|
|
132
|
+
WriteEscaped(out, m.first);
|
|
133
|
+
out += ':';
|
|
134
|
+
m.second.Write(out);
|
|
135
|
+
}
|
|
136
|
+
out += '}';
|
|
137
|
+
break;
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
static void WriteEscaped(std::string& out, std::string_view s) {
|
|
143
|
+
out += '"';
|
|
144
|
+
for (unsigned char c : s) {
|
|
145
|
+
switch (c) {
|
|
146
|
+
case '"': out += "\\\""; break;
|
|
147
|
+
case '\\': out += "\\\\"; break;
|
|
148
|
+
case '\b': out += "\\b"; break;
|
|
149
|
+
case '\f': out += "\\f"; break;
|
|
150
|
+
case '\n': out += "\\n"; break;
|
|
151
|
+
case '\r': out += "\\r"; break;
|
|
152
|
+
case '\t': out += "\\t"; break;
|
|
153
|
+
default:
|
|
154
|
+
if (c < 0x20) {
|
|
155
|
+
char tmp[8];
|
|
156
|
+
std::snprintf(tmp, sizeof(tmp), "\\u%04x", c);
|
|
157
|
+
out += tmp;
|
|
158
|
+
} else {
|
|
159
|
+
out += static_cast<char>(c);
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
out += '"';
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
Type type_;
|
|
167
|
+
bool bool_ = false;
|
|
168
|
+
int64_t int_ = 0;
|
|
169
|
+
double dbl_ = 0;
|
|
170
|
+
std::string str_;
|
|
171
|
+
Array arr_;
|
|
172
|
+
Object obj_;
|
|
173
|
+
};
|
|
174
|
+
|
|
175
|
+
/// Parser recursivo descendente. Devuelve nullopt y `error` en fallo.
|
|
176
|
+
struct ParseResult {
|
|
177
|
+
std::optional<Value> value;
|
|
178
|
+
std::string error;
|
|
179
|
+
};
|
|
180
|
+
|
|
181
|
+
inline ParseResult Parse(std::string_view text) {
|
|
182
|
+
size_t pos = 0;
|
|
183
|
+
ParseResult r;
|
|
184
|
+
|
|
185
|
+
// Límite de anidamiento (objetos/arrays): evita stack overflow con JSON
|
|
186
|
+
// adversarial profundamente anidado en entradas externas (bridge/control).
|
|
187
|
+
static constexpr int kMaxDepth = 128;
|
|
188
|
+
|
|
189
|
+
struct Parser {
|
|
190
|
+
std::string_view t;
|
|
191
|
+
size_t& pos;
|
|
192
|
+
std::string err;
|
|
193
|
+
int depth = 0;
|
|
194
|
+
|
|
195
|
+
void SkipWs() {
|
|
196
|
+
while (pos < t.size()) {
|
|
197
|
+
char c = t[pos];
|
|
198
|
+
if (c == ' ' || c == '\t' || c == '\n' || c == '\r') ++pos;
|
|
199
|
+
else break;
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
bool Fail(std::string msg) { if (err.empty()) err = std::move(msg); return false; }
|
|
203
|
+
|
|
204
|
+
bool ParseValue(Value& out) {
|
|
205
|
+
SkipWs();
|
|
206
|
+
if (pos >= t.size()) return Fail("fin inesperado");
|
|
207
|
+
char c = t[pos];
|
|
208
|
+
switch (c) {
|
|
209
|
+
case '{': return ParseObject(out);
|
|
210
|
+
case '[': return ParseArray(out);
|
|
211
|
+
case '"': {
|
|
212
|
+
std::string s;
|
|
213
|
+
if (!ParseString(s)) return false;
|
|
214
|
+
out = Value(std::move(s));
|
|
215
|
+
return true;
|
|
216
|
+
}
|
|
217
|
+
case 't':
|
|
218
|
+
return Literal("true") ? (out = Value(true), true) : false;
|
|
219
|
+
case 'f':
|
|
220
|
+
return Literal("false") ? (out = Value(false), true) : false;
|
|
221
|
+
case 'n':
|
|
222
|
+
return Literal("null") ? (out = Value(nullptr), true) : false;
|
|
223
|
+
default:
|
|
224
|
+
return ParseNumber(out);
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
bool Literal(std::string_view lit) {
|
|
229
|
+
if (t.compare(pos, lit.size(), lit) != 0)
|
|
230
|
+
return Fail("literal inválido");
|
|
231
|
+
pos += lit.size();
|
|
232
|
+
return true;
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
bool ParseNumber(Value& out) {
|
|
236
|
+
size_t start = pos;
|
|
237
|
+
bool isDouble = false;
|
|
238
|
+
if (pos < t.size() && (t[pos] == '-' || t[pos] == '+')) ++pos;
|
|
239
|
+
while (pos < t.size()) {
|
|
240
|
+
char c = t[pos];
|
|
241
|
+
if (c >= '0' && c <= '9') { ++pos; continue; }
|
|
242
|
+
if (c == '.' || c == 'e' || c == 'E' || c == '+' || c == '-') {
|
|
243
|
+
if (c == '.' || c == 'e' || c == 'E') isDouble = true;
|
|
244
|
+
++pos; continue;
|
|
245
|
+
}
|
|
246
|
+
break;
|
|
247
|
+
}
|
|
248
|
+
if (pos == start) return Fail("número inválido");
|
|
249
|
+
std::string num(t.substr(start, pos - start));
|
|
250
|
+
if (isDouble) {
|
|
251
|
+
out = Value(std::strtod(num.c_str(), nullptr));
|
|
252
|
+
} else {
|
|
253
|
+
errno = 0;
|
|
254
|
+
long long v = std::strtoll(num.c_str(), nullptr, 10);
|
|
255
|
+
out = Value(static_cast<int64_t>(v));
|
|
256
|
+
}
|
|
257
|
+
return true;
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
bool ParseHex4(unsigned& out) {
|
|
261
|
+
if (pos + 4 > t.size()) return Fail("\\u truncado");
|
|
262
|
+
unsigned v = 0;
|
|
263
|
+
for (int i = 0; i < 4; ++i) {
|
|
264
|
+
char c = t[pos++];
|
|
265
|
+
v <<= 4;
|
|
266
|
+
if (c >= '0' && c <= '9') v |= static_cast<unsigned>(c - '0');
|
|
267
|
+
else if (c >= 'a' && c <= 'f') v |= static_cast<unsigned>(c - 'a' + 10);
|
|
268
|
+
else if (c >= 'A' && c <= 'F') v |= static_cast<unsigned>(c - 'A' + 10);
|
|
269
|
+
else return Fail("\\u inválido");
|
|
270
|
+
}
|
|
271
|
+
out = v;
|
|
272
|
+
return true;
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
void AppendUtf8(std::string& s, unsigned cp) {
|
|
276
|
+
if (cp < 0x80) {
|
|
277
|
+
s += static_cast<char>(cp);
|
|
278
|
+
} else if (cp < 0x800) {
|
|
279
|
+
s += static_cast<char>(0xC0 | (cp >> 6));
|
|
280
|
+
s += static_cast<char>(0x80 | (cp & 0x3F));
|
|
281
|
+
} else if (cp < 0x10000) {
|
|
282
|
+
s += static_cast<char>(0xE0 | (cp >> 12));
|
|
283
|
+
s += static_cast<char>(0x80 | ((cp >> 6) & 0x3F));
|
|
284
|
+
s += static_cast<char>(0x80 | (cp & 0x3F));
|
|
285
|
+
} else {
|
|
286
|
+
s += static_cast<char>(0xF0 | (cp >> 18));
|
|
287
|
+
s += static_cast<char>(0x80 | ((cp >> 12) & 0x3F));
|
|
288
|
+
s += static_cast<char>(0x80 | ((cp >> 6) & 0x3F));
|
|
289
|
+
s += static_cast<char>(0x80 | (cp & 0x3F));
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
bool ParseString(std::string& out) {
|
|
294
|
+
++pos; // comilla inicial ya validada por caller
|
|
295
|
+
out.clear();
|
|
296
|
+
while (pos < t.size()) {
|
|
297
|
+
char c = t[pos++];
|
|
298
|
+
if (c == '"') return true;
|
|
299
|
+
if (c != '\\') { out += c; continue; }
|
|
300
|
+
if (pos >= t.size()) return Fail("escape truncado");
|
|
301
|
+
char e = t[pos++];
|
|
302
|
+
switch (e) {
|
|
303
|
+
case '"': out += '"'; break;
|
|
304
|
+
case '\\': out += '\\'; break;
|
|
305
|
+
case '/': out += '/'; break;
|
|
306
|
+
case 'b': out += '\b'; break;
|
|
307
|
+
case 'f': out += '\f'; break;
|
|
308
|
+
case 'n': out += '\n'; break;
|
|
309
|
+
case 'r': out += '\r'; break;
|
|
310
|
+
case 't': out += '\t'; break;
|
|
311
|
+
case 'u': {
|
|
312
|
+
unsigned u;
|
|
313
|
+
if (!ParseHex4(u)) return false;
|
|
314
|
+
if (u >= 0xD800 && u <= 0xDBFF &&
|
|
315
|
+
pos + 1 < t.size() && t[pos] == '\\' && t[pos + 1] == 'u') {
|
|
316
|
+
pos += 2;
|
|
317
|
+
unsigned lo;
|
|
318
|
+
if (!ParseHex4(lo)) return false;
|
|
319
|
+
if (lo >= 0xDC00 && lo <= 0xDFFF)
|
|
320
|
+
u = 0x10000 + ((u - 0xD800) << 10) + (lo - 0xDC00);
|
|
321
|
+
else
|
|
322
|
+
AppendUtf8(out, u), u = lo; // par inválido: emite ambos
|
|
323
|
+
}
|
|
324
|
+
AppendUtf8(out, u);
|
|
325
|
+
break;
|
|
326
|
+
}
|
|
327
|
+
default: return Fail("escape desconocido");
|
|
328
|
+
}
|
|
329
|
+
}
|
|
330
|
+
return Fail("string sin cerrar");
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
bool ParseArray(Value& out) {
|
|
334
|
+
if (++depth > kMaxDepth) return Fail("JSON demasiado anidado");
|
|
335
|
+
++pos;
|
|
336
|
+
Array arr;
|
|
337
|
+
SkipWs();
|
|
338
|
+
if (pos < t.size() && t[pos] == ']') { ++pos; --depth; out = Value(std::move(arr)); return true; }
|
|
339
|
+
while (true) {
|
|
340
|
+
Value item;
|
|
341
|
+
if (!ParseValue(item)) return false;
|
|
342
|
+
arr.push_back(std::move(item));
|
|
343
|
+
SkipWs();
|
|
344
|
+
if (pos >= t.size()) return Fail("array sin cerrar");
|
|
345
|
+
if (t[pos] == ',') { ++pos; continue; }
|
|
346
|
+
if (t[pos] == ']') { ++pos; break; }
|
|
347
|
+
return Fail("',' o ']' esperado");
|
|
348
|
+
}
|
|
349
|
+
--depth;
|
|
350
|
+
out = Value(std::move(arr));
|
|
351
|
+
return true;
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
bool ParseObject(Value& out) {
|
|
355
|
+
if (++depth > kMaxDepth) return Fail("JSON demasiado anidado");
|
|
356
|
+
++pos;
|
|
357
|
+
Object obj;
|
|
358
|
+
SkipWs();
|
|
359
|
+
if (pos < t.size() && t[pos] == '}') { ++pos; --depth; out = Value(std::move(obj)); return true; }
|
|
360
|
+
while (true) {
|
|
361
|
+
SkipWs();
|
|
362
|
+
if (pos >= t.size() || t[pos] != '"')
|
|
363
|
+
return Fail("clave string esperada");
|
|
364
|
+
std::string key;
|
|
365
|
+
if (!ParseString(key)) return false;
|
|
366
|
+
SkipWs();
|
|
367
|
+
if (pos >= t.size() || t[pos] != ':')
|
|
368
|
+
return Fail("':' esperado");
|
|
369
|
+
++pos;
|
|
370
|
+
Value val;
|
|
371
|
+
if (!ParseValue(val)) return false;
|
|
372
|
+
obj.emplace_back(std::move(key), std::move(val));
|
|
373
|
+
SkipWs();
|
|
374
|
+
if (pos >= t.size()) return Fail("objeto sin cerrar");
|
|
375
|
+
if (t[pos] == ',') { ++pos; continue; }
|
|
376
|
+
if (t[pos] == '}') { ++pos; break; }
|
|
377
|
+
return Fail("',' o '}' esperado");
|
|
378
|
+
}
|
|
379
|
+
--depth;
|
|
380
|
+
out = Value(std::move(obj));
|
|
381
|
+
return true;
|
|
382
|
+
}
|
|
383
|
+
};
|
|
384
|
+
|
|
385
|
+
Parser p{text, pos};
|
|
386
|
+
Value root;
|
|
387
|
+
if (!p.ParseValue(root)) {
|
|
388
|
+
r.error = p.err.empty() ? "JSON inválido" : p.err;
|
|
389
|
+
return r;
|
|
390
|
+
}
|
|
391
|
+
r.value = std::move(root);
|
|
392
|
+
return r;
|
|
393
|
+
}
|
|
394
|
+
|
|
395
|
+
/// Escape de un string para incrustarlo como literal JS entre comillas simples.
|
|
396
|
+
/// Se usa para inyectar payloads vía evaluate_javascript/postMessage roundtrips.
|
|
397
|
+
inline std::string JsLiteral(std::string_view s) {
|
|
398
|
+
std::string out = "'";
|
|
399
|
+
for (char c : s) {
|
|
400
|
+
switch (c) {
|
|
401
|
+
case '\'': out += "\\'"; break;
|
|
402
|
+
case '\\': out += "\\\\"; break;
|
|
403
|
+
case '\n': out += "\\n"; break;
|
|
404
|
+
case '\r': out += "\\r"; break;
|
|
405
|
+
default:
|
|
406
|
+
if (static_cast<unsigned char>(c) < 0x20) {
|
|
407
|
+
char tmp[8];
|
|
408
|
+
std::snprintf(tmp, sizeof(tmp), "\\u%04x", c);
|
|
409
|
+
out += tmp;
|
|
410
|
+
} else {
|
|
411
|
+
out += c;
|
|
412
|
+
}
|
|
413
|
+
}
|
|
414
|
+
}
|
|
415
|
+
out += '\'';
|
|
416
|
+
return out;
|
|
417
|
+
}
|
|
418
|
+
|
|
419
|
+
} // namespace ow::json
|
package/include/ow_api.h
ADDED
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
// Copyright 2026 Owear Contributors
|
|
2
|
+
// SPDX-License-Identifier: Apache-2.0
|
|
3
|
+
//
|
|
4
|
+
// api/ow_api.h — ABI-C estable del sistema de módulos Owear (.owm).
|
|
5
|
+
//
|
|
6
|
+
// Cada API vive en su propio folder (api/fs, api/process, …) y compila a una
|
|
7
|
+
// shared library independiente (.so/.dll/.dylib) que el kernel carga con
|
|
8
|
+
// dlopen. Actualización modular por API.
|
|
9
|
+
//
|
|
10
|
+
// Un módulo exporta EXACTAMENTE un símbolo obligatorio:
|
|
11
|
+
// const ow_module_desc_t* ow_module_descriptor(void);
|
|
12
|
+
// y opcionalmente recibe el host con:
|
|
13
|
+
// void ow_module_set_host(const ow_module_host_t* host);
|
|
14
|
+
//
|
|
15
|
+
// Contrato de memoria:
|
|
16
|
+
// - Los buffers apuntados por res viven SOLO durante la llamada; el host
|
|
17
|
+
// copia inmediatamente después de retornar.
|
|
18
|
+
// - Prohibido lanzar excepciones hacia el host.
|
|
19
|
+
//
|
|
20
|
+
#pragma once
|
|
21
|
+
|
|
22
|
+
#include <stdint.h>
|
|
23
|
+
#include <stddef.h>
|
|
24
|
+
|
|
25
|
+
#if defined(_WIN32)
|
|
26
|
+
#if defined(OW_MODULE_BUILD)
|
|
27
|
+
#define OW_MODULE_EXPORT __declspec(dllexport)
|
|
28
|
+
#else
|
|
29
|
+
#define OW_MODULE_EXPORT
|
|
30
|
+
#endif
|
|
31
|
+
#else
|
|
32
|
+
#if defined(OW_MODULE_BUILD)
|
|
33
|
+
#define OW_MODULE_EXPORT __attribute__((visibility("default")))
|
|
34
|
+
#else
|
|
35
|
+
#define OW_MODULE_EXPORT
|
|
36
|
+
#endif
|
|
37
|
+
#endif
|
|
38
|
+
|
|
39
|
+
#ifdef __cplusplus
|
|
40
|
+
extern "C" {
|
|
41
|
+
#endif
|
|
42
|
+
|
|
43
|
+
typedef struct ow_request {
|
|
44
|
+
const char* json; ///< argumentos como array JSON. Nunca null ("[]").
|
|
45
|
+
uint32_t json_len;
|
|
46
|
+
const uint8_t* bin; ///< binario adjunto (puede ser null)
|
|
47
|
+
uint32_t bin_len;
|
|
48
|
+
uint32_t window_id; ///< ventana invocante (0 = sin ventana)
|
|
49
|
+
uint32_t reserved;
|
|
50
|
+
void* host; ///< contexto del kernel (opaco para el módulo)
|
|
51
|
+
} ow_request_t;
|
|
52
|
+
|
|
53
|
+
typedef struct ow_response {
|
|
54
|
+
int32_t status; ///< 0 = ok; != 0 = error
|
|
55
|
+
const char* error; ///< mensaje de error (si status != 0)
|
|
56
|
+
const char* json; ///< resultado JSON ("null" permitido)
|
|
57
|
+
uint32_t json_len;
|
|
58
|
+
const uint8_t* bin; ///< binario de salida (opcional)
|
|
59
|
+
uint32_t bin_len;
|
|
60
|
+
} ow_response_t;
|
|
61
|
+
|
|
62
|
+
typedef void (*ow_fn_t)(const ow_request_t* req, ow_response_t* res);
|
|
63
|
+
|
|
64
|
+
typedef struct ow_fn_entry {
|
|
65
|
+
const char* name;
|
|
66
|
+
ow_fn_t fn;
|
|
67
|
+
} ow_fn_entry_t;
|
|
68
|
+
|
|
69
|
+
typedef struct ow_module_desc {
|
|
70
|
+
const char* name; ///< "fs", "process", "dialog", …
|
|
71
|
+
const char* version;
|
|
72
|
+
const ow_fn_entry_t* fns;
|
|
73
|
+
uint32_t fn_count;
|
|
74
|
+
} ow_module_desc_t;
|
|
75
|
+
|
|
76
|
+
/// Único símbolo que un .owm debe exportar.
|
|
77
|
+
typedef const ow_module_desc_t* (*ow_module_entry_t)(void);
|
|
78
|
+
|
|
79
|
+
// ── Host callbacks (F-infra) ────────────────────────────────────────────────
|
|
80
|
+
// El kernel pasa esta tabla vía ow_module_set_host() justo después del dlopen.
|
|
81
|
+
// Permite a los módulos emitir eventos (watchers, PTY stdout, tray clicks…).
|
|
82
|
+
|
|
83
|
+
#define OW_HOST_ABI_VERSION 1
|
|
84
|
+
|
|
85
|
+
typedef void (*ow_host_emit_event_t)(void* ctx, uint32_t window_id,
|
|
86
|
+
const char* name, const char* json);
|
|
87
|
+
typedef void (*ow_host_log_t)(void* ctx, int level, const char* msg);
|
|
88
|
+
|
|
89
|
+
typedef struct ow_module_host {
|
|
90
|
+
uint32_t version; ///< OW_HOST_ABI_VERSION
|
|
91
|
+
void* ctx; ///< contexto opaco del kernel
|
|
92
|
+
ow_host_emit_event_t emit_event;
|
|
93
|
+
///< window_id == 0 → broadcast (todas las ventanas + SDK por control socket)
|
|
94
|
+
ow_host_log_t log; ///< level: 0 debug · 1 info · 2 warn · 3 error
|
|
95
|
+
void* reserved[4];
|
|
96
|
+
} ow_module_host_t;
|
|
97
|
+
|
|
98
|
+
/// Opcional: el módulo guarda la tabla si quiere emitir eventos.
|
|
99
|
+
typedef void (*ow_module_set_host_t)(const ow_module_host_t* host);
|
|
100
|
+
|
|
101
|
+
#ifdef __cplusplus
|
|
102
|
+
} // extern "C"
|
|
103
|
+
#endif
|
package/package.json
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@owear/linux-x64-gnu",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Owear runtime — kernel nativo + módulos stock (.owm) + headers. Linux x64 (glibc).",
|
|
5
|
+
"license": "Apache-2.0",
|
|
6
|
+
"homepage": "https://owear.dev",
|
|
7
|
+
"repository": {
|
|
8
|
+
"type": "git",
|
|
9
|
+
"url": "git+https://github.com/owear/owear.git",
|
|
10
|
+
"directory": "packages/runtime-linux-x64-gnu"
|
|
11
|
+
},
|
|
12
|
+
"publishConfig": {
|
|
13
|
+
"access": "public"
|
|
14
|
+
},
|
|
15
|
+
"os": ["linux"],
|
|
16
|
+
"cpu": ["x64"],
|
|
17
|
+
"libc": ["glibc"],
|
|
18
|
+
"engines": { "node": ">=20" },
|
|
19
|
+
"bin": { "owear": "bin/owear" },
|
|
20
|
+
"files": ["bin", "include", "README.md", "LICENSE"],
|
|
21
|
+
"scripts": {
|
|
22
|
+
"prepack": "node ../../tools/pack-runtime.mjs --target linux-x64-gnu"
|
|
23
|
+
}
|
|
24
|
+
}
|