<commit_before>/// @file
/// @author  <NAME>
/// @author  <NAME>
/// @author  <NAME>
/// @version 2.32
/// 
/// @section LICENSE
/// 
/// This program is free software; you can redistribute it and/or modify it under
/// the terms of the BSD license: http://www.opensource.org/licenses/bsd-license.php

#ifdef HAVE_SDL

#ifdef __APPLE__
#include <TargetConditionals.h>
#include <OpenGL/gl.h>
#elif _OPENGLES1
#include <GLES/gl.h>
#else
#ifdef _WIN32
#include <windows.h>
#endif
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <gl/GL.h>
#endif

#include <SDL/SDL.h>
#include <ctype.h> // tolower()
#include <SDL/SDL_syswm.h>

#include <hltypes/hthread.h>

#include "april.h"
#include "Keys.h"
#include "Platform.h"
#include "RenderSystem.h"
#include "SDL_Window.h"

extern int gAprilShouldInvokeQuitCallback;

namespace april
{
#if TARGET_OS_MAC && !TARGET_OS_IPHONE
	void platform_cursorVisibilityUpdate();
	bool platform_CursorIsVisible();
#endif
	
	SDL_Window::SDL_Window() : Window()
	{
		this->name = APRIL_WS_SDL;
		// centered SDL window
#ifndef __APPLE__
		SDL_putenv("SDL_VIDEO_WINDOW_POS");
		SDL_putenv("SDL_VIDEO_CENTERED=1");
#endif
		this->cursorInside = true;
		this->scrollHorizontal = false;
		this->screen = NULL;
		this->created = false;
#ifdef _OPENGLES1
		this->glesContext = NULL;
#endif
	}
	
	SDL_Window::~SDL_Window()
	{
		this->destroy();
	}
	
	bool SDL_Window::create(int w, int h, bool fullscreen, chstr title)
	{
		if (!Window::create(w, h, fullscreen, title))
		{
			return false;
		}
		this->cursorInside = true;
		// initialize only SDL video subsystem
		SDL_Init(SDL_INIT_VIDEO);
		// and immediately set window title
		SDL_WM_SetCaption(this->title.c_str(), this->title.c_str());

#ifdef _OPENGL
#ifndef _OPENGLES1
		// set up opengl attributes desired for the context
		SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1);
		SDL_GL_SetAttribute(SDL_GL_SWAP_CONTROL, 1);
		this->screen = SDL_SetVideoMode(w, h, 0, SDL_OPENGL | (fullscreen ? SDL_FULLSCREEN : 0));
#else
		SDL_GLES_Init(SDL_GLES_VERSION_1_1);
		this->screen = SDL_SetVideoMode(0, 0, 16, SDL_SWSURFACE);
		this->glesContext = SDL_GLES_CreateContext();
		SDL_GLES_MakeCurrent(this->glesContext);
#endif
		glClear(GL_COLOR_BUFFER_BIT);
#else
		this->screen = SDL_SetVideoMode(w, h, 0, (fullscreen ? SDL_FULLSCREEN : 0));
#endif

