Compare commits

...

70 Commits

Author SHA1 Message Date
Lephenixnoir 8c5f68f16e
fxcg50/modion: rename file ion.c -> modion.c 2024-03-18 22:41:16 +01:00
Lephenixnoir fc9026798d
fxcg50/modion: clean up module implementation 2024-03-18 22:40:39 +01:00
Lephenixnoir 3775f28536
fxcg50/modkandinsky: use QSTRs to identify common colors 2024-03-18 22:34:48 +01:00
Lephenixnoir 5d51f205b9
fxcg50: officialize reserving 300 kB from _uram for the uPy GC 2024-03-18 22:10:41 +01:00
Lephenixnoir 3e2f7f0aa0
sh: slight cleanup of scheduled dupdate mechanism
Mostly renaming and integrating pe_dupdate() it with existing modules.
2024-03-18 22:08:37 +01:00
Lephenixnoir 71ddfff567
sh: switch add-in icon based on auto-detected dev builds
A dev build is detected if:
- There are dirty tracked files: "git status -uno --porcelain" nonempty
- We are not on main: "git describe" != "git describe main"
2024-03-18 22:07:59 +01:00
Lephenixnoir 4bec7be97f
numworks: extend timer tracking into autofree resource header
I imagine that we're going to use this more later, be it for serial/USB,
in-memory files, and maybe more.
2024-03-09 16:10:44 +01:00
Lephenixnoir fff06099a1
numworks: don't set global dwindow in kandinsky
This interferes with the shell and gives fairly bad results with print()
and input(). This way of doing things lets the shell print correctly.
The shell will override the borders, however. It's not immediately
obvious how to fix that properly with the auto-update system in main.c.
I'll try to move the auto screen update back to modkandinsky.c.
2024-03-05 07:28:57 +00:00
Lephenixnoir e7349aa227
numworks: replace custom time module with extension of official one 2024-03-04 22:40:32 +00:00
Lephenixnoir dfb73611f9
Merge remote-tracking branch 'slyvtt/numworks' into dev
This merges PR #14 with SlyVTT's Numworks support.
2024-03-03 13:17:10 +00:00
Lephenixnoir b804cb7583
docs: second pass on casiplot module 2024-03-03 13:10:20 +00:00
Lephenixnoir 38c0ec1d15
docs: add casioplot module (first pass) 2024-03-03 07:28:47 +00:00
Lephenixnoir eaf5f43da4
modgint: add dpoly() function (no doc yet) 2024-03-01 22:47:35 +00:00
Lephenixnoir c14f770483
docs: add example for drect(), dline() and the like 2024-02-26 22:38:10 +01:00
Lephenixnoir 430fe06adc
docs: add documentation for gint.getkey_opt() 2024-02-26 09:04:53 +01:00
Sylvain PILLOT d298d0538d added the full U+00A characters table for NW module Kandinsky 2024-02-25 19:40:53 +01:00
Lephenixnoir 3523a83bda
docs: first version of gint image documentation (fr+en) 2024-02-25 17:13:23 +01:00
Lephenixnoir ae3a135a81
docs: start of image documentation in gint (French only so far) 2024-02-25 16:31:19 +01:00
Lephenixnoir 30fcf592d1
docs: more modgint doc including first images 2024-02-25 14:45:30 +01:00
Lephenixnoir f9f94c999a
ports/fxcg50: provide basic memory status view on SHIFT+VARS 2024-02-25 12:46:01 +01:00
Lephenixnoir 7abbcc1dfc
ports/fx9860g3: reduce console scrollback to 50 lines / 4kB 2024-02-25 12:45:43 +01:00
Lephenixnoir a1848c0920
ports/sh: reduce console memory usage
Includes 2 previous commits.

So far simply by freezing old console lines and removing edition
metadata. Plus a little bit of compaction.

Benchmark: _uram memory free after startup and help() on G-III.
- Before this change: 2880
- After adding stredit_t in every line: 1880
- After keeping stredit_t only for last line: 4596

There's more to be done (double buffer), but later. I'm also gonna cut
down on the history size, probably.
2024-02-25 11:11:48 +01:00
Lephenixnoir 3f02fd86ea
ports/sh: WIP console memory usage: use stredit_t only on last line 2024-02-25 09:11:05 +01:00
Lephenixnoir 95eb40e25d
ports/sh: WIP console memory usage: introduce stredit_t
Currently takes more memory because I kept an stredit_t in every single
line but once I remove it from all but the last one we'll see some
progress.
2024-02-25 08:49:49 +01:00
Lephenixnoir 93e021f144
ports/sh: fix long-standing console line counting bug
The computation of how many screen lines at given console lines takes
used the globally-configured font instead of the widget's font. This
cause certain lines to have their size over-estimated on fx-9860G III,
leading to weird scrolling and too few lines being displayed in some
situations.

The bug was present on the fx-CG 50 but did not occur because the font
used for rendering the console is the default font.
2024-02-24 15:43:13 +01:00
Lephenixnoir c84caa533d
ports/fx9860g3: memory info view on SHIFT+VARS 2024-02-24 15:02:33 +01:00
Lephenixnoir bcdb3b74ca
ports/fx9860g3: smaller banner 2024-02-24 14:33:02 +01:00
Sylvain PILLOT 35b73ba954 added better (ie. cleaner) support for unicode characters in Kandinsky module 2024-02-24 10:49:28 +01:00
Sylvain PILLOT 5f385d8bcc convert NW font to unicode and added main letters with accent - WIP 2024-02-24 09:14:12 +01:00
Sylvain PILLOT 05bfce99d6 added a better mechanism for differentiating compilation of modules between FxCG50 and fx9860G 2024-02-13 19:56:28 +01:00
Sylvain PILLOT 3e302b1b34 Merge branch 'dev' into numworks 2024-02-13 18:31:03 +01:00
Sylvain PILLOT 26afb31ff2 added some extra verification on get_pixel() after validation on actual hardware 2024-02-13 18:27:47 +01:00
Lephenixnoir a2b9c32cea
ports/sh: seed random module (untested) 2024-02-13 14:32:58 +01:00
Sylvain PILLOT 6225b1d667 added more examples using Kandinsky/Ion/Time modules from NW + reorganisation of NW examples 2024-02-10 10:39:24 +01:00
Sylvain PILLOT 0f386bd779 made fully compatible Ion::keydown() function with key number support + update of documentation accordingly 2024-02-10 10:33:44 +01:00
Sylvain PILLOT 7fdf3aeb2e added automatic refresh and video output support on Kandinsky module 2024-02-08 21:01:31 +01:00
Sylvain PILLOT 263b9e83c8 Merge branch 'dev' into numworks 2024-02-08 08:02:21 +01:00
Sylvain PILLOT 2cb2bc6e67 add CGEXT_Set_Margin_Color() function in Kandinsky module 2024-02-07 21:30:59 +01:00
Lephenixnoir 0f867e9f72
WIP 2024-02-07 08:08:10 +01:00
Sylvain PILLOT d4dcc59d71 Translated NW modules documentation in English 2024-02-05 16:37:26 +01:00
Sylvain PILLOT 9bc05bbf03 Improved NW modules documentation 2024-02-05 09:20:35 +01:00
Lephenixnoir b1e39d3ab6
sh: add videocapture function (x² key) in debug mode 2024-02-04 23:03:05 +01:00
Lephenixnoir af8bacd271
fx9860g3: move console_line_t allocation to new PRAM0 allocator 2024-02-04 22:59:54 +01:00
Lephenixnoir d4501baadb
ports/sh: monitor memory usage at load and execution 2024-02-04 21:49:08 +01:00
Lephenixnoir 627c81c1b6
ports/sh: show current folder + proper errors if file browsing fails
The second half is from the latest commits on gint and JustUI.
2024-02-04 20:19:11 +01:00
Lephenixnoir 262a90f15c
ports/sh: disable unstable use of random memory 2024-02-04 20:00:47 +01:00
Sylvain PILLOT 9156a21afa Added missing safegard #ifdef FXCG50 for correct build of fx9860G version without NW modules 2024-02-04 19:38:12 +01:00
Sylvain PILLOT 226f82ec8c Add Kandinsky Extension for fxCG to be able to use wide screen - bugfix NW sreen is 222px high usable, not 240px 2024-02-04 17:29:36 +01:00
Sylvain PILLOT ba515dc168 Add Kandinsky Extension for fxCG to be able to use wide screen 2024-02-04 16:00:40 +01:00
Lephenixnoir 6ab101e1bd
modgint: refresh screen during getkey() + getkey_opt() delay + example 2024-02-03 20:44:45 +01:00
Sylvain PILLOT 6ae6d06387 Additional RAM - but a bit less greedy in the approach ;) 2024-02-03 20:11:17 +01:00
Sylvain PILLOT e7ad1e267d Add extra memory for fxCG50 hardware and fxCG emulator - todo for fxCG10/20 2024-02-03 18:09:48 +01:00
Lephenixnoir 6969309cd7
modgint: image support for fx-CG 2024-02-03 15:52:46 +01:00
Lephenixnoir eeb392a833
ports/sh: fix fx_image rendering control race condition 2024-02-03 10:17:53 +01:00
Lephenixnoir 00f009e0c2
docs: fix key listings, remove aliases 2024-02-03 09:53:12 +01:00
Sylvain PILLOT 0fb961768a Added examples in ports/sh/examples + documentation in docs/sh 2024-02-03 09:05:00 +01:00
Sylvain PILLOT 8072e5b1a9 added monotonic() function in NW time module 2024-02-02 22:16:56 +01:00
Lephenixnoir 7f647b57d7
docs: basic start to gint module documentation 2024-02-02 21:44:02 +01:00
Lephenixnoir b1ec8bcde4
modgint: more complete fx_image example 2024-02-02 21:43:45 +01:00
Sylvain PILLOT e3fd785200 Huge Cleaning - NW modules on fxCG50 - Ready for PR - Phase II 2024-02-02 20:36:19 +01:00
Sylvain PILLOT 12565ba8d2 Huge Cleaning - NW modules on fxCG50 - Ready for PR 2024-02-02 20:32:15 +01:00
Sylvain PILLOT 96077c1653 updated .gitignore 2024-01-30 22:55:23 +01:00
Lephenixnoir b9a457680b
modgint: basic image support, starting to work on G-III 2024-01-30 22:24:35 +01:00
Sylvain PILLOT 226d94494f NW modules for fxCG50 PythonExtra - Ok, now working correctly 2024-01-30 22:05:37 +01:00
Sylvain PILLOT bca7048525 WIP : NW modules for fxCG50 - corrected Kandinsky Draw_String when string contains \n char 2024-01-30 20:36:31 +01:00
Sylvain PILLOT 0868d96d2b WIP NW module on Casio - Restoration of correct dwindow and timers at module exit 2024-01-30 18:49:17 +01:00
Sylvain PILLOT 2ac213cae0 pursue NW support with dedicated modules (Ion, Time and Kandinsky now working) (only for fxCG50) 2024-01-29 23:16:01 +01:00
Sylvain PILLOT c207c7b6f7 start NW support with dedicated modules (only for fxCG50) 2024-01-29 07:43:24 +01:00
Sylvain PILLOT 774c8d0f44 added dpoly() function 2024-01-20 12:59:04 +01:00
Lephenixnoir 3a3ad0e155
ports/sh: enable delayed SHIFT/ALPHA
This little trick is necessary to have them in JustUI.
2024-01-16 19:48:26 +01:00
66 changed files with 4705 additions and 234 deletions

3
.gitignore vendored
View File

@ -23,3 +23,6 @@ user.props
# MacOS desktop metadata files
.DS_Store
# vscode
.vscode/

View File

@ -0,0 +1,120 @@
# Using Numworks' modules `kandinsky`, `ion` and `time` with PythonExtra
PythonExtra offers the possibility of using certain Numworks modules in order to make the scripts of this machine compatible as is on Casio fx-CG50 (no support on the fx9860G due to insufficient memory and lack of color screen ).
This is a Work in Progress (WIP) and the support is subject to extensive testing at this stage. The port concerns the Numworks `kandinsky`, `ion` and `time` modules which are machine specific and are now supported via this implementation. The `math`, `cmath`, `random` modules being identical between the `Numworks` version and the `builtins` modules of MicroPython, they are therefore not part of this implementation but are perfectly usable without modification in the scripts.
Note: the `turtle` and `matplotlib.pyplot` modules are not included in this implementation. It is possible to use the `turtle`, `matplotlib` and `casioplot` modules from Casio Education which are perfectly functional and provided as an example in `ports/sh/examples`.
## `kandinsky`
The `kandinsky` module provides support for graphics primitives via high-performance `gint` routines. All the functions of this module are available:
- `color(r,g,b)`: Generates the value of the color r,g,b. You can also simply use a tuple to define a color: (r,g,b).
- `get_pixel(x,y)`: Returns the color of the pixel at x,y coordinates as a tuple (r,g,b).
- `set_pixel(x,y,color)`: Lights the pixel x,y of the color color.
- `draw_string(text,x,y,[color1],[color2])`: Displays the text at x,y coordinates. The arguments color1 (text color) and color2 (text background color) are optional.
- `fill_rect(x,y,width,height,color)`: Fills a rectangle of width width and height height with the color color at the point of x and y coordinates.
The module also offers a certain number of colors explicitly named and accessible by a character string. The following values can be used instead of the color parameters of the `kandinsky` functions:
- "red", "r"
- "green", "g"
- "blue", "b"
- "black", "k"
- "white", "w"
- "yellow", "y"
- "pink"
- "magenta"
- "grey", "gray"
- "purple"
- "orange"
- "cyan"
- "brown"
The following functions are additions to take advantage of the wide screen of the fxCG and are therefore an extension of the `Kandinsky` module. They are therefore by definition not compatible with Python Numwork. These functions can be recognized by their names which all begin with `CGEXT_`:
- `CGEXT_Enable_Wide_Screen()`: Enables the fxCG wide screen, no settings are necessary. The x-coordinates of the physical screen can be negative to encroach on the left white stripe and greater than 319 pixels to encroach on the right white stripe;
- `CGEXT_Disable_Wide_Screen()`: Cancels the activation of the fxCG extended screen, no settings are necessary. The x coordinates of the physical screen will be constrained between 0 and 320 pixels. Beyond that, the route will not be carried out.
- `CGEXT_Is_Wide_Screen_Enabled()`: Returns `True` if the extended screen is active and `False` otherwise.
- `CGEXT_Set_Margin_Color( color )` : Paints the margin of the `Numworks` screen on fxCG with the specified color.
Note 1: after having made a plot in the extended area, it must be active to allow its deletion (typically via a call to the `fill_rect()` function with the appropriate parameters).
Note 2: In non-extended mode (by default when initializing the `Kandinsky` module) the screen coordinates go from (0,0) to (319,221) centered on the fxCG screen. In extended mode, the screen coordinates range from (-38,-1) to (358,223).
## `ion`
The `ion` module gives access to the `keydown(k)` function which returns True if the key k placed as an argument is pressed and False otherwise.
The “conversion” of the keys between the Numworks machine and Casio fxCG50 is done according to the following mapping:
| Numworks | Casio fxCG50 | Numworks Key # |
|----------|--------------|---------------------|
| KEY_LEFT | KEY_LEFT | 0 |
| KEY_UP | KEY_UP | 1 |
| KEY_DOWN | KEY_DOWN | 2 |
| KEY_RIGHT | KEY_RIGHT | 2 |
| KEY_OK | KEY_F1 | 4 |
| KEY_BACK | KEY_EXIT | 5 |
| KEY_HOME | KEY_MENU | 6 |
| KEY_ONOFF | KEY_ACON | 7 |
| ... | ... | ... |
| KEY_SHIFT | KEY_SHIFT | 12 |
| KEY_ALPHA | KEY_ALPHA | 13 |
| KEY_XNT | KEY_XOT | 14 |
| KEY_VAR | KEY_VARS | 15 |
| KEY_TOOLBOX | KEY_OPTN | 16 |
| KEY_BACKSPACE | KEY_DEL | 17 |
| KEY_EXP | KEY_EXP | 17 |
| KEY_LN | KEY_LN | 19 |
| KEY_LOG | KEY_LOG | 20 |
| KEY_IMAGINARY | KEY_F2 | 21 |
| KEY_COMMA | KEY_COMMA | 22 |
| KEY_POWER | KEY_POWER | 23 |
| KEY_SINE | KEY_SIN | 24 |
| KEY_COSINE | KEY_COS | 25 |
| KEY_TANGENT | KEY_TAN | 26 |
| KEY_PI | KEY_F3 | 27 |
| KEY_SQRT | KEY_F4 | 28 |
| KEY_SQUARE | KEY_SQUARE | 29 |
| KEY_SEVEN | KEY_7 | 30 |
| KEY_EIGHT | KEY_8 | 31 |
| KEY_NINE | KEY_9 | 32 |
| KEY_LEFTPARENTHESIS | KEY_LEFTP | 33 |
| KEY_RIGHTPARENTHESIS | KEY_RIGHTP | 34 |
| ... | ... | ... |
| KEY_FOUR | KEY_4 | 36 |
| KEY_FIVE | KEY_5 | 37 |
| KEY_SIX | KEY_6 | 38 |
| KEY_MULTIPLICATION | KEY_MUL | 39 |
| KEY_DIVISION | KEY_DIV | 40 |
| ... | ... | ... |
| KEY_ONE | KEY_1 | 42 |
| KEY_TWO | KEY_2 | 43 |
| KEY_THREE | KEY_3 | 44 |
| KEY_PLUS | KEY_ADD | 45 |
| KEY_MINUS | KEY_SUB | 46 |
| ... | ... | ... |
| KEY_ZERO | KEY_0 | 48 |
| KEY_DOT | KEY_DOT | 49 |
| KEY_EE | KEY_F5 | 50 |
| KEY_ANS | KEY_NEG | 51 |
| KEY_EXE | KEY_EXE | 52 |
## `time`
The `time` module gives access to two functions:
- `monotonic()`: Returns the clock value at the time the function is called.
- `sleep(t)`: Suspends execution for t seconds.

View File

