1 //
2 //   Copyright (C) 2005, 2006, 2007, 2008, 2009, 2010,
3 //   2011 Free Software Foundation, Inc
4 //
5 // This program is free software; you can redistribute it and/or modify
6 // it under the terms of the GNU General Public License as published by
7 // the Free Software Foundation; either version 3 of the License, or
8 // (at your option) any later version.
9 //
10 // This program is distributed in the hope that it will be useful,
11 // but WITHOUT ANY WARRANTY; without even the implied warranty of
12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 // GNU General Public License for more details.
14 //
15 // You should have received a copy of the GNU General Public License
16 // along with this program; if not, write to the Free Software
17 // Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
18 
19 #ifndef GNASH_GUI_H
20 #define GNASH_GUI_H
21 
22 #ifdef HAVE_CONFIG_H
23 #include "gnashconfig.h"
24 #endif
25 
26 #include <boost/intrusive_ptr.hpp>
27 #include <memory>
28 #include <string>
29 #include <map>
30 #include <utility>
31 #include <functional>
32 
33 #include "snappingrange.h"  // for InvalidatedRanges
34 #include "GnashKey.h"
35 #include "VirtualClock.h"
36 #include "SystemClock.h"
37 #include "GnashEnums.h"
38 #include "movie_root.h"
39 
40 #ifdef USE_SWFTREE
41 #include "tree.hh" // for tree
42 #endif
43 
44 // Define this to enable fps debugging without touching
45 // gnashconfig.h
46 //#define GNASH_FPS_DEBUG
47 
48 /// Define this to disable region updates debugging altogether. If undefined,
49 /// debugging will be a runtime option. The flag and flag-setting functions
50 /// will not be disabled (too ugly).
51 ///
52 /// This should go in gnashconfig.h
53 ///
54 /// This has the side effect that all frames will be re-rendered completely
55 /// but in contrast to FORCE_REDRAW it won't re-render when no motion
56 /// has been detected in the movie (for example when the movie is stopped).
57 ///
58 //#define DISABLE_REGION_UPDATES_DEBUGGING 1
59 
60 
61 /// Define this to support keyboard-based pointer movements
62 #define ENABLE_KEYBOARD_MOUSE_MOVEMENTS 1
63 
64 // Forward declarations
65 namespace gnash {
66     class SWFRect;
67     class ScreenShotter;
68     class RunResources;
69     class movie_root;
70     class movie_definition;
71     class Renderer;
72     class SWFRect;
73 }
74 namespace boost {
75     template <typename Signature> class function;
76 }
77 
78 namespace gnash {
79 
80 /// Enumerates mouse cursor types.
81 enum gnash_cursor_type {
82   CURSOR_HAND,
83   CURSOR_NORMAL,
84   CURSOR_INPUT
85 };
86 
87 /// Parent class from which all GUI implementations will depend.
88 class Gui {
89 
90 public:
91 
92     virtual ~Gui();
93 
94     /// \brief/
95     ///  Initialise the gui and the associated renderer.
96     ///
97     /// @param argc The commandline argument count.
98     /// @param argv The commandline arguments.
99     /// @return True on success; false on failure.
100     virtual bool init(int argc, char **argv[]) = 0;
101 
102     /// Set main loop delay in milliseconds.
setInterval(unsigned int interval)103     virtual void setInterval(unsigned int interval) {
104       _interval = interval;
105     }
106 
107     /// Return the clock provided by this Gui.
108     //
109     /// The Gui clock will be paused when the gui is put
110     /// in pause mode and resumed when gui playback is resumed.
111     ///
getClock()112     virtual VirtualClock& getClock() { return _virtualClock; }
113 
114     /// Set the time in milliseconds after which the programme should exit.
115     virtual void setTimeout(unsigned int timeout) = 0;
116 
117     void setScreenShotter(std::unique_ptr<ScreenShotter> ss);
118 
119     /// \brief
120     /// Create and display our window.
121     ///
122     /// @param title The window title.
123     /// @param width The desired window width in pixels.
124     /// @param height The desired window height in pixels.
125     /// @param xPosition The desired window X position from the top left corner.
126     /// @param yPosition The desired window Y position from the top left corner.
127     virtual bool createWindow(const char* title, int width, int height,
128                        int xPosition = 0, int yPosition = 0) = 0;
129 
130     virtual void resizeWindow(int width, int height);
131 
132     /// Start main rendering loop.
133     virtual bool run() = 0;
134 
135     /// Always called on exit.
136     //
137     /// Handles any common functions, then calls virtual quitUI().
138     void quit();
139 
140     /// Render the current buffer.
141     /// For OpenGL, this means that the front and back buffers are swapped.
142     virtual void renderBuffer() = 0;
143 
144     /// Gives the GUI a *hint* which region of the stage should be redrawn.
145     //
146     /// There is *no* restriction what the GUI might do with these coordinates.
147     /// Normally the GUI forwards the information to the renderer so that
148     /// it avoids rendering regions that did not change anyway. The GUI can
149     /// also alter the bounds before passing them to the renderer and it's
150     /// absolutely legal for the GUI to simply ignore the call.
151     ///
152     /// Coordinates are in TWIPS!
153     ///
154     /// Note this information is given to the GUI and not directly to the
155     /// renderer because both of them need to support this feature for
156     /// correct results. It is up to the GUI to forward this information to
157     /// the renderer.
158     ///
159     // does not need to be implemented (optional feature),
160     // but still needs to be available.
161     //
162     virtual void setInvalidatedRegion(const SWFRect& bounds);
163     virtual void setInvalidatedRegions(const InvalidatedRanges& ranges);
164 
165     // Called right before rendering anything (after setInvalidatedRegion).
166     // Used by GTK-AGG.
beforeRendering()167     virtual void beforeRendering() { /* nop */ };
168 
169     // Should return TRUE when the GUI/Renderer combination supports multiple
170     // invalidated bounds regions.
want_multiple_regions()171     virtual bool want_multiple_regions() { return false; }
172 
173     /// Asks the GUI handler if the next frame should be redrawn completely.
174     //
175     /// For example, when the contents of the player window have been destroyed,
176     /// then want_redraw() should return true so that setInvalidatedRegion() is
177     /// called with the coordinates of the complete screen.
178     virtual bool want_redraw();
179 
180     /// Sets the current mouse cursor for the Gui window.
181     virtual void setCursor(gnash_cursor_type newcursor);
182 
183     virtual void setClipboard(const std::string& copy);
184 
185     // Information for System.capabilities to be reimplemented in
186     // each gui.
getPixelAspectRatio()187     virtual double getPixelAspectRatio() const { return 0; }
188 
screenResolution()189     virtual std::pair<int, int> screenResolution() const {
190         return std::make_pair(0, 0);
191     }
192 
getScreenDPI()193     virtual double getScreenDPI() const { return 0; }
194 
195     /// Get the screen color type.
196     //
197     /// The choice is between "color" and something designating
198     /// monochrome (not sure what). If this isn't implemented in the
199     /// gui we return "color".
getScreenColor()200     virtual std::string getScreenColor() const {
201         return "color";
202     }
203 
204     /// @return Whether or not the movie should be looped indefinitely.
loops()205     bool loops() const { return _loop; }
206 
207     /// @return Whether the movie is running fullscreen or not.
isFullscreen()208     bool isFullscreen() const { return _fullscreen; }
209 
210     /// Mouse notification callback to be called when the mouse is moved.
211     //
212     /// @param x    The mouse coordinate X component in user/window
213     ///             coordinate space (pixels).
214     /// @param y    The mouse coordinate Y component in user/window
215     ///             coordinate space (pixels).
216     void notifyMouseMove(int x, int y);
217 
218     /// Mouse notification callback to be called when the mouse is clicked.
219     //
220     /// @param mouse_pressed Determines whether the mouse button is being
221     ///                      pressed (true) or being released (false)
222     void notifyMouseClick(bool mouse_pressed);
223 
224     /// Send a mouse wheel event to the stage.
225     //
226     /// @param delta    A number expressing the extent of the wheel scroll.
227     void notifyMouseWheel(int delta);
228 
229     /// Key event notification to be called when a key is pressed or depressed
230     //
231     /// @param k The key code.
232     /// @param modifier
233     ///   Modifier key identifiers from gnash::key::modifier ORed together
234     /// @param pressed
235     ///   Determines whether the key is being pressed (true)
236     ///   or being released (false)
237     ///
238     void notify_key_event(gnash::key::code k, int modifier, bool pressed);
239 
240     /// Resize the client area view and the window accordingly.
241     //
242     /// @param width  The desired width in pixels.
243     /// @param height The desired height in pixels.
244     void resize_view(int width, int height);
245 
246     /// Update stage SWFMatrix accordingly to window size and flash Stage
247     /// configuration (scaleMode, alignment)
248     //
249     /// This method should be called from the core lib when Stage configuration
250     /// change or is called by resize_view.
251     ///
252     void updateStageMatrix();
253 
254     /// \brief
255     /// Give movie an heart-beat.
256     //
257     /// This is to take place after the
258     /// interval specified in the call to setInterval().
259     ///
260     /// Wheter or not this beat advanced the movie to the next frame
261     /// depends on elapsed time since last advancement.
262     ///
263     /// @return true if this beat resulted in actual frame advancement.
264     ///
265     bool advanceMovie(bool doDisplay = true);
266 
267     /// Convenience static wrapper around advanceMovie for callbacks happiness.
268     //
269     /// NOTE: this function always return TRUE, for historical reasons.
270     /// TODO: bring code up-to-date to drop this legacy return code..
271     ///
advance_movie(Gui * gui)272     static bool advance_movie(Gui* gui) {
273         gui->advanceMovie();
274         return true;
275     }
276 
277     /// Force immediate redraw
278     ///
279     void refreshView();
280 
281     /// Attempt to run in a fullscreen window both for plugin and
282     /// standalone player.
283     //
284     /// Use isFullscreen() to see if gnash thinks
285     /// it's running in fullscreen or not. The switch to fullscreen may
286     /// fail if, for instance, the window manager refuses to allow it, but
287     /// the flag will be set anyway.
288     virtual void setFullscreen();
289 
290     /// Return from fullscreen to normal mode.
291     ///
292     virtual void unsetFullscreen();
293 
294     /// Hide the menu bar when using standalone player
295     ///
296     virtual void hideMenu();
297 
298     /// Sets whether the gui should show the system mouse pointer
299     //
300     /// @param show true if the mouse should be shown.
301     /// @return true if the state changed.
302     virtual bool showMouse(bool show);
303 
304     /// Sets whether the menus should be shown (for fscommand)
305     //
306     /// @param show true if the menu bar should be shown.
307     virtual void showMenu(bool show);
308 
309     /// Sets whether scaling should be allowed (for fscommand)
310     //
311     /// @param allow true if stage scaling should be allowed
312     virtual void allowScale(bool allow);
313 
314     // Toggle between fullscreen and normal mode
315     void toggleFullscreen();
316 
317     /// Put the application in "stop" mode
318     //
319     /// When in stop mode the application won't be advanced.
320     ///
321     void stop();
322 
323     /// Put the application in "play" mode
324     //
325     /// When in play mode the application will be advanced as usual.
326     ///
327     void play();
328 
329     /// Toggle between "stop" and "play" mode
330     //
331     /// See stop() and play()
332     ///
333     void pause();
334 
335     /// Start the movie
336     //
337     /// This function will create an instance of the registered top-level
338     /// movie definition, set variables into it and place it to the stage.
339     ///
340     void start();
341 
342     /// See stop(), play() and pause()
isStopped()343     bool isStopped() const { return _stopped; }
344 
345     /// Whether gnash is is running as a plugin
isPlugin()346     bool isPlugin() const { return ((_xid)); }
347 
348     /// Take a screenshot now!
349     void takeScreenShot();
350 
351     /// Set the maximum number of frame advances before Gnash exits.
setMaxAdvances(unsigned long ul)352     void setMaxAdvances(unsigned long ul) { if (ul) _maxAdvances = ul; }
353 
showUpdatedRegions(bool x)354     void showUpdatedRegions(bool x) { _showUpdatedRegions = x; }
showUpdatedRegions()355     bool showUpdatedRegions() const { return _showUpdatedRegions; }
356 
357     /// Instruct the core to restart the movie and
358     /// set state to play(). This does not change pause
359     /// state.
360     void restart();
361 
362     /// Set rendering quality, if not locked by RC file..
363     void setQuality(Quality q);
364 
365     /// Get current rendering quality
366     Quality getQuality() const;
367 
368     /// Toggle sound state between muted and unmuted. If
369     /// there is no active sound handler this does nothing.
370     void toggleSound();
371 
372 #ifdef GNASH_FPS_DEBUG
373     /// Set the interval between FPS debugging prints
374     //
375     /// See fpsCounterTick()
376     ///
setFpsTimerInterval(float interval)377     void setFpsTimerInterval(float interval)
378     {
379 	    assert(interval >= 0.0);
380 	    fps_timer_interval = interval;
381     }
382 #endif // def GNASH_FPS_DEBUG
383 
384 
385 #ifdef USE_SWFTREE
386     /// Return a tree containing information about the movie playing.
387     std::unique_ptr<movie_root::InfoTree> getMovieInfo() const;
388 #endif
389 
390     typedef std::map<std::string, std::string> VariableMap;
391 
392     /// Add variables to set into instances of the top-level movie definition
393     void addFlashVars(VariableMap& vars);
394 
395     /// Set the definition of top-level movie
396     void setMovieDefinition(movie_definition* md);
397 
398     /// Set the stage to advance/display
399     void setStage(movie_root* stage);
400 
401     /// Set the name of a file to dump audio to
setAudioDump(const std::string & fname)402     void setAudioDump(const std::string& fname) {
403         _audioDump = fname;
404     }
405 
406     /// The root movie, or "Stage"
getStage()407     movie_root* getStage() { return _stage; };
408 
409     /// Handle error message from the core
410     //
411     /// @param msg        The error message recieved
412     ///
error(const std::string &)413     virtual void error(const std::string& /*msg*/) {}
414 
415     /// Prompt user with a question she can answer with yes/no
416     //
417     /// @param question
418     ///        The question to ask user
419     ///
420     /// @return
421     ///        true for YES, false for NO
422     ///
423     /// The default implementation always returns true.
424     ///
425     virtual bool yesno(const std::string& question);
426 
427     /// Width of a window pixel, in stage pseudopixel units.
getXScale()428     float getXScale() const { return _xscale; };
429 
430     /// Height of a window pixel, in stage pseudopixel units.
getYScale()431     float getYScale() const { return _yscale; };
432 
433     /// Height of a window pixel, in stage pseudopixel units.
getFPS()434     float getFPS() const { return (_movieDef) ? _movieDef->get_frame_rate() : 0;
435     };
436 
437 protected:
438 
439     /// Default constructor. Initialises members to safe defaults.
440     Gui(RunResources& r);
441 
442     /** \brief
443      * Expanded constructor for more control over member values.
444      *
445      * @param xid The X11 Window ID to attach to. If this is argument is zero,
446      * a new window is created.
447      *
448      * @param scale The scale used to resize the window size, which has been
449      * established by extracting information from the SWF file.
450      *
451      * @param loop Defines whether or not the movie should be played once or
452      * looped indefinitely.
453      *
454      * @param depth Colour depth to be used in the client area of our window.
455      */
456     Gui(unsigned long xid, float scale, bool loop, RunResources& r);
457 
458     /// End main rendering loop calling GUI-specific exit functions.
459     //
460     /// Do not call this directly. Call quit() instead.
461     //
462     /// The default implementation calls exit(EXIT_SUCCESS), which isn't nice.
463     /// Please implement the proper main loop quitter in the subclasses.
quitUI()464     virtual void quitUI() {
465         std::exit(EXIT_SUCCESS);
466     }
467 
468     /// Watch a file descriptor.
469     //
470     /// An implementing Gui should monitor the file descriptor in its main
471     /// loop. When the file descriptor is triggered, the implementation should
472     /// call callCallback().
473     ///
474     /// @param fd The file descriptor to be watched
watchFD(int)475     virtual bool watchFD(int /* fd */)
476     {
477         log_unimpl("This GUI does not implement FD watching.");
478         return false;
479     }
480 
481 
482     /// Determines if playback should restart after the movie ends.
483     bool _loop;
484 
485     /// The X Window ID to attach to. If zero, we create a new window.
486     unsigned long _xid;
487 
488     // This would be 0,0,_width,_height, so maybe
489     // we should not duplicate the info with those
490     // explicit values too..
491     geometry::Range2d<int> _validbounds;
492 
493     /// Desired window width.
494     int _width;
495 
496     /// Desired window height.
497     int _height;
498 
499     /// Per-run resources
500     RunResources& _runResources;
501 
502     /// Main loop interval: the time between successive advance_movie calls.
503     unsigned int _interval;
504 
505     /// The handler which is called to update the client area of our window.
506     std::shared_ptr<Renderer> _renderer;
507 
508     /// Signals that the next frame must be re-rendered completely because the
509     /// window size did change.
510     bool _redraw_flag;
511 
512     // True if Gnash is running in fullscreen
513     bool _fullscreen;
514 
515     // True if mouse pointer is showing
516     bool _mouseShown;
517 
518     // Maximum number of advances before exit; 0 for no limit.
519     unsigned long _maxAdvances;
520 
521     /// Counter to keep track of frame advances
522     unsigned long _advances;
523 
524     /// Name of a file to dump audio to
525     std::string _audioDump;
526 
527     /// Called by Gui::stop().  This can be used by GUIs to implement pause
528     /// widgets (so that resuming a stopped animation is more user-friendly)
stopHook()529     virtual void stopHook() {}
530 
531     /// Called by Gui::play().
playHook()532     virtual void playHook() {}
533 
534     /// Determines whether the Gui is visible (not obscured).
visible()535     virtual bool visible() { return true; }
536 private:
537 
538     struct Display;
539 
540     std::map<int /* fd */, std::function<void ()> > _fd_callbacks;
541 
542     /// Width of a window pixel, in stage pseudopixel units.
543     float _xscale;
544 
545     /// Height of a window pixel, in stage pseudopixel units.
546     float _yscale;
547 
548     /// Window pixel X offset of stage origin
549     std::int32_t _xoffset;
550 
551     /// Window pixel Y offset of stage origin
552     std::int32_t _yoffset;
553 
554     bool display(movie_root* m);
555 
556 #ifdef GNASH_FPS_DEBUG
557     unsigned int fps_counter;
558 
559     float fps_rate_min, fps_rate_max;
560 
561     // Number of calls to fpsCounterTick, which is also
562     // the number of calls to movie_advance()
563     unsigned int fps_counter_total;
564 
565     std::uint64_t fps_timer, fps_start_timer;
566 
567     ///	The time, in seconds, between prints (which also resets the fps counter).
568     //
569     ///	interval must be >= 0
570     ///
571     float fps_timer_interval;
572 
573     /// Number of frames rendering of which was dropped
574     unsigned int frames_dropped;
575 
576     /// \brief
577     /// Should be called on every frame advance (including inter-frames caused
578     /// by mouse events).
579     //
580     /// Based on fps-timer_interval. See setFpsTimerInterval.
581     ///
582     void fpsCounterTick();
583 
584 #endif // def GNASH_FPS_DEBUG
585 
586     VariableMap _flashVars;
587 
588     boost::intrusive_ptr<movie_definition> _movieDef;
589 
590     /// The root movie, or "Stage"
591     movie_root* _stage;
592 
593     /// True if the application has been put into "stop" mode
594     bool _stopped;
595 
596     /// True if the application didn't start yet
597     bool _started;
598 
599     /// If true, updated regions (invalidated ranges) are visibly outlined.
600     bool _showUpdatedRegions;
601 
602     SystemClock _systemClock;
603     InterruptableVirtualClock _virtualClock;
604 
605     /// Checked on each advance for screenshot activity if it exists.
606     std::unique_ptr<ScreenShotter> _screenShotter;
607 
608 #ifdef ENABLE_KEYBOARD_MOUSE_MOVEMENTS
609     int _xpointer;
610     int _ypointer;
611     bool _keyboardMouseMovements;
612     int _keyboardMouseMovementsStep;
613 #endif // ENABLE_KEYBOARD_MOUSE_MOVEMENTS
614 };
615 
616 /// Named constructors
617 namespace gui {
618   std::unique_ptr<Gui> createFBGui(unsigned long xid, float scale, bool loop, RunResources& r);
619 }
620 std::unique_ptr<Gui> createGTKGui(unsigned long xid, float scale, bool loop, RunResources& r);
621 std::unique_ptr<Gui> createKDEGui(unsigned long xid, float scale, bool loop, RunResources& r);
622 std::unique_ptr<Gui> createQt4Gui(unsigned long xid, float scale, bool loop, RunResources& r);
623 std::unique_ptr<Gui> createSDLGui(unsigned long xid, float scale, bool loop, RunResources& r);
624 std::unique_ptr<Gui> createFLTKGui(unsigned long xid, float scale, bool loop, RunResources& r);
625 std::unique_ptr<Gui> createAQUAGui(unsigned long xid, float scale, bool loop, RunResources& r);
626 std::unique_ptr<Gui> createRISCOSGui(unsigned long xid, float scale, bool loop, RunResources& r);
627 std::unique_ptr<Gui> createAOS4Gui(unsigned long xid, float scale, bool loop, RunResources& r);
628 std::unique_ptr<Gui> createHaikuGui(unsigned long xid, float scale, bool loop, RunResources& r);
629 std::unique_ptr<Gui> createDumpGui(unsigned long xid, float scale, bool loop, RunResources& r);
630 
631 
632 } // end of gnash namespace
633 
634 // end of _GUI_H_
635 #endif
636 
637 // Local Variables:
638 // mode: C++
639 // indent-tabs-mode: nil
640 // End:
641