void Win32Window::setWindowSize(Vec2i size) {
	
	SetWindowLong(m_hWnd, GWL_STYLE, WS_OVERLAPPEDWINDOW);
	
	depth_ = 0;
	
	DWORD windowStyle = WS_OVERLAPPEDWINDOW;
	DWORD windowExtendedStyle = WS_EX_APPWINDOW;
	
	RECT rcWnd;
	
	SetRect(&rcWnd, 0, 0, size.x, size.y);
	AdjustWindowRectEx(&rcWnd, windowStyle, GetMenu(m_hWnd) != NULL, windowExtendedStyle);
	
	int dx = rcWnd.right - rcWnd.left - size.x;
	int dy = rcWnd.bottom - rcWnd.top - size.y;
	
	SetWindowPos(m_hWnd, HWND_NOTOPMOST, 0, 0, size.x + dx, size.y + dy, SWP_SHOWWINDOW);
	
	if(isFullscreen_) {
		isFullscreen_ = false;
		onToggleFullscreen();
	}
	
	onResize(size.x, size.y);
}
void SDL2Window::changeMode(DisplayMode mode, bool makeFullscreen) {
	
	if(!m_window) {
		m_size = mode.resolution;
		m_fullscreen = makeFullscreen;
		return;
	}
	
	if(m_fullscreen == makeFullscreen && m_size == mode.resolution) {
		return;
	}
	
	bool wasFullscreen = m_fullscreen;
	
	m_renderer->beforeResize(wasFullscreen || makeFullscreen);
	
	if(makeFullscreen) {
		if(mode.resolution != Vec2i_ZERO) {
			SDL_DisplayMode sdlmode;
			SDL_DisplayMode requested;
			requested.driverdata = NULL;
			requested.format = 0;
			requested.refresh_rate = 0;
			requested.w = mode.resolution.x;
			requested.h = mode.resolution.y;
			int display = SDL_GetWindowDisplayIndex(m_window);
			if(!SDL_GetClosestDisplayMode(display, &requested, &sdlmode)) {
				if(SDL_GetDesktopDisplayMode(display, &sdlmode)) {
					return;
				}
			}
			if(SDL_SetWindowDisplayMode(m_window, &sdlmode) < 0) {
				return;
			}
		}
	}
	
	Uint32 flags = getSDLFlagsForMode(mode.resolution, makeFullscreen);
	if(SDL_SetWindowFullscreen(m_window, flags) < 0) {
		return;
	}
	
	if(!makeFullscreen) {
		SDL_SetWindowSize(m_window, mode.resolution.x, mode.resolution.y);
	}
	
	if(wasFullscreen != makeFullscreen) {
		onToggleFullscreen(makeFullscreen);
	}
	
	if(makeFullscreen) {
		// SDL regrettably sends resize events when a fullscreen window is minimized.
		// Because of that we ignore all size change events when fullscreen.
		// Instead, handle the size change here.
		updateSize();
	}
	
	tick();
}
void Win32Window::setFullscreenMode(Vec2i resolution, unsigned _depth) {
	
	SetWindowLong(m_hWnd, GWL_STYLE, WS_POPUP | WS_VISIBLE);
	
	depth_ = _depth;
	
	SetWindowPos(m_hWnd, HWND_TOP, 0, 0, resolution.x, resolution.y, SWP_SHOWWINDOW);
	
	if(!isFullscreen_) {
		isFullscreen_ = true;
		onToggleFullscreen();
	}
	
	onResize(resolution.x, resolution.y);
}
void SDLWindow::setWindowSize(Vec2i size) {
	
	if(!isFullscreen_ && size == getSize()) {
		return;
	}
	
	if(!setMode(DisplayMode(size, 0), false)) {
		return;
	}
	
	if(isFullscreen_) {
		isFullscreen_ = false;
		updateSize(true);
		onToggleFullscreen();
	}
}
void SDLWindow::setFullscreenMode(Vec2i resolution, unsigned _depth) {
	
	if(isFullscreen_ && size_ == resolution && depth_ == _depth) {
		return;
	}
	
	if(!setMode(DisplayMode(resolution, depth_), true)) {
		return;
	}
	
	if(!isFullscreen_) {
		isFullscreen_ = true;
		updateSize(true);
		onToggleFullscreen();
	} else {
		updateSize(true);
	}
	
}
bool SDL2Window::initialize() {

    arx_assert(!m_displayModes.empty());

    SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1);
    SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 24);

