1 /*
2  *  Copyright (c) 2012-2016, 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 #ifndef H_GEOGRAM_GFX_GUI_APPLICATION_H
47 #define H_GEOGRAM_GFX_GUI_APPLICATION_H
48 
49 #include <geogram_gfx/basic/common.h>
50 #include <geogram_gfx/gui/events.h>
51 
52 #ifndef GEO_OS_ANDROID
53 #  define GEO_GLFW
54 #endif
55 
56 /**
57  * \file geogram_gfx/gui/application.h
58  * \brief Base class for all applications.
59  */
60 
61 namespace GEO {
62 
63     class Image;
64     class ApplicationData;
65 
66     /**
67      * \brief Base class for all applications.
68      * \details This class handles the cross-platform creation of a window,
69      *  OpenGL context, and ImGui. Client code may use SimpleApplication
70      *  instead.
71      */
72     class GEOGRAM_GFX_API Application {
73     public:
74 
75         /**
76          * \brief Application constructor.
77 	 * \param[in] name the name of the application
78          */
79          Application(const std::string& name);
80 
81         /**
82          * \brief Application destructor.
83          */
84          virtual ~Application();
85 
86         /**
87          * \brief Gets the instance.
88          * \return a pointer to the instance.
89          */
instance()90          static Application* instance() {
91 	     return instance_;
92 	 }
93 
94 
95         /**
96 	 * \brief Gets the name of this application.
97 	 * \return the name.
98 	 */
name()99          const std::string& name() const {
100 	     return name_;
101 	 }
102 
103 
104         /**
105          * \brief Starts the main event loop of the application.
106 	 * \param[in] argc , argv optional command line parameters. If specified
107 	 *  then they are used to initialize geogram, else geogram is supposed
108 	 *  to be already initialized by caller.
109          */
110          virtual void start(int argc=0, char** argv=nullptr);
111 
112         /**
113          * \brief Stops the application.
114          */
115         virtual void stop();
116 
117 	/**
118 	 * \brief Gets the style.
119 	 * \return a string with the current style;
120 	 */
get_style()121 	const std::string& get_style() const {
122 	    return style_;
123 	}
124 
125         /**
126          * \brief Sets the style of the application.
127 	 * \param[in] value one of Dark, Light, DarkGray, LightGray
128 	 * \see get_styles()
129          */
130         virtual void set_style(const std::string& value);
131 
132         /**
133 	 * \brief Gets the possible styles.
134 	 * \return A semi-colon separated list of the possible
135 	 *  styles.
136 	 */
137         static std::string get_styles();
138 
139         /**
140          * \brief Sets the font size.
141 	 * \param[in] value the font size.
142          */
143         void set_font_size(index_t value);
144 
145         /**
146 	 * \brief Gets the font size.
147 	 * \return the font size.
148 	 */
get_font_size()149         index_t get_font_size() const {
150 	    return font_size_;
151 	}
152 
153 	/**
154 	 * \brief Indicates that the main window should be redrawn.
155 	 */
156         virtual void update();
157 
158 	/**
159 	 * \brief Draws a dockspace that fills the current
160 	 *  window.
161 	 */
162 	void draw_dock_space();
163 
164 	/**
165 	 * \brief Lock updates.
166 	 * \details If this function is called, updates are ignored.
167 	 *  It is useful when a RenderingContext operation is occuring, to
168 	 *  prevent the Console for triggering a drawing operation.
169 	 */
lock_updates()170 	void lock_updates() {
171 	    ++nb_update_locks_;
172 	}
173 
174 	/**
175 	 * \brief Unlock updates.
176 	 */
unlock_updates()177 	void unlock_updates() {
178 	    // Note: Under Windows, when the Graphite window is iconified,
179 	    // it can happen that nb_update_locks_ is already 0 when
180 	    // reaching this point.
181 	    if(nb_update_locks_ > 0) {
182 		--nb_update_locks_;
183 	    }
184 	}
185 
186 	/**
187 	 * \brief Redraws the main window.
188 	 * \details This function is called by commands that animate
189 	 *  objects during computation, by the progress bar and by
190 	 *  console output.
191 	 */
192 	virtual void draw();
193 
194 	/**
195 	 * \brief Gets the global scaling to be applied to all GUI elements.
196 	 * \return 1.0 if the default font is used, more/less if a larger/
197 	 *  smaller font is used.
198 	 */
199 	double scaling() const;
200 
201 	/**
202 	 * \brief Sets full-screen mode.
203 	 * \details All arguments to zero sets default mode.
204 	 * \param[in] w , h width and height in pixels
205 	 * \param[in] hz refresh rate in Hz
206 	 * \param[in] monitor the id of the monitor
207 	 */
208 	void set_full_screen_mode(
209 	    index_t w=0, index_t h=0, index_t hz=0,
210 	    index_t monitor=0
211 	);
212 
213 	/**
214 	 * \brief Sets windowed mode.
215 	 * \param[in] w , h width and height in pixels. If
216 	 *  zero, use current dimensions.
217 	 */
218 	void set_windowed_mode(index_t w=0, index_t h=0);
219 
220 	/**
221 	 * \brief Lists the video modes that can be used for
222 	 *  set_full_screen_mode()
223 	 * \details The video modes are listed in the terminal.
224 	 */
225 	void list_video_modes();
226 
227 	/**
228 	 * \brief Iconifies this application.
229 	 */
230 	void iconify();
231 
232 	/**
233 	 * \brief Restores this application.
234 	 */
235 	void restore();
236 
237         /**
238          * \brief Sets the gui state.
239          * \param[in] x a string that encodes
240          *  the windows geometries and docking configuration
241          *  obtained through get_gui_state()
242          */
243 	void set_gui_state(std::string x);
244 
245         /**
246          * \brief Gets the gui state.
247          * \return a string that encodes
248          *  the windows geometries and docking configuration
249 	 */
250 	std::string get_gui_state() const;
251 
252 	/**
253 	 * \brief Sets full-screen mode.
254 	 * \param[in] x true if full-screen mode should be used,
255 	 *  false if windowed-mode should be used.
256 	 */
257 	void set_full_screen(bool x);
258 
259 	/**
260 	 * \brief Tests whether this application is in full-screen mode.
261 	 * \retval true if full-screen mode is used.
262 	 * \retval false if windowed mode is used.
263 	 */
264 	bool get_full_screen() const;
265 
266 	/**
267 	 * \brief Gets the width of the window.
268 	 * \return the width of the window in pixels.
269 	 */
get_width()270 	index_t get_width() const {
271 	    return width_;
272 	}
273 
274 	/**
275 	 * \brief Gets the height of the window.
276 	 * \return the height of the window in pixels.
277 	 */
get_height()278 	index_t get_height() const {
279 	    return height_;
280 	}
281 
282 	/**
283 	 * \brief Gets the width of the window.
284 	 * \return the width of the frame buffer in pixels.
285 	 */
get_frame_buffer_width()286 	index_t get_frame_buffer_width() const {
287 	    return frame_buffer_width_;
288 	}
289 
290 	/**
291 	 * \brief Gets the height of the window.
292 	 * \return the height of the frame buffer in pixels.
293 	 */
get_frame_buffer_height()294 	index_t get_frame_buffer_height() const {
295 	    return frame_buffer_height_;
296 	}
297 
298         /**
299          * \brief Sets whether drag and drop events should be
300          *  taken into account.
301          * \param[in] value true if drag and drop events should be taken into
302          *  account, false otherwise
303          */
set_accept_drops(bool value)304 	void set_accept_drops(bool value) {
305 	    accept_drops_ = value;
306 	}
307 
308         /**
309          * \brief Tests whether drag and drop events are taken into
310          *  account.
311          * \retval true if drag and drop events are taken into account
312          * \retval false otherwise
313          */
get_accept_drops()314 	bool get_accept_drops() const {
315 	    return accept_drops_;
316 	}
317 
318         /**
319 	 * \brief Sets the icon of the window.
320 	 * \param[in] image a pointer to the image to be used as the icon.
321 	 */
322         void set_window_icon(Image* image);
323 
324         /**
325 	 * \brief Callback called whenenver a mouse button changed.
326 	 * \param[in] button the button
327 	 * \param[in] action the action (one of
328 	 *  EVENT_ACTION_UP, EVENT_ACTION_DOWN, EVENT_ACTION_DRAG)
329 	 * \param[in] mods the current key modifiers (not implemented yet)
330 	 * \param[in] source the event source (one of EVENT_SOURCE_MOUSE,
331 	 *   EVENT_SOURCE_FINGER, EVENT_SOURCE_STYLUS)
332 	 */
333         virtual void mouse_button_callback(
334 	    int button, int action, int mods=0, int source=EVENT_SOURCE_MOUSE
335 	);
336 
337         /**
338 	 * \brief Callback called whenenver the mouse wheel is moved.
339 	 * \param[in] xoffset , yoffset wheel displacement
340 	 */
341         virtual void scroll_callback(double xoffset, double yoffset);
342 
343         /**
344 	 * \brief Callback called whenever the mouse cursor is moved.
345 	 * \param[in] x , y the new position of the mouse cursor.
346 	 * \param[in] source the event source (one of EVENT_SOURCE_MOUSE,
347 	 *   EVENT_SOURCE_FINGER, EVENT_SOURCE_STYLUS)
348 	 */
349         virtual void cursor_pos_callback(
350 	    double x, double y, int source=EVENT_SOURCE_MOUSE
351 	);
352 
353         /**
354 	 * \brief Callback called whenever files are dropped in the window.
355 	 * \param[in] nb number of files.
356 	 * \param[in] f the array of file names.
357 	 */
358         virtual void drop_callback(int nb, const char** f);
359 
360         /**
361 	 * \brief Callback called whenever a key is pushed (high level version)
362 	 * \param[in] c the ASCII code of the character that corresponds to the
363 	 *  pushed key.
364 	 */
365         virtual void char_callback(unsigned int c);
366 
367         /**
368 	 * \brief Callback called whenever a key is pushed (low level version)
369 	 * \param[in] key key code (window system specific)
370 	 * \param[in] scancode scan code (window system specific)
371 	 * \param[in] action push or release (window system specific)
372 	 * \param[in] mods current key modifieds (window system specific)
373 	 */
374         virtual void key_callback(int key, int scancode, int action, int mods);
375 
376         /**
377          * \brief Restarts the gui.
378 	 * \details A flag is set and the gui is restarted at the next frame.
379 	 */
restart_gui()380         void restart_gui() {
381 	    ImGui_restart_ = true;
382 	}
383 
384         /**
385 	 * \brief Gets a pointer to the implementation-specific data.
386 	 * \details For internal use only.
387 	 * \return a pointer to the implementation-specific data.
388 	 */
impl_data()389         ApplicationData* impl_data() {
390 	    return data_;
391 	}
392 
393         /**
394 	 * \brief Gets a pointer to the implementation-specific window.
395 	 * \details For internal use only.
396 	 * \return a pointer to the implementation-specific window.
397 	 */
398         void* impl_window();
399 
400         /**
401 	 * \brief MacOS non-sense
402 	 * \return a scaling factor between real pixels and logical
403 	 *  pixels or something, well I do not understand. Sometimes
404 	 *  you need to multiply by it, sometimes to divide, and
405 	 *  sometimes you need to use pixel_ratio() instead.
406 	 */
hidpi_scaling()407         double hidpi_scaling() const {
408 	    return hidpi_scaling_;
409 	}
410 
411         /**
412 	 * \brief More MacOS non-sense
413 	 * \return something like hidpi_scaling(), that is a scaling
414 	 *  factor between real pixels and logical
415 	 *  pixels or something, well I do not understand.
416 	 *  Sometimes you need to multiply by it, sometimes to divide,
417 	 *  and sometimes you need to use hidpi_scaling() instead.
418 	 */
pixel_ratio()419         double pixel_ratio() const {
420 	    return pixel_ratio_;
421 	}
422 
423         /**
424 	 * \brief Used internally.
425 	 */
reset_soft_keyboard_flag()426         void reset_soft_keyboard_flag() {
427 	    soft_keyboard_visible_ = false;
428 	}
429 
430     protected:
431 
432         /**
433          * \brief Converts a key to a symbolic string with the name of the key.
434          * \param[in] key the key.
435          * \return a string with the symbolic name of the key.
436          */
437         const char* key_to_string(int key);
438 
439 	/**
440 	 * \brief This function is called when the GUI should be redisplayed.
441 	 * \details This function is meant to be overloaded by subclasses.
442 	 *   default implementation does nothing.
443 	 */
444         virtual void draw_gui();
445 
446 	/**
447 	 * \brief This function is called when the 3d content should be
448 	 *  redisplayed.
449 	 * \details This function is meant to be overloaded by subclasses.
450 	 *   default implementation does nothing.
451 	 */
452         virtual void draw_graphics();
453 
454         /**
455 	 * \brief This function is called before starting drawing operations.
456 	 * \details Some implementations use it to initialize / restore graphic
457 	 *  objects.
458 	 */
459         virtual void pre_draw();
460 
461         /**
462 	 * \brief This function is called after all drawing operations.
463 	 * \details It can be used to execute queued commands.
464 	 */
465         virtual void post_draw();
466 
467 	/**
468 	 * \brief Tests whether the window needs to be redrawn.
469 	 * \retval true if the window needs to be redrawn.
470 	 * \retval false if the window is up to date.
471 	 */
472 	virtual bool needs_to_redraw() const;
473 
474 	/**
475 	 * \brief Creates the window using GLFW.
476 	 */
477 	virtual void create_window();
478 
479 	/**
480 	 * \brief Deletes the window created by GLFW.
481 	 */
482 	virtual void delete_window();
483 
484 	/**
485 	 * \brief Called whenever window size changes
486 	 * \param[in] w , h the new window width and height in pixels.
487 	 * \param[in] fb_w , fb_h the new framebuffer width and height in
488 	 *   pixels.
489 	 * \details Called whenenver the size of the window does not
490 	 *  match the current size.
491 	 */
492          virtual void resize(index_t w, index_t h, index_t fb_w, index_t fb_h);
493 
494 	/**
495 	 * \brief Draws one frame.
496 	 * \details This triggers a GUI and/or a scene update as needed.
497 	 */
498 	virtual void one_frame();
499 
500 	/**
501 	 * \brief Enters the main application loop.
502 	 * \details create_window() needs to be called before.
503 	 *  This initializes OpenGL and ImGui before the first frame is
504 	 *  displayed.
505 	 */
506 	virtual void main_loop();
507 
508 	/**
509 	 * \brief Initializes OpenGL and GLUP objects.
510 	 */
511 	virtual void GL_initialize();
512 
513 	/**
514 	 * \brief Deallocates OpenGL and GLUP objects.
515 	 */
516 	virtual void GL_terminate();
517 
518 	/**
519 	 * \brief Initializes the ImGui library.
520 	 */
521 	virtual void ImGui_initialize();
522 
523 	/**
524 	 * \brief Loads the fonts in ImGui.
525 	 */
526 	virtual void ImGui_load_fonts();
527 
528 	/**
529 	 * \brief Deallocates objects used by the ImGui library.
530 	 */
531 	virtual void ImGui_terminate();
532 
533 	/**
534 	 * \brief Notifies ImGui that a new frame has just started.
535 	 */
536 	virtual void ImGui_new_frame();
537 
538         /*
539 	 * \param[in] argc , argv command line parameters, used
540 	 *  to initialize geogram.
541 	 */
542         virtual void geogram_initialize(int argc, char** argv);
543 
544         /**
545          * \brief Initializes the callbacks if not already initialized.
546          */
547         void callbacks_initialize();
548 
549         /**
550 	 * \brief Gets all the filenames specified on the command line.
551 	 * \return a const reference to a vector of strings with the filenames.
552 	 */
filenames()553         const std::vector<std::string>& filenames() const  {
554 	    return filenames_;
555 	}
556 
animate()557         bool animate() const {
558 	    return animate_;
559 	}
560 
animate_ptr()561         bool* animate_ptr() {
562 	    return &animate_;
563 	}
564 
start_animation()565         void start_animation() {
566 	    animate_ = true;
567 	}
568 
stop_animation()569         void stop_animation() {
570 	    animate_ = false;
571 	}
572 
573       private:
574         static Application* instance_; /**< a pointer to the instance */
575         ApplicationData* data_;        /**< implementation dependent */
576         index_t width_;                /**< window width */
577         index_t height_;               /**< window height */
578         index_t frame_buffer_width_;   /**< frame buffer width (glViewport) */
579         index_t frame_buffer_height_;  /**< frame buffer height (glViewport) */
580         bool in_main_loop_;            /**< main loop is running */
581         bool accept_drops_;            /**< app. accepts dropping files */
582         double scaling_;               /**< global scaling for to all sizes */
583         index_t nb_update_locks_;      /**< lock graphic updates */
584         std::string style_;            /**< ImGui style (Dark, Light, ...) */
585         bool ImGui_restart_;           /**< ImGui needs to be restarted */
586         bool ImGui_reload_font_;       /**< font size has changed */
587         bool ImGui_initialized_;       /**< ImGui was initialized */
588         index_t font_size_;            /**< current font size */
589         index_t nb_frames_update_;     /**< if 0, take a small sleep */
590         double hidpi_scaling_;         /**< for retina displays */
591         double pixel_ratio_;           /**< for retina displays */
592         std::string name_;             /**< application name */
593         bool currently_drawing_gui_;   /**< currently drawing ImGui elements */
594         std::vector<std::string> filenames_; /**< from the command line */
595         bool animate_;                 /**< true if drawing always */
596 
597 
598       protected:
599         bool ImGui_firsttime_init_;  /**< true if ImGui was once initialized */
600         bool menubar_visible_;
601         bool phone_screen_;          /**< true if running on a phone */
602         bool soft_keyboard_visible_;
603 
604 #ifdef GEO_OS_EMSCRIPTEN
605        friend void emscripten_one_frame();
606 #endif
607     };
608 
609 }
610 
611 #endif
612 
613