@ -0,0 +1,120 @@
# Utilisation des modules `kandinsky`, `ion` et `time` de l'implémentation Python `Numworks`
PythonExtra offre la possibilité d'utiliser certains modules de la Numworks afin de rendre les scripts de cette machine compatible en l'état sur Casio fx-CG50 (pas de support sur la fx9860G pour cause de mémoire insuffisante et d'abence d'écran couleur).
Il s'agit d'un Work in Progress (WIP) et le support est sujet à tests approfondis à ce stade. Le port concerne les modules `kandinsky`, `ion` et `time` de la Numworks qui sont spécifiques à la machine et sont désormais supportés via cette implémentation. Les modules `math`, `cmath`, `random` étant identiques entre la version `Numworks` et les modules `builtins` de MicroPython, ils ne font donc pas partie de cette implémentation mais sont parfaitement utilisables sans modification dans les scripts.
Note : les modules `turtle` et `matplotlib.pyplot` ne sont pas repris dans cette implémentation. Il est possible d'utiliser les modules `turtle`, `matplotlib` et `casioplot` de Casio Education qui sont parfaitement fonctionnels et fournis en example dans `ports/sh/examples`.
## `kandinsky`
Le module `kandinsky` offre le support des primitives graphiques via les routines hautes performance de `gint`. Toutes les fonctions de ce module sont disponibles :
- `color(r,g,b)` : Génère la valeur de la couleur r,g,b. Vous pouvez aussi simplement utiliser un tuple pour définir une couleur : (r,g,b).
- `get_pixel(x,y)` : Renvoie la couleur du pixel aux coordonnées x,y sous forme de tuple (r,g,b).
- `set_pixel(x,y,color)` : Allume le pixel x,y de la couleur color.
- `draw_string(text,x,y,[color1],[color2])` : Affiche le texte text aux coordonnées x,y. Les arguments color1 (couleur du texte) et color2 (couleur de lʼarrière plan du texte) sont optionnels.
- `fill_rect(x,y,width,height,color)` : Remplit un rectangle de largeur width et de hauteur height avec la couleur color au point de coordonnées x et y.
Le module offre de plus un certain nombre de couleurs explicitement nommées et accessibles par une chaine de caractères. Les valeurs suivantes sont utilisables en lieu et place des paramètres de couleur des fonctions de `kandinsky` :
- "red", "r"
- "green", "g"
- "blue", "b"
- "black", "k"
- "white", "w"
- "yellow", "y"
- "pink"
- "magenta"
- "grey", "gray"
- "purple"
- "orange"
- "cyan"
- "brown"
Les fonctions suivantes sont des ajouts pour tirer partie de l'écran large de la fxCG et qui sont donc une extension du module `Kandinsky`. Elles ne sont donc par définition pas compatible avec le Python Numwork. Ces fonctions sont reconnaisables à leurs appellations qui commencent toutes par `CGEXT_` :
- `CGEXT_Enable_Wide_Screen()` : Active l'écran étendu de la fxCG, aucun paramètre n'est nécessaire. Les coordonnées x de l'écran physique peuvent être négatives pour empiéter sur la bande blanche de gauche et supérieures à 319 pixels pour empièter sur la bande blanche de droite;
- `CGEXT_Disable_Wide_Screen()` : Annule l'activation de l'écran étendu de la fxCG, aucun paramètre n'est nécessaire. Les coordonnées x de l'écran physique seront contraintes entre 0 et 320 pixels. Au-delà, le tracé ne sera pas effectué.
- `CGEXT_Is_Wide_Screen_Enabled()` : Retourne `True` si l'écran étendu est actif et `False` dans le cas contraire.
- `CGEXT_Set_Margin_Color( color )` : Trace les marge de la fxCG50 (pourtours de l'écran `Numworks`) de la couleur passée en argument.
Note 1 : après avoir réalisé un tracé dans la zone étendue, il faut que celle-ci soit active pour permettre son effacement (typiquement via un appel à la fonction `fill_rect()` avec les paramètres adéquats).
Note 2 : En mode non étendu (par défaut à l'initialisation du module `Kandinsky`) les coordonnées de l'écran vont de (0,0) à (319,221) centré sur l'écran de la fxCG. En mode étendu, les coordonnées de l'écran vont de (-38,-1) à (358,223).
## `ion`
Le module `ion` donne accès à la fonction `keydown(k)` qui renvoie True si la touche k placée en argument est appuyée et False sinon.
La "conversion" des touches entre la machine Numworks et Casio fxCG50 se fait selon le mapping suivant :
| Numworks | Casio fxCG50 | Numworks Key # |
|----------|--------------|---------------------|
| KEY_LEFT | KEY_LEFT | 0 |
| KEY_UP | KEY_UP | 1 |
| KEY_DOWN | KEY_DOWN | 2 |
| KEY_RIGHT | KEY_RIGHT | 2 |
| KEY_OK | KEY_F1 | 4 |
| KEY_BACK | KEY_EXIT | 5 |
| KEY_HOME | KEY_MENU | 6 |
| KEY_ONOFF | KEY_ACON | 7 |
| ... | ... | ... |
| KEY_SHIFT | KEY_SHIFT | 12 |
| KEY_ALPHA | KEY_ALPHA | 13 |
| KEY_XNT | KEY_XOT | 14 |
| KEY_VAR | KEY_VARS | 15 |
| KEY_TOOLBOX | KEY_OPTN | 16 |
| KEY_BACKSPACE | KEY_DEL | 17 |
| KEY_EXP | KEY_EXP | 17 |
| KEY_LN | KEY_LN | 19 |
| KEY_LOG | KEY_LOG | 20 |
| KEY_IMAGINARY | KEY_F2 | 21 |
| KEY_COMMA | KEY_COMMA | 22 |
| KEY_POWER | KEY_POWER | 23 |
| KEY_SINE | KEY_SIN | 24 |
| KEY_COSINE | KEY_COS | 25 |
| KEY_TANGENT | KEY_TAN | 26 |
| KEY_PI | KEY_F3 | 27 |
| KEY_SQRT | KEY_F4 | 28 |
| KEY_SQUARE | KEY_SQUARE | 29 |
| KEY_SEVEN | KEY_7 | 30 |
| KEY_EIGHT | KEY_8 | 31 |
| KEY_NINE | KEY_9 | 32 |
| KEY_LEFTPARENTHESIS | KEY_LEFTP | 33 |
| KEY_RIGHTPARENTHESIS | KEY_RIGHTP | 34 |
| ... | ... | ... |
| KEY_FOUR | KEY_4 | 36 |
| KEY_FIVE | KEY_5 | 37 |
| KEY_SIX | KEY_6 | 38 |
| KEY_MULTIPLICATION | KEY_MUL | 39 |
| KEY_DIVISION | KEY_DIV | 40 |
| ... | ... | ... |
| KEY_ONE | KEY_1 | 42 |
| KEY_TWO | KEY_2 | 43 |
| KEY_THREE | KEY_3 | 44 |
| KEY_PLUS | KEY_ADD | 45 |
| KEY_MINUS | KEY_SUB | 46 |
| ... | ... | ... |
| KEY_ZERO | KEY_0 | 48 |
| KEY_DOT | KEY_DOT | 49 |
| KEY_EE | KEY_F5 | 50 |
| KEY_ANS | KEY_NEG | 51 |
| KEY_EXE | KEY_EXE | 52 |
## `time`
Le module `time` donne accès à deux fonctions :
- `monotonic()` : Renvoie la valeur de lʼhorloge au moment où la fonction est appelée.
- `sleep(t)` : Suspend lʼexécution pendant t secondes.

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.4 KiB

View File

@ -0,0 +1,53 @@
# `casioplot`: CASIO's official I/O module
`casioplot` is a drawing library provided by CASIO as support for the official `turtle` and `matplotlib` modules. It was [announced in February 2020](https://www.planet-casio.com/Fr/forums/topic16154-1-modules-graphiques-python-en-avril-matplotlib-et-turtle.html) and [released in April](https://www.planet-casio.com/Fr/forums/topic16243-1-rendu-graphique-en-python-partie-1-decouverte-de-matplotlib-et-turtle.html). The module is notably available on G-III and fx-CG models with the same API. As of March 2024, `casioplot` is the only custom module in CASIO's official port of MicroPython. PythonExtra also provides the module for source compatibility.
```py
import casioplot
# or
from casioplot import *
```
**Contents**
- [Rendering functions](#rendering-functions)
- [Considerations for rendering](#considerations-for-rendering)
- [Differences with the official `casioplot` module](#differences-with-the-official-casioplot-module)
## Rendering functions
```py
show_screen() -> None
clear_screen() -> None
set_pixel(x: int, y: int, ?color: (int, int, int)) -> None
get_pixel(x: int, y: int) -> (int, int, int)
draw_string(x: int, y: int, text: str, ?color: (int, int, int), ?size: str) -> None
```
The rendering area is of size 128x64 on G-III and 384x216 on fx-CG. While the G-III supports only two colors (black and white), the fx-CG supports 65536 colors (16-bit RGB565). Still, the color format is _always_ (r,g,b) triplets with 0 ≤ r,g,b ≤ 255. This helps compatibility between models and with PC, at a performance cost. Calculators process these colors by _approximating_ them to the closest color they can represent.
Like the gint module, `casioplot` functions draw to an internal buffer called the VRAM, and the module only pushes that to the screen when explicitly asked to or at the end of the program.
`show_screen()` pushes the VRAM to screen after something has been drawn.
`clear_screen()` fills the VRAM in white.
`set_pixel()` changes the color of the pixel at (x,y) into the provided `color` encoded as an (r,g,b) triplet (black if not provided). Building triplets is a bit slow so don't write something like `set_pixel(x, y, (0,0,0))` in a loop, store the triplet in a variable and use that instead.
`get_pixel()` returns the color of the pixel at (x,y) in VRAM as an (r,g,b) triplet. Because VRAM stores colors in the same format as the display, which typically doesn't support all 16 million colors, `get_pixel()` immediately after `set_pixel()`usually returns an _approximation_ of the original color. Because it allocates a triplet, this function is also excruciatingly slow to call in a loop, to the point of creating GC-driven lag spikes that in some situations make it completely unusable.
`draw_string()` draws text. (x,y) is the location of the top-left corner of the rendered string. The text color is optional; if specified it should be an (r,g,b) triplet, otherwise it is black. The font size can be either of the strings `"small"`, `"medium"` or `"large"` and defaults to medium. On the G-III, the font sizes `"small"` and `"medium"` are identical, but on the fx-CG all three fonts are different. Any `'\n'` in the string is replaced with spaces when drawing.
TODO: Example
## Considerations for rendering
_Text mode._ `casioplot` automatically switches PythonExtra into graphics mode when imported. Programs that want to render graphics frames after a `print()` should `show_screen()` _before_ rendering the first frame since `print()` switches to text mode.
_Performance._ Writing to VRAM is the main reason why rendering in `casioplot` is sometimes much faster than in Basic. Basic pushes the rendered data to the display after every call, which is slow. Unfortunately, `casioplot` ends up losing for complex drawing tasks for which Basic has complex functions (like DrawStat) while `casioplot` just has single pixels. In this case, functions from the [gint module](modgint-en.md) can be of help.
## Differences with the official `casioplot` module
- PythonExtra's implementation doesn't automatically show the screen when the program finishes executing.
- The fonts for `draw_string()` are not identical on the G-III port.
- The fonts for `draw_string()` currently only support ASCII.
- The drawable space on fx-CG is at the top left section of the screen.

View File

@ -0,0 +1,53 @@
# `casioplot` : Module officiel d'I/O de CASIO
`casioplot` est une bibliothèque de dessin fournie par CASIO pour supporter les modules officiels `turtle` et `matplotlib`. Le module a été [annoncé en Février 2020](https://www.planet-casio.com/Fr/forums/topic16154-1-modules-graphiques-python-en-avril-matplotlib-et-turtle.html) et [publié en Avril](https://www.planet-casio.com/Fr/forums/topic16243-1-rendu-graphique-en-python-partie-1-decouverte-de-matplotlib-et-turtle.html). Le module est disponible sur Graph 35+E II et Graph 90+E avec la même API. C'est le seul module personnalisé dans le port MicroPython de CASIO (à date de Mars 2024). PythonExtra fournit le module par compatibilité pour les scripts officiels.
```py
import casioplot
# or
from casioplot import *
```
**Sommaire**
- [Fonctions de dessin](#fonctions-de-dessin)
- [Considérations spéciales pour le rendu](#considérations-spéciales-pour-le-rendu)
- [Différences avec le module `casioplot` officiel](#différences-avec-le-module-casioplot-officiel)
## Fonctions de dessin
```py
show_screen() -> None
clear_screen() -> None
set_pixel(x: int, y: int, ?color: (int, int, int)) -> None
get_pixel(x: int, y: int) -> (int, int, int)
draw_string(x: int, y: int, text: str, ?color: (int, int, int), ?size: str) -> None
```
La zone de dessin est de 128x64 pixels sur Graph 35+E II et 384x216 pixels sur la Graph 90+E. La Graph 35+E II ne supporte que deux couleurs (noir et blanc) tandis que la Graph 90+E peut afficher 65536 couleurs (RGB565 16 bits). Cependant, le format de couleurs est _quand même_ des triplets (r,g,b) avec 0 ≤ r,g,b ≤ 255. Ce choix maintient la compabilité entre les modèles et avec le PC, en échange d'un coût en performance. Les calculatrices approximent ces couleurs en les remplaçant par la couleur la plus proche qu'elles peuvent afficher.
Comme le module gint, `casioplot` dessine systématiquement dans un buffer interne qu'on appelle la VRAM, et n'affiche le résultat à l'écran que quand on lui demande explicitement, ou à la fin de l'exécution.
`show_screen()` affiche à l'écran les contenus de la VRAM après un dessin.
`clear_screen()` remplit la VRAM en blanc.
`set_pixel()` remplace la couleur du pixel à la position (x,y) par `color`, qui doit être un triplet (r,g,b) si fournie, et sera noir si absente. Construire des triplets est un peu lent donc il vaut mieux éviter d'écrire des appels comme `set_pixel(x, y, (0,0,0))` dans des boucles ; il est plus performant de stocker le triplet dans une variable et d'utiliser la variable.
`get_pixel()` renvoie la couleur du pixel à la position (x,y) de la VRAM sous la forme d'un triplet (r,g,b). Comme la VRAM stocke les couleurs au même format que l'écran, qui ne supporte généralement pas 16 millions de couleurs, appeler `get_pixel()` juste après `set_pixel()` renvoie une _approximation_ de la couleur originale. Comme cette fonction alloue un triplet, elle est atrocement lente à appeler en boucle, au point où elle peut créer du lag simplement par sa pression sur le GC, ce qui la rend parfois inutilisable.
`draw_string()` affiche du texte. (x,y) est la position du coin haut-gauche du rectangle dans lequel le texte est dessiné. La couleur est optionnelle ; si elle est spécifiée il faut que ce soit un triplet (r,g,b), sinon c'est noir. La taille peut être l'une des trois chaînes `"small"`, `"medium"` ou `"large"` ; la valeur par défaut est `"medium"`. Sur la Graph 35+E II, les polices `"small"` et `"medium"` sont identiques, tandis que sur la Graph 90+E les trois polices sont différentes. Tout `'\n'` dans la chaîne est remplacé par un espace avant d'afficher.
TODO: Exemple
## Considérations spéciales pour le rendu
_Mode texte._ `casioplot` passe automatiquement PythonExtra en mode graphique à l'import. Les programmes qui veulent faire du dessin graphique après un `print()` doivent appeler `show_screen()` _avant_ de commencer à dessiner le premier frame parce que `print()` repasse en mode texte.
_Performance._ Le fait que les fonctions de dessin écrivent silencieusement dans la VRAM est la principale raison pour laquelle `casioplot` est parfois beaucoup plus rapide que le dessin en Basic. PRGM réaffiche la VRAM à l'écran après chaque appel d'une fonction de dessin, ce qui est lent. Malheureusement, `casioplot` redevient plus lent que le Basic pour les dessins complexes pour lesquels il y a des fonctions spécialisées en Basic (comme `DrawStat`), puisque `casioplot` ne peut dessiner qu'un pixel à la fois. Dans ces situations, il vaut mieux utiliser les fonctions de dessin du [module gint](modgint-fr.md).
## Différences avec le module `casioplot` officiel
- L'implémentation PythonExtra du module n'affiche pas automatiquement l'écran à la fin du programme.
- Les polices pour `draw_string()` diffèrent des polices officielles sur Graph 35+E II.
- Les polices pour `draw_string()` ne supportent actuellement que les caractères ASCII.
- Les zone de dessin sur Graph 90+E est en haut à gauche de l'écran.

433
docs/sh/modgint-en.md Normal file
View File

@ -0,0 +1,433 @@
# `gint`: Wrapper for the gint library
PythonExtra is developed with the [fxSDK](https://gitea.planet-casio.com/Lephenixnoir/fxsdk) and uses the [gint kernel](https://gitea.planet-casio.com/Lephenixnoir/gint) as a runtime. The Python module `gint` provides access to gint's internal functions for rendering, keyboard, and more. Since gint has many versatile functions with good performance, it is beneficial to use it instead of e.g. `casioplot` or `turtle`.
The `gint` module tries to match its API with the original API of the C library, which is why few functions use keyword arguments or overloading. There are a few differences documented at the end of this document. For details not described in this document, one can refer to [gint's header files](https://gitea.planet-casio.com/Lephenixnoir/gint/src/branch/master/include/gint) which are always applicable unless this document explicitly says otherwise.
All constants, functions, etc. discussed here are in the `gint` module.
```py
import gint
# or:
from gint import *
```
**Contents**
- [Keyboard input](#keyboard-input)
- [Drawing and rendering](#drawing-and-rendering)
- [Differences with gint's C API](#differences-with-gints-c-api)
## Keyboard input
Reference headers: [`<gint/keyboard.h>`](https://gitea.planet-casio.com/Lephenixnoir/gint/src/branch/master/include/gint/keyboard.h) and [`<gint/keycodes.h>`](https://gitea.planet-casio.com/Lephenixnoir/gint/src/branch/master/include/gint/keycodes.h).
The module provides integer constants to refer to keyboard keys, with the following names:
| | | | | | |
|------------|------------|------------|------------|------------|-------------|
|`KEY_F1` |`KEY_F2` |`KEY_F3` |`KEY_F4` |`KEY_F5` |`KEY_F6` |
|`KEY_SHIFT` |`KEY_OPTN` |`KEY_VARS` |`KEY_MENU` |`KEY_LEFT` |`KEY_UP` |
|`KEY_ALPHA` |`KEY_SQUARE`|`KEY_POWER` |`KEY_EXIT` |`KEY_DOWN` |`KEY_RIGHT` |
|`KEY_XOT` |`KEY_LOG` |`KEY_LN` |`KEY_SIN` |`KEY_COS` |`KEY_TAN` |
|`KEY_FRAC` |`KEY_FD` |`KEY_LEFTP` |`KEY_RIGHTP`|`KEY_COMMA` |`KEY_ARROW` |
|`KEY_7` |`KEY_8` |`KEY_9` |`KEY_DEL` |`KEY_ACON` | |
|`KEY_4` |`KEY_5` |`KEY_6` |`KEY_MUL` |`KEY_DIV` | |
|`KEY_1` |`KEY_2` |`KEY_3` |`KEY_ADD` |`KEY_SUB` | |
|`KEY_0` |`KEY_DOT` |`KEY_EXP` |`KEY_NEG` |`KEY_EXE` | |
### Keyboard events
```
key_event:
.time -> int
.mod -> bool
.shift -> bool
.alpha -> bool
.type -> KEYEV_NONE | KEYEV_DOWN | KEYEV_UP | KEYEV_HOLD
.key -> KEY_*
```
gint communicates information about keyboard activity through _events_. Events indicate when a key (`.key` field) is pressed, repeated or released (`.type` field equal to `KEYEV_DOWN`, `KEYEV_HOLD` and `KEYEV_UP` respectively), when (`.time` field), and whether modifiers (SHIFT, `.shift` field, and ALPHA, `.alpha` field) where active at that time.
(The `.mod` field isn't very interesting, and the `KEYEV_NONE` value for the `.type` field is discussed later with `pollevent()`.)
The functions `getkey()`, `getkey_opt()`, `pollevent()` and `waitevent()` all return events.
### Waiting for a key press
```py
getkey() -> key_event
getkey_opt(opts: GETKEY_*, timeout_ms: int | None) -> key_event
```
The function `getkey()` pauses the program until a key is pressed or repeated, and returns the associated event (which is always of type `KEYEV_DOWN` or `KEYEV_HOLD`). By default, only arrow keys are repeated, once after 400 ms, then every 40 ms.
A few things can happen while `getkey()` is waiting. The user can press SHIFT or ALPHA which will trigger modifiers and affect the `.shift` and `.alpha` fields of the returned event. The user can also go to the main menu by pressing MENU or turn the calculator off with SHIFT+AC/ON.
_Example._ In a selection menu with N possible items, one could navigate with the up and down arrow keys, jump to the top or bottom with SHIFT up and SHIFT down, and validate with EXE.
```py
ev = getkey()
if ev.key == KEY_EXE:
pass # Validate
elif ev.key == KEY_UP and ev.shift:
pos = 0 # Jump to top
elif ev.key == KEY_DOWN and ev.shift:
pos = N-1 # Jump to bottom
elif ev.key == KEY_UP and pos > 0:
pos -= 1 # Move one place up
elif ev.key == KEY_DOWN and pos < N-1:
pos += 1 # Move one place down
```
The function `getkey_opt()` generalizes `getkey()` and provides more options to customize its behavior. The `opts` parameter accepts a combination (sum) of options from the table below, and the parameter `timeout_ms`, when not `None`, specifies how long `getkey_opt()` should wait before returning a `KEYEV_NONE` event if nothing happens. The delay is specified in milliseconds but the actual resolution is about 7-8 ms.
| Option | `getkey()` | Description |
|------------------------|------------|--------------------------------------------------|
| `GETKEY_MOD_SHIFT` | Yes | SHIFT is a modifier key |
| `GETKEY_MOD_ALPHA` | Yes | ALPHA is a modifier key |
| `GETKEY_BACKLIGHT` | Yes | SHIFT+OPTN toggles display backlight |
| `GETKEY_MENU` | Yes | MENU brings up the calculator's main menu |
| `GETKEY_REP_ARROWS` | Yes | Repeats arrow keys when held |
| `GETKEY_REP_ALL` | No | Repeats all keys when held |
| `GETKEY_REP_PROFILE` | Yes* | Enable custom repeat delays |
| `GETKEY_FEATURES` | Yes* | Enable custom global shortcuts |
| `GETKEY_MENU_DUPDATE` | Yes | Update display after return to menu (fx-CG) |
| `GETKEY_MENU_EVENT` | No | Send a `KEYEV_OSMENU` event after return to menu |
| `GETKEY_POWEROFF` | Yes | SHIFT+AC/ON turns the calculator off |
`getkey()` is equivalent to `getkey_opt(opts, None)` where `opts` is the sum of all options that have "Yes" in the `getkey()` column above.
The two options `GETKEY_REP_PROFILE` and `GETKEY_FEATURES` are only useful when configured through other functions that are not yet available in PythonExtra.
### Reading keyboard events in real time
```py
pollevent() -> key_event
waitevent() -> key_event
clearevents() -> None
```
gint records keyboard activity in the background while the program is running. Events are placed in a queue until the program reads them. This is how `getkey()` learns about keyboard activity, for example.
The `pollevent()` function provides direct access to events. `pollevent()` returns the oldest event that hasn't yet been read by the program. If there are no events waiting to be read, `pollevent()` returns a "fake" event with type `KEYEV_NONE` to indicate that the queue is empty.
Since `pollevent()` returns instantly, it can be used to read keyboard activity without pausing the program.
_Example._ A game loop could, at every frame, read all pending events to determine when the player pressed the SHIFT key (in this example the "action" key) to perform an action.
```py
# Render game...
while True:
ev = pollevent()
if ev.type == KEYEV_NONE:
break # We're done reading events
if ev.type == KEYEV_DOWN and ev.key == KEY_SHIFT:
pass # The SHIFT key was just pressed!
# Implicitly ignores other keys
# Simulate game...
```
The `waitevent()` function operates similarly, but if there are no pending events it waits for something to happen before returning. It is used quite rarely because in waiting situations one usually uses `getkey()` instead.
The function `clearevents()` reads and ignores all events, i.e. it "throws away" all the information about recent keyboard activity. It is useful to know the immediate state of the keyboard with `keydown()'` (see below). `clearevents()` is equivalent to the following definition:
```py
def clearevents():
ev = pollevent()
while ev.type != KEYEV_NONE:
ev = pollevent()
```
### Reading the immediate state of the keyboard
```py
keydown(key: int) -> bool
keydown_all(*keys: [int]) -> bool
keydown_any(*keys: [int]) -> bool
```
After events have been read and the event queue is empty, one can query the immediate state of keys with the `keydown()` function. `keydown(k)` returns `True` if key `k` is currently pressed, `False` otherwise. This function only works **after events have been read**, which is usually done either with `pollevent()` or with `clearevents()`.
_Example._ A game loop could check the state of the left/right keys at every frame to move the player.
```py
while True:
ev = pollevent()
# ... same thing as the pollevent() example
if keydown(KEY_LEFT):
player_x -= 1
if keydown(KEY_RIGHT):
player_x += 1
```
`keydown_all()` takes a series of keys as parameters and returns `True` if they are all pressed. `keydown_any()` is similar and returns `True` if at least one of the listed keys is pressed.
### Quickly querying key state changes
```py
cleareventflips() -> None
keypressed(key: int) -> bool
keyreleased(key: int) -> bool
```
`keydown()` only tells whether keys are pressed at a given time; it cannot be used to check when keys change from the released state to the pressed state or the other way around. To do this, one must either read individual events (which can be annoying), remember which keys were pressed at the previous frame, or use the functions described below.
`keypressed(k)` and `keyreleased(k)` indicate whether key `k` was pressed/released since the last call to `cleareventflips()`. As previously, here "pressed/released" means "as indicated by keyboard events read" not as a real-time state change.
_Example._ A game loop could test both the immediate state of some keys and state changes for other keys by using immediate functions after `cleareventflips()` followed by `clearevents()`.
```py
# Render game...
cleareventflips()
clearevents()
if keypressed(KEY_SHIFT):
pass # Action !
if keydown(KEY_LEFT):
player_x -= 1
if keydown(KEY_RIGHT):
player_x += 1
# Simulate game...
```
### Miscellaneous keyboard functions
```py
keycode_function(key: int) -> int
keycode_digit(key: int) -> int
```
`keycode_function(k)` returns the F-key number of `k` (i.e. 1 for `KEY_F1`, 2 for `KEY_F2`, etc.) and -1 for other keys.
`keycode_digit(k)` returns the digit associated with `k` (i.e. 0 for `KEY_0`, 1 for `KEY_1`, etc.) and -1 for other keys.
## Drawing and rendering
Reference headers: [`<gint/display.h>`](https://gitea.planet-casio.com/Lephenixnoir/gint/src/branch/master/include/gint/display.h), and for some details [`<gint/display-fx.h>`](https://gitea.planet-casio.com/Lephenixnoir/gint/src/branch/master/include/gint/display-fx.h) and [`<gint/display-cg.h>`](https://gitea.planet-casio.com/Lephenixnoir/gint/src/branch/master/include/gint/display-cg.h).
### Color manipulation
```py
C_WHITE: int # White
C_BLACK: int # Black
C_LIGHT: int # Light gray (on B&W: gray engine)
C_DARK: int # Dark gray (on B&W: gray engine)
C_NONE: int # Transparent
C_INVERT: int # Function: inverse
# Black-and-white (B&W) models only:
C_LIGHTEN: int # Function: lighten (gray engine)
C_DARKEN: int # Function: darken (gray engine)
# fx-CG models only:
C_RED: int # Pure red
C_GREEN: int # Pure green
C_BLUE: int # Pure blue
C_RGB(r: int, g: int, b: int) -> int
```
Colors are all integers (manipulating `(r,g,b)` tuples is excruciatingly slow and requires memory allocations all over the place). A few default colors are provided.
On the fx-CG series, the `C_RGB()` function can be used to create colors from three components ranging from 0 to 31.
TODO: Explain the gray engine.
### Basic rendering functions
```py
DWIDTH: int
DHEIGHT: int
dupdate() -> None
dclear(color: int) -> None
dpixel(x: int, y: int, color: int) -> None
dgetpixel(x: int, y: int) -> int
```
The integers `DWIDTH` and `DHEIGHT` indicate the screen's dimensions. The screen is 128x64 on black-and-white models (like the G-III) and 396x224 on the fx-CG series (the full screen is available).
All rendering functions draw to an internal image called the "VRAM"; rendering calls are thus not immediate visible on the screen. For the result to be visible one must call the `dupdate()` function, which transfers the contents of the VRAM to the real display. Usually, this is done after rendering everything we need on one frame instead of after each drawing function call.
In PythonExtra, `dupdate()` also indicates a "switch to graphics mode". Due to certain optimizations any call to `print()` is considered a "switch to text mode", and while in text mode the shell might redraw at any time. In order to draw after using text mode, one must call `dupdate()` to force a switch to graphics mode before starting rendering. Otherwise the shell and program might render at the same time and produce incoherent results.
`dclear()` fills the screen with a uniform color.
`dpixel()` changes a pixel's color. Coordinates, as in every other drawing function, are (x,y) where `x` is in the range 0 to DWIDTH-1 inclusive (0 being left) and `y` is in the range 0 to DHEIGHT-1 inclusive (0 being top).
`dgetpixel()` returns the color of a pixel. Note that `dgetpixel()` reads from VRAM, not from the display.
_Example ([`ex_draw1.py`](../../ports/sh/examples/ex_draw1.py))._
```py
from gint import *
dclear(C_WHITE)
for y in range(10):
for x in range(10):
if (x^y) & 1:
dpixel(x, y, C_BLACK)
dupdate()
```
![](images/modgint-draw1-cg.png) ![](images/modgint-draw1-fx.png)
### Geometric shape rendering functions
```py
drect(x1: int, y1: int, x2: int, y2: int, color: int) -> None
drect_border(x1: int, y1: int, x2: int, y2: int, fill_color: int,
border_width: int, border_color: int) -> None
dline(x1: int, y1: int, x2: int, y2: int, color: int) -> None
dhline(y: int, color: int) -> None
dvline(x: int, color: int) -> None
dcircle(x: int, y: int, radius: int, fill_color: int,
border_color: int) -> None
dellipse(x1: int, y1: int, x2: int, y2: int, fill_color: int,
border_color: int) -> None
```
`drect()` draws a flat rectangle spanning from (x1, y1) to (x2, y2) (both inclusive). The order of points does not matter, i.e. x1 ≥ x2 and y1 ≥ y2 are both allowed.
`drect_border()` is similar but also draws a border. The border is located _inside_ the rectangle.
`dline()` draws a straight line from (x1, y1) to (x2, y2). The shortcut functions `dhline()` and `dvline()` draw a full horizontal and vertical line across the screen respectively.
`dcircle()` draws a circle defined by its center and radius using the Bresenham algorithm. The colors for the circle's interior and its edge can be specified separately, including as `C_NONE` (transparent). By construction, `dcircle()` can only draw circles of odd diameter; for even diameters, use `dellipse()`.
`dellipse()` draws an ellipse defined by its bounding box. Both (x1, y1) and (x2, y2) are included in the bounding box. To render an ellipse from its center coordinates (x,y) and semi-major/minor axes a/b, use `dellipse(x-a, y-b, x+a, y+b, fill_color, border_color)`.
_Exemple ([`ex_draw2.py`](../../ports/sh/examples/ex_draw2.py))._
![](images/modgint-draw2-cg.png) ![](images/modgint-draw2-fx.png)
_Example ([`ex_circle.py`](../../ports/sh/examples/ex_circle.py))._
![](images/modgint-circle-cg.png) ![](images/modgint-circle-fx.png)
### Image rendering functions
```py
dimage(x: int, y: int, img: image) -> None
dsubimage(x: int, y: int, img: image, left: int, top: int, width: int, height: int) -> None
```
**On black-and-white models**
```py
image:
.profile -> IMAGE_MONO | ...
.width -> int
.height -> int
.data -> buffer-like
# Constructor
image(profile: IMAGE_*, width: int, height: int, data: buffer-like) -> image
```
Images on black-and-white models have either 2 or 4 colors (when the gray engine is enabled). Each image has a `.profile` field indicating the image format (the name "profile" comes from old gint versions), two fields `.width` and `.height` specifying its size, and a `.data` field providing direct access to pixel data.
The table below lists the four available formats.
| Format | Colors | Layers | Name in fxconv |
|--------------------|---------------------------------|--------|----------------|
| `IMAGE_MONO` | Black/white (2) | 1 | `mono` |
| `IMAGE_MONO_ALPHA` | Black/white, transparent (3) | 2 | `mono_alpha` |
| `IMAGE_GRAY` | Shades of gray (4) | 2 | `gray` |
| `IMAGE_GRAY_ALPHA` | Shades of gray, transparent (5) | 3 | `gray_alpha` |
The raw storage format for data is a bit complicated. The image is stored in row-major order; each line is represented left-to-right by a series of words each covering 32 pixels. Each word contains one bit per pixel (as a 4-byte integer) for each layer.
The easiest way to obtain an image is to generate the associated code with the fxconv tool from the [fxSDK](https://gitea.planet-casio.com/Lephenixnoir/fxsdk). The options `--bopti-image-fx --fx` specify a conversion for black-and-white models and the metadata `profile:mono` selects the encoding. The table above lists the value of `profile:` to use for each desired format. For instance for [`fx_image_7seg.py`](../../ports/sh/examples/fx_image_7seg.png) :
![](../../ports/sh/examples/fx_image_7seg.png)
```bash
% fxconv --bopti-image fx_image_7seg.png -o 7seg.py --fx profile:mono name:seg --py
```
```py
import gint
seg = gint.image(0, 79, 12, b'|\x00||\x00|||||\x00\x00\xba\x02::\x82\xb8\xb8:\xba\xba\x00\x00\xc6\x06\x06\x06\xc6\xc0\xc0\x06\xc6\xc6\x00\x00\xc6\x06\x06\x06\xc6\xc0\xc0\x06\xc6\xc6\x00\x00\x82\x02\x02\x02\x82\x80\x80\x02\x82\x82\x00\x00\x00\x00|||||\x00||\x00\x00\x82\x02\xb8:::\xba\x02\xba:\x00\x00\xc6\x06\xc0\x06\x06\x06\xc6\x06\xc6\x06\x00\x00\xc6\x06\xc0\x06\x06\x06\xc6\x06\xc6\x06\x00\x00\xc6\x06\xc0\x06\x06\x06\xc6\x06\xc6\x06\x00\x00\xba\x02\xb8:\x02:\xba\x02\xba:\x00\x00|\x00||\x00||\x00||\x00\x00')
```
The option `--py-compact` generates much shorter code (less `\x`), however the resulting file can generally not be read or modified by a text editor. The only easy option to use that file is to send it to the calculator and import it as-is. (It can also by manipulated by a program if needed.)
`dimage()` renders an image, positioned so that the top-left corner of the image is located at (x, y).
`dsubimage()` renders a sub-rectangle of an image. The rectangle starts at position (left, top) within the image and is of size `width` by `height`. It is positioned so that the (left, top) pixel is drawn at (x, y) on the screen.
_Example ([`fx_image.py`](../../ports/sh/examples/fx_image.py))._
![](images/modgint-image-fx.png)
**On color models**
```py
image:
.format -> IMAGE_RGB565 | ...
.flags -> int
.color_count -> int
.width -> int
.height -> int
.stride -> int
.data -> buffer-like
.palette -> buffer-like
# Constructor
image(format: IMAGE_*, color_count: int, width: int, height: int, stride: int, data: buffer-like, palette: buffer-like) -> image
# Format-specific constructors
image_rgb565(width: int, height: int, data: buffer-like) -> image
image_rgb565a(width: int, height: int, data: buffer-like) -> image
image_p8_rgb565(width: int, height: int, data: buffer-like, palette: buffer-like) -> image
image_p8_rgb565a(width: int, height: int, data: buffer-like, palette: buffer-like) -> image
image_p4_rgb565(width: int, height: int, data: buffer-like, palette: buffer-like) -> image
image_p4_rgb565a(width: int, height: int, data: buffer-like, palette: buffer-like) -> image
```
Images on color models are available in multiple formats as indicated by the `.format` field; the possible values are listed below. Formats differ in the number of colors, the presence of a transparent color, and the presence of a palette. Of course, the less colors the smaller the memory footprint of the image, so in general it is very beneficial to use the smallest format in which an image fits.
| Format | Colors | Palette | Name in fxconv |
|--------------------|---------------------|-------------|----------------|
| `IMAGE_RGB565` | 65536 | No | `rgb565` |
| `IMAGE_RGB565A` | 65535 + transparent | No | `rgb565a` |
| `IMAGE_P8_RGB565` | 256 | Yes (1-256) | `p8_rgb565` |
| `IMAGE_P8_RGB565A` | 255 + transparent | Yes (1-256) | `p8_rgb565a` |
| `IMAGE_P4_RGB565` | 16 | Yes (16) | `p4_rgb565` |
| `IMAGE_P4_RGB565A` | 15 + transparent | Yes (16) | `p4_rgb565a` |
The `.color_count` field indicates the number of colors in the palette, when there is one. For P8 formats this number varies between 1 and 256, and for P4 formats it is always equal to 16. The `.width` and `.height` fields indicate the image's size. Finally, the `.data` and `.palette` fields provide direct access to the raw data for the image's pixels and color palette.
(The `.flags` field has details of memory management that shouldn't be important to Python scripts. The `.stride` fields indicates the distance in bytes between rows of pixels in `.data` and should similarly be of rare use.)
The functions `dimage()` and `dsubimage()` work the same way as for black-and-white models; please see above.
As for black-and-white models, images can be converted into a Python format with fxconv. For instance with [`cg_image_puzzle.png`](../../ports/sh/examples/cg_image_puzzle.png) (full encoding elided):
![](../../ports/sh/examples/cg_image_puzzle.png)
```bash
% fxconv --bopti-image cg_image_puzzle.png -o puzzle.py --cg profile:p4_rgb565 name:puzzle --py
```
```py
import gint
puzzle = gint.image(6, 16, 64, 32, 32,
b'\xbb\xbb\xbb\xbb\xbb ... \xdd\xdd\xdd\xdd\xdd\xdd\xdd',
b'\xff\xff\xcfW\x86\xd8\xbe|\xceP\xe5\x8a\x963f9u\x9c}\xa8\x9dxD\xfa\x83\xceLNZ\xcci\xa7')
```
The option `--py-compact` is recommended to reduce code size; please see details in the black-and-white section above.
_Exemple ([`cg_image.py`](../../ports/sh/examples/cg_image.py))._
![](images/modgint-image-cg.png)
## Differences with gint's C API
- `dsubimage()` doesn't have its final parameter `int flags`. The flags are only minor optimizations and could be removed in future gint versions.
- Image constructors`image()` and `image_<format>()` don't exist in the C API.
- Asynchronous volatile-flag-based timeouts are replaced with synchronous millisecond delays (integer value or `None`).
TODO: There are more.

434
docs/sh/modgint-fr.md Normal file
View File

@ -0,0 +1,434 @@
# `gint`: Module d'accès aux fonctionnalités de gint
PythonExtra est écrit à l'aide du [fxSDK](https://gitea.planet-casio.com/Lephenixnoir/fxsdk) et utilise [gint](https://gitea.planet-casio.com/Lephenixnoir/gint) pour exécuter l'add-in. Le module Python `gint` permet d'accéder aux fonctions internes de gint en Python pour le dessin, le clavier, etc. Comme gint possède beaucoup de fonctions utiles avec de bonnes performances, il est intéressant de s'en servir au lieu d'utiliser e.g. `casioplot` ou `turtle`.
Le module `gint` essaie de garder en Python la même API que dans la version originale de la bibliothèque en C, c'est pourquoi peu de fonctions utilisent des arguments nommés ou autres fonctions surchargées. Il y a quelques différences, documentées à la fin de cette page. En cas de doute, la documentation fournie par les [fichiers d'en-tête de gint](https://gitea.planet-casio.com/Lephenixnoir/gint/src/branch/master/include/gint) (les `.h`) est tout à fait applicable pour comprendre les comportements que cette page n'explique pas.
Tous les noms de constantes, fonctions, etc. discutés dans cet article sont dans le module `gint`.
```py
import gint
# ou:
from gint import *
```
**Sommaire**
- [Saisie au clavier](#saisie-au-clavier)
- [Dessin à l'écran](#dessin-à-lécran)
- [Différences avec l'API C de gint](#différences-avec-lapi-c-de-gint)
## Saisie au clavier
Les en-têtes de référence sont [`<gint/keyboard.h>`](https://gitea.planet-casio.com/Lephenixnoir/gint/src/branch/master/include/gint/keyboard.h) et [`<gint/keycodes.h>`](https://gitea.planet-casio.com/Lephenixnoir/gint/src/branch/master/include/gint/keycodes.h).
### Noms des touches
Le module fournit des constantes entières désignant toutes les touches du clavier. Les noms sont les suivants :
| | | | | | |
|------------|------------|------------|------------|------------|-------------|
|`KEY_F1` |`KEY_F2` |`KEY_F3` |`KEY_F4` |`KEY_F5` |`KEY_F6` |
|`KEY_SHIFT` |`KEY_OPTN` |`KEY_VARS` |`KEY_MENU` |`KEY_LEFT` |`KEY_UP` |
|`KEY_ALPHA` |`KEY_SQUARE`|`KEY_POWER` |`KEY_EXIT` |`KEY_DOWN` |`KEY_RIGHT` |
|`KEY_XOT` |`KEY_LOG` |`KEY_LN` |`KEY_SIN` |`KEY_COS` |`KEY_TAN` |
|`KEY_FRAC` |`KEY_FD` |`KEY_LEFTP` |`KEY_RIGHTP`|`KEY_COMMA` |`KEY_ARROW` |
|`KEY_7` |`KEY_8` |`KEY_9` |`KEY_DEL` |`KEY_ACON` | |
|`KEY_4` |`KEY_5` |`KEY_6` |`KEY_MUL` |`KEY_DIV` | |
|`KEY_1` |`KEY_2` |`KEY_3` |`KEY_ADD` |`KEY_SUB` | |
|`KEY_0` |`KEY_DOT` |`KEY_EXP` |`KEY_NEG` |`KEY_EXE` | |
### Événements clavier
```
key_event:
.time -> int
.mod -> bool
.shift -> bool
.alpha -> bool
.type -> KEYEV_NONE | KEYEV_DOWN | KEYEV_UP | KEYEV_HOLD
.key -> KEY_*
```
gint communique les informations sur ce qu'il se passe au clavier via des _événements_. Les événements indiquent quand une touche (champ `.key`) a été pressée, maintenue, ou relâchée (champ `.type` égal à `KEYEV_DOWN`, `KEYEV_HOLD` et `KEYEV_UP` respectivement), quand (champ `.time`) et si des modifieurs (SHIFT ou ALPHA, champs `.shift` et `.alpha`) étaient actifs à ce moment-là.
(Le champ `.mod` n'est pas très intéressant, et la valeur `KEYEV_NONE` de `.type` est discutée dans `pollevent()`.)
Les fonctions `getkey()`, `getekey_opt()`, `pollevent()` et `waitevent()` renvoient toutes des événements.
### Saisie d'une touche avec attente
```py
getkey() -> key_event
getkey_opt(opts: GETKEY_*, timeout_ms: int | None) -> key_event
```
La fonction `getkey()` met le programme en pause jusqu'à ce qu'une touche soit pressée ou répétée, et renvoie l'événement associé (qui est forcément de type `KEYEV_DOWN` ou `KEYEV_HOLD`). Par défaut, les seules touches qui sont répétées sont les touches directionnelles, une première fois après 400 ms, et ensuite toutes les 40 ms.
Pas mal de choses peuvent se produire pendant l'exécution de `getkey()`. L'utilisateur peut appuyer sur SHIFT ou ALPHA, ce qui affecte les champs `.shift` et `.alpha` de l'événement renvoyé. L'utilisateur peut également se rendre au menu principal avec MENU et éteindre la calculatrice avec SHIFT+AC/ON.
_Exemple._ Dans un menu de sélection de N éléments, on pourrait naviguer avec les touches haut et bas, sauter directement au début ou à la fin avec SHIFT haut et SHIFT bas, et valider avec EXE.
```py
ev = getkey()
if ev.key == KEY_EXE:
pass # Valider
elif ev.key == KEY_UP and ev.shift:
pos = 0 # Revenir au début
elif ev.key == KEY_DOWN and ev.shift:
pos = N-1 # Aller à la fin
elif ev.key == KEY_UP and pos > 0:
pos -= 1 # Monter d'une position
elif ev.key == KEY_DOWN and pos < N-1:
pos += 1 # Descendre d'une position
```
La fonction `getkey_opt()` est une généralisation de `getkey()` qui donne accès à plus d'options pour personnaliser le comportement de la fonction. Le paramètre `opts` accepte une combinaison d'options dans le tableau ci-dessous, et le paramètre `timeout_ms`, quand il n'est pas `None`, spécifie au bout de combien de temps la fonction doit s'arrêter et renvoyer un événement de type `KEYEV_NONE` s'il n'y a aucune activité sur le clavier. Le délai est exprimé en millisecondes mais la précision réelle est de l'ordre de 7-8 ms.
| Option | `getkey()` | Description |
|------------------------|------------|------------------------------------------------------------|
| `GETKEY_MOD_SHIFT` | Oui | La touche SHIFT est un modifieur |
| `GETKEY_MOD_ALPHA` | Oui | La touche ALPHA est un modifieur |
| `GETKEY_BACKLIGHT` | Oui | SHIFT+OPTN allume/éteint le rétroéclairage |
| `GETKEY_MENU` | Oui | MENU retourne au menu principal de la calculatrice |
| `GETKEY_REP_ARROWS` | Oui | Répète les touches directionnelles si maintenues |
| `GETKEY_REP_ALL` | Non | Répète toutes les touches si maintenues |
| `GETKEY_REP_PROFILE` | Oui* | Personnalisation des durées de répétition |
| `GETKEY_FEATURES` | Oui* | Personnalisation des raccourcis claviers globaux |
| `GETKEY_MENU_DUPDATE` | Oui | Rafraîchit l'écran après un retour au menu (Prizm/G90) |
| `GETKEY_MENU_EVENT` | Non | Envoie un événement `KEYEV_OSMENU` après un retour au menu |
| `GETKEY_POWEROFF` | Oui | SHIFT+AC/ON éteint la calculatrice |
`getkey()` est équivalent à `getkey_opt(opts, None)``opts` est la somme de toutes les options marquées "Oui" dans la colonne `getkey()` ci-dessus.
Les deux options `GETKEY_REP_PROFILE` et `GETKEY_FEATURES` ne sont utiles qu'au travers de fonctions de configuration qui ne sont pour l'instant pas disponibles dans PythonExtra.
### Lecture des événements en temps réel
```py
pollevent() -> key_event
waitevent() -> key_event
clearevents() -> None
```
gint enregistre l'activité du clavier en tâche de fond pendant que le programme s'exécute. Les événements sont mis dans une file d'attente jusqu'à ce que le programme les lise. C'est comme ça par exemple que `getkey()` détermine quoi renvoyer.
Il est possible d'accéder aux événements directement à l'aide de la fonction `pollevent()`. `pollevent()` renvoie l'événement le plus ancien qui n'a pas encore été lu par le programme. Si le programme a lu tous les événements et qu'il n'y a plus rien en attente, `pollevent()` renvoie un "faux" évenement de type `KEYEV_NONE` pour indiquer qu'il n'y a plus rien à lire.
Comme `pollevent()` retourne instanténement, on peut s'en servir pour lire l'activité du clavier sans mettre le programme en pause.
_Exemple._ Une boucle de jeu pourrait, à chaque frame, lire tous les événements en attente pour déterminer quand le joueur appuie sur la touche SHIFT ("action" dans cet exemple) pour déclencher une action.
```py
# Dessiner le jeu...
while True:
ev = pollevent()
if ev.type == KEYEV_NONE:
break # Fin de la lecture des événements
if ev.type == KEYEV_DOWN and ev.key == KEY_SHIFT:
pass # La touche SHIFT vient d'être pressée !
# Ignore implicitement les autres touches
# Simuler le jeu...
```
La fonction `waitevent()` est similaire, mais si tous les événements ont été lus elle attend qu'un événement se produise avant de retourner. Elle est plus rarement utilisée parce qu'en général quand on veut attendre on utilise `getkey()`.
La fonction `clearevents()` lit et ignore tous les événements, i.e. elle "jette" toutes les informations sur ce qu'il s'est passé au clavier. Elle est utile pour connaître l'état instantané du clavier avec `keydown()` (voir ci-dessous). `clearevents()` est équivalente à la définition suivante :
```py
def clearevents():
ev = pollevent()
while ev.type != KEYEV_NONE:
ev = pollevent()
```
### Lecture de l'état instantané du clavier
```py
keydown(key: int) -> bool
keydown_all(*keys: [int]) -> bool
keydown_any(*keys: [int]) -> bool
```
Une fois les événements lus, on peut tester individuellement si les touches sont pressées ou pas à l'aide de la fonction `keydown()`. `keydown(k)` renvoie `True` si la touche `k` est pressée, `False` sinon. Cette fonction ne marche **que si les événements ont été lus**, ce qu'on fait souvent soit avec `pollevent()` soit avec `clearevents()`.
_Exemple._ Une boucle de jeu pourrait tester si les touches gauche/droite sont pressées à chaque frame pour déplacer le joueur.
```py
while True:
ev = pollevent()
# ... comme dans l'exemple pollevent()
if keydown(KEY_LEFT):
player_x -= 1
if keydown(KEY_RIGHT):
player_x += 1
```
La fonction `keydown_all()` prent une série de touches en paramètre et renvoie `True` si elles sout toutes pressées. `keydown_any()` est similaire et renvoie `True` si au moins une des touches listées est pressée.
### Lecture rapide des changements de position des touches
```py
cleareventflips() -> None
keypressed(key: int) -> bool
keyreleased(key: int) -> bool
```
`keydown()` indique uniquement l'état instantané des touches. Elle ne permet pas de déterminer à quel moment une touche passe de l'état relâché à l'état pressé ou l'inverse. Pour ça, il faut soit utiliser les événements (ce qui est un peu lourd), soit se souvenir de quelles touches étaient pressées à "l'instant" précédent, soit utiliser les fonctions ci-dessous.
Les fonctions `keypressed(k)` et `keyreleased(k)` indiquent si la touche a été pressée/relâchée depuis le dernier appel à `cleareventflips()`. Attention la notion de "pressée/relâchée" ici n'est pas le temps réel mais la lecture des événements.
_Exemple._ Une boucle de jeu peut tester à la fois l'état immédiat et les changements d'état des touches en utilisant les fonctions instantanée après `cleareventflips()` suivi de `clearevents()`.
```py
# Dessiner le jeu...
cleareventflips()
clearevents()
if keypressed(KEY_SHIFT):
pass # Action !
if keydown(KEY_LEFT):
player_x -= 1
if keydown(KEY_RIGHT):
player_x += 1
# Simuler le jeu...
```
### Fonctions diverses concernant le clavier
```py
keycode_function(key: int) -> int
keycode_digit(key: int) -> int
```
`keycode_function(k)` renvoie le numéro de F-touche de `k` (i.e. 1 pour `KEY_F1`, 2 pour `KEY_F2`, etc.) et -1 pour les autres touches.
`keycode_digit(k)` renvoie le chiffre associé à `k` (i.e. 0 pour `KEY_0`, 1 pour `KEY_1`, etc.) et -1 pour les autres touches.
## Dessin à l'écran
Les en-têtes de référence sont [`<gint/display.h>`](https://gitea.planet-casio.com/Lephenixnoir/gint/src/branch/master/include/gint/display.h), et pour certains détails techniques [`<gint/display-fx.h>`](https://gitea.planet-casio.com/Lephenixnoir/gint/src/branch/master/include/gint/display-fx.h) et [`<gint/display-cg.h>`](https://gitea.planet-casio.com/Lephenixnoir/gint/src/branch/master/include/gint/display-cg.h).
### Manipulation de couleurs
```py
C_WHITE: int # Blanc
C_BLACK: int # Noir
C_LIGHT: int # Gris clair (sur mono: moteur de gris)
C_DARK: int # Gris foncé (sur mono: moteur de gris)
C_NONE: int # Transparent
C_INVERT: int # Inverseur de couleur
# Graph mono uniquement :
C_LIGHTEN: int # Éclaircisseur de couleur (moteur de gris)
C_DARKEN: int # Assombrisseur de couleur (moteur de gris)
# Graph 90+E uniquement :
C_RED: int # Rouge pur
C_GREEN: int # Vert pur
C_BLUE: int # Bleu pur
C_RGB(r: int, g: int, b: int) -> int
```
Les couleurs sont toutes des nombres entiers (manipuler des tuples `(r,g,b)` est atrocement lent par comparaison et requiert des allocations mémoire dans tous les sens). Une poignée de couleurs est fournie par défaut.
Sur Graph 90+E, la fonction `C_RGB()` peut être utilisée pour créer des couleurs à partir de trois composantes de valeur 0 à 31.
TODO: Expliquer le moteur de gris.
### Fonctions de dessin basiques
```py
DWIDTH: int
DHEIGHT: int
dupdate() -> None
dclear(color: int) -> None
dpixel(x: int, y: int, color: int) -> None
dgetpixel(x: int, y: int) -> int
```
Les entiers `DWIDTH` et `DHEIGHT` indiquent la taille de l'écran. C'est 128x64 sur les Graph mono (type Graph 35+E II), 396x224 sur la Prizm et Graph 90+E (le plein écran est disponible).
Toutes les fonctions de dessin opèrent sur une image interne appellée "VRAM" ; l'effet du dessin n'est donc pas visible immédiatement à l'écran. Pour que le dessin se voie il faut appeler la fonction `dupdate()` qui transfère les contenus de la VRAM à l'écran réel. Généralement, on fait ça après avoir affiché tout ce dont on a besoin et pas après chaque appel de fonction de dessin.
Dans PythonExtra, la fonction `dupdate()` indique aussi implicitement qu'on « passe en mode graphique ». À cause de certaines optimisations tout appel à `print()` est considéré comme un « passage en mode texte » et pendant qu'on est en mode texte le shell peut redessiner à tout moment. Si on veut dessiner après avoir utilisé le mode texte, il faut appeler `dupdate()` pour forcer un passage en mode graphique avant de commencer à dessiner. Sinon le dessin du shell pourrait interférer avec le dessin du programme.
La fonction `dclear()` remplit l'écran d'une couleur unie.
La fonction `dpixel()` modifie la couleur d'un pixel. Les coordonnées, comme pour toutes les fonctions de dessin, sont (x,y) où `x` varie entre 0 et DWIDTH-1 inclus (0 étant à gauche), et `y` varie entre 0 et DHEIGHT-1 inclus (0 étant en haut).
La fonction `dgetpixel()` renvoie la couleur d'un pixel. Attention, `dgetpixel()` lit dans la VRAM, pas sur l'écran.
_Exemple ([`ex_draw1.py`](../../ports/sh/examples/ex_draw1.py))._
```py
from gint import *
dclear(C_WHITE)
for y in range(10):
for x in range(10):
if (x^y) & 1:
dpixel(x, y, C_BLACK)
dupdate()
```
![](images/modgint-draw1-cg.png) ![](images/modgint-draw1-fx.png)
### Fonctions de dessin de formes géométriques
```py
drect(x1: int, y1: int, x2: int, y2: int, color: int) -> None
drect_border(x1: int, y1: int, x2: int, y2: int, fill_color: int,
border_width: int, border_color: int) -> None
dline(x1: int, y1: int, x2: int, y2: int, color: int) -> None
dhline(y: int, color: int) -> None
dvline(x: int, color: int) -> None
dcircle(x: int, y: int, radius: int, fill_color: int,
border_color: int) -> None
dellipse(x1: int, y1: int, x2: int, y2: int, fill_color: int,
border_color: int) -> None
```
`drect()` dessine un rectangle plein allant de (x1, y1) à (x2, y2) (tous les deux inclus). L'ordre des points ne compte pas, i.e. x1 ≥ x2 ou y1 ≥ y2 est autorisé.
`drect_border()` est similaire mais dessine une bordure de largeur `border_width` et de couleur `border_color`. La bordure est dessinée _à l'intérieur_ du rectangle.
`dline()` dessine une ligne droite entre les points (x1, y1) et (x2, y2). Les raccourcis `dhline()` et `dvline()` dessinent respectivement une ligne horizontale et verticale à travers tout l'écran.
`dcircle()` dessine un cercle défini par son centre et rayon avec l'algorithme de Bresenham. La couleur de l'intérieur et du bord peuvent être spécifiées séparément, y compris avec `C_NONE` (transparente). Par construction, `dcircle()` ne peut construire que des cercles de diamètre impair ; pour les diamètres plus fin, utilisez `dellipse()`.
`dellipse()` dessine une ellipse définie par son rectangle englobant. Les points (x1, y1) et (x2, y2) sont tous les deux inclus dans le rectangle. Pour dessiner une ellipse à partir de son centre (x, y) et de ses demi-grand/petit axes a/b, utilisez `dellipse(x-a, y-b, x+a, y+b, fill_color, border_color)`.
_Exemple ([`ex_draw2.py`](../../ports/sh/examples/ex_draw2.py))._
![](images/modgint-draw2-cg.png) ![](images/modgint-draw2-fx.png)
_Exemple ([`ex_circle.py`](../../ports/sh/examples/ex_circle.py))._
![](images/modgint-circle-cg.png) ![](images/modgint-circle-fx.png)
### Fonctions de dessin d'images
```py
dimage(x: int, y: int, img: image) -> None
dsubimage(x: int, y: int, img: image, left: int, top: int, width: int, height: int) -> None
```
**Sur Graph mono**
```py
image:
.profile -> IMAGE_MONO | ...
.width -> int
.height -> int
.data -> buffer-like
# Constructeur
image(profile: IMAGE_*, width: int, height: int, data: buffer-like) -> image
```
Les images sur Graph mono sont en noir-et-blanc ou 4 couleurs (quand le moteur de gris est utilisé). Chaque image a un champ `.profile` indiquant le format d'image (le nom "profile" est hérité d'anciennes versions de gint), deux champs `.width` et `.height` indiquant sa taille, et un champ `.data` donnant accès aux données brutes.
Les quatre formats disponibles sont les suivants.
| Format | Couleurs | Calques | Nom dans fxconv |
|--------------------|----------------------------------|---------|-----------------|
| `IMAGE_MONO` | Noir/blanc (2) | 1 | `mono` |
| `IMAGE_MONO_ALPHA` | Noir/blanc, transparent (3) | 2 | `mono_alpha` |
| `IMAGE_GRAY` | Niveaux de gris (4) | 2 | `gray` |
| `IMAGE_GRAY_ALPHA` | Niveaux de gris, transparent (5) | 3 | `gray_alpha` |
Le format des données brutes est un peu compliqué. L'image est stockée ligne par ligne de haut en bas ; chaque ligne est représentée de gauche à droite par une série de mots représentant chacun 32 pixels. Chaque mot contient un bit par pixel (sous la forme d'un entier de 4 octets) pour chaque calque.
Le plus simple pour obtenir une image est de générer le code associé avec l'outil fxconv du [fxSDK](https://gitea.planet-casio.com/Lephenixnoir/fxsdk). Les options `--bopti-image-fx --fx` spécifient qu'on convertit une image pour Graph mono et la métadonnée `profile:mono` choisit le format de l'image produite. Le tableau ci-dessus liste la valeur de `profile:` à spécifier pour chaque format. Par exemple pour l'image [`fx_image_7seg.py`](../../ports/sh/examples/fx_image_7seg.png) :
![](../../ports/sh/examples/fx_image_7seg.png)
```bash
% fxconv --bopti-image fx_image_7seg.png -o 7seg.py --fx profile:mono name:seg --py
```
```py
import gint
seg = gint.image(0, 79, 12, b'|\x00||\x00|||||\x00\x00\xba\x02::\x82\xb8\xb8:\xba\xba\x00\x00\xc6\x06\x06\x06\xc6\xc0\xc0\x06\xc6\xc6\x00\x00\xc6\x06\x06\x06\xc6\xc0\xc0\x06\xc6\xc6\x00\x00\x82\x02\x02\x02\x82\x80\x80\x02\x82\x82\x00\x00\x00\x00|||||\x00||\x00\x00\x82\x02\xb8:::\xba\x02\xba:\x00\x00\xc6\x06\xc0\x06\x06\x06\xc6\x06\xc6\x06\x00\x00\xc6\x06\xc0\x06\x06\x06\xc6\x06\xc6\x06\x00\x00\xc6\x06\xc0\x06\x06\x06\xc6\x06\xc6\x06\x00\x00\xba\x02\xb8:\x02:\xba\x02\xba:\x00\x00|\x00||\x00||\x00||\x00\x00')
```
L'option `--py-compact` permet de générer du code beaucoup plus compact (avec moins de `\x`), par contre le fichier obtenu ne peut pas être lu par un éditeur de texte. La seule option simple pour l'utiliser est de l'envoyer sur la calculatrice et l'importer tel quel (on peut aussi le manipuler avec un programme).
La fonction `dimage()` dessine une image à l'écran en positionnant le coin haut gauche de l'image à la position (x, y).
La fonction `dsubimage()` permet de dessiner un sous-rectangle d'une image. Le sous-rectangle commence à la position (left, top) de l'image et s'étend sur une largeur `width` et une hauteur `height`. Le sous-rectangle est dessiné de façon à ce que le pixel (left, top) arrive à la position (x, y) de l'écran.
_Exemple ([`fx_image.py`](../../ports/sh/examples/fx_image.py))._
![](images/modgint-image-fx.png)
**Sur Graph couleur**
```py
image:
.format -> IMAGE_RGB565 | ...
.flags -> int
.color_count -> int
.width -> int
.height -> int
.stride -> int
.data -> buffer-like
.palette -> buffer-like
# Constructeur
image(format: IMAGE_*, color_count: int, width: int, height: int, stride: int, data: buffer-like, palette: buffer-like) -> image
# Constructeurs spécialisés par format
image_rgb565(width: int, height: int, data: buffer-like) -> image
image_rgb565a(width: int, height: int, data: buffer-like) -> image
image_p8_rgb565(width: int, height: int, data: buffer-like, palette: buffer-like) -> image
image_p8_rgb565a(width: int, height: int, data: buffer-like, palette: buffer-like) -> image
image_p4_rgb565(width: int, height: int, data: buffer-like, palette: buffer-like) -> image
image_p4_rgb565a(width: int, height: int, data: buffer-like, palette: buffer-like) -> image
```
Les images sur Graph couleur sont déclinées en différents formats indiqués par le champ `.format`, dont les valeurs possibles sont listées ci-dessous. Les différences sont dans le nombre de couleurs, la présence ou pas de transparence, et la présence ou pas d'une palette de couleurs. Bien sûr, moins il y a de couleurs moins l'image prend de place en mémoire, donc de façon générale il est très bénéfique d'utiliser le plus petit format possible pour chaque image.
| Format | Couleurs | Palette | Nom dans fxconv |
|--------------------|---------------------|-------------|-----------------|
| `IMAGE_RGB565` | 65536 | Non | `rgb565` |
| `IMAGE_RGB565A` | 65535 + transparent | Non | `rgb565a` |
| `IMAGE_P8_RGB565` | 256 | Oui (1-256) | `p8_rgb565` |
| `IMAGE_P8_RGB565A` | 255 + transparent | Oui (1-256) | `p8_rgb565a` |
| `IMAGE_P4_RGB565` | 16 | Oui (16) | `p4_rgb565` |
| `IMAGE_P4_RGB565A` | 15 + transparent | Oui (16) | `p4_rgb565a` |
Le champ `.color_count` indique le nombre de couleurs dans la palette quand il y en a une. Pour les formats P8 ce nombre varie entre 1 et 256, pour les formats P4 il est toujours égal à 16. Les deux champs `.width` et `.height` indiquent la taille de l'image. Enfin, les champs `.data` et `.palette` permettent d'accéder aux données brutes des pixels ainsi que de la palette.
(Le champ `.flags` est un détail de gestion de mémoire qui ne devrait pas importer aux scripts Python. Le champ `.stride` indique combien d'octets séparent chaque ligne de pixels dans `.data` et sera de même rarement utilisé.)
Les fonctions `dimage()` et `dsubimage()` ont le même fonctionnement que pour les Graph mono ; voir ci-dessus.
Comme pour Graph mono les images peuvent être converties avec fxconv. Par exemple pour l'image [`cg_image_puzzle.png`](../../ports/sh/examples/cg_image_puzzle.png) (code abrégé) :
![](../../ports/sh/examples/cg_image_puzzle.png)
```bash
% fxconv --bopti-image cg_image_puzzle.png -o puzzle.py --cg profile:p4_rgb565 name:puzzle --py
```
```py
import gint
puzzle = gint.image(6, 16, 64, 32, 32,
b'\xbb\xbb\xbb\xbb\xbb ... \xdd\xdd\xdd\xdd\xdd\xdd\xdd',
b'\xff\xff\xcfW\x86\xd8\xbe|\xceP\xe5\x8a\x963f9u\x9c}\xa8\x9dxD\xfa\x83\xceLNZ\xcci\xa7')
```
L'option `--py-compact` est recommandée pour réduire la taille du code ; voir les détails dans la section Graph mono.
_Exemple ([`cg_image.py`](../../ports/sh/examples/cg_image.py))._
![](images/modgint-image-cg.png)
## Différences avec l'API C de gint
- `dsubimage()` n'a pas de paramètre `int flags`. Les flags en question ne ont que des optimisations mineures et pourraient disparaître dans une version future de gint.
- Les constructeurs d'image `image()` et `image_<format>()` n'existent pas dans l'API C.
- Les timeouts asynchrones à base d'entiers volatiles sont remplacés par des timeouts synchrones avec des durées optionnelles en millisecondes (entier ou `None`).
TODO : Il y en a d'autres.

View File

@ -1,18 +1,22 @@
include ../../py/mkenv.mk
SH_CFLAGS := -DFX9860G
TARGETCASIO := "FX9860G"
SH_LDFLAGS := -T fx9860g.ld -ljustui-fx -lm -lgint-fx -lc -lgint-fx -lgcc
SH_ASSETS := \
img_fkeys_main.png img_modifier_states.png \
font_5x7.png font_4x4.png font_4x6.png
font_5x7.png font_4x4.png font_4x6.png font_3x5.png
SH_METADATA := fxconv-metadata.txt
SH_CONVFLAGS := --fx
all: PythonEx.g1a
PythonEx.g1a: $(BUILD)/firmware.bin icon.png
fxgxa --g1a -n PythonExtra -i icon.png $< -o $@
ICON_RELEASE := icon.png
ICON_DEV := icon-dev.png
PythonEx.g1a: $(BUILD)/firmware.bin icon.png icon-dev.png
fxgxa --g1a -n PythonExtra -i $(ICON_$(BUILDTYPE)) $< -o $@
send: all
fxlink -sw PythonEx.g1a

BIN
ports/fx9860g3/font_3x5.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 16 KiB

View File

@ -10,14 +10,12 @@ font_*.png:
grid.border: 0
font_5x7.png:
type: font
charset: print
grid.size: 5x7
grid.padding: 1
grid.border: 0
font_4x4.png:
type: font
charset: print
grid.size: 4x4
grid.padding: 1
@ -25,9 +23,15 @@ font_4x4.png:
proportional: true
font_4x6.png:
type: font
charset: print
grid.size: 5x6
grid.padding: 1
grid.border: 0
proportional: true
font_3x5.png:
charset: ascii
height: 5
grid.size: 5x6
grid.padding: 1
proportional: true

BIN
ports/fx9860g3/icon-dev.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

View File

@ -1,2 +1 @@
/icon.xcf
/PythonExtra.g3a
/*.g3a

View File

@ -1,16 +1,20 @@
include ../../py/mkenv.mk
SH_CFLAGS := -DFXCG50
TARGETCASIO := "FXCG50"
SH_LDFLAGS := -T fxcg50.ld -ljustui-cg -lm -lgint-cg -lc -lgint-cg -lgcc
SH_ASSETS := img_modifier_states.png font_9.png font_13.png font_19.png
SH_ASSETS := img_modifier_states.png font_9.png font_13.png font_19.png PoliceNW
SH_METADATA := fxconv-metadata.txt
SH_CONVFLAGS := --cg
all: PythonExtra.g3a
PythonExtra.g3a: $(BUILD)/firmware.bin icon-uns.png icon-sel.png
fxgxa --g3a -n PythonExtra --icon-uns=icon-uns.png --icon-sel=icon-sel.png $< -o $@
ICONS_RELEASE := --icon-uns=icon-uns.png --icon-sel=icon-sel.png
ICONS_DEV := --icon-uns=icon-uns-dev.png --icon-sel=icon-sel-dev.png
PythonExtra.g3a: $(BUILD)/firmware.bin
fxgxa --g3a -n PythonExtra $(ICONS_$(BUILDTYPE)) $< -o $@
send: all
fxlink -sw PythonExtra.g3a

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 24 KiB

View File

@ -29,3 +29,12 @@ font_19.png:
grid.padding: 0
grid.border: 0
proportional: true
PoliceNW:
name: numworks
type: font
charset: unicode
grid.size: 10x16
grid.padding: 0
grid.border: 0
proportional: true

Binary file not shown.

After

Width:  |  Height:  |  Size: 15 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

BIN
ports/fxcg50/icon.xcf Normal file

Binary file not shown.

1
ports/sh/.gitignore vendored
View File

@ -1,2 +1,3 @@
/exclude/
/PythonExtra*.zip
/numworks/ion_backup.c

View File

@ -17,7 +17,10 @@ SRC_C = \
ports/sh/modcasioplot.c \
ports/sh/modgint.c \
ports/sh/mphalport.c \
ports/sh/objgintimage.c \
ports/sh/pyexec.c \
ports/sh/resources.c \
ports/sh/stredit.c \
ports/sh/widget_shell.c \
shared/runtime/gchelper_generic.c \
shared/runtime/stdout_helpers.c \
@ -27,8 +30,38 @@ SRC_QSTR += \
ports/sh/main.c \
ports/sh/modcasioplot.c \
ports/sh/modgint.c \
ports/sh/objgintimage.c \
ports/sh/pyexec.c \
ifeq ($(TARGETCASIO),"FXCG50")
$(info ************ FXCG50 VERSION ************ )
$(info ********* Add Numworks modules ********* )
SRC_C += \
ports/sh/numworks/modkandinsky.c \
ports/sh/numworks/modion.c \
SRC_QSTR += \
ports/sh/numworks/modkandinsky.c \
ports/sh/numworks/modion.c \
endif
ifeq ($(TARGETCASIO),"FX9860G")
$(info *********** FX9860G VERSION *********** )
endif
ifeq ($(shell [[ x"$$(git describe)" == x"$$(git describe main)" ]] \
&& [[ -z "$$(git status -uno --porcelain)" ]] \
&& echo y),y)
$(info * RELEASE BUILD)
BUILDTYPE := RELEASE
else
$(info * DEVELOPMENT BUILD)
BUILDTYPE := DEV
endif
ASSETS_O := $(SH_ASSETS:%=$(BUILD)/sh_assets/%.o)
OBJ = $(PY_O) $(ASSETS_O) $(addprefix $(BUILD)/, $(SRC_C:.c=.o))

View File

@ -6,6 +6,7 @@
#include <gint/keyboard.h>
#include <gint/display.h>
#include <gint/kmalloc.h>
#include <gint/defs/util.h>
#include <stdlib.h>
#include <string.h>
@ -16,7 +17,7 @@
#include "keymap.h"
/* Compute the shell's horizontal layout as:
1. Text width (region where the text is renderer)
1. Text width (region where the text is rendered)
2. Spacing left of the scrollbar
3. Width of the scrollbar */
static void view_params(int w,
@ -39,105 +40,25 @@ static void view_params(int w,
#endif
}
//=== Dynamic console lines ===//
//=== Static console lines ===//
bool console_line_init(console_line_t *line, int prealloc_size)
void console_fline_update_render_lines(console_fline_t *FL, int width)
{
char *data = malloc(prealloc_size + 1);
if(!data)
return false;
data[0] = 0;
line->data = data;
line->size = 0;
line->alloc_size = prealloc_size + 1;
line->render_lines = 0;
line->prefix = 0;
return true;
}
void console_line_deinit(console_line_t *line)
{
free(line->data);
memset(line, 0, sizeof *line);
}
bool console_line_alloc(console_line_t *line, int n)
{
if(line->alloc_size >= n + 1)
return true;
/* Always increase the size by at least 16 so we can insert many times in a
row without worrying about excessive allocations. */
int newsize = max(line->alloc_size + 16, n + 1);
char *newdata = realloc(line->data, newsize);
if(!newdata)
return false;
line->data = newdata;
line->alloc_size = newsize;
return true;
}
int console_line_capacity(console_line_t *line)
{
return PE_CONSOLE_LINE_MAX_LENGTH - line->size;
}
void console_line_set_prefix(console_line_t *line, int prefix_size)
{
line->prefix = min(max(0, prefix_size), line->size);
}
bool console_line_insert(console_line_t *line, int p, char const *str, int n)
{
if(p < 0 || p > line->size || n > console_line_capacity(line))
return false;
if(!console_line_alloc(line, line->size + n))
return false;
/* Move the end of the string (plus the NUL) n bytes forward */
memmove(line->data + p + n, line->data + p, line->size - p + 1);
memcpy(line->data + p, str, n);
line->size += n;
return true;
}
int console_line_delete(console_line_t *line, int p, int n)
{
if(p < line->prefix) {
int unremovable = line->prefix - p;
p += unremovable;
n -= unremovable;
}
n = min(n, line->size - p);
if(n < 0)
return 0;
/* Move the end of the string (plus the NUL) n bytes backwards */
memmove(line->data + p, line->data + p + n, line->size - n - p + 1);
line->size -= n;
return n;
}
void console_line_update_render_lines(console_line_t *line, int width)
{
char const *p = line->data;
line->render_lines = 0;
char const *p = FL->data;
FL->render_lines = 0;
do {
line->render_lines++;
FL->render_lines++;
p = drsize(p, NULL, width, NULL);
}
while(*p);
}
int console_line_render(int x, int y, console_line_t *line, int w, int dy,
int console_fline_render(int x, int y, console_fline_t *FL, int w, int dy,
int show_from, int show_until, int cursor)
{
char const *p = line->data;
char const *endline = p + strlen(p);
char const *p = FL->data;
char const *endline = p + FL->size;
int line_offset = 0;
int line_number = 0;
@ -175,9 +96,12 @@ bool linebuf_init(linebuf_t *buf, int capacity, int backlog_size)
if(capacity <= 0)
return false;
buf->lines = malloc(capacity * sizeof *buf->lines);
buf->lines = kmalloc(capacity * sizeof *buf->lines, PE_CONSOLE_LINE_ALLOC);
if(!buf->lines)
return false;
memset(buf->lines, 0, capacity * sizeof *buf->lines);
stredit_init(&buf->edit, 0, 0, 0);
buf->capacity = capacity;
buf->start = 0;
@ -192,7 +116,10 @@ bool linebuf_init(linebuf_t *buf, int capacity, int backlog_size)
void linebuf_deinit(linebuf_t *buf)
{
free(buf->lines);
stredit_reset(&buf->edit);
for(int i = 0; i < buf->capacity; i++)
free(buf->lines[i]);
kfree((void *)buf->lines);
memset(buf, 0, sizeof *buf);
}
@ -203,7 +130,7 @@ void linebuf_deinit(linebuf_t *buf)
lines independent of rotation, we use their absolute line number.
Such numbers refer to lines stored in the buffer if:
(index) 0 <= linebuf_index_to_nth(buf, index) < size
(index) 0 <= index < size
(nth) 0 <= nth < size
(abs) 0 <= abs - buf->absolute < size */
@ -213,13 +140,16 @@ GINLINE static int linebuf_nth_to_index(linebuf_t const *buf, int nth)
}
/* Get the nth line. */
GINLINE static console_line_t *linebuf_get_nth_line(linebuf_t const *buf,
GINLINE static console_fline_t *linebuf_get_nth_line(linebuf_t const *buf,
int nth)
{
if(nth < 0 || nth >= buf->size)
return NULL;
return &buf->lines[linebuf_nth_to_index(buf, nth)];
if(nth == buf->size - 1)
return (console_fline_t *)buf->edit.raw;
else
return buf->lines[linebuf_nth_to_index(buf, nth)];
}
/* Move `index` by `diff`; assumes |diff| <= buf->capacity. */
@ -238,31 +168,40 @@ int linebuf_end(linebuf_t const *buf)
return buf->absolute + buf->size;
}
console_line_t *linebuf_get_line(linebuf_t const *buf, int abs)
console_fline_t *linebuf_get_line(linebuf_t const *buf, int abs)
{
return linebuf_get_nth_line(buf, abs - buf->absolute);
}
console_line_t *linebuf_get_last_line(linebuf_t const *buf)
stredit_t *linebuf_get_last_line(linebuf_t *buf)
{
return linebuf_get_nth_line(buf, buf->size - 1);
return (buf->size > 0) ? &buf->edit : NULL;
}
console_line_t *linebuf_newline(linebuf_t *buf)
stredit_t *linebuf_newline(linebuf_t *buf)
{
/* Make space if the buffer is full */
linebuf_recycle_oldest_lines(buf, buf->size - buf->capacity + 1);
/* Freeze the current last line's size */
/* Freeze the current last line and reset the editor */
if(buf->size > 0) {
console_line_t *L = linebuf_get_nth_line(buf, buf->size - 1);
buf->total_size_except_last += L->size;
buf->total_size_except_last += buf->edit.size;
int size = buf->edit.size;
console_fline_t *frozen = (void *)stredit_freeze_and_reset(&buf->edit);
frozen->size = size;
int last_nth = linebuf_nth_to_index(buf, buf->size - 1);
assert(buf->lines[last_nth] == NULL);
buf->lines[last_nth] = frozen;
}
buf->size++;
console_line_t *L = linebuf_get_nth_line(buf, buf->size - 1);
console_line_init(L, 16);
return L;
int last_nth = linebuf_nth_to_index(buf, buf->size - 1);
buf->lines[last_nth] = NULL;
stredit_init(&buf->edit, 16, CONSOLE_FLINE_SIZE,
PE_CONSOLE_LINE_MAX_LENGTH);
return &buf->edit;
}
void linebuf_recycle_oldest_lines(linebuf_t *buf, int count)
@ -272,11 +211,11 @@ void linebuf_recycle_oldest_lines(linebuf_t *buf, int count)
return;
for(int nth = 0; nth < count; nth++) {
console_line_t *L = linebuf_get_nth_line(buf, nth);
buf->total_rendered -= L->render_lines;
console_fline_t *FL = linebuf_get_nth_line(buf, nth);
buf->total_rendered -= FL->render_lines;
if(nth != buf->size - 1)
buf->total_size_except_last -= L->size;
console_line_deinit(L);
buf->total_size_except_last -= FL->size;
free(FL);
}
buf->start = linebuf_index_add(buf, buf->start, count);
@ -311,10 +250,10 @@ void linebuf_update_render(linebuf_t *buf, int width, bool lazy)
view_params(width, &text_w, NULL, NULL);
for(int abs = start; abs < end; abs++) {
console_line_t *L = linebuf_get_nth_line(buf, abs - buf->absolute);
buf->total_rendered -= L->render_lines;
console_line_update_render_lines(L, text_w);
buf->total_rendered += L->render_lines;
console_fline_t *FL = linebuf_get_nth_line(buf, abs - buf->absolute);
buf->total_rendered -= FL->render_lines;
console_fline_update_render_lines(FL, text_w);
buf->total_rendered += FL->render_lines;
}
buf->absolute_rendered = max(buf->absolute_rendered, end - 2);
@ -377,7 +316,10 @@ void console_compute_view(console_t *cons, font_t const *font,
cons->render_font = font;
cons->render_width = width;
cons->render_lines = lines;
font_t const *old_font = dfont(font);
linebuf_update_render(&cons->lines, width, lazy);
dfont(old_font);
}
console_scrollpos_t console_clamp_scrollpos(console_t const *cons,
@ -390,7 +332,7 @@ console_scrollpos_t console_clamp_scrollpos(console_t const *cons,
return max(0, min(pos, cons->lines.total_rendered - cons->render_lines));
}
void console_render(int x, int y0, console_t const *cons, int dy,
void console_render(int x, int y0, console_t *cons, int dy,
console_scrollpos_t pos)
{
int total_lines = cons->lines.total_rendered;
@ -403,6 +345,12 @@ void console_render(int x, int y0, console_t const *cons, int dy,
font_t const *old_font = dfont(cons->render_font);
/* Normally only frozen lines have a valid size field. Update the size of
the last line so we don't have to make an exception for it. */
stredit_t *ed = linebuf_get_last_line(&cons->lines);
console_fline_t *ed_FL = (console_fline_t *)ed->raw;
ed_FL->size = ed->size;
/* Show only visible lines. We want to avoid counting all the lines in the
console, and instead start from the end. */
int line_y = visible_lines + pos;
@ -416,13 +364,13 @@ void console_render(int x, int y0, console_t const *cons, int dy,
/* If there isn't enough content to fill the view, start at the top. */
line_y = min(line_y, pos);
while(i < L_end && line_y < visible_lines) {
console_line_t *line = linebuf_get_line(&cons->lines, i);
while(i < L_end && line_y < visible_lines) {
console_fline_t *FL = linebuf_get_line(&cons->lines, i);
bool show_cursor = (i == L_end - 1);
y = console_line_render(x, y, line, text_w, dy, -line_y,
y = console_fline_render(x, y, FL, text_w, dy, -line_y,
visible_lines - line_y, show_cursor ? cons->cursor : -1);
line_y += line->render_lines;
line_y += FL->render_lines;
i++;
}
@ -447,16 +395,16 @@ void console_render(int x, int y0, console_t const *cons, int dy,
//=== Edition functions ===//
GINLINE static console_line_t *last_line(console_t *cons)
GINLINE static stredit_t *last_line(console_t *cons)
{
return linebuf_get_last_line(&cons->lines);
}
bool console_set_cursor(console_t *cons, int pos)
{
console_line_t *L = last_line(cons);
stredit_t *ed = last_line(cons);
if(pos < L->prefix || pos > L->size)
if(pos < ed->prefix || pos > ed->size)
return false;
cons->cursor = pos;
@ -470,8 +418,8 @@ bool console_move_cursor(console_t *cons, int cursor_movement)
char *console_get_line(console_t *cons, bool copy)
{
console_line_t *L = last_line(cons);
char *str = L->data + L->prefix;
stredit_t *ed = last_line(cons);
char *str = stredit_data(ed) + ed->prefix;
return copy ? strdup(str) : str;
}
@ -482,11 +430,11 @@ bool console_write_raw(console_t *cons, char const *str, int n)
/* Split string into chunks smaller than each line's storage capacity. */
while(n > 0) {
console_line_t *L = last_line(cons);
int capacity = console_line_capacity(L);
stredit_t *ed = last_line(cons);
int capacity = stredit_capacity(ed);
int round_size = min(n, capacity);
if(!console_line_insert(L, cons->cursor, str, round_size))
if(!stredit_insert(ed, cons->cursor, str, round_size))
return false;
cons->cursor += round_size;
cons->render_needed = true;
@ -524,27 +472,26 @@ bool console_write(console_t *cons, char const *buf, int n)
}
else if(buf[offset] == 8) {
offset++;
console_line_t *L = last_line(cons);
stredit_t *ed = last_line(cons);
if(cons->cursor > 0) {
console_line_delete(L, cons->cursor-1, 1);
stredit_delete(ed, cons->cursor-1, 1);
cons->cursor--;
}
}
else if(buf[offset] == '\e') {
console_line_t *L = last_line(cons);
stredit_t *ed = last_line(cons);
offset++;
/* TODO: Handle more complex escape sequences */
if(offset + 2 <= n && buf[offset] == '[' && buf[offset+1] == 'K') {
console_line_delete(L, cons->cursor,
L->size - cons->cursor);
stredit_delete(ed, cons->cursor, ed->size - cons->cursor);
offset += 2;
}
if(offset + 2 <= n && buf[offset] == 1 && buf[offset+1] == 'D') {
cons->cursor -= (cons->cursor > 0);
}
if(offset + 2 <= n && buf[offset] == 1 && buf[offset+1] == 'C') {
cons->cursor += (cons->cursor < L->size);
cons->cursor += (cons->cursor < ed->size);
}
}
@ -556,21 +503,21 @@ bool console_write(console_t *cons, char const *buf, int n)
void console_lock_prefix(console_t *cons)
{
console_line_set_prefix(last_line(cons), cons->cursor);
stredit_set_prefix(last_line(cons), cons->cursor);
}
void console_delete_at_cursor(console_t *cons, int n)
{
int real_n = console_line_delete(last_line(cons), cons->cursor - n, n);
int real_n = stredit_delete(last_line(cons), cons->cursor - n, n);
cons->cursor -= real_n;
cons->render_needed = true;
}
void console_clear_current_line(console_t *cons)
{
console_line_t *L = last_line(cons);
console_line_delete(L, L->prefix, L->size - L->prefix);
cons->cursor = L->prefix;
stredit_t *ed = last_line(cons);
stredit_delete(ed, ed->prefix, ed->size - ed->prefix);
cons->cursor = ed->prefix;
cons->render_needed = true;
}

View File

@ -24,59 +24,46 @@
#include <gint/keyboard.h>
#include <gint/display.h>
#include <gint/defs/attributes.h>
#include <stdbool.h>
#include "stredit.h"
/* Maximum line length, to ensure the console can threshold its memory usage
while cleaning only entire lines. Lines longer than this get split. */
#define PE_CONSOLE_LINE_MAX_LENGTH 1024
//=== Dynamic console lines ===//
/* Allocation arena for arrays of lines. */
// TODO: Split circular buffers
#ifdef FX9860G
#define PE_CONSOLE_LINE_ALLOC NULL
#else
#define PE_CONSOLE_LINE_ALLOC NULL
#endif
//=== Static console lines ===//
/* A line whose contents have been frozen. The only variable left is how many
render lines it takes, which is kept at the beginning of the buffer along
with its size. */
typedef struct
{
/* Line contents, NUL-terminated. The buffer might be larger. */
char *data;
/* Size of contents (not counting the NUL). */
int16_t size;
/* Allocated size (always ≥ size+1). */
int16_t alloc_size;
/* Number or render lines used (updated on-demand). */
int16_t render_lines;
/* Number of initial characters that can't be edited. */
int16_t prefix;
/* Number of non-NUL bytes in data[] (read-only) */
uint16_t size;
/* Number of render lines occupied by the line (read-write) */
uint8_t render_lines;
/* Raw NUL-terminated data (read-only) */
char data[];
} console_line_t;
} console_fline_t;
/* Create a new console line with at least init_chars characters of content
available. Returns false on error. Previous contents are not freed! */
bool console_line_init(console_line_t *line, int init_chars);
/* Clean up a line and free its contents. */
void console_line_deinit(console_line_t *line);
/* Realloc the line to ensure n characters plus a NUL can be written. */
bool console_line_alloc(console_line_t *line, int n);
/* Determine how many characters can be written before the line has to be
broken up. */
int console_line_capacity(console_line_t *line);
/* Set the prefix_size first characters of the line to not be editable. The
line must already have that many characters printed. */
void console_line_set_prefix(console_line_t *line, int prefix_size);
/* Insert n characters at position p. */
bool console_line_insert(console_line_t *line, int p, char const *str, int n);
/* Remove n characters at position p. Returns the number of characters
actually removed after bounds checking. */
int console_line_delete(console_line_t *line, int p, int n);
/* sizeof(console_fline_t) without alignment */
#define CONSOLE_FLINE_SIZE 3
/* Update the number of render lines for the chosen width. */
void console_line_update_render_lines(console_line_t *line, int width);
void console_fline_update_render_lines(console_fline_t *FL, int width);
/* Render a vertical slice of the wrapped line. */
int console_line_render(int x, int y, console_line_t *line, int w, int dy,
int console_fline_render(int x, int y, console_fline_t *FL, int w, int dy,
int show_from, int show_until, int cursor);
//=== Rotating line storage ===//
@ -85,15 +72,23 @@ int console_line_render(int x, int y, console_line_t *line, int w, int dy,
typedef struct
{
/* A rotating array of `capacity` lines starting at position `start` and
holding `size` lines. The array is pre-allocated. */
console_line_t *lines;
holding `size` lines. The array is pre-allocated. The last line, if
there is one, is stored as NULL and its address is edit->raw. */
console_fline_t **lines;
/* Editor for the last line. The pointer to the last line is stored there
instead of in the `lines` array because it gets reallocated regularly
during edition. */
stredit_t edit;
/* Invariants:
- capacity > 0
- 0 <= size <= capacity
- 0 <= start < capacity
- When size is 0, start is undefined. */
int capacity, start, size;
int16_t capacity, start, size;
/* Total number of rendered lines for the buffer. */
int16_t total_rendered;
/* To keep track of lines' identity, the rotating array includes an extra
numbering system. Each line is assigned an *absolute* line number which
@ -114,11 +109,9 @@ typedef struct
int total_size_except_last;
/* Last absolute line that has been both laid out for rendering and frozen
for edition (ie. followed by another line). Lazy rendering can always
start at `absolute_rendered+1`. */
for edition (ie. followed by another line). Lazy layout would start at
`absolute_rendered+1`. */
int absolute_rendered;
/* Total number of rendered lines for the buffer. */
int total_rendered;
} linebuf_t;
@ -137,13 +130,13 @@ int linebuf_start(linebuf_t const *buf);
int linebuf_end(linebuf_t const *buf);
/* Get a pointer to the line with the given absolute number. */
console_line_t *linebuf_get_line(linebuf_t const *buf, int absolute_number);
console_fline_t *linebuf_get_line(linebuf_t const *buf, int absolute_number);
/* Get a pointer to the last line in the buffer (usually the editable line). */
console_line_t *linebuf_get_last_line(linebuf_t const *buf);
/* Get a pointer to the last line in the buffer (the editable line). */
stredit_t *linebuf_get_last_line(linebuf_t *buf);
/* Add a new line to the buffer (recycling an old one if needed). */
console_line_t *linebuf_new_line(linebuf_t *buf);
stredit_t *linebuf_new_line(linebuf_t *buf);
/* Recycle the `n` oldest lines from the buffer. */
void linebuf_recycle_oldest_lines(linebuf_t *buf, int n);
@ -167,15 +160,15 @@ typedef struct
linebuf_t lines;
/* Cursor position within the last line. */
int cursor;
int16_t cursor;
/* Whether new data has been added and a frame should be rendered. */
bool render_needed;
/* View geometry parameters from last console_compute_view(). */
font_t const *render_font;
int render_width;
int render_lines;
int16_t render_width;
int16_t render_lines;
} console_t;
@ -213,7 +206,7 @@ console_scrollpos_t console_clamp_scrollpos(console_t const *cons,
/* Render the console at (x,y). The render `width`, the number of `lines` and
the text `font` are all as specified by the latest console_compute_view().
`dy` indicates line height. */
void console_render(int x, int y, console_t const *cons, int dy,
void console_render(int x, int y, console_t *cons, int dy,
console_scrollpos_t pos);
//=== Edition functions ===//

View File

@ -23,6 +23,105 @@ void pe_debug_panic(char const *msg)
exit(1);
}
struct pe_debug_meminfo pe_debug_startup_meminfo[PE_DEBUG_STARTUP_N];
void pe_debug_get_meminfo(struct pe_debug_meminfo *info)
{
kmalloc_gint_stats_t *s;
#ifdef FX9860G
s = kmalloc_get_gint_stats(kmalloc_get_arena("_uram"));
info->_uram_used = s->used_memory;
info->_uram_free = s->free_memory;
s = kmalloc_get_gint_stats(kmalloc_get_arena("pram0"));
info->pram0_used = s->used_memory;
info->pram0_free = s->free_memory;
#endif
#ifdef FXCG50
s = kmalloc_get_gint_stats(kmalloc_get_arena("_uram"));
info->_uram_used = s->used_memory;
info->_uram_free = s->free_memory;
s = kmalloc_get_gint_stats(kmalloc_get_arena("_ostk"));
info->_ostk_used = s->used_memory;
info->_ostk_free = s->free_memory;
#endif
}
void pe_debug_browse_meminfo(void)
{
dclear(C_WHITE);
struct pe_debug_meminfo infonow;
pe_debug_get_meminfo(&infonow);
#ifdef FX9860G
static char const * const names[] = {
"main", "cons.", "upy", "prom.", "ui",
"now", /* extra element compared to original enum */
};
dprint(1, 0, C_BLACK, "Memory info");
extern font_t font_3x5, font_4x6;
font_t const *old_font = dfont(&font_4x6);
dtext(33, 9, C_BLACK, "_uram");
dtext(75, 9, C_BLACK, "pram0");
dline(2, 16, DWIDTH-3, 16, C_BLACK);
for(int i = 0; i < PE_DEBUG_STARTUP_N + 1; i++) {
int y = 7 * i + 18;
struct pe_debug_meminfo *info = &pe_debug_startup_meminfo[i];
if(i >= PE_DEBUG_STARTUP_N) {
dline(2, y, DWIDTH-3, y, C_BLACK);
y += 2;
info = &infonow;
}
dtext(2, y, C_BLACK, names[i]);
dfont(&font_3x5);
dprint(33, y+1, C_BLACK,
"%d,%d", info->_uram_used, info->_uram_free);
dprint(75, y+1, C_BLACK,
"%d,%d", info->pram0_used, info->pram0_free);
dfont(&font_4x6);
}
dfont(old_font);
#endif
#ifdef FXCG50
static char const * const names[] = {
"main", "console", "upy", "prompt", "ui",
"now", /* extra element compared to original enum */
};
dtext(1, 1, C_BLACK, "Memory info");
dtext(83, 24, C_BLACK, "_uram");
dtext(170, 24, C_BLACK, "_ostk");
dline(10, 36, DWIDTH-11, 36, C_BLACK);
for(int i = 0; i < PE_DEBUG_STARTUP_N + 1; i++) {
int y = 15 * i + 39;
struct pe_debug_meminfo *info = &pe_debug_startup_meminfo[i];
if(i >= PE_DEBUG_STARTUP_N) {
dline(10, y, DWIDTH-11, y, C_BLACK);
y += 4;
info = &infonow;
}
dtext(10, y, C_BLACK, names[i]);
dprint(83, y+1, C_BLACK,
"%d,%d", info->_uram_used, info->_uram_free);
dprint(170, y+1, C_BLACK,
"%d,%d", info->_ostk_used, info->_ostk_free);
}
#endif
dupdate();
while((getkey().key) != KEY_EXIT);
}
#if PE_DEBUG
#if 0 // Timeout fxlink not supported yet
@ -49,6 +148,8 @@ static bool timeout_popup(void)
}
#endif
static bool videocapture = false;
void pe_debug_init(void)
{
usb_interface_t const *intf[] = { &usb_ff_bulk, NULL };
@ -82,17 +183,26 @@ static void print_strn(void *env, const char *str, size_t len) {
mp_print_t const mp_debug_print = { NULL, print_strn };
void pe_debug_kmalloc(void)
void pe_debug_kmalloc(char const *prefix)
{
kmalloc_gint_stats_t *s;
kmalloc_gint_stats_t *s1, *s2;
s1 = kmalloc_get_gint_stats(kmalloc_get_arena("_uram"));
s = kmalloc_get_gint_stats(kmalloc_get_arena("_uram"));
pe_debug_printf("[_uram] used=%d free=%d\n",
s->used_memory, s->free_memory);
#ifdef FX9860G
s2 = kmalloc_get_gint_stats(kmalloc_get_arena("pram0"));
pe_debug_printf("%s: _uram[used=%d free=%d] pram0[used=%d free=%d]\n",
prefix,
s1->used_memory, s1->free_memory,
s2->used_memory, s2->free_memory);
#endif
s = kmalloc_get_gint_stats(kmalloc_get_arena("_ostk"));
pe_debug_printf("[_ostk] used=%d free=%d\n",
s->used_memory, s->free_memory);
#ifdef FXCG50
s2 = kmalloc_get_gint_stats(kmalloc_get_arena("_ostk"));
pe_debug_printf("%s: _uram[used=%d free=%d] _ostk[used=%d free=%d]\n",
prefix,
s1->used_memory, s1->free_memory,
s2->used_memory, s2->free_memory);
#endif
}
void pe_debug_screenshot(void)
@ -101,4 +211,22 @@ void pe_debug_screenshot(void)
usb_fxlink_screenshot(true);
}
void pe_debug_toggle_videocapture(void)
{
videocapture = !videocapture;
}
void pe_debug_run_videocapture(void)
{
if(videocapture) {
usb_open_wait();
usb_fxlink_videocapture(true);
}
}
void pe_debug_close(void)
{
usb_close();
}
#endif /* PE_DEBUG */

View File

@ -23,22 +23,79 @@ void pe_debug_init(void);
void pe_debug_panic(char const *msg)
__attribute__((noreturn));
/*** Memory watch utilities (enabled even when PE_DEBUG=0) ***/
#ifdef FX9860G
struct pe_debug_meminfo {
uint16_t _uram_used, _uram_free;
uint32_t pram0_used, pram0_free;
};
#endif
#ifdef FXCG50
struct pe_debug_meminfo {
uint16_t _uram_used, _uram_free;
uint32_t _ostk_used, _ostk_free;
};
#endif
enum {
/* Just after entering main() */
PE_DEBUG_STARTUP_MAIN,
/* After allocating console */
PE_DEBUG_STARTUP_CONSOLE,
/* After initializing MicroPython */
PE_DEBUG_STARTUP_UPY,
/* After printing the first prompt */
PE_DEBUG_STARTUP_PROMPT,
/* After allocating and initializing the GUI */
PE_DEBUG_STARTUP_UI,
PE_DEBUG_STARTUP_N,
};
/* Fetch data about current memory statistics. */
void pe_debug_get_meminfo(struct pe_debug_meminfo *info);
/* Buffer for storing meminfo during startup. */
extern struct pe_debug_meminfo pe_debug_startup_meminfo[PE_DEBUG_STARTUP_N];
#define pe_debug_get_startup_meminfo(NAME) \
pe_debug_get_meminfo(&pe_debug_startup_meminfo[PE_DEBUG_STARTUP_ ## NAME])
/* Browse memory info through a GUI. */
void pe_debug_browse_meminfo(void);
/*** Debugging functions enabled only when PE_DEBUG=1 ***/
/* Print to the debug stream. This function is also called DEBUG_printf in
MicroPython code. */
int pe_debug_printf(char const *fmt, ...);
/* Print information about allocation status. */
void pe_debug_kmalloc(void);
void pe_debug_kmalloc(char const *prefix);
/* Take a screenshot. */
void pe_debug_screenshot(void);
/* Toggle video capture. */
void pe_debug_toggle_videocapture(void);
/* Send a video capture frame if video capture is enabled. */
void pe_debug_run_videocapture(void);
/* Close the debugging ressources */
void pe_debug_close(void);
#if !PE_DEBUG
#define PE_DEBUG_NOOP do {} while(0)
#define pe_debug_init(...) PE_DEBUG_NOOP
#define pe_debug_printf(...) PE_DEBUG_NOOP
#define pe_debug_kmalloc(...) PE_DEBUG_NOOP
#define pe_debug_screenshot(...) PE_DEBUG_NOOP
#define pe_debug_init(...) PE_DEBUG_NOOP
#define pe_debug_printf(...) PE_DEBUG_NOOP
#define pe_debug_kmalloc(...) PE_DEBUG_NOOP
#define pe_debug_screenshot(...) PE_DEBUG_NOOP
#define pe_debug_toggle_videocapture(...) PE_DEBUG_NOOP
#define pe_debug_run_videocapture(...) PE_DEBUG_NOOP
#define pe_debug_close(...) PE_DEBUG_NOOP
#endif
#endif /* __PYTHONEXTRA_DEBUG_H */

View File

@ -0,0 +1,73 @@
from gint import *
import math
palette = b'\x00\x00\xf8\x00\xff\xff'
data = b'\x80\x81\x80\x81\x82\x81\x80\x81\x80'
img = image(IMAGE_P8_RGB565, color_count=3, width=3, height=3,
stride=3, data=data, palette=palette)
print(img)
img_alpha = image_p8_rgb565a(3, 3, data, palette)
print(img_alpha)
# h ∈ [0,360), s ∈ [0,1], l ∈ [0,1]
def hsl2rgb(H, S, L):
C = (1 - abs(2*L - 1)) * S
Hp = H / 60
X = C * (1 - abs(Hp % 2 - 1))
R, G, B = [(C,X,0), (X,C,0), (0,C,X), (0,X,C), (X,0,C), (C,0,X)][int(Hp)]
m = L - C / 2
return (R + m, G + m, B + m)
gradient_data = bytearray(32*32*2)
for y in range(32):
for x in range(32):
i = (32 * y + x) * 2
lx, ly = x-15.5, y-15.5
radius = (lx*lx + ly*ly) / (16**2)
if radius > 1:
gradient_data[i] = 0x00
gradient_data[i+1] = 0x01
continue
h = math.atan2(-ly, lx) * 180 / math.pi
h = h if h >= 0 else h + 360
s = radius
l = 0.5
r, g, b = hsl2rgb(h, s, l)
color = C_RGB(int(r*32), int(g*32), int(b*32))
gradient_data[i] = color >> 8
gradient_data[i+1] = color & 0xff
gradient = image_rgb565a(32, 32, gradient_data)
print(gradient)
from cg_image_puzzle import *
print(puzzle)
# Take graphics control
dupdate()
h = DWIDTH // 2
dclear(C_WHITE)
drect(h, 0, DWIDTH-1, DHEIGHT-1, C_RGB(28,28,28))
dimage(10, 10, img)
dimage(15, 10, img)
dimage(20, 10, img)
dimage(h+10, 10, img_alpha)
dimage(h+15, 10, img_alpha)
dimage(h+20, 10, img_alpha)
dimage(h-16, 20, gradient)
dimage(40, 100, puzzle)
x, y = 110, 100
hw = puzzle.width // 2
hh = puzzle.height // 2
dsubimage(x-1, y-1, puzzle, 0, 0, hw, hh)
dsubimage(x+hw+1, y-1, puzzle, hw, 0, hw, hh)
dsubimage(x-1, y+hh+1, puzzle, 0, hh, hw, hh)
dsubimage(x+hw+1, y+hh+1, puzzle, hw, hh, hw, hh)
dupdate()
getkey()

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.7 KiB

Binary file not shown.

View File

@ -0,0 +1,8 @@
from gint import *
dclear(C_WHITE)
for y in range(10):
for x in range(10):
if (x^y) & 1:
dpixel(x, y, C_BLACK)
dupdate()
getkey()

View File

@ -0,0 +1,46 @@
from gint import *
import math
if DWIDTH==128:
C_RGB = lambda r, g, b: 0
_ = lambda x, y: x
else:
_ = lambda x, y: y
dclear(C_WHITE)
c1 = _(C_BLACK, C_RGB(24, 24, 24))
c2 = _(C_BLACK, C_BLACK)
c3 = _(C_NONE, C_RGB(24, 24, 24))
x1 = _(5,20)
x2 = _(40,90)
x3 = _(120,360)
y1 = _(2,20)
y2 = _(19,60)
y3 = _(30,135)
y4 = _(45,170)
y5 = _(50,190)
xp = _(90,200)
yp = _(25,100)
rp = _(20,50)
drect(x1, y1, x2, y2, c1)
drect_border(x1, y3, x2, y4, c3, 2, c2)
dvline(x3, c1)
dvline(x3+2, c1)
dvline(x3+4, c1)
dhline(y5, c2)
dhline(y5+2, c2)
dhline(y5+4, c2)
p = [2*math.pi*i/7 for i in range(7)]
p = [(int(xp+rp*math.cos(a)), int(yp+rp*math.sin(a))) for a in p]
for i in range(7):
dline(*p[i], *p[(i+2)%7], C_BLACK)
dupdate()
getkey()

View File

@ -0,0 +1,10 @@
import gint
while True:
ev = gint.getkey_opt(gint.GETKEY_DEFAULT, 5000)
if ev.type == gint.KEYEV_NONE:
print("no press after 5000 ms")
else:
print(ev)
if ev.key == gint.KEY_EXIT and not ev.shift and not ev.alpha:
break

View File

@ -0,0 +1,16 @@
from gint import *
dclear(C_WHITE)
listvert = [0,0,127,0,127,63,0,63]
fill = C_RED
border = C_BLUE
dpoly(listvert, fill, border)
listvert2 = [100, 50, 200, 50, 225, 75, 200, 100, 100, 100, 75, 75]
listvert3 = [100, 100, 200, 100, 225, 125, 200, 150, 100, 150, 75, 125]
dpoly(listvert2, C_NONE, C_BLACK)
dpoly(listvert3, C_GREEN, C_NONE)
dupdate()
getkey()

View File

@ -0,0 +1,44 @@
from gint import *
img = image(IMAGE_MONO, width=3, height=3, data=b'\xa0\x00\x00\x00\x50\x00\x00\x00\xa0\x00\x00\x00')
print(img)
print(img.format, img.width, img.height)
print(img.data)
segments = image(IMAGE_MONO, 79, 12, bytearray(b'|\x00||\x00|||||\x00\x00\xba\x02::\x82\xb8\xb8:\xba\xba\x00\x00\xc6\x06\x06\x06\xc6\xc0\xc0\x06\xc6\xc6\x00\x00\xc6\x06\x06\x06\xc6\xc0\xc0\x06\xc6\xc6\x00\x00\x82\x02\x02\x02\x82\x80\x80\x02\x82\x82\x00\x00\x00\x00|||||\x00||\x00\x00\x82\x02\xb8:::\xba\x02\xba:\x00\x00\xc6\x06\xc0\x06\x06\x06\xc6\x06\xc6\x06\x00\x00\xc6\x06\xc0\x06\x06\x06\xc6\x06\xc6\x06\x00\x00\xc6\x06\xc0\x06\x06\x06\xc6\x06\xc6\x06\x00\x00\xba\x02\xb8:\x02:\xba\x02\xba:\x00\x00|\x00||\x00||\x00||\x00\x00'))
print(segments)
# ..xx x.. | ..xx x..
# .x.. .x. | .xxx xx.
# x..x ..x | xxxx xxx
# x.xx x.x | xxxx xxx
# x..x ..x | xxxx xxx
# .x.. .x. | .xxx xx.
# ..xx x.. | ..xx x..
alpha = image(IMAGE_MONO_ALPHA, 7, 7, b'\x38\x00\x00\x00\x38\x00\x00\x00\x7c\x00\x00\x00\x44\x00\x00\x00\xfe\x00\x00\x00\x92\x00\x00\x00\xfe\x00\x00\x00\xba\x00\x00\x00\xfe\x00\x00\x00\x92\x00\x00\x00\x7c\x00\x00\x00\x44\x00\x00\x00\x38\x00\x00\x00\x38\x00\x00\x00')
# Take graphics control again after the print()
dupdate()
dclear(C_WHITE)
drect(63, 0, 127, 63, C_BLACK)
dimage(10, 5, img)
dimage(10, 15, segments)
def digit(x, y, num):
dsubimage(x, y, segments, 8*num, 0, 7, segments.height)
digit(15+0*8, 45, 4)
digit(15+1*8, 45, 2)
digit(15+2*8, 45, 7)
digit(15+3*8, 45, 3)
d = segments.data
for i in range(len(d) / 2):
d[i] = ~d[i]
dimage(10, 30, segments)
dimage(60, 5, alpha)
dupdate()
getkey()

Binary file not shown.

After

Width:  |  Height:  |  Size: 660 B

View File

@ -0,0 +1,223 @@
# PacMan par Kevin FEDYNA
# https://nsi.xyz/numapps/pac-man-en-python-numworks/
from kandinsky import fill_rect, draw_string
from ion import keydown
from math import sqrt
from random import randint
from time import monotonic
try:
from kandinsky import get_keys
color = (192, 53, 53)
except:
color = (255, 183, 52)
terrain = (262143,131841,187245,187245,131073,186285,135969,252783,249903,251823,1152,251823,249903,251823,131841,187245,147465,219051,135969,195453,131073,262143)
bits = 18
width = 320
height = 222
colors = ((0, 0, 0), (32, 48, 248), (248, 224, 8), tuple(color))
ghost_color = ((255, 184, 255), (255, 0,0), (255, 184, 82), (0, 255, 255))
pacgommes = [0,130302,9360,74898,131070,75858,126174,8208,8208,8208,8208,8208,8208,8208,130302,74898,49140,43092,126174,66690,131070,0]
superpacgommes = [0,0,65538,0,0,0,0,0,0,0,0,0,0,0,0,0,65538,0,0,0,0,0,0]
frightened = 0
lives = 2
won = 0
lvl = 0
score = 0
chained = 0
class Entity:
def __init__(self, x, y, clr, d=0):
self.x = x
self.y = y
self.d = d
self.nd = d
self.f = 0
self.out = 0
self.color = clr
fill_rect(int(self.x*10)+136,int(self.y*10)-3,8,8,self.color)
def espace(self,dx=-1,dy=-1):
if dx == dy:
dx, dy = ((-0.1,0),(0,-0.1),(0,0.1),(0.1,0))[self.nd]
return not terrain[int(self.y + 5.5*dy)]>>(bits-1-int(self.x + 5.5*dx)) & 1 and ((dx != 0 and self.y%1 == 0.5) or (dy != 0 and self.x%1== 0.5))
def move(self):
global frightened, ghosts, score, chained, lives, total, won
dx, dy = ((-0.1,0),(0,-0.1),(0,0.1),(0.1,0))[self.d]
if self.espace(dx,dy):
fill_rect(int(self.x*10)+136,int(self.y*10)-3,8,8,colors[0])
self.x = (round(self.x + dx, 1) - 0.5) % 16.5 + 0.5
self.y = round(self.y + dy, 1)
fill_rect(int(self.x*10)+136,int(self.y*10)-3,8,8,self.color)
if self.color == colors[2]:
if pacgommes[int(self.y)] >> (bits - 1 - int(self.x)) & 1:
pacgommes[int(self.y)] -= 1 << (bits - 1 - int(self.x))
score += 10
if superpacgommes[int(self.y)] >> (bits - 1 - int(self.x)) & 1:
superpacgommes[int(self.y)] -= 1 << (bits - 1 - int(self.x))
score += 50
chained = 0
frightened = monotonic()
for g in ghosts:
if g.out:
g.color = colors[1]
g.d = (3, 2, 1, 0)[g.d]
g.f = 1
for g in range(4):
if sqrt((self.x-ghosts[g].x)**2+(self.y-ghosts[g].y)**2) < 0.6:
if ghosts[g].f:
chained += 1
total += 1
score += (1 << chained)*100
ghosts[g].f = 0
ghosts[g].color = ghost_color[g]
ghosts[g].x = 9
ghosts[g].y = 8.5
if total == 16:
score += 12000
else:
for gp in range(4):
ghosts[gp].f = 0
ghosts[gp].color = ghost_color[gp]
ghosts[gp].x = 9
ghosts[gp].y = 10.5
ghosts[gp].out = 0
self.x = 9
self.y = 16.5
self.d, self.nd = 0, 0
lives -= 1
return render()
if not won and score > 10000:
lives += 1
won = 1
px, py = int(self.x - 5.5*dx), int(self.y - 5.5*dy)
if pacgommes[py]>>(bits-1-px) & 1:
fill_rect(px*10+144,py*10+5,2,2,(250, 207, 173))
if superpacgommes[py]>>(bits-1-px) & 1:
fill_rect(px*10+143,py*10+4,4,4,(250, 207, 173))
def ia(self,x,y):
if self.f:
while True:
d = randint(0,3)
dx, dy = ((-0.1,0),(0,-0.1),(0,0.1),(0.1,0))[d]
if d != (3,2,1,0)[self.d] and self.espace(dx,dy):
self.d = d
break
else:
distances = [9999 for _ in range(4)]
for i in range(4):
if i != (3,2,1,0)[self.d]:
dx, dy = ((-0.1,0),(0,-0.1),(0,0.1),(0.1,0))[i]
if self.espace(dx,dy):
distances[i] = sqrt((self.y + dy - y)**2 + (self.x + dx - x)**2)
self.d = distances.index(min(distances))
def prebuild():
fill_rect(0,0,width,height,colors[0])
fill_rect(138, 0, 2, height, colors[3])
draw_string("PAC-MAN", 35, 10, colors[3], colors[0])
draw_string("nsi.xyz/pacman", 0, 204,colors[0], colors[3])
draw_string("Score :", 35, 40, (255,)*3, colors[0])
draw_string("Niveau :", 30, 90, (255,)*3, colors[0])
def render():
global terrain, pacgommes, superpacgommes, lives, arrivee
if lives == -1:
return 42
draw_string(str(lvl),70-5*len(str(lvl)),110,(255,)*3,colors[0])
fill_rect(0,150,138,20,colors[0])
for i in range(lives):
fill_rect(60-(lives-1)*20+i*40,150,20,20,colors[2])
for l in range(len(terrain)):
for c in range(bits):
fill_rect(c*10+140,l*10+1,10,10,colors[0])
if pacgommes[l]>>(bits-1-c) & 1:
fill_rect(c*10+144,l*10+5,2,2,(250, 207, 173))
if superpacgommes[l]>>(bits-1-c) & 1:
fill_rect(c*10+143,l*10+4,4,4,(250, 207, 173))
if terrain[l]>>(bits-1-c) & 1:
for d in ((1,0),(0,1),(-1,0),(0,-1)):
if 0 <= l + d[0] <= len(terrain) - 1 and 0 <= c + d[1] <= bits - 1 and not terrain[l + d[0]]>>(bits-1-(c+d[1])) & 1:
fill_rect(c*10+140+9*(d[1]==1),l*10+1+9*(d[0]==1),1+9*(d[1]==0),1+9*(d[0]==0),colors[1])
arrivee = monotonic()
def engine():
global frightened, ghosts, pacgommes, superpacgommes, lvl, arrivee, total
while True:
pacgommes = [0,130302,9360,74898,131070,75858,126174,8208,8208,8208,8208,8208,8208,8208,130302,74898,49140,43092,126174,66690,131070,0]
superpacgommes = [0,0,65538,0,0,0,0,0,0,0,0,0,0,0,0,0,65538,0,0,0,0,0,0]
lvl += 1
total = 0
render()
pacman = Entity(9, 16.5, colors[2])
ghosts = [Entity(9, 10.5, ghost_color[i]) for i in range(4)]
while sum(pacgommes) + sum(superpacgommes):
depart = monotonic()
for i in range(4):
if keydown(i):
if i == (3,2,1,0)[pacman.d]:
pacman.d = i
pacman.nd = i
while monotonic() - depart < 0.01:
if pacman.espace():
pacman.d = pacman.nd
if pacman.move() == 42:
draw_string("GAME OVER",185,100,colors[3],colors[0])
return 69
draw_string(str(score),70-5*len(str(score)),60,(255,)*3,colors[0])
""" Fantomes """
if frightened:
if monotonic() - frightened > 6.5:
for g in ghosts:
if g.f:
g.color = (255,)*3
if monotonic() - frightened > 8.5:
frightened = 0
for g in range(4):
ghosts[g].color = ghost_color[g]
ghosts[g].f = 0
if arrivee:
if monotonic() - arrivee > 0 and not ghosts[1].out:
ghosts[1].out = 1
ghosts[1].y = 8.5
if monotonic() - arrivee > 2.5 and not ghosts[0].out:
ghosts[0].out = 1
ghosts[0].y = 8.5
if monotonic() - arrivee > 5 and not ghosts[3].out:
ghosts[3].out = 1
ghosts[3].y = 8.5
if monotonic() - arrivee > 7.5 and not ghosts[2].out:
ghosts[2].out = 1
ghosts[2].y = 8.5
fill_rect(220,101,20,10,colors[0])
arrivee = 0
pdx, pdy = ((-0.1,0),(0,-0.1),(0,0.1),(0.1,0))[pacman.d]
# Pinky
ghosts[0].ia(pacman.x + 20 * pdx, pacman.y + 20 * pdy)
ghosts[0].move()
# Inky
ghosts[3].ia(max(min(ghosts[1].x + 2*(pacman.x + 20 * pdx - ghosts[1].x), 16.5), 1.5), max(min(ghosts[1].y +2*(pacman.y + 20 * pdy - ghosts[1].y), 21.5), 1.5))
ghosts[3].move()
# Blinky
ghosts[1].ia(pacman.x, pacman.y)
ghosts[1].move()
# Clyde
if sqrt((ghosts[2].x - pacman.x)**2 + (ghosts[2].y - pacman.y)**2) > 4:
ghosts[2].ia(pacman.x, pacman.y)
else:
ghosts[2].ia(1.5, 20.5)
ghosts[2].move()
prebuild()
engine()

View File

@ -0,0 +1,303 @@
# Snake from Golem64
# https://my.numworks.com/python/golem64/snake
#Version 1.7 STABLE
#Tip: You should try to press
#some keys in the menu...
from random import *
from kandinsky import *
from ion import *
from time import *
#from pomme import *
def oscolor():
try:
get_keys()
except:
return 'orange'
else:
return 'red'
def lastPos(i,x,y):
if i[-1]==3:
pos=[x-10,y]
elif i[-1]==2:
pos=[x,y-10]
elif i[-1]==0:
pos=[x+10,y]
elif i[-1]==1:
pos=[x,y+10]
pos[0],pos[1]=checkTeleport(pos[0],pos[1])
return pos
def newApple(appleC,bgC):
applex=randint(0,31)*10+4
appley=randint(0,21)*10+5
while get_pixel(applex,appley)!=bgC:
applex=randint(0,31)*10+4
appley=randint(0,21)*10+5
fill_rect(applex-4,appley-4,10,10,appleC)
return applex,appley
def checkTeleport(x,y):
if x<4:
x=314
if x>314:
x=4
if y<5:
y=215
if y>215:
y=5
return x,y
def getMove(u):
for k in range(4):
if keydown(k)==True and u+k!=3: return k
return u
def clearDraw(): fill_rect(0,0,320,222,(255,255,255))
def clearHome(): print("\n \n \n \n \n \n \n \n \n \n \n \n \n ")
def redraw():
draw_string("(DELETE to exit)",0,0)
printLetter([1,1,1,1,0,0,1,1,1,0,0,1,1,1,1],70,80,10,(0,204,0))
fill_rect(95,80,2,4,(0,0,0))
fill_rect(95,86,2,4,(0,0,0))
fill_rect(100,84,4,2,(255,0,0))
fill_rect(104,82,2,2,(255,0,0))
fill_rect(104,86,2,2,(255,0,0))
printLetter([1,1,1,1,0,1,1,0,1,1,0,1,1,0,1],110,80,10,(0,0,0))
printLetter([1,1,1,1,0,1,1,1,1,1,0,1,1,0,1],150,80,10,(0,0,0))
printLetter([1,0,1,1,0,1,1,1,0,1,0,1,1,0,1],190,80,10,(0,0,0))
printLetter([1,1,1,1,0,0,1,1,1,1,0,0,1,1,1],230,80,10,(0,0,0))
def printLetter(letter,x,y,size,color):
for yi in range(5):
for xi in range(3):
if letter[yi*3+xi]==1:
fill_rect(x+(xi*size),y+(yi*size),size,size,color)
def menu():
clearDraw()
printLetter([1,1,1,1,0,1,1,0,1,1,0,1,1,0,1],110,80,10,(0,0,0))
printLetter([1,1,1,1,0,1,1,1,1,1,0,1,1,0,1],150,80,10,(0,0,0))
printLetter([1,0,1,1,0,1,1,1,0,1,0,1,1,0,1],190,80,10,(0,0,0))
printLetter([1,1,1,1,0,0,1,1,1,1,0,0,1,1,1],230,80,10,(0,0,0))
anim=[1,1,1,1,1,1,1,1,1,4,4,3,3,4,4,1,1]
ax=0
ay=120
aendx=-110
aendy=120
u=1
aback=0
for i in range(len(anim)):
ax=ax+((anim[i]==1)-(anim[i]==3))*10
ay=ay+((anim[i]==2)-(anim[i]==4))*10
if aendx<0:
aendx=aendx+10
else:
aendx=aendx+((anim[i-11]==1)-(anim[i-11]==3))*10
aendy=aendy+((anim[i-11]==2)-(anim[i-11]==4))*10
fill_rect(aendx,aendy,10,10,(255,255,255))
fill_rect(ax,ay,10,10,(0,204,0))
# aback=lastPos(anim,ax,ay)
# if u==26 or u==24:
# fill_rect(ax-1,ay-1,3,1,(0,0,0))
# fill_rect(ax-1,ay+1,3,1,(0,0,0))
# fill_rect(aback[0],aback[1],10,10,(0,204,0))
# elif u==34 or u==25:
# fill_rect(ax-1,ay-1,1,3,(0,0,0))
# fill_rect(ax+1,ay-1,1,3,(0,0,0))
# fill_rect(aback[0]-2,aback[1]-2,5,5,(0,204,0))
sleep(0.05)
fill_rect(ax+5,ay,2,4,(0,0,0))
fill_rect(ax+5,ay+6,2,4,(0,0,0))
fill_rect(ax+10,ay+4,4,2,(255,0,0))
fill_rect(ax+14,ay+2,2,2,(255,0,0))
fill_rect(ax+14,ay+6,2,2,(255,0,0))
draw_string("(DELETE to exit)",0,0)
draw_string("> Play <",125,140,oscolor())
draw_string(" Options ",110,165)
darkMode=0
Speed=0.05
power=5
score=1
exit=0
sel=1
while keydown(KEY_OK)!=True and exit==0:
if keydown(KEY_DOWN) and sel==1:
draw_string(" Play ",125,140)
draw_string("> Options <",110,165,oscolor())
sel=2
elif keydown(KEY_UP) and sel==2:
draw_string("> Play <",125,140,oscolor())
draw_string(" Options ",110,165)
sel=1
if keydown(KEY_LEFTPARENTHESIS) and keydown(KEY_RIGHTPARENTHESIS):
draw_string("Dark mode enabled !",80,195)
darkMode=1
if keydown(KEY_BACKSPACE):
exit=1
sleep(0.1)
if sel==2 and exit!=1:
fill_rect(0,130,300,60,(255,255,255))
Speed=0.05
power=5
score=1
draw_string("Speed:"+str(Speed),50,140,oscolor(),'white')
draw_string("Power:+"+str(power),200,140)
draw_string("Score:+"+str(score),50,170)
draw_string("Play",220,170)
sel=1
sleep(0.2)
while keydown(KEY_OK)!=True or sel!=4:
if keydown(KEY_RIGHT):
sel=sel+1
elif keydown(KEY_DOWN):
sel=sel+2
elif keydown(KEY_LEFT):
sel=sel-1
elif keydown(KEY_UP):
sel=sel-2
if sel<0:
sel=0
if sel>4:
sel=4
if sel==1:
draw_string("Speed:"+str(Speed),50,140,oscolor(),'white')
draw_string("Power:+"+str(power),200,140)
draw_string("Score:+"+str(score),50,170)
draw_string("Play",220,170)
if keydown(KEY_OK):
clearHome()
Speed=input("Speed:")
redraw()
elif sel==2:
draw_string("Speed:"+str(Speed),50,140)
draw_string("Power:+"+str(power),200,140,oscolor(),'white')
draw_string("Score:+"+str(score),50,170)
draw_string("Play",220,170)
if keydown(KEY_OK):
clearHome()
power=int(input("Power:+"))
redraw()
elif sel==3:
draw_string("Speed:"+str(Speed),50,140)
draw_string("Power:+"+str(power),200,140)
draw_string("Score:+"+str(score),50,170,oscolor(),'white')
draw_string("Play",220,170)
if keydown(KEY_OK):
clearHome()
score=int(input("Score:"))
redraw()
elif sel==4:
draw_string("Speed:"+str(Speed),50,140)
draw_string("Power:+"+str(power),200,140)
draw_string("Score:+"+str(score),50,170)
draw_string("Play",220,170,oscolor(),'white')
if (keydown(KEY_LEFTPARENTHESIS) and keydown(KEY_RIGHTPARENTHESIS)) or darkMode==1:
draw_string("Dark mode enabled !",80,195)
darkMode=1
if keydown(KEY_BACKSPACE):
exit=1
break
sleep(0.1)
if exit!=1:
if darkMode==1:
launch(1,Speed,power,score)
elif darkMode==0:
launch(0,Speed,power,score)
elif exit==1:
clearDraw()
return
def launch(darkmode=0,speed=0.05,applePower=5,appleScore=1):
bgC=(248,252,248)
borderC=(0,0,0)
snakeC=(0,204,0)
appleC=(248,0,0)
if darkmode==1:
bgC=(0,0,0)
borderC=(0,0,204)
fill_rect(0,0,320,222,bgC)
# fill_rect(315,0,5,222,borderC)
# fill_rect(0,0,5,222,borderC)
# fill_rect(0,0,320,1,(197,52,49))
fill_rect(0,221,320,1,(0,0,0))
try:
get_keys()
except:
fill_rect(0,0,320,1,(255,181,49))
else:
fill_rect(0,0,320,1,(197,52,49))
snake=[3,3,3,3,3]
x=154
y=115
endx=104
endy=115
u,v=3,3
length=5
applex,appley=newApple(appleC,bgC)
score,touched=0,0
while touched!=borderC and touched!=snakeC:
if keydown(0) or keydown(1) or keydown(2) or keydown(3):
u=getMove(u)
if keydown(KEY_BACKSPACE):
while keydown(KEY_BACKSPACE):
sleep(0.1)
while keydown(KEY_BACKSPACE)!=True:
sleep(0.1)
while keydown(KEY_BACKSPACE):
sleep(0.1)
snake.append(u)
if x==applex and y==appley:
length=length+float(applePower)
applex,appley=newApple(appleC,bgC)
score=score+int(appleScore)
x=x+((u==3)-(u==0))*10
y=y+((u==2)-(u==1))*10
x,y=checkTeleport(x,y)
if length:
length=length-1
else:
snake.remove(snake[0])
endx=endx+((v==3)-(v==0))*10
endy=endy+((v==2)-(v==1))*10
endx,endy=checkTeleport(endx,endy)
v=snake[0]
fill_rect(endx-4,endy-4,10,10,bgC)
touched=get_pixel(x,y)
if x<0 or x>320 or y<0 or y>220:
touched=borderC
if touched!=appleC and touched!=bgC:
touched=borderC
fill_rect(x-4,y-4,10,10,snakeC)
back=lastPos(snake,x,y)
if u==3 or u==0:
fill_rect(x,y-4,2,4,(0,0,0))
fill_rect(x,y+2,2,4,(0,0,0))
fill_rect(back[0]-4,back[1]-4,10,10,snakeC)
elif u==2 or u==1:
fill_rect(x-4,y,4,2,(0,0,0))
fill_rect(x+2,y,4,2,(0,0,0))
fill_rect(back[0]-4,back[1]-4,10,10,snakeC)
sleep(float(speed))
# EPILEPSY WARNING !!!
# snakeC=(randint(0,255),randint(0,255),randint(0,255))
while snakeC==appleC or snakeC==bgC:
snakeC=(randint(0,255),randint(0,255),randint(0,255))
# beau()
if len(snake)==640:
if darkmode==1:
draw_string("You win !",120,100,'white','black')
draw_string("(You reached the max length)",20,120,'white','black')
else:
draw_string("You win !",120,100)
draw_string("(You reached the max length)",20,120)
touched=borderC
if darkmode==1:
draw_string("Score:"+str(score),10,10,'white','black')
draw_string("(OK=play again, DELETE=Menu)",10,30,'white','black')
else:
draw_string("Score:"+str(score),10,10)
draw_string("(OK=play again, DELETE=Menu)",10,30)
choice=0
while choice==0:
if keydown(KEY_OK):
choice=1
launch(darkmode,speed,applePower,appleScore)
elif keydown(KEY_BACKSPACE):
choice=2
menu()
print("Score:",score)
menu()

View File

@ -0,0 +1,27 @@
# This script draws a Mandelbrot fractal set
# N_iteration: degree of precision
import kandinsky
import ion
import time
N_iteration = 10
for x in range(320):
for y in range(222):
# Compute the mandelbrot sequence for the point c = (c_r, c_i) with start value z = (z_r, z_i)
z = complex(0,0)
# Rescale to fit the drawing screen 320x222
c = complex(3.5*x/319-2.5, -2.5*y/221+1.25)
i = 0
while (i < N_iteration) and abs(z) < 2:
i = i + 1
z = z*z+c
# Choose the color of the dot from the Mandelbrot sequence
rgb = int(255*i/N_iteration)
col = kandinsky.color(int(rgb),int(rgb*0.75),int(rgb*0.25))
# Draw a pixel colored in 'col' at position (x,y)
kandinsky.set_pixel(x,y,col)
while not ion.keydown(ion.KEY_EXE):
time.sleep(0.1)

View File

@ -0,0 +1,46 @@
from kandinsky import *
from gint import *
draw_string( "Hello Kandinsky", 10, 10, "white", "red" )
draw_string( "Hello Kandinsky", 10, 200, "k" )
fill_rect( 25, 25, 100, 100, 00000 )
fill_rect( 60, 25, 10, 100, 65000 )
fill_rect( 25, 60, 100, 10, 00031 )
fill_rect( 100, 100, 25, 25, "green" )
fill_rect( 200, 100, 25, 25, (255,255,0) )
fill_rect( 200, 50, 25, 25, (128,0,255) )
set_pixel( 150, 150, "red" )
set_pixel( 160, 160, (0,0,255) )
# The following functions are only valid on fxCG (this is an extension of Kandinsky to take benefit of wide screen
color = "black"
if CGEXT_Is_Wide_Screen_Enabled() :
color = "red"
fill_rect( -100, 150, 500, 10, color )
CGEXT_Enable_Wide_Screen()
if CGEXT_Is_Wide_Screen_Enabled() :
color = "green"
else :
color = "purple"
fill_rect( -100, 165, 500, 10, color )
CGEXT_Disable_Wide_Screen()
if CGEXT_Is_Wide_Screen_Enabled() :
color = "blue"
else :
color = "red"
fill_rect( -100, 180, 500, 10, color )
getkey()

View File

@ -0,0 +1,227 @@
# PacMan par Kevin FEDYNA
# https://nsi.xyz/numapps/pac-man-en-python-numworks/
# converti sur fxCG50 et PythonExtra par SlyVTT
from kandinsky import fill_rect, draw_string
from ion import keydown, KEY_LEFT, KEY_UP, KEY_DOWN, KEY_RIGHT
from math import sqrt
from random import randint
from time import monotonic
try:
from kandinsky import get_keys
color = (192, 53, 53)
except:
color = (255, 183, 52)
terrain = (262143,131841,187245,187245,131073,186285,135969,252783,249903,251823,1152,251823,249903,251823,131841,187245,147465,219051,135969,195453,131073,262143)
bits = 18
width = 320
height = 222
colors = ((0, 0, 0), (32, 48, 248), (248, 224, 8), tuple(color))
ghost_color = ((255, 184, 255), (255, 0,0), (255, 184, 82), (0, 255, 255))
pacgommes = [0,130302,9360,74898,131070,75858,126174,8208,8208,8208,8208,8208,8208,8208,130302,74898,49140,43092,126174,66690,131070,0]
superpacgommes = [0,0,65538,0,0,0,0,0,0,0,0,0,0,0,0,0,65538,0,0,0,0,0,0]
frightened = 0
lives = 2
won = 0
lvl = 0
score = 0
chained = 0
class Entity:
def __init__(self, x, y, clr, d=0):
self.x = x
self.y = y
self.d = d
self.nd = d
self.f = 0
self.out = 0
self.color = clr
fill_rect(int(self.x*10)+136,int(self.y*10)-3,8,8,self.color)
def espace(self,dx=-1,dy=-1):
if dx == dy:
dx, dy = ((-0.1,0),(0,-0.1),(0,0.1),(0.1,0))[self.nd]
return not terrain[int(self.y + 5.5*dy)]>>(bits-1-int(self.x + 5.5*dx)) & 1 and ((dx != 0 and self.y%1 == 0.5) or (dy != 0 and self.x%1== 0.5))
def move(self):
global frightened, ghosts, score, chained, lives, total, won
dx, dy = ((-0.1,0),(0,-0.1),(0,0.1),(0.1,0))[self.d]
if self.espace(dx,dy):
fill_rect(int(self.x*10)+136,int(self.y*10)-3,8,8,colors[0])
self.x = (round(self.x + dx, 1) - 0.5) % 16.5 + 0.5
self.y = round(self.y + dy, 1)
fill_rect(int(self.x*10)+136,int(self.y*10)-3,8,8,self.color)
if self.color == colors[2]:
if pacgommes[int(self.y)] >> (bits - 1 - int(self.x)) & 1:
pacgommes[int(self.y)] -= 1 << (bits - 1 - int(self.x))
score += 10
if superpacgommes[int(self.y)] >> (bits - 1 - int(self.x)) & 1:
superpacgommes[int(self.y)] -= 1 << (bits - 1 - int(self.x))
score += 50
chained = 0
frightened = monotonic()
for g in ghosts:
if g.out:
g.color = colors[1]
g.d = (3, 2, 1, 0)[g.d]
g.f = 1
for g in range(4):
if sqrt((self.x-ghosts[g].x)**2+(self.y-ghosts[g].y)**2) < 0.6:
if ghosts[g].f:
chained += 1
total += 1
score += (1 << chained)*100
ghosts[g].f = 0
ghosts[g].color = ghost_color[g]
ghosts[g].x = 9
ghosts[g].y = 8.5
if total == 16:
score += 12000
else:
for gp in range(4):
ghosts[gp].f = 0
ghosts[gp].color = ghost_color[gp]
ghosts[gp].x = 9
ghosts[gp].y = 10.5
ghosts[gp].out = 0
self.x = 9
self.y = 16.5
self.d, self.nd = 0, 0
lives -= 1
return render()
if not won and score > 10000:
lives += 1
won = 1
px, py = int(self.x - 5.5*dx), int(self.y - 5.5*dy)
if pacgommes[py]>>(bits-1-px) & 1:
fill_rect(px*10+144,py*10+5,2,2,(250, 207, 173))
if superpacgommes[py]>>(bits-1-px) & 1:
fill_rect(px*10+143,py*10+4,4,4,(250, 207, 173))
def ia(self,x,y):
if self.f:
while True:
d = randint(0,3)
dx, dy = ((-0.1,0),(0,-0.1),(0,0.1),(0.1,0))[d]
if d != (3,2,1,0)[self.d] and self.espace(dx,dy):
self.d = d
break
else:
distances = [9999 for _ in range(4)]
for i in range(4):
if i != (3,2,1,0)[self.d]:
dx, dy = ((-0.1,0),(0,-0.1),(0,0.1),(0.1,0))[i]
if self.espace(dx,dy):
distances[i] = sqrt((self.y + dy - y)**2 + (self.x + dx - x)**2)
self.d = distances.index(min(distances))
def prebuild():
fill_rect(0,0,width,height,colors[0])
fill_rect(138, 0, 2, height, colors[3])
draw_string("PAC-MAN", 35, 10, colors[3], colors[0])
draw_string("nsi.xyz/pacman", 0, 204,colors[0], colors[3])
draw_string("Score :", 35, 40, (255,)*3, colors[0])
draw_string("Niveau :", 30, 90, (255,)*3, colors[0])
def render():
global terrain, pacgommes, superpacgommes, lives, arrivee
if lives == -1:
return 42
draw_string(str(lvl),70-5*len(str(lvl)),110,(255,)*3,colors[0])
fill_rect(0,150,138,20,colors[0])
for i in range(lives):
fill_rect(60-(lives-1)*20+i*40,150,20,20,colors[2])
for l in range(len(terrain)):
for c in range(bits):
fill_rect(c*10+140,l*10+1,10,10,colors[0])
if pacgommes[l]>>(bits-1-c) & 1:
fill_rect(c*10+144,l*10+5,2,2,(250, 207, 173))
if superpacgommes[l]>>(bits-1-c) & 1:
fill_rect(c*10+143,l*10+4,4,4,(250, 207, 173))
if terrain[l]>>(bits-1-c) & 1:
for d in ((1,0),(0,1),(-1,0),(0,-1)):
if 0 <= l + d[0] <= len(terrain) - 1 and 0 <= c + d[1] <= bits - 1 and not terrain[l + d[0]]>>(bits-1-(c+d[1])) & 1:
fill_rect(c*10+140+9*(d[1]==1),l*10+1+9*(d[0]==1),1+9*(d[1]==0),1+9*(d[0]==0),colors[1])
arrivee = monotonic()
def engine():
global frightened, ghosts, pacgommes, superpacgommes, lvl, arrivee, total
while True:
pacgommes = [0,130302,9360,74898,131070,75858,126174,8208,8208,8208,8208,8208,8208,8208,130302,74898,49140,43092,126174,66690,131070,0]
superpacgommes = [0,0,65538,0,0,0,0,0,0,0,0,0,0,0,0,0,65538,0,0,0,0,0,0]
lvl += 1
total = 0
render()
pacman = Entity(9, 16.5, colors[2])
ghosts = [Entity(9, 10.5, ghost_color[i]) for i in range(4)]
while sum(pacgommes) + sum(superpacgommes):
depart = monotonic()
lk=[KEY_LEFT, KEY_UP, KEY_DOWN, KEY_RIGHT]
for i in range(4):
if keydown(lk[i]):
if i == (3,2,1,0)[pacman.d]:
pacman.d = i
pacman.nd = i
while monotonic() - depart < 0.01:
if pacman.espace():
pacman.d = pacman.nd
if pacman.move() == 42:
draw_string("GAME OVER",185,100,colors[3],colors[0])
return 69
draw_string(str(score),70-5*len(str(score)),60,(255,)*3,colors[0])
""" Fantomes """
if frightened:
if monotonic() - frightened > 6.5:
for g in ghosts:
if g.f:
g.color = (255,)*3
if monotonic() - frightened > 8.5:
frightened = 0
for g in range(4):
ghosts[g].color = ghost_color[g]
ghosts[g].f = 0
if arrivee:
if monotonic() - arrivee > 0 and not ghosts[1].out:
ghosts[1].out = 1
ghosts[1].y = 8.5
if monotonic() - arrivee > 2.5 and not ghosts[0].out:
ghosts[0].out = 1
ghosts[0].y = 8.5
if monotonic() - arrivee > 5 and not ghosts[3].out:
ghosts[3].out = 1
ghosts[3].y = 8.5
if monotonic() - arrivee > 7.5 and not ghosts[2].out:
ghosts[2].out = 1
ghosts[2].y = 8.5
fill_rect(220,101,20,10,colors[0])
arrivee = 0
pdx, pdy = ((-0.1,0),(0,-0.1),(0,0.1),(0.1,0))[pacman.d]
# Pinky
ghosts[0].ia(pacman.x + 20 * pdx, pacman.y + 20 * pdy)
ghosts[0].move()
# Inky
ghosts[3].ia(max(min(ghosts[1].x + 2*(pacman.x + 20 * pdx - ghosts[1].x), 16.5), 1.5), max(min(ghosts[1].y +2*(pacman.y + 20 * pdy - ghosts[1].y), 21.5), 1.5))
ghosts[3].move()
# Blinky
ghosts[1].ia(pacman.x, pacman.y)
ghosts[1].move()
# Clyde
if sqrt((ghosts[2].x - pacman.x)**2 + (ghosts[2].y - pacman.y)**2) > 4:
ghosts[2].ia(pacman.x, pacman.y)
else:
ghosts[2].ia(1.5, 20.5)
ghosts[2].move()
fill_rect(0,0,319,219,"black")
prebuild()
engine()

View File

@ -0,0 +1,77 @@
from kandinsky import *
from math import *
import ion
import time
def cercle1(x0,y0,r,c,e):
for i in range(2*e):
xd=x0-int((r-i*0.5)/sqrt(2))
xf=x0+int((r-i*0.5)/sqrt(2))
for x in range(xd,xf+1):
x1=x
y1=y0+int(sqrt((r-i*0.5)**2-(x-x0)**2))
if sqrt((160-x1)**2+(111-y1)**2)<r:
set_pixel(x1,y1,c)
for j in range(3):
x2=x0+y1-y0
y2=y0+x0-x1
if sqrt((160-x2)**2+(111-y2)**2)<r:
set_pixel(x2,y2,c)
x1,y1=x2,y2
def cercle2(x0,y0,r,c,e):
for i in range(2*e):
xd=x0-int((r-i*0.5)/sqrt(2))
xf=x0+int((r-i*0.5)/sqrt(2))
for x in range(xd,xf+1):
x1=x
y1=y0+int(sqrt((r-i*0.5)**2-(x-x0)**2))
set_pixel(x,y1,c)
for j in range(3):
x2=x0+y1-y0
y2=y0+x0-x1
set_pixel(x2,y2,c)
x1,y1=x2,y2
def rosace1(n,r,c,e):
x,y=160+r,111
for i in range(n):
x1=int(160+r*cos(i*2*pi/n))
y1=int(111+r*sin(i*2*pi/n))
cercle1(x1,y1,r,c,e)
cercle1(160,111,r,c,e)
def rosace2(n,r,c,e):
x,y=160+r,111
for i in range(n):
x1=int(160+r*cos(i*2*pi/n))
y1=int(111+r*sin(i*2*pi/n))
cercle2(x1,y1,r,c,e)
def rosace3(n,r,c1,c2,c3,e):
for k in range(2):
rj=r
for j in range(n-2):
rj=int(rj-rj/(2*n))
if k==0:rj+=1
x,y=160+rj,111
for i in range(n):
x1=int(160+rj*cos(i*2*pi/n))
y1=int(111+rj*sin(i*2*pi/n))
if k==0:
col=c3
else:
if j==0 or j>n-4:col=c2
else:col=c1
cercle2(x1,y1,rj,col,e)
col1=color(5,50,120)
col2=color(255,45,45)
col3=color(245,225,25)
#rosace1(12,50,col1,2)
#rosace2(12,50,col1,1)
rosace3(10,55,col1,col2,col3,1)
while not ion.keydown(ion.KEY_EXE):
time.sleep(0.1)

View File

@ -0,0 +1,326 @@
# Snake from Golem64
# https://my.numworks.com/python/golem64/snake
#
# converted to PythonExtra on fx-CG50 by SlyVTT
#
# Version 1.7 STABLE
# Tip: You should try to press
# some keys in the menu...
from random import *
from kandinsky import *
from ion import *
from time import *
def oscolor():
try:
get_keys()
except:
return 'orange'
else:
return 'red'
def lastPos(i,x,y):
if i[-1]==3:
pos=[x-10,y]
elif i[-1]==2:
pos=[x,y-10]
elif i[-1]==0:
pos=[x+10,y]
elif i[-1]==1:
pos=[x,y+10]
pos[0],pos[1]=checkTeleport(pos[0],pos[1])
return pos
def newApple(appleC,bgC):
applex=randint(0,31)*10+4
appley=randint(0,21)*10+5
while get_pixel(applex,appley)!=bgC:
applex=randint(0,31)*10+4
appley=randint(0,21)*10+5
fill_rect(applex-4,appley-4,10,10,appleC)
return applex,appley
def checkTeleport(x,y):
if x<4:
x=314
if x>314:
x=4
if y<5:
y=215
if y>215:
y=5
return x,y
def getMove(u):
lk=[KEY_LEFT, KEY_UP, KEY_DOWN, KEY_RIGHT]
for k in range(4):
if keydown(lk[k])==True and u+k!=3: return k
return u
def clearDraw():
fill_rect(0,0,320,222,(255,255,255))
def clearHome():
print("\n \n \n \n \n \n \n \n \n \n \n \n \n ")
def redraw():
draw_string("(DELETE to exit)",0,0)
printLetter([1,1,1,1,0,0,1,1,1,0,0,1,1,1,1],70,80,10,(0,204,0))
fill_rect(95,80,2,4,(0,0,0))
fill_rect(95,86,2,4,(0,0,0))
fill_rect(100,84,4,2,(255,0,0))
fill_rect(104,82,2,2,(255,0,0))
fill_rect(104,86,2,2,(255,0,0))
printLetter([1,1,1,1,0,1,1,0,1,1,0,1,1,0,1],110,80,10,(0,0,0))
printLetter([1,1,1,1,0,1,1,1,1,1,0,1,1,0,1],150,80,10,(0,0,0))
printLetter([1,0,1,1,0,1,1,1,0,1,0,1,1,0,1],190,80,10,(0,0,0))
printLetter([1,1,1,1,0,0,1,1,1,1,0,0,1,1,1],230,80,10,(0,0,0))
def printLetter(letter,x,y,size,color):
for yi in range(5):
for xi in range(3):
if letter[yi*3+xi]==1:
fill_rect(x+(xi*size),y+(yi*size),size,size,color)
def menu():
clearDraw()
printLetter([1,1,1,1,0,1,1,0,1,1,0,1,1,0,1],110,80,10,(0,0,0))
printLetter([1,1,1,1,0,1,1,1,1,1,0,1,1,0,1],150,80,10,(0,0,0))
printLetter([1,0,1,1,0,1,1,1,0,1,0,1,1,0,1],190,80,10,(0,0,0))
printLetter([1,1,1,1,0,0,1,1,1,1,0,0,1,1,1],230,80,10,(0,0,0))
anim=[1,1,1,1,1,1,1,1,1,4,4,3,3,4,4,1,1]
ax=0
ay=120
aendx=-110
aendy=120
u=1
aback=0
for i in range(len(anim)):
ax=ax+((anim[i]==1)-(anim[i]==3))*10
ay=ay+((anim[i]==2)-(anim[i]==4))*10
if aendx<0:
aendx=aendx+10
else:
aendx=aendx+((anim[i-11]==1)-(anim[i-11]==3))*10
aendy=aendy+((anim[i-11]==2)-(anim[i-11]==4))*10
fill_rect(aendx,aendy,10,10,(255,255,255))
fill_rect(ax,ay,10,10,(0,204,0))
# aback=lastPos(anim,ax,ay)
# if u==26 or u==24:
# fill_rect(ax-1,ay-1,3,1,(0,0,0))
# fill_rect(ax-1,ay+1,3,1,(0,0,0))
# fill_rect(aback[0],aback[1],10,10,(0,204,0))
# elif u==34 or u==25:
# fill_rect(ax-1,ay-1,1,3,(0,0,0))
# fill_rect(ax+1,ay-1,1,3,(0,0,0))
# fill_rect(aback[0]-2,aback[1]-2,5,5,(0,204,0))
sleep(0.05)
fill_rect(ax+5,ay,2,4,(0,0,0))
fill_rect(ax+5,ay+6,2,4,(0,0,0))
fill_rect(ax+10,ay+4,4,2,(255,0,0))
fill_rect(ax+14,ay+2,2,2,(255,0,0))
fill_rect(ax+14,ay+6,2,2,(255,0,0))
draw_string("(DELETE to exit)",0,0)
draw_string("> Play <",125,140,oscolor())
draw_string(" Options ",110,165)
darkMode=0
Speed=0.05
power=5
score=1
exit=0
sel=1
while keydown(KEY_OK)!=True and exit==0:
if keydown(KEY_DOWN) and sel==1:
draw_string(" Play ",125,140)
draw_string("> Options <",110,165,oscolor())
sel=2
elif keydown(KEY_UP) and sel==2:
draw_string("> Play <",125,140,oscolor())
draw_string(" Options ",110,165)
sel=1
if keydown(KEY_LEFTPARENTHESIS) and keydown(KEY_RIGHTPARENTHESIS):
draw_string("Dark mode enabled !",80,195)
darkMode=1
if keydown(KEY_BACKSPACE):
exit=1
sleep(0.1)
if sel==2 and exit!=1:
fill_rect(0,130,300,60,(255,255,255))
Speed=0.05
power=5
score=1
draw_string("Speed:"+str(Speed),50,140,oscolor(),'white')
draw_string("Power:+"+str(power),200,140)
draw_string("Score:+"+str(score),50,170)
draw_string("Play",220,170)
sel=1
sleep(0.2)
while keydown(KEY_OK)!=True or sel!=4:
if keydown(KEY_RIGHT):
sel=sel+1
elif keydown(KEY_DOWN):
sel=sel+2
elif keydown(KEY_LEFT):
sel=sel-1
elif keydown(KEY_UP):
sel=sel-2
if sel<0:
sel=0
if sel>4:
sel=4
if sel==1:
draw_string("Speed:"+str(Speed),50,140,oscolor(),'white')
draw_string("Power:+"+str(power),200,140)
draw_string("Score:+"+str(score),50,170)
draw_string("Play",220,170)
if keydown(KEY_OK):
clearHome()
Speed=input("Speed:")
redraw()
elif sel==2:
draw_string("Speed:"+str(Speed),50,140)
draw_string("Power:+"+str(power),200,140,oscolor(),'white')
draw_string("Score:+"+str(score),50,170)
draw_string("Play",220,170)
if keydown(KEY_OK):
clearHome()
power=int(input("Power:+"))
redraw()
elif sel==3:
draw_string("Speed:"+str(Speed),50,140)
draw_string("Power:+"+str(power),200,140)
draw_string("Score:+"+str(score),50,170,oscolor(),'white')
draw_string("Play",220,170)
if keydown(KEY_OK):
clearHome()
score=int(input("Score:"))
redraw()
elif sel==4:
draw_string("Speed:"+str(Speed),50,140)
draw_string("Power:+"+str(power),200,140)
draw_string("Score:+"+str(score),50,170)
draw_string("Play",220,170,oscolor(),'white')
if (keydown(KEY_LEFTPARENTHESIS) and keydown(KEY_RIGHTPARENTHESIS)) or darkMode==1:
draw_string("Dark mode enabled !",80,195)
darkMode=1
if keydown(KEY_BACKSPACE):
exit=1
break
sleep(0.1)
if exit!=1:
if darkMode==1:
launch(1,Speed,power,score)
elif darkMode==0:
launch(0,Speed,power,score)
elif exit==1:
clearDraw()
return
def launch(darkmode=0,speed=0.05,applePower=5,appleScore=1):
bgC=(248,252,248)
borderC=(0,0,0)
snakeC=(0,204,0)
appleC=(248,0,0)
if darkmode==1:
bgC=(0,0,0)
borderC=(0,0,204)
fill_rect(0,0,320,222,bgC)
# fill_rect(315,0,5,222,borderC)
# fill_rect(0,0,5,222,borderC)
# fill_rect(0,0,320,1,(197,52,49))
fill_rect(0,221,320,1,(0,0,0))
try:
get_keys()
except:
fill_rect(0,0,320,1,(255,181,49))
else:
fill_rect(0,0,320,1,(197,52,49))
snake=[3,3,3,3,3]
x=154
y=115
endx=104
endy=115
u,v=3,3
length=5
applex,appley=newApple(appleC,bgC)
score,touched=0,0
while touched!=borderC and touched!=snakeC:
if keydown(KEY_LEFT) or keydown(KEY_UP) or keydown(KEY_DOWN) or keydown(KEY_RIGHT):
u=getMove(u)
if keydown(KEY_BACKSPACE):
while keydown(KEY_BACKSPACE):
sleep(0.1)
while keydown(KEY_BACKSPACE)!=True:
sleep(0.1)
while keydown(KEY_BACKSPACE):
sleep(0.1)
snake.append(u)
if x==applex and y==appley:
length=length+float(applePower)
applex,appley=newApple(appleC,bgC)
score=score+int(appleScore)
x=x+((u==3)-(u==0))*10
y=y+((u==2)-(u==1))*10
x,y=checkTeleport(x,y)
if length:
length=length-1
else:
snake.remove(snake[0])
endx=endx+((v==3)-(v==0))*10
endy=endy+((v==2)-(v==1))*10
endx,endy=checkTeleport(endx,endy)
v=snake[0]
fill_rect(endx-4,endy-4,10,10,bgC)
touched=get_pixel(x,y)
if x<0 or x>320 or y<0 or y>220:
touched=borderC
if touched!=appleC and touched!=bgC:
touched=borderC
fill_rect(x-4,y-4,10,10,snakeC)
back=lastPos(snake,x,y)
if u==3 or u==0:
fill_rect(x,y-4,2,4,(0,0,0))
fill_rect(x,y+2,2,4,(0,0,0))
fill_rect(back[0]-4,back[1]-4,10,10,snakeC)
elif u==2 or u==1:
fill_rect(x-4,y,4,2,(0,0,0))
fill_rect(x+2,y,4,2,(0,0,0))
fill_rect(back[0]-4,back[1]-4,10,10,snakeC)
sleep(float(speed))
# EPILEPSY WARNING !!!
# snakeC=(randint(0,255),randint(0,255),randint(0,255))
while snakeC==appleC or snakeC==bgC:
snakeC=(randint(0,255),randint(0,255),randint(0,255))
# beau()
if len(snake)==640:
if darkmode==1:
draw_string("You win !",120,100,'white','black')
draw_string("(You reached the max length)",20,120,'white','black')
else:
draw_string("You win !",120,100)
draw_string("(You reached the max length)",20,120)
touched=borderC
if darkmode==1:
draw_string("Score:"+str(score),10,10,'white','black')
draw_string("(OK=play again, DELETE=Menu)",10,30,'white','black')
else:
draw_string("Score:"+str(score),10,10)
draw_string("(OK=play again, DELETE=Menu)",10,30)
choice=0
while choice==0:
if keydown(KEY_OK):
choice=1
launch(darkmode,speed,applePower,appleScore)
elif keydown(KEY_BACKSPACE):
choice=2
menu()
print("Score:",score)
try:
CGEXT_Set_Margin_Color( "black" )
except:
print("fxCG Extension not supported")
menu()

View File

@ -0,0 +1,15 @@
from time import *
print(monotonic())
sleep(1)
print(monotonic())
sleep(2)
print(monotonic())
sleep(1)
print(monotonic())

View File

@ -0,0 +1,4 @@
from kandinsky import *
# Check whether global dwindow from kandisky interferes with shell
input("Happy? (y/n): ")

View File

@ -35,6 +35,7 @@
#include "console.h"
#include "widget_shell.h"
#include "debug.h"
#include "resources.h"
//=== Application globals ===//
@ -54,9 +55,12 @@ struct pe_globals {
// TODO: Put pe_globals in a header for use by the loop hook in mpconfigport.h
widget_shell *pe_shell;
/* Whether a delayed dupdate has been scheduled asynchronously. */
bool pe_dupdate_scheduled;
struct pe_globals PE = { 0 };
//=== Hook for redirecting stdout/stderr to the shell ===//
static ssize_t stdouterr_write(void *data, void const *buf, size_t size)
@ -66,7 +70,7 @@ static ssize_t stdouterr_write(void *data, void const *buf, size_t size)
return size;
}
fs_descriptor_type_t stdouterr_type = {
static fs_descriptor_type_t const stdouterr_type = {
.read = NULL,
.write = stdouterr_write,
.lseek = NULL,
@ -130,6 +134,14 @@ static bool async_filter(key_event_t ev)
return false;
}
#if PE_DEBUG
if(ev.key == KEY_SQUARE) {
if(ev.type == KEYEV_DOWN)
pe_debug_toggle_videocapture();
return false;
}
#endif
return true;
}
@ -141,6 +153,7 @@ void pe_after_python_exec(int input_kind, int exec_flags, void *ret_val,
(void)ret_val;
(void)ret;
clearevents();
pe_resources_autofree();
}
//=== Rendering ===//
@ -162,6 +175,19 @@ void pe_enter_graphics_mode(void)
PE.shell->widget.update = 0;
}
void pe_schedule_dupdate(void)
{
pe_enter_graphics_mode();
pe_dupdate_scheduled = true;
}
void pe_dupdate(void)
{
dupdate();
pe_debug_run_videocapture();
pe_dupdate_scheduled = false;
}
void pe_draw(void)
{
dclear(C_WHITE);
@ -180,7 +206,7 @@ void pe_draw(void)
dsubimage(377, 207, &img_modifier_states, 16*icon, 0, 15, 14,
DIMAGE_NONE);
#endif
dupdate();
pe_dupdate();
}
//=== Application control functions ===//
@ -214,6 +240,30 @@ static void pe_print_prompt(int which)
console_lock_prefix(PE.console);
}
static void pe_update_title(void)
{
char const *folder = jfileselect_current_folder(PE.fileselect);
#ifdef FX9860G
/* Use a static variable to ensure the title shows even if OoM */
static char title[22];
if(!folder)
jlabel_set_text(PE.title, "Python");
else {
sprintf(title, "Python[%-13.13s]", folder);
jlabel_set_text(PE.title, title);
}
#endif
#ifdef FXCG50
if(!folder)
jlabel_set_text(PE.title, "PythonExtra");
else
jlabel_asprintf(PE.title, "PythonExtra (%s)", folder);
#endif
}
/* Handle a GUI event. If `shell_bound` is true, only actions that have an
effect on the shell are allowed and the return value is any full line that
is entered in the shell. Otherwise, the full GUI is available and the return
@ -246,6 +296,7 @@ static char *pe_handle_event(jevent e, bool shell_bound)
jwidget_set_visible(PE.title, PE.show_title_in_shell);
pe_reset_micropython();
pe_draw();
char *str = malloc(8 + strlen(module) + 1);
if(str) {
@ -259,15 +310,19 @@ static char *pe_handle_event(jevent e, bool shell_bound)
pe_print_prompt(1);
}
}
if(!shell_bound && e.type == JFILESELECT_LOADED)
pe_update_title();
if(e.type != JWIDGET_KEY || e.key.type == KEYEV_UP)
return NULL;
int key = e.key.key;
pe_debug_kmalloc("key");
if(key == KEY_SQUARE && !e.key.shift && e.key.alpha)
pe_debug_screenshot();
if(key == KEY_TAN)
pe_debug_kmalloc();
pe_debug_kmalloc("tan");
if(!shell_bound && key == KEY_F1) {
jscene_show_and_focus(PE.scene, PE.fileselect);
@ -277,6 +332,10 @@ static char *pe_handle_event(jevent e, bool shell_bound)
jscene_show_and_focus(PE.scene, PE.shell);
jwidget_set_visible(PE.title, PE.show_title_in_shell);
}
if(!shell_bound && key == KEY_VARS && e.key.shift) {
pe_debug_browse_meminfo();
PE.scene->widget.update = true;
}
return NULL;
}
@ -304,14 +363,42 @@ int pe_readline(vstr_t *line, char const *prompt)
int main(int argc, char **argv)
{
#ifdef FX9860G
/* Use PRAM0 as an arena for special allocs to save memory elsewhere */
kmalloc_arena_t arena_pram0 = { 0 };
arena_pram0.name = "pram0";
arena_pram0.is_default = false;
arena_pram0.start = (void *)0xfe200000;
arena_pram0.end = (void *)0xfe228000; /* 160 kB! */
kmalloc_init_arena(&arena_pram0, true);
kmalloc_add_arena(&arena_pram0);
#endif
pe_debug_init();
pe_debug_kmalloc();
pe_debug_printf("---\n");
pe_debug_get_startup_meminfo(MAIN);
//=== Init sequence ===//
keydev_set_async_filter(keydev_std(), async_filter);
PE.console = console_create(8192, 200);
/* Make delayed shift/alpha permanent so the stay between calls to
jscene_run() and can be used for delayed key combos */
keydev_transform_t tr = keydev_transform(keydev_std());
tr.enabled |= KEYDEV_TR_DELAYED_SHIFT;
tr.enabled |= KEYDEV_TR_DELAYED_ALPHA;
keydev_set_transform(keydev_std(), tr);
#ifdef FX9860G
int console_scrollback = 50;
int console_max_bytes = 4096;
#else
int console_scrollback = 200;
int console_max_bytes = 8192;
#endif
PE.console = console_create(console_max_bytes, console_scrollback);
pe_debug_get_startup_meminfo(CONSOLE);
/* Set up standard streams */
close(STDOUT_FILENO);
@ -345,16 +432,21 @@ int main(int argc, char **argv)
#if PE_DEBUG
/* Add some Python ram */
// https://www.planet-casio.com/Fr/forums/topic15269-10-khicas-add-in-calcul-formel-pour-graph-90e-et-35eii.html#189284
void *py_ram_start = (void*)0x88053800;
/* void *py_ram_start = (void*)0x88053800;
void *py_ram_end = (void*)0x8807f000;
gc_add(py_ram_start, py_ram_end);
gc_add(py_ram_start, py_ram_end); */
#endif
#else
/* Get everything from the OS stack (~ 350 ko) */
/* Get everything from the OS stack (~ 350 kB) */
size_t gc_area_size;
void *gc_area = kmalloc_max(&gc_area_size, "_ostk");
gc_init(gc_area, gc_area + gc_area_size);
/* Also get most of _uram; we can try and fit in the remaining ~150 kB */
void *uram_area = kmalloc(300000, "_uram");
if(uram_area)
gc_add(uram_area, uram_area + 300000);
/* Other options:
- All of _uram (leaving the OS heap for the shell/GUI/etc)
- The OS' extra VRAM
@ -372,13 +464,17 @@ int main(int argc, char **argv)
MP_OBJ_NEW_QSTR(qstr_from_str("."));
#endif
pe_debug_get_startup_meminfo(UPY);
pyexec_event_repl_init();
pe_print_prompt(1);
pe_debug_get_startup_meminfo(PROMPT);
//=== GUI setup ===//
PE.scene = jscene_create_fullscreen(NULL);
PE.title = jlabel_create("PythonExtra", PE.scene);
PE.title = jlabel_create("<temp>", PE.scene);
jwidget *stack = jwidget_create(PE.scene);
jfkeys *fkeys = jfkeys_create2(&img_fkeys_main, "/FILES;/SHELL", PE.scene);
(void)fkeys;
@ -412,6 +508,8 @@ int main(int argc, char **argv)
jwidget_set_padding(stack, 0, 6, 0, 6);
#endif
pe_debug_get_startup_meminfo(UI);
/* Initial state */
jfileselect_browse(PE.fileselect, "/");
jscene_show_and_focus(PE.scene, PE.fileselect);
@ -425,6 +523,8 @@ int main(int argc, char **argv)
//=== Deinitialization ===//
pe_debug_close();
gc_sweep_all();
mp_deinit();
console_destroy(PE.console);

View File

@ -7,10 +7,14 @@
#include "py/runtime.h"
#include "py/obj.h"
#include "debug.h"
#include <gint/display.h>
#include <stdlib.h>
#include <string.h>
extern void pe_enter_graphics_mode(void);
extern void pe_dupdate(void);
#ifdef FX9860G
extern font_t font_4x4;
extern font_t font_4x6;
@ -69,9 +73,8 @@ static mp_obj_t init(void)
static mp_obj_t show_screen(void)
{
void pe_enter_graphics_mode(void);
pe_enter_graphics_mode();
dupdate();
pe_dupdate();
return mp_const_none;
}

View File

@ -9,12 +9,18 @@
// considered relevant for high-level Python development).
//---
#include "debug.h"
#include "py/runtime.h"
#include "py/objtuple.h"
#include "objgintimage.h"
#include <gint/display.h>
#include <gint/keyboard.h>
#include <gint/timer.h>
#include <gint/drivers/keydev.h>
#include <stdlib.h>
void pe_enter_graphics_mode(void);
extern void pe_enter_graphics_mode(void);
extern void pe_dupdate(void);
#define FUN_0(NAME) \
MP_DEFINE_CONST_FUN_OBJ_0(modgint_ ## NAME ## _obj, modgint_ ## NAME)
@ -117,17 +123,64 @@ STATIC mp_obj_t modgint_keyreleased(mp_obj_t arg1)
return mp_obj_new_bool(keyreleased(key) != 0);
}
/* Version of getkey_opt() that includes a VM hook */
STATIC key_event_t getkey_opt_internal(int opt, int timeout_ms)
{
/* Preset keydev transforms so they stay between calls */
keydev_t *d = keydev_std();
keydev_transform_t tr = keydev_transform(d);
key_event_t ev;
int o = KEYDEV_TR_REPEATS +
KEYDEV_TR_DELETE_MODIFIERS +
KEYDEV_TR_DELETE_RELEASES +
(opt & (GETKEY_MOD_SHIFT + GETKEY_MOD_ALPHA));
keydev_set_transform(d, (keydev_transform_t){ o, tr.repeater });
bool has_timeout = (timeout_ms >= 0);
while(!has_timeout || timeout_ms > 0) {
/* Program a delay of whatever's left or 20 ms, whichever is smaller.
It's not easy to reload a timer currently so just reconfigure. */
volatile int flag = 0;
int round_ms = has_timeout ? min(timeout_ms, 20) : 20;
int t = timer_configure(TIMER_ETMU, round_ms * 1000,
GINT_CALL_SET(&flag));
timer_start(t);
/* Run getkey_opt() for that short period */
ev = getkey_opt(opt, &flag);
timer_stop(t);
if(ev.type != KEYEV_NONE)
break;
/* The whole reason this function exists -- run the VM hook */
MICROPY_VM_HOOK_LOOP;
if(has_timeout)
timeout_ms -= round_ms;
}
keydev_set_transform(d, tr);
return ev;
}
STATIC mp_obj_t modgint_getkey(void)
{
key_event_t ev = getkey();
key_event_t ev = getkey_opt_internal(GETKEY_DEFAULT, -1);
return mk_key_event(ev);
}
// TODO: getkey_opt: timeout parameter?
STATIC mp_obj_t modgint_getkey_opt(mp_obj_t arg1)
STATIC mp_obj_t modgint_getkey_opt(mp_obj_t arg1, mp_obj_t arg2)
{
int options = mp_obj_get_int(arg1);
key_event_t ev = getkey_opt(options, NULL);
int timeout_ms = -1;
if(arg2 != mp_const_none)
timeout_ms = mp_obj_get_int(arg2);
key_event_t ev = getkey_opt_internal(options, timeout_ms);
return mk_key_event(ev);
}
@ -152,7 +205,7 @@ FUN_VAR(keydown_any, 0);
FUN_1(keypressed);
FUN_1(keyreleased);
FUN_0(getkey);
FUN_1/*2*/(getkey_opt);
FUN_2(getkey_opt);
FUN_1(keycode_function);
FUN_1(keycode_digit);
@ -178,7 +231,7 @@ STATIC mp_obj_t modgint_dclear(mp_obj_t arg1)
STATIC mp_obj_t modgint_dupdate(void)
{
pe_enter_graphics_mode();
dupdate();
pe_dupdate();
return mp_const_none;
}
@ -274,6 +327,34 @@ STATIC mp_obj_t modgint_dellipse(size_t n_args, const mp_obj_t *args)
return mp_const_none;
}
STATIC mp_obj_t modgint_dpoly(mp_obj_t arg1, mp_obj_t arg2, mp_obj_t arg3)
{
mp_uint_t nbitems;
mp_obj_t *items;
mp_obj_list_get(arg1, &nbitems, &items);
int len = nbitems / 2;
int *x = malloc(len * sizeof *x);
int *y = malloc(len * sizeof *y);
if(!x || !y)
goto dpoly_end;
for(int i = 0; i < len; i++) {
x[i] = mp_obj_get_int(items[2*i]);
y[i] = mp_obj_get_int(items[2*i+1]);
}
mp_int_t fill = mp_obj_get_int(arg2);
mp_int_t border = mp_obj_get_int(arg3);
dpoly(x, y, len, fill, border);
dpoly_end:
free(x);
free(y);
return mp_const_none;
}
// TODO: modgint: Font management?
STATIC mp_obj_t modgint_dtext_opt(size_t n, mp_obj_t const *args)
@ -300,6 +381,108 @@ STATIC mp_obj_t modgint_dtext(size_t n, mp_obj_t const *args)
return mp_const_none;
}
/* fx-CG-specific image constructors */
#ifdef FXCG50
STATIC mp_obj_t modgint_image_rgb565(mp_obj_t arg1, mp_obj_t arg2,
mp_obj_t arg3)
{
int width = mp_obj_get_int(arg1);
int height = mp_obj_get_int(arg2);
return objgintimage_make(&mp_type_gintimage, IMAGE_RGB565, 0, width,
height, width * 2, arg3, mp_const_none);
}
STATIC mp_obj_t modgint_image_rgb565a(mp_obj_t arg1, mp_obj_t arg2,
mp_obj_t arg3)
{
int width = mp_obj_get_int(arg1);
int height = mp_obj_get_int(arg2);
return objgintimage_make(&mp_type_gintimage, IMAGE_RGB565A, 0, width,
height, width * 2, arg3, mp_const_none);
}
STATIC mp_obj_t modgint_image_p8_rgb565(size_t n, mp_obj_t const *args)
{
int width = mp_obj_get_int(args[0]);
int height = mp_obj_get_int(args[1]);
mp_obj_t data = args[2];
mp_obj_t palette = args[3];
int color_count = mp_obj_get_int(mp_obj_len(palette)) / 2;
int stride = width;
return objgintimage_make(&mp_type_gintimage, IMAGE_P8_RGB565,
color_count, width, height, stride, data, palette);
}
STATIC mp_obj_t modgint_image_p8_rgb565a(size_t n, mp_obj_t const *args)
{
int width = mp_obj_get_int(args[0]);
int height = mp_obj_get_int(args[1]);
mp_obj_t data = args[2];
mp_obj_t palette = args[3];
int color_count = mp_obj_get_int(mp_obj_len(palette)) / 2;
int stride = width;
return objgintimage_make(&mp_type_gintimage, IMAGE_P8_RGB565A,
color_count, width, height, stride, data, palette);
}
STATIC mp_obj_t modgint_image_p4_rgb565(size_t n, mp_obj_t const *args)
{
int width = mp_obj_get_int(args[0]);
int height = mp_obj_get_int(args[1]);
mp_obj_t data = args[2];
mp_obj_t palette = args[3];
int stride = (width + 1) / 2;
return objgintimage_make(&mp_type_gintimage, IMAGE_P4_RGB565, 16,
width, height, stride, data, palette);
}
STATIC mp_obj_t modgint_image_p4_rgb565a(size_t n, mp_obj_t const *args)
{
int width = mp_obj_get_int(args[0]);
int height = mp_obj_get_int(args[1]);
mp_obj_t data = args[2];
mp_obj_t palette = args[3];
int stride = (width + 1) / 2;
return objgintimage_make(&mp_type_gintimage, IMAGE_P4_RGB565A, 16,
width, height, stride, data, palette);
}
#endif /* FXCG50 */
STATIC mp_obj_t modgint_dimage(mp_obj_t arg1, mp_obj_t arg2, mp_obj_t arg3)
{
mp_int_t x = mp_obj_get_int(arg1);
mp_int_t y = mp_obj_get_int(arg2);
bopti_image_t img;
objgintimage_get(arg3, &img);
dimage(x, y, &img);
return mp_const_none;
}
STATIC mp_obj_t modgint_dsubimage(size_t n_args, const mp_obj_t *args)
{
mp_int_t x = mp_obj_get_int(args[0]);
mp_int_t y = mp_obj_get_int(args[1]);
// args[2] is the image
mp_int_t left = mp_obj_get_int(args[3]);
mp_int_t top = mp_obj_get_int(args[4]);
mp_int_t width = mp_obj_get_int(args[5]);
mp_int_t height = mp_obj_get_int(args[6]);
bopti_image_t img;
objgintimage_get(args[2], &img);
dsubimage(x, y, &img, left, top, width, height, DIMAGE_NONE);
return mp_const_none;
}
FUN_0(__init__);
#ifdef FXCG50
@ -316,8 +499,19 @@ FUN_2(dhline);
FUN_2(dvline);
FUN_BETWEEN(dcircle, 5, 5);
FUN_BETWEEN(dellipse, 6, 6);
FUN_3(dpoly);
FUN_BETWEEN(dtext_opt, 8, 8);
FUN_BETWEEN(dtext, 4, 4);
#ifdef FXCG50
FUN_3(image_rgb565);
FUN_3(image_rgb565a);
FUN_BETWEEN(image_p8_rgb565, 4, 4);
FUN_BETWEEN(image_p8_rgb565a, 4, 4);
FUN_BETWEEN(image_p4_rgb565, 4, 4);
FUN_BETWEEN(image_p4_rgb565a, 4, 4);
#endif
FUN_3(dimage);
FUN_BETWEEN(dsubimage, 7, 7);
/* Module definition */
@ -452,7 +646,12 @@ STATIC const mp_rom_map_elem_t modgint_module_globals_table[] = {
INT(C_LIGHT),
INT(C_DARK),
INT(C_BLACK),
INT(C_INVERT),
INT(C_NONE),
#ifdef FX9860G
INT(C_LIGHTEN),
INT(C_DARKEN),
#endif
#ifdef FXCG50
INT(C_RED),
INT(C_GREEN),
@ -471,8 +670,42 @@ STATIC const mp_rom_map_elem_t modgint_module_globals_table[] = {
OBJ(dvline),
OBJ(dcircle),
OBJ(dellipse),
OBJ(dpoly),
OBJ(dtext_opt),
OBJ(dtext),
{ MP_ROM_QSTR(MP_QSTR_image), MP_ROM_PTR(&mp_type_gintimage) },
#ifdef FXCG50
OBJ(image_rgb565),
OBJ(image_rgb565a),
OBJ(image_p8_rgb565),
OBJ(image_p8_rgb565a),
OBJ(image_p4_rgb565),
OBJ(image_p4_rgb565a),
#endif
OBJ(dimage),
OBJ(dsubimage),
/* <gint/image.h> */
#ifdef FX9860G
INT(IMAGE_MONO),
INT(IMAGE_MONO_ALPHA),
INT(IMAGE_GRAY),
INT(IMAGE_GRAY_ALPHA),
#endif
#ifdef FXCG50
INT(IMAGE_RGB565),
INT(IMAGE_RGB565A),
INT(IMAGE_P8_RGB565),
INT(IMAGE_P8_RGB565A),
INT(IMAGE_P4_RGB565),
INT(IMAGE_P4_RGB565A),
INT(IMAGE_FLAGS_DATA_RO),
INT(IMAGE_FLAGS_PALETTE_RO),
INT(IMAGE_FLAGS_DATA_ALLOC),
INT(IMAGE_FLAGS_PALETTE_ALLOC),
#endif
};
STATIC MP_DEFINE_CONST_DICT(
modgint_module_globals, modgint_module_globals_table);

24
ports/sh/modtime.c Normal file
View File

@ -0,0 +1,24 @@
//---------------------------------------------------------------------------//
// ____ PythonExtra //
//.-'`_ o `;__, A community port of MicroPython for CASIO calculators. //
//.-'` `---` ' License: MIT (except some files; see LICENSE) //
//---------------------------------------------------------------------------//
// pe.modtime: Custom extensions to the `time` module
#include <time.h>
#include "py/runtime.h"
STATIC mp_obj_t time_monotonic(void) {
// TODO: Use libprof instead
uint64_t ms = ((uint64_t)clock() * 1000000000) / CLOCKS_PER_SEC;
return mp_obj_new_float((double)ms);
}
MP_DEFINE_CONST_FUN_OBJ_0(mp_time_monotonic_obj, time_monotonic);
STATIC mp_obj_t mp_time_time_get(void) {
mp_float_t seconds = (mp_float_t)rtc_ticks() / 128;
return mp_obj_new_float(seconds);
}
#define MICROPY_PY_TIME_EXTRA_GLOBALS \
{ MP_ROM_QSTR(MP_QSTR_monotonic), MP_ROM_PTR(&mp_time_monotonic_obj) },

View File

@ -7,6 +7,7 @@
#include <stdint.h>
#include <alloca.h>
#include <gint/rtc.h>
#include "widget_shell.h"
/* Debugging options: PythonExtra debug tools (pretty much required for any
@ -87,6 +88,8 @@ extern const struct _mp_print_t mp_debug_print;
#define MICROPY_PY_RANDOM_EXTRA_FUNCS (1)
#define MICROPY_PY_SYS (1)
#define MICROPY_PY_TIME (1)
#define MICROPY_PY_TIME_TIME_TIME_NS (1)
#define MICROPY_PY_TIME_INCLUDEFILE "ports/sh/modtime.c"
// TODO: Enable the os module:
// #define MICROPY_PY_UOS (1)
// TODO: Enable other modules
@ -95,6 +98,9 @@ extern const struct _mp_print_t mp_debug_print;
/* Enable alias of u-modules */
#define MICROPY_MODULE_WEAK_LINKS (1)
/* Seed for random module */
#define MICROPY_PY_RANDOM_SEED_INIT_FUNC (rtc_ticks())
/* Command executed automatically after every shell input */
void pe_after_python_exec(
int input_kind, int exec_flags, void *ret_val, int *ret);
@ -103,8 +109,11 @@ void pe_after_python_exec(
/* Command executed regularly during execution */
extern void pe_draw(void);
extern widget_shell *pe_shell;
extern void pe_dupdate(void);
extern bool pe_dupdate_scheduled;
#define MICROPY_VM_HOOK_LOOP \
{ if(pe_shell->widget.update) pe_draw(); }
{ if(pe_shell->widget.update) pe_draw(); \
if(pe_dupdate_scheduled) pe_dupdate(); }
/* extra built in names to add to the global namespace
#define MICROPY_PORT_BUILTINS \
@ -119,3 +128,4 @@ typedef long mp_off_t;
#define MICROPY_HW_MCU_NAME "sh-4a"
#define MP_STATE_PORT MP_STATE_VM

179
ports/sh/numworks/modion.c Normal file
View File

@ -0,0 +1,179 @@
//---------------------------------------------------------------------------//
// ____ PythonExtra //
//.-'`_ o `;__, A community port of MicroPython for CASIO calculators. //
//.-'` `---` ' License: MIT (except some files; see LICENSE) //
//---------------------------------------------------------------------------//
// pe.io: Compatibility module for NumWorks Ion library
#include "py/objtuple.h"
#include "py/runtime.h"
#include <gint/keyboard.h>
#include <stdio.h>
#include <stdlib.h>
/* BEGINING OF KEY TRANSLATION */
// the following table aims at providing a keymap for NW on Casio
// line that are commented correspond to keys that are similar (with exact same
// name) between NW and Casio
#define KEY_LEFT 0
#define KEY_UP 1
#define KEY_DOWN 2
#define KEY_RIGHT 3
#define KEY_OK 4
#define KEY_BACK 5
#define KEY_HOME 6
#define KEY_ONOFF 7
/* -- */
#define KEY_SHIFT 12
#define KEY_ALPHA 13
#define KEY_XNT 14
#define KEY_VAR 15
#define KEY_TOOLBOX 16
#define KEY_BACKSPACE 17
#define KEY_EXP 18
#define KEY_LN 19
#define KEY_LOG 20
#define KEY_IMAGINARY 21
#define KEY_COMMA 22
#define KEY_POWER 23
#define KEY_SINE 24
#define KEY_COSINE 25
#define KEY_TANGENT 26
#define KEY_PI 27
#define KEY_SQRT 28
#define KEY_SQUARE 29
#define KEY_SEVEN 30
#define KEY_EIGHT 31
#define KEY_NINE 32
#define KEY_LEFTPARENTHESIS 33
#define KEY_RIGHTPARENTHESIS 34
/* -- */
#define KEY_FOUR 36
#define KEY_FIVE 37
#define KEY_SIX 38
#define KEY_MULTIPLICATION 39
#define KEY_DIVISION 40
/* -- */
#define KEY_ONE 42
#define KEY_TWO 43
#define KEY_THREE 44
#define KEY_PLUS 45
#define KEY_MINUS 46
/* -- */
#define KEY_ZERO 48
#define KEY_DOT 49
#define KEY_EE 50
#define KEY_ANS 51
#define KEY_EXE 52
int KeyTranslationMap[ 53 ] = { 0x85, 0x86, 0x75, 0x76, 0x91, // gint LEFT, UP, DOWN, RIGHT, F1
0x74, 0x84, 0x07, -1, -1, // gint EXIT, MENU, ACON, __, __
-1, -1, 0x81, 0x71, 0x61, // gint __, __, SHIFT, ALPHA, XOT
0x83, 0x82, 0x44, 0x13, 0x63, // gint VARS, OPTN, DEL, EXP, LN
0x62, 0x92, 0x55, 0x73, 0x64, // gint LOG, F2, COMMA, POWER, SIN
0x65, 0x66, 0x93, 0x94, 0x72, // gint COS, TAN, F3, F4, SQUARE
0x41, 0x42, 0x43, 0x53, 0x54, // gint 7, 8, 9, LEFP, RIGHTP
-1, 0x31, 0x32, 0x33, 0x34, // gint __, 4, 5, 6, MUL
0x35, -1, 0x21, 0x22, 0x23, // gint DIV, __, 1, 2, 3
0x24, 0x25, -1, 0x11, 0x12, // gint ADD, SUB, __, 0, DOT
0x95, 0x14, 0x15 }; // gint F5, NEG, EXE
/* END OF KEY TRANSLATION */
STATIC mp_obj_t ion_keydown(mp_obj_t arg1) {
mp_int_t key = mp_obj_get_int(arg1);
if (key < KEY_LEFT || key > KEY_EXE )
return mp_obj_new_bool(false);
int translatedKey = KeyTranslationMap[ key ];
if (translatedKey==-1)
return mp_obj_new_bool(false);
clearevents();
bool down = keydown(translatedKey) != 0;
return mp_obj_new_bool(down);
}
MP_DEFINE_CONST_FUN_OBJ_1(ion_keydown_obj, ion_keydown);
/* Module definition */
// Helper: define small integer constant "I" as "I" in the module
#define INT(I) \
{ MP_ROM_QSTR(MP_QSTR_##I), MP_OBJ_NEW_SMALL_INT(I) }
STATIC const mp_rom_map_elem_t ion_module_globals_table[] = {
{MP_OBJ_NEW_QSTR(MP_QSTR___name__), MP_OBJ_NEW_QSTR(MP_QSTR_ion)},
/*Numworks keycodes */
/* BE CAREFUL THERE ARE MISSING SLOTS */
INT(KEY_LEFT), // value 0
INT(KEY_UP),
INT(KEY_DOWN),
INT(KEY_RIGHT),
INT(KEY_OK),
INT(KEY_BACK),
INT(KEY_HOME),
INT(KEY_ONOFF), // value 7
INT(KEY_SHIFT), // value 12
INT(KEY_ALPHA),
INT(KEY_XNT),
INT(KEY_VAR),
INT(KEY_TOOLBOX),
INT(KEY_BACKSPACE),
INT(KEY_EXP),
INT(KEY_LN),
INT(KEY_LOG),
INT(KEY_IMAGINARY),
INT(KEY_COMMA),
INT(KEY_POWER),
INT(KEY_SINE),
INT(KEY_COSINE),
INT(KEY_TANGENT),
INT(KEY_PI),
INT(KEY_SQRT),
INT(KEY_SQUARE),
INT(KEY_SEVEN),
INT(KEY_EIGHT),
INT(KEY_NINE),
INT(KEY_LEFTPARENTHESIS),
INT(KEY_RIGHTPARENTHESIS), // value 34
INT(KEY_FOUR), // value 36
INT(KEY_FIVE),
INT(KEY_SIX),
INT(KEY_MULTIPLICATION),
INT(KEY_DIVISION), // value 40
INT(KEY_ONE), // value 42
INT(KEY_TWO),
INT(KEY_THREE),
INT(KEY_PLUS),
INT(KEY_MINUS), // value 46
INT(KEY_ZERO), // value 48
INT(KEY_DOT),
INT(KEY_EE),
INT(KEY_ANS),
INT(KEY_EXE), // value 52
{ MP_ROM_QSTR(MP_QSTR_keydown), MP_ROM_PTR(&ion_keydown_obj) }
};
STATIC MP_DEFINE_CONST_DICT(ion_module_globals, ion_module_globals_table);
const mp_obj_module_t ion_module = {
.base = {&mp_type_module},
.globals = (mp_obj_dict_t *)&ion_module_globals,
};
MP_REGISTER_MODULE(MP_QSTR_ion, ion_module);

View File

@ -0,0 +1,349 @@
//---------------------------------------------------------------------------//
// ____ PythonExtra //
//.-'`_ o `;__, A community port of MicroPython for CASIO calculators. //
//.-'` `---` ' License: MIT (except some files; see LICENSE) //
//---------------------------------------------------------------------------//
// pe.modkandinsky: Compatibility module for NumWorks Kandinsky library
#include "py/obj.h"
#include "py/runtime.h"
#include <gint/display.h>
#include <gint/drivers/r61524.h>
#include <gint/timer.h>
#include "../resources.h"
#include <stdlib.h>
#include <string.h>
extern void pe_schedule_dupdate(void);
extern font_t numworks;
static bool is_dwindowed;
#define NW_MAX_X 320
#define NW_MAX_Y 222
/* Parameters used in windowed mode to center the screen of the NW in the fxCG screen*/
#define DELTAXNW ((DWIDTH - NW_MAX_X) / 2) // we center the NW screen on Casio's screen
#define DELTAYNW ((DHEIGHT - NW_MAX_Y) / 2)
/* refresh rate of the screen */
#define TARGET_FPS 20
/* Definition of color on Numworks */
// Data can be found here
// https://github.com/numworks/epsilon/blob/master/escher/include/escher/palette.h
// and here
// https://github.com/numworks/epsilon/blob/master/python/port/port.cpp#L221
#define NW_RGB(r, g, b) (((r >> 3) << 11) | ((g >> 2) << 5) | (b >> 3))
#define NW_BLUE NW_RGB(0x50, 0x75, 0xF2)
#define NW_RED NW_RGB(0xFF, 0x00, 0x0C)
#define NW_GREEN NW_RGB(0x50, 0xC1, 0x02)
#define NW_WHITE NW_RGB(0xFF, 0xFF, 0xFF)
#define NW_BLACK NW_RGB(0x00, 0x00, 0x00)
#define NW_YELLOW NW_RGB(0xFF, 0xCC, 0x7B)
#define NW_PURPLE NW_RGB(0x6E, 0x2D, 0x79)
#define NW_BROWN NW_RGB(0x8D, 0x73, 0x50)
#define NW_CYAN NW_RGB(0x00, 0xFF, 0xFF)
#define NW_ORANGE NW_RGB(0xFE, 0x87, 0x1F)
#define NW_PINK NW_RGB(0xFF, 0xAB, 0xB6)
#define NW_MAGENTA NW_RGB(0xFF, 0x05, 0x88)
#define NW_GRAY NW_RGB(0xA7, 0xA7, 0xA7)
// There are possibly some others to be listed correctly
static int callback(void) {
pe_schedule_dupdate();
return TIMER_CONTINUE;
}
static void set_window(void) {
if(is_dwindowed) {
struct dwindow nw;
nw.left = DELTAXNW;
nw.top = DELTAYNW;
nw.right = 320 + DELTAXNW;
nw.bottom = 222 + DELTAYNW;
dwindow_set(nw);
}
}
static void reset_window(void)
{
if(is_dwindowed) {
struct dwindow nw;
nw.left = 0;
nw.top = 0;
nw.right = DWIDTH;
nw.bottom = DHEIGHT;
dwindow_set(nw);
}
}
static mp_obj_t Kandinsky_make_color(color_t color) {
int r, g, b;
r = (color >> 8) & 0xf8;
g = (color >> 3) & 0xfc;
b = (color << 3) & 0xfc;
mp_obj_t items[3] = {
MP_OBJ_NEW_SMALL_INT(r),
MP_OBJ_NEW_SMALL_INT(g),
MP_OBJ_NEW_SMALL_INT(b),
};
return mp_obj_new_tuple(3, items);
}
static mp_obj_t Kandinsky_init(void) {
void pe_enter_graphics_mode(void);
pe_enter_graphics_mode();
dclear(NW_WHITE);
/* Start in windowed 320x222 windowed mode */
is_dwindowed = true;
/* Allocate an auto-freed timer until the end of the program's execution */
int t = pe_timer_configure(TIMER_ANY, (1000000/TARGET_FPS),
GINT_CALL(callback), false);
if (t >= 0)
timer_start(t);
return mp_const_none;
}
static mp_obj_t Kandinsky_color(size_t n, mp_obj_t const *args) {
int r = mp_obj_get_int(args[0]);
int g = mp_obj_get_int(args[1]);
int b = mp_obj_get_int(args[2]);
int color = NW_RGB(r, g, b);
return mp_obj_new_int(color);
}
// TODO: Use qstrs here
int Internal_Get_Color_From_String(int qstr) {
if (qstr == MP_QSTR_red || qstr == MP_QSTR_r)
return NW_RED;
else if (qstr == MP_QSTR_green || qstr == MP_QSTR_g)
return NW_GREEN;
else if (qstr == MP_QSTR_blue || qstr == MP_QSTR_b)
return NW_BLUE;
else if (qstr == MP_QSTR_black || qstr == MP_QSTR_k)
return NW_BLACK;
else if (qstr == MP_QSTR_white || qstr == MP_QSTR_w)
return NW_WHITE;
else if (qstr == MP_QSTR_yellow || qstr == MP_QSTR_y)
return NW_YELLOW;
else if (qstr == MP_QSTR_pink)
return NW_PINK;
else if (qstr == MP_QSTR_magenta)
return NW_MAGENTA;
else if (qstr == MP_QSTR_grey || qstr == MP_QSTR_gray)
return NW_GRAY;
else if (qstr == MP_QSTR_purple)
return NW_PURPLE;
else if (qstr == MP_QSTR_orange)
return NW_ORANGE;
else if (qstr == MP_QSTR_cyan)
return NW_CYAN;
else if (qstr == MP_QSTR_brown)
return NW_BROWN;
else
return C_NONE;
}
int Internal_Treat_Color(mp_obj_t color) {
const mp_obj_type_t *type = mp_obj_get_type(color);
if (type == &mp_type_str)
return Internal_Get_Color_From_String(mp_obj_str_get_qstr(color));
else if (type == &mp_type_int)
return mp_obj_get_int(color);
else if (type == &mp_type_tuple) {
size_t tuple_len;
mp_obj_t *items;
mp_obj_tuple_get(color, &tuple_len, &items);
int r = mp_obj_get_int(items[0]);
int g = mp_obj_get_int(items[1]);
int b = mp_obj_get_int(items[2]);
return NW_RGB(r, g, b);
}
else
return NW_BLACK;
}
static mp_obj_t Kandinsky_fill_rect(size_t n, mp_obj_t const *args) {
int x = mp_obj_get_int(args[0]) + DELTAXNW;
int y = mp_obj_get_int(args[1]) + DELTAYNW;
int w = mp_obj_get_int(args[2]);
int h = mp_obj_get_int(args[3]);
int color = Internal_Treat_Color(args[4]);
set_window();
drect(x, y, x + w - 1, y + h - 1, color);
reset_window();
return mp_const_none;
}
static mp_obj_t Kandinsky_set_pixel(size_t n, mp_obj_t const *args) {
int x = mp_obj_get_int(args[0]) + DELTAXNW;
int y = mp_obj_get_int(args[1]) + DELTAYNW;
int color;
if (n == 3)
color = Internal_Treat_Color(args[2]);
else
color = NW_BLACK;
/* TODO: Terribly inefficient */
set_window();
dpixel(x, y, color);
reset_window();
return mp_const_none;
}
static mp_obj_t Kandinsky_get_pixel(mp_obj_t _x, mp_obj_t _y) {
int x = mp_obj_get_int(_x) + DELTAXNW;
int y = mp_obj_get_int(_y) + DELTAYNW;
if ((!is_dwindowed && x >= 0 && x < DWIDTH && y >= 0 && y < DHEIGHT) || (is_dwindowed && x >= 0 && x < NW_MAX_X && y >= 0 && y < NW_MAX_Y)) {
color_t color = gint_vram[DWIDTH * y + x];
return Kandinsky_make_color(color);
}
return Kandinsky_make_color(0x0000);
}
static mp_obj_t Kandinsky_draw_string(size_t n, mp_obj_t const *args) {
int x = mp_obj_get_int(args[1]) + DELTAXNW + 1; // values used to adjust the visual result as per actual NW
int y = mp_obj_get_int(args[2]) + DELTAYNW + 2; // values used to adjust the visual result as per actual NW
size_t text_len;
char const *text = mp_obj_str_get_data(args[0], &text_len);
color_t colortext = NW_BLACK;
if (n >= 4) {
colortext = Internal_Treat_Color(args[3]);
}
color_t colorback = C_NONE;
if (n >= 5) {
colorback = Internal_Treat_Color(args[4]);
}
font_t const *old_font = dfont(&numworks);
set_window();
int u = 0;
int v = 0;
for (int l = 0; l < (int)text_len; l++) {
if (text[l] == '\n') {
u = 0;
v += 16;
} else {
/* The following test is for support of unicode characters that are encoded on 1 char or more */
/* we need to pass multiple chars to dtext to take care of unicode encoding */
if(((unsigned char) text[l]) <= 0x7F){
drect(x + u - 1, y + v - 1, x + u + 9, y + v + 15, colorback);
dtext_opt(x + u, y + v, colortext, C_NONE, DTEXT_LEFT, DTEXT_TOP, &text[l], 1);
u += 10;
}
else if(((unsigned char) text[l]) >= 0x80 && ((unsigned char) text[l]) <= 0xDF){
drect(x + u - 1, y + v - 1, x + u + 9, y + v + 15, colorback);
dtext_opt(x + u, y + v, colortext, C_NONE, DTEXT_LEFT, DTEXT_TOP, &text[l], 2);
u += 10;
l+=1;
}
else if(((unsigned char) text[l]) >= 0xE0 && ((unsigned char) text[l]) <= 0xEF){
drect(x + u - 1, y + v - 1, x + u + 9, y + v + 15, colorback);
dtext_opt(x + u, y + v, colortext, C_NONE, DTEXT_LEFT, DTEXT_TOP, &text[l], 3);
u += 10;
l+=2;
}
else if(((unsigned char) text[l]) >= 0xF0 && ((unsigned char) text[l]) <= 0xF7){
drect(x + u - 1, y + v - 1, x + u + 9, y + v + 15, colorback);
dtext_opt(x + u, y + v, colortext, C_NONE, DTEXT_LEFT, DTEXT_TOP, &text[l], 4);
u += 10;
l+=3;
}
}
}
reset_window();
dfont(old_font);
return mp_const_none;
}
static mp_obj_t Kandinsky_CGEXT_Enable_Wide_Screen( void ) {
is_dwindowed = false; // we mark as not windowed
return mp_const_none;
}
static mp_obj_t Kandinsky_CGEXT_Disable_Wide_Screen( void ) {
is_dwindowed = true; // we mark as windowed
return mp_const_none;
}
static mp_obj_t Kandinsky_CGEXT_Is_Wide_Screen_Enabled( void ) {
return mp_obj_new_bool( is_dwindowed );
}
static mp_obj_t Kandinsky_CGEXT_Set_Margin_Color( mp_obj_t color ) {
color_t colorside = NW_BLACK;
colorside = Internal_Treat_Color(color);
dclear(colorside);
return mp_obj_new_bool( is_dwindowed );
}
/* Extension of Kandinsky for fxCG - all names starting with "CGEXT_" */
MP_DEFINE_CONST_FUN_OBJ_0(Kandinsky_CGEXT_Enable_Wide_Screen_obj, Kandinsky_CGEXT_Enable_Wide_Screen);
MP_DEFINE_CONST_FUN_OBJ_0(Kandinsky_CGEXT_Disable_Wide_Screen_obj, Kandinsky_CGEXT_Disable_Wide_Screen);
MP_DEFINE_CONST_FUN_OBJ_0(Kandinsky_CGEXT_Is_Wide_Screen_Enabled_obj, Kandinsky_CGEXT_Is_Wide_Screen_Enabled);
MP_DEFINE_CONST_FUN_OBJ_1(Kandinsky_CGEXT_Set_Margin_Color_obj, Kandinsky_CGEXT_Set_Margin_Color);
/* Standard Kandinsky function as per Numworks specification */
MP_DEFINE_CONST_FUN_OBJ_0(Kandinsky_init_obj, Kandinsky_init);
MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(Kandinsky_set_pixel_obj, 3, 3, Kandinsky_set_pixel);
MP_DEFINE_CONST_FUN_OBJ_2(Kandinsky_get_pixel_obj, Kandinsky_get_pixel);
MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(Kandinsky_draw_string_obj, 3, 5, Kandinsky_draw_string);
MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(Kandinsky_fill_rect_obj, 5, 5, Kandinsky_fill_rect);
MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(Kandinsky_color_obj, 3, 3, Kandinsky_color);
STATIC const mp_rom_map_elem_t kandinsky_module_globals_table[] = {
{MP_OBJ_NEW_QSTR(MP_QSTR___name__), MP_OBJ_NEW_QSTR(MP_QSTR_kandinsky)},
{MP_ROM_QSTR(MP_QSTR___init__), MP_ROM_PTR(&Kandinsky_init_obj)},
{MP_ROM_QSTR(MP_QSTR_fill_rect), MP_ROM_PTR(&Kandinsky_fill_rect_obj)},
{MP_ROM_QSTR(MP_QSTR_color), MP_ROM_PTR(&Kandinsky_color_obj)},
{MP_ROM_QSTR(MP_QSTR_set_pixel), MP_ROM_PTR(&Kandinsky_set_pixel_obj)},
{MP_ROM_QSTR(MP_QSTR_get_pixel), MP_ROM_PTR(&Kandinsky_get_pixel_obj)},
{MP_ROM_QSTR(MP_QSTR_draw_string), MP_ROM_PTR(&Kandinsky_draw_string_obj)},
{MP_ROM_QSTR(MP_QSTR_CGEXT_Enable_Wide_Screen), MP_ROM_PTR(&Kandinsky_CGEXT_Enable_Wide_Screen_obj)},
{MP_ROM_QSTR(MP_QSTR_CGEXT_Disable_Wide_Screen), MP_ROM_PTR(&Kandinsky_CGEXT_Disable_Wide_Screen_obj)},
{MP_ROM_QSTR(MP_QSTR_CGEXT_Is_Wide_Screen_Enabled), MP_ROM_PTR(&Kandinsky_CGEXT_Is_Wide_Screen_Enabled_obj)},
{MP_ROM_QSTR(MP_QSTR_CGEXT_Set_Margin_Color), MP_ROM_PTR(&Kandinsky_CGEXT_Set_Margin_Color_obj)},
};
STATIC MP_DEFINE_CONST_DICT(kandinsky_module_globals,
kandinsky_module_globals_table);
const mp_obj_module_t kandinsky_module = {
.base = {&mp_type_module},
.globals = (mp_obj_dict_t *)&kandinsky_module_globals,
};
MP_REGISTER_MODULE(MP_QSTR_kandinsky, kandinsky_module);

7
ports/sh/objgintfont.c Normal file
View File

@ -0,0 +1,7 @@
/* gint.font(name, flags, line_heignt, data_height, block_count, glyph_count,
char_spacing, blocks, glyphs, ...)
Fixed-width fonts:
... width, storage_size
Proportional fonts:
... glyph_index, glyph_width */

372
ports/sh/objgintimage.c Normal file
View File

@ -0,0 +1,372 @@
//---------------------------------------------------------------------------//
// ____ PythonExtra //
//.-'`_ o `;__, A community port of MicroPython for CASIO calculators. //
//.-'` `---` ' License: MIT (except some files; see LICENSE) //
//---------------------------------------------------------------------------//
#include "objgintimage.h"
#include "py/runtime.h"
#include "py/objarray.h"
#include <string.h>
STATIC mp_obj_t ptr_to_memoryview(void *ptr, int size, int typecode, bool rw)
{
if(ptr == NULL)
return mp_const_none;
if(rw)
typecode |= MP_OBJ_ARRAY_TYPECODE_FLAG_RW;
return mp_obj_new_memoryview(typecode, size, ptr);
}
#ifdef FX9860G
/* Heuristic to check if the image is read-only or not */
STATIC bool pointer_is_ro(void *data)
{
uintptr_t addr = (uintptr_t)data;
return !addr || (addr >= 0x00300000 && addr <= 0x00500000);
}
STATIC int image_data_size(int profile, int width, int height)
{
int layers = image_layer_count(profile);
int longwords = (width + 31) >> 5;
return layers * longwords * height * 4;
}
/* gint.image(profile, width, height, data)
Keyword labels are allowed but the order must remain the same. */
STATIC mp_obj_t image_make_new(const mp_obj_type_t *type, size_t n_args,
size_t n_kw, const mp_obj_t *args)
{
enum { ARG_profile, ARG_width, ARG_height, ARG_data };
static mp_arg_t const allowed_args[] = {
{ MP_QSTR_profile, MP_ARG_INT | MP_ARG_REQUIRED,
{.u_rom_obj = MP_ROM_NONE} },
{ MP_QSTR_width, MP_ARG_INT | MP_ARG_REQUIRED,
{.u_rom_obj = MP_ROM_NONE} },
{ MP_QSTR_height, MP_ARG_INT | MP_ARG_REQUIRED,
{.u_rom_obj = MP_ROM_NONE} },
{ MP_QSTR_data, MP_ARG_OBJ | MP_ARG_REQUIRED,
{.u_rom_obj = MP_ROM_NONE} },
};
mp_arg_val_t vals[MP_ARRAY_SIZE(allowed_args)];
mp_arg_parse_all_kw_array(n_args, n_kw, args, MP_ARRAY_SIZE(allowed_args),
allowed_args, vals);
int profile = vals[ARG_profile].u_int;
int width = vals[ARG_width].u_int;
int height = vals[ARG_height].u_int;
mp_obj_t data = vals[ARG_data].u_obj;
return objgintimage_make(type, profile, width, height, data);
}
mp_obj_t objgintimage_make(const mp_obj_type_t *type, int profile, int width,
int height, mp_obj_t data)
{
/* The supplied object must implement the buffer protocol */
mp_buffer_info_t buf;
if(!mp_get_buffer(data, &buf, MP_BUFFER_READ))
mp_raise_TypeError("data must be a buffer object");
/* Size and bounds checks */
int data_len = MP_OBJ_SMALL_INT_VALUE(mp_obj_len(data));
if(width <= 0 || height <= 0)
mp_raise_ValueError("image width/height must be >0");
if(profile < 0 || profile >= 8)
mp_raise_ValueError("invalid image profile");
if(data_len < image_data_size(profile, width, height))
mp_raise_ValueError("data len() should be >= 4*ceil(w/32)*h*layers");
/* Construct image! */
mp_obj_gintimage_t *self = mp_obj_malloc(mp_obj_gintimage_t, type);
self->img.profile = profile;
self->img.width = width;
self->img.height = height;
self->img.data = NULL;
self->data = data;
return MP_OBJ_FROM_PTR(self);
}
mp_obj_t objgintimage_make_from_gint_image(bopti_image_t const *img)
{
/* The original image is assumed to be valid. */
mp_obj_gintimage_t *self = mp_obj_malloc(mp_obj_gintimage_t,
&mp_type_gintimage);
memcpy(&self->img, img, sizeof *img);
int data_size = image_data_size(img->profile, img->width, img->height);
bool rw = !pointer_is_ro(img->data);
self->data = ptr_to_memoryview(img->data, data_size, 'B', rw);
return MP_OBJ_FROM_PTR(self);
}
STATIC void image_print(mp_print_t const *print, mp_obj_t self_in,
mp_print_kind_t kind)
{
(void)kind;
mp_obj_gintimage_t *self = MP_OBJ_TO_PTR(self_in);
char const *data_str =
self->data != mp_const_none ? "py" :
pointer_is_ro(self->img.data) ? "ro" : "rw";
static char const * const fmt_names[] = {
"mono", "mono_alpha", "gray", "gray_alpha"
};
char const *format_str =
(self->img.profile < 4) ? fmt_names[self->img.profile] : "?";
mp_printf(print, "<%s image (%d layers), %dx%d (%s, %d bytes)>",
format_str, image_layer_count(self->img.profile), self->img.width,
self->img.height, data_str,
image_data_size(self->img.profile, self->img.width, self->img.height));
}
STATIC void image_attr(mp_obj_t self_in, qstr attr, mp_obj_t *dest)
{
if(dest[0] == MP_OBJ_NULL) {
mp_obj_gintimage_t *self = MP_OBJ_TO_PTR(self_in);
if(attr == MP_QSTR_format)
dest[0] = MP_OBJ_NEW_SMALL_INT(self->img.profile);
else if(attr == MP_QSTR_width)
dest[0] = MP_OBJ_NEW_SMALL_INT(self->img.width);
else if(attr == MP_QSTR_height)
dest[0] = MP_OBJ_NEW_SMALL_INT(self->img.height);
else if(attr == MP_QSTR_data)
dest[0] = self->data;
}
else {
mp_raise_msg(&mp_type_AttributeError,
MP_ERROR_TEXT("gint.image doesn't support changing attributes"));
}
}
#endif /* FX9860G */
#ifdef FXCG50
/* gint.image(format, color_count, width, height, stride, data, palette)
Keyword labels are allowed but the order must remain the same. */
STATIC mp_obj_t image_make_new(const mp_obj_type_t *type, size_t n_args,
size_t n_kw, const mp_obj_t *args)
{
enum { ARG_format, ARG_color_count, ARG_width, ARG_height, ARG_stride,
ARG_data, ARG_palette };
static mp_arg_t const allowed_args[] = {
{ MP_QSTR_format, MP_ARG_INT | MP_ARG_REQUIRED,
{.u_rom_obj = MP_ROM_NONE} },
{ MP_QSTR_color_count, MP_ARG_INT | MP_ARG_REQUIRED,
{.u_rom_obj = MP_ROM_NONE} },
{ MP_QSTR_width, MP_ARG_INT | MP_ARG_REQUIRED,
{.u_rom_obj = MP_ROM_NONE} },
{ MP_QSTR_height, MP_ARG_INT | MP_ARG_REQUIRED,
{.u_rom_obj = MP_ROM_NONE} },
{ MP_QSTR_stride, MP_ARG_INT | MP_ARG_REQUIRED,
{.u_rom_obj = MP_ROM_NONE} },
{ MP_QSTR_data, MP_ARG_OBJ | MP_ARG_REQUIRED,
{.u_rom_obj = MP_ROM_NONE} },
{ MP_QSTR_palette, MP_ARG_OBJ,
{.u_rom_obj = MP_ROM_NONE} },
};
mp_arg_val_t vals[MP_ARRAY_SIZE(allowed_args)];
mp_arg_parse_all_kw_array(n_args, n_kw, args, MP_ARRAY_SIZE(allowed_args),
allowed_args, vals);
int format = vals[ARG_format].u_int;
int color_count = vals[ARG_color_count].u_int;
int width = vals[ARG_width].u_int;
int height = vals[ARG_height].u_int;
int stride = vals[ARG_stride].u_int;
mp_obj_t data = vals[ARG_data].u_obj;
mp_obj_t palette = vals[ARG_palette].u_obj;
return objgintimage_make(type, format, color_count, width, height, stride,
data, palette);
}
mp_obj_t objgintimage_make(const mp_obj_type_t *type, int format,
int color_count, int width, int height, int stride, mp_obj_t data,
mp_obj_t palette)
{
bool has_palette = palette != mp_const_none;
/* Type checks */
mp_buffer_info_t buf;
if(!mp_get_buffer(data, &buf, MP_BUFFER_READ))
mp_raise_TypeError("data must be a buffer object");
if(palette != mp_const_none && !mp_get_buffer(palette,&buf,MP_BUFFER_READ))
mp_raise_TypeError("palette must be None or a buffer object");
/* Size and bounds checks */
int data_len = MP_OBJ_SMALL_INT_VALUE(mp_obj_len(data));
int palette_len =
has_palette ? MP_OBJ_SMALL_INT_VALUE(mp_obj_len(palette)) : 0;
if(width <= 0 || height <= 0)
mp_raise_ValueError("image width/height must be >0");
if(format < 0 || format >= 7 || format == IMAGE_DEPRECATED_P8)
mp_raise_ValueError("invalid image format");
if(data_len < stride * height)
mp_raise_ValueError("data len() should be >= stride * height");
if(IMAGE_IS_RGB16(format) && (color_count > 0 || has_palette))
mp_raise_ValueError("RGB format should have 0 colors and no palette");
if(IMAGE_IS_P8(format) &&
(color_count < 1 || color_count > 256 || !has_palette))
mp_raise_ValueError("P8 format should have palette and 1..256 colors");
if(IMAGE_IS_P4(format) && (color_count != 16 || !has_palette))
mp_raise_ValueError("P4 format should have palette and 16 colors");
if(has_palette && palette_len < 2 * color_count)
mp_raise_ValueError("palette len() should be >= 2*color_count");
/* Construct image! */
mp_obj_gintimage_t *self = mp_obj_malloc(mp_obj_gintimage_t, type);
self->img.format = format;
self->img.color_count = color_count;
self->img.width = width;
self->img.height = height;
self->img.stride = stride;
self->img.data = NULL;
self->img.palette = NULL;
self->data = data;
self->palette = palette;
return MP_OBJ_FROM_PTR(self);
}
mp_obj_t objgintimage_make_from_gint_image(bopti_image_t const *img)
{
/* The original image is assumed to be valid. */
mp_obj_gintimage_t *self = mp_obj_malloc(mp_obj_gintimage_t,
&mp_type_gintimage);
memcpy(&self->img, img, sizeof *img);
int data_size = img->stride * img->height;
int typecode = 'B';
if(IMAGE_IS_RGB16(img->format)) {
// TODO: assert stride even
data_size >>= 1;
typecode = 'H';
}
self->data = ptr_to_memoryview(img->data, data_size, typecode,
(img->flags & IMAGE_FLAGS_DATA_ALLOC) != 0);
self->palette = ptr_to_memoryview((void *)img->palette,
img->color_count, 'H',
(img->flags & IMAGE_FLAGS_PALETTE_ALLOC) != 0);
return MP_OBJ_FROM_PTR(self);
}
STATIC char const *flag_string(int ro, int alloc)
{
static char const *flag_names[] = {
"rw", "ro", "alloc-rw", "alloc-ro",
};
return flag_names[!!ro + 2 * !!alloc];
}
STATIC void image_print(mp_print_t const *print, mp_obj_t self_in,
mp_print_kind_t kind)
{
(void)kind;
mp_obj_gintimage_t *self = MP_OBJ_TO_PTR(self_in);
int f = self->img.flags;
char const *data_str = self->data != mp_const_none ? "py" :
flag_string(f & IMAGE_FLAGS_DATA_RO, f & IMAGE_FLAGS_DATA_ALLOC);
char const *palette_str = self->palette != mp_const_none ? "py" :
flag_string(f & IMAGE_FLAGS_PALETTE_RO, f & IMAGE_FLAGS_PALETTE_ALLOC);
static char const * const fmt_names[] = {
"RGB565", "RGB565A", "LEGACY_P8",
"P4_RGB565A", "P8_RGB565", "P8_RGB565A", "P4_RGB565",
};
char const *format_str =
(self->img.format < 7) ? fmt_names[self->img.format] : "?";
mp_printf(print, "<%s image, %dx%d (%s)",
format_str, self->img.width, self->img.height, data_str);
if(IMAGE_IS_INDEXED(self->img.format))
mp_printf(print, ", %d colors (%s)",
self->img.color_count, palette_str);
mp_printf(print, ">");
}
STATIC void image_attr(mp_obj_t self_in, qstr attr, mp_obj_t *dest)
{
if(dest[0] == MP_OBJ_NULL) {
mp_obj_gintimage_t *self = MP_OBJ_TO_PTR(self_in);
if(attr == MP_QSTR_format)
dest[0] = MP_OBJ_NEW_SMALL_INT(self->img.format);
else if(attr == MP_QSTR_flags)
dest[0] = MP_OBJ_NEW_SMALL_INT(self->img.flags);
else if(attr == MP_QSTR_color_count)
dest[0] = MP_OBJ_NEW_SMALL_INT(self->img.color_count);
else if(attr == MP_QSTR_width)
dest[0] = MP_OBJ_NEW_SMALL_INT(self->img.width);
else if(attr == MP_QSTR_height)
dest[0] = MP_OBJ_NEW_SMALL_INT(self->img.height);
else if(attr == MP_QSTR_stride)
dest[0] = MP_OBJ_NEW_SMALL_INT(self->img.stride);
else if(attr == MP_QSTR_data)
dest[0] = self->data;
else if(attr == MP_QSTR_palette)
dest[0] = self->palette;
}
else {
mp_raise_msg(&mp_type_AttributeError,
MP_ERROR_TEXT("gint.image doesn't support changing attributes"));
}
}
#endif /* FXCG50 */
void objgintimage_get(mp_obj_t self_in, bopti_image_t *img)
{
if(!mp_obj_is_type(self_in, &mp_type_gintimage))
mp_raise_TypeError(MP_ERROR_TEXT("image must be a gint.image"));
mp_obj_gintimage_t *self = MP_OBJ_TO_PTR(self_in);
*img = self->img;
img->data = NULL;
if(self->data != mp_const_none) {
mp_buffer_info_t buf;
if(!mp_get_buffer(self->data, &buf, MP_BUFFER_READ))
mp_raise_TypeError("data not a buffer object?!");
img->data = buf.buf;
}
#ifdef FXCG50
img->palette = NULL;
if(self->palette != mp_const_none) {
mp_buffer_info_t buf;
if(!mp_get_buffer(self->palette, &buf, MP_BUFFER_READ))
mp_raise_TypeError("palette not a buffer object?!");
img->palette = buf.buf;
}
#endif
}
MP_DEFINE_CONST_OBJ_TYPE(
mp_type_gintimage,
MP_QSTR_image,
MP_TYPE_FLAG_NONE,
make_new, image_make_new,
print, image_print,
attr, image_attr
);

55
ports/sh/objgintimage.h Normal file
View File

@ -0,0 +1,55 @@
//---------------------------------------------------------------------------//
// ____ PythonExtra //
//.-'`_ o `;__, A community port of MicroPython for CASIO calculators. //
//.-'` `---` ' License: MIT (except some files; see LICENSE) //
//---------------------------------------------------------------------------//
// pe.objgintimage: Type of gint images for rendering and editing
#ifndef __PYTHONEXTRA_OBJGINTIMAGE_H
#define __PYTHONEXTRA_OBJGINTIMAGE_H
#include "py/obj.h"
#include <gint/display.h>
#ifdef FXCG50
#include <gint/image.h>
#endif
extern const mp_obj_type_t mp_type_gintimage;
/* A raw gint image with its pointers extracted into Python objects, allowing
manipulation through bytes() and bytearray() methods. The base image is
[img]. The members [data] and [palette] (which must be bytes, bytearray or
None) act as overrides for the corresponding fields of [img], which are
considered garbage/scratch and is constantly updated from the Python objects
before using the image.
Particular care should be given to not manipulating bytes and bytearrays in
ways that cause reallocation, especially when memory is scarce. */
typedef struct _mp_obj_gintimage_t {
mp_obj_base_t base;
bopti_image_t img;
mp_obj_t data;
#ifdef FXCG50
mp_obj_t palette;
#endif
} mp_obj_gintimage_t;
/* Project a gint image object into a standard bopti image structure for use in
C-API image functions. */
void objgintimage_get(mp_obj_t self_in, bopti_image_t *img);
/* Build a gint image object from a valid bopti image structure. */
mp_obj_t objgintimage_make_from_gint_image(bopti_image_t const *img);
/* Lower-level image object constructor. */
#if defined(FX9860G)
mp_obj_t objgintimage_make(const mp_obj_type_t *type, int profile, int width,
int height, mp_obj_t data);
#elif defined(FXCG50)
mp_obj_t objgintimage_make(const mp_obj_type_t *type, int format,
int color_count, int width, int height, int stride, mp_obj_t data,
mp_obj_t palette);
#endif
#endif /* __PYTHONEXTRA_OBJGINTIMAGE_H */

View File

@ -194,11 +194,20 @@ STATIC int parse_compile_execute(const void *source, mp_parse_input_kind_t input
void pyexec_event_repl_init(void) {
MP_STATE_VM(repl_line) = vstr_new(32);
#ifdef FX9860G
mp_hal_stdout_tx_str("MicroPython " MICROPY_GIT_TAG "\n");
#else
mp_hal_stdout_tx_str(MICROPY_BANNER_NAME_AND_VERSION);
mp_hal_stdout_tx_str("; " MICROPY_BANNER_MACHINE);
mp_hal_stdout_tx_str("\r\n");
mp_hal_stdout_tx_str("\n");
#endif
#if MICROPY_PY_BUILTINS_HELP
mp_hal_stdout_tx_str("Type \"help()\" for more information.\r\n");
#ifdef FX9860G
mp_hal_stdout_tx_str("Type \"help()\" for info.\n");
#else
mp_hal_stdout_tx_str("Type \"help()\" for more information.\n");
#endif
#endif
vstr_reset(MP_STATE_VM(repl_line));
}

69
ports/sh/resources.c Normal file
View File

@ -0,0 +1,69 @@
//---------------------------------------------------------------------------//
// ____ PythonExtra //
//.-'`_ o `;__, A community port of MicroPython for CASIO calculators. //
//.-'` `---` ' License: MIT (except some files; see LICENSE) //
//---------------------------------------------------------------------------//
#include "resources.h"
#include "debug.h"
/*** Timers ***/
/* There should be at most 32 timers, which is fine. */
static uint32_t timer_used = 0;
#if PE_DEBUG
static uint32_t timer_warn = 0;
#endif
int pe_timer_configure(int timer, uint64_t delay, gint_call_t call, bool waf)
{
int t = timer_configure(timer, delay, call);
if(t < 0)
return t;
if(t >= 32) {
timer_stop(t);
return -1;
}
timer_used |= (1 << t);
#if PE_DEBUG
if(waf)
timer_warn |= (1 << t);
#endif
return t;
}
void pe_timer_stop(int t)
{
if(t < 0 || t >= 32)
return;
#if PE_DEBUG
if(!(timer_used & (1 << t)))
pe_debug_printf("autofree: bad timer %d\n", t);
timer_warn &= ~(1 << t);
#endif
timer_used &= ~(1 << t);
}
static void pe_timer_autofree(void)
{
for(int t = 0; t < 32; t++) {
if(!(timer_used & (1 << t)))
continue;
#if PE_DEBUG
if(timer_warn & (1 << t))
pe_debug_printf("autofree: timer %d\n", t);
#endif
timer_stop(t);
}
}
/*** Autofree function ***/
void pe_resources_autofree(void)
{
pe_timer_autofree();
}

29
ports/sh/resources.h Normal file
View File

@ -0,0 +1,29 @@
//---------------------------------------------------------------------------//
// ____ PythonExtra //
//.-'`_ o `;__, A community port of MicroPython for CASIO calculators. //
//.-'` `---` ' License: MIT (except some files; see LICENSE) //
//---------------------------------------------------------------------------//
// pe.resources: Utility for allocating auto-freed resources in Python code
//
// This header provides wrapper around gint or other functions that allocate
// resources. Its purpose is to track these resources and allow PythonExtra to
// free them automatically when the execution of Python program finishes. This
// avoids any situation where important resources are exhausted over the course
// of multiple executions.
//
// This header only provides wrappers around allocation and deallocation
// functions; other usual functions can be used directly. Each allocation
// function has an extra boolean parameter indicating whether to raise a
// warning if debug mode is enabled and the resource is auto-freed. It should
// be set to true when there is a mechanism to free the resource, false when
// there isn't and auto-free is the expected outcome.
//---
#include <gint/timer.h>
/* Autofree all resources. */
void pe_resources_autofree(void);
/* gint's timer_configure() and timer_stop() */
int pe_timer_configure(int, uint64_t, gint_call_t, bool warn_autofree);
void pe_timer_stop(int);

112
ports/sh/stredit.c Normal file
View File

@ -0,0 +1,112 @@
//---------------------------------------------------------------------------//
// ____ PythonExtra //
//.-'`_ o `;__, A community port of MicroPython for CASIO calculators. //
//.-'` `---` ' License: MIT (except some files; see LICENSE) //
//---------------------------------------------------------------------------//
#include "stredit.h"
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#include <gint/defs/util.h>
bool stredit_init(stredit_t *ed, int prealloc_size, int reserved_bytes,
int max_size)
{
if(!max_size) {
memset(ed, 0, sizeof *ed);
return true;
}
char *raw = malloc(reserved_bytes + prealloc_size + 1);
if(!raw)
return false;
memset(raw, 0, reserved_bytes);
raw[reserved_bytes] = 0;
ed->raw = raw;
ed->reserved = reserved_bytes;
ed->max_size = max_size;
ed->size = 0;
ed->alloc_size = reserved_bytes + prealloc_size + 1;
ed->prefix = 0;
return true;
}
char *stredit_freeze_and_reset(stredit_t *ed)
{
/* Downsize the allocation if it's larger than needed. */
int size_needed = ed->reserved + ed->size + 1;
if(ed->alloc_size >= size_needed + 4)
ed->raw = realloc(ed->raw, size_needed);
char *raw = ed->raw;
ed->raw = NULL;
stredit_reset(ed);
return raw;
}
void stredit_reset(stredit_t *ed)
{
free(ed->raw);
memset(ed, 0, sizeof *ed);
}
bool stredit_alloc(stredit_t *ed, int n)
{
if(ed->alloc_size >= ed->reserved + n + 1)
return true;
/* Always increase the size by at least 16 so we can insert many times in a
row without worrying about excessive allocations. */
int newsize = max(ed->alloc_size + 16, ed->reserved + n + 1);
char *newraw = realloc(ed->raw, newsize);
if(!newraw)
return false;
ed->raw = newraw;
ed->alloc_size = newsize;
return true;
}
void stredit_set_prefix(stredit_t *ed, int prefix_size)
{
ed->prefix = min(max(0, prefix_size), (int)ed->size);
}
bool stredit_insert(stredit_t *ed, int p, char const *insert_str, int n)
{
if(p < 0 || p > ed->size || ed->size + n > ed->max_size)
return false;
if(!stredit_alloc(ed, ed->size + n))
return false;
char *str = ed->raw + ed->reserved;
/* Move the end of the string (plus the NUL) n bytes forward */
memmove(str + p + n, str + p, ed->size - p + 1);
memcpy(str + p, insert_str, n);
ed->size += n;
return true;
}
int stredit_delete(stredit_t *ed, int p, int n)
{
if(p < ed->prefix) {
int unremovable = ed->prefix - p;
p += unremovable;
n -= unremovable;
}
n = min(n, ed->size - p);
if(n < 0)
return 0;
char *str = ed->raw + ed->reserved;
/* Move the end of the string (plus the NUL) n bytes backwards */
memmove(str + p, str + p + n, ed->size - n - p + 1);
ed->size -= n;
return n;
}

80
ports/sh/stredit.h Normal file
View File

@ -0,0 +1,80 @@
//---------------------------------------------------------------------------//
// ____ PythonExtra //
//.-'`_ o `;__, A community port of MicroPython for CASIO calculators. //
//.-'` `---` ' License: MIT (except some files; see LICENSE) //
//---------------------------------------------------------------------------//
// pe.stredit: String edition utilities
//
// This header implements string edition utilities used in the console to
// handle user input and printing on the last line. To minimize memory
// friction, edited strings are stored in a format compatible with the
// storage format of frozen console lines, i.e. with a few reserved bytes at
// the beginning for metadata.
//---
#ifndef __PYTHONEXTRA_STREDIT_H
#define __PYTHONEXTRA_STREDIT_H
#include <stddef.h>
#include <stdint.h>
#include <stdbool.h>
typedef struct
{
/* The raw buffer has reserved bytes at the beginning. To avoid confusion,
we call this initial pointer "raw" and raw+reserved "str". */
char *raw;
/* Maximum length (text only, not counting the NUL). */
uint16_t max_size;
/* Number of reserved bytes. */
uint16_t reserved;
/* Size of contents (not counting the NUL). */
uint16_t size;
/* Allocated size (always ≥ size+1). */
uint16_t alloc_size;
/* Number of initial characters that can't be edited. */
uint16_t prefix;
} stredit_t;
/* Create a new editable string with at least init_chars characters of content
available. Returns false on error. Previous contents are not freed! If
max_size is 0, clears the stredit without allocating a new string. */
bool stredit_init(stredit_t *ed, int init_chars, int reserved_bytes,
int max_size);
/* Get the data pointer out of an stredit. */
static inline char *stredit_data(stredit_t *ed)
{
return ed->raw + ed->reserved;
}
/* Reset an editable string. This frees and destroys the string. */
void stredit_reset(stredit_t *ed);
/* Finish editing; return the raw pointer (with its ownership) and reset the
editor structure. free() the raw pointer after use. */
char *stredit_freeze_and_reset(stredit_t *ed);
/* Number of bytes that can be added before the size limit is reached. */
static inline int stredit_capacity(stredit_t *ed)
{
return ed->max_size - ed->size;
}
/* Realloc the string to ensure n characters plus a NUL can be written. */
bool stredit_alloc(stredit_t *ed, int n);
/* Set the prefix_size first characters of the strings to not be editable. The
string must already have that many characters printed. This is used in the
console to prevent erasing the prompt. */
void stredit_set_prefix(stredit_t *ed, int prefix_size);
/* Insert n characters at position p. */
bool stredit_insert(stredit_t *ed, int p, char const *str, int n);
/* Remove n characters at position p. Returns the number of characters
actually removed after bounds checking. */
int stredit_delete(stredit_t *ed, int p, int n);
#endif /* __PYTHONEXTRA_STREDIT_H */