Files
eepp/src/eepp/window/window.cpp
Martín Lucas Golini 2541dccfc9 7GUIs circle drawer WIP.
UILoader rendering glitch fix.
Git create branch fix checkout in subrepos.
UIMessageBox don't focus on any disabled input by default.
2024-02-08 22:25:18 -03:00

625 lines
16 KiB
C++

#include <SOIL2/src/SOIL2/SOIL2.h>
#include <eepp/graphics/globalbatchrenderer.hpp>
#include <eepp/graphics/renderer/openglext.hpp>
#include <eepp/graphics/renderer/renderer.hpp>
#include <eepp/graphics/texturefactory.hpp>
#include <eepp/system/filesystem.hpp>
#include <eepp/version.hpp>
#include <eepp/window/clipboard.hpp>
#include <eepp/window/cursormanager.hpp>
#include <eepp/window/engine.hpp>
#include <eepp/window/input.hpp>
#include <eepp/window/window.hpp>
#ifdef EE_GLES1_LATE_INCLUDE
#if EE_PLATFORM == EE_PLATFORM_IOS
#include <OpenGLES/ES1/gl.h>
#include <OpenGLES/ES1/glext.h>
#else
#include <GLES/gl.h>
#ifndef GL_GLEXT_PROTOTYPES
#define GL_GLEXT_PROTOTYPES
#endif
#include <GLES/glext.h>
#endif
#endif
#if EE_PLATFORM == EE_PLATFORM_EMSCRIPTEN
#include <emscripten.h>
#endif
namespace EE { namespace Window {
Window::FrameData::FrameData() : FrameElapsed( NULL ), ElapsedTime() {}
Window::FrameData::~FrameData() {
eeSAFE_DELETE( FrameElapsed );
}
Window::Window( WindowSettings Settings, ContextSettings Context, Clipboard* Clipboard,
Input* Input, CursorManager* CursorManager ) :
mClipboard( Clipboard ),
mInput( Input ),
mCursorManager( CursorManager ),
mCurrentView( NULL ),
mNumCallBacks( 0 ),
mIME( this ) {
mWindow.WindowConfig = Settings;
mWindow.ContextConfig = Context;
setFrameRateLimit( Context.FrameRateLimit );
}
Window::~Window() {
eeSAFE_DELETE( mClipboard );
eeSAFE_DELETE( mInput );
eeSAFE_DELETE( mCursorManager );
}
Sizei Window::getSize() {
return Sizei( mWindow.WindowConfig.Width, mWindow.WindowConfig.Height );
}
Vector2f Window::getCenter() {
return Sizef( mWindow.WindowConfig.Width, mWindow.WindowConfig.Height ) * 0.5f;
}
const Uint32& Window::getWidth() const {
return mWindow.WindowConfig.Width;
}
const Uint32& Window::getHeight() const {
return mWindow.WindowConfig.Height;
}
const Sizei& Window::getDesktopResolution() {
return mWindow.DesktopResolution;
}
void Window::setSize( Uint32 Width, Uint32 Height ) {
setSize( Width, Height, isWindowed() );
}
bool Window::isWindowed() const {
return 0 != !( mWindow.WindowConfig.Style & WindowStyle::Fullscreen );
}
bool Window::isResizeable() const {
return 0 != ( mWindow.WindowConfig.Style & WindowStyle::Resize );
}
void Window::set2DProjection( const Uint32& Width, const Uint32& Height ) {
GLi->matrixMode( GL_PROJECTION );
GLi->loadIdentity();
GLi->ortho( 0.0f, Width, Height, 0.0f, -1000.0f, 1000.0f );
GLi->matrixMode( GL_MODELVIEW );
GLi->loadIdentity();
}
void Window::setProjection( const Transform& transform ) {
GLi->matrixMode( GL_PROJECTION );
GLi->loadMatrixf( transform.getMatrix() );
GLi->matrixMode( GL_MODELVIEW );
GLi->loadIdentity();
}
Vector2f Window::mapPixelToCoords( const Vector2i& point ) {
return mapPixelToCoords( point, getView() );
}
Vector2f Window::mapPixelToCoords( const Vector2i& point, const View& view ) {
// First, convert from viewport coordinates to homogeneous coordinates
Vector2f normalized;
Rect viewport = getViewport( view );
normalized.x = -1.f + 2.f * ( point.x - viewport.Left ) / viewport.Right;
normalized.y = 1.f - 2.f * ( point.y - viewport.Top ) / viewport.Bottom;
// Then transform by the inverse of the view matrix
return view.getInverseTransform().transformPoint( normalized );
}
Vector2i Window::mapCoordsToPixel( const Vector2f& point ) {
return mapCoordsToPixel( point, getView() );
}
Vector2i Window::mapCoordsToPixel( const Vector2f& point, const View& view ) {
// First, transform the point by the view matrix
Vector2f normalized = view.getTransform().transformPoint( point );
// Then convert to viewport coordinates
Vector2i pixel;
Rect viewport = getViewport( view );
pixel.x = static_cast<int>( ( normalized.x + 1.f ) / 2.f * viewport.Right + viewport.Left );
pixel.y = static_cast<int>( ( -normalized.y + 1.f ) / 2.f * viewport.Bottom + viewport.Top );
return pixel;
}
void Window::setCloseRequestCallback( const WindowRequestCloseCallback& closeRequestCallback ) {
mCloseRequestCallback = closeRequestCallback;
}
void Window::setQuitCallback( const WindowQuitCallback& quitCallback ) {
mQuitCallback = quitCallback;
}
void Window::setViewport( const Int32& x, const Int32& y, const Uint32& Width,
const Uint32& Height ) {
GLi->viewport( x, getHeight() - ( y + Height ), Width, Height );
}
Rect Window::getViewport( const View& view ) {
float width = static_cast<float>( getSize().getWidth() );
float height = static_cast<float>( getSize().getHeight() );
const Rectf& viewport = view.getViewport();
return Rect( static_cast<int>( 0.5f + width * viewport.Left ),
static_cast<int>( 0.5f + height * viewport.Top ),
static_cast<int>( 0.5f + width * viewport.Right ),
static_cast<int>( 0.5f + height * viewport.Bottom ) );
}
void Window::setView( const View& view, bool forceRefresh ) {
const View* viewPtr = &view;
if ( viewPtr != mCurrentView || viewPtr->isDirty() || forceRefresh ) {
mCurrentView = viewPtr;
Rect viewport = getViewport( *mCurrentView );
setViewport( viewport.Left, viewport.Top, viewport.Right, viewport.Bottom );
setProjection( viewPtr->getTransform() );
}
}
const View& Window::getDefaultView() const {
return mDefaultView;
}
const View& Window::getView() const {
return *mCurrentView;
}
void Window::createView() {
mDefaultView.reset( Rectf( 0, 0, mWindow.WindowConfig.Width, mWindow.WindowConfig.Height ) );
setView( mDefaultView );
}
void Window::setup2D( const bool& KeepView ) {
EE::Window::Window* curWin = Engine::instance()->getCurrentWindow();
if ( curWin != this )
Engine::instance()->setCurrentWindow( this );
GLi->pixelStorei( GL_UNPACK_ALIGNMENT, 1 );
GLi->pixelStorei( GL_PACK_ALIGNMENT, 1 );
setClearColor( mWindow.ClearColor );
GLi->lineSmooth();
GLi->enable( GL_TEXTURE_2D ); // Enable Textures
GLi->disable( GL_DEPTH_TEST );
if ( GLv_2 == GLi->version() || GLv_ES1 == GLi->version() ) {
GLi->disable( GL_LIGHTING );
}
if ( !KeepView ) {
setView( mDefaultView, true );
}
BlendMode::setMode( BlendMode::Alpha(), true );
if ( GLv_3CP != GLi->version() && GLv_3 != GLi->version() && GLv_ES2 != GLi->version() ) {
#if !defined( EE_GLES2 ) || defined( EE_GLES_BOTH )
glTexEnvi( GL_POINT_SPRITE, GL_COORD_REPLACE, GL_TRUE );
#endif
}
if ( GLv_2 == GLi->version() || GLv_ES1 == GLi->version() ) {
GLi->enableClientState( GL_VERTEX_ARRAY );
GLi->enableClientState( GL_TEXTURE_COORD_ARRAY );
GLi->enableClientState( GL_COLOR_ARRAY );
}
if ( curWin != this )
Engine::instance()->setCurrentWindow( curWin );
}
const WindowInfo* Window::getWindowInfo() const {
return &mWindow;
}
void Window::setClearColor( const RGB& Color ) {
mWindow.ClearColor = Color;
GLi->clearColor( static_cast<Float>( mWindow.ClearColor.r ) / 255.0f,
static_cast<Float>( mWindow.ClearColor.g ) / 255.0f,
static_cast<Float>( mWindow.ClearColor.b ) / 255.0f, 255.0f );
}
RGB Window::getClearColor() const {
return mWindow.ClearColor;
}
bool Window::takeScreenshot( std::string filepath, const Image::SaveType& Format ) {
GlobalBatchRenderer::instance()->draw();
bool CreateNewFile = false;
std::string File, Ext;
if ( filepath.size() ) {
File = filepath.substr( filepath.find_last_of( "/\\" ) + 1 );
Ext = File.substr( File.find_last_of( "." ) + 1 );
if ( FileSystem::isDirectory( filepath ) || !Ext.size() )
CreateNewFile = true;
} else {
filepath = Sys::getProcessPath();
CreateNewFile = true;
}
if ( CreateNewFile ) { // Search if file path is given, and if have and extension
bool find = false;
Int32 FileNum = 1;
std::string TmpPath = filepath;
if ( !FileSystem::isDirectory( filepath ) )
FileSystem::makeDir( filepath );
Ext = "." + Image::saveTypeToExtension( Format );
while ( !find && FileNum < 10000 ) {
TmpPath = String::format( "%s%05d%s", filepath.c_str(), FileNum, Ext.c_str() );
FileNum++;
if ( !FileSystem::fileExists( TmpPath ) )
find = true;
if ( FileNum == 10000 && find == false )
return false;
}
return 0 != SOIL_save_screenshot( TmpPath.c_str(), Format, 0, 0, mWindow.WindowConfig.Width,
mWindow.WindowConfig.Height );
} else {
std::string Direc = FileSystem::fileRemoveFileName( filepath );
if ( !FileSystem::isDirectory( Direc ) )
FileSystem::makeDir( Direc );
return 0 != SOIL_save_screenshot( filepath.c_str(), Format, 0, 0,
mWindow.WindowConfig.Width, mWindow.WindowConfig.Height );
}
}
bool Window::isRunning() const {
return mWindow.Created;
}
bool Window::isOpen() const {
return mWindow.Created;
}
void Window::close() {
mWindow.Created = false;
}
void Window::setFrameRateLimit( const Uint32& FrameRateLimit ) {
mFrameData.FPS.Limit = (Float)FrameRateLimit;
}
Uint32 Window::getFrameRateLimit() {
return static_cast<Uint32>( mFrameData.FPS.Limit );
}
Uint32 Window::getFPS() const {
return mFrameData.FPS.Current;
}
const Time& Window::getElapsed() const {
return mFrameData.ElapsedTime;
}
void Window::updateElapsedTime() {
if ( NULL == mFrameData.FrameElapsed ) {
mFrameData.FrameElapsed = eeNew( Clock, () );
}
mFrameData.ElapsedTime = mFrameData.FrameElapsed->getElapsedTimeAndReset();
}
const Time& Window::getSleepTimePerSecond() const {
return mFrameData.FPS.CurSleepTime;
}
const Time& Window::getRenderTimePerSecond() const {
return mFrameData.FPS.CurRenderTime;
}
const Sizei& Window::getLastWindowedSize() const {
return mLastWindowedSize;
}
bool Window::showMessageBox( const MessageBoxType&, const std::string&, const std::string& ) {
return false;
}
InputMethod& Window::getIME() {
return mIME;
}
void Window::calculateFps() {
if ( mFrameData.FPS.LastCheck.getElapsedTime().asSeconds() >= 1.f ) {
mFrameData.FPS.Current = mFrameData.FPS.Count;
mFrameData.FPS.Count = 0;
mFrameData.FPS.CurSleepTime = mFrameData.FPS.SleepTime;
mFrameData.FPS.SleepTime = Time::Zero;
mFrameData.FPS.CurRenderTime = mFrameData.FPS.RenderTime;
mFrameData.FPS.RenderTime = Time::Zero;
mFrameData.FPS.LastCheck.restart();
}
mFrameData.FPS.Count++;
}
void Window::limitFps() {
if ( mFrameData.FPS.Limit > 0 ) {
Time frameTime( Milliseconds( 1000.0 / mFrameData.FPS.Limit ) );
Time remainingTime = frameTime - mFrameData.FPS.FrameTime;
if ( frameTime - mFrameData.ElapsedTime > Time::Zero ) {
remainingTime += mFrameData.FPS.Error;
}
mFrameData.FPS.Error = Time::Zero;
if ( remainingTime > Time::Zero ) {
Clock checkElapsed;
Sys::sleep( remainingTime );
Time elapsed = checkElapsed.getElapsedTimeAndReset();
Time timeDiff = elapsed - remainingTime;
mFrameData.FPS.SleepTime += elapsed;
if ( timeDiff > Time::Zero ) {
mFrameData.FPS.Error += timeDiff;
}
}
}
}
void Window::clear() {
GLi->clear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT );
}
void Window::display( bool clear ) {
GlobalBatchRenderer::instance()->draw();
swapBuffers();
if ( mCurrentView->isDirty() )
setView( *mCurrentView );
#if EE_PLATFORM != EE_PLATFORM_EMSCRIPTEN
if ( clear )
this->clear();
#endif
mFrameData.FPS.FrameTime = mFrameData.FPS.RenderClock.getElapsedTime();
mFrameData.FPS.RenderTime += mFrameData.FPS.FrameTime;
updateElapsedTime();
limitFps();
calculateFps();
mFrameData.FPS.RenderClock.restart();
}
Clipboard* Window::getClipboard() const {
return mClipboard;
}
Input* Window::getInput() const {
return mInput;
}
CursorManager* Window::getCursorManager() const {
return mCursorManager;
}
Uint32 Window::pushResizeCallback( const WindowResizeCallback& cb ) {
mNumCallBacks++;
mCallbacks[mNumCallBacks] = cb;
return mNumCallBacks;
}
void Window::popResizeCallback( const Uint32& CallbackId ) {
mCallbacks[CallbackId] = 0;
mCallbacks.erase( mCallbacks.find( CallbackId ) );
}
void Window::startOnScreenKeyboard() {}
void Window::stopOnScreenKeyboard() {}
bool Window::isOnScreenKeyboardActive() const {
return false;
}
void Window::sendVideoResizeCb() {
for ( std::map<Uint32, WindowResizeCallback>::iterator i = mCallbacks.begin();
i != mCallbacks.end(); ++i ) {
i->second( this );
}
}
void Window::logSuccessfulInit( const std::string& BackendName ) {
std::string msg(
"Engine Initialized Succesfully.\n\tVersion: " + Version::getVersionName() +
" (codename: \"" + Version::getCodename() + "\")" +
"\n\tBuild time: " + Version::getBuildTime() + "\n\tPlatform: " + Sys::getPlatform() +
"\n\tOS: " + Sys::getOSName( true ) + "\n\tArch: " + Sys::getOSArchitecture() +
"\n\tCPU Cores: " + String::toString( Sys::getCPUCount() ) +
"\n\tProcess Path: " + Sys::getProcessPath() + "\n\tCurrent Working Directory: " +
FileSystem::getCurrentWorkingDirectory() + "\n\tDisk Free Space: " +
String::toString(
FileSystem::sizeToString( FileSystem::getDiskFreeSpace( Sys::getProcessPath() ) ) ) +
"\n\tWindow/Input Backend: " + BackendName + "\n\tGL Backend: " + GLi->versionStr() +
"\n\tGL Vendor: " + GLi->getVendor() + "\n\tGL Renderer: " + GLi->getRenderer() +
"\n\tGL Version: " + GLi->getVersion() + "\n\tGL Shading Language Version: " +
GLi->getShadingLanguageVersion() + "\n\tResolution: " + String::toString( getWidth() ) +
"x" + String::toString( getHeight() ) );
#ifndef EE_SILENT
Log::info( msg );
#else
Log::instance()->writel( msg );
#endif
}
void Window::logFailureInit( const std::string& ClassName, const std::string& BackendName ) {
Log::error( "Error on %s::Init. Backend %s failed to start.", ClassName.c_str(),
BackendName.c_str() );
}
void Window::onCloseRequest() {
if ( mCloseRequestCallback && !mCloseRequestCallback( this ) )
return;
close();
}
void Window::onQuit() {
if ( mQuitCallback )
mQuitCallback( this );
}
std::string Window::getTitle() {
return mWindow.WindowConfig.Title;
}
eeWindowContex Window::getContext() const {
#if defined( EE_GLEW_AVAILABLE ) && \
( EE_PLATFORM == EE_PLATFORM_WIN || defined( EE_X11_PLATFORM ) || \
EE_PLATFORM == EE_PLATFORM_MACOS )
return mWindow.Context;
#else
return 0;
#endif
}
void Window::getMainContext() {}
void Window::setDefaultContext() {
#if defined( EE_GLEW_AVAILABLE ) && ( EE_PLATFORM == EE_PLATFORM_WIN || defined( EE_X11_PLATFORM ) )
setCurrentContext( mWindow.Context );
#endif
}
void Window::minimize() {}
void Window::maximize() {}
bool Window::isMaximized() {
return false;
}
void Window::hide() {}
void Window::raise() {}
void Window::flash( WindowFlashOperation ) {}
void Window::show() {}
void Window::setPosition( int, int ) {}
Vector2i Window::getPosition() {
return Vector2i::Zero;
}
void Window::setCurrentContext( eeWindowContex ) {}
void Window::setCurrent() {}
void Window::centerToDisplay() {
if ( isWindowed() ) {
setPosition( mWindow.DesktopResolution.getWidth() / 2 - mWindow.WindowConfig.Width / 2,
mWindow.DesktopResolution.getHeight() / 2 - mWindow.WindowConfig.Height / 2 );
}
}
Rect Window::getBorderSize() {
return Rect();
}
Float Window::getScale() {
return 1.f;
}
void Window::startTextInput() {}
bool Window::isTextInputActive() const {
return false;
}
void Window::stopTextInput() {}
void Window::setTextInputRect( const Rect& ) {}
void Window::clearComposition() {}
bool Window::hasScreenKeyboardSupport() {
return false;
}
bool Window::isScreenKeyboardShown() {
return false;
}
bool Window::isThreadedGLContext() {
return false;
}
void Window::setGLContextThread() {}
void Window::unsetGLContextThread() {}
#if EE_PLATFORM == EE_PLATFORM_EMSCRIPTEN
static void eepp_mainloop() {
Engine::instance()->getCurrentWindow()->getMainLoop()();
}
#endif
void Window::runMainLoop( std::function<void()> func, int fps ) {
mMainLoop = std::move( func );
#if EE_PLATFORM == EE_PLATFORM_EMSCRIPTEN
emscripten_set_main_loop( eepp_mainloop, std::max( mFrameData.FPS.Limit, std::max( 0, fps ) ),
1 );
#else
if ( fps >= 0 )
setFrameRateLimit( fps );
while ( isRunning() ) {
mMainLoop();
}
#endif
}
int Window::getCurrentDisplayIndex() {
return 0;
}
}} // namespace EE::Window