#if ARX_PLATFORM == ARX_PLATFORM_WIN32
    // Used on Windows to prevent software opengl fallback.
    // The linux situation:
    // Causes SDL to require visuals without caveats.
    // On linux some drivers only supply multisample capable GLX Visuals
    // with a GLX_NON_CONFORMANT_VISUAL_EXT caveat.
    // see: https://www.opengl.org/registry/specs/EXT/visual_rating.txt
    SDL_GL_SetAttribute(SDL_GL_ACCELERATED_VISUAL, 1);
#endif

    // TODO EGL and core profile are not supported yet
    SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_COMPATIBILITY);

    if(gldebug::isEnabled()) {
        SDL_GL_SetAttribute(SDL_GL_CONTEXT_FLAGS, SDL_GL_CONTEXT_DEBUG_FLAG);
    }


    int x = SDL_WINDOWPOS_UNDEFINED, y = SDL_WINDOWPOS_UNDEFINED;
    Uint32 windowFlags = getSDLFlagsForMode(m_size, m_fullscreen);
    windowFlags |= SDL_WINDOW_OPENGL | SDL_WINDOW_RESIZABLE | SDL_WINDOW_HIDDEN;

    for(int msaa = m_maxMSAALevel; msaa > 0; msaa--) {
        bool lastTry = (msaa == 1);

        // Cleanup context and window from previous tries
        if(m_glcontext) {
            SDL_GL_DeleteContext(m_glcontext);
            m_glcontext = NULL;
        }
        if(m_window) {
            SDL_DestroyWindow(m_window);
            m_window = NULL;
        }

        SDL_ClearError();

        SDL_GL_SetAttribute(SDL_GL_MULTISAMPLEBUFFERS, msaa > 1 ? 1 : 0);
        SDL_GL_SetAttribute(SDL_GL_MULTISAMPLESAMPLES, msaa > 1 ? msaa : 0);

        m_window = SDL_CreateWindow(m_title.c_str(), x, y, m_size.x, m_size.y, windowFlags);
        if(!m_window) {
            if(lastTry) {
                LogError << "Could not create window: " << SDL_GetError();
                return false;
            }
            continue;
        }

        m_glcontext = SDL_GL_CreateContext(m_window);
        if(!m_glcontext) {
            if(lastTry) {
                LogError << "Could not create GL context: " << SDL_GetError();
                return false;
            }
            continue;
        }

        // Verify that the MSAA setting matches what was requested
        int msaaEnabled, msaaValue;
        SDL_GL_GetAttribute(SDL_GL_MULTISAMPLEBUFFERS, &msaaEnabled);
        SDL_GL_GetAttribute(SDL_GL_MULTISAMPLESAMPLES, &msaaValue);
        if(!lastTry) {
            if(!msaaEnabled || msaaValue < msaa) {
                continue;
            }
        }
        if(msaaEnabled) {
            m_MSAALevel = msaaValue;
        } else {
            m_MSAALevel = 0;
        }

        // Verify that we actually got an accelerated context
        (void)glGetError(); // clear error flags
        GLint texunits = 0;
        glGetIntegerv(GL_MAX_TEXTURE_UNITS, &texunits);
        if(glGetError() != GL_NO_ERROR || texunits < GLint(m_minTextureUnits)) {
            if(lastTry) {
                LogError << "Not enough GL texture units available: have " << texunits
                         << ", need at least " << m_minTextureUnits;
                return false;
            }
            continue;
        }

        // All good
        const char * system = "(unknown)";
        {
            ARX_SDL_SysWMinfo info;
            info.version.major = 2;
            info.version.minor = 0;
            info.version.patch = 4;
            if(SDL_GetWindowWMInfo(m_window, reinterpret_cast<SDL_SysWMinfo *>(&info))) {
                switch(info.subsystem) {
                case ARX_SDL_SYSWM_UNKNOWN:
                    break;
                case ARX_SDL_SYSWM_WINDOWS:
                    system = "Windows";
                    break;
                case ARX_SDL_SYSWM_X11:
                    system = "X11";
                    break;
#if SDL_VERSION_ATLEAST(2, 0, 3)
                case ARX_SDL_SYSWM_WINRT:
                    system = "WinRT";
                    break;
#endif
                case ARX_SDL_SYSWM_DIRECTFB:
                    system = "DirectFB";
                    break;
                case ARX_SDL_SYSWM_COCOA:
                    system = "Cocoa";
                    break;
                case ARX_SDL_SYSWM_UIKIT:
                    system = "UIKit";
                    break;
#if SDL_VERSION_ATLEAST(2, 0, 2)
                case ARX_SDL_SYSWM_WAYLAND:
                    system = "Wayland";
                    break;
                case ARX_SDL_SYSWM_MIR:
                    system = "Mir";
                    break;
#endif
#if SDL_VERSION_ATLEAST(2, 0, 4)
                case ARX_SDL_SYSWM_ANDROID:
                    system = "Android";
                    break;
#endif
                }
            }
        }

        int red = 0, green = 0, blue = 0, alpha = 0, depth = 0, doublebuffer = 0;
        SDL_GL_GetAttribute(SDL_GL_RED_SIZE, &red);
        SDL_GL_GetAttribute(SDL_GL_GREEN_SIZE, &green);
        SDL_GL_GetAttribute(SDL_GL_BLUE_SIZE, &blue);
        SDL_GL_GetAttribute(SDL_GL_ALPHA_SIZE, &alpha);
        SDL_GL_GetAttribute(SDL_GL_DEPTH_SIZE, &depth);
        SDL_GL_GetAttribute(SDL_GL_DOUBLEBUFFER, &doublebuffer);
        LogInfo << "Window: " << system << " r:" << red << " g:" << green << " b:" << blue
                << " a:" << alpha << " depth:" << depth << " aa:" << msaa << "x"
                << " doublebuffer:" << doublebuffer;
        break;
    }

    // Use the executable icon for the window
