Source code for moderngl_window.context.glfw.window

from typing import Tuple
import glfw

from PIL import Image

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


class Window(BaseWindow):
    """
    Window based on GLFW
    """

    #: Name of the window
    name = "glfw"
    #: GLFW specific key constants
    keys = Keys

    # GLFW do support other buttons, but we are limited by other libraries
    _mouse_button_map = {
        0: 1,
        1: 2,
        2: 3,
    }

[docs] def __init__(self, **kwargs): super().__init__(**kwargs) if not glfw.init(): raise ValueError("Failed to initialize glfw") # Configure the OpenGL context glfw.window_hint(glfw.CONTEXT_CREATION_API, glfw.NATIVE_CONTEXT_API) glfw.window_hint(glfw.CLIENT_API, glfw.OPENGL_API) glfw.window_hint(glfw.CONTEXT_VERSION_MAJOR, self.gl_version[0]) glfw.window_hint(glfw.CONTEXT_VERSION_MINOR, self.gl_version[1]) glfw.window_hint(glfw.OPENGL_PROFILE, glfw.OPENGL_CORE_PROFILE) glfw.window_hint(glfw.OPENGL_FORWARD_COMPAT, True) glfw.window_hint(glfw.RESIZABLE, self.resizable) glfw.window_hint(glfw.DOUBLEBUFFER, True) glfw.window_hint(glfw.DEPTH_BITS, 24) glfw.window_hint(glfw.STENCIL_BITS, 8) glfw.window_hint(glfw.SAMPLES, self.samples) glfw.window_hint(glfw.SCALE_TO_MONITOR, glfw.TRUE) monitor = None self._window = glfw.create_window( self.width, self.height, self.title, monitor, None ) self._has_focus = True if self.fullscreen: self._set_fullscreen(True) if not self._window: glfw.terminate() raise ValueError("Failed to create window") self.cursor = self._cursor self._buffer_width, self._buffer_height = glfw.get_framebuffer_size( self._window ) glfw.make_context_current(self._window) if self.vsync: glfw.swap_interval(1) else: glfw.swap_interval(0) glfw.set_key_callback(self._window, self.glfw_key_event_callback) glfw.set_cursor_pos_callback(self._window, self.glfw_mouse_event_callback) glfw.set_mouse_button_callback(self._window, self.glfw_mouse_button_callback) glfw.set_scroll_callback(self._window, self.glfw_mouse_scroll_callback) glfw.set_window_size_callback(self._window, self.glfw_window_resize_callback) glfw.set_char_callback(self._window, self.glfw_char_callback) glfw.set_window_focus_callback(self._window, self.glfw_window_focus) glfw.set_cursor_enter_callback(self._window, self.glfw_cursor_enter) glfw.set_window_iconify_callback(self._window, self.glfw_window_iconify) glfw.set_window_close_callback(self._window, self.glfw_window_close) self.init_mgl_context() self.set_default_viewport()
def _set_fullscreen(self, value: bool) -> None: monitor = glfw.get_primary_monitor() mode = glfw.get_video_mode(monitor) refresh_rate = mode.refresh_rate if self.vsync else glfw.DONT_CARE self.resizable = not value glfw.window_hint(glfw.RESIZABLE, self.resizable) if value: # enable fullscreen self._non_fullscreen_size = self.width, self.height self._non_fullscreen_position = self.position glfw.set_window_monitor( self._window, monitor, 0, 0, mode.size.width, mode.size.height, refresh_rate, ) glfw.window_hint(glfw.RED_BITS, mode.bits.red) glfw.window_hint(glfw.GREEN_BITS, mode.bits.green) glfw.window_hint(glfw.BLUE_BITS, mode.bits.blue) glfw.window_hint(glfw.REFRESH_RATE, mode.refresh_rate) else: # disable fullscreen glfw.set_window_monitor( self._window, None, *self._non_fullscreen_position, *self._non_fullscreen_size, refresh_rate ) if self.vsync: glfw.swap_interval(1) else: glfw.swap_interval(0) def _set_vsync(self, value: bool) -> None: glfw.swap_interval(value) @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]): glfw.set_window_size(self._window, 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 glfw.get_window_pos(self._window) @position.setter def position(self, value: Tuple[int, int]): self._position = glfw.set_window_pos(self._window, value[0], value[1]) @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): if not self.mouse_exclusivity: if value is True: glfw.set_input_mode(self._window, glfw.CURSOR, glfw.CURSOR_NORMAL) elif value is False: glfw.set_input_mode(self._window, glfw.CURSOR, glfw.CURSOR_HIDDEN) 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): self._mouse_exclusivity = value if value is True: self._mouse_pos = glfw.get_cursor_pos(self._window) glfw.set_input_mode(self._window, glfw.CURSOR, glfw.CURSOR_DISABLED) else: self.cursor = self._cursor @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): glfw.set_window_title(self._window, value) self._title = value
[docs] def close(self) -> None: """Suggest to glfw the window should be closed soon""" self.is_closing = True self._close_func()
@property def is_closing(self): """bool: Checks if the window is scheduled for closing""" return glfw.window_should_close(self._window) @is_closing.setter def is_closing(self, value: bool): glfw.set_window_should_close(self._window, value)
[docs] def swap_buffers(self): """Swap buffers, increment frame counter and pull events""" glfw.swap_buffers(self._window) self._frames += 1 glfw.poll_events()
def _handle_modifiers(self, mods): """Checks key modifiers""" self._modifiers.shift = mods & 1 == 1 self._modifiers.ctrl = mods & 2 == 2 self._modifiers.alt = mods & 4 == 4 def _set_icon(self, icon_path) -> None: image = Image.open(icon_path) glfw.set_window_icon(self._window, 1, image)
[docs] def glfw_key_event_callback(self, window, key, scancode, action, mods): """Key event callback for glfw. Translates and forwards keyboard event to :py:func:`keyboard_event` Args: window: Window event origin key: The key that was pressed or released. scancode: The system-specific scancode of the key. action: ``GLFW_PRESS``, ``GLFW_RELEASE`` or ``GLFW_REPEAT`` mods: Bit field describing which modifier keys were held down. """ if self.exit_key is not None and key == self._exit_key: self.close() if action == self.keys.ACTION_PRESS and self._fs_key is not None and key == self._fs_key: self.fullscreen = not self.fullscreen self._handle_modifiers(mods) if action == self.keys.ACTION_PRESS: self._key_pressed_map[key] = True elif action == self.keys.ACTION_RELEASE: self._key_pressed_map[key] = False self._key_event_func(key, action, self._modifiers)
[docs] def glfw_mouse_event_callback(self, window, xpos, ypos): """Mouse position event callback from glfw. Translates the events forwarding them to :py:func:`cursor_event`. Screen coordinates relative to the top-left corner Args: window: The window xpos: viewport x pos ypos: viewport y pos """ xpos, ypos = int(xpos), int(ypos) dx, dy = self._calc_mouse_delta(xpos, ypos) if self.mouse_states.any: self._mouse_drag_event_func(xpos, ypos, dx, dy) else: self._mouse_position_event_func(xpos, ypos, dx, dy)
[docs] def glfw_mouse_button_callback(self, window, button, action, mods): """Handle mouse button events and forward them to the example Args: window: The window button: The button creating the event action: Button action (press or release) mods: They modifiers such as ctrl or shift """ self._handle_modifiers(mods) button = self._mouse_button_map.get(button, None) if button is None: return xpos, ypos = glfw.get_cursor_pos(self._window) if action == glfw.PRESS: self._handle_mouse_button_state_change(button, True) self._mouse_press_event_func(xpos, ypos, button) else: self._handle_mouse_button_state_change(button, False) self._mouse_release_event_func(xpos, ypos, button)
[docs] def glfw_mouse_scroll_callback(self, window, x_offset: float, y_offset: float): """Handle mouse scroll events and forward them to the example Args: window: The window x_offset (float): x wheel offset y_offest (float): y wheel offset """ self._mouse_scroll_event_func(x_offset, y_offset)
[docs] def glfw_char_callback(self, window, codepoint: int): """Handle text input (only unicode charaters) Args: window: The glfw window codepoint (int): The unicode codepoint """ self._unicode_char_entered_func(chr(codepoint))
[docs] def glfw_window_resize_callback(self, window, width, height): """ Window resize callback for glfw Args: window: The window width: New width height: New height """ self._width, self._height = width, height self._buffer_width, self._buffer_height = glfw.get_framebuffer_size( self._window ) self.set_default_viewport() super().resize(self._buffer_width, self._buffer_height)
[docs] def glfw_window_focus(self, window, focused: int): """Called when the window focus is changed. Args: window: The window instance focused (int): 0: de-focus, 1: focused """ self._has_focus = True if focused == 1 else False
[docs] def glfw_cursor_enter(self, window, enter: int): """called when the cursor enters or leaves the content area of the window. Args: window: the window instance enter (int): 0: leave, 1: enter """ pass
[docs] def glfw_window_iconify(self, window, iconified: int): """Called when the window is minimized or restored. Args: window: The window iconified (int): 1 = minimized, 0 = restored. """ self._iconify_func(True if iconified == 1 else False)
[docs] def glfw_window_close(self, window): """Called when the window is closed""" self.close()
[docs] def destroy(self): """Gracefully terminate GLFW""" glfw.terminate()