glpg-pygame 1.0.0__tar.gz

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.
@@ -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,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,152 @@
1
+ # GLPG — GameLib for Pygame
2
+
3
+ A tiny wrapper for pygame that makes game development fast and approachable.
4
+ Write games in a few lines instead of dozens.
5
+
6
+ ```
7
+ pip install glpg
8
+ ```
9
+
10
+ If you need a newer Python version, `pygame-ce` is also supported:
11
+
12
+ ```
13
+ pip install pygame-ce glpg
14
+ ```
15
+
16
+ ---
17
+
18
+ ## Quickstart — Managed Loop
19
+
20
+ ```python
21
+ import glpg as gl
22
+
23
+ gl.window("720p", title="My Game", fps=60, bg_color="#1a1a2e")
24
+
25
+ @gl.on_update
26
+ def update(dt):
27
+ if gl.key("escape"):
28
+ gl.quit()
29
+
30
+ @gl.on_draw
31
+ def draw():
32
+ gl.draw.rect(100, 100, 64, 64, "#e63946")
33
+ gl.draw.circle(400, 300, 40, "cyan")
34
+ gl.draw.text("Hello GLPG", 20, 20, size=32, color="white")
35
+
36
+ gl.run()
37
+ ```
38
+
39
+ ## Quickstart — Manual Loop
40
+
41
+ ```python
42
+ import glpg as gl
43
+
44
+ gl.window("720p", title="Manual Loop")
45
+
46
+ x = 100
47
+
48
+ while (dt := gl.tick()) is not None:
49
+ if gl.key("escape"):
50
+ gl.quit()
51
+ if gl.key("right"):
52
+ x += 200 * dt
53
+
54
+ gl.draw.rect(x, 300, 48, 48, "white")
55
+ gl.flip()
56
+ ```
57
+
58
+ ---
59
+
60
+ ## API Reference
61
+
62
+ ### Window
63
+
64
+ | Call | Description |
65
+ |------|-------------|
66
+ | `gl.window(size, title, icon, fps, bg_color)` | Initialise the window. Call once before anything else. |
67
+ | `gl.get_size()` | Returns `(width, height)` |
68
+ | `gl.get_width()` / `gl.get_height()` | Individual dimensions |
69
+ | `gl.set_title(title)` | Change the window title |
70
+ | `gl.set_fps(fps)` | Update the FPS cap at runtime |
71
+ | `gl.set_bg_color(color)` | Update the clear colour |
72
+ | `gl.get_screen()` | Raw `pygame.Surface` for advanced use |
73
+
74
+ **Size strings:** `"480p"` (854×480) · `"720p"` (1280×720) · `"1080p"` (1920×1080) · `"1440p"` (2560×1440)
75
+ Or pass a custom `(width, height)` tuple.
76
+
77
+ ---
78
+
79
+ ### Managed Loop
80
+
81
+ | Call | Description |
82
+ |------|-------------|
83
+ | `@gl.on_update` | Decorator that runs each frame and receives `dt` (seconds) |
84
+ | `@gl.on_draw` | Decorator that runs each frame after clearing the screen |
85
+ | `gl.run()` | Start the managed loop and block until the window closes |
86
+ | `gl.quit()` | Signal the loop to stop |
87
+ | `gl.is_running()` | `True` while the loop is active |
88
+ | `gl.get_fps()` | Measured FPS for the current frame |
89
+
90
+ ---
91
+
92
+ ### Manual Loop
93
+
94
+ | Call | Description |
95
+ |------|-------------|
96
+ | `gl.tick()` | Begin a frame, returns `dt` in seconds or `None` when finished |
97
+ | `gl.flip()` | Present the frame at the end of your loop |
98
+ | `gl.quit()` | Stop the loop on the next tick |
99
+
100
+ ---
101
+
102
+ ### Drawing
103
+
104
+ All draw calls go through `gl.draw.*`.
105
+
106
+ | Call | Description |
107
+ |------|-------------|
108
+ | `gl.draw.rect(x, y, w, h, color, border, radius)` | Rectangle. Use `radius` for rounded corners. |
109
+ | `gl.draw.circle(x, y, radius, color, border)` | Circle centred at `(x, y)` |
110
+ | `gl.draw.ellipse(x, y, w, h, color, border)` | Ellipse inside a bounding box |
111
+ | `gl.draw.line(x1, y1, x2, y2, color, width)` | Line between two points |
112
+ | `gl.draw.polygon(points, color, border)` | Polygon from a list of `(x, y)` points |
113
+ | `gl.draw.text(content, x, y, size, color, font, center, antialias)` | Text. `center=True` centres on `(x, y)`. Returns `pygame.Rect` |
114
+ | `gl.draw.image(path, x, y, scale, center, cache)` | Image from file. Returns `pygame.Rect` |
115
+ | `gl.draw.clear(color)` | Clear the screen. Defaults to window `bg_color` |
116
+ | `gl.draw.clear_image_cache()` | Evict cached image surfaces between scenes |
117
+
118
+ **Colors** accept:
119
+ - Named strings: `"red"`, `"white"`, `"cornflowerblue"`
120
+ - Hex strings: `"#e63946"`, `"#ff0000ff"` (with alpha)
121
+ - RGB tuples: `(255, 99, 71)`, `(255, 99, 71, 128)`
122
+
123
+ `border=0` means filled. Any positive value draws an outline.
124
+
125
+ ---
126
+
127
+ ### Input — Keyboard
128
+
129
+ | Call | Description |
130
+ |------|-------------|
131
+ | `gl.key("W")` | `True` while the key is held |
132
+ | `gl.key_pressed("space")` | `True` only on the first frame the key is pressed |
133
+ | `gl.key_released("shift")` | `True` only on the release frame |
134
+
135
+ **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"`.
136
+
137
+ ---
138
+
139
+ ### Input — Mouse
140
+
141
+ | Call | Description |
142
+ |------|-------------|
143
+ | `gl.mouse.pos` | `(x, y)` cursor position |
144
+ | `gl.mouse.x` / `gl.mouse.y` | Individual coordinates |
145
+ | `gl.mouse.clicked("left")` | `True` only on the click frame. Buttons: `"left"`, `"right"`, `"middle"` |
146
+ | `gl.mouse.held("right")` | `True` while the button is held |
147
+
148
+ ---
149
+
150
+ ## License
151
+
152
+ MIT License 2026
@@ -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"
@@ -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}")
@@ -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)
@@ -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)
@@ -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()
@@ -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}.")
@@ -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,15 @@
1
+ LICENSE
2
+ README.md
3
+ pyproject.toml
4
+ glpg/__init__.py
5
+ glpg/_color.py
6
+ glpg/_state.py
7
+ glpg/draw.py
8
+ glpg/input.py
9
+ glpg/loop.py
10
+ glpg/window.py
11
+ glpg_pygame.egg-info/PKG-INFO
12
+ glpg_pygame.egg-info/SOURCES.txt
13
+ glpg_pygame.egg-info/dependency_links.txt
14
+ glpg_pygame.egg-info/requires.txt
15
+ glpg_pygame.egg-info/top_level.txt
@@ -0,0 +1,6 @@
1
+
2
+ [pygame]
3
+ pygame>=2.0.0
4
+
5
+ [pygame-ce]
6
+ pygame-ce>=2.0.0
@@ -0,0 +1,34 @@
1
+ [build-system]
2
+ requires = ["setuptools>=61.0"]
3
+ build-backend = "setuptools.build_meta"
4
+
5
+ [project]
6
+ name = "glpg-pygame"
7
+ version = "1.0.0"
8
+ description = "A shorthand wrapper for pygame that makes game development fast and easy"
9
+ readme = "README.md"
10
+ license = { text = "MIT" }
11
+ requires-python = ">=3.10"
12
+ dependencies = []
13
+ keywords = ["pygame", "game", "gamedev", "wrapper", "shorthand"]
14
+ classifiers = [
15
+ "Development Status :: 5 - Production/Stable",
16
+ "Intended Audience :: Developers",
17
+ "Topic :: Games/Entertainment",
18
+ "Topic :: Software Development :: Libraries",
19
+ "License :: OSI Approved :: MIT License",
20
+ "Programming Language :: Python :: 3",
21
+ "Programming Language :: Python :: 3.10",
22
+ "Programming Language :: Python :: 3.11",
23
+ "Programming Language :: Python :: 3.12",
24
+ "Programming Language :: Python :: 3.13",
25
+ "Programming Language :: Python :: 3.14",
26
+ ]
27
+
28
+ [project.optional-dependencies]
29
+ pygame = ["pygame>=2.0.0"]
30
+ pygame-ce = ["pygame-ce>=2.0.0"]
31
+
32
+ [project.urls]
33
+ Homepage = "https://github.com/BOU4EYT/GLPG-GameLib"
34
+ Repository = "https://github.com/BOU4EYT/GLPG-GameLib"
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+