Skip to content

Commit f629039

Browse files
committed
commit of source doc
1 parent d4a8757 commit f629039

File tree

2 files changed

+390
-2
lines changed

2 files changed

+390
-2
lines changed

.gitignore

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,2 @@
1-
*.pdf
2-
*.md
1+
./*.pdf
2+
./*.md

src/README.md

Lines changed: 388 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,388 @@
1+
---
2+
title: MiniLibX Python Manual
3+
abstract: >
4+
This documentation is a PORT of the ORIGINAL MiniLibX docs.
5+
6+
It describes the Python package that provides access to the
7+
MiniLibX graphics library. It allows creating windows, drawing pixels,
8+
handling images, and capturing keyboard and mouse input through a thin
9+
wrapper over the original C API, keeping function names and behavior as
10+
close as possible to the native MiniLibX library.
11+
author: "Nora de Fitero Teijeira (dde-fite)"
12+
titlepage-logo: "media/42_MiniLibX_Python_Manual.jpg"
13+
logo-width: 300px
14+
acknowledgements: >
15+
The original MiniLibX documentation was created by Olivier Crouzet under the MIT license.
16+
This is a derivative work based on his work, created on a non-profit basis with the aim of sharing knowledge among the 42 student community.
17+
18+
This derivative work is published under the MIT license by Nora de Fitero Teijeira.
19+
---
20+
21+
# Introduction
22+
23+
The MiniLibX Python Wrapper allows you to create graphical software easily without any knowledge of X-Window/Wayland/Vulkan on Unix/Linux, or AppKit on macOS. It provides:
24+
25+
- Window creation and management
26+
- Pixel-level drawing
27+
- Image manipulation for faster rendering
28+
- Keyboard and mouse input handling
29+
- PNG and XPM image loading
30+
31+
## How a graphics server works
32+
33+
This library interacts with the underlying graphics system of your operating system. Before diving into usage, it's helpful to understand how graphics servers manage windows and handle user input.
34+
35+
36+
### Historical X-Window concept
37+
38+
X-Window is a network-oriented graphical system for Unix. It is based on two main parts:
39+
40+
- On one side, your software wants to draw something on the screen and or get keyboard & mouse entries.
41+
42+
- On the other side, the X-Server manages the screen, keyboard and mouse
43+
(It is often referred to as a \"display\").
44+
45+
A network connection must be established between these two entities to
46+
send drawing orders (from the software to the X-Server), and
47+
keyboard/mouse events (from the X-Server to the software).
48+
49+
Nowadays, most of the time, both run on the same computer.
50+
51+
### Modern graphical approach
52+
53+
Modern computers come with a powerful GPU that is directly accessed by
54+
applications. Along GPU libraries like Vulkan or OpenGL, the Wayland
55+
protocol ensure communication with the compositor program that manages
56+
the various windows on screen and the user input events. For your own
57+
application:
58+
59+
- The Vulkan or OpenGL library allow you to directly draw any content into
60+
your window.
61+
- The Wayland compositor handles the place of your window on screen and
62+
send you back the keyboard and mouse inputs from the user.
63+
64+
Unfortunately, this gain of graphical power through GPU access removes
65+
the networking aspects that exist with X-Window. It is not possible for
66+
a program to access a remote GPU and show its window on a remote
67+
display. But current software architectures are more likely based on a
68+
local display application that gets data in JSON through a web API.
69+
70+
# Getting started
71+
72+
## Requirements
73+
### Arch Linux
74+
```bash
75+
sudo pacman -S libxcb xcb-util-keysyms zlib libbsd vulkan-icd-loader vulkan-tools shaderc
76+
```
77+
78+
### Debian/Ubuntu
79+
```bash
80+
sudo apt install libxcb libxcb-keysyms libvulkan libz libbsd glslc
81+
```
82+
83+
## Installation
84+
First compile MiniLibX.
85+
```bash
86+
make install
87+
```
88+
89+
Create a virtual environment with your preferred manager and open it:
90+
91+
- For bash/zsh:
92+
```bash
93+
python -m venv .venv
94+
source .venv/bin/activate
95+
```
96+
97+
- For fish:
98+
```bash
99+
python -m venv .venv
100+
source .venv/bin/activate.fish
101+
```
102+
103+
And install the package:
104+
```bash
105+
pip install mlx_CLXV-2.2-py3-none-any.whl
106+
```
107+
108+
## Example of use
109+
110+
This small Python script displays a small black window with text. It will also print the screen dimensions to stdout and listen for user clicks.
111+
112+
We will explain the functions used in this example later.
113+
114+
```python
115+
from mlx import Mlx
116+
117+
def mymouse(button, x, y, mystuff):
118+
print(f"Got mouse event! button {button} at {x},{y}.")
119+
120+
def mykey(keynum, mystuff):
121+
print(f"Got key {keynum}, and got my stuff back:")
122+
print(mystuff)
123+
if keynum == 32:
124+
m.mlx_mouse_hook(win_ptr, None, None)
125+
126+
m = Mlx()
127+
mlx_ptr = m.mlx_init()
128+
win_ptr = m.mlx_new_window(mlx_ptr, 200, 200, "test")
129+
m.mlx_clear_window(mlx_ptr, win_ptr)
130+
m.mlx_string_put(mlx_ptr, win_ptr, 20, 20, 255, "Hello PyMlx!")
131+
(ret, w, h) = m.mlx_get_screen_size(mlx_ptr)
132+
print(f"Got screen size: {w} x {h} .")
133+
134+
stuff = [1, 2]
135+
m.mlx_mouse_hook(win_ptr, mymouse, None)
136+
m.mlx_key_hook(win_ptr, mykey, stuff)
137+
138+
m.mlx_loop(mlx_ptr)
139+
```
140+
141+
# Behind the Scenes
142+
143+
When an instance of the Mlx class is created, the first thing it does is construct the path to the C library called libmlx.so.
144+
145+
```python
146+
def __init__(self):
147+
module_dir = os.path.dirname(os.path.abspath(__file__))
148+
self.so_file = os.path.join(module_dir, "libmlx.so")
149+
#...
150+
```
151+
152+
- \_\_file\_\_ is a special Python variable that contains the path to the file where this code is executed.
153+
154+
- os.path.dirname extracts the path from __file__ and uses os.path.join to create the path to the library.
155+
156+
It then declares the mlx_func variable, which acts as a bridge between Python and C. Using the CDLL function from Python's ctypes module, it loads the library and calls the original functions.
157+
158+
```python
159+
def __init__(self):
160+
# ...
161+
self.mlx_func = CDLL(self.so_file)
162+
# ...
163+
```
164+
165+
For each C function available in the Python wrapper, there is a declaration within the Mlx class:
166+
167+
```python
168+
def mlx_init(self):
169+
self.mlx_func.mlx_init.restype = c_void_p
170+
return self.mlx_func.mlx_init()
171+
```
172+
173+
You can see how it calls mlx_func.mlx_init(). This mlx_init() is already the original C function. It is necessary to specify the data type returned by the function with mlx_init.restype, which in this case is c_void_p (equivalent to void *).
174+
175+
All of this is passed to CDLL, which, using the previously loaded library, will execute the function and return whatever the function returns.
176+
177+
178+
# Initialization and cleanup: mlx_init(), mlx_release()
179+
180+
## Synopsis
181+
182+
```python
183+
from mlx import Mlx
184+
185+
def mlx_init() -> int: # void *
186+
187+
def mlx_release() -> int: # void *
188+
```
189+
190+
## Description
191+
192+
First of all, you need to initialize the connection between your software and the graphic and user sub-systems. Once this completed, you'll be able to use other MiniLibX functions to send and receive the messages from the display, like "I want to draw a yellow pixel in this window" or "did the user hit a key?".
193+
194+
The mlx_init function will create this connection. No parameters are needed, ant it will return a void * identifier, used for further calls to the library routines. The mlx_release function can be used at the end of the program to disconnect from the graphic system and release resources.
195+
196+
If **mlx_init()** fails to set up the connection to the display, it will return None.
197+
198+
199+
## Return values
200+
201+
If **mlx_init()** set up the connection to the display correctly, it will return an **int as a pointer**; otherwise, it returns **None**.
202+
203+
# Managing windows: mlx_new_window(), mlx_clear_window, mlx_destroy_window
204+
205+
## Synopsis
206+
c_void_p, c_uint, c_uint, c_char_p
207+
```python
208+
def mlx_new_window(mlx_ptr: int, width: int, height: int, title: str ) -> int: # void *
209+
210+
def mlx_clear_window(mlx_ptr: int, win_ptr: int) -> int:
211+
212+
def mlx_destroy_window(mlx_ptr: int, win_ptr: int ) -> int:
213+
```
214+
215+
## Description
216+
217+
The **mlx_new_window** () function creates a new window on the screen,
218+
using the *width* and *height* parameters to determine its size, and
219+
*title* as the text that should be displayed in the window\'s title bar.
220+
The *mlx_ptr* parameter is the connection identifier returned by
221+
**mlx_init** () (see the **mlx** man page). **mlx_new_window** ()
222+
returns a *void \** window identifier that can be used by other MiniLibX
223+
calls. Note that the MiniLibX can handle an arbitrary number of separate
224+
windows.
225+
226+
**mlx_clear_window** () and **mlx_destroy_window** () respectively clear
227+
(in black) and destroy the given window. They both have the same
228+
parameters: *mlx_ptr* is the screen connection identifier, and *win_ptr*
229+
is a window identifier.
230+
231+
## Return values
232+
233+
If **mlx_new_window()** fails to create a new window (whatever the
234+
reason), it will return NULL, otherwise a non-null pointer is returned
235+
as a window identifier. **mlx_clear_window** and **mlx_destroy_window**
236+
return nothing.
237+
238+
# Drawing inside windows: mlx_pixel_put(), mlx_string_put()
239+
240+
## Synopsis
241+
242+
int
243+
244+
**mlx_pixel_put** ( *void \*mlx_ptr, void \*win_ptr, unsigned int x,
245+
unsigned int y, unsigned int color* );
246+
247+
int
248+
249+
**mlx_string_put** ( *void \*mlx_ptr, void \*win_ptr, unsigned int x,
250+
unsigned int y, unsigned int color, char \*string* );
251+
252+
## Description
253+
254+
The **mlx_pixel_put** () function draws a defined pixel in the window
255+
*win_ptr* using the ( *x* , *y* ) coordinates, and the specified *color*
256+
. The origin (0,0) is the upper left corner of the window, the x and y
257+
axis respectively pointing right and down. The connection identifier,
258+
*mlx_ptr* , is needed (see the **mlx** man page).
259+
260+
Parameters for **mlx_string_put** () have the same meaning. Instead of a
261+
simple pixel, the specified *string* will be displayed at ( *x* , *y* ).
262+
263+
Both functions will discard any display outside the window. This makes
264+
**mlx_pixel_put** slow. Consider using images instead.
265+
266+
## Color management
267+
268+
The *color* parameter has an unsigned integer type. The displayed colour
269+
needs to be encoded in this integer, following a defined scheme. All
270+
displayable colours can be split in 3 basic colours: red, green and
271+
blue. Three associated values, in the 0-255 range, represent how much of
272+
each colour is mixed up to create the original colour. The fourth byte
273+
represent transparency, where 0 is fully transparent and 255 opaque.
274+
Theses four values must be set inside the unsigned integer to display
275+
the right colour. The bytes of this integer are filled as shown in the
276+
picture below:
277+
278+
| B | G | R | A | colour integer
279+
+---+---+---+---+
280+
281+
While filling the integer, make sure you avoid endian problems. Example:
282+
the \"blue\" byte will be the least significant byte inside the integer
283+
on a little endian machine.
284+
285+
# Handle events:
286+
287+
## Synopsis
288+
289+
int
290+
291+
**mlx_loop** ( *void \*mlx_ptr* );
292+
293+
int
294+
295+
**mlx_key_hook** ( *void \*win_ptr, int (\*funct_ptr)(), void \*param*
296+
);
297+
298+
int
299+
300+
**mlx_mouse_hook** ( *void \*win_ptr, int (\*funct_ptr)(), void \*param*
301+
);
302+
303+
int
304+
305+
**mlx_expose_hook** ( *void \*win_ptr, int (\*funct_ptr)(), void
306+
\*param* );
307+
308+
int
309+
310+
**mlx_loop_hook** ( *void \*mlx_ptr, int (\*funct_ptr)(), void \*param*
311+
);
312+
313+
int
314+
315+
**mlx_loop_exit** ( *void \*mlx_ptr* );
316+
317+
## Events
318+
319+
The graphical system is bi-directional. On one hand, the program sends
320+
orders to the screen to display pixels, images, and so on. On the other
321+
hand, it can get information from the keyboard and mouse associated to
322+
the screen. To do so, the program receives \"events\" from the keyboard
323+
or the mouse.
324+
325+
## Description
326+
327+
To receive events, you must use **mlx_loop** (). This function never
328+
returns, unless **mlx_loop_exit** is called. It is an infinite loop that
329+
waits for an event, and then calls a user-defined function associated
330+
with this event. A single parameter is needed, the connection identifier
331+
*mlx_ptr* (see the **mlx manual).**
332+
333+
You can assign different functions to the three following events:\
334+
- A key is released\
335+
- The mouse button is pressed\
336+
- A part of the window should be re-drawn (this is called an \"expose\"
337+
event, and it is your program\'s job to handle it in the Unix/Linux X11
338+
environment, but at the opposite it never happens on Unix/Linux
339+
Wayland-Vulkan nor on MacOS).\
340+
341+
Each window can define a different function for the same event.
342+
343+
The three functions **mlx_key_hook** (), **mlx_mouse_hook** () and
344+
**mlx_expose_hook** () work exactly the same way. *funct_ptr* is a
345+
pointer to the function you want to be called when an event occurs. This
346+
assignment is specific to the window defined by the *win_ptr*
347+
identifier. The *param* address will be passed back to your function
348+
every time it is called, and should be used to store the parameters it
349+
might need.
350+
351+
The syntax for the **mlx_loop_hook** () function is similar to the
352+
previous ones, but the given function will be called when no event
353+
occurs, and is not bound to a specific window.
354+
355+
When it catches an event, the MiniLibX calls the corresponding function
356+
with fixed parameters:
357+
358+
359+
expose_hook(void *param);
360+
key_hook(unsigned int keycode, void *param);
361+
mouse_hook(unsigned int button, unsigned int x, unsigned int y, void *param);
362+
loop_hook(void *param);
363+
364+
These function names are arbitrary. They here are used to distinguish
365+
parameters according to the event. These functions are NOT part of the
366+
MiniLibX.
367+
368+
*param* is the address specified in the mlx\_\*\_hook calls. This
369+
address is never used nor modified by the MiniLibX. On key and mouse
370+
events, additional information is passed: *keycode* tells you which key
371+
is pressed (just try to find out :) ), ( *x* , *y* ) are the coordinates
372+
of the mouse click in the window, and *button* tells you which mouse
373+
button was pressed.
374+
375+
## GOING FURTHER WITH EVENTS
376+
377+
The MiniLibX provides a much generic access to other available events.
378+
The *mlx.h* include define **mlx_hook()** in the same manner
379+
mlx\_\*\_hook functions work. The event and mask values will be taken
380+
from the historical X11 include file \"X.h\". Some Wayland and MacOS
381+
events are mapped to these values when it makes sense, and the mask may
382+
not be used in some configurations.
383+
384+
See source code of the MiniLibX to find out how it will call your own
385+
function for a specific event.
386+
387+
# Got any suggestions?
388+
If you find any errors or have any new ideas for improving this repository, feel free to open an Issue or Pull Request, or contact me at my email address: nora@defitero.com

0 commit comments

Comments
 (0)