		if (this->screen == NULL)
		{
#ifdef __APPLE__
#if !TARGET_OS_IPHONE
			// TODO elsewhere, add support for platform-specific msgbox code
			//NSRunAlertPanel(@"Could not open display", @"Game could not set the screen resolution. Perhaps resetting game configuration will help.", @"Ok", nil, nil);
#endif
#endif
			april::log("SDL: requested display mode could not be provided");
			exit(0);
		}
#ifndef _WIN32
		april::rendersys->clear();
		april::rendersys->presentFrame();
#endif
		SDL_FillRect(SDL_GetVideoSurface(), NULL, 0);
		// not running yet
		this->running = false;
		// cursor is visible by default
		this->cursorVisible = true;
		// key repeat
		SDL_EnableKeyRepeat(100, 50);
		this->checkEvents();
		SDL_EnableUNICODE(1);
		this->created = true;
		return true;
	}
	
	bool SDL_Window::destroy()
	{
		if (!Window::destroy())
		{
			return false;
		}
#ifdef _OPENGLES1
		SDL_GLES_DeleteContext(this->glesContext);
		SDL_GLES_Quit();
#endif
		SDL_Quit();
		return true;
	}
	
	void SDL_Window::setTitle(chstr value)
	{
		Window::setTitle(value);
		SDL_WM_SetCaption(this->title.c_str(), this->title.c_str());
	}
	
	bool SDL_Window::isCursorVisible()
	{
#if TARGET_OS_MAC && !TARGET_OS_IPHONE
		return this->cursorVisible;
#else
		return (SDL_ShowCursor(SDL_QUERY) != 0);
#endif
	}
	
	void SDL_Window::setCursorVisible(bool value)
	{
		Window::setCursorVisible(value);
		// TODO - refactor
#if TARGET_OS_MAC && !TARGET_OS_IPHONE
		// intentionally do nothing; let the platform specific code take over
		// if sdl-hidden mouse goes over the dock, then it will show again,
		// and won't be hidden again
#else
		// on other platforms, SDL does a good enough job
		SDL_ShowCursor(this->cursorVisible ? 1 : 0);
#endif
	}
	
	int SDL_Window::getWidth()
	{
		return SDL_GetVideoInfo()->current_w;
	}
	
	int SDL_Window::getHeight()
	{
		return SDL_GetVideoInfo()->current_h;
	}
	
	void* SDL_Window::getBackendId()
	{
#ifdef _WIN32
		SDL_SysWMinfo wmInfo;
		SDL_VERSION(&wmInfo.version);
		SDL_GetWMInfo(&wmInfo);
		return (void*)wmInfo.window;
#else
		// not implemented
		return NULL;
#endif
	}
	
	bool SDL_Window::updateOneFrame()
	{
		// check if we should quit...
		if (gAprilShouldInvokeQuitCallback != 0)
		{
			SDL_Event sdlEvent;
			sdlEvent.type = SDL_QUIT;
			SDL_PushEvent(&sdlEvent);
			gAprilShouldInvokeQuitCallback = 0;
		}
		// first process sdl events
		this->checkEvents();
		return (this->_handleDisplayAndUpdate() && Window::updateOneFrame());
	}

	void SDL_Window::checkEvents()
	{
		SDL_Event sdlEvent;
		while (SDL_PollEvent(&sdlEvent))
		{
			switch (sdlEvent.type)
			{
			case SDL_VIDEORESIZE:
				// do a SetVideoMode here, if we really want this
				//g_game->doResize(event.resize.w, event.resize.h);
				break;
			case SDL_QUIT:
				if (this->handleQuitRequest(true))
				{
					this->running = false;
				}
				break;
			case SDL_KEYUP:
			case SDL_KEYDOWN:
#ifdef __APPLE__
				// on mac os, we need to handle command+q
				if (SDL_GetModState() & KMOD_META && (tolower(sdlEvent.key.keysym.unicode) == 'q' || sdlEvent.key.keysym.sym == SDLK_q))
				{
					if (this->handleQuitRequest(true))
					{
						this->running = false;
					}
				}
				else
#endif
				{
					this->_handleKeyEvent((sdlEvent.type == SDL_KEYUP ? AKEYEVT_UP : AKEYEVT_DOWN),
						sdlEvent.key.keysym.sym, sdlEvent.key.keysym.unicode);
				}
				break;
			case SDL_MOUSEBUTTONUP:
			case SDL_MOUSEBUTTONDOWN:
			case SDL_MOUSEMOTION:
				this->_handleMouseEvent(sdlEvent);
				break;
			case SDL_ACTIVEEVENT:
				if (sdlEvent.active.state & SDL_APPINPUTFOCUS)
				{
					this->handleFocusChangeEvent(sdlEvent.active.gain != 0);
				}
				if (sdlEvent.active.state & SDL_APPMOUSEFOCUS)
				{
					if (sdlEvent.active.gain && this->isCursorVisible() && !this->cursorVisible)
					{
						SDL_ShowCursor(0);
					}
					this->cursorInside = (sdlEvent.active.gain != 0);
				}
				break;
			default:
				break;
			}
		}
#if TARGET_OS_MAC && !TARGET_OS_IPHONE
		platform_cursorVisibilityUpdate();
#endif
	}
	
	void SDL_Window::presentFrame()
	{
#ifdef _OPENGL
#ifndef _OPENGLES1
		SDL_GL_SwapBuffers();
#else
		SDL_GLES_SwapBuffers();
#endif
#endif
	}
	
	void SDL_Window::_handleKeyEvent(Window::KeyEventType type, SDLKey keysym, unsigned int unicode)
	{
		april::KeySym akeysym = AK_UNKNOWN;
	
		#define _s2a(sdlk, ak) case sdlk: akeysym = ak; break; 
		#define _s2a_u0(sdlk, ak) case sdlk: akeysym = ak; unicode = 0; break;
		#define s2a(sdlk, ak) _s2a(SDLK_ ## sdlk, AK_ ## ak)
		#define s2a_u0(sdlk, ak) _s2a_u0(SDLK_ ## sdlk, AK_ ## ak)
		#define sea(key) s2a(key, key)
		#define sea_u0(key) s2a_u0(key, key)
		
		switch (keysym)
		{
			// control character keys
			s2a(BACKSPACE, BACK);
#ifdef __APPLE__
			s2a(DELETE, BACK);
#else
		case SDLK_DELETE:
			akeysym = AK_DELETE; //sea(DELETE);<commit_msg>SDL: fixed delete key event propagation<commit_after>/// @file
/// @author  <NAME>
/// @author  <NAME>
/// @author  <NAME>
/// @version 2.32
/// 
/// @section LICENSE
/// 
/// This program is free software; you can redistribute it and/or modify it under
/// the terms of the BSD license: http://www.opensource.org/licenses/bsd-license.php

#ifdef HAVE_SDL

#ifdef __APPLE__
#include <TargetConditionals.h>
#include <OpenGL/gl.h>
#elif _OPENGLES1
#include <GLES/gl.h>
#else
#ifdef _WIN32
#include <windows.h>
#endif
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <gl/GL.h>
#endif

#include <SDL/SDL.h>
#include <ctype.h> // tolower()
#include <SDL/SDL_syswm.h>

#include <hltypes/hthread.h>

#include "april.h"
#include "Keys.h"
#include "Platform.h"
#include "RenderSystem.h"
#include "SDL_Window.h"

extern int gAprilShouldInvokeQuitCallback;

namespace april
{
#if TARGET_OS_MAC && !TARGET_OS_IPHONE
	void platform_cursorVisibilityUpdate();
	bool platform_CursorIsVisible();
#endif
	
	SDL_Window::SDL_Window() : Window()
	{
		this->name = APRIL_WS_SDL;
		// centered SDL window
#ifndef __APPLE__
		SDL_putenv("SDL_VIDEO_WINDOW_POS");
		SDL_putenv("SDL_VIDEO_CENTERED=1");
#endif
		this->cursorInside = true;
		this->scrollHorizontal = false;
		this->screen = NULL;
		this->created = false;
#ifdef _OPENGLES1
		this->glesContext = NULL;
#endif
	}
	
	SDL_Window::~SDL_Window()
	{
		this->destroy();
	}
	
	bool SDL_Window::create(int w, int h, bool fullscreen, chstr title)
	{
		if (!Window::create(w, h, fullscreen, title))
		{
			return false;
		}
		this->cursorInside = true;
		// initialize only SDL video subsystem
		SDL_Init(SDL_INIT_VIDEO);
		// and immediately set window title
		SDL_WM_SetCaption(this->title.c_str(), this->title.c_str());

#ifdef _OPENGL
#ifndef _OPENGLES1
		// set up opengl attributes desired for the context
		SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1);
		SDL_GL_SetAttribute(SDL_GL_SWAP_CONTROL, 1);
		this->screen = SDL_SetVideoMode(w, h, 0, SDL_OPENGL | (fullscreen ? SDL_FULLSCREEN : 0));
#else
		SDL_GLES_Init(SDL_GLES_VERSION_1_1);
		this->screen = SDL_SetVideoMode(0, 0, 16, SDL_SWSURFACE);
		this->glesContext = SDL_GLES_CreateContext();
		SDL_GLES_MakeCurrent(this->glesContext);
#endif
		glClear(GL_COLOR_BUFFER_BIT);
#else
		this->screen = SDL_SetVideoMode(w, h, 0, (fullscreen ? SDL_FULLSCREEN : 0));
#endif

		if (this->screen == NULL)
		{
#ifdef __APPLE__
#if !TARGET_OS_IPHONE
			// TODO elsewhere, add support for platform-specific msgbox code
			//NSRunAlertPanel(@"Could not open display", @"Game could not set the screen resolution. Perhaps resetting game configuration will help.", @"Ok", nil, nil);
#endif
#endif
			april::log("SDL: requested display mode could not be provided");
			exit(0);
		}
#ifndef _WIN32
		april::rendersys->clear();
		april::rendersys->presentFrame();
#endif
		SDL_FillRect(SDL_GetVideoSurface(), NULL, 0);
		// not running yet
		this->running = false;
		// cursor is visible by default
		this->cursorVisible = true;
		// key repeat
		SDL_EnableKeyRepeat(100, 50);
		this->checkEvents();
		SDL_EnableUNICODE(1);
		this->created = true;
		return true;
	}
	
	bool SDL_Window::destroy()
	{
		if (!Window::destroy())
		{
			return false;
		}
#ifdef _OPENGLES1
		SDL_GLES_DeleteContext(this->glesContext);
		SDL_GLES_Quit();
#endif
		SDL_Quit();
		return true;
	}
	
	void SDL_Window::setTitle(chstr value)
	{
		Window::setTitle(value);
		SDL_WM_SetCaption(this->title.c_str(), this->title.c_str());
	}
	
	bool SDL_Window::isCursorVisible()
	{
#if TARGET_OS_MAC && !TARGET_OS_IPHONE
		return this->cursorVisible;
#else
		return (SDL_ShowCursor(SDL_QUERY) != 0);
#endif
	}
	
	void SDL_Window::setCursorVisible(bool value)
	{
		Window::setCursorVisible(value);
		// TODO - refactor
#if TARGET_OS_MAC && !TARGET_OS_IPHONE
		// intentionally do nothing; let the platform specific code take over
		// if sdl-hidden mouse goes over the dock, then it will show again,
		// and won't be hidden again
#else
		// on other platforms, SDL does a good enough job
		SDL_ShowCursor(this->cursorVisible ? 1 : 0);
#endif
	}
	
	int SDL_Window::getWidth()
	{
		return SDL_GetVideoInfo()->current_w;
	}
	
	int SDL_Window::getHeight()
	{
		return SDL_GetVideoInfo()->current_h;
	}
	
	void* SDL_Window::getBackendId()
	{
#ifdef _WIN32
		SDL_SysWMinfo wmInfo;
		SDL_VERSION(&wmInfo.version);
		SDL_GetWMInfo(&wmInfo);
		return (void*)wmInfo.window;
#else
		// not implemented
		return NULL;
#endif
	}
	
	bool SDL_Window::updateOneFrame()
	{
		// check if we should quit...
		if (gAprilShouldInvokeQuitCallback != 0)
		{
			SDL_Event sdlEvent;
			sdlEvent.type = SDL_QUIT;
			SDL_PushEvent(&sdlEvent);
			gAprilShouldInvokeQuitCallback = 0;
		}
		// first process sdl events
		this->checkEvents();
		return (this->_handleDisplayAndUpdate() && Window::updateOneFrame());
	}

	void SDL_Window::checkEvents()
	{
		SDL_Event sdlEvent;
		while (SDL_PollEvent(&sdlEvent))
		{
			switch (sdlEvent.type)
			{
			case SDL_VIDEORESIZE:
				// do a SetVideoMode here, if we really want this
				//g_game->doResize(event.resize.w, event.resize.h);
				break;
			case SDL_QUIT:
				if (this->handleQuitRequest(true))
				{
					this->running = false;
				}
				break;
			case SDL_KEYUP:
			case SDL_KEYDOWN:
#ifdef __APPLE__
				// on mac os, we need to handle command+q
				if (SDL_GetModState() & KMOD_META && (tolower(sdlEvent.key.keysym.unicode) == 'q' || sdlEvent.key.keysym.sym == SDLK_q))
				{
					if (this->handleQuitRequest(true))
					{
						this->running = false;
					}
				}
				else
#endif
				{
					this->_handleKeyEvent((sdlEvent.type == SDL_KEYUP ? AKEYEVT_UP : AKEYEVT_DOWN),
						sdlEvent.key.keysym.sym, sdlEvent.key.keysym.unicode);
				}
				break;
			case SDL_MOUSEBUTTONUP:
			case SDL_MOUSEBUTTONDOWN:
			case SDL_MOUSEMOTION:
				this->_handleMouseEvent(sdlEvent);
				break;
			case SDL_ACTIVEEVENT:
				if (sdlEvent.active.state & SDL_APPINPUTFOCUS)
				{
					this->handleFocusChangeEvent(sdlEvent.active.gain != 0);
				}
				if (sdlEvent.active.state & SDL_APPMOUSEFOCUS)
				{
					if (sdlEvent.active.gain && this->isCursorVisible() && !this->cursorVisible)
					{
						SDL_ShowCursor(0);
					}
					this->cursorInside = (sdlEvent.active.gain != 0);
				}
				break;
			default:
				break;
			}
		}
#if TARGET_OS_MAC && !TARGET_OS_IPHONE
		platform_cursorVisibilityUpdate();
#endif
	}
	
	void SDL_Window::presentFrame()
	{
#ifdef _OPENGL
#ifndef _OPENGLES1
		SDL_GL_SwapBuffers();
#else
		SDL_GLES_SwapBuffers();
#endif
#endif
	}
	
	void SDL_Window::_handleKeyEvent(Window::KeyEventType type, SDLKey keysym, unsigned int unicode)
	{
		april::KeySym akeysym = AK_UNKNOWN;
	
		#define _s2a(sdlk, ak) case sdlk: akeysym = ak; break; 
		#define _s2a_u0(sdlk, ak) case sdlk: akeysym = ak; unicode = 0; break;
		#define s2a(sdlk, ak) _s2a(SDLK_ ## sdlk, AK_ ## ak)
		#define s2a_u0(sdlk, ak) _s2a_u0(SDLK_ ## sdlk, AK_ ## ak)
		#define sea(key) s2a(key, key)
		#define sea_u0(key) s2a_u0(key, key)
		
		switch (keysym)
		{
			// control character keys
			s2a(BACKSPACE, BACK);
#ifdef __APPLE__
			s2a_u0(DELETE, DELETE);
#else
		case SDLK_DELETE:
			akeysym = AK_DELETE; //sea(DELETE);
{'fraction_non_alphanumeric': 0.13279776458260567, 'fraction_numerical': 0.008312958435207823, 'mean_word_length': 3.970833333333333, 'pattern_counts': {'":': 0, '<': 33, '<?xml version=': 0, '>': 117, 'https://': 0, 'lorem ipsum': 0, 'www.': 2, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '2209945', 'n_tokens_mistral': 6190, 'n_tokens_neox': 5294, 'n_words': 1158}