File: window.py

package info (click to toggle)
python-moderngl-window 3.1.1-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 69,096 kB
  • sloc: python: 12,076; makefile: 21
file content (136 lines) | stat: -rw-r--r-- 4,229 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
from pathlib import Path
from typing import Any, Optional

import moderngl

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


class Window(BaseWindow):
    """Headless window.

    Do not currently support any form window events or key input.
    """

    #: Name of the window
    name = "headless"
    keys = Keys

    def __init__(self, **kwargs: Any):
        super().__init__(**kwargs)
        self._fbo: Optional[moderngl.Framebuffer] = None
        self._vsync = False  # We don't care about vsync in headless mode
        self._resizable = False  # headless window is not resizable
        self._cursor = False  # Headless don't have a cursor
        self._headless = True
        self.init_mgl_context()
        self.set_default_viewport()

    @property
    def fbo(self) -> moderngl.Framebuffer:
        """moderngl.Framebuffer: The default framebuffer"""
        if self._fbo is None:
            raise RuntimeError("No framebuffer created yet")
        return self._fbo

    def init_mgl_context(self) -> None:
        """Create an standalone context and framebuffer"""
        if self._backend is not None:
            self._ctx = moderngl.create_standalone_context(
                require=self.gl_version_code,
                backend=self._backend,
            )
        else:
            self._ctx = moderngl.create_standalone_context(
                require=self.gl_version_code,
            )

        self._create_fbo()
        self.use()

    def _create_fbo(self) -> None:
        if self._fbo:
            for attachment in self._fbo.color_attachments:
                attachment.release()
            if self._fbo.depth_attachment:
                self._fbo.depth_attachment.release()
            self._fbo.release()

        self._fbo = self.ctx.framebuffer(
            color_attachments=self.ctx.texture(self.size, 4, samples=self._samples),
            depth_attachment=self.ctx.depth_texture(self.size, samples=self._samples),
        )

    @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:
        if value == (self._width, self._height):
            return
        self._width, self._height = value
        self._create_fbo()

    def use(self) -> None:
        """Bind the window's framebuffer"""
        if self._fbo is None:
            raise RuntimeError("No framebuffer created yet")
        self._fbo.use()

    def clear(
        self,
        red: float = 0.0,
        green: float = 0.0,
        blue: float = 0.0,
        alpha: float = 0.0,
        depth: float = 1.0,
        viewport: Optional[tuple[int, int, int, int]] = None,
    ) -> None:
        """
        Binds and clears the default framebuffer

        Args:
            red (float): color component
            green (float): color component
            blue (float): color component
            alpha (float): alpha component
            depth (float): depth value
            viewport (tuple): The viewport
        """
        self.use()
        self._ctx.clear(
            red=red, green=green, blue=blue, alpha=alpha, depth=depth, viewport=viewport
        )

    def swap_buffers(self) -> None:
        """
        Placeholder. We currently don't do double buffering in headless mode.
        This may change in the future.
        """
        # NOTE: No double buffering currently
        self._frames += 1
        self._ctx.finish()

    def _set_icon(self, icon_path: Path) -> None:
        """Do nothing when icon is set"""
        pass

    def _set_fullscreen(self, value: bool) -> None:
        """Do nothing when fullscreen is toggled"""
        pass

    def _set_vsync(self, value: bool) -> None:
        pass

    def destroy(self) -> None:
        """Destroy the context"""
        self._ctx.release()