glpg-pygame 1.0.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- glpg/__init__.py +72 -0
- glpg/_color.py +40 -0
- glpg/_state.py +21 -0
- glpg/draw.py +204 -0
- glpg/input.py +168 -0
- glpg/loop.py +148 -0
- glpg/window.py +97 -0
- glpg_pygame-1.0.0.dist-info/METADATA +180 -0
- glpg_pygame-1.0.0.dist-info/RECORD +12 -0
- glpg_pygame-1.0.0.dist-info/WHEEL +5 -0
- glpg_pygame-1.0.0.dist-info/licenses/LICENSE +21 -0
- glpg_pygame-1.0.0.dist-info/top_level.txt +1 -0
glpg/__init__.py
ADDED
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
# glpg/__init__.py — public API
|
|
2
|
+
"""
|
|
3
|
+
GLPG — GameLib for Pygame
|
|
4
|
+
A shorthand wrapper that makes pygame fast and approachable.
|
|
5
|
+
|
|
6
|
+
MIT License 2026
|
|
7
|
+
|
|
8
|
+
Quickstart (managed loop):
|
|
9
|
+
import glpg as gl
|
|
10
|
+
|
|
11
|
+
gl.window("720p", title="My Game")
|
|
12
|
+
|
|
13
|
+
@gl.on_update
|
|
14
|
+
def update(dt):
|
|
15
|
+
if gl.key("escape"):
|
|
16
|
+
gl.quit()
|
|
17
|
+
|
|
18
|
+
@gl.on_draw
|
|
19
|
+
def draw():
|
|
20
|
+
gl.draw.rect(100, 100, 64, 64, "#e63946")
|
|
21
|
+
gl.draw.text("Hello GLPG", 20, 20, size=32, color="white")
|
|
22
|
+
|
|
23
|
+
gl.run()
|
|
24
|
+
|
|
25
|
+
Quickstart (manual loop):
|
|
26
|
+
import glpg as gl
|
|
27
|
+
|
|
28
|
+
gl.window("720p", title="My Game")
|
|
29
|
+
|
|
30
|
+
while (dt := gl.tick()) is not None:
|
|
31
|
+
if gl.key("escape"):
|
|
32
|
+
gl.quit()
|
|
33
|
+
gl.draw.rect(100, 100, 64, 64, "red")
|
|
34
|
+
gl.flip()
|
|
35
|
+
"""
|
|
36
|
+
|
|
37
|
+
import pygame as _pg
|
|
38
|
+
|
|
39
|
+
# ── window ────────────────────────────────────────────────────────────────
|
|
40
|
+
from .window import (
|
|
41
|
+
window,
|
|
42
|
+
get_screen,
|
|
43
|
+
get_size,
|
|
44
|
+
get_width,
|
|
45
|
+
get_height,
|
|
46
|
+
set_title,
|
|
47
|
+
set_fps,
|
|
48
|
+
set_bg_color,
|
|
49
|
+
)
|
|
50
|
+
|
|
51
|
+
# ── loop ──────────────────────────────────────────────────────────────────
|
|
52
|
+
from .loop import (
|
|
53
|
+
on_update,
|
|
54
|
+
on_draw,
|
|
55
|
+
run,
|
|
56
|
+
tick,
|
|
57
|
+
flip,
|
|
58
|
+
quit,
|
|
59
|
+
is_running,
|
|
60
|
+
get_fps,
|
|
61
|
+
)
|
|
62
|
+
|
|
63
|
+
# ── input ─────────────────────────────────────────────────────────────────
|
|
64
|
+
from .input import key, key_pressed, key_released, mouse
|
|
65
|
+
|
|
66
|
+
# ── draw (namespace) ──────────────────────────────────────────────────────
|
|
67
|
+
from . import draw
|
|
68
|
+
|
|
69
|
+
# ── meta ──────────────────────────────────────────────────────────────────
|
|
70
|
+
__version__ = "1.0.0"
|
|
71
|
+
__author__ = "GLPG"
|
|
72
|
+
__license__ = "MIT"
|
glpg/_color.py
ADDED
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
# glpg/_color.py — universal color parser
|
|
2
|
+
|
|
3
|
+
import pygame as pg
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
def parse(color):
|
|
7
|
+
"""
|
|
8
|
+
Accept any of:
|
|
9
|
+
- RGB / RGBA tuple → (255, 0, 0) | (255, 0, 0, 128)
|
|
10
|
+
- Hex string → "#ff0000" | "#ff0000ff"
|
|
11
|
+
- Named string → "red" "white" "cornflowerblue" etc.
|
|
12
|
+
Returns an (R, G, B) or (R, G, B, A) tuple.
|
|
13
|
+
"""
|
|
14
|
+
if isinstance(color, (tuple, list)):
|
|
15
|
+
if len(color) in (3, 4):
|
|
16
|
+
return tuple(int(c) for c in color)
|
|
17
|
+
raise ValueError(f"Color tuple must have 3 or 4 values, got {len(color)}: {color}")
|
|
18
|
+
|
|
19
|
+
if isinstance(color, str):
|
|
20
|
+
stripped = color.strip()
|
|
21
|
+
|
|
22
|
+
if stripped.startswith("#"):
|
|
23
|
+
h = stripped.lstrip("#")
|
|
24
|
+
if len(h) == 6:
|
|
25
|
+
return (int(h[0:2], 16), int(h[2:4], 16), int(h[4:6], 16))
|
|
26
|
+
if len(h) == 8:
|
|
27
|
+
return (int(h[0:2], 16), int(h[2:4], 16), int(h[4:6], 16), int(h[6:8], 16))
|
|
28
|
+
raise ValueError(f"Invalid hex color: '{color}' — expected #RRGGBB or #RRGGBBAA")
|
|
29
|
+
|
|
30
|
+
# Named color — let pygame validate it
|
|
31
|
+
try:
|
|
32
|
+
c = pg.Color(stripped)
|
|
33
|
+
return (c.r, c.g, c.b) if c.a == 255 else (c.r, c.g, c.b, c.a)
|
|
34
|
+
except ValueError:
|
|
35
|
+
raise ValueError(
|
|
36
|
+
f"Unknown color name: '{color}'. "
|
|
37
|
+
"Use a pygame color name, hex string, or RGB tuple."
|
|
38
|
+
)
|
|
39
|
+
|
|
40
|
+
raise TypeError(f"Unsupported color type: {type(color).__name__} — value: {color!r}")
|
glpg/_state.py
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
# glpg/_state.py — internal shared state across all modules
|
|
2
|
+
|
|
3
|
+
import pygame as pg
|
|
4
|
+
|
|
5
|
+
# Display
|
|
6
|
+
screen = None
|
|
7
|
+
clock = None
|
|
8
|
+
fps = 60
|
|
9
|
+
bg_color = (0, 0, 0)
|
|
10
|
+
|
|
11
|
+
# Loop
|
|
12
|
+
running = False
|
|
13
|
+
|
|
14
|
+
# Input — reset/updated every frame by input._process_events()
|
|
15
|
+
_events = []
|
|
16
|
+
_keys_pressed = set() # fired only on first-frame press
|
|
17
|
+
_keys_released = set() # fired only on release frame
|
|
18
|
+
_keys_held = set() # true for every frame the key is down
|
|
19
|
+
_mouse_clicked = set() # fired only on click frame
|
|
20
|
+
_mouse_held = set() # true for every frame button is down
|
|
21
|
+
_mouse_pos = (0, 0)
|
glpg/draw.py
ADDED
|
@@ -0,0 +1,204 @@
|
|
|
1
|
+
# glpg/draw.py — drawing helpers
|
|
2
|
+
|
|
3
|
+
import pygame as pg
|
|
4
|
+
from . import _state
|
|
5
|
+
from . import _color as _c
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
# ── font cache ───────────────────────────────────────────────────────────────
|
|
9
|
+
|
|
10
|
+
_font_cache: dict[tuple, pg.font.Font] = {}
|
|
11
|
+
_image_cache: dict[str, pg.Surface] = {}
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def _font(name: str | None, size: int) -> pg.font.Font:
|
|
15
|
+
key = (name, size)
|
|
16
|
+
if key not in _font_cache:
|
|
17
|
+
if name is None:
|
|
18
|
+
_font_cache[key] = pg.font.SysFont(None, size)
|
|
19
|
+
else:
|
|
20
|
+
try:
|
|
21
|
+
_font_cache[key] = pg.font.SysFont(name, size)
|
|
22
|
+
except Exception:
|
|
23
|
+
_font_cache[key] = pg.font.SysFont(None, size)
|
|
24
|
+
return _font_cache[key]
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
def _surf() -> pg.Surface:
|
|
28
|
+
if _state.screen is None:
|
|
29
|
+
raise RuntimeError("Call gl.window() before drawing.")
|
|
30
|
+
return _state.screen
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
# ── shapes ───────────────────────────────────────────────────────────────────
|
|
34
|
+
|
|
35
|
+
def rect(x, y, w, h, color="white", border: int = 0, radius: int = 0):
|
|
36
|
+
"""
|
|
37
|
+
Draw a rectangle.
|
|
38
|
+
|
|
39
|
+
Args:
|
|
40
|
+
x, y : Top-left position.
|
|
41
|
+
w, h : Width and height.
|
|
42
|
+
color : Fill colour (name / hex / RGB tuple).
|
|
43
|
+
border : Line width in pixels; 0 = filled.
|
|
44
|
+
radius : Corner radius for rounded rectangles.
|
|
45
|
+
|
|
46
|
+
Example:
|
|
47
|
+
gl.draw.rect(100, 100, 64, 64, "#e63946")
|
|
48
|
+
gl.draw.rect(100, 100, 64, 64, "white", border=2, radius=8)
|
|
49
|
+
"""
|
|
50
|
+
pg.draw.rect(_surf(), _c.parse(color), (x, y, w, h), border, border_radius=radius)
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
def circle(x, y, radius, color="white", border: int = 0):
|
|
54
|
+
"""
|
|
55
|
+
Draw a circle.
|
|
56
|
+
|
|
57
|
+
Args:
|
|
58
|
+
x, y : Centre position.
|
|
59
|
+
radius : Radius in pixels.
|
|
60
|
+
color : Colour (name / hex / RGB tuple).
|
|
61
|
+
border : Line width; 0 = filled.
|
|
62
|
+
|
|
63
|
+
Example:
|
|
64
|
+
gl.draw.circle(200, 200, 40, "cyan")
|
|
65
|
+
"""
|
|
66
|
+
pg.draw.circle(_surf(), _c.parse(color), (int(x), int(y)), int(radius), border)
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
def ellipse(x, y, w, h, color="white", border: int = 0):
|
|
70
|
+
"""
|
|
71
|
+
Draw an ellipse inside the bounding box (x, y, w, h).
|
|
72
|
+
|
|
73
|
+
Example:
|
|
74
|
+
gl.draw.ellipse(100, 100, 200, 80, "yellow")
|
|
75
|
+
"""
|
|
76
|
+
pg.draw.ellipse(_surf(), _c.parse(color), (int(x), int(y), int(w), int(h)), border)
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
def line(x1, y1, x2, y2, color="white", width: int = 1):
|
|
80
|
+
"""
|
|
81
|
+
Draw a line from (x1, y1) to (x2, y2).
|
|
82
|
+
|
|
83
|
+
Example:
|
|
84
|
+
gl.draw.line(0, 0, 400, 300, "#ff0000", width=3)
|
|
85
|
+
"""
|
|
86
|
+
pg.draw.line(_surf(), _c.parse(color), (x1, y1), (x2, y2), max(1, width))
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
def polygon(points: list[tuple], color="white", border: int = 0):
|
|
90
|
+
"""
|
|
91
|
+
Draw a polygon from a list of (x, y) points.
|
|
92
|
+
|
|
93
|
+
Example:
|
|
94
|
+
gl.draw.polygon([(100,50), (150,150), (50,150)], "green")
|
|
95
|
+
"""
|
|
96
|
+
pg.draw.polygon(_surf(), _c.parse(color), points, border)
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
# ── text ─────────────────────────────────────────────────────────────────────
|
|
100
|
+
|
|
101
|
+
def text(
|
|
102
|
+
content,
|
|
103
|
+
x, y,
|
|
104
|
+
size: int = 24,
|
|
105
|
+
color="white",
|
|
106
|
+
font: str | None = None,
|
|
107
|
+
center: bool = False,
|
|
108
|
+
antialias: bool = True,
|
|
109
|
+
) -> pg.Rect:
|
|
110
|
+
"""
|
|
111
|
+
Draw text at (x, y).
|
|
112
|
+
|
|
113
|
+
Args:
|
|
114
|
+
content : String (or anything str()-able) to render.
|
|
115
|
+
x, y : Position (top-left unless center=True).
|
|
116
|
+
size : Font size in points.
|
|
117
|
+
color : Text colour.
|
|
118
|
+
font : System font name, or None for the default font.
|
|
119
|
+
center : If True, the text is centred on (x, y).
|
|
120
|
+
antialias : Smooth edges (default True).
|
|
121
|
+
|
|
122
|
+
Returns:
|
|
123
|
+
pygame.Rect of the rendered text.
|
|
124
|
+
|
|
125
|
+
Example:
|
|
126
|
+
gl.draw.text("Score: 0", 20, 20, size=32, color="gold")
|
|
127
|
+
gl.draw.text("PAUSED", cx, cy, size=64, color="white", center=True)
|
|
128
|
+
"""
|
|
129
|
+
f = _font(font, size)
|
|
130
|
+
surf = f.render(str(content), antialias, _c.parse(color))
|
|
131
|
+
if center:
|
|
132
|
+
r = surf.get_rect(center=(int(x), int(y)))
|
|
133
|
+
else:
|
|
134
|
+
r = surf.get_rect(topleft=(int(x), int(y)))
|
|
135
|
+
_surf().blit(surf, r)
|
|
136
|
+
return r
|
|
137
|
+
|
|
138
|
+
|
|
139
|
+
# ── image ────────────────────────────────────────────────────────────────────
|
|
140
|
+
|
|
141
|
+
def image(
|
|
142
|
+
path: str,
|
|
143
|
+
x, y,
|
|
144
|
+
scale: float = 1.0,
|
|
145
|
+
center: bool = False,
|
|
146
|
+
cache: bool = True,
|
|
147
|
+
) -> pg.Rect:
|
|
148
|
+
"""
|
|
149
|
+
Draw an image from a file path.
|
|
150
|
+
|
|
151
|
+
Args:
|
|
152
|
+
path : Relative or absolute path to the image file.
|
|
153
|
+
x, y : Position (top-left unless center=True).
|
|
154
|
+
scale : Scale multiplier (1.0 = original size).
|
|
155
|
+
center : If True, the image is centred on (x, y).
|
|
156
|
+
cache : Cache the loaded surface (default True).
|
|
157
|
+
|
|
158
|
+
Returns:
|
|
159
|
+
pygame.Rect of the drawn image.
|
|
160
|
+
|
|
161
|
+
Example:
|
|
162
|
+
gl.draw.image("assets/player.png", player.x, player.y)
|
|
163
|
+
gl.draw.image("assets/logo.png", cx, cy, scale=0.5, center=True)
|
|
164
|
+
"""
|
|
165
|
+
if cache and path in _image_cache:
|
|
166
|
+
surf = _image_cache[path]
|
|
167
|
+
else:
|
|
168
|
+
try:
|
|
169
|
+
surf = pg.image.load(path).convert_alpha()
|
|
170
|
+
except pg.error as e:
|
|
171
|
+
raise FileNotFoundError(f"Could not load image '{path}': {e}")
|
|
172
|
+
if cache:
|
|
173
|
+
_image_cache[path] = surf
|
|
174
|
+
|
|
175
|
+
if scale != 1.0:
|
|
176
|
+
w = max(1, int(surf.get_width() * scale))
|
|
177
|
+
h = max(1, int(surf.get_height() * scale))
|
|
178
|
+
surf = pg.transform.scale(surf, (w, h))
|
|
179
|
+
|
|
180
|
+
if center:
|
|
181
|
+
r = surf.get_rect(center=(int(x), int(y)))
|
|
182
|
+
else:
|
|
183
|
+
r = surf.get_rect(topleft=(int(x), int(y)))
|
|
184
|
+
|
|
185
|
+
_surf().blit(surf, r)
|
|
186
|
+
return r
|
|
187
|
+
|
|
188
|
+
|
|
189
|
+
def clear_image_cache():
|
|
190
|
+
"""Evict all cached image surfaces (useful between scenes)."""
|
|
191
|
+
_image_cache.clear()
|
|
192
|
+
|
|
193
|
+
|
|
194
|
+
# ── surface ──────────────────────────────────────────────────────────────────
|
|
195
|
+
|
|
196
|
+
def clear(color=None):
|
|
197
|
+
"""
|
|
198
|
+
Fill the screen with a solid colour.
|
|
199
|
+
Defaults to the bg_color set in gl.window() if no colour is given.
|
|
200
|
+
|
|
201
|
+
Example:
|
|
202
|
+
gl.draw.clear("#0d0d0d")
|
|
203
|
+
"""
|
|
204
|
+
_surf().fill(_c.parse(color) if color is not None else _state.bg_color)
|
glpg/input.py
ADDED
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
# glpg/input.py — keyboard and mouse input
|
|
2
|
+
|
|
3
|
+
import pygame as pg
|
|
4
|
+
from . import _state
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
# ── key name → pygame constant map ──────────────────────────────────────────
|
|
8
|
+
|
|
9
|
+
_KEY_MAP: dict[str, int] = {
|
|
10
|
+
# Letters
|
|
11
|
+
**{k: getattr(pg, f"K_{k}") for k in "abcdefghijklmnopqrstuvwxyz"},
|
|
12
|
+
# Digits
|
|
13
|
+
**{str(n): getattr(pg, f"K_{n}") for n in range(10)},
|
|
14
|
+
# Special keys
|
|
15
|
+
"space": pg.K_SPACE,
|
|
16
|
+
"enter": pg.K_RETURN,
|
|
17
|
+
"return": pg.K_RETURN,
|
|
18
|
+
"escape": pg.K_ESCAPE,
|
|
19
|
+
"esc": pg.K_ESCAPE,
|
|
20
|
+
"backspace": pg.K_BACKSPACE,
|
|
21
|
+
"tab": pg.K_TAB,
|
|
22
|
+
"delete": pg.K_DELETE,
|
|
23
|
+
"insert": pg.K_INSERT,
|
|
24
|
+
"home": pg.K_HOME,
|
|
25
|
+
"end": pg.K_END,
|
|
26
|
+
"pageup": pg.K_PAGEUP,
|
|
27
|
+
"pagedown": pg.K_PAGEDOWN,
|
|
28
|
+
# Arrows
|
|
29
|
+
"up": pg.K_UP,
|
|
30
|
+
"down": pg.K_DOWN,
|
|
31
|
+
"left": pg.K_LEFT,
|
|
32
|
+
"right": pg.K_RIGHT,
|
|
33
|
+
# Modifiers (both sides)
|
|
34
|
+
"shift": pg.K_LSHIFT,
|
|
35
|
+
"lshift": pg.K_LSHIFT,
|
|
36
|
+
"rshift": pg.K_RSHIFT,
|
|
37
|
+
"ctrl": pg.K_LCTRL,
|
|
38
|
+
"lctrl": pg.K_LCTRL,
|
|
39
|
+
"rctrl": pg.K_RCTRL,
|
|
40
|
+
"alt": pg.K_LALT,
|
|
41
|
+
"lalt": pg.K_LALT,
|
|
42
|
+
"ralt": pg.K_RALT,
|
|
43
|
+
# Function keys
|
|
44
|
+
**{f"f{n}": getattr(pg, f"K_F{n}") for n in range(1, 13)},
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
_MOUSE_MAP: dict[str, int] = {
|
|
48
|
+
"left": 1,
|
|
49
|
+
"middle": 2,
|
|
50
|
+
"right": 3,
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
# ── internal event processor (called by loop each frame) ────────────────────
|
|
55
|
+
|
|
56
|
+
def _process_events():
|
|
57
|
+
"""Read all queued pygame events and update _state input sets."""
|
|
58
|
+
_state._keys_pressed = set()
|
|
59
|
+
_state._keys_released = set()
|
|
60
|
+
_state._mouse_clicked = set()
|
|
61
|
+
_state._mouse_pos = pg.mouse.get_pos()
|
|
62
|
+
|
|
63
|
+
pressed_buttons = pg.mouse.get_pressed(num_buttons=3)
|
|
64
|
+
_state._mouse_held = {
|
|
65
|
+
btn for btn, held in zip([1, 2, 3], pressed_buttons) if held
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
for event in pg.event.get():
|
|
69
|
+
if event.type == pg.QUIT:
|
|
70
|
+
_state.running = False
|
|
71
|
+
|
|
72
|
+
elif event.type == pg.KEYDOWN:
|
|
73
|
+
_state._keys_pressed.add(event.key)
|
|
74
|
+
_state._keys_held.add(event.key)
|
|
75
|
+
|
|
76
|
+
elif event.type == pg.KEYUP:
|
|
77
|
+
_state._keys_released.add(event.key)
|
|
78
|
+
_state._keys_held.discard(event.key)
|
|
79
|
+
|
|
80
|
+
elif event.type == pg.MOUSEBUTTONDOWN:
|
|
81
|
+
_state._mouse_clicked.add(event.button)
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
# ── key helpers ──────────────────────────────────────────────────────────────
|
|
85
|
+
|
|
86
|
+
def _resolve_key(k) -> int:
|
|
87
|
+
if isinstance(k, int):
|
|
88
|
+
return k
|
|
89
|
+
k_low = k.lower()
|
|
90
|
+
if k_low in _KEY_MAP:
|
|
91
|
+
return _KEY_MAP[k_low]
|
|
92
|
+
attr = f"K_{k_low}"
|
|
93
|
+
if hasattr(pg, attr):
|
|
94
|
+
return getattr(pg, attr)
|
|
95
|
+
raise ValueError(
|
|
96
|
+
f"Unknown key: '{k}'. Use a letter, digit, or a name like "
|
|
97
|
+
"'space', 'enter', 'up', 'shift', 'f1', etc."
|
|
98
|
+
)
|
|
99
|
+
|
|
100
|
+
|
|
101
|
+
def key(k) -> bool:
|
|
102
|
+
"""True every frame the key is held down.
|
|
103
|
+
|
|
104
|
+
Example:
|
|
105
|
+
if gl.key("W"): player.move_up()
|
|
106
|
+
"""
|
|
107
|
+
return _resolve_key(k) in _state._keys_held
|
|
108
|
+
|
|
109
|
+
|
|
110
|
+
def key_pressed(k) -> bool:
|
|
111
|
+
"""True only on the first frame the key is pressed (not held).
|
|
112
|
+
|
|
113
|
+
Example:
|
|
114
|
+
if gl.key_pressed("space"): player.jump()
|
|
115
|
+
"""
|
|
116
|
+
return _resolve_key(k) in _state._keys_pressed
|
|
117
|
+
|
|
118
|
+
|
|
119
|
+
def key_released(k) -> bool:
|
|
120
|
+
"""True only on the frame the key is released.
|
|
121
|
+
|
|
122
|
+
Example:
|
|
123
|
+
if gl.key_released("shift"): player.stop_sprint()
|
|
124
|
+
"""
|
|
125
|
+
return _resolve_key(k) in _state._keys_released
|
|
126
|
+
|
|
127
|
+
|
|
128
|
+
# ── mouse ────────────────────────────────────────────────────────────────────
|
|
129
|
+
|
|
130
|
+
class _Mouse:
|
|
131
|
+
"""Mouse position and button state.
|
|
132
|
+
|
|
133
|
+
Example:
|
|
134
|
+
gl.mouse.pos # (x, y)
|
|
135
|
+
gl.mouse.x # x only
|
|
136
|
+
gl.mouse.clicked() # left click this frame
|
|
137
|
+
gl.mouse.held("right")
|
|
138
|
+
"""
|
|
139
|
+
|
|
140
|
+
@property
|
|
141
|
+
def pos(self) -> tuple[int, int]:
|
|
142
|
+
"""Current (x, y) cursor position."""
|
|
143
|
+
return _state._mouse_pos
|
|
144
|
+
|
|
145
|
+
@property
|
|
146
|
+
def x(self) -> int:
|
|
147
|
+
return _state._mouse_pos[0]
|
|
148
|
+
|
|
149
|
+
@property
|
|
150
|
+
def y(self) -> int:
|
|
151
|
+
return _state._mouse_pos[1]
|
|
152
|
+
|
|
153
|
+
def clicked(self, button: str = "left") -> bool:
|
|
154
|
+
"""True only on the frame the button is clicked."""
|
|
155
|
+
btn = _MOUSE_MAP.get(button.lower())
|
|
156
|
+
if btn is None:
|
|
157
|
+
raise ValueError(f"Unknown mouse button: '{button}'. Use 'left', 'right', or 'middle'.")
|
|
158
|
+
return btn in _state._mouse_clicked
|
|
159
|
+
|
|
160
|
+
def held(self, button: str = "left") -> bool:
|
|
161
|
+
"""True every frame the button is held down."""
|
|
162
|
+
btn = _MOUSE_MAP.get(button.lower())
|
|
163
|
+
if btn is None:
|
|
164
|
+
raise ValueError(f"Unknown mouse button: '{button}'. Use 'left', 'right', or 'middle'.")
|
|
165
|
+
return btn in _state._mouse_held
|
|
166
|
+
|
|
167
|
+
|
|
168
|
+
mouse = _Mouse()
|
glpg/loop.py
ADDED
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
# glpg/loop.py — managed and manual game loop
|
|
2
|
+
|
|
3
|
+
import pygame as pg
|
|
4
|
+
from . import _state
|
|
5
|
+
from . import input as _input
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
# ── registered hooks (managed loop) ─────────────────────────────────────────
|
|
9
|
+
|
|
10
|
+
_update_fn = None
|
|
11
|
+
_draw_fn = None
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
def on_update(fn):
|
|
15
|
+
"""
|
|
16
|
+
Decorator — register a function to run every frame.
|
|
17
|
+
Receives `dt` (delta-time in seconds since last frame).
|
|
18
|
+
|
|
19
|
+
Example:
|
|
20
|
+
@gl.on_update
|
|
21
|
+
def update(dt):
|
|
22
|
+
player.x += speed * dt
|
|
23
|
+
"""
|
|
24
|
+
global _update_fn
|
|
25
|
+
_update_fn = fn
|
|
26
|
+
return fn
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
def on_draw(fn):
|
|
30
|
+
"""
|
|
31
|
+
Decorator — register a function to run every frame for rendering.
|
|
32
|
+
Called after the screen is cleared.
|
|
33
|
+
|
|
34
|
+
Example:
|
|
35
|
+
@gl.on_draw
|
|
36
|
+
def draw():
|
|
37
|
+
gl.draw.rect(player.x, player.y, 32, 32, "red")
|
|
38
|
+
"""
|
|
39
|
+
global _draw_fn
|
|
40
|
+
_draw_fn = fn
|
|
41
|
+
return fn
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
# ── managed loop ─────────────────────────────────────────────────────────────
|
|
45
|
+
|
|
46
|
+
def run():
|
|
47
|
+
"""
|
|
48
|
+
Start the managed game loop. Blocks until the window is closed.
|
|
49
|
+
Requires gl.window() to have been called first.
|
|
50
|
+
Register logic with @gl.on_update and rendering with @gl.on_draw.
|
|
51
|
+
|
|
52
|
+
Example:
|
|
53
|
+
gl.window("720p", title="My Game")
|
|
54
|
+
|
|
55
|
+
@gl.on_update
|
|
56
|
+
def update(dt): ...
|
|
57
|
+
|
|
58
|
+
@gl.on_draw
|
|
59
|
+
def draw(): ...
|
|
60
|
+
|
|
61
|
+
gl.run()
|
|
62
|
+
"""
|
|
63
|
+
_require_window("gl.run()")
|
|
64
|
+
_state.running = True
|
|
65
|
+
|
|
66
|
+
while _state.running:
|
|
67
|
+
dt = _state.clock.tick(_state.fps) / 1000.0
|
|
68
|
+
_input._process_events()
|
|
69
|
+
|
|
70
|
+
if not _state.running:
|
|
71
|
+
break
|
|
72
|
+
|
|
73
|
+
_state.screen.fill(_state.bg_color)
|
|
74
|
+
|
|
75
|
+
if _update_fn:
|
|
76
|
+
_update_fn(dt)
|
|
77
|
+
if _draw_fn:
|
|
78
|
+
_draw_fn()
|
|
79
|
+
|
|
80
|
+
pg.display.flip()
|
|
81
|
+
|
|
82
|
+
pg.quit()
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
# ── manual loop ──────────────────────────────────────────────────────────────
|
|
86
|
+
|
|
87
|
+
def tick() -> float | None:
|
|
88
|
+
"""
|
|
89
|
+
Manual loop mode — call once at the top of your own while loop.
|
|
90
|
+
Handles events, clears the screen, and returns dt in seconds.
|
|
91
|
+
Returns None (and calls pg.quit) when the window should close.
|
|
92
|
+
|
|
93
|
+
Example:
|
|
94
|
+
gl.window("720p")
|
|
95
|
+
while (dt := gl.tick()) is not None:
|
|
96
|
+
if gl.key("escape"):
|
|
97
|
+
gl.quit()
|
|
98
|
+
gl.draw.rect(x, y, 32, 32, "white")
|
|
99
|
+
gl.flip()
|
|
100
|
+
"""
|
|
101
|
+
_require_window("gl.tick()")
|
|
102
|
+
|
|
103
|
+
if not _state.running:
|
|
104
|
+
_state.running = True
|
|
105
|
+
|
|
106
|
+
dt = _state.clock.tick(_state.fps) / 1000.0
|
|
107
|
+
_input._process_events()
|
|
108
|
+
|
|
109
|
+
if not _state.running:
|
|
110
|
+
pg.quit()
|
|
111
|
+
return None
|
|
112
|
+
|
|
113
|
+
_state.screen.fill(_state.bg_color)
|
|
114
|
+
return dt
|
|
115
|
+
|
|
116
|
+
|
|
117
|
+
def flip():
|
|
118
|
+
"""
|
|
119
|
+
Manual loop mode — push the current frame to the display.
|
|
120
|
+
Call once at the end of your loop body.
|
|
121
|
+
"""
|
|
122
|
+
pg.display.flip()
|
|
123
|
+
|
|
124
|
+
|
|
125
|
+
# ── shared utilities ─────────────────────────────────────────────────────────
|
|
126
|
+
|
|
127
|
+
def quit():
|
|
128
|
+
"""Signal the loop to stop at the end of the current frame."""
|
|
129
|
+
_state.running = False
|
|
130
|
+
|
|
131
|
+
|
|
132
|
+
def is_running() -> bool:
|
|
133
|
+
"""True while the game loop is active."""
|
|
134
|
+
return _state.running
|
|
135
|
+
|
|
136
|
+
|
|
137
|
+
def get_fps() -> float:
|
|
138
|
+
"""Return the actual measured FPS this frame."""
|
|
139
|
+
if _state.clock:
|
|
140
|
+
return _state.clock.get_fps()
|
|
141
|
+
return 0.0
|
|
142
|
+
|
|
143
|
+
|
|
144
|
+
# ── internal ─────────────────────────────────────────────────────────────────
|
|
145
|
+
|
|
146
|
+
def _require_window(caller: str):
|
|
147
|
+
if _state.screen is None:
|
|
148
|
+
raise RuntimeError(f"Call gl.window() before {caller}.")
|
glpg/window.py
ADDED
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
# glpg/window.py — display initialisation
|
|
2
|
+
|
|
3
|
+
import pygame as pg
|
|
4
|
+
from . import _state
|
|
5
|
+
from . import _color as _c
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
SIZES = {
|
|
9
|
+
"480p": (854, 480),
|
|
10
|
+
"720p": (1280, 720),
|
|
11
|
+
"1080p": (1920, 1080),
|
|
12
|
+
"1440p": (2560, 1440),
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
def window(size="720p", title="GLPG", icon=None, fps=60, bg_color=(0, 0, 0)):
|
|
17
|
+
"""
|
|
18
|
+
Initialise the game window. Call this once before anything else.
|
|
19
|
+
|
|
20
|
+
Args:
|
|
21
|
+
size : Resolution string "480p" | "720p" | "1080p" | "1440p"
|
|
22
|
+
or a custom (width, height) tuple.
|
|
23
|
+
title : Window title bar string.
|
|
24
|
+
icon : Path to an image file to use as the window icon (optional).
|
|
25
|
+
fps : Target frames per second (default 60).
|
|
26
|
+
bg_color : Background clear colour — name, hex, or RGB tuple.
|
|
27
|
+
|
|
28
|
+
Example:
|
|
29
|
+
gl.window("1080p", title="My Game", fps=144, bg_color="#1a1a2e")
|
|
30
|
+
"""
|
|
31
|
+
if not pg.get_init():
|
|
32
|
+
pg.init()
|
|
33
|
+
|
|
34
|
+
# ── resolve resolution ──────────────────────────────────────────────────
|
|
35
|
+
if isinstance(size, str):
|
|
36
|
+
resolution = SIZES.get(size.lower())
|
|
37
|
+
if resolution is None:
|
|
38
|
+
raise ValueError(
|
|
39
|
+
f"Unknown size string '{size}'. "
|
|
40
|
+
f"Choose from {list(SIZES.keys())} or pass a (w, h) tuple."
|
|
41
|
+
)
|
|
42
|
+
elif isinstance(size, (tuple, list)) and len(size) == 2:
|
|
43
|
+
resolution = (int(size[0]), int(size[1]))
|
|
44
|
+
else:
|
|
45
|
+
raise TypeError(f"size must be a resolution string or (w, h) tuple, got: {size!r}")
|
|
46
|
+
|
|
47
|
+
# ── create display ──────────────────────────────────────────────────────
|
|
48
|
+
_state.screen = pg.display.set_mode(resolution)
|
|
49
|
+
_state.clock = pg.time.Clock()
|
|
50
|
+
_state.fps = fps
|
|
51
|
+
_state.bg_color = _c.parse(bg_color)
|
|
52
|
+
|
|
53
|
+
pg.display.set_caption(title)
|
|
54
|
+
|
|
55
|
+
if icon:
|
|
56
|
+
try:
|
|
57
|
+
icon_surf = pg.image.load(icon).convert_alpha()
|
|
58
|
+
pg.display.set_icon(icon_surf)
|
|
59
|
+
except pg.error as e:
|
|
60
|
+
raise FileNotFoundError(f"Could not load icon '{icon}': {e}")
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
# ── helpers ─────────────────────────────────────────────────────────────────
|
|
64
|
+
|
|
65
|
+
def get_screen():
|
|
66
|
+
"""Return the raw pygame Surface."""
|
|
67
|
+
return _state.screen
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
def get_size():
|
|
71
|
+
"""Return (width, height) of the current window."""
|
|
72
|
+
if _state.screen:
|
|
73
|
+
return _state.screen.get_size()
|
|
74
|
+
raise RuntimeError("Window not initialised — call gl.window() first.")
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
def get_width():
|
|
78
|
+
return get_size()[0]
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
def get_height():
|
|
82
|
+
return get_size()[1]
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
def set_title(title: str):
|
|
86
|
+
"""Update the window title bar text."""
|
|
87
|
+
pg.display.set_caption(title)
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
def set_fps(fps: int):
|
|
91
|
+
"""Change the target FPS cap."""
|
|
92
|
+
_state.fps = fps
|
|
93
|
+
|
|
94
|
+
|
|
95
|
+
def set_bg_color(color):
|
|
96
|
+
"""Change the background clear colour."""
|
|
97
|
+
_state.bg_color = _c.parse(color)
|
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: glpg-pygame
|
|
3
|
+
Version: 1.0.0
|
|
4
|
+
Summary: A shorthand wrapper for pygame that makes game development fast and easy
|
|
5
|
+
License: MIT
|
|
6
|
+
Project-URL: Homepage, https://github.com/BOU4EYT/GLPG-GameLib
|
|
7
|
+
Project-URL: Repository, https://github.com/BOU4EYT/GLPG-GameLib
|
|
8
|
+
Keywords: pygame,game,gamedev,wrapper,shorthand
|
|
9
|
+
Classifier: Development Status :: 5 - Production/Stable
|
|
10
|
+
Classifier: Intended Audience :: Developers
|
|
11
|
+
Classifier: Topic :: Games/Entertainment
|
|
12
|
+
Classifier: Topic :: Software Development :: Libraries
|
|
13
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
14
|
+
Classifier: Programming Language :: Python :: 3
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
20
|
+
Requires-Python: >=3.10
|
|
21
|
+
Description-Content-Type: text/markdown
|
|
22
|
+
License-File: LICENSE
|
|
23
|
+
Provides-Extra: pygame
|
|
24
|
+
Requires-Dist: pygame>=2.0.0; extra == "pygame"
|
|
25
|
+
Provides-Extra: pygame-ce
|
|
26
|
+
Requires-Dist: pygame-ce>=2.0.0; extra == "pygame-ce"
|
|
27
|
+
Dynamic: license-file
|
|
28
|
+
|
|
29
|
+
# GLPG — GameLib for Pygame
|
|
30
|
+
|
|
31
|
+
A tiny wrapper for pygame that makes game development fast and approachable.
|
|
32
|
+
Write games in a few lines instead of dozens.
|
|
33
|
+
|
|
34
|
+
```
|
|
35
|
+
pip install glpg
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
If you need a newer Python version, `pygame-ce` is also supported:
|
|
39
|
+
|
|
40
|
+
```
|
|
41
|
+
pip install pygame-ce glpg
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
---
|
|
45
|
+
|
|
46
|
+
## Quickstart — Managed Loop
|
|
47
|
+
|
|
48
|
+
```python
|
|
49
|
+
import glpg as gl
|
|
50
|
+
|
|
51
|
+
gl.window("720p", title="My Game", fps=60, bg_color="#1a1a2e")
|
|
52
|
+
|
|
53
|
+
@gl.on_update
|
|
54
|
+
def update(dt):
|
|
55
|
+
if gl.key("escape"):
|
|
56
|
+
gl.quit()
|
|
57
|
+
|
|
58
|
+
@gl.on_draw
|
|
59
|
+
def draw():
|
|
60
|
+
gl.draw.rect(100, 100, 64, 64, "#e63946")
|
|
61
|
+
gl.draw.circle(400, 300, 40, "cyan")
|
|
62
|
+
gl.draw.text("Hello GLPG", 20, 20, size=32, color="white")
|
|
63
|
+
|
|
64
|
+
gl.run()
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
## Quickstart — Manual Loop
|
|
68
|
+
|
|
69
|
+
```python
|
|
70
|
+
import glpg as gl
|
|
71
|
+
|
|
72
|
+
gl.window("720p", title="Manual Loop")
|
|
73
|
+
|
|
74
|
+
x = 100
|
|
75
|
+
|
|
76
|
+
while (dt := gl.tick()) is not None:
|
|
77
|
+
if gl.key("escape"):
|
|
78
|
+
gl.quit()
|
|
79
|
+
if gl.key("right"):
|
|
80
|
+
x += 200 * dt
|
|
81
|
+
|
|
82
|
+
gl.draw.rect(x, 300, 48, 48, "white")
|
|
83
|
+
gl.flip()
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
---
|
|
87
|
+
|
|
88
|
+
## API Reference
|
|
89
|
+
|
|
90
|
+
### Window
|
|
91
|
+
|
|
92
|
+
| Call | Description |
|
|
93
|
+
|------|-------------|
|
|
94
|
+
| `gl.window(size, title, icon, fps, bg_color)` | Initialise the window. Call once before anything else. |
|
|
95
|
+
| `gl.get_size()` | Returns `(width, height)` |
|
|
96
|
+
| `gl.get_width()` / `gl.get_height()` | Individual dimensions |
|
|
97
|
+
| `gl.set_title(title)` | Change the window title |
|
|
98
|
+
| `gl.set_fps(fps)` | Update the FPS cap at runtime |
|
|
99
|
+
| `gl.set_bg_color(color)` | Update the clear colour |
|
|
100
|
+
| `gl.get_screen()` | Raw `pygame.Surface` for advanced use |
|
|
101
|
+
|
|
102
|
+
**Size strings:** `"480p"` (854×480) · `"720p"` (1280×720) · `"1080p"` (1920×1080) · `"1440p"` (2560×1440)
|
|
103
|
+
Or pass a custom `(width, height)` tuple.
|
|
104
|
+
|
|
105
|
+
---
|
|
106
|
+
|
|
107
|
+
### Managed Loop
|
|
108
|
+
|
|
109
|
+
| Call | Description |
|
|
110
|
+
|------|-------------|
|
|
111
|
+
| `@gl.on_update` | Decorator that runs each frame and receives `dt` (seconds) |
|
|
112
|
+
| `@gl.on_draw` | Decorator that runs each frame after clearing the screen |
|
|
113
|
+
| `gl.run()` | Start the managed loop and block until the window closes |
|
|
114
|
+
| `gl.quit()` | Signal the loop to stop |
|
|
115
|
+
| `gl.is_running()` | `True` while the loop is active |
|
|
116
|
+
| `gl.get_fps()` | Measured FPS for the current frame |
|
|
117
|
+
|
|
118
|
+
---
|
|
119
|
+
|
|
120
|
+
### Manual Loop
|
|
121
|
+
|
|
122
|
+
| Call | Description |
|
|
123
|
+
|------|-------------|
|
|
124
|
+
| `gl.tick()` | Begin a frame, returns `dt` in seconds or `None` when finished |
|
|
125
|
+
| `gl.flip()` | Present the frame at the end of your loop |
|
|
126
|
+
| `gl.quit()` | Stop the loop on the next tick |
|
|
127
|
+
|
|
128
|
+
---
|
|
129
|
+
|
|
130
|
+
### Drawing
|
|
131
|
+
|
|
132
|
+
All draw calls go through `gl.draw.*`.
|
|
133
|
+
|
|
134
|
+
| Call | Description |
|
|
135
|
+
|------|-------------|
|
|
136
|
+
| `gl.draw.rect(x, y, w, h, color, border, radius)` | Rectangle. Use `radius` for rounded corners. |
|
|
137
|
+
| `gl.draw.circle(x, y, radius, color, border)` | Circle centred at `(x, y)` |
|
|
138
|
+
| `gl.draw.ellipse(x, y, w, h, color, border)` | Ellipse inside a bounding box |
|
|
139
|
+
| `gl.draw.line(x1, y1, x2, y2, color, width)` | Line between two points |
|
|
140
|
+
| `gl.draw.polygon(points, color, border)` | Polygon from a list of `(x, y)` points |
|
|
141
|
+
| `gl.draw.text(content, x, y, size, color, font, center, antialias)` | Text. `center=True` centres on `(x, y)`. Returns `pygame.Rect` |
|
|
142
|
+
| `gl.draw.image(path, x, y, scale, center, cache)` | Image from file. Returns `pygame.Rect` |
|
|
143
|
+
| `gl.draw.clear(color)` | Clear the screen. Defaults to window `bg_color` |
|
|
144
|
+
| `gl.draw.clear_image_cache()` | Evict cached image surfaces between scenes |
|
|
145
|
+
|
|
146
|
+
**Colors** accept:
|
|
147
|
+
- Named strings: `"red"`, `"white"`, `"cornflowerblue"`
|
|
148
|
+
- Hex strings: `"#e63946"`, `"#ff0000ff"` (with alpha)
|
|
149
|
+
- RGB tuples: `(255, 99, 71)`, `(255, 99, 71, 128)`
|
|
150
|
+
|
|
151
|
+
`border=0` means filled. Any positive value draws an outline.
|
|
152
|
+
|
|
153
|
+
---
|
|
154
|
+
|
|
155
|
+
### Input — Keyboard
|
|
156
|
+
|
|
157
|
+
| Call | Description |
|
|
158
|
+
|------|-------------|
|
|
159
|
+
| `gl.key("W")` | `True` while the key is held |
|
|
160
|
+
| `gl.key_pressed("space")` | `True` only on the first frame the key is pressed |
|
|
161
|
+
| `gl.key_released("shift")` | `True` only on the release frame |
|
|
162
|
+
|
|
163
|
+
**Key names:** letters `a–z`, digits `0–9`, `"space"`, `"enter"`, `"escape"`, `"backspace"`, `"tab"`, `"up"`, `"down"`, `"left"`, `"right"`, `"shift"`, `"ctrl"`, `"alt"` (also `"lshift"`, `"rshift"` etc.), `"f1"–"f12"`, `"delete"`, `"insert"`, `"home"`, `"end"`, `"pageup"`, `"pagedown"`.
|
|
164
|
+
|
|
165
|
+
---
|
|
166
|
+
|
|
167
|
+
### Input — Mouse
|
|
168
|
+
|
|
169
|
+
| Call | Description |
|
|
170
|
+
|------|-------------|
|
|
171
|
+
| `gl.mouse.pos` | `(x, y)` cursor position |
|
|
172
|
+
| `gl.mouse.x` / `gl.mouse.y` | Individual coordinates |
|
|
173
|
+
| `gl.mouse.clicked("left")` | `True` only on the click frame. Buttons: `"left"`, `"right"`, `"middle"` |
|
|
174
|
+
| `gl.mouse.held("right")` | `True` while the button is held |
|
|
175
|
+
|
|
176
|
+
---
|
|
177
|
+
|
|
178
|
+
## License
|
|
179
|
+
|
|
180
|
+
MIT License 2026
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
glpg/__init__.py,sha256=d4AiACD21rMpVmBN801vpvohhgCVQ0xVZGkGlmoayEQ,2201
|
|
2
|
+
glpg/_color.py,sha256=DPAOxP47K3ZH2fYGAjjDdH0citSR35Xld-TTG41Zbbw,1484
|
|
3
|
+
glpg/_state.py,sha256=wBxRisT5SnUPuuH0Nh2BjOdKdWm9vSPPCeiPrn-qV0k,590
|
|
4
|
+
glpg/draw.py,sha256=6FfxAGa8DesjaZaf_7Zs0L3KdubRF2mBEv_vGoSOWE4,6497
|
|
5
|
+
glpg/input.py,sha256=Dl3jynPpBMR7LPyJ7OP51vHOUetPAuV9h9VFj9KT0GI,5009
|
|
6
|
+
glpg/loop.py,sha256=WVLFdVMPsLS6r2_H0ah6JQX0FCKJAEqaErOUiKKxUQ4,3971
|
|
7
|
+
glpg/window.py,sha256=CytSDnvh1ttyaAnGY7t1bjvCliI9mpoF_Mnssq2fhY4,3123
|
|
8
|
+
glpg_pygame-1.0.0.dist-info/licenses/LICENSE,sha256=EzvueUQeKoGE8Le2T3i-SdeHlX0InCkKAuRKohCFiaw,1084
|
|
9
|
+
glpg_pygame-1.0.0.dist-info/METADATA,sha256=tCa_Vf-Vfe7X5U9hNNlKpwwGFj-6gjHdVUSL7OHYcR4,5797
|
|
10
|
+
glpg_pygame-1.0.0.dist-info/WHEEL,sha256=aeYiig01lYGDzBgS8HxWXOg3uV61G9ijOsup-k9o1sk,91
|
|
11
|
+
glpg_pygame-1.0.0.dist-info/top_level.txt,sha256=ukNoXghIvGSXw6dqg_4H__0WCH14jYefXpKsNjQ7SO8,5
|
|
12
|
+
glpg_pygame-1.0.0.dist-info/RECORD,,
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 BOU4EYT
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
glpg
|