indexing description: "[ Base class for interactive scenes. Descendants have to implement `redraw' and `initialize_scene'. An EM_SCENE consists of: - An `event_loop' that runs the scene and dispatches events to subscribers. - A `screen' onto which the scene is drawn when running. There are some handle_xxx features to handle important events for user interaction that descendants can redefine to interact. An EM_SCENE is supposed to be used as follows (i.e. by EM_APPLICATION): - initialize_scene: called first to initialize the scene - run: called after `initialize_scene' to run the scene - other features: called by event_loop.dispatch If you want that another scene is executed after your scene ends use `set_next_scene' accordingly. Then call `start_next_scene' to start it. If you want to end the program call `quit'. Use animation features to get animatable objects (i.e. sprites) running. Like this all animatable objects get animated before the scene is redrawn. Subclasses must call `make_scene' at creation. ]" date: "$Date$" revision: "$Revision$" deferred class EM_SCENE inherit EM_TIME_SINGLETON export {NONE} all end EM_SHARED_SCENE export {NONE} all end feature {NONE} -- Initialization make_scene is -- Default initialization. do is_make_scene_called := True create animation_event create event_loop.make_poll end is_make_scene_called: BOOLEAN -- Is `make_scene' called? feature -- Initialization initialize_scene is -- Initialize scene. -- This is called before the scene is shown on screen. deferred end uninitialize_scene is -- Uninitialize scene. -- This is called right before next scene is displayed. -- Redefine this to clean up some needed things (openGl properties, images, ...) do end feature -- Access next_scene: EM_SCENE -- Scene that should be executed after this scene ends. -- If next_scene = Void the program just ends. event_loop: EM_EVENT_LOOP -- Event loop that makes scene running screen: EM_VIDEO_SURFACE -- Surface where scene is drawed feature -- Status report is_running: BOOLEAN -- Is scene currently running? -- Otherwise no events are dispatched and no animation will run right now. feature -- Status setting set_frame_counter_visibility (a_value: BOOLEAN) is -- If `a_value' is `True' make frame counter visible, -- else make it invisible. do is_frame_counter_displayed := a_value end feature -- Element change set_next_scene (a_scene: like next_scene) is -- Set `next_scene' to `a_scene'. do next_scene := a_scene ensure next_scene_set: next_scene = a_scene end set_next_scene_from_outside (a_callback: like callback_for_set_next_scene_and_start) is -- Set `next_scene' to `a_scene' from an other thread -- Make sure that start_next_scene is called by the main SDL Thread do callback_for_set_next_scene_and_start := a_callback next_scene_is_set := true ensure next_scene_set: callback_for_set_next_scene_and_start = a_callback next_scene_set: next_scene_is_set = true end next_scene_is_set: BOOLEAN -- Indicates that next scene is set and must be proceeded by -- the main SDL Thread (syncronise both threads) callback_for_set_next_scene_and_start: FUNCTION[ANY, TUPLE[], EM_SCENE] -- we have to create next scene in same thread as the SDL thread -- this function should point to a feature were next scene is created feature -- Miscellaneous start_next_scene is -- Stop `Current' and advance to `next_scene'. do uninitialize_scene event_loop.stop end start_next_scene_threaded is -- Call creation procedure for next scene and start this scene immediadely do if next_scene_is_set then if callback_for_set_next_scene_and_start = void then next_scene := void else callback_for_set_next_scene_and_start.call (void) next_scene := callback_for_set_next_scene_and_start.last_result end start_next_scene end end quit is -- Stop scene whitout starting another one. do next_scene := Void event_loop.stop end run (a_screen: EM_VIDEO_SURFACE) is -- Run scene and show it on `a_screen'. require a_screen_not_void: a_screen /= Void do if is_frame_counter_displayed then create frame_counter.make end screen := a_screen event_loop.key_down_event.subscribe (agent handle_key_down_event (?)) event_loop.key_up_event.subscribe (agent handle_key_up_event (?)) event_loop.mouse_button_down_event.subscribe (agent handle_mouse_button_down_event (?)) event_loop.mouse_button_up_event.subscribe (agent handle_mouse_button_up_event (?)) event_loop.mouse_motion_event.subscribe (agent handle_mouse_motion_event (?)) event_loop.quit_event.subscribe (agent handle_quit_event (?)) event_loop.update_event.subscribe (agent handle_update_event) event_loop.update_event.subscribe (agent animate) event_loop.update_event.subscribe (agent redraw) event_loop.joystick_axis_event.subscribe (agent handle_joystick_axis_event (?)) event_loop.joystick_ball_event.subscribe (agent handle_joystick_ball_event (?)) event_loop.joystick_button_down_event.subscribe (agent handle_joystick_button_down_event (?)) event_loop.joystick_button_up_event.subscribe (agent handle_joystick_button_up_event (?)) event_loop.joystick_hat_event.subscribe (agent handle_joystick_hat_event (?)) event_loop.update_event.subscribe (agent start_next_scene_threaded) is_running := True set_running_scene (Current) event_loop.dispatch set_running_scene (Void) is_running := False end feature -- Animation start_animating (an_animatable: EM_ANIMATABLE) is -- Subscribe `an_animatable' to be animated when `Current' is running. do if not animation_event.has (agent an_animatable.go_to_time) then animation_event.subscribe (agent an_animatable.go_to_time) end end stop_animating (an_animatable: EM_ANIMATABLE) is -- Unsubscribe `an_animatable' from beeing animated when `Current' is running. do if animation_event.has (agent an_animatable.go_to_time) then animation_event.unsubscribe (agent an_animatable.go_to_time) end end animate is -- Let all subscribed animatable objects perform their animation. -- (Calls `go_to_time' of animatable objects with current time tick) local cur_time: INTEGER do cur_time := time.ticks animation_event.publish ([cur_time]) end feature -- Events animation_event: EM_EVENT_CHANNEL [TUPLE [INTEGER]] -- Animation event, allows animatable objects to perform animation -- (i.e. moving them selves) before they get drawed. -- As an argument the reference time in milliseconds is passed -- up to which the animatable objects should draw them selves. -- This event gets published right before the scene is redrawed. feature -- Drawing redraw is -- Redraw scene. deferred end feature {NONE} -- Keyboard management handle_key_down_event (a_keyboard_event: EM_KEYBOARD_EVENT) is -- Handle key down event. -- (Descendants can redefine this feature -- to do something when a key is pressed.) require a_keyboard_event_not_void: a_keyboard_event /= Void do end handle_key_up_event (a_keyboard_event: EM_KEYBOARD_EVENT) is -- Handle key up event. -- (Descendants can redefine this feature -- to do something when a key is released.) require a_keyboard_event_not_void: a_keyboard_event /= Void do end feature {NONE} -- Mouse management handle_mouse_button_down_event (a_mouse_button_event: EM_MOUSEBUTTON_EVENT) is -- Handle mouse button down event. -- (Descendants can redefine this feature -- to do something when a mouse button is pressed.) require a_mouse_button_event_not_void: a_mouse_button_event /= Void do end handle_mouse_button_up_event (a_mouse_button_event: EM_MOUSEBUTTON_EVENT) is -- Handle mouse button up event. -- (Descendants can redefine this feature -- to do something when a mouse button is released.) require a_mouse_button_event_not_void: a_mouse_button_event /= Void do end handle_mouse_motion_event (a_mouse_motion_event: EM_MOUSEMOTION_EVENT) is -- Handle mouse button move event. -- (Descendants can redefine this feature -- to do something when the mouse is moved) require a_mouse_motion_event_not_void: a_mouse_motion_event /= Void do end feature {NONE} -- Joystick management handle_joystick_axis_event (a_joystick_event: EM_JOYSTICK_AXIS_EVENT) is -- Handle joystick axis event. -- (Descendants can redefine this feature -- to do something when the mouse is moved) require a_joystick_event_not_void: a_joystick_event /= Void do end handle_joystick_ball_event (a_joystick_event: EM_JOYSTICK_BALL_EVENT) is -- Handle joystick ball event. -- (Descendants can redefine this feature -- to do something when the mouse is moved) require a_joystick_event_not_void: a_joystick_event /= Void do end handle_joystick_button_down_event (a_joystick_event: EM_JOYSTICK_BUTTON_EVENT) is -- Handle joystick button down event. -- (Descendants can redefine this feature -- to do something when the mouse is moved) require a_joystick_event_not_void: a_joystick_event /= Void do end handle_joystick_button_up_event (a_joystick_event: EM_JOYSTICK_BUTTON_EVENT) is -- Handle joystick button up event. -- (Descendants can redefine this feature -- to do something when the mouse is moved) require a_joystick_event_not_void: a_joystick_event /= Void do end handle_joystick_hat_event (a_joystick_event: EM_JOYSTICK_HAT_EVENT) is -- Handle joystick hat event. -- (Descendants can redefine this feature -- to do something when the mouse is moved) require a_joystick_event_not_void: a_joystick_event /= Void do end feature {NONE} -- Miscellaneous event handlers handle_quit_event (a_quit_event: EM_QUIT_EVENT) is -- Handle quit event. -- If you override this feature you have to either call -- the original version or `quit' yourself. -- (Descendants can redefine this feature -- to do something when application is quit.) require a_quit_event_not_void: a_quit_event /= Void do quit end handle_update_event is -- Handle the outside event. -- (Descendants can redefine this feature -- to do something after each event loop pass.) do end feature {NONE} -- Implementation frame_counter: EM_FRAME_COUNTER -- Frame counter is_frame_counter_displayed: BOOLEAN -- Is frame counter displayed? invariant make_scene_called: is_make_scene_called animation_event_not_void: animation_event /= Void event_loop_not_void: event_loop /= Void screen_not_void_when_running: is_running implies screen /= Void end