1 /*
2  *  Copyright (c) 2012-2014, Bruno Levy
3  *  All rights reserved.
4  *
5  *  Redistribution and use in source and binary forms, with or without
6  *  modification, are permitted provided that the following conditions are met:
7  *
8  *  * Redistributions of source code must retain the above copyright notice,
9  *  this list of conditions and the following disclaimer.
10  *  * Redistributions in binary form must reproduce the above copyright notice,
11  *  this list of conditions and the following disclaimer in the documentation
12  *  and/or other materials provided with the distribution.
13  *  * Neither the name of the ALICE Project-Team nor the names of its
14  *  contributors may be used to endorse or promote products derived from this
15  *  software without specific prior written permission.
16  *
17  *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
18  *  AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19  *  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20  *  ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
21  *  LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
22  *  CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
23  *  SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
24  *  INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
25  *  CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
26  *  ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
27  *  POSSIBILITY OF SUCH DAMAGE.
28  *
29  *  If you modify this software, you should include a notice giving the
30  *  name of the person performing the modification, the date of modification,
31  *  and the reason for such modification.
32  *
33  *  Contact: Bruno Levy
34  *
35  *     Bruno.Levy@inria.fr
36  *     http://www.loria.fr/~levy
37  *
38  *     ALICE Project
39  *     LORIA, INRIA Lorraine,
40  *     Campus Scientifique, BP 239
41  *     54506 VANDOEUVRE LES NANCY CEDEX
42  *     FRANCE
43  *
44  */
45 
46 #include <geogram_gfx/gui/simple_application.h>
47 #include <geogram_gfx/lua/lua_glup.h>
48 #include <geogram/lua/lua_io.h>
49 #include <geogram/basic/command_line.h>
50 #include <geogram/basic/file_system.h>
51 #include <algorithm>
52 
53 extern "C" {
54 #include <geogram/third_party/lua/lua.h>
55 #include <geogram/third_party/lua/lauxlib.h>
56 #include <geogram/third_party/lua/lualib.h>
57 }
58 
59 extern void register_embedded_lua_files(void);
60 
61 namespace {
62 
63     using namespace GEO;
64 
65     /**
66      * \brief An application that demonstrates both
67      *  GLUP primitives and glup_viewer application
68      *  framework.
69      */
70     class GeoCodApplication : public SimpleApplication {
71     public:
72 
73         /**
74          * \brief GeoCodApplication constructor.
75          */
GeoCodApplication()76         GeoCodApplication() : SimpleApplication("geocod") {
77 	    set_default_filename("hello.lua");
78 	    init_graphics_called_ = false;
79 	    use_text_editor_ = true;
80 	    text_editor_was_visible_ = false;
81             // Define the 3d region that we want to display
82             // (xmin, ymin, zmin, xmax, ymax, zmax)
83             set_region_of_interest(0.0, 0.0, 0.0, 1.0, 1.0, 1.0);
84 	    register_embedded_lua_files();
85 	    exec_command("require(\"preamble\")");
86 	    console_->show_command_prompt();
87         }
88 
89         /**
90          * \brief GeoCodApplication destructor.
91          */
~GeoCodApplication()92 	~GeoCodApplication() override {
93         }
94 
95         /**
96          * \brief Displays and handles the GUI for object properties.
97          * \details Overloads Application::draw_object_properties().
98          */
draw_object_properties()99 	void draw_object_properties() override {
100 	    if(!lua_error_occured_) {
101 		exec_command("imgui.draw_object_properties()");
102 	    }
103         }
104 
105         /**
106          * \brief Draws the scene according to currently set primitive and
107          *  drawing modes.
108          */
draw_scene()109 	void draw_scene() override {
110 	    if(text_editor_was_visible_ && !text_editor_visible_) {
111 		run_program();
112 	    }
113 	    if(!lua_error_occured_) {
114 		exec_command("GLUP.draw_scene()");
115 	    }
116 	    text_editor_was_visible_ = text_editor_visible_;
117         }
118 
119         /**
120          * \brief Initializes graphics objects.
121          * \details This function overloads Application::init_graphics(). It
122          *  is called as soon as the OpenGL context is ready for rendering. It
123          *  is meant to initialize the graphic objects used by the application.
124          */
GL_initialize()125 	void GL_initialize() override {
126             SimpleApplication::GL_initialize();
127 	    if(!lua_error_occured_) {
128 		exec_command("GLUP.init_graphics()");
129 	    }
130 	    init_graphics_called_ = true;
131         }
132 
embedded_files_menu(const std::string & prefix)133 	void embedded_files_menu(const std::string& prefix) {
134 	    std::vector<std::string> embedded_files;
135 	    list_embedded_lua_files(embedded_files);
136 	    std::sort(embedded_files.begin(), embedded_files.end());
137 	    for(index_t i=0; i<embedded_files.size(); ++i) {
138 		if(String::string_starts_with(
139 		       embedded_files[i],prefix) &&
140 		   ImGui::MenuItem(embedded_files[i].c_str())
141 		) {
142 		    const char* data;
143 		    get_embedded_lua_file(embedded_files[i].c_str(), &data);
144 		    text_editor_.load_data(data);
145 		    run_program();
146 		    current_file_ = "";
147 		}
148 	    }
149 	}
150 
151 
draw_fileops_menu()152 	virtual void draw_fileops_menu() override {
153 	    if(ImGui::BeginMenu("New...")) {
154 		if(ImGui::MenuItem("empty file")) {
155 		    text_editor_.clear();
156 		    current_file_ = "";
157 		    exec_command("require(\"preamble\")");
158 		}
159 		ImGui::Separator();
160 		ImGui::MenuItem("From template...", nullptr, false, false);
161 		embedded_files_menu("templates/");
162 		ImGui::EndMenu();
163 	    }
164 	    if(ImGui::BeginMenu("Load example...")) {
165 		embedded_files_menu("examples/");
166 		ImGui::EndMenu();
167 	    }
168 	    if(ImGui::BeginMenu("Load game...")) {
169 		embedded_files_menu("games/");
170 		ImGui::EndMenu();
171 	    }
172 	    /*
173 	    if(ImGui::BeginMenu("Load Shift and Tab\'s adventure...")) {
174 		embedded_files_menu("book/");
175 		ImGui::EndMenu();
176 	    }
177 	    */
178 	    if(ImGui::BeginMenu("Load internal lib...")) {
179 		ImGui::MenuItem("These files are those", nullptr, false, false);
180 		ImGui::MenuItem(
181 		    "that are read by require(\"...\")",
182 		    nullptr, false, false
183 	        );
184 		ImGui::MenuItem("You cannot modify them",
185 				nullptr, false, false);
186 		ImGui::MenuItem("(but you can take a look)",
187 				nullptr, false, false);
188 		ImGui::Separator();
189 		embedded_files_menu("lib/");
190 		ImGui::EndMenu();
191 	    }
192 	    if(ImGui::MenuItem(
193 		   "Run program",
194 		   phone_screen_ ? nullptr : "[F5]"
195 	    )) {
196 		run_program();
197 	    }
198 	}
199 
run_program()200 	void run_program() {
201 	    exec_command("require(\"preamble\")");
202 	    if(exec_command(text_editor_.text().c_str())) {
203 		Logger::out("LUA") << "Program is OK." << std::endl;
204 	    } else {
205 		adjust_lua_glup_state(lua_state_);
206 	    }
207 	    if(!lua_error_occured_ && init_graphics_called_) {
208 		exec_command("GLUP.init_graphics()");
209 	    }
210 	    start_animation();
211 	}
212 
213         /**
214          * \brief Draws the application menus.
215          * \details This function overloads
216          *  Application::draw_application_menus(). It can be used to create
217          *  additional menus in the main menu bar.
218          */
draw_application_menus()219 	void draw_application_menus() override {
220 	    if(!lua_error_occured_) {
221 		exec_command("imgui.draw_application_menus()");
222 	    }
223 	}
224 
225 	/**
226 	 * \copydoc Application::load()
227 	 */
load(const std::string & filename)228 	bool load(const std::string& filename) override {
229 	    text_editor_.load(filename);
230 	    run_program();
231 	    current_file_ = filename;
232 	    return true;
233 	}
234 
235 	/**
236 	 * \copydoc Application::save()
237 	 */
save(const std::string & filename)238 	bool save(const std::string& filename) override {
239 	    text_editor_.save(filename);
240 	    current_file_ = filename;
241 	    return true;
242 	}
243 
244 	/**
245 	 * \copydoc Application::supported_read_file_extensions()
246 	 */
supported_read_file_extensions()247 	std::string supported_read_file_extensions() override {
248 	    return "lua";
249 	}
250 
251 	/**
252 	 * \copydoc Application::supported_write_file_extensions()
253 	 */
supported_write_file_extensions()254 	std::string supported_write_file_extensions() override {
255 	    return "lua";
256 	}
257 
258 	/**
259 	 * \copydoc SimpleApplication::key_callback
260 	 */
key_callback(int key,int scancode,int action,int mods)261 	void key_callback(
262 	    int key, int scancode, int action, int mods
263 	) override {
264 	    SimpleApplication::key_callback(key, scancode, action, mods);
265 	    const char* k_str = key_to_string(key);
266 	    // Get string from printable character
267 	    // (note: with GLFW, this does not interpret the key mapping,
268 	    //  so Q and A are swapped on a French keyboard...)
269 	    if(*k_str == '\0') {
270 		static char buff[2];
271 		k_str = buff;
272 		buff[0] = char(key);
273 		buff[1] = '\0';
274 	    }
275 	    if(action == 1) {
276 		on_key_pressed(k_str);
277 	    } else if(action == 0) {
278 		on_key_released(k_str);
279 	    }
280 	}
281 
on_key_pressed(const char * c)282 	bool on_key_pressed(const char* c) {
283 	    if(!strcmp(c,"F5")) {
284 		run_program();
285 	    } else if(!strcmp(c,"F2") && current_file_ != "") {
286 		if(save(current_file_)) {
287 		    Logger::out("I/O")
288 			<< "Saved " << current_file_ << std::endl;
289 		}
290 	    } else {
291 		exec_command(
292 		    (
293 			std::string("imgui.on_key_pressed(\"") + c + "\")"
294 			).c_str()
295 		    );
296 	    }
297 	    return true;
298 	}
299 
on_key_released(const char * c)300 	bool on_key_released(const char* c) {
301 	    exec_command(
302 		(
303 		    std::string("imgui.on_key_released(\"") + c + "\")"
304 		).c_str()
305 	    );
306 	    return true;
307 	}
308 
309     protected:
draw_viewer_properties()310 	void draw_viewer_properties() override {
311 	    if(ImGui::Button(
312 		   "run",
313 		   ImVec2(-ImGui::GetContentRegionAvail().x/2.0f,0.0f))
314 	    ) {
315 		run_program();
316 	    }
317 	    ImGui::SameLine();
318 	    if(ImGui::Button("edit", ImVec2(-1.0f, 0.0f))) {
319                 text_editor_visible_ = !text_editor_visible_;
320 	    }
321             ImGui::Separator();
322 	    SimpleApplication::draw_viewer_properties();
323 	}
324 
325     private:
326 	bool init_graphics_called_;
327 	bool text_editor_was_visible_;
328     };
329 
330 }
331 
main(int argc,char ** argv)332 int main(int argc, char** argv) {
333     GeoCodApplication app;
334     app.start(argc, argv);
335     return 0;
336 }
337