MayaFlux 0.4.0
Digital-First Multimedia Processing Framework
Loading...
Searching...
No Matches
Lila.cpp
Go to the documentation of this file.
1#include "Lila.hpp"
2
4#include "Server.hpp"
5
6#include "Commentator.hpp"
7
8#ifdef MAYAFLUX_PLATFORM_MACOS
9#include <CoreFoundation/CoreFoundation.h>
10#endif
11
12#ifdef MAYAFLUX_PLATFORM_WINDOWS
14#endif
15
16namespace Lila {
17
19 : m_interpreter(std::make_unique<ClangInterpreter>())
20 , m_current_mode(OperationMode::Direct)
21 , server_loop_rate(0.1F)
22{
23 LILA_DEBUG(Emitter::SYSTEM, "Lila instance created");
24}
25
27{
28 stop_server();
29 LILA_DEBUG(Emitter::SYSTEM, "Lila instance destroyed");
30}
31
32bool Lila::initialize(OperationMode mode, int server_port, bool skip_host_library_load) noexcept
33{
34 LILA_INFO(Emitter::SYSTEM, "Initializing Lila");
35 m_current_mode = mode;
36
37 if (!initialize_interpreter(skip_host_library_load)) {
38 LILA_ERROR(Emitter::SYSTEM, "Failed to initialize interpreter");
39 return false;
40 }
41
42 if (mode == OperationMode::Server || mode == OperationMode::Both) {
43 if (!initialize_server(server_port)) {
44 LILA_ERROR(Emitter::SYSTEM, "Failed to initialize server");
45 return false;
46 }
47 }
48
49 LILA_INFO(Emitter::SYSTEM, "Lila initialized successfully");
50 return true;
51}
52
53bool Lila::initialize_interpreter(bool skip_host_library_load)
54{
55 LILA_DEBUG(Emitter::SYSTEM, "Initializing interpreter subsystem");
56 return m_interpreter->initialize(skip_host_library_load);
57}
58
60{
61 if (m_server && m_server->is_running()) {
62 LILA_WARN(Emitter::SYSTEM, "Stopping existing server before starting new one");
63 stop_server();
64 }
65
66 LILA_DEBUG(Emitter::SYSTEM,
67 std::string("Initializing server on port ") + std::to_string(port));
68
69 m_server = std::make_unique<Server>(port);
70
71 m_server->set_message_handler([this](std::string_view message) {
72 return this->handle_server_message(message);
73 });
74
75 return m_server->start();
76}
77
78std::expected<std::string, std::string> Lila::handle_server_message(std::string_view message)
79{
80 if (message.empty()) {
81 return R"({"status":"error","message":"Empty message"})";
82 }
83
84 auto result = m_interpreter->eval(std::string(message));
85
86 if (result.success) {
87 if (m_success_callback) {
88 m_success_callback();
89 }
90 return "";
91 }
92
93 if (m_error_callback) {
94 m_error_callback(result.error);
95 }
96
97 return std::unexpected(escape_json(result.error));
98}
99
100bool Lila::eval(const std::string& code)
101{
102 if (!m_interpreter) {
103 LILA_ERROR(Emitter::SYSTEM, "Cannot eval: interpreter not initialized");
104 return false;
105 }
106
107 auto result = m_interpreter->eval(code);
108
109 if (result.success && m_success_callback) {
110 m_success_callback();
111 } else if (!result.success && m_error_callback) {
112 m_error_callback(result.error);
113 }
114
115 return result.success;
116}
117
118bool Lila::eval_file(const std::string& filepath)
119{
120 if (!m_interpreter) {
121 LILA_ERROR(Emitter::SYSTEM, "Cannot eval file: interpreter not initialized");
122 return false;
123 }
124
125 auto result = m_interpreter->eval_file(filepath);
126 return result.success;
127}
128
129void Lila::start_server(int port)
130{
131 LILA_INFO(Emitter::SYSTEM,
132 std::string("Starting server on port ") + std::to_string(port));
133
134 initialize_server(port);
135}
136
138{
139 if (m_server) {
140 LILA_INFO(Emitter::SYSTEM, "Stopping server");
141 m_server->stop();
142 m_server.reset();
143 }
144}
145
147{
148 return m_server && m_server->is_running();
149}
150
151#ifdef MAYAFLUX_PLATFORM_WINDOWS
152void Lila::set_main_thread_id(uint32_t thread_id)
153{
154 MayaFlux::Parallel::g_MainThreadId = static_cast<DWORD>(thread_id);
155}
156#endif
157
158void* Lila::get_symbol_address(const std::string& name)
159{
160 return m_interpreter ? m_interpreter->get_symbol_address(name) : nullptr;
161}
162
163std::vector<std::string> Lila::get_defined_symbols()
164{
165 return m_interpreter ? m_interpreter->get_defined_symbols() : std::vector<std::string> {};
166}
167
168void Lila::add_include_path(const std::string& path)
169{
170 if (m_interpreter) {
171 m_interpreter->add_include_path(path);
172 }
173}
174
175void Lila::add_compile_flag(const std::string& flag)
176{
177 if (m_interpreter) {
178 m_interpreter->add_compile_flag(flag);
179 }
180}
181
182void Lila::on_success(std::function<void()> callback)
183{
184 m_success_callback = std::move(callback);
185 LILA_DEBUG(Emitter::SYSTEM, "Success callback registered");
186}
187
188void Lila::on_error(std::function<void(const std::string&)> callback)
189{
190 m_error_callback = std::move(callback);
191 LILA_DEBUG(Emitter::SYSTEM, "Error callback registered");
192}
193
194void Lila::on_server_started(std::function<void()> callback)
195{
196 if (m_server) {
197 m_server->on_server_started(std::move(callback));
198 LILA_DEBUG(Emitter::SYSTEM, "Server started callback registered");
199 }
200}
201
202void Lila::on_server_client_connected(std::function<void(const ClientInfo&)> callback)
203{
204 if (m_server) {
205 m_server->on_client_connected(std::move(callback));
206 LILA_DEBUG(Emitter::SYSTEM, "Client connected callback registered");
207 }
208}
209
210void Lila::on_server_client_disconnected(std::function<void(const ClientInfo&)> callback)
211{
212 if (m_server) {
213 m_server->on_client_disconnected(std::move(callback));
214 LILA_DEBUG(Emitter::SYSTEM, "Client disconnected callback registered");
215 }
216}
217
218std::string Lila::get_last_error() const
219{
220 return m_interpreter ? m_interpreter->get_last_error() : "Interpreter not initialized";
221}
222
223OperationMode Lila::get_current_mode() const
224{
225 return m_current_mode;
226}
227
228void Lila::await_shutdown(const std::atomic<bool>* external_flag)
229{
230 LILA_INFO(Emitter::SYSTEM, "Entering main event loop");
231
232#ifdef MAYAFLUX_PLATFORM_MACOS
233 while (!m_shutdown_requested.load(std::memory_order_acquire) && (!external_flag || external_flag->load(std::memory_order_acquire))) {
234 if (!is_server_running()) {
235 LILA_ERROR(Emitter::SYSTEM, "Server stopped unexpectedly");
236 break;
237 }
238 CFRunLoopRunInMode(kCFRunLoopDefaultMode, server_loop_rate, false);
239 }
240
241#elif defined(MAYAFLUX_PLATFORM_WINDOWS)
242 if (MayaFlux::Parallel::g_MainThreadId == 0) {
243 MayaFlux::Parallel::g_MainThreadId = GetCurrentThreadId();
244 }
245
246 MSG msg;
247 PeekMessage(&msg, NULL, WM_USER, WM_USER, PM_NOREMOVE);
248
249 while (!m_shutdown_requested.load(std::memory_order_acquire)
250 && (!external_flag || external_flag->load(std::memory_order_acquire))) {
251 if (!is_server_running()) {
252 LILA_ERROR(Emitter::SYSTEM, "Server stopped unexpectedly");
253 break;
254 }
255
256 while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) {
257 if (msg.message == WM_QUIT) {
258 m_shutdown_requested.store(true, std::memory_order_release);
259 break;
260 }
261
262 if (msg.message == MAYAFLUX_WM_DISPATCH) {
263 auto* task = reinterpret_cast<std::function<void()>*>(msg.lParam);
264 if (task) {
265 (*task)();
266 task->~function();
267 HeapFree(GetProcessHeap(), 0, task);
268 }
269 } else {
270 TranslateMessage(&msg);
271 DispatchMessage(&msg);
272 }
273 }
274
275 std::this_thread::sleep_for(
276 std::chrono::milliseconds(static_cast<int>(server_loop_rate * 1000.0F)));
277 }
278
279#else
280 while (!m_shutdown_requested.load(std::memory_order_acquire) && (!external_flag || external_flag->load(std::memory_order_acquire))) {
281 if (!is_server_running()) {
282 LILA_ERROR(Emitter::SYSTEM, "Server stopped unexpectedly");
283 break;
284 }
285 std::this_thread::sleep_for(std::chrono::milliseconds(static_cast<int>(server_loop_rate * 1000)));
286 }
287#endif
288
289 LILA_INFO(Emitter::SYSTEM, "Exiting main event loop");
290}
291
293{
294 m_shutdown_requested.store(true, std::memory_order_release);
295
296#ifdef MAYAFLUX_PLATFORM_MACOS
297 CFRunLoopStop(CFRunLoopGetMain());
298#elif defined(MAYAFLUX_PLATFORM_WINDOWS)
299 PostThreadMessage(MayaFlux::Parallel::g_MainThreadId, WM_QUIT, 0, 0);
300#endif
301}
302
303std::string Lila::escape_json(const std::string& str)
304{
305 std::string escaped;
306 escaped.reserve(str.size());
307
308 for (char c : str) {
309 switch (c) {
310 case '"':
311 escaped += "\\\"";
312 break;
313 case '\\':
314 escaped += "\\\\";
315 break;
316 case '\n':
317 escaped += "\\n";
318 break;
319 case '\r':
320 escaped += "\\r";
321 break;
322 case '\t':
323 escaped += "\\t";
324 break;
325 default:
326 if (c >= 0x20) {
327 escaped += c;
328 }
329 }
330 }
331 return escaped;
332}
333
334} // namespace Lila
#define LILA_WARN(emitter, msg)
#define LILA_ERROR(emitter, msg)
#define LILA_DEBUG(emitter, msg)
#define LILA_INFO(emitter, msg)
Embedded Clang interpreter for live code evaluation in MayaFlux.
std::vector< std::string > get_defined_symbols()
Gets a list of all defined symbols.
Definition Lila.cpp:163
static std::string escape_json(const std::string &str)
Escapes a string for safe JSON encoding.
Definition Lila.cpp:303
bool initialize(OperationMode mode=OperationMode::Direct, int server_port=9090, bool skip_host_library_load=false) noexcept
Initializes the live coding environment.
Definition Lila.cpp:32
bool initialize_interpreter(bool skip_host_library_load)
Definition Lila.cpp:53
bool eval(const std::string &code)
Evaluates a C++ code snippet directly.
Definition Lila.cpp:100
void on_error(std::function< void(const std::string &)> callback)
Registers a callback for code evaluation errors.
Definition Lila.cpp:188
std::expected< std::string, std::string > handle_server_message(std::string_view message)
Definition Lila.cpp:78
void stop_server()
Stops the TCP server and disconnects all clients.
Definition Lila.cpp:137
OperationMode get_current_mode() const
Gets the current operation mode.
Definition Lila.cpp:223
void * get_symbol_address(const std::string &name)
Gets the address of a symbol defined in the interpreter.
Definition Lila.cpp:158
void on_server_client_disconnected(std::function< void(const ClientInfo &)> callback)
Registers a callback for client disconnections (server mode)
Definition Lila.cpp:210
bool initialize_server(int port)
Definition Lila.cpp:59
void request_shutdown()
Request shutdown from any thread.
Definition Lila.cpp:292
void on_server_client_connected(std::function< void(const ClientInfo &)> callback)
Registers a callback for new client connections (server mode)
Definition Lila.cpp:202
void add_compile_flag(const std::string &flag)
Adds a compile flag for code evaluation.
Definition Lila.cpp:175
void add_include_path(const std::string &path)
Adds an include path for code evaluation.
Definition Lila.cpp:168
~Lila()
Destructor; cleans up interpreter and server resources.
Definition Lila.cpp:26
void start_server(int port=9090)
Starts the TCP server for remote live coding.
Definition Lila.cpp:129
bool eval_file(const std::string &filepath)
Evaluates a C++ code file directly.
Definition Lila.cpp:118
std::string get_last_error() const
Gets the last error message.
Definition Lila.cpp:218
bool is_server_running() const
Checks if the server is currently running.
Definition Lila.cpp:146
void await_shutdown(const std::atomic< bool > *external_flag)
Blocks until server shutdown (main thread event loop)
Definition Lila.cpp:228
Lila()
Constructs a Lila instance.
Definition Lila.cpp:18
void on_success(std::function< void()> callback)
Registers a callback for successful code evaluation.
Definition Lila.cpp:182
void on_server_started(std::function< void()> callback)
Registers a callback for server start events.
Definition Lila.cpp:194
Information about a connected client.
Definition EventBus.hpp:58