1 /*
2 * Copyright (C) 2006-2019 Christopho, Solarus - http://www.solarus-games.org
3 *
4 * Solarus 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 3 of the License, or
7 * (at your option) any later version.
8 *
9 * Solarus 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 along
15 * with this program. If not, see <http://www.gnu.org/licenses/>.
16 */
17 #include "solarus/core/CurrentQuest.h"
18 #include "solarus/core/Size.h"
19 #include "solarus/graphics/SoftwareVideoMode.h"
20 #include "solarus/graphics/Video.h"
21 #include "solarus/lua/LuaContext.h"
22 #include "solarus/lua/LuaTools.h"
23 #include <lua.hpp>
24
25 namespace Solarus {
26
27 /**
28 * Name of the Lua table representing the video module.
29 */
30 const std::string LuaContext::video_module_name = "sol.video";
31
32 /**
33 * \brief Initializes the video features provided to Lua.
34 */
register_video_module()35 void LuaContext::register_video_module() {
36
37 std::vector<luaL_Reg> functions = {
38 { "get_window_title", video_api_get_window_title },
39 { "set_window_title", video_api_set_window_title },
40 { "get_mode", video_api_get_mode },
41 { "set_mode", video_api_set_mode },
42 { "switch_mode", video_api_switch_mode },
43 { "is_mode_supported", video_api_is_mode_supported },
44 { "get_modes", video_api_get_modes },
45 { "is_fullscreen", video_api_is_fullscreen },
46 { "set_fullscreen", video_api_set_fullscreen },
47 { "is_cursor_visible", video_api_is_cursor_visible },
48 { "set_cursor_visible", video_api_set_cursor_visible },
49 { "get_quest_size", video_api_get_quest_size },
50 { "get_window_size", video_api_get_window_size },
51 { "set_window_size", video_api_set_window_size },
52 { "reset_window_size", video_api_reset_window_size }
53 };
54 if (CurrentQuest::is_format_at_least({ 1, 6 })) {
55 functions.insert(functions.end(), {
56 { "get_shader", video_api_get_shader },
57 { "set_shader", video_api_set_shader},
58 });
59 }
60 register_functions(video_module_name, functions);
61 lua_getglobal(current_l, "sol");
62 // ... sol
63 lua_getfield(current_l, -1, "video");
64 // ... sol video
65 lua_setfield(current_l, LUA_REGISTRYINDEX, video_module_name.c_str());
66 // ... sol
67 lua_pop(current_l, 1);
68 }
69
70 /**
71 * \brief Pushes the sol.video table onto the stack.
72 * \param l A Lua state.
73 */
push_video(lua_State * l)74 void LuaContext::push_video(lua_State* l) {
75 lua_getfield(l, LUA_REGISTRYINDEX, video_module_name.c_str());
76 }
77
78 /**
79 * \brief Implementation of sol.video.get_window_title().
80 * \param l the Lua context that is calling this function
81 * \return number of values to return to Lua
82 */
video_api_get_window_title(lua_State * l)83 int LuaContext::video_api_get_window_title(lua_State* l) {
84
85 return state_boundary_handle(l, [&] {
86 const std::string& window_title =
87 Video::get_window_title();
88
89 push_string(l, window_title);
90 return 1;
91 });
92 }
93
94 /**
95 * \brief Implementation of sol.video.set_window_title().
96 * \param l the Lua context that is calling this function
97 * \return number of values to return to Lua
98 */
video_api_set_window_title(lua_State * l)99 int LuaContext::video_api_set_window_title(lua_State* l) {
100
101 return state_boundary_handle(l, [&] {
102 const std::string& window_title = LuaTools::check_string(l, 1);
103
104 Video::set_window_title(window_title);
105 return 1;
106 });
107 }
108
109 /**
110 * \brief Implementation of sol.video.get_mode().
111 * \param l the Lua context that is calling this function
112 * \return number of values to return to Lua
113 */
video_api_get_mode(lua_State * l)114 int LuaContext::video_api_get_mode(lua_State* l) {
115
116 return state_boundary_handle(l, [&] {
117
118 get().warning_deprecated(
119 { 1, 6 },
120 "sol.video.get_mode()",
121 "Use sol.video.get_shader() instead."
122 );
123
124 const SoftwareVideoMode& mode = Video::get_video_mode();
125
126 push_string(l, mode.get_name());
127 return 1;
128 });
129 }
130
131 /**
132 * \brief Implementation of sol.video.set_mode().
133 * \param l the Lua context that is calling this function
134 * \return Number of values to return to Lua.
135 */
video_api_set_mode(lua_State * l)136 int LuaContext::video_api_set_mode(lua_State* l) {
137
138 return state_boundary_handle(l, [&] {
139
140 get().warning_deprecated(
141 { 1, 6 },
142 "sol.video.set_mode()",
143 "Use sol.video.set_shader() instead."
144 );
145
146 std::string mode_name = LuaTools::check_string(l, 1);
147 const SoftwareVideoMode* mode = Video::get_video_mode_by_name(mode_name);
148
149 if (mode != nullptr && Video::get_video_mode().get_name() != mode_name) {
150 Video::set_video_mode(*mode);
151 }
152
153 return 0;
154 });
155 }
156
157 /**
158 * \brief Implementation of sol.video.switch_mode().
159 * \param l the Lua context that is calling this function
160 * \return number of values to return to Lua
161 */
video_api_switch_mode(lua_State * l)162 int LuaContext::video_api_switch_mode(lua_State* l) {
163
164 return state_boundary_handle(l, [&] {
165
166 get().warning_deprecated(
167 { 1, 6 },
168 "sol.video.switch_mode()",
169 "Use sol.video.set_shader() instead."
170 );
171
172 Video::switch_video_mode();
173
174 return 0;
175 });
176 }
177
178 /**
179 * \brief Implementation of sol.video.get_modes().
180 * \param l the Lua context that is calling this function
181 * \return number of values to return to Lua
182 */
video_api_get_modes(lua_State * l)183 int LuaContext::video_api_get_modes(lua_State* l) {
184
185 return state_boundary_handle(l, [&] {
186
187 get().warning_deprecated(
188 { 1, 6 },
189 "sol.video.get_modes()",
190 "Use sol.main.get_resource_ids(\"shader\") instead."
191 );
192
193 const std::vector<const SoftwareVideoMode*>& modes =
194 Video::get_video_modes();
195
196 lua_newtable(l);
197
198 int i = 1;
199 for (const SoftwareVideoMode* mode: modes) {
200 push_string(l, mode->get_name());
201 lua_rawseti(l, -2, i);
202 ++i;
203 }
204
205 return 1;
206 });
207 }
208
209 /**
210 * \brief Implementation of sol.video.is_mode_supported().
211 * \param l the Lua context that is calling this function
212 * \return number of values to return to Lua
213 */
video_api_is_mode_supported(lua_State * l)214 int LuaContext::video_api_is_mode_supported(lua_State* l) {
215
216 return state_boundary_handle(l, [&] {
217
218 get().warning_deprecated(
219 { 1, 6 },
220 "sol.video.is_mode_supported()",
221 "Use sol.shader.create() instead."
222 );
223
224 std::string mode_name = LuaTools::check_string(l, 1);
225 const SoftwareVideoMode* mode = Video::get_video_mode_by_name(mode_name);
226
227 bool supported = mode != nullptr && Video::is_mode_supported(*mode);
228
229 lua_pushboolean(l, supported);
230 return 1;
231 });
232 }
233
234 /**
235 * \brief Implementation of sol.video.is_fullscreen().
236 * \param l the Lua context that is calling this function
237 * \return number of values to return to Lua
238 */
video_api_is_fullscreen(lua_State * l)239 int LuaContext::video_api_is_fullscreen(lua_State* l) {
240
241 return state_boundary_handle(l, [&] {
242 bool fullscreen = Video::is_fullscreen();
243
244 lua_pushboolean(l, fullscreen);
245 return 1;
246 });
247 }
248
249 /**
250 * \brief Implementation of sol.video.set_fullscreen().
251 * \param l the Lua context that is calling this function
252 * \return number of values to return to Lua
253 */
video_api_set_fullscreen(lua_State * l)254 int LuaContext::video_api_set_fullscreen(lua_State* l) {
255
256 return state_boundary_handle(l, [&] {
257 bool fullscreen = LuaTools::opt_boolean(l, 1, true);
258
259 Video::set_fullscreen(fullscreen);
260
261 return 0;
262 });
263 }
264
265 /**
266 * \brief Implementation of sol.video.is_cursor_visible().
267 * \param l the Lua context that is calling this function
268 * \return number of values to return to Lua
269 */
video_api_is_cursor_visible(lua_State * l)270 int LuaContext::video_api_is_cursor_visible(lua_State *l) {
271
272 return state_boundary_handle(l, [&] {
273 bool visible_cursor = Video::is_cursor_visible();
274
275 lua_pushboolean(l, visible_cursor);
276 return 1;
277 });
278 }
279
280 /**
281 * \brief Implementation of sol.video.set_fullscreen().
282 * \param l the Lua context that is calling this function
283 * \return number of values to return to Lua
284 */
video_api_set_cursor_visible(lua_State * l)285 int LuaContext::video_api_set_cursor_visible(lua_State* l) {
286
287 return state_boundary_handle(l, [&] {
288 bool visible_cursor = LuaTools::opt_boolean(l, 1, true);
289
290 Video::set_cursor_visible(visible_cursor);
291
292 return 0;
293 });
294 }
295
296 /**
297 * \brief Implementation of sol.video.get_quest_size().
298 * \param l the Lua context that is calling this function
299 * \return number of values to return to Lua
300 */
video_api_get_quest_size(lua_State * l)301 int LuaContext::video_api_get_quest_size(lua_State* l) {
302
303 return state_boundary_handle(l, [&] {
304 const Size& quest_size = Video::get_quest_size();
305
306 lua_pushinteger(l, quest_size.width);
307 lua_pushinteger(l, quest_size.height);
308 return 2;
309 });
310 }
311
312 /**
313 * \brief Implementation of sol.video.get_window_size().
314 * \param l The Lua context that is calling this function.
315 * \return Number of values to return to Lua.
316 */
video_api_get_window_size(lua_State * l)317 int LuaContext::video_api_get_window_size(lua_State* l) {
318
319 return state_boundary_handle(l, [&] {
320 const Size& window_size = Video::get_window_size();
321
322 lua_pushinteger(l, window_size.width);
323 lua_pushinteger(l, window_size.height);
324 return 2;
325 });
326 }
327
328 /**
329 * \brief Implementation of sol.video.set_window_size().
330 * \param l The Lua context that is calling this function.
331 * \return Number of values to return to Lua.
332 */
video_api_set_window_size(lua_State * l)333 int LuaContext::video_api_set_window_size(lua_State* l) {
334
335 return state_boundary_handle(l, [&] {
336 int width = LuaTools::check_int(l, 1);
337 int height = LuaTools::check_int(l, 2);
338
339 if (width <= 0) {
340 LuaTools::arg_error(l, 1, "Window width must be positive");
341 }
342 if (height <= 0) {
343 LuaTools::arg_error(l, 2, "Window height must be positive");
344 }
345
346 Video::set_window_size(Size(width, height));
347
348 return 0;
349 });
350 }
351
352 /**
353 * \brief Implementation of sol.video.reset_window_size().
354 * \param l The Lua context that is calling this function.
355 * \return Number of values to return to Lua.
356 */
video_api_reset_window_size(lua_State * l)357 int LuaContext::video_api_reset_window_size(lua_State* l) {
358
359 return state_boundary_handle(l, [&] {
360 \
361 get().warning_deprecated(
362 { 1, 6 },
363 "sol.video.reset_window_size()",
364 "Use sol.video.set_window_size() instead."
365 );
366
367 Video::reset_window_size();
368
369 return 0;
370 });
371 }
372
373 /**
374 * \brief Implementation of sol.video.get_shader().
375 * \param l the Lua context that is calling this function
376 * \return number of values to return to Lua
377 */
video_api_get_shader(lua_State * l)378 int LuaContext::video_api_get_shader(lua_State* l) {
379
380 return state_boundary_handle(l, [&] {
381
382 const ShaderPtr& shader = Video::get_shader();
383
384 if (shader == nullptr) {
385 lua_pushnil(l);
386 }
387 else {
388 push_shader(l, *shader);
389 }
390 return 1;
391 });
392 }
393
394 /**
395 * \brief Implementation of sol.video.set_shader().
396 * \param l the Lua context that is calling this function
397 * \return Number of values to return to Lua.
398 */
video_api_set_shader(lua_State * l)399 int LuaContext::video_api_set_shader(lua_State* l) {
400
401 return state_boundary_handle(l, [&] {
402
403 ShaderPtr shader = nullptr;
404 if (!lua_isnil(l, 1)) {
405 if (is_shader(l, 1)) {
406 shader = check_shader(l, 1);
407 }
408 else {
409 LuaTools::type_error(l, 2, "shader or nil");
410 }
411 }
412
413 Video::set_shader(shader);
414
415 return 0;
416 });
417 }
418
419 /**
420 * \brief Calls sol.video.on_draw() if it exists.
421 * \param screen The destination surface representing the screen.
422 */
video_on_draw(const SurfacePtr & screen)423 void LuaContext::video_on_draw(const SurfacePtr &screen) {
424
425 if (!CurrentQuest::is_format_at_least({ 1, 6 })) {
426 return;
427 }
428 push_video(current_l);
429 on_draw(screen);
430 lua_pop(current_l, 1);
431 }
432
433 }
434
435