2014-04-08 23:18:23 -04:00
// Copyright 2014 Citra Emulator Project
2014-12-16 21:38:14 -08:00
// Licensed under GPLv2 or any later version
2014-04-08 23:18:23 -04:00
// Refer to the license.txt file included.
2013-09-04 21:00:29 -04:00
2014-04-08 23:18:23 -04:00
# pragma once
2013-09-04 21:00:29 -04:00
2014-04-08 23:18:23 -04:00
# include "common/common.h"
2014-04-23 18:43:57 -07:00
# include "common/scm_rev.h"
2014-10-23 21:17:01 -07:00
# include "common/string_util.h"
2014-09-03 18:12:58 -07:00
# include "common/key_map.h"
2015-03-07 17:21:19 -05:00
# include "common/math_util.h"
2014-09-03 18:12:58 -07:00
2014-11-13 18:12:27 +01:00
/**
* Abstraction class used to provide an interface between emulation code and the frontend
* (e.g. SDL, QGLWidget, GLFW, etc...).
*
* Design notes on the interaction between EmuWindow and the emulation core:
* - Generally, decisions on anything visible to the user should be left up to the GUI.
* For example, the emulation core should not try to dictate some window title or size.
* This stuff is not the core's business and only causes problems with regards to thread-safety
* anyway.
* - Under certain circumstances, it may be desirable for the core to politely request the GUI
* to set e.g. a minimum window size. However, the GUI should always be free to ignore any
* such hints.
* - EmuWindow may expose some of its state as read-only to the emulation core, however care
* should be taken to make sure the provided information is self-consistent. This requires
* some sort of synchronization (most of this is still a TODO).
2014-11-13 20:45:37 +01:00
* - DO NOT TREAT THIS CLASS AS A GUI TOOLKIT ABSTRACTION LAYER. That's not what it is. Please
* re-read the upper points again and think about it if you don't see this.
2014-11-13 18:12:27 +01:00
*/
2013-09-04 21:00:29 -04:00
class EmuWindow
{
public :
2014-11-13 18:12:27 +01:00
/// Data structure to store emuwindow configuration
2014-09-12 17:06:13 -07:00
struct WindowConfig {
2013-09-04 21:00:29 -04:00
bool fullscreen ;
int res_width ;
int res_height ;
2014-10-12 22:46:33 +02:00
std : : pair < unsigned , unsigned > min_client_area_size ;
2013-09-04 21:00:29 -04:00
} ;
2015-03-07 17:21:19 -05:00
/// Describes the layout of the window framebuffer (size and top/bottom screen positions)
struct FramebufferLayout {
/**
* Factory method for constructing a default FramebufferLayout
* @param width Window framebuffer width in pixels
* @param height Window framebuffer height in pixels
* @return Newly created FramebufferLayout object with default screen regions initialized
*/
2015-03-07 18:26:28 -05:00
static FramebufferLayout DefaultScreenLayout ( unsigned width , unsigned height ) ;
2015-03-07 17:21:19 -05:00
unsigned width ;
unsigned height ;
MathUtil : : Rectangle < unsigned > top_screen ;
MathUtil : : Rectangle < unsigned > bottom_screen ;
} ;
2013-09-04 21:00:29 -04:00
/// Swap buffers to display the next frame
virtual void SwapBuffers ( ) = 0 ;
2014-04-01 18:20:08 -04:00
/// Polls window events
virtual void PollEvents ( ) = 0 ;
2013-09-04 21:00:29 -04:00
/// Makes the graphics context current for the caller thread
virtual void MakeCurrent ( ) = 0 ;
/// Releases (dunno if this is the "right" word) the GLFW context from the caller thread
virtual void DoneCurrent ( ) = 0 ;
2014-09-12 17:06:13 -07:00
virtual void ReloadSetKeymaps ( ) = 0 ;
2014-09-08 21:46:02 -07:00
/// Signals a key press action to the HID module
2015-03-08 21:45:45 -04:00
void KeyPressed ( KeyMap : : HostDeviceKey key ) ;
2014-09-03 18:12:58 -07:00
2014-09-08 21:46:02 -07:00
/// Signals a key release action to the HID module
2015-03-08 21:45:45 -04:00
void KeyReleased ( KeyMap : : HostDeviceKey key ) ;
2014-09-03 18:12:58 -07:00
2015-03-08 03:13:26 -04:00
/**
* Signal that a touch pressed event has occurred (e.g. mouse click pressed)
* @param framebuffer_x Framebuffer x-coordinate that was pressed
* @param framebuffer_y Framebuffer y-coordinate that was pressed
*/
2015-03-09 00:14:59 -04:00
void TouchPressed ( unsigned framebuffer_x , unsigned framebuffer_y ) ;
2015-03-08 03:13:26 -04:00
2015-03-09 00:14:59 -04:00
/// Signal that a touch released event has occurred (e.g. mouse click released)
void TouchReleased ( ) ;
2015-03-08 03:13:26 -04:00
/**
* Signal that a touch movement event has occurred (e.g. mouse was moved over the emu window)
* @param framebuffer_x Framebuffer x-coordinate
* @param framebuffer_y Framebuffer y-coordinate
*/
2015-03-09 00:14:59 -04:00
void TouchMoved ( unsigned framebuffer_x , unsigned framebuffer_y ) ;
/**
* Gets the current pad state (which buttons are pressed and the circle pad direction).
* @note This should be called by the core emu thread to get a state set by the window thread.
* @todo Fix this function to be thread-safe.
* @return PadState object indicating the current pad state
*/
const Service : : HID : : PadState GetPadState ( ) const {
return pad_state ;
}
/**
* Gets the current touch screen state (touch X/Y coordinates and whether or not it is pressed).
* @note This should be called by the core emu thread to get a state set by the window thread.
* @todo Fix this function to be thread-safe.
* @return std::tuple of (x, y, pressed) where `x` and `y` are the touch coordinates and
* `pressed` is true if the touch screen is currently being pressed
*/
2015-03-14 15:01:02 -05:00
const std : : tuple < u16 , u16 , bool > GetTouchState ( ) const {
2015-03-09 00:14:59 -04:00
return std : : make_tuple ( touch_x , touch_y , touch_pressed ) ;
}
2015-03-08 03:13:26 -04:00
2014-11-13 18:12:27 +01:00
/**
* Returns currently active configuration.
* @note Accesses to the returned object need not be consistent because it may be modified in another thread
*/
2014-10-12 22:46:33 +02:00
const WindowConfig & GetActiveConfig ( ) const {
return active_config ;
2014-04-08 23:18:23 -04:00
}
2014-11-13 18:12:27 +01:00
/**
* Requests the internal configuration to be replaced by the specified argument at some point in the future.
* @note This method is thread-safe, because it delays configuration changes to the GUI event loop. Hence there is no guarantee on when the requested configuration will be active.
*/
2014-09-12 17:06:13 -07:00
void SetConfig ( const WindowConfig & val ) {
2014-10-12 18:14:57 +02:00
config = val ;
2014-04-08 23:18:23 -04:00
}
2014-08-29 22:23:12 -07:00
2014-10-12 18:14:57 +02:00
/**
2015-03-07 17:21:19 -05:00
* Gets the framebuffer layout (width, height, and screen regions)
2014-11-13 18:12:27 +01:00
* @note This method is thread-safe
2014-10-12 18:14:57 +02:00
*/
2015-03-07 17:21:19 -05:00
const FramebufferLayout & GetFramebufferLayout ( ) const {
return framebuffer_layout ;
2014-04-08 23:18:23 -04:00
}
2013-09-04 21:00:29 -04:00
protected :
2015-03-09 00:14:59 -04:00
EmuWindow ( ) {
2014-11-13 20:45:37 +01:00
// TODO: Find a better place to set this.
config . min_client_area_size = std : : make_pair ( 400u , 480u ) ;
2014-10-12 22:46:33 +02:00
active_config = config ;
2015-03-09 00:14:59 -04:00
pad_state . hex = 0 ;
touch_x = 0 ;
touch_y = 0 ;
touch_pressed = false ;
2014-10-12 22:46:33 +02:00
}
2013-09-04 21:00:29 -04:00
virtual ~ EmuWindow ( ) { }
2014-11-13 18:12:27 +01:00
/**
* Processes any pending configuration changes from the last SetConfig call.
2014-11-13 18:24:15 +01:00
* This method invokes OnMinimalClientAreaChangeRequest if the corresponding configuration
* field changed.
2014-11-13 18:12:27 +01:00
* @note Implementations will usually want to call this from the GUI thread.
2014-11-13 20:31:34 +01:00
* @todo Actually call this in existing implementations.
2014-11-13 18:12:27 +01:00
*/
void ProcessConfigurationChanges ( ) {
// TODO: For proper thread safety, we should eventually implement a proper
// multiple-writer/single-reader queue...
if ( config . min_client_area_size ! = active_config . min_client_area_size ) {
OnMinimalClientAreaChangeRequest ( config . min_client_area_size ) ;
config . min_client_area_size = active_config . min_client_area_size ;
}
}
/**
2015-03-07 17:21:19 -05:00
* Update framebuffer layout with the given parameter.
2014-11-13 18:12:27 +01:00
* @note EmuWindow implementations will usually use this in window resize event handlers.
*/
2015-03-07 17:21:19 -05:00
void NotifyFramebufferLayoutChanged ( const FramebufferLayout & layout ) {
framebuffer_layout = layout ;
2014-10-12 18:14:57 +02:00
}
2013-09-04 21:00:29 -04:00
2014-11-13 18:12:27 +01:00
/**
* Update internal client area size with the given parameter.
* @note EmuWindow implementations will usually use this in window resize event handlers.
*/
2014-10-12 22:46:33 +02:00
void NotifyClientAreaSizeChanged ( const std : : pair < unsigned , unsigned > & size ) {
2014-10-12 18:14:57 +02:00
client_area_width = size . first ;
client_area_height = size . second ;
}
2013-09-04 21:00:29 -04:00
private :
2014-11-13 18:24:15 +01:00
/**
* Handler called when the minimal client area was requested to be changed via SetConfig.
* For the request to be honored, EmuWindow implementations will usually reimplement this function.
*/
2014-10-12 22:46:33 +02:00
virtual void OnMinimalClientAreaChangeRequest ( const std : : pair < unsigned , unsigned > & minimal_size ) {
2014-11-13 18:24:15 +01:00
// By default, ignore this request and do nothing.
2014-10-12 22:46:33 +02:00
}
2015-03-07 17:21:19 -05:00
FramebufferLayout framebuffer_layout ; ///< Current framebuffer layout
2014-10-12 18:14:57 +02:00
unsigned client_area_width ; ///< Current client width, should be set by window impl.
unsigned client_area_height ; ///< Current client height, should be set by window impl.
2013-09-04 21:00:29 -04:00
2014-10-12 22:46:33 +02:00
WindowConfig config ; ///< Internal configuration (changes pending for being applied in ProcessConfigurationChanges)
WindowConfig active_config ; ///< Internal active configuration
2015-03-08 03:13:26 -04:00
2015-03-08 21:45:45 -04:00
bool touch_pressed ; ///< True if touchpad area is currently pressed, otherwise false
2015-03-09 00:14:59 -04:00
u16 touch_x ; ///< Touchpad X-position in native 3DS pixel coordinates (0-320)
u16 touch_y ; ///< Touchpad Y-position in native 3DS pixel coordinates (0-240)
Service : : HID : : PadState pad_state ;
2013-09-04 21:00:29 -04:00
} ;