# Source code for moderngl_window.context.pygame2.window

    from pathlib import Path
    from typing import Any

    import pygame
    import pygame._sdl2
    import pygame.display
    import pygame.event

    from moderngl_window.context.base import BaseWindow
    from moderngl_window.context.pygame2.keys import Keys

    class Window(BaseWindow):
        """
        Basic window implementation using pygame2.
        """

        #: Name of the window
        name = "pygame2"
        #: pygame specific key constants
        keys = Keys

        _mouse_button_map = {
            1: 1,
            3: 2,
            2: 3,
        }

        def __init__(self, **kwargs: Any):
            super().__init__(**kwargs)

            pygame.display.init()

            pygame.display.gl_set_attribute(pygame.GL_CONTEXT_MAJOR_VERSION, self.gl_version[0])
            pygame.display.gl_set_attribute(pygame.GL_CONTEXT_MINOR_VERSION, self.gl_version[1])
            pygame.display.gl_set_attribute(
                pygame.GL_CONTEXT_PROFILE_MASK, pygame.GL_CONTEXT_PROFILE_CORE
            )
            pygame.display.gl_set_attribute(pygame.GL_CONTEXT_FORWARD_COMPATIBLE_FLAG, 1)
            pygame.display.gl_set_attribute(pygame.GL_DOUBLEBUFFER, 1)
            pygame.display.gl_set_attribute(pygame.GL_DEPTH_SIZE, 24)
            pygame.display.gl_set_attribute(pygame.GL_STENCIL_SIZE, 8)

            if self.samples > 1:
                pygame.display.gl_set_attribute(pygame.GL_MULTISAMPLEBUFFERS, 1)
                pygame.display.gl_set_attribute(pygame.GL_MULTISAMPLESAMPLES, self.samples)

            self._depth = 24
            self._flags = pygame.OPENGL | pygame.DOUBLEBUF

            if self.resizable:
                self._flags |= pygame.RESIZABLE

            if not self._visible:
                self._flags |= pygame.HIDDEN

            self._set_mode()
            self.title = self._title
            self.cursor = self._cursor
            # Get the reference for the internal sdl2 window
            # Makes us able to control window position and other properties.
            self._sdl_window = pygame._sdl2.video.Window.from_display_module()

            if self.fullscreen:
                self._set_fullscreen(True)

            self.init_mgl_context()
            self.set_default_viewport()

        def _set_mode(self) -> None:
            self._surface = pygame.display.set_mode(
                size=(self._width, self._height),
                flags=self._flags,
                depth=self._depth,
                vsync=self._vsync,
            )

        def _set_fullscreen(self, value: bool) -> None:
            if value:
                self._sdl_window.set_fullscreen(True)
            else:
                self._sdl_window.set_windowed()

        def _set_vsync(self, value: bool) -> None:
            self._vsync = value
            self._set_mode()

        @property
        def size(self) -> tuple[int, int]:
            """tuple[int, int]: current window size.

            This property also support assignment::

                # Resize the window to 1000 x 1000
                window.size = 1000, 1000
            """
            return self._width, self._height

        @size.setter
        def size(self, value: tuple[int, int]) -> None:
            self._width, self._height = value
            self._set_mode()
            self.resize(value[0], value[1])

        @property
        def position(self) -> tuple[int, int]:
            """tuple[int, int]: The current window position.

            This property can also be set to move the window::

                # Move window to 100, 100
                window.position = 100, 100
            """
            return self._sdl_window.position

        @position.setter
        def position(self, value: tuple[int, int]) -> None:
            self._sdl_window.position = value

        @property
        def visible(self) -> bool:
            """bool: Is the window visible?

            This property can also be set::

                # Hide or show the window
                window.visible = False
            """
            return self._visible

        @visible.setter
        def visible(self, value: bool) -> None:
            self._visible = value
            if value:
                self._sdl_window.show()
            else:
                self._sdl_window.hide()

        @property
        def cursor(self) -> bool:
            """bool: Should the mouse cursor be visible inside the window?

            This property can also be assigned to::

                # Disable cursor
                window.cursor = False
            """
            return self._cursor

        @cursor.setter
        def cursor(self, value: bool) -> None:
            pygame.mouse.set_visible(value)
            self._cursor = value

        @property
        def mouse_exclusivity(self) -> bool:
            """bool: If mouse exclusivity is enabled.

            When you enable mouse-exclusive mode, the mouse cursor is no longer
            available. It is not merely hidden – no amount of mouse movement
            will make it leave your application. This is for example useful
            when you don't want the mouse leaving the screen when rotating
            a 3d scene.

            This property can also be set::

                window.mouse_exclusivity = True
            """
            return self._mouse_exclusivity

        @mouse_exclusivity.setter
        def mouse_exclusivity(self, value: bool) -> None:
            if self._cursor:
                self.cursor = False

            pygame.event.set_grab(value)
            self._mouse_exclusivity = value

        @property
        def title(self) -> str:
            """str: Window title.

            This property can also be set::

                window.title = "New Title"
            """
            return self._title

        @title.setter
        def title(self, value: str) -> None:
            pygame.display.set_caption(value)
            self._title = value

        def swap_buffers(self) -> None:
            """Swap buffers, set viewport, trigger events and increment frame counter"""
            pygame.display.flip()
            self.set_default_viewport()
            self.process_events()
            self._frames += 1

        def _set_icon(self, icon_path: Path) -> None:
            icon = pygame.image.load(icon_path)
            pygame.display.set_icon(icon)

        def resize(self, width: int, height: int) -> None:
            """Resize callback

            Args:
                width: New window width
                height: New window height
            """
            self._width = width
            self._height = height
            self._buffer_width, self._buffer_height = self._width, self._height
            self.set_default_viewport()

            super().resize(self._buffer_width, self._buffer_height)

        def close(self) -> None:
            """Close the window"""
            super().close()
            self._close_func()

        def _handle_mods(self) -> None:
            """Update key mods"""
            mods = pygame.key.get_mods()
            self._modifiers.shift = mods & pygame.KMOD_SHIFT
            self._modifiers.ctrl = mods & pygame.KMOD_CTRL
            self._modifiers.alt = mods & pygame.KMOD_ALT

        def process_events(self) -> None:
            """Handle all queued events in pygame2 dispatching events to standard methods"""

            for event in pygame.event.get():
                if event.type == pygame.MOUSEMOTION:
                    self._handle_mods()
                    if self.mouse_states.any:
                        self._mouse_drag_event_func(
                            event.pos[0],
                            event.pos[1],
                            event.rel[0],
                            event.rel[1],
                        )
                    else:
                        self._mouse_position_event_func(
                            event.pos[0],
                            event.pos[1],
                            event.rel[0],
                            event.rel[1],
                        )

                elif event.type == pygame.MOUSEBUTTONDOWN:
                    self._handle_mods()
                    button = self._mouse_button_map.get(event.button, None)
                    if button is not None:
                        self._handle_mouse_button_state_change(button, True)
                        self._mouse_press_event_func(
                            event.pos[0],
                            event.pos[1],
                            button,
                        )

                elif event.type == pygame.MOUSEBUTTONUP:
                    self._handle_mods()
                    button = self._mouse_button_map.get(event.button, None)
                    if button is not None:
                        self._handle_mouse_button_state_change(button, False)
                        self._mouse_release_event_func(
                            event.pos[0],
                            event.pos[1],
                            button,
                        )

                elif event.type in [pygame.KEYDOWN, pygame.KEYUP]:
                    self._handle_mods()

                    if self._exit_key is not None and event.key == self._exit_key:
                        self.close()

                    # Pygame can't do fullscreen yet, but this would toggle it.
                    if (
                        event.type == pygame.KEYUP
                        and self._fs_key is not None
                        and event.key == self._fs_key
                    ):
                        self.fullscreen = not self.fullscreen

                    if event.type == pygame.KEYDOWN:
                        self._key_pressed_map[event.key] = True
                    elif event.type == pygame.KEYUP:
                        self._key_pressed_map[event.key] = False

                    self._key_event_func(event.key, event.type, self._modifiers)

                elif event.type == pygame.TEXTINPUT:
                    self._handle_mods()
                    self._unicode_char_entered_func(event.text)

                elif event.type == pygame.MOUSEWHEEL:
                    self._handle_mods()
                    self._mouse_scroll_event_func(float(event.x), float(event.y))

                elif event.type == pygame.QUIT:
                    self.close()

                elif event.type == pygame.VIDEORESIZE:
                    self.resize(event.size[0], event.size[1])

                elif event.type == pygame.ACTIVEEVENT:
                    # # We might support these in the future
                    # Mouse cursor state
                    # if event.state == 0:
                    #     if event.gain:
                    #         print("Mouse enters viewport")
                    #     else:
                    #         print("Mouse leaves viewport")

                    # Window focus state
                    # if event.state == 1:
                    #     if event.gain:
                    #         print("Window gained focus")
                    #     else:
                    #         print("Window lost focus")

                    # Window iconify state
                    if getattr(event, "state", None) == 2:
                        if event.gain:
                            self._visible = True
                            self._iconify_func(False)
                        else:
                            self._visible = False
                            self._iconify_func(True)

                # This is also a problem on linux, but is too disruptive during resize events
                # elif event.type == pygame.VIDEOEXPOSE:
                #     # On OS X we only get VIDEOEXPOSE when restoring the windoe
                #     self._iconify_func(False)

                elif event.type == pygame.USEREVENT:
                    self._on_generic_event_func(event)

        def destroy(self) -> None:
            """Gracefully close the window"""
            pygame.quit()