1 /++
2 Integration with Godot editor's output and debugger tabs
3 +/
4 module godot.api.output;
5 
6 import godot.abi, godot;
7 
8 /++
9 The release-mode Godot-D assert handler redirects assert messages to the Godot
10 error handlers and terminates the program.
11 +/
12 // nothrow
13 void godotAssertHandlerCrash(string file, size_t line, string msg) {
14     import core.exception;
15     import std.experimental.allocator.mallocator;
16 
17     char[] buffer = cast(char[]) Mallocator.instance.allocate(file.length + msg.length + 2);
18     scope (exit)
19         Mallocator.instance.deallocate(cast(void[]) buffer);
20 
21     buffer[0 .. file.length] = file[];
22     buffer[file.length] = '\0';
23     buffer[file.length + 1 .. $ - 1] = msg[];
24     buffer[$ - 1] = '\0';
25 
26     _godot_api.print_error(&buffer.ptr[file.length + 1], "", buffer.ptr, cast(int) line, true);
27 
28     version (D_Exceptions)
29         throw new AssertError(msg, file, line);
30     else {
31         assertHandler = null;
32         assert(0, msg);
33     }
34 }
35 
36 /++
37 The debug-mode Godot-D assert handler redirects assert messages to the Godot
38 error handlers (including Debugger tab in editor and system console).
39 
40 Unlike the default D assert handler, this one doesn't terminate the program,
41 allowing the messages to remain in Godot's Debugger tab and matching how Godot
42 error macros behave.
43 +/
44 // nothrow
45 void godotAssertHandlerEditorDebug(string file, size_t line, string msg) {
46     import core.exception;
47     import std.experimental.allocator.mallocator;
48 
49     char[] buffer = cast(char[]) Mallocator.instance.allocate(file.length + msg.length + 2);
50     scope (exit)
51         Mallocator.instance.deallocate(cast(void[]) buffer);
52 
53     buffer[0 .. file.length] = file[];
54     buffer[file.length] = '\0';
55     buffer[file.length + 1 .. $ - 1] = msg[];
56     buffer[$ - 1] = '\0';
57 
58     _godot_api.print_error(&buffer.ptr[file.length + 1], "", buffer.ptr, cast(int) line, true);
59 
60     //version(assert) // any `assert(x)` gets compiled; usually a debug version
61     //{
62     //	// TODO: if in Editor Debugger, debug_break like GDScript asserts
63     //}
64     //else // only `assert(0)`/`assert(false)` get compiled; usually a release version
65     {
66         // crash on always-false asserts
67         version (D_Exceptions)
68             throw new AssertError(msg, file, line);
69         else {
70             assertHandler = null;
71             assert(0, msg);
72         }
73     }
74 }
75 
76 /**
77 Print to Godot's console and stdout.
78 
79 Params:
80 	args = any Godot-compatible types or strings
81 */
82 void print(Args...)(Args args, string fn = __FUNCTION__, string f = __FILE__, int l = __LINE__) {
83     import godot.string, godot.variant;
84 
85     String str;
86     static if (Args.length == 0)
87         str = String(" ");
88     foreach (arg; args) {
89         static if (is(typeof(arg) : String))
90             str ~= arg;
91         else static if (is(typeof(arg) : NodePath))
92             str ~= arg.str;
93         else static if (is(typeof(arg) : string))
94             str ~= String(arg);
95         else static if (is(typeof(arg) : Variant))
96             str ~= arg.as!String;
97         else static if (Variant.compatibleToGodot!(typeof(arg)))
98             str ~= Variant(arg).as!String;
99         else
100             static assert(0, "Unable to print type " ~ typeof(arg).stringof);
101     }
102     auto utfstr = str.utf8;
103     _godot_api.print_warning(cast(char*) utfstr.data, &fn[0], &f[0], l, true);
104 }