#if ARX_PLATFORM == ARX_PLATFORM_WIN32
    {
        SDL_SysWMinfo info;
        SDL_VERSION(&info.version);
        if(SDL_GetWindowWMInfo(m_window, &info) && info.subsystem == SDL_SYSWM_WINDOWS) {
            platform::WideString filename;
            filename.allocate(filename.capacity());
            while(true) {
                DWORD size = GetModuleFileNameW(NULL, filename.data(), filename.size());
                if(size < filename.size()) {
                    filename.resize(size);
                    break;
                }
                filename.allocate(filename.size() * 2);
            }
            HICON largeIcon = 0;
            HICON smallIcon = 0;
            ExtractIconExW(filename, 0, &largeIcon, &smallIcon, 1);
            if(smallIcon) {
                SendMessage(info.info.win.window, WM_SETICON, ICON_SMALL, LPARAM(smallIcon));
            }
            if(largeIcon) {
                SendMessage(info.info.win.window, WM_SETICON, ICON_BIG, LPARAM(largeIcon));
            }
        }
    }
#endif

    setVSync(m_vsync);

    SDL_ShowWindow(m_window);
    SDL_ShowCursor(SDL_DISABLE);

    m_renderer->initialize();

    onCreate();
    onToggleFullscreen(m_fullscreen);
    updateSize(true);

    onShow(true);
    onFocus(true);

    return true;
}
bool SDL2Window::initialize() {
	
	arx_assert(!m_displayModes.empty());
	
	SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1);
	SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 24);
	
	// We need an accelerated OpenGL context or we'll likely fail later
	SDL_GL_SetAttribute(SDL_GL_ACCELERATED_VISUAL, 1);
	// TODO EGL and core profile are not supported yet
	SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_COMPATIBILITY);
	SDL_GL_SetAttribute(SDL_GL_CONTEXT_EGL, 0);
	
	if(gldebug::isEnabled()) {
		SDL_GL_SetAttribute(SDL_GL_CONTEXT_FLAGS, SDL_GL_CONTEXT_DEBUG_FLAG);
	}
	
	
	int x = SDL_WINDOWPOS_UNDEFINED, y = SDL_WINDOWPOS_UNDEFINED;
	Uint32 windowFlags = getSDLFlagsForMode(m_size, m_fullscreen);
	windowFlags |= SDL_WINDOW_OPENGL | SDL_WINDOW_RESIZABLE | SDL_WINDOW_HIDDEN;
	
	for(int msaa = m_maxMSAALevel; msaa > 0; msaa--) {
		bool lastTry = (msaa == 1);
		
		// Cleanup context and window from previous tries
		if(m_glcontext) {
			SDL_GL_DeleteContext(m_glcontext);
			m_glcontext = NULL;
		}
		if(m_window) {
			SDL_DestroyWindow(m_window);
			m_window = NULL;
		}
		
		SDL_ClearError();
		
		SDL_GL_SetAttribute(SDL_GL_MULTISAMPLEBUFFERS, msaa > 1 ? 1 : 0);
		SDL_GL_SetAttribute(SDL_GL_MULTISAMPLESAMPLES, msaa > 1 ? msaa : 0);
		
		m_window = SDL_CreateWindow(m_title.c_str(), x, y, m_size.x, m_size.y, windowFlags);
		if(!m_window) {
			if(lastTry) {
				LogError << "Could not create window: " << SDL_GetError();
				return false;
			}
			continue;
		}
		
		m_glcontext = SDL_GL_CreateContext(m_window);
		if(!m_glcontext) {
			if(lastTry) {
				LogError << "Could not create GL context: " << SDL_GetError();
				return false;
			}
			continue;
		}
		
		// Verify that the MSAA setting matches what was requested
		int msaaEnabled, msaaValue;
		SDL_GL_GetAttribute(SDL_GL_MULTISAMPLEBUFFERS, &msaaEnabled);
		SDL_GL_GetAttribute(SDL_GL_MULTISAMPLESAMPLES, &msaaValue);
		if(!lastTry) {
			if(!msaaEnabled || msaaValue < msaa) {
				continue;
			}
		}
		if(msaaEnabled) {
			m_MSAALevel = msaaValue;
		} else {
			m_MSAALevel = 0;
		}
		
		// Verify that we actually got an accelerated context
		(void)glGetError(); // clear error flags
		GLint texunits = 0;
		glGetIntegerv(GL_MAX_TEXTURE_UNITS, &texunits);
		if(glGetError() != GL_NO_ERROR || texunits < GLint(m_minTextureUnits)) {
			if(lastTry) {
				LogError << "Not enough GL texture units available: have " << texunits
				         << ", need at least " << m_minTextureUnits;
				return false;
			}
			continue;
		}
		
		// All good
		int red = 0, green = 0, blue = 0, alpha = 0, depth = 0, doublebuffer = 0;
		SDL_GL_GetAttribute(SDL_GL_RED_SIZE, &red);
		SDL_GL_GetAttribute(SDL_GL_GREEN_SIZE, &green);
		SDL_GL_GetAttribute(SDL_GL_BLUE_SIZE, &blue);
		SDL_GL_GetAttribute(SDL_GL_ALPHA_SIZE, &alpha);
		SDL_GL_GetAttribute(SDL_GL_DEPTH_SIZE, &depth);
		SDL_GL_GetAttribute(SDL_GL_DOUBLEBUFFER, &doublebuffer);
		LogInfo << "Window: r:" << red << " g:" << green << " b:" << blue << " a:" << alpha
		        << " depth:" << depth << " aa:" << msaa << "x doublebuffer:" << doublebuffer;
		break;
	}
	
	setVSync(m_vsync);
	
	SDL_ShowWindow(m_window);
	SDL_ShowCursor(SDL_DISABLE);
	
	m_renderer->initialize();
	
	onCreate();
	onToggleFullscreen(m_fullscreen);
	updateSize(true);
	
	onShow(true);
	onFocus(true);
	
	return true;
}