1 | // Aseprite |
2 | // Copyright (C) 2018-2022 Igara Studio S.A. |
3 | // Copyright (C) 2001-2018 David Capello |
4 | // |
5 | // This program is distributed under the terms of |
6 | // the End-User License Agreement for Aseprite. |
7 | |
8 | #ifdef HAVE_CONFIG_H |
9 | #include "config.h" |
10 | #endif |
11 | |
12 | #include "app/script/engine.h" |
13 | |
14 | #include "app/app.h" |
15 | #include "app/console.h" |
16 | #include "app/doc_exporter.h" |
17 | #include "app/doc_range.h" |
18 | #include "app/pref/preferences.h" |
19 | #include "app/script/luacpp.h" |
20 | #include "app/script/security.h" |
21 | #include "app/sprite_sheet_type.h" |
22 | #include "app/tilemap_mode.h" |
23 | #include "app/tileset_mode.h" |
24 | #include "app/tools/ink_type.h" |
25 | #include "base/chrono.h" |
26 | #include "base/file_handle.h" |
27 | #include "base/fs.h" |
28 | #include "base/fstream_path.h" |
29 | #include "doc/anidir.h" |
30 | #include "doc/blend_mode.h" |
31 | #include "doc/color_mode.h" |
32 | #include "filters/target.h" |
33 | #include "ui/mouse_button.h" |
34 | |
35 | #include <fstream> |
36 | #include <sstream> |
37 | #include <stack> |
38 | #include <string> |
39 | |
40 | namespace app { |
41 | namespace script { |
42 | |
43 | namespace { |
44 | |
45 | // High precision clock. |
46 | base::Chrono luaClock; |
47 | |
48 | // Stack of script filenames that are being executed. |
49 | std::stack<std::string> current_script_dirs; |
50 | |
51 | // Just one debugger delegate is possible. |
52 | DebuggerDelegate* g_debuggerDelegate = nullptr; |
53 | |
54 | class AddScriptFilename { |
55 | public: |
56 | AddScriptFilename(const std::string& fn) { |
57 | current_script_dirs.push(fn); |
58 | } |
59 | ~AddScriptFilename() { |
60 | current_script_dirs.pop(); |
61 | } |
62 | }; |
63 | |
64 | int print(lua_State* L) |
65 | { |
66 | std::string output; |
67 | int n = lua_gettop(L); /* number of arguments */ |
68 | int i; |
69 | lua_getglobal(L, "tostring" ); |
70 | for (i=1; i<=n; i++) { |
71 | lua_pushvalue(L, -1); // function to be called |
72 | lua_pushvalue(L, i); // value to print |
73 | lua_call(L, 1, 1); |
74 | size_t l; |
75 | const char* s = lua_tolstring(L, -1, &l); // get result |
76 | if (s == nullptr) |
77 | return luaL_error(L, "'tostring' must return a string to 'print'" ); |
78 | if (i > 1) |
79 | output.push_back('\t'); |
80 | output.insert(output.size(), s, l); |
81 | lua_pop(L, 1); // pop result |
82 | } |
83 | if (!output.empty()) { |
84 | auto app = App::instance(); |
85 | if (app && app->scriptEngine()) |
86 | app->scriptEngine()->consolePrint(output.c_str()); |
87 | else { |
88 | std::printf("%s\n" , output.c_str()); |
89 | std::fflush(stdout); |
90 | } |
91 | } |
92 | return 0; |
93 | } |
94 | |
95 | static int dofilecont(lua_State *L, int d1, lua_KContext d2) |
96 | { |
97 | (void)d1; |
98 | (void)d2; |
99 | return lua_gettop(L) - 1; |
100 | } |
101 | |
102 | int dofile(lua_State *L) |
103 | { |
104 | const char* argFname = luaL_optstring(L, 1, NULL); |
105 | std::string fname = argFname; |
106 | |
107 | if (!base::is_file(fname) && |
108 | !current_script_dirs.empty()) { |
109 | // Try to complete a relative filename |
110 | std::string altFname = |
111 | base::join_path(base::get_file_path(current_script_dirs.top()), |
112 | fname); |
113 | if (base::is_file(altFname)) |
114 | fname = altFname; |
115 | } |
116 | |
117 | lua_settop(L, 1); |
118 | if (luaL_loadfile(L, fname.c_str()) != LUA_OK) |
119 | return lua_error(L); |
120 | { |
121 | AddScriptFilename add(fname); |
122 | lua_callk(L, 0, LUA_MULTRET, 0, dofilecont); |
123 | } |
124 | return dofilecont(L, 0, 0); |
125 | } |
126 | |
127 | int os_clock(lua_State* L) |
128 | { |
129 | lua_pushnumber(L, luaClock.elapsed()); |
130 | return 1; |
131 | } |
132 | |
133 | int unsupported(lua_State* L) |
134 | { |
135 | // debug.getinfo(1, "n").name |
136 | lua_getglobal(L, "debug" ); |
137 | lua_getfield(L, -1, "getinfo" ); |
138 | lua_remove(L, -2); |
139 | lua_pushinteger(L, 1); |
140 | lua_pushstring(L, "n" ); |
141 | lua_call(L, 2, 1); |
142 | lua_getfield(L, -1, "name" ); |
143 | return luaL_error(L, "unsupported function '%s'" , |
144 | lua_tostring(L, -1)); |
145 | } |
146 | |
147 | } // anonymous namespace |
148 | |
149 | void register_app_object(lua_State* L); |
150 | void register_app_pixel_color_object(lua_State* L); |
151 | void register_app_fs_object(lua_State* L); |
152 | void register_app_command_object(lua_State* L); |
153 | void register_app_preferences_object(lua_State* L); |
154 | |
155 | void register_brush_class(lua_State* L); |
156 | void register_cel_class(lua_State* L); |
157 | void register_cels_class(lua_State* L); |
158 | void register_color_class(lua_State* L); |
159 | void register_color_space_class(lua_State* L); |
160 | #ifdef ENABLE_UI |
161 | void register_dialog_class(lua_State* L); |
162 | #endif |
163 | void register_events_class(lua_State* L); |
164 | void register_frame_class(lua_State* L); |
165 | void register_frames_class(lua_State* L); |
166 | void register_grid_class(lua_State* L); |
167 | void register_image_class(lua_State* L); |
168 | void register_image_iterator_class(lua_State* L); |
169 | void register_image_spec_class(lua_State* L); |
170 | void register_images_class(lua_State* L); |
171 | void register_layer_class(lua_State* L); |
172 | void register_layers_class(lua_State* L); |
173 | void register_palette_class(lua_State* L); |
174 | void register_palettes_class(lua_State* L); |
175 | void register_plugin_class(lua_State* L); |
176 | void register_point_class(lua_State* L); |
177 | void register_range_class(lua_State* L); |
178 | void register_rect_class(lua_State* L); |
179 | void register_selection_class(lua_State* L); |
180 | void register_site_class(lua_State* L); |
181 | void register_size_class(lua_State* L); |
182 | void register_slice_class(lua_State* L); |
183 | void register_slices_class(lua_State* L); |
184 | void register_sprite_class(lua_State* L); |
185 | void register_sprites_class(lua_State* L); |
186 | void register_tag_class(lua_State* L); |
187 | void register_tags_class(lua_State* L); |
188 | void register_tileset_class(lua_State* L); |
189 | void register_tilesets_class(lua_State* L); |
190 | void register_tool_class(lua_State* L); |
191 | void register_version_class(lua_State* L); |
192 | void register_websocket_class(lua_State* L); |
193 | |
194 | void set_app_params(lua_State* L, const Params& params); |
195 | |
196 | // We use our own fopen() that supports Unicode filename on Windows |
197 | extern "C" FILE* lua_user_fopen(const char* fname, |
198 | const char* mode) |
199 | { |
200 | return base::open_file_raw(fname, mode); |
201 | } |
202 | |
203 | Engine::Engine() |
204 | : L(luaL_newstate()) |
205 | , m_delegate(nullptr) |
206 | , m_printLastResult(false) |
207 | { |
208 | #if _DEBUG |
209 | int top = lua_gettop(L); |
210 | #endif |
211 | |
212 | // Standard Lua libraries |
213 | luaL_requiref(L, LUA_GNAME, luaopen_base, 1); |
214 | luaL_requiref(L, LUA_COLIBNAME, luaopen_coroutine, 1); |
215 | luaL_requiref(L, LUA_TABLIBNAME, luaopen_table, 1); |
216 | luaL_requiref(L, LUA_IOLIBNAME, luaopen_io, 1); |
217 | luaL_requiref(L, LUA_OSLIBNAME, luaopen_os, 1); |
218 | luaL_requiref(L, LUA_STRLIBNAME, luaopen_string, 1); |
219 | luaL_requiref(L, LUA_MATHLIBNAME, luaopen_math, 1); |
220 | luaL_requiref(L, LUA_UTF8LIBNAME, luaopen_utf8, 1); |
221 | luaL_requiref(L, LUA_DBLIBNAME, luaopen_debug, 1); |
222 | lua_pop(L, 9); |
223 | |
224 | // Overwrite Lua functions |
225 | lua_register(L, "print" , print); |
226 | lua_register(L, "dofile" , dofile); |
227 | |
228 | lua_getglobal(L, "os" ); |
229 | for (const char* name : { "remove" , "rename" , "exit" , "tmpname" }) { |
230 | lua_pushcfunction(L, unsupported); |
231 | lua_setfield(L, -2, name); |
232 | } |
233 | lua_pushcfunction(L, os_clock); |
234 | lua_setfield(L, -2, "clock" ); |
235 | lua_pop(L, 1); |
236 | |
237 | // Wrap io.open() |
238 | lua_getglobal(L, "io" ); |
239 | lua_getfield(L, -1, "open" ); |
240 | lua_pushcclosure(L, secure_io_open, 1); |
241 | lua_setfield(L, -2, "open" ); |
242 | lua_pop(L, 1); |
243 | |
244 | // Wrap os.execute() |
245 | lua_getglobal(L, "os" ); |
246 | lua_getfield(L, -1, "execute" ); |
247 | lua_pushcclosure(L, secure_os_execute, 1); |
248 | lua_setfield(L, -2, "execute" ); |
249 | lua_pop(L, 1); |
250 | |
251 | // Generic code used by metatables |
252 | run_mt_index_code(L); |
253 | |
254 | // Register global app object |
255 | register_app_object(L); |
256 | register_app_pixel_color_object(L); |
257 | register_app_fs_object(L); |
258 | register_app_command_object(L); |
259 | register_app_preferences_object(L); |
260 | |
261 | // Register constants |
262 | lua_newtable(L); |
263 | lua_pushvalue(L, -1); |
264 | lua_setglobal(L, "ColorMode" ); |
265 | setfield_integer(L, "RGB" , doc::ColorMode::RGB); |
266 | setfield_integer(L, "GRAY" , doc::ColorMode::GRAYSCALE); |
267 | setfield_integer(L, "GRAYSCALE" , doc::ColorMode::GRAYSCALE); |
268 | setfield_integer(L, "INDEXED" , doc::ColorMode::INDEXED); |
269 | setfield_integer(L, "TILEMAP" , doc::ColorMode::TILEMAP); |
270 | lua_pop(L, 1); |
271 | |
272 | lua_newtable(L); |
273 | lua_pushvalue(L, -1); |
274 | lua_setglobal(L, "AniDir" ); |
275 | setfield_integer(L, "FORWARD" , doc::AniDir::FORWARD); |
276 | setfield_integer(L, "REVERSE" , doc::AniDir::REVERSE); |
277 | setfield_integer(L, "PING_PONG" , doc::AniDir::PING_PONG); |
278 | lua_pop(L, 1); |
279 | |
280 | lua_newtable(L); |
281 | lua_pushvalue(L, -1); |
282 | lua_setglobal(L, "BlendMode" ); |
283 | setfield_integer(L, "NORMAL" , doc::BlendMode::NORMAL); |
284 | setfield_integer(L, "MULTIPLY" , doc::BlendMode::MULTIPLY); |
285 | setfield_integer(L, "SCREEN" , doc::BlendMode::SCREEN); |
286 | setfield_integer(L, "OVERLAY" , doc::BlendMode::OVERLAY); |
287 | setfield_integer(L, "DARKEN" , doc::BlendMode::DARKEN); |
288 | setfield_integer(L, "LIGHTEN" , doc::BlendMode::LIGHTEN); |
289 | setfield_integer(L, "COLOR_DODGE" , doc::BlendMode::COLOR_DODGE); |
290 | setfield_integer(L, "COLOR_BURN" , doc::BlendMode::COLOR_BURN); |
291 | setfield_integer(L, "HARD_LIGHT" , doc::BlendMode::HARD_LIGHT); |
292 | setfield_integer(L, "SOFT_LIGHT" , doc::BlendMode::SOFT_LIGHT); |
293 | setfield_integer(L, "DIFFERENCE" , doc::BlendMode::DIFFERENCE); |
294 | setfield_integer(L, "EXCLUSION" , doc::BlendMode::EXCLUSION); |
295 | setfield_integer(L, "HSL_HUE" , doc::BlendMode::HSL_HUE); |
296 | setfield_integer(L, "HSL_SATURATION" , doc::BlendMode::HSL_SATURATION); |
297 | setfield_integer(L, "HSL_COLOR" , doc::BlendMode::HSL_COLOR); |
298 | setfield_integer(L, "HSL_LUMINOSITY" , doc::BlendMode::HSL_LUMINOSITY); |
299 | setfield_integer(L, "ADDITION" , doc::BlendMode::ADDITION); |
300 | setfield_integer(L, "SUBTRACT" , doc::BlendMode::SUBTRACT); |
301 | setfield_integer(L, "DIVIDE" , doc::BlendMode::DIVIDE); |
302 | lua_pop(L, 1); |
303 | |
304 | lua_newtable(L); |
305 | lua_pushvalue(L, -1); |
306 | lua_setglobal(L, "RangeType" ); |
307 | setfield_integer(L, "EMPTY" , DocRange::kNone); |
308 | setfield_integer(L, "LAYERS" , DocRange::kLayers); |
309 | setfield_integer(L, "FRAMES" , DocRange::kFrames); |
310 | setfield_integer(L, "CELS" , DocRange::kCels); |
311 | lua_pop(L, 1); |
312 | |
313 | lua_newtable(L); |
314 | lua_pushvalue(L, -1); |
315 | lua_setglobal(L, "SpriteSheetType" ); |
316 | setfield_integer(L, "HORIZONTAL" , SpriteSheetType::Horizontal); |
317 | setfield_integer(L, "VERTICAL" , SpriteSheetType::Vertical); |
318 | setfield_integer(L, "ROWS" , SpriteSheetType::Rows); |
319 | setfield_integer(L, "COLUMNS" , SpriteSheetType::Columns); |
320 | setfield_integer(L, "PACKED" , SpriteSheetType::Packed); |
321 | lua_pop(L, 1); |
322 | |
323 | lua_newtable(L); |
324 | lua_pushvalue(L, -1); |
325 | lua_setglobal(L, "SpriteSheetDataFormat" ); |
326 | setfield_integer(L, "JSON_HASH" , SpriteSheetDataFormat::JsonHash); |
327 | setfield_integer(L, "JSON_ARRAY" , SpriteSheetDataFormat::JsonArray); |
328 | lua_pop(L, 1); |
329 | |
330 | lua_newtable(L); |
331 | lua_pushvalue(L, -1); |
332 | lua_setglobal(L, "BrushType" ); |
333 | setfield_integer(L, "CIRCLE" , doc::kCircleBrushType); |
334 | setfield_integer(L, "SQUARE" , doc::kSquareBrushType); |
335 | setfield_integer(L, "LINE" , doc::kLineBrushType); |
336 | setfield_integer(L, "IMAGE" , doc::kImageBrushType); |
337 | lua_pop(L, 1); |
338 | |
339 | lua_newtable(L); |
340 | lua_pushvalue(L, -1); |
341 | lua_setglobal(L, "BrushPattern" ); |
342 | setfield_integer(L, "ORIGIN" , doc::BrushPattern::ALIGNED_TO_SRC); |
343 | setfield_integer(L, "TARGET" , doc::BrushPattern::ALIGNED_TO_DST); |
344 | setfield_integer(L, "NONE" , doc::BrushPattern::PAINT_BRUSH); |
345 | lua_pop(L, 1); |
346 | |
347 | lua_newtable(L); |
348 | lua_pushvalue(L, -1); |
349 | lua_setglobal(L, "Ink" ); |
350 | setfield_integer(L, "SIMPLE" , app::tools::InkType::SIMPLE); |
351 | setfield_integer(L, "ALPHA_COMPOSITING" , app::tools::InkType::ALPHA_COMPOSITING); |
352 | setfield_integer(L, "COPY_COLOR" , app::tools::InkType::COPY_COLOR); |
353 | setfield_integer(L, "LOCK_ALPHA" , app::tools::InkType::LOCK_ALPHA); |
354 | setfield_integer(L, "SHADING" , app::tools::InkType::SHADING); |
355 | lua_pop(L, 1); |
356 | |
357 | lua_newtable(L); |
358 | lua_pushvalue(L, -1); |
359 | lua_setglobal(L, "FilterChannels" ); |
360 | setfield_integer(L, "RED" , TARGET_RED_CHANNEL); |
361 | setfield_integer(L, "GREEN" , TARGET_GREEN_CHANNEL); |
362 | setfield_integer(L, "BLUE" , TARGET_BLUE_CHANNEL); |
363 | setfield_integer(L, "ALPHA" , TARGET_ALPHA_CHANNEL); |
364 | setfield_integer(L, "GRAY" , TARGET_GRAY_CHANNEL); |
365 | setfield_integer(L, "INDEX" , TARGET_INDEX_CHANNEL); |
366 | setfield_integer(L, "RGB" , TARGET_RED_CHANNEL | TARGET_GREEN_CHANNEL | TARGET_BLUE_CHANNEL); |
367 | setfield_integer(L, "RGBA" , TARGET_RED_CHANNEL | TARGET_GREEN_CHANNEL | TARGET_BLUE_CHANNEL | TARGET_ALPHA_CHANNEL); |
368 | setfield_integer(L, "GRAYA" , TARGET_GRAY_CHANNEL | TARGET_ALPHA_CHANNEL); |
369 | lua_pop(L, 1); |
370 | |
371 | lua_newtable(L); |
372 | lua_pushvalue(L, -1); |
373 | lua_setglobal(L, "MouseButton" ); |
374 | setfield_integer(L, "NONE" , (int)ui::kButtonNone); |
375 | setfield_integer(L, "LEFT" , (int)ui::kButtonLeft); |
376 | setfield_integer(L, "RIGHT" , (int)ui::kButtonRight); |
377 | setfield_integer(L, "MIDDLE" , (int)ui::kButtonMiddle); |
378 | setfield_integer(L, "X1" , (int)ui::kButtonX1); |
379 | setfield_integer(L, "X2" , (int)ui::kButtonX2); |
380 | lua_pop(L, 1); |
381 | |
382 | lua_newtable(L); |
383 | lua_pushvalue(L, -1); |
384 | lua_setglobal(L, "TilemapMode" ); |
385 | setfield_integer(L, "PIXELS" , TilemapMode::Pixels); |
386 | setfield_integer(L, "TILES" , TilemapMode::Tiles); |
387 | lua_pop(L, 1); |
388 | |
389 | lua_newtable(L); |
390 | lua_pushvalue(L, -1); |
391 | lua_setglobal(L, "TilesetMode" ); |
392 | setfield_integer(L, "MANUAL" , TilesetMode::Manual); |
393 | setfield_integer(L, "AUTO" , TilesetMode::Auto); |
394 | setfield_integer(L, "STACK" , TilesetMode::Stack); |
395 | lua_pop(L, 1); |
396 | |
397 | lua_newtable(L); |
398 | lua_pushvalue(L, -1); |
399 | lua_setglobal(L, "SelectionMode" ); |
400 | setfield_integer(L, "REPLACE" , (int)gen::SelectionMode::REPLACE); |
401 | setfield_integer(L, "ADD" , (int)gen::SelectionMode::ADD); |
402 | setfield_integer(L, "SUBTRACT" , (int)gen::SelectionMode::SUBTRACT); |
403 | setfield_integer(L, "INTERSECT" , (int)gen::SelectionMode::INTERSECT); |
404 | lua_pop(L, 1); |
405 | |
406 | // Register classes/prototypes |
407 | register_brush_class(L); |
408 | register_cel_class(L); |
409 | register_cels_class(L); |
410 | register_color_class(L); |
411 | register_color_space_class(L); |
412 | #ifdef ENABLE_UI |
413 | register_dialog_class(L); |
414 | #endif |
415 | register_events_class(L); |
416 | register_frame_class(L); |
417 | register_frames_class(L); |
418 | register_grid_class(L); |
419 | register_image_class(L); |
420 | register_image_iterator_class(L); |
421 | register_image_spec_class(L); |
422 | register_images_class(L); |
423 | register_layer_class(L); |
424 | register_layers_class(L); |
425 | register_palette_class(L); |
426 | register_palettes_class(L); |
427 | register_plugin_class(L); |
428 | register_point_class(L); |
429 | register_range_class(L); |
430 | register_rect_class(L); |
431 | register_selection_class(L); |
432 | register_site_class(L); |
433 | register_size_class(L); |
434 | register_slice_class(L); |
435 | register_slices_class(L); |
436 | register_sprite_class(L); |
437 | register_sprites_class(L); |
438 | register_tag_class(L); |
439 | register_tags_class(L); |
440 | register_tileset_class(L); |
441 | register_tilesets_class(L); |
442 | register_tool_class(L); |
443 | register_version_class(L); |
444 | #if ENABLE_WEBSOCKET |
445 | register_websocket_class(L); |
446 | #endif |
447 | |
448 | // Check that we have a clean start (without dirty in the stack) |
449 | ASSERT(lua_gettop(L) == top); |
450 | } |
451 | |
452 | Engine::~Engine() |
453 | { |
454 | ASSERT(L == nullptr); |
455 | } |
456 | |
457 | void Engine::destroy() |
458 | { |
459 | #ifdef ENABLE_UI |
460 | close_all_dialogs(); |
461 | #endif |
462 | lua_close(L); |
463 | L = nullptr; |
464 | } |
465 | |
466 | void Engine::printLastResult() |
467 | { |
468 | m_printLastResult = true; |
469 | } |
470 | |
471 | bool Engine::evalCode(const std::string& code, |
472 | const std::string& filename) |
473 | { |
474 | bool ok = true; |
475 | try { |
476 | if (luaL_loadbuffer(L, code.c_str(), code.size(), filename.c_str()) || |
477 | lua_pcall(L, 0, 1, 0)) { |
478 | const char* s = lua_tostring(L, -1); |
479 | if (s) |
480 | onConsoleError(s); |
481 | ok = false; |
482 | m_returnCode = -1; |
483 | } |
484 | else { |
485 | // Return code |
486 | if (lua_isinteger(L, -1)) |
487 | m_returnCode = lua_tointeger(L, -1); |
488 | else |
489 | m_returnCode = 0; |
490 | |
491 | // Code was executed correctly |
492 | if (m_printLastResult) { |
493 | if (!lua_isnone(L, -1)) { |
494 | const char* result = lua_tostring(L, -1); |
495 | if (result) |
496 | onConsolePrint(result); |
497 | } |
498 | } |
499 | } |
500 | lua_pop(L, 1); |
501 | } |
502 | catch (const std::exception& ex) { |
503 | onConsoleError(ex.what()); |
504 | ok = false; |
505 | m_returnCode = -1; |
506 | } |
507 | |
508 | // Collect script garbage. |
509 | lua_gc(L, LUA_GCCOLLECT); |
510 | return ok; |
511 | } |
512 | |
513 | bool Engine::evalFile(const std::string& filename, |
514 | const Params& params) |
515 | { |
516 | std::stringstream buf; |
517 | { |
518 | std::ifstream s(FSTREAM_PATH(filename)); |
519 | // Returns false if we cannot open the file |
520 | if (!s) |
521 | return false; |
522 | buf << s.rdbuf(); |
523 | } |
524 | std::string absFilename = base::get_absolute_path(filename); |
525 | |
526 | AddScriptFilename add(absFilename); |
527 | set_app_params(L, params); |
528 | |
529 | if (g_debuggerDelegate) |
530 | g_debuggerDelegate->startFile(absFilename, buf.str()); |
531 | |
532 | bool result = evalCode(buf.str(), "@" + absFilename); |
533 | |
534 | if (g_debuggerDelegate) |
535 | g_debuggerDelegate->endFile(absFilename); |
536 | |
537 | return result; |
538 | } |
539 | |
540 | void Engine::startDebugger(DebuggerDelegate* debuggerDelegate) |
541 | { |
542 | g_debuggerDelegate = debuggerDelegate; |
543 | |
544 | lua_Hook hook = [](lua_State* L, lua_Debug* ar) { |
545 | int ret = lua_getinfo(L, "l" , ar); |
546 | if (ret == 0 || ar->currentline < 0) |
547 | return; |
548 | |
549 | g_debuggerDelegate->hook(L, ar); |
550 | }; |
551 | |
552 | lua_sethook(L, hook, LUA_MASKCALL | LUA_MASKRET | LUA_MASKLINE | LUA_MASKCOUNT, 1); |
553 | } |
554 | |
555 | void Engine::stopDebugger() |
556 | { |
557 | lua_sethook(L, nullptr, 0, 0); |
558 | } |
559 | |
560 | void Engine::onConsoleError(const char* text) |
561 | { |
562 | if (text && m_delegate) |
563 | m_delegate->onConsoleError(text); |
564 | else |
565 | onConsolePrint(text); |
566 | } |
567 | |
568 | void Engine::onConsolePrint(const char* text) |
569 | { |
570 | if (!text) |
571 | return; |
572 | |
573 | if (m_delegate) |
574 | m_delegate->onConsolePrint(text); |
575 | else { |
576 | std::printf("%s\n" , text); |
577 | std::fflush(stdout); |
578 | } |
579 | } |
580 | |
581 | } // namespace script |
582 | } // namespace app |
583 | |