Line data Source code
1 : /* Copyright (C) 2021 Wildfire Games.
2 : * This file is part of 0 A.D.
3 : *
4 : * 0 A.D. is free software: you can redistribute it and/or modify
5 : * it under the terms of the GNU General Public License as published by
6 : * the Free Software Foundation, either version 2 of the License, or
7 : * (at your option) any later version.
8 : *
9 : * 0 A.D. is distributed in the hope that it will be useful,
10 : * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 : * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 : * GNU General Public License for more details.
13 : *
14 : * You should have received a copy of the GNU General Public License
15 : * along with 0 A.D. If not, see <http://www.gnu.org/licenses/>.
16 : */
17 :
18 : #include "precompiled.h"
19 :
20 : #include "gui/ObjectBases/IGUIObject.h"
21 : #include "gui/SettingTypes/CGUIColor.h"
22 : #include "gui/SettingTypes/CGUIList.h"
23 : #include "gui/SettingTypes/CGUISeries.h"
24 : #include "gui/SettingTypes/CGUISize.h"
25 : #include "gui/Scripting/JSInterface_GUIProxy.h"
26 : #include "lib/external_libraries/libsdl.h"
27 : #include "maths/Size2D.h"
28 : #include "maths/Vector2D.h"
29 : #include "ps/Hotkey.h"
30 : #include "ps/CLogger.h"
31 : #include "scriptinterface/Object.h"
32 : #include "scriptinterface/ScriptConversions.h"
33 :
34 : #include <string>
35 :
36 : #define SET(obj, name, value) STMT(JS::RootedValue v_(rq.cx); Script::ToJSVal(rq, &v_, (value)); JS_SetProperty(rq.cx, obj, (name), v_))
37 : // ignore JS_SetProperty return value, because errors should be impossible
38 : // and we can't do anything useful in the case of errors anyway
39 :
40 14 : template<> void Script::ToJSVal<SDL_Event_>(const ScriptRequest& rq, JS::MutableHandleValue ret, SDL_Event_ const& val)
41 : {
42 : const char* typeName;
43 :
44 14 : switch (val.ev.type)
45 : {
46 0 : case SDL_WINDOWEVENT: typeName = "windowevent"; break;
47 4 : case SDL_KEYDOWN: typeName = "keydown"; break;
48 2 : case SDL_KEYUP: typeName = "keyup"; break;
49 0 : case SDL_MOUSEMOTION: typeName = "mousemotion"; break;
50 0 : case SDL_MOUSEBUTTONDOWN: typeName = "mousebuttondown"; break;
51 0 : case SDL_MOUSEBUTTONUP: typeName = "mousebuttonup"; break;
52 0 : case SDL_QUIT: typeName = "quit"; break;
53 2 : case SDL_HOTKEYPRESS: typeName = "hotkeypress"; break;
54 4 : case SDL_HOTKEYDOWN: typeName = "hotkeydown"; break;
55 2 : case SDL_HOTKEYUP: typeName = "hotkeyup"; break;
56 0 : case SDL_HOTKEYPRESS_SILENT: typeName = "hotkeypresssilent"; break;
57 0 : case SDL_HOTKEYUP_SILENT: typeName = "hotkeyupsilent"; break;
58 0 : default: typeName = "(unknown)"; break;
59 : }
60 :
61 28 : JS::RootedObject obj(rq.cx, JS_NewPlainObject(rq.cx));
62 14 : if (!obj)
63 : {
64 0 : ret.setUndefined();
65 0 : return;
66 : }
67 :
68 14 : SET(obj, "type", typeName);
69 :
70 14 : switch (val.ev.type)
71 : {
72 6 : case SDL_KEYDOWN:
73 : case SDL_KEYUP:
74 : {
75 : // SET(obj, "which", (int)val.ev.key.which); // (not in wsdl.h)
76 : // SET(obj, "state", (int)val.ev.key.state); // (not in wsdl.h)
77 :
78 6 : JS::RootedObject keysym(rq.cx, JS_NewPlainObject(rq.cx));
79 6 : if (!keysym)
80 : {
81 0 : ret.setUndefined();
82 0 : return;
83 : }
84 12 : JS::RootedValue keysymVal(rq.cx, JS::ObjectValue(*keysym));
85 6 : JS_SetProperty(rq.cx, obj, "keysym", keysymVal);
86 :
87 : // SET(keysym, "scancode", (int)val.ev.key.keysym.scancode); // (not in wsdl.h)
88 6 : SET(keysym, "sym", (int)val.ev.key.keysym.sym);
89 : // SET(keysym, "mod", (int)val.ev.key.keysym.mod); // (not in wsdl.h)
90 : {
91 6 : SET(keysym, "unicode", JS::UndefinedHandleValue);
92 : }
93 : // TODO: scripts have no idea what all the key/mod enum values are;
94 : // we should probably expose them as constants if we expect scripts to use them
95 :
96 6 : break;
97 : }
98 0 : case SDL_MOUSEMOTION:
99 : {
100 : // SET(obj, "which", (int)val.ev.motion.which); // (not in wsdl.h)
101 : // SET(obj, "state", (int)val.ev.motion.state); // (not in wsdl.h)
102 0 : SET(obj, "x", (int)val.ev.motion.x);
103 0 : SET(obj, "y", (int)val.ev.motion.y);
104 : // SET(obj, "xrel", (int)val.ev.motion.xrel); // (not in wsdl.h)
105 : // SET(obj, "yrel", (int)val.ev.motion.yrel); // (not in wsdl.h)
106 0 : break;
107 : }
108 0 : case SDL_MOUSEBUTTONDOWN:
109 : case SDL_MOUSEBUTTONUP:
110 : {
111 : // SET(obj, "which", (int)val.ev.button.which); // (not in wsdl.h)
112 0 : SET(obj, "button", (int)val.ev.button.button);
113 0 : SET(obj, "state", (int)val.ev.button.state);
114 0 : SET(obj, "x", (int)val.ev.button.x);
115 0 : SET(obj, "y", (int)val.ev.button.y);
116 0 : SET(obj, "clicks", (int)val.ev.button.clicks);
117 0 : break;
118 : }
119 8 : case SDL_HOTKEYPRESS:
120 : case SDL_HOTKEYDOWN:
121 : case SDL_HOTKEYUP:
122 : case SDL_HOTKEYPRESS_SILENT:
123 : case SDL_HOTKEYUP_SILENT:
124 : {
125 8 : SET(obj, "hotkey", static_cast<const char*>(val.ev.user.data1));
126 8 : break;
127 : }
128 : }
129 :
130 14 : ret.setObject(*obj);
131 : }
132 :
133 12 : template<> void Script::ToJSVal<IGUIObject*>(const ScriptRequest& UNUSED(rq), JS::MutableHandleValue ret, IGUIObject* const& val)
134 : {
135 12 : if (val == nullptr)
136 7 : ret.setNull();
137 : else
138 5 : ret.setObject(*val->GetJSObject());
139 12 : }
140 :
141 0 : template<> bool Script::FromJSVal<IGUIObject*>(const ScriptRequest& rq, JS::HandleValue v, IGUIObject*& out)
142 : {
143 0 : if (!v.isObject())
144 : {
145 0 : ScriptException::Raise(rq, "Value is not an IGUIObject.");
146 0 : return false;
147 : }
148 0 : out = IGUIProxyObject::FromPrivateSlot<IGUIObject>(v.toObjectOrNull());
149 0 : if (!out)
150 : {
151 0 : ScriptException::Raise(rq, "Value is not an IGUIObject.");
152 0 : return false;
153 : }
154 0 : return true;
155 : }
156 :
157 0 : template<> void Script::ToJSVal<CGUIString>(const ScriptRequest& rq, JS::MutableHandleValue ret, const CGUIString& val)
158 : {
159 0 : Script::ToJSVal(rq, ret, val.GetOriginalString());
160 0 : }
161 :
162 0 : template<> bool Script::FromJSVal<CGUIString>(const ScriptRequest& rq, JS::HandleValue v, CGUIString& out)
163 : {
164 0 : std::wstring val;
165 0 : if (!FromJSVal(rq, v, val))
166 0 : return false;
167 0 : out.SetValue(val);
168 0 : return true;
169 : }
170 :
171 0 : JSVAL_VECTOR(CVector2D)
172 0 : JSVAL_VECTOR(std::vector<CVector2D>)
173 0 : JSVAL_VECTOR(CGUIString)
174 :
175 0 : template<> void Script::ToJSVal<CGUIColor>(const ScriptRequest& rq, JS::MutableHandleValue ret, const CGUIColor& val)
176 : {
177 0 : ToJSVal<CColor>(rq, ret, val);
178 0 : }
179 :
180 : /**
181 : * The color depends on the predefined color database stored in the current GUI page.
182 : */
183 : template<> bool Script::FromJSVal<CGUIColor>(const ScriptRequest& rq, JS::HandleValue v, CGUIColor& out) = delete;
184 :
185 0 : template<> void Script::ToJSVal<CRect>(const ScriptRequest& rq, JS::MutableHandleValue ret, const CRect& val)
186 : {
187 0 : Script::CreateObject(
188 : rq,
189 : ret,
190 : "left", val.left,
191 : "right", val.right,
192 : "top", val.top,
193 : "bottom", val.bottom);
194 0 : }
195 :
196 0 : template<> void Script::ToJSVal<CGUISize>(const ScriptRequest& rq, JS::MutableHandleValue ret, const CGUISize& val)
197 : {
198 0 : val.ToJSVal(rq, ret);
199 0 : }
200 :
201 0 : template<> bool Script::FromJSVal<CGUISize>(const ScriptRequest& rq, JS::HandleValue v, CGUISize& out)
202 : {
203 0 : return out.FromJSVal(rq, v);
204 : }
205 :
206 0 : template<> void Script::ToJSVal<CGUIList>(const ScriptRequest& rq, JS::MutableHandleValue ret, const CGUIList& val)
207 : {
208 0 : ToJSVal(rq, ret, val.m_Items);
209 0 : }
210 :
211 0 : template<> bool Script::FromJSVal<CGUIList>(const ScriptRequest& rq, JS::HandleValue v, CGUIList& out)
212 : {
213 0 : return FromJSVal(rq, v, out.m_Items);
214 : }
215 :
216 0 : template<> void Script::ToJSVal<CGUISeries>(const ScriptRequest& rq, JS::MutableHandleValue ret, const CGUISeries& val)
217 : {
218 0 : ToJSVal(rq, ret, val.m_Series);
219 0 : }
220 :
221 0 : template<> bool Script::FromJSVal<CGUISeries>(const ScriptRequest& rq, JS::HandleValue v, CGUISeries& out)
222 : {
223 0 : return FromJSVal(rq, v, out.m_Series);
224 : }
225 :
226 0 : template<> void Script::ToJSVal<EVAlign>(const ScriptRequest& rq, JS::MutableHandleValue ret, const EVAlign& val)
227 : {
228 0 : std::string word;
229 0 : switch (val)
230 : {
231 0 : case EVAlign::TOP:
232 0 : word = "top";
233 0 : break;
234 :
235 0 : case EVAlign::BOTTOM:
236 0 : word = "bottom";
237 0 : break;
238 :
239 0 : case EVAlign::CENTER:
240 0 : word = "center";
241 0 : break;
242 :
243 0 : default:
244 0 : word = "error";
245 0 : ScriptException::Raise(rq, "Invalid EVAlign");
246 0 : break;
247 : }
248 0 : ToJSVal(rq, ret, word);
249 0 : }
250 :
251 0 : template<> bool Script::FromJSVal<EVAlign>(const ScriptRequest& rq, JS::HandleValue v, EVAlign& out)
252 : {
253 0 : std::string word;
254 0 : FromJSVal(rq, v, word);
255 :
256 0 : if (word == "top")
257 0 : out = EVAlign::TOP;
258 0 : else if (word == "bottom")
259 0 : out = EVAlign::BOTTOM;
260 0 : else if (word == "center")
261 0 : out = EVAlign::CENTER;
262 : else
263 : {
264 0 : out = EVAlign::TOP;
265 0 : LOGERROR("Invalid alignment (should be 'left', 'right' or 'center')");
266 0 : return false;
267 : }
268 0 : return true;
269 : }
270 :
271 0 : template<> void Script::ToJSVal<EAlign>(const ScriptRequest& rq, JS::MutableHandleValue ret, const EAlign& val)
272 : {
273 0 : std::string word;
274 0 : switch (val)
275 : {
276 0 : case EAlign::LEFT:
277 0 : word = "left";
278 0 : break;
279 0 : case EAlign::RIGHT:
280 0 : word = "right";
281 0 : break;
282 0 : case EAlign::CENTER:
283 0 : word = "center";
284 0 : break;
285 0 : default:
286 0 : word = "error";
287 0 : ScriptException::Raise(rq, "Invalid alignment (should be 'left', 'right' or 'center')");
288 0 : break;
289 : }
290 0 : ToJSVal(rq, ret, word);
291 0 : }
292 :
293 0 : template<> bool Script::FromJSVal<EAlign>(const ScriptRequest& rq, JS::HandleValue v, EAlign& out)
294 : {
295 0 : std::string word;
296 0 : FromJSVal(rq, v, word);
297 :
298 0 : if (word == "left")
299 0 : out = EAlign::LEFT;
300 0 : else if (word == "right")
301 0 : out = EAlign::RIGHT;
302 0 : else if (word == "center")
303 0 : out = EAlign::CENTER;
304 : else
305 : {
306 0 : out = EAlign::LEFT;
307 0 : LOGERROR("Invalid alignment (should be 'left', 'right' or 'center')");
308 0 : return false;
309 : }
310 0 : return true;
311 : }
312 :
313 0 : template<> void Script::ToJSVal<CGUISpriteInstance>(const ScriptRequest& rq, JS::MutableHandleValue ret, const CGUISpriteInstance& val)
314 : {
315 0 : ToJSVal(rq, ret, val.GetName());
316 0 : }
317 :
318 0 : template<> bool Script::FromJSVal<CGUISpriteInstance>(const ScriptRequest& rq, JS::HandleValue v, CGUISpriteInstance& out)
319 : {
320 0 : std::string name;
321 0 : if (!FromJSVal(rq, v, name))
322 0 : return false;
323 :
324 0 : out.SetName(name);
325 0 : return true;
326 : }
327 :
328 0 : template<> void Script::ToJSVal<CSize2D>(const ScriptRequest& rq, JS::MutableHandleValue ret, const CSize2D& val)
329 : {
330 0 : Script::CreateObject(rq, ret, "width", val.Width, "height", val.Height);
331 0 : }
332 :
333 0 : template<> bool Script::FromJSVal<CSize2D>(const ScriptRequest& rq, JS::HandleValue v, CSize2D& out)
334 : {
335 0 : if (!v.isObject())
336 : {
337 0 : LOGERROR("CSize2D value must be an object!");
338 0 : return false;
339 : }
340 :
341 0 : if (!FromJSProperty(rq, v, "width", out.Width))
342 : {
343 0 : LOGERROR("Failed to get CSize2D.Width property");
344 0 : return false;
345 : }
346 :
347 0 : if (!FromJSProperty(rq, v, "height", out.Height))
348 : {
349 0 : LOGERROR("Failed to get CSize2D.Height property");
350 0 : return false;
351 : }
352 :
353 0 : return true;
354 : }
355 :
356 0 : template<> void Script::ToJSVal<CVector2D>(const ScriptRequest& rq, JS::MutableHandleValue ret, const CVector2D& val)
357 : {
358 0 : Script::CreateObject(rq, ret, "x", val.X, "y", val.Y);
359 0 : }
360 :
361 0 : template<> bool Script::FromJSVal<CVector2D>(const ScriptRequest& rq, JS::HandleValue v, CVector2D& out)
362 : {
363 0 : if (!v.isObject())
364 : {
365 0 : LOGERROR("CVector2D value must be an object!");
366 0 : return false;
367 : }
368 :
369 0 : if (!FromJSProperty(rq, v, "x", out.X))
370 : {
371 0 : LOGERROR("Failed to get CVector2D.X property");
372 0 : return false;
373 : }
374 :
375 0 : if (!FromJSProperty(rq, v, "y", out.Y))
376 : {
377 0 : LOGERROR("Failed to get CVector2D.Y property");
378 0 : return false;
379 : }
380 :
381 0 : return true;
382 : }
383 :
384 : #undef SET
|