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