├── CHANGELOG ├── MANIFEST ├── Makefile ├── Makefile.PL ├── README.md ├── README.pod ├── Term-Graille-0.10.tar.gz ├── examples ├── animations │ ├── cheetah │ │ ├── cheetah1.png │ │ ├── cheetah2.png │ │ ├── cheetah3.png │ │ ├── cheetah4.png │ │ ├── cheetah5.png │ │ ├── cheetah6.png │ │ ├── cheetah7.png │ │ └── cheetah8.png │ ├── chimp │ │ ├── chimp1.png │ │ ├── chimp10.png │ │ ├── chimp2.png │ │ ├── chimp3.png │ │ ├── chimp4.png │ │ ├── chimp5.png │ │ ├── chimp6.png │ │ ├── chimp7.png │ │ ├── chimp8.png │ │ └── chimp9.png │ └── dragon.png ├── breakout.pl ├── convert2grl.pl ├── demo.pl ├── editor.pl ├── fontDemo.pl ├── fonts │ ├── Anvil Alternate.grf │ ├── Area51 Overwide.grf │ ├── Area51 Serif Bold.grf │ ├── Area51.grf │ ├── Cinema Bold.grf │ ├── Computer.grf │ ├── Coolant.grf │ ├── Everest Bold.grf │ ├── Everest Condensed SemiBold.grf │ ├── Everest Condensed.grf │ ├── Everest.grf │ ├── Mutual.grf │ ├── Reflections Dark.grf │ ├── Reflections.grf │ ├── Tentacle Bold.grf │ ├── Tentacle.grf │ ├── ZX Times.grf │ ├── ZX Venice.grf │ ├── asm2grf.pl │ └── font2grf.pl ├── graillepiano.pl ├── image2grl.pl ├── invaders.pl ├── lineDemo.pl ├── perl2.grl ├── perl2.jpg ├── pulseaudio.pl ├── save.grl ├── spritemaker.pl ├── sprites │ ├── File.spr │ ├── Hash.spr │ ├── Launcher.spr │ ├── bar.spr │ ├── boat.spr │ ├── cow.spr │ ├── crwoned.spr │ ├── default.spb │ ├── die.spr │ ├── dummy.spb │ ├── empty.spr │ ├── forbidden.spr │ ├── game1.spb │ ├── help.spr │ ├── info.spr │ ├── m.spr │ ├── man1.spr │ ├── manhatwalk1.spr │ ├── manhatwalk2.spr │ ├── manhatwalk3.spr │ ├── manhatwalk4.spr │ ├── mm.spr │ ├── oyster.spr │ ├── perly.spr │ ├── photos.spr │ ├── rocket.spr │ ├── spiral.spr │ ├── testmessage.spb │ ├── warn.spr │ └── warn2.spr ├── surmaphoto.png ├── testanimate.pl ├── turtle.pl └── visualiser.pl ├── lib ├── Algorithm │ └── Line │ │ └── Bresenham.pm └── Term │ ├── Graille.pm │ └── Graille │ ├── Audio.pm │ ├── Chart.pm │ ├── Dialog.pm │ ├── Font.pm │ ├── Interact.pm │ ├── Menu.pm │ ├── Selector.pm │ ├── Sprite.pm │ └── Textarea.pm ├── old_files └── Readme.md └── t └── 1.t /CHANGELOG: -------------------------------------------------------------------------------- 1 | CHANGELOG 2 | Term::Graille - Graphical Display in the terminal using UTF8 Braille characters 3 | 4 | 0.10 5 | -Term::Graille Multicharacter scrolling of canvas 6 | -Term::Graille::Interact user interaction module added 7 | -Term::Graille::Menu now used Interact; Term::Graille::IO deprecated to be removed in 0.11 8 | -Term::Graille::Dialog added 9 | -Term::Graille::Selector with limited file selecting capability, 10 | -Term::Graille::Textarea with scrolling editable text 11 | -Term::Graille::Audio added 12 | - examples/editor.pl a terminal editor with a hierachical menu 13 | - examples/piano.pl testing Audio Module 14 | 15 | 16 | 17 | 0.09 18 | -starting Charting module 19 | -Term::Graille::IO user interaction module 20 | -Term::Graille::Menu A modal drop down menu for user interaction 21 | -prototype sprite editor started 22 | 23 | 0.08 24 | - minor bug fixes 25 | - Variable thickness lines added (requires Algorithm::Line::Bresenham 0.151) 26 | 27 | 0.07 28 | - Adds textAt () function allowing heterogenous content on the canvas 29 | - Adds colour to pixel and text and turtle graphics on the $canvas 30 | - Adds a Graille::Font module that imports converts and loads and saves fonts 31 | - loadGrf() moved to the new separate Graille::Font module 32 | 33 | 0.06 34 | - Added print colour("reset") as suggested by https://github.com/jwrightecs 35 | - Added block2braille (convert 2d binary data to 2d braille) 36 | - Added blockBlit (blit 2d blocks of characters) 37 | - Added loadGrf (load blocks e.g. fonts) 38 | - Added importCanvas and exportCanvas (load/save canvas from/to file) 39 | - Added font2grf to examples folder (convert fonts from https://damieng.com/typography/zx-origins to grf) 40 | - Added image2grl (convert image to file loadable by Term::Graille *Needs Image::Magick) 41 | - Added testanimate (convert a folder containingg frames into Graille Animation *Needs Image::Magick) 42 | 43 | 0.05 44 | Failing CpanTesters 45 | - Error in 1.t corected 46 | - Added Import and export images 47 | - Created an image converter (Depends on Image::Magick) 48 | 49 | 0.04 50 | Corrected error hightlighted by https://old.reddit.com/user/tarje 51 | - Perl Version set at v5.10.0 52 | - License file removed 53 | 54 | 0.03 June 2022 55 | First version unploaded to CPAN 56 | 57 | 0.02 June 2022 58 | First version converted form Drawille 59 | -------------------------------------------------------------------------------- /MANIFEST: -------------------------------------------------------------------------------- 1 | CHANGELOG 2 | examples/animations/cheetah/cheetah1.png 3 | examples/animations/cheetah/cheetah2.png 4 | examples/animations/cheetah/cheetah3.png 5 | examples/animations/cheetah/cheetah4.png 6 | examples/animations/cheetah/cheetah5.png 7 | examples/animations/cheetah/cheetah6.png 8 | examples/animations/cheetah/cheetah7.png 9 | examples/animations/cheetah/cheetah8.png 10 | examples/animations/dragon.png 11 | examples/demo.pl 12 | examples/editor.pl 13 | examples/fontDemo.pl 14 | 'examples/fonts/Anvil Alternate.grf' 15 | 'examples/fonts/Area51 Overwide.grf' 16 | 'examples/fonts/Area51 Serif Bold.grf' 17 | examples/fonts/Area51.grf 18 | examples/fonts/asm2grf.pl 19 | 'examples/fonts/Cinema Bold.grf' 20 | examples/fonts/Computer.grf 21 | examples/fonts/Coolant.grf 22 | 'examples/fonts/Everest Bold.grf' 23 | 'examples/fonts/Everest Condensed SemiBold.grf' 24 | 'examples/fonts/Everest Condensed.grf' 25 | examples/fonts/Everest.grf 26 | examples/fonts/Mutual.grf 27 | 'examples/fonts/Reflections Dark.grf' 28 | examples/fonts/Reflections.grf 29 | 'examples/fonts/Tentacle Bold.grf' 30 | examples/fonts/Tentacle.grf 31 | 'examples/fonts/ZX Times.grf' 32 | 'examples/fonts/ZX Venice.grf' 33 | examples/graillepiano.pl 34 | examples/image2grl.pl 35 | examples/lineDemo.pl 36 | examples/perl2.grl 37 | examples/perl2.jpg 38 | examples/pulseaudio.pl 39 | examples/spriteEditor.pl 40 | examples/sprites/bar.spr 41 | examples/sprites/boat.spr 42 | examples/sprites/cow.spr 43 | examples/sprites/crwoned.spr 44 | examples/sprites/default.spb 45 | examples/sprites/die.spr 46 | examples/sprites/dummy.spb 47 | examples/sprites/empty.spr 48 | examples/sprites/File.spr 49 | examples/sprites/forbidden.spr 50 | examples/sprites/game1.spb 51 | examples/sprites/Hash.spr 52 | examples/sprites/help.spr 53 | examples/sprites/info.spr 54 | examples/sprites/Launcher.spr 55 | examples/sprites/m.spr 56 | examples/sprites/man1.spr 57 | examples/sprites/manhatwalk1.spr 58 | examples/sprites/manhatwalk2.spr 59 | examples/sprites/manhatwalk3.spr 60 | examples/sprites/manhatwalk4.spr 61 | examples/sprites/mm.spr 62 | examples/sprites/oyster.spr 63 | examples/sprites/perly.spr 64 | examples/sprites/photos.spr 65 | examples/sprites/rocket.spr 66 | examples/sprites/spiral.spr 67 | examples/sprites/testmessage.spb 68 | examples/sprites/warn.spr 69 | examples/sprites/warn2.spr 70 | examples/testanimate.pl 71 | examples/testBeep1.pl 72 | examples/visualiser.pl 73 | lib/Term/Graille.pm 74 | lib/Term/Graille/Audio.pm 75 | lib/Term/Graille/Chart.pm 76 | lib/Term/Graille/Dialog.pm 77 | lib/Term/Graille/Font.pm 78 | lib/Term/Graille/Interact.pm 79 | lib/Term/Graille/Menu.pm 80 | lib/Term/Graille/Selector.pm 81 | lib/Term/Graille/Textarea.pm 82 | Makefile.PL 83 | MANIFEST This list of files 84 | t/1.t 85 | -------------------------------------------------------------------------------- /Makefile.PL: -------------------------------------------------------------------------------- 1 | use 5.008004; 2 | use ExtUtils::MakeMaker; 3 | 4 | WriteMakefile( 5 | NAME => 'Term::Graille', 6 | VERSION_FROM => 'lib/Term/Graille.pm', 7 | ABSTRACT_FROM => 'lib/Term/Graille.pm', 8 | AUTHOR => 'Saif Ahmed', 9 | LICENSE => 'Artistic', 10 | MIN_PERL_VERSION => 'v5.10.0', 11 | PREREQ_PM => { 12 | 'strict' => 0, 13 | 'warnings' => 0, 14 | 'Algorithm::Line::Bresenham' =>'0.151', 15 | 'Time::HiRes' => 0, 16 | 'Storable' =>0, 17 | 'Term::ReadKey'=>0, 18 | 'Data::Dumper' =>0, 19 | }, 20 | (eval { ExtUtils::MakeMaker->VERSION(6.46) } ? (META_MERGE => { 21 | 'meta-spec' => { version => 2 }, 22 | resources => { 23 | repository => { 24 | type => 'git', 25 | url => 'https://github.com/saiftynet/Term-Graille.git', 26 | web => 'https://github.com/saiftynet/Term-Graille', 27 | }, 28 | }}) 29 | : () 30 | ), 31 | ); 32 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Term-Graille 2 | Braille Characters for Terminal Graphical Applications 3 | 4 | ![termgraille](https://user-images.githubusercontent.com/34284663/180637940-01b583a0-1a71-4a5d-a29b-394a940ce46f.gif) 5 | 6 | 7 | Drawille is a method of using Braille characters as a mechanism for graphical display in a terminal. Ports of the original by [asciimoo](https://github.com/asciimoo/drawille) exist inmultiple other languages including [Perl](https://metacpan.org/dist/Term-Drawille), [Java](https://github.com/null93/drawille), [Rust](https://crates.io/crates/drawille), [NodeJS](https://www.npmjs.com/package/drawille), and others. The [Perl version](https://github.com/hoelzro/term-drawille) was initially produced by [@hoelzro](https://hoelz.ro/), but not much changed since about 8 years ago. 8 | 9 | This is another Perl version, targeting ANSI compatible consoles. It delivers significantly higher performance and extends Term::Drawille by including features like:- 10 | 11 | * Integrated Turtle-like drawing 12 | * Built-in drawing primitives including line and curve, circle, ellipse, polyline more on the way 13 | * Scrolling with or without wrapping 14 | * Some frame border manipulation 15 | * Text overlays 16 | 17 | Tools in other languages have been used in publications, system monitoring tools and for games. Term::Graille allows Perl programmers access to a expanding set of features for developing simple general purpose graphics, graphing applications, games etc from the console. 18 | 19 | Associated with Term::Graille is Algorithm::Line::Bresenham. The plotting primitives in the latter are used in Graille 20 | 21 | Features to be included are colors, sprites, maps, and more on user request. For more information see the [wiki](https://github.com/saiftynet/Term-Graille/wiki) 22 | 23 | Version 0.06 24 | 25 | ## Importing images 26 | 27 | Graille does not have the resolution for fancy graphics. Having said that, images converted into mono with appropriate dithering can be surpisingly recognisable at the low resolution oferred. The `image2grl.pl` script in the examples folder uses Image Magick to perform the transromation down to a Graille canvas's resolutions, and plotted on to the canvas pixel by pixel. Graille offers the option to import or export `.grl` files. 28 | 29 | ![grailleimage](https://user-images.githubusercontent.com/34284663/179080305-c24ab071-505b-485b-bff5-cb44ed76c27c.png) 30 | 31 | ## Animation Demo 32 | 33 | The animation demo uses Image::Magick to convert a folder of frames into monochrome images, which are then converted to bitmap plots on a canvas. These bitmap plots are displayed sequentially creating an animation. 34 | 35 | This is an animation derived from a sprite sheet at [Adobe stock photos](https://stock.adobe.com/uk/images/cheetah-run-cycle-animation-sprite-sheet-silhouette-animation-frames-running-chasing/183196184) which do a 30 day free trial. 36 | 37 | ![animate](https://user-images.githubusercontent.com/34284663/177872104-57463dc3-f7f7-47a8-a9ef-3c85b4dd923f.gif) 38 | 39 | ## Fonts 40 | 41 | In the initial demo above a Turtle script was used to produce a drawing of text. This is rather combersome. Fortunately bitmap fonts exist, and can be easily converted to Braille. [DamienG](https://damieng.com/typography/zx-origins/) has produced a series of 8X8 fonts which can translated into 4X2 braille characters. The fonts are consistently coded, so `font2grf.pl` in the example folders converts the Z80 assembly data into `.grf` files for importing into Graille. Fonts are not the only thing that can be transformed, and one imagines potential for sprites or tilemaps to be similarly encoded. 42 | 43 | ![fonts](https://user-images.githubusercontent.com/34284663/179078012-69f9f535-8d41-46b0-ba68-0a5dbe613cd9.gif) 44 | 45 | ## Variable thickness Lines 46 | 47 | 48 | [Herbert Breuning](https://github.com/lichtkind) had made this suggestion. There are many examples of thick line algorithms on the net. [Alan Murphy](http://homepages.enterprise.net/murphy/thickline/index.html) gives the classic version, which has been [improved](http://kt8216.unixcab.org/murphy/index.html) and it is this code that has been ported to Perl and used in Algorth::Line::Bresenham v0.151. INtegrating these into Graille was in v0.08. 49 | 50 | ![Screenshot from 2022-08-02 18-32-01](https://user-images.githubusercontent.com/34284663/182438208-793f8c7a-6861-4f2c-b414-86c66ceb92b9.png) 51 | 52 | ## Drop down menu 53 | 54 | In developing a sprite editor on the console, mulitiple user interactions were required. Interactive graphical menus are not easy in the console. Tickit and Term::Menus are powerful modules. Term::Graille::Menu goes for simplicity rather than power. This comes in v 0.09. The sprite editor itself is not complete yet, but would be very cumbersome without something to aid visual development with multiple options and features.Term::Graille::Menu makes it very simple to create menus for ANSI terminals. 55 | 56 | The menu in this image is created using: 57 | ``` 58 | my $menu=new Term::Graille::Menu( 59 | menu=>[["File","New Sprite Bank","Save Sprite Bank","Load Sprite Bank","Quit"], 60 | ["Sprites","New Sprite","Delete Sprite","Import Sprite","Export Sprite"], 61 | ["Edit","Clear","MirrorX","MirrorY","Rotate+","Rotate-", 62 | ["Reformat","2x4","4x4"], 63 | ["Scroll","left","right","up","down"]], 64 | "About"], 65 | redraw=>\&main::refreshScreen, 66 | callback=>\&main::menuActions, 67 | ); 68 | 69 | ``` 70 | 71 | 72 | ![menu](https://user-images.githubusercontent.com/34284663/185751328-f5b67fa4-c77d-40b0-ac3a-0c6c93239fae.gif) 73 | 74 | ## Audio 75 | 76 | A component of interaction often neglected in terminal aplications is sound. Sound also adds an extra dimension to games. Term::Graille::Audio attempts to add sound with the least possible dependencies. It curently depends on pulseaudio in Linux to create a /dev/dsp (and Win32::Sound on Windows although this is as yet untested). At its simplest Audio can play a sound from its built-in sound sample set which it can autogenerate, although this is customisable and may include a speech synthesizer module based on [SP0256 allophones](https://github.com/saiftynet/SP0256). 77 | 78 | ``` 79 | use Term::Graille::Audio; 80 | 81 | my $beep=Term::Graille::Audio->new(); # TERM::Graille's Audio module 82 | $beep->playSound(undef, "A#1"); 83 | ``` 84 | 85 | 86 | [piano.webm](https://user-images.githubusercontent.com/34284663/205505757-3207fa25-c1be-492c-b353-73cb365349f2.webm) 87 | 88 | 89 | ## Games 90 | 91 | One of objective of combining in interactivity with graphics and sound is the cfreation of Games for the [PerlaySation Games Console](https://github.com/saiftynet/PerlayStation) 92 | 93 | ![invaders](https://user-images.githubusercontent.com/34284663/210085591-fe1d0ce2-fd52-4d1b-a27f-8df37bc9dc09.gif) 94 | 95 | [breakout.webm](https://user-images.githubusercontent.com/34284663/211404936-fb79f676-a505-413e-bad0-d1ac28c7f7d3.webm) 96 | 97 | -------------------------------------------------------------------------------- /README.pod: -------------------------------------------------------------------------------- 1 | =head1 NAME 2 | 3 | Term::Graille - Graphical Display in the terminal using UTF8 Braille characters 4 | 5 | =head1 SYNOPSIS 6 | 7 | my $canvas = Term::Graille->new( 8 | width => 72, # pixel width 9 | height => 64, # pixel height 10 | top=>3, # row position in terminal (optional,defaults to 1) 11 | left=>10, # column position (optional,defaults to 1) 12 | borderStyle => "double", # 13 | ); 14 | 15 | =head1 DESCRIPTION 16 | 17 | Inspired by Drawille by asciimoo, which has many variants (including 18 | a perl variant Term::Drawille by RHOELZ), this is a clone with a few 19 | extras. The goal is to achieve performance and features. with built-in 20 | turtle-like graphics, line and curve drawing (Algorithm::Line::Bresenham), 21 | scrolling, border setting, and more in development. 22 | 23 | =begin html 24 | 25 | 26 | 27 | =end html 28 | 29 | 30 | =head1 FUNCTIONS 31 | 32 | 33 | =cut 34 | 35 | =head3 Cnew(%params)> 36 | 37 | Creates a new canavas; params are 38 | C The pixel width, required C the pixel height, required, 39 | C terminal row (optional,default 1) C terminal column ( 40 | optional,default 1) C border type (optional,one of 'simple', 41 | 'double', 'thin', 'thick' or 'shadow') C border colour ( 42 | optional), C Title text for top border,(optional) C<titleColour> 43 | Title colour (optional) 44 | 45 | 46 | =cut 47 | 48 | =head3 C<$canvas-E<gt>draw()> , C<$canvas-E<gt>draw($row, $column)> 49 | 50 | Draws the canvas to the terminal window. Optional row and column 51 | parameters may be passed to position the displayed canvas. If 52 | borderStyle is specified, the border is drawn, If title is specified 53 | this is added to the top border 54 | 55 | 56 | =cut 57 | 58 | =head3 C<$canvas-E<gt>as_string()> 59 | 60 | Returns the string containing the canvas of utf8 braille symbols, rows 61 | being separated by newline. 62 | 63 | 64 | =cut 65 | 66 | =head3 C<$canvas-E<gt>set($x,$y,$pixelValue)> 67 | 68 | Sets a particular pixel on (default if C<$pixelValue> not sent) or off. 69 | 70 | 71 | =cut 72 | 73 | =head3 C<$canvas-E<gt>unset($x,$y)> 74 | 75 | Sets the pixel value at C<$x,$y> to blank 76 | 77 | 78 | =cut 79 | 80 | =head3 C<$canvas-E<gt>pixel($x,$y)> 81 | 82 | Gets the pixel value at C<$x,$y> 83 | 84 | 85 | =cut 86 | 87 | =head3 C<$canvas-E<gt>clear()> 88 | 89 | Re-initialises the canvas with blank braille characters 90 | 91 | 92 | =cut 93 | 94 | =head3 C<$canvas-E<gt>line($x1,$y1,$x2,$y2,$value)> 95 | 96 | Uses Algorithm::Line::Bresenham to draw a line from C<$x1,$y1> to C<$x2,$y2>. 97 | The optional value C<$value> sets or unsets the pixels. If C<$value> is a 98 | valid colour (see below) the line will be drawn with that colour. 99 | 100 | 101 | =cut 102 | 103 | =head3 C<$canvas-E<gt>circle($x1,$y1,$radius,$value)> 104 | 105 | Uses Algorithm::Line::Bresenham to draw a circle centered at C<$x1,$y1> 106 | with radius C<$radius> to C<$x2,$y2>. 107 | The optional value C<$value> sets or unsets the pixels. If C<$value> is a 108 | valid colour (see below) the line will be drawn with that colour. 109 | 110 | 111 | =cut 112 | 113 | =head3 C<$canvas-E<gt>ellipse_rect($x1,$y1,$x2,$y2,$value)> 114 | 115 | Uses Algorithm::Line::Bresenham to draw a rectangular ellipse, (an 116 | ellipse bounded by a rectangle defined by C<$x1,$y1,$x2,$y2>). 117 | The optional value C<$value> sets or unsets the pixels. If C<$value> is a 118 | valid colour (see below) the line will be drawn with that colour. 119 | 120 | 121 | =cut 122 | 123 | =head3 C<$canvas-E<gt>quad_bezier($x1,$y1,$x2,$y2,$x3,$y3,$value)> 124 | 125 | Uses Algorithm::Line::Bresenham to draw a quadratic bezier, defined by 126 | end points C<$x1,$y1,$x3,$y3>) and control point C<$x2,$y2>. 127 | The optional value C<$value> sets or unsets the pixels. If C<$value> is a 128 | valid colour (see below) the line will be drawn with that colour. 129 | 130 | 131 | =cut 132 | 133 | =head3 C<$canvas-E<gt>polyline($x1,$y1,....,$xn,$yn,$value)> 134 | 135 | Uses Algorithm::Line::Bresenham to draw a poly line, form a 136 | sequences of points. 137 | The optional value C<$value> sets or unsets the pixels. If C<$value> is a 138 | valid colour (see below) the line will be drawn with that colour. 139 | 140 | 141 | =cut 142 | 143 | =head2 Character Level Functions; 144 | 145 | =head3 C<$canvas-E<gt>scroll($direction,$wrap)> 146 | 147 | Scrolls in C<$direction>. $direction may be 148 | "l", "left", "r","right", "u","up","d", "down". 149 | Beacuse of the use of Braille characters, up/down scrolling is 4 pixels 150 | at a time, whereas left right scrolling is 2 pixels at a time. If 151 | C<$wrap> is a true value, the screen wraps around. 152 | 153 | 154 | =cut 155 | 156 | =head3 C<$canvas-E<gt>blockBlit($block,$gridX, $gridY)> 157 | 158 | Allows blitting a 2d arrays to a grid location in the canvas. Useful for 159 | printing using a Graille font. 160 | 161 | 162 | =cut 163 | 164 | =head3 C<$canvas-E<gt>exportCanvas($filename)> , C<$canvas-E<gt>importCanvas($filename,[$toBuffer])> 165 | 166 | This allows the loading and unloading of a canvas from a file. There is 167 | no checking of the dimension of the canvas being imported at the moment. 168 | Import can be direct to the canvas, the function return the loaded data 169 | as an ArrayRef, the optional c<$toBuffer> parameter, if true prevents 170 | loading the data onto the canvas. 171 | 172 | 173 | =cut 174 | 175 | =head3 C<$canvas-E<gt>textAt($x,$y,$text,$fmt)> 176 | 177 | Printing text on the C<$canvas>. This is different fromthe exported 178 | C<printAt()> function. the characters are printed on the C<$canvas> 179 | and may be scrolled with the canvas and will overwrite or be over written 180 | othe $canvas drawing actions. The optional C<$fmt> allows the setting of colour; 181 | 182 | 183 | =cut 184 | 185 | =head2 Enhancements 186 | 187 | =head3 C<$canvas-E<gt>logo($script)> 188 | 189 | Interface to Graille's built in Turtle interpreter. 190 | A string is taken and split by senicolons or newlines into instructions. 191 | The instructions are trimmed, and split by the first space character into 192 | command and parameters. Very simple in other words. No syntax checking 193 | is done 194 | 195 | C<"fd distance"> pen moves forward a certain distance. 196 | C<"lt angle">, C<"rt angle"> turns left or right. 197 | C<"bk distance"> pen moves back a certain distance. 198 | C<"pu">, C<"pd"> Pen is up or down, up means no drawing takes place, 199 | and down means the turtle draws as it moves. 200 | C<"dir"> set the direction at a specific angle in dgrees, 201 | with 0 being directly left. 202 | C<"mv">moves pen to specific coordinates without drawing. 203 | C<"ce"> centers the turtle in the middle of a canvas 204 | C<"sp"> allows animated drawing by specifiying the the number 205 | of centiseconds between instructions 206 | 207 | 208 | =cut 209 | 210 | =head3 Exported Routines 211 | 212 | Graille exports some functions for additional console graphical manipulation 213 | This includes drawing of borders, printing characters at specific locations 214 | in the terminal window, and colouring the characters, clearing the screen, etc. 215 | 216 | printAt($row,$column,@textRows); 217 | 218 | Prints text sent as a scalar, a list of scalars or a reference to list 219 | of scalars, at a specific location on the screen. Lists are printed 220 | from the same column but increasing row positions. 221 | 222 | border($top,$left,$bottom,$right,$style,$colour); 223 | 224 | Draws a border box. 225 | 226 | paint($txt,$fmt) 227 | 228 | Paints text the colour and background specified, $text may be a string, or ref 229 | to a list of strings. This is combined with C<printAt()> abouve 230 | 231 | clearScreen() 232 | 233 | Guess what? clears the enire screen. This is different from C<$canvas-E<gt>clear()> 234 | which clears the Graille canvas. 235 | 236 | 237 | block2braille($block) 238 | 239 | Given a block of binary data (a 2D Array ref of 8-bit data), return a 240 | corresponding 2d Array ref of braille blocks. This is handy to convert, 241 | say, binary font data tinto Braille blocks for blittting into the canvas; 242 | 243 | pixelAt($block,$px,$py) 244 | 245 | Given a binary block of data e.g. a font or a sprite offered as a 2D Array ref 246 | find the pixel value at a certain coordinate in that block. 247 | 248 | 249 | 250 | =cut 251 | 252 | =head1 AUTHOR 253 | 254 | Saif Ahmed 255 | 256 | =head1 LICENSE 257 | 258 | Artistic 259 | 260 | =head1 INSTALLATION 261 | 262 | 263 | 264 | Manual install: 265 | 266 | $ perl Makefile.PL 267 | $ make 268 | $ make install 269 | 270 | 271 | =cut 272 | 273 | -------------------------------------------------------------------------------- /Term-Graille-0.10.tar.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/saiftynet/Term-Graille/c467644004a72a34c607ebdf6d8ec4c25afbac70/Term-Graille-0.10.tar.gz -------------------------------------------------------------------------------- /examples/animations/cheetah/cheetah1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/saiftynet/Term-Graille/c467644004a72a34c607ebdf6d8ec4c25afbac70/examples/animations/cheetah/cheetah1.png -------------------------------------------------------------------------------- /examples/animations/cheetah/cheetah2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/saiftynet/Term-Graille/c467644004a72a34c607ebdf6d8ec4c25afbac70/examples/animations/cheetah/cheetah2.png -------------------------------------------------------------------------------- /examples/animations/cheetah/cheetah3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/saiftynet/Term-Graille/c467644004a72a34c607ebdf6d8ec4c25afbac70/examples/animations/cheetah/cheetah3.png -------------------------------------------------------------------------------- /examples/animations/cheetah/cheetah4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/saiftynet/Term-Graille/c467644004a72a34c607ebdf6d8ec4c25afbac70/examples/animations/cheetah/cheetah4.png -------------------------------------------------------------------------------- /examples/animations/cheetah/cheetah5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/saiftynet/Term-Graille/c467644004a72a34c607ebdf6d8ec4c25afbac70/examples/animations/cheetah/cheetah5.png -------------------------------------------------------------------------------- /examples/animations/cheetah/cheetah6.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/saiftynet/Term-Graille/c467644004a72a34c607ebdf6d8ec4c25afbac70/examples/animations/cheetah/cheetah6.png -------------------------------------------------------------------------------- /examples/animations/cheetah/cheetah7.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/saiftynet/Term-Graille/c467644004a72a34c607ebdf6d8ec4c25afbac70/examples/animations/cheetah/cheetah7.png -------------------------------------------------------------------------------- /examples/animations/cheetah/cheetah8.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/saiftynet/Term-Graille/c467644004a72a34c607ebdf6d8ec4c25afbac70/examples/animations/cheetah/cheetah8.png -------------------------------------------------------------------------------- /examples/animations/chimp/chimp1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/saiftynet/Term-Graille/c467644004a72a34c607ebdf6d8ec4c25afbac70/examples/animations/chimp/chimp1.png -------------------------------------------------------------------------------- /examples/animations/chimp/chimp10.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/saiftynet/Term-Graille/c467644004a72a34c607ebdf6d8ec4c25afbac70/examples/animations/chimp/chimp10.png -------------------------------------------------------------------------------- /examples/animations/chimp/chimp2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/saiftynet/Term-Graille/c467644004a72a34c607ebdf6d8ec4c25afbac70/examples/animations/chimp/chimp2.png -------------------------------------------------------------------------------- /examples/animations/chimp/chimp3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/saiftynet/Term-Graille/c467644004a72a34c607ebdf6d8ec4c25afbac70/examples/animations/chimp/chimp3.png -------------------------------------------------------------------------------- /examples/animations/chimp/chimp4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/saiftynet/Term-Graille/c467644004a72a34c607ebdf6d8ec4c25afbac70/examples/animations/chimp/chimp4.png -------------------------------------------------------------------------------- /examples/animations/chimp/chimp5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/saiftynet/Term-Graille/c467644004a72a34c607ebdf6d8ec4c25afbac70/examples/animations/chimp/chimp5.png -------------------------------------------------------------------------------- /examples/animations/chimp/chimp6.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/saiftynet/Term-Graille/c467644004a72a34c607ebdf6d8ec4c25afbac70/examples/animations/chimp/chimp6.png -------------------------------------------------------------------------------- /examples/animations/chimp/chimp7.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/saiftynet/Term-Graille/c467644004a72a34c607ebdf6d8ec4c25afbac70/examples/animations/chimp/chimp7.png -------------------------------------------------------------------------------- /examples/animations/chimp/chimp8.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/saiftynet/Term-Graille/c467644004a72a34c607ebdf6d8ec4c25afbac70/examples/animations/chimp/chimp8.png -------------------------------------------------------------------------------- /examples/animations/chimp/chimp9.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/saiftynet/Term-Graille/c467644004a72a34c607ebdf6d8ec4c25afbac70/examples/animations/chimp/chimp9.png -------------------------------------------------------------------------------- /examples/animations/dragon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/saiftynet/Term-Graille/c467644004a72a34c607ebdf6d8ec4c25afbac70/examples/animations/dragon.png -------------------------------------------------------------------------------- /examples/breakout.pl: -------------------------------------------------------------------------------- 1 | #! /usr/bin/env perl 2 | use strict; use warnings; 3 | use utf8; 4 | use lib "../lib"; 5 | use open ":std", ":encoding(UTF-8)"; 6 | use Term::Graille qw/colour paint printAt cursorAt clearScreen border blockBlit block2braille pixelAt/; 7 | use Term::Graille::Interact ; 8 | use Term::Graille::Sprite; 9 | use Term::Graille::Audio; 10 | use Term::Graille::Font qw/loadGrf fontWrite/; 11 | use Time::HiRes qw/sleep/; 12 | 13 | my $beep=Term::Graille::Audio->new(); # TERM::Graille's Audio module 14 | my $grf=loadGrf("./fonts/ZX Times.grf"); 15 | my $stopUpdates=0; 16 | my $canvas = Term::Graille->new( 17 | width => 148, 18 | height => 60, 19 | top=>3, 20 | left=>4, 21 | borderStyle => "double", 22 | title=>"Breakout", 23 | ); 24 | my $lives=3; 25 | my $score=0; 26 | my $level=1; 27 | my $spriteBank=Term::Graille::SpriteBank->new(); 28 | my $dir="./sprites/"; 29 | foreach my $colour(qw/red yellow green magenta cyan white blue/){ 30 | $spriteBank->addShape($colour."brick",[[colour($colour)."█","█","█","▌".colour("reset")]]); 31 | } 32 | 33 | my $bat=new Term::Graille::Sprite(data=>[[("█") x 6]],pos=>[20,0],bounds=>[1,14,60,0]); 34 | my $ball=new Term::Graille::Sprite(pos=>[20,3],vel=>[1,1],bounds=>[1,14,70,-1],skip=>6); 35 | $spriteBank->addSprite("player",$bat); 36 | $spriteBank->addSprite("ball",$ball); 37 | 38 | my $help={}; 39 | 40 | newWall(); 41 | $spriteBank->drawAll($canvas); 42 | sleep 5; 43 | my $io=Term::Graille::Interact->new(); 44 | $io->{debugCursor}=[22,45]; 45 | 46 | $io->addAction(undef,"[D",{note=>"Move Left ",proc=>sub{ 47 | $bat->blank($canvas); 48 | $bat->move([-1,0]) unless $bat->{pos}->[0] <= 2 }} ); 49 | $io->addAction(undef,"[C",{note=>"Move Right ",proc=>sub{ 50 | $bat->blank($canvas); 51 | $bat->move([1,0]) unless $bat->{pos}->[0] >= 68 }} ); 52 | $io->addAction(undef,"p",{note=>"PaUSE ", proc=>sub{ 53 | $stopUpdates=$stopUpdates?0:1;}} ); 54 | 55 | $io->updateAction(\&update); 56 | $io->run(); 57 | 58 | sub update{ 59 | missed() unless @{$spriteBank->{groups}->{ball}}; 60 | foreach my $bll (@{$spriteBank->{groups}->{ball}}){ 61 | my ($edge,$dir)=$bll->edge(); 62 | 63 | if ($bll->collide($bat)){ 64 | $bll->{vel}->[1]=abs ($bll->{vel}->[1]); 65 | my $batside=4*($bll->{pos}->[0]-$bat->{pos}->[0]+1)/($bat->{geometry}->[0]+2); 66 | $bll->{vel}->[0]=(-2,-1,1,2)[$batside]; 67 | $beep->playSound(undef,"B1") if ($bll->{skipped}==$bll->{skip}); 68 | # printAt(20,25,($bll->{pos}->[0]-$bat->{pos}->[0]+1)." " ); 69 | } 70 | elsif ($edge ne ""){ 71 | if ($edge =~ "Bot"){ 72 | $bll->{destroyed}=1; 73 | } 74 | $bll->{vel}->[1]=abs($bll->{vel}->[1])*$dir->[1] if ($dir->[1]); 75 | $bll->{vel}->[0]=abs($bll->{vel}->[0])*$dir->[0] if ($dir->[0]);; 76 | # printAt(20,15,$edge." ".$bll->{vel}->[1]); 77 | } 78 | else{ 79 | nextLevel() unless @{$spriteBank->{groups}->{wall}}; 80 | foreach my $brk (@{$spriteBank->{groups}->{wall}}){ 81 | if ($bll->collide($brk)){ 82 | $beep->playSound(undef,"A#1"); 83 | $score+=$brk->{etc}->{points}; 84 | special($brk,$bll); 85 | if ($bll->{pos}->[1] ==$brk->{pos}->[1]){ # if hitting side of brick 86 | $bll->{vel}->[0]=($bll->{pos}->[0] <=$brk->{pos}->[0])?-abs ($bll->{vel}->[0]):abs ($bll->{vel}->[0]); 87 | } 88 | else { 89 | $bll->{vel}->[1]=($bll->{pos}->[1] >$brk->{pos}->[1])?abs ($bll->{vel}->[1]):-abs ($bll->{vel}->[1]); 90 | } 91 | $bll->move(); 92 | $bll->blankPrev($canvas); 93 | } 94 | } 95 | } 96 | } 97 | $spriteBank->update($canvas); 98 | $canvas->draw(); 99 | #printAt(20,10,$ball->{pos}->[0]." ".$ball->{pos}->[1]); 100 | printAt(2,40,"Score: $score Level: $level, Lives ".colour("red")."♥ "x$lives.colour("reset") ); 101 | } 102 | 103 | sub newWall{ 104 | for my $row (0..5){ 105 | for my $col (0..16){ 106 | my $colour=(qw/red yellow green magenta cyan white blue/)[rand()*7]; 107 | $spriteBank->addSprite("wall", new Term::Graille::Sprite (shape=>$colour."brick",pos=>[2+4*$col+2*($row%2),14-$row], etc=>{points=>5,type=>$colour})); 108 | } 109 | } 110 | } 111 | 112 | sub missed{ 113 | $lives--; 114 | $beep->playMusic(undef,["D1","C1","B1","A1"]); 115 | if ($lives){ 116 | fontWrite($canvas,$grf,19,4,"Missed !"); 117 | $canvas->draw(); 118 | sleep 5; 119 | fontWrite($canvas,$grf,17,4," "); 120 | $ball=new Term::Graille::Sprite(pos=>[$bat->{pos}->[0],3],vel=>[1,1],bounds=>[1,14,70,-1],skip=>6); 121 | $spriteBank->addSprite("ball",$ball); 122 | } 123 | else{ 124 | fontWrite($canvas,$grf,9,8,"The Wall Wins"); 125 | 126 | $canvas->draw(); 127 | $io->stop(); 128 | exit; 129 | } 130 | } 131 | 132 | sub nextLevel{ 133 | $beep->playMusic(undef,["A2","B2","C2","D2"]); 134 | fontWrite($canvas,$grf,2,4,"Wall ".$level++." Destroyed !"); 135 | $canvas->draw(); 136 | sleep 2; 137 | $canvas->clear(); 138 | newWall(); 139 | $spriteBank->drawAll($canvas); 140 | } 141 | 142 | sub special{ 143 | my ($brick,$ball)=@_; 144 | $brick->{destroyed}=1; 145 | my $type=$brick->{etc}->{type}; 146 | my $message=" $type brick hit "; 147 | if ($type eq "blue"){ 148 | $message.="10pts Ball moves faster "; 149 | $score+=10; 150 | $ball->{skip}=4; 151 | } 152 | elsif($type eq "red"){ 153 | $message.="100pts Ball moves slower "; 154 | $score+=100; 155 | $ball->{skip}=8; 156 | } 157 | elsif($type eq "magenta"){ 158 | $message.="1000pts Destroys nearby bricks "; 159 | $score+=1000; 160 | foreach my $brk (@{$spriteBank->{groups}->{wall}}){ 161 | if ((($brk->{pos}->[0]-$ball->{pos}->[0])**2 + ($brk->{pos}->[1]-$ball->{pos}->[1])**2)<30){ 162 | $brk->{destroyed}=1; 163 | } 164 | } 165 | } 166 | elsif($type eq "yellow"){ 167 | $message.="500pts Destroys entire row "; 168 | $score+=1000; 169 | foreach my $brk (@{$spriteBank->{groups}->{wall}}){ 170 | unless ($brk->{pos}->[1]-$brick->{pos}->[1]){ 171 | $brk->{destroyed}=1; 172 | } 173 | } 174 | } 175 | elsif($type eq "green"){ 176 | $message.="1000pts all green bricks disappear "; 177 | $score+=1000; 178 | foreach my $brk (@{$spriteBank->{groups}->{wall}}){ 179 | if ($brk->{etc}->{type} eq "green"){ 180 | $brk->{destroyed}=1; 181 | } 182 | } 183 | } 184 | elsif($type eq "white"){ 185 | $message.="100pts multiple balls"; 186 | $score+=100; 187 | $spriteBank->addSprite("ball",new Term::Graille::Sprite(pos=>[@{$bat->{pos}}],vel=>[int(rand()*3)-1,1],bounds=>[1,14,70,-1],skip=>6)); 188 | 189 | } 190 | 191 | unless (exists $help->{$type} && $help->{type}++>2){ 192 | $help->{$type}//=1; 193 | $canvas->textAt(17,15,$message); 194 | $canvas->draw(); 195 | sleep 1; 196 | $canvas->textAt(17,15," "x length $message,); 197 | $canvas->draw(); 198 | } 199 | 200 | } 201 | 202 | -------------------------------------------------------------------------------- /examples/convert2grl.pl: -------------------------------------------------------------------------------- 1 | #! /usr/bin/env perl 2 | use strict; use warnings; 3 | use lib "../lib/"; 4 | use Term::Graille qw/colour paint printAt clearScreen border/; 5 | use Image::Magick; 6 | 7 | # convert2grl--converts an image to .grl format. 8 | 9 | my $imgFile=$ARGV[0]; 10 | 11 | my $canvas = Term::Graille->new( 12 | width => 72, 13 | height => 60, 14 | top=>4, 15 | left=>10, 16 | borderStyle => "double", 17 | ); 18 | clearScreen(); 19 | loadImage($imgFile||"perl2.jpg"); 20 | $canvas->exportCanvas("save.grl"); 21 | 22 | #$canvas->importCanvas("save.grl"); 23 | 24 | sub loadImage{ 25 | my $imgfile=shift; 26 | my $image = Image::Magick->new(); 27 | my $wh = $image->Read($imgfile); 28 | $image->Resize(geometry => "$canvas->{width}x$canvas->{height}"); 29 | warn $wh if $wh; 30 | $image->set('monochrome'=>'True'); 31 | for (my $x =0;$x<$canvas->{width};$x++){ 32 | for (my $y=0;$y<$canvas->{height};$y++){ 33 | $canvas->set($x,$y) if $image->GetPixel("x"=>$x,"y",$canvas->{height}-$y); 34 | } 35 | } 36 | } 37 | 38 | $canvas->draw(); 39 | print "\n"; 40 | print $imgFile//"no File input";; 41 | -------------------------------------------------------------------------------- /examples/demo.pl: -------------------------------------------------------------------------------- 1 | #! /usr/bin/env perl 2 | use strict; use warnings; 3 | use lib "../lib/"; 4 | use Term::Graille qw/colour paint printAt clearScreen border/; 5 | use Term::Graille::Font qw/loadGrf fontWrite/; 6 | use Time::HiRes "sleep"; 7 | my $canvas = Term::Graille->new( 8 | width => 72, 9 | height => 64, 10 | top=>3, 11 | left=>10, 12 | borderStyle => "double", 13 | ); 14 | clearScreen(); 15 | 16 | my $grf=loadGrf("./fonts/ZX Times.grf"); 17 | logo(); 18 | turtle(); 19 | cube(); 20 | lines(); 21 | waves(); 22 | 23 | printAt(20,50," "); 24 | 25 | sub logo{ 26 | $canvas->clear(); 27 | $canvas->{borderColour}="cyan"; 28 | $canvas->{title}="Term::Graille"; 29 | $canvas->logo("pu;rt 90; fd 25; rt 90;fd 31;lt 180;pd;"); 30 | printAt(5,50, paint("Introducing Term::Graille", "white on_black")); 31 | printAt(7,50, paint(["Inspired by Drawille by", 32 | "asciimoo, which has many", 33 | "variants (including a ", 34 | "Perl variant Term::Drawille", 35 | "by RHOELZ), this is a clone", 36 | "with a few extras. The goal", 37 | "is to deliver better", 38 | "performance and features. "],"yellow")); 39 | 40 | 41 | 42 | fontWrite($canvas,$grf,4,5,"GRAILLE"); 43 | $canvas->textAt(18,14,"Testing version $Term::Graille::VERSION","green italic"); 44 | $canvas->textAt(18,10,"with Bresenham $Algorithm::Line::Bresenham::VERSION","green italic"); 45 | $canvas->ellipse_rect(20,63, 52,55,"red"); 46 | $canvas->quad_bezier(20,59,22,40,35,40); 47 | $canvas->quad_bezier(37,40,50,40,52,59); 48 | $canvas->polyline(35,40,35,35,20,35,20,33,55,33,55,35,37,35,37,40,"yellow"); 49 | $canvas->draw(); 50 | sleep 2; 51 | for (0..20){ 52 | $canvas->scroll("d"); 53 | sleep 0.05; 54 | $canvas->draw(); 55 | } 56 | } 57 | 58 | sub turtle{ 59 | $canvas->clear(); 60 | $canvas->{borderColour}="magenta"; 61 | $canvas->{title}="Turtle"; 62 | printAt(5,50, paint("Turtle Graphics ", "white on_black")); 63 | printAt(7,50, paint(["This early version supports ", 64 | "Turtle-like drawing as well e.g.", 65 | "fd,bk,lt,rt,pu,pd,ce,sp "," "x30,], "yellow")); 66 | printAt(11,50, paint(["\$canvas->logo('fd 35;lt 75')", 67 | " for (0..23); ", 68 | " "x 30," "x30," "x 30," "x30,], "green")); 69 | $canvas->logo("mv 15,30;pc magenta;sp 8;"); 70 | my @clrs=qw/red yellow green cyan magenta blue green red yellow/; 71 | $canvas->logo("fd 50;lt 160;pc $clrs[$_]") for (0..8); 72 | sleep 2; 73 | } 74 | 75 | 76 | sub cube{ 77 | # adapted from http://www.rosettacode.org/wiki/Draw_a_rotating_cube 78 | my $size = 60; 79 | my ($height, $width) = ($size, $size * sqrt 8/9); 80 | my $mid = $width / 2; 81 | my $rot = atan2(0, -1) / 3; # middle corners every 60 degrees 82 | $canvas->{borderColour}="green"; 83 | 84 | printAt(5,50, paint("Animated drawings ", "white on_black")); 85 | printAt(7,50, paint(["Rotating cube. This algorithm ", 86 | "was adapted from code from ", 87 | "http://www.rosettacode.org/ ", 88 | " "x 30," "x30," "x30," "x30,],"yellow")); 89 | $canvas->{title}="Rotating Cube"; 90 | foreach my $a (0..300) 91 | { 92 | my $angle = $a/30; 93 | my @points = map { $mid + $mid * cos $angle + $_ * $rot, 94 | $height * ($_ % 2 + 1) / 3 } 0 .. 5; 95 | $canvas->line(@points[0, 1], $mid, 0,"red"); 96 | $canvas->line(@points[0, 1], @points[2, 3],"green"); 97 | $canvas->line(@points[4, 5], $mid, 0,"yellow"); 98 | $canvas->line(@points[4, 5], @points[6, 7],"blue"); 99 | $canvas->line(@points[8, 9],$mid, 0,"magenta"); 100 | $canvas->line(@points[8, 9],@points[10, 11],"white"); 101 | $canvas->line(@points[2, 3], $mid, $height,"cyan"); 102 | $canvas->line(@points[2, 3], @points[4, 5],"blue"); 103 | $canvas->line(@points[6, 7],$mid, $height,"red"); 104 | $canvas->line(@points[6, 7],@points[8, 9],"green"); 105 | $canvas->line(@points[10, 11],$mid, $height,"yellow"); 106 | $canvas->line(@points[10, 11],@points[0, 1]); 107 | $canvas->draw(); 108 | sleep .01; 109 | $canvas->clear(); 110 | } 111 | sleep 2; 112 | } 113 | 114 | sub lines{ 115 | $canvas->clear();; 116 | $canvas->{borderColour}="yellow"; 117 | $canvas->{title}="Lines, Curves, Circles Etc"; 118 | printAt(5,50, paint("Drawing Primitives ", "white on_black")); 119 | printAt(7,50, paint("Lines ","yellow")); 120 | printAt(8,50, paint(["\$canvas->line(10,10, ", 121 | " 30,30) "],"green")); 122 | printAt(10,50, paint("Curves ","yellow")); 123 | printAt(11,50, paint(["\$canvas->quad_bezier(10,10, ", 124 | " 30,30,50,10); "],"green")); 125 | printAt(13,50, paint("Circles ","yellow")); 126 | printAt(14,50, paint("\$canvas->circle(30,30,5); ", "green")); 127 | 128 | $canvas->quad_bezier(10,30,37,-10,64,30,"green"); 129 | $canvas->line(10,30,10,45,"green"); 130 | $canvas->line(64,30,64,45,"green"); 131 | 132 | for my $rl(0..1){ # draw eyes 133 | for (0..4){ 134 | $canvas->ellipse_rect(15+$_+30*$rl,45,30-$_+30*$rl,40,"magenta"); 135 | } 136 | } 137 | 138 | 139 | $canvas->quad_bezier(25,25,37,10,49,25,"red"); 140 | $canvas->quad_bezier(25,25,37,20,49,25,"red"); 141 | 142 | $canvas->circle(37,33,5,"yellow"); 143 | $canvas->polyline(10,55,15,58,20,55,25,58,30,55,35,58, 144 | 40,55,45,58,50,55,55,58,60,55,65,58, 145 | 65,50,10,50,10,55,"yellow"); 146 | $canvas->draw(); 147 | sleep 2; 148 | } 149 | 150 | sub waves{ 151 | $canvas->clear();; 152 | $canvas->{borderColour}="blue"; 153 | $canvas->{title}="Waves"; 154 | printAt(5,50, paint("Charting/graphing ", "white on_black")); 155 | printAt(7,50, paint(["This example plots a line ", 156 | "on the left most two pixels ", 157 | "and then scrolls the frame left ", 158 | "by 2 pixels. e.g :- ", 159 | " "x30], "yellow")); 160 | printAt(12,50, paint(["foreach my \$c (1..1000){ ", 161 | "\$canvas->line(70,20*sin(\$c/20)+", 162 | "40,71,20*sin((\$c+0.5)/20)+40); ", 163 | "\$canvas->scroll('l'); ", 164 | "sleep 0.01; ", 165 | "\$canvas->draw();} "], "green")); 166 | foreach my $c (1..500){ 167 | sleep .01; 168 | $canvas->line(70,20*sin($c/20)+40,71,20*sin(($c+0.5)/20)+40,"red"); 169 | $canvas->line(70,20*cos($c/2)+40,71,20*cos(($c+0.5)/2)+40,"green"); 170 | $canvas->scroll("l"); 171 | $canvas->draw(); 172 | } 173 | 174 | sleep 5 175 | 176 | } 177 | 178 | 179 | 180 | -------------------------------------------------------------------------------- /examples/editor.pl: -------------------------------------------------------------------------------- 1 | #! /usr/bin/env perl 2 | 3 | use strict; use warnings; 4 | use utf8; 5 | use lib "../lib"; 6 | use open ":std", ":encoding(UTF-8)"; 7 | use Term::Graille qw/colour paint printAt cursorAt clearScreen border blockBlit block2braille pixelAt/; 8 | use Term::Graille::Interact ; 9 | use Term::Graille::Menu; 10 | use Term::Graille::Textarea; 11 | use Term::Graille::Selector; 12 | use Term::Graille::Dialog ; 13 | use Time::HiRes qw/sleep/; 14 | use Data::Dumper; 15 | 16 | my $io=Term::Graille::Interact->new(); 17 | $io->{debugCursor}=[22,45]; 18 | 19 | my @colours=qw/red blue green yellow cyan magenta white/; 20 | 21 | # setup keyboard short cuts 22 | 23 | my $menu=new Term::Graille::Menu( # no object offered so id will be o0 24 | menu=>[["File","New","Load","Insert","Save","Quit"], 25 | ["Edit",["Select","All","Start","End"],["Selection","Copy","Cut","Paste"],"Search","Replace"], 26 | ["Run","Logo","Perl","Python"], 27 | ["test","Death","Message","Input","Selector"], 28 | "About"], 29 | redraw=>\&refreshScreen, 30 | dispatcher=>\&menuActions, 31 | ); 32 | 33 | # in this case the dispatcher directs to subroutines based on name of 34 | my %actions = ( New => sub{new()}, 35 | Load => sub{ load()}, 36 | Save => sub{ save() }, 37 | Insert => sub{ load(undef,undef,1) }, 38 | Editor=>sub{mode("Editor")}, 39 | Viewer=>sub{mode("Viewer")}, 40 | Message=>sub{message("Test Message")}, 41 | "Logo"=>sub{logo()}, 42 | ); 43 | 44 | sub menuActions{ # dispatcher for menu 45 | my ($action,$gV)=@_; 46 | if (exists $actions{$action}){ 47 | $actions{$action}->($gV) 48 | } 49 | else{ 50 | printAt(2,60,$action) 51 | } 52 | }; 53 | 54 | my $currentFile=""; 55 | 56 | my $textarea=new Term::Graille::Textarea(); # no objectId offered so id will be o0 57 | 58 | my $canvas= Term::Graille->new( 59 | width => 80, 60 | height => 50, 61 | top=>4, 62 | left=>8, 63 | borderStyle => "double", 64 | title=>"Running $currentFile", 65 | ); 66 | 67 | $io->addObject(object => $menu,trigger=>"m"); 68 | $io->addObject(object => $textarea,trigger=>"e"); 69 | $io->start("o1"); 70 | $io->run("o1"); 71 | refreshScreen(); 72 | 73 | sub refreshScreen{ 74 | clearScreen() unless (shift); 75 | $textarea->draw(); 76 | } 77 | 78 | sub new{ 79 | $textarea->{text}=[""]; 80 | $textarea->{title}="Untitled"; 81 | $textarea->{cursor}=[0,0]; 82 | refreshScreen(1); 83 | $io->start("o1"); 84 | 85 | } 86 | 87 | sub load{ 88 | my ($dir,$filters,$insert)=@_; 89 | $dir//="."; 90 | $filters//="\.txt|\.pl|\.logo\$"; 91 | opendir(my $DIR, $dir) || die "Can't open directory $dir: $!"; 92 | my @files = sort(grep {(/$filters/i) && -f "$dir/$_" } readdir($DIR)); 93 | closedir $DIR; 94 | my $selector=new Term::Graille::Selector( 95 | redraw=>\&refreshScreen, 96 | callback=>\&confirmLoad, 97 | options=>[@files], 98 | transient=>1, 99 | title=>"Load File", 100 | ); 101 | my $selId=$io->addObject(object => $selector); 102 | $io->start($selId); 103 | 104 | } 105 | 106 | sub confirmLoad{ # if about to overwrite warn 107 | my ($param)=@_; 108 | my $file=$param->{selected}; 109 | my $dialog=new Term::Graille::Dialog( 110 | redraw=>\&refreshScreen, 111 | callback=>\&loadFile, 112 | message=>"Are you sure you want to load this file? You will lose current work", 113 | param=>{file=>$file,action=>"load"}, 114 | icon=>"info", 115 | title=>"Confirm Load", 116 | buttons=>[qw/Load Cancel/], 117 | ); 118 | my $dialogId=$io->addObject(object => $dialog); 119 | $io->start($dialogId); 120 | 121 | } 122 | 123 | 124 | 125 | sub loadFile{ 126 | my $param=shift; 127 | if ($param->{button} && $param->{button} eq "Cancel"){ 128 | $io->close(); 129 | $io->start("o1"); 130 | return; 131 | }; 132 | my $file=$param->{file}; 133 | open(my $fh, "<", "$file") or die("Can't open file $file:$! "); 134 | chomp(my @lines = <$fh>); 135 | close($fh); 136 | $textarea->{text}=[@lines]; 137 | $io->close(); 138 | $textarea->{title}=$file; 139 | $textarea->{cursor}=[0,0]; 140 | $currentFile=$file; 141 | $io->start("o1"); 142 | } 143 | 144 | sub save{ 145 | my ($dir,$filters)=@_; 146 | $dir//="."; 147 | $filters//="\.txt|\.pl|\.logo\$"; 148 | opendir(my $DIR, $dir) || die "Can't open directory $dir: $!"; 149 | my @files = sort(grep {(/$filters/i) && -f "$dir/$_" } readdir($DIR)); 150 | closedir $DIR; 151 | my $selector=new Term::Graille::Selector( 152 | redraw=>\&refreshScreen, 153 | #callback=>\&saveFile, 154 | callback=>\&confirmSave, 155 | options=>[@files], 156 | transient=>1, 157 | selected =>$currentFile, 158 | title=>"Save File", 159 | ); 160 | my $selId=$io->addObject(object => $selector); 161 | $io->start($selId); 162 | 163 | } 164 | 165 | sub confirmSave{ # if about to overwrite warn 166 | my ($param)=@_; 167 | my $file=$param->{selected}; 168 | if (-e $file){ 169 | my $dialog=new Term::Graille::Dialog( 170 | redraw=>\&refreshScreen, 171 | callback=>\&saveFile, 172 | message=>"Are you sure you want to save this file? it will overwrite $file", 173 | param=>{file=>$file,action=>"save"}, 174 | icon=>"info", 175 | title=>"Confirm Save", 176 | buttons=>[qw/Overwrite Cancel/] 177 | ); 178 | my $dialogId=$io->addObject(object => $dialog); 179 | $io->start($dialogId); 180 | } 181 | else{ 182 | saveFile({file=>$file}); 183 | } 184 | 185 | } 186 | 187 | sub saveFile{ 188 | my ($param)=@_; 189 | $io->close(); 190 | if ($param->{button} && $param->{button} eq "Cancel"){ 191 | $io->start("o1"); 192 | return; 193 | }; 194 | my $file=$param->{file}; 195 | open(my $fh, ">", "$file") or die("Can't open file $file:$! "); 196 | print $fh join("\n",@{$textarea->{text}}); 197 | close($fh); 198 | $textarea->{title}=$file; 199 | $currentFile=$file; 200 | $io->start("o1"); 201 | } 202 | 203 | sub message{ 204 | my ($message,$input)=@_; 205 | my $dialog=new Term::Graille::Dialog( 206 | redraw=>\&refreshScreen, 207 | callback=>\&messageReturn, 208 | message=>$message, 209 | icon=>"info", 210 | title=>"Test Message", 211 | ); 212 | $dialog->mode("ync"); 213 | my $dialogId=$io->addObject(object => $dialog); 214 | $io->start($dialogId); 215 | } 216 | 217 | sub messageReturn{ 218 | my ($ret,$params)=@_; 219 | $ret||="nothing" ; 220 | $params="" unless (defined $params); 221 | $io->close(); 222 | if ($ret){ printAt(2,50,"$ret $params returned") } 223 | 224 | } 225 | 226 | sub logo{ 227 | $canvas->logo(join (";",@{$textarea->{text}})); 228 | $canvas->{title}="Running $currentFile"; 229 | $canvas-> draw(); 230 | } 231 | 232 | -------------------------------------------------------------------------------- /examples/fontDemo.pl: -------------------------------------------------------------------------------- 1 | #! /usr/bin/env perl 2 | use strict; use warnings; 3 | use utf8; 4 | 5 | use open ":std", ":encoding(UTF-8)"; 6 | use Term::Graille qw/colour paint printAt clearScreen border/; 7 | use Term::Graille::Font qw/convertDG saveGrf loadGrf fontWrite/; 8 | use Time::HiRes qw/sleep/; 9 | 10 | 11 | my $canvas = Term::Graille->new( 12 | width => 120, 13 | height => 60, 14 | top=>4, 15 | left=>10, 16 | borderStyle => "double", 17 | ); 18 | 19 | my @fontNames=("Area51","Cinema Bold","Mutual","Everest","ZX Times"); 20 | my @fonts = map {my $f=loadGrf("./fonts/$_.grf");$f} @fontNames; 21 | my @texts = map {" $_->{info} 8x8 Fonts imported from https://damieng.com/typography/zx-origins/ "} @fonts; 22 | 23 | my @pointers=(0) x scalar @fontNames; 24 | 25 | fontWrite($canvas,$fonts[0],10,8,"Font Demo"); 26 | $canvas->textAt(30,25,"Mixed text is also possible"); 27 | $canvas->textAt(30,22,"printable directly on canvas"); 28 | 29 | $canvas->draw(); 30 | sleep 2; 31 | 32 | for (0..100){ 33 | for my $fn (0..$#fontNames){ 34 | fontWrite($canvas,$fonts[$fn],55,1+$fn*3,substr($texts[$fn],$pointers[$fn]++,1)); 35 | $pointers[$fn]=0 if ($pointers[$fn]>=length $texts[$fn]); 36 | } 37 | 38 | for (0..3){ 39 | $canvas->scroll("l"); 40 | sleep .05; 41 | $canvas->draw(); 42 | } 43 | 44 | } 45 | 46 | 47 | 48 | -------------------------------------------------------------------------------- /examples/fonts/Anvil Alternate.grf: -------------------------------------------------------------------------------- 1 | {'c' => [["⢀","⡤","⣄","⡄"],["⢸","⣇","⣠","⠆"]], 2 | '}' => [["⠈","⠹","⣇","⡀"],["⢀","⣸","⡇","⠀"]], 3 | 'G' => [["⣴","⠋","⠳","⠇"],["⢿","⣄","⢽","⡏"]], 4 | 'J' => [["⠀","⠈","⢹","⡏"],["⢾","⣀","⡼","⠃"]], 5 | 'd' => [["⢀","⡤","⣹","⡇"],["⠸","⣇","⡸","⣇"]], 6 | 'u' => [["⢀","⡄","⢀","⡄"],["⠹","⣇","⡼","⣇"]], 7 | 'm' => [["⣤","⣠","⣠","⡀"],["⣿","⠸","⢸","⡇"]], 8 | 'A' => [["⠀","⣴","⢻","⡇"],["⣹","⡏","⣹","⡇"]], 9 | ';' => [["⠀","⠰","⠆","⠀"],["⠀","⡲","⠂","⠀"]], 10 | 'H' => [["⢹","⣇","⣸","⡏"],["⣹","⡇","⢸","⣇"]], 11 | '7' => [["⠟","⠉","⣽","⠃"],["⢀","⣽","⡋","⠁"]], 12 | '"' => [["⠨","⠋","⠝","⠁"],["⠀","⠀","⠀","⠀"]], 13 | 'f' => [["⠀","⣴","⠋","⠓"],["⢉","⣿","⡉","⠀"]], 14 | '?' => [["⠸","⠋","⣹","⠆"],["⠀","⢨","⡅","⠀"]], 15 | 'I' => [["⠈","⢹","⡏","⠁"],["⢀","⣸","⣇","⡀"]], 16 | 'Q' => [["⣴","⠋","⢹","⡆"],["⠻","⣬","⡳","⣅"]], 17 | 'x' => [["⢤","⡀","⣤","⠄"],["⣠","⡟","⢧","⡀"]], 18 | ',' => [["⠀","⠀","⠀","⠀"],["⠀","⡲","⠂","⠀"]], 19 | 'e' => [["⠀","⣠","⢤","⡀"],["⢸","⣗","⣪","⠆"]], 20 | '3' => [["⠟","⣩","⡟","⠁"],["⣦","⣀","⣹","⠆"]], 21 | 'O' => [["⣴","⠋","⢻","⡄"],["⢻","⣄","⡼","⠃"]], 22 | '4' => [["⢴","⡇","⣾","⠀"],["⠛","⠒","⣿","⠂"]], 23 | 'w' => [["⣤","⢠","⢠","⡄"],["⣿","⠞","⢾","⡇"]], 24 | ')' => [["⠀","⠙","⣦","⠀"],["⠀","⣠","⠟","⠀"]], 25 | '8' => [["⣴","⢋","⣹","⠆"],["⢾","⣉","⡼","⠃"]], 26 | 'D' => [["⢹","⡏","⢷","⡀"],["⣸","⣇","⣸","⠇"]], 27 | 'W' => [["⣿","⢀","⢸","⡇"],["⣿","⠎","⢾","⡇"]], 28 | '_' => [["⠀","⠀","⠀","⠀"],["⣀","⣀","⣀","⣀"]], 29 | '<' => [["⢀","⡴","⠋","⠀"],["⠈","⠳","⣄","⠀"]], 30 | 'M' => [["⣿","⡄","⣼","⡇"],["⣿","⠘","⢸","⡇"]], 31 | 'R' => [["⢹","⡏","⣹","⠆"],["⣹","⡏","⢳","⣄"]], 32 | 'Z' => [["⠟","⢉","⡽","⠃"],["⣴","⣋","⣠","⡆"]], 33 | 'y' => [["⢤","⡄","⢠","⡤"],["⠀","⣹","⣏","⠀"]], 34 | '&' => [["⠰","⣏","⣓","⡀"],["⢾","⣁","⣻","⣄"]], 35 | "£" => [["⢀","⡾","⠱","⠇"],["⣹","⣏","⣡","⡆"]], 36 | 'U' => [["⣿","⠁","⢹","⡇"],["⠹","⣆","⡾","⠁"]], 37 | 'o' => [["⠀","⣠","⢤","⡄"],["⠸","⣇","⡼","⠃"]], 38 | 'E' => [["⢹","⣏","⡙","⠇"],["⣹","⣇","⣠","⡆"]], 39 | '$' => [["⠠","⣞","⡳","⡄"],["⠘","⢮","⡽","⠂"]], 40 | '@' => [["⣴","⢫","⢽","⡆"],["⢿","⣌","⣋","⠄"]], 41 | 's' => [["⢀","⡤","⢄","⡄"],["⠰","⣍","⣳","⠆"]], 42 | 'K' => [["⢹","⣇","⡼","⠃"],["⣹","⡇","⢻","⡄"]], 43 | '~' => [["⡾","⠳","⣴","⠇"],["⠀","⠀","⠀","⠀"]], 44 | '5' => [["⣿","⣉","⡙","⠇"],["⣦","⣀","⣹","⠆"]], 45 | 'j' => [["⠀","⠀","⢈","⡅"],["⠠","⣄","⣹","⠇"]], 46 | 'k' => [["⢴","⡇","⣠","⠄"],["⣸","⡟","⣧","⡀"]], 47 | 'F' => [["⢹","⣏","⡙","⠇"],["⣹","⣇","⠀","⠀"]], 48 | '0' => [["⣴","⢉","⢵","⡄"],["⠻","⣊","⡸","⠃"]], 49 | '\\' => [["⠳","⣄","⠀","⠀"],["⠀","⠈","⠳","⡄"]], 50 | '-' => [["⢀","⣀","⣀","⠀"],["⠀","⠀","⠀","⠀"]], 51 | 'X' => [["⠹","⣇","⡽","⠃"],["⣴","⡋","⢳","⣄"]], 52 | ':' => [["⠀","⠰","⠆","⠀"],["⠀","⠰","⠆","⠀"]], 53 | '(' => [["⠀","⣴","⠋","⠀"],["⠀","⠻","⣄","⠀"]], 54 | '2' => [["⠸","⠋","⣹","⠆"],["⣴","⣋","⣠","⡆"]], 55 | '^' => [["⣠","⣾","⣦","⡀"],["⠀","⡿","⡇","⠀"]], 56 | 'i' => [["⠀","⢈","⡅","⠀"],["⠀","⣹","⣇","⠀"]], 57 | 'T' => [["⠟","⢹","⡏","⠻"],["⠀","⣸","⣇","⠀"]], 58 | 'p' => [["⢀","⡤","⢤","⡀"],["⢹","⡗","⠼","⠇"]], 59 | 't' => [["⠤","⣾","⠤","⠄"],["⠀","⢿","⣠","⡆"]], 60 | 'n' => [["⢀","⣄","⢤","⡄"],["⢹","⡇","⢸","⡇"]], 61 | '+' => [["⢀","⣰","⣇","⡀"],["⠀","⠸","⠃","⠀"]], 62 | '9' => [["⠠","⣞","⢉","⣷"],["⢰","⣌","⣡","⠟"]], 63 | 'v' => [["⢀","⡄","⢀","⡄"],["⢹","⣧","⠞","⠁"]], 64 | 'r' => [["⢀","⣄","⢤","⡄"],["⢀","⣿","⡀","⠀"]], 65 | 'a' => [["⠀","⣠","⢤","⡄"],["⢸","⣇","⢼","⣇"]], 66 | 'B' => [["⢹","⡏","⣹","⠆"],["⣹","⣏","⣸","⠇"]], 67 | ' ' => [["⠀","⠀","⠀","⠀"],["⠀","⠀","⠀","⠀"]], 68 | '1' => [["⠀","⢴","⡇","⠀"],["⠀","⣸","⣇","⠀"]], 69 | '\'' => [["⠀","⠝","⠁","⠀"],["⠀","⠀","⠀","⠀"]], 70 | 'N' => [["⣿","⢦","⢹","⡏"],["⣿","⡀","⢻","⣇"]], 71 | '%' => [["⠺","⢃","⡴","⠃"],["⡴","⠋","⢴","⠆"]], 72 | 'C' => [["⣴","⠋","⠳","⠇"],["⢿","⣄","⣠","⠆"]], 73 | 'V' => [["⣿","⠁","⣽","⠋"],["⣿","⡼","⠃","⠀"]], 74 | 'z' => [["⢠","⡤","⢤","⡄"],["⢨","⣞","⣡","⡆"]], 75 | '.' => [["⠀","⠀","⠀","⠀"],["⠀","⠶","⠀","⠀"]], 76 | '6' => [["⣴","⣋","⡙","⠇"],["⠻","⣄","⣹","⡆"]], 77 | 'info' => ' Anvil Alternate font ','!' => [["⠀","⢴","⡇","⠀"],["⠀","⢨","⡅","⠀"]], 78 | '[' => [["⢨","⡟","⠉","⠀"],["⢸","⣇","⣀","⠀"]], 79 | 'h' => [["⢴","⣇","⢤","⡀"],["⣸","⡇","⣸","⡇"]], 80 | '=' => [["⠠","⠤","⠤","⠄"],["⠈","⠉","⠉","⠁"]], 81 | '#' => [["⢤","⡿","⣼","⠇"],["⣾","⢳","⡟","⠂"]], 82 | '/' => [["⠀","⢀","⡴","⠃"],["⡴","⠋","⠀","⠀"]], 83 | ']' => [["⠈","⠉","⣿","⠀"],["⢀","⣠","⣟","⠀"]], 84 | 'P' => [["⢹","⡏","⣹","⠆"],["⣹","⣏","⠀","⠀"]], 85 | '*' => [["⠠","⣴","⣦","⠄"],["⠈","⠙","⠋","⠁"]], 86 | 'b' => [["⢴","⣇","⢤","⡀"],["⣸","⣇","⣸","⠇"]], 87 | '|' => [["⠀","⢸","⡇","⠀"],["⠀","⢸","⡇","⠀"]], 88 | 'l' => [["⠀","⢴","⡇","⠀"],["⠀","⣸","⣇","⠀"]], 89 | 'Y' => [["⠹","⣇","⣸","⠏"],["⠀","⣸","⣇","⠀"]], 90 | "©" => [["⡔","⣩","⢭","⢢"],["⠣","⣛","⣒","⠜"]], 91 | '>' => [["⠈","⠳","⣄","⠀"],["⢀","⡴","⠋","⠀"]], 92 | '{' => [["⢀","⣸","⠏","⠁"],["⠀","⢸","⣇","⡀"]], 93 | 'L' => [["⢹","⡏","⠀","⠀"],["⣸","⣇","⣠","⡆"]], 94 | 'S' => [["⠰","⣏","⡓","⠇"],["⡷","⣄","⣹","⠆"]], 95 | 'g' => [["⠀","⣠","⠤","⠄"],["⢸","⣇","⣹","⡏"]], 96 | 'q' => [["⢀","⡤","⢤","⡀"],["⠸","⠧","⢺","⡏"]]} 97 | -------------------------------------------------------------------------------- /examples/fonts/Area51 Overwide.grf: -------------------------------------------------------------------------------- 1 | {'Q' => [["⢰","⠉","⡆","⠀"],["⠘","⠔","⠅","⠀"]], 2 | "£" => [["⢰","⣉","⠂","⠀"],["⠸","⠤","⠄","⠀"]], 3 | 'i' => [["⠠","⡅","⠀","⠀"],["⠀","⠧","⠀","⠀"]], 4 | 'w' => [["⡄","⠀","⡄","⠀"],["⠗","⠑","⠇","⠀"]], 5 | 'J' => [["⠀","⠀","⡇","⠀"],["⠘","⠤","⠃","⠀"]], 6 | '0' => [["⢰","⠉","⡆","⠀"],["⠘","⠤","⠃","⠀"]], 7 | '%' => [["⠰","⢀","⠆","⠀"],["⠰","⠁","⠆","⠀"]], 8 | '|' => [["⠀","⡇","⠀","⠀"],["⠀","⠇","⠀","⠀"]], 9 | '<' => [["⢀","⠔","⠀","⠀"],["⠀","⠑","⠀","⠀"]], 10 | 'I' => [["⠈","⡏","⠀","⠀"],["⠠","⠧","⠀","⠀"]], 11 | 'X' => [["⠣","⡠","⠃","⠀"],["⠎","⠈","⠆","⠀"]], 12 | 'r' => [["⢠","⡠","⠄","⠀"],["⠸","⠀","⠀","⠀"]], 13 | 'U' => [["⢸","⠀","⡇","⠀"],["⠘","⠤","⠃","⠀"]], 14 | '9' => [["⠰","⣉","⡆","⠀"],["⠀","⠔","⠁","⠀"]], 15 | '7' => [["⠈","⢉","⠇","⠀"],["⠀","⠎","⠀","⠀"]], 16 | ',' => [["⠀","⠀","⠀","⠀"],["⢀","⠆","⠀","⠀"]], 17 | 'a' => [["⠀","⠤","⡀","⠀"],["⠐","⠭","⠇","⠀"]], 18 | '2' => [["⠈","⢉","⠆","⠀"],["⠰","⠥","⠄","⠀"]], 19 | '>' => [["⠐","⢄","⠀","⠀"],["⠐","⠁","⠀","⠀"]], 20 | 'V' => [["⢸","⠀","⡇","⠀"],["⠸","⠔","⠁","⠀"]], 21 | 'g' => [["⢀","⠤","⡄","⠀"],["⠈","⣒","⠇","⠀"]], 22 | ' ' => [["⠀","⠀","⠀","⠀"],["⠀","⠀","⠀","⠀"]], 23 | 'Y' => [["⠣","⡠","⠃","⠀"],["⠀","⠇","⠀","⠀"]], 24 | 'G' => [["⢰","⢉","⡁","⠀"],["⠘","⠤","⠇","⠀"]], 25 | 'P' => [["⢸","⣉","⠆","⠀"],["⠸","⠀","⠀","⠀"]], 26 | 'v' => [["⠠","⡀","⡠","⠀"],["⠀","⠱","⠁","⠀"]], 27 | '!' => [["⠀","⡇","⠀","⠀"],["⠀","⠅","⠀","⠀"]], 28 | '.' => [["⠀","⠀","⠀","⠀"],["⠀","⠆","⠀","⠀"]], 29 | 'O' => [["⢰","⠉","⡆","⠀"],["⠘","⠤","⠃","⠀"]], 30 | 'q' => [["⢀","⠤","⡄","⠀"],["⠈","⠒","⡇","⠀"]], 31 | '4' => [["⢀","⠎","⡇","⠀"],["⠈","⠉","⠇","⠀"]], 32 | '&' => [["⠰","⣉","⡄","⠀"],["⠘","⠤","⠇","⠀"]], 33 | 'A' => [["⢰","⣉","⡆","⠀"],["⠸","⠀","⠇","⠀"]], 34 | 'H' => [["⢸","⣀","⡇","⠀"],["⠸","⠀","⠇","⠀"]], 35 | ']' => [["⠈","⢹","⠀","⠀"],["⠠","⠼","⠀","⠀"]], 36 | '6' => [["⢠","⣊","⠀","⠀"],["⠘","⠤","⠃","⠀"]], 37 | 'b' => [["⢸","⠤","⡀","⠀"],["⠸","⠤","⠃","⠀"]], 38 | 'Z' => [["⠈","⡩","⠃","⠀"],["⠸","⠤","⠄","⠀"]], 39 | 'e' => [["⢀","⠤","⡀","⠀"],["⠘","⠭","⠅","⠀"]], 40 | '{' => [["⢀","⠎","⠀","⠀"],["⠀","⠣","⠀","⠀"]], 41 | 'u' => [["⢠","⠀","⡄","⠀"],["⠘","⠤","⠇","⠀"]], 42 | 'x' => [["⢄","⢀","⠄","⠀"],["⠔","⠑","⠄","⠀"]], 43 | '+' => [["⢀","⣆","⠀","⠀"],["⠀","⠃","⠀","⠀"]], 44 | 's' => [["⢀","⠤","⠄","⠀"],["⠠","⠭","⠂","⠀"]], 45 | '5' => [["⢸","⣉","⠁","⠀"],["⠠","⠤","⠃","⠀"]], 46 | 'j' => [["⠀","⠀","⡅","⠀"],["⠰","⣀","⠇","⠀"]], 47 | 'd' => [["⢀","⠤","⡇","⠀"],["⠘","⠤","⠇","⠀"]], 48 | 'T' => [["⠈","⡏","⠀","⠀"],["⠀","⠇","⠀","⠀"]], 49 | 'y' => [["⢠","⠀","⡄","⠀"],["⠈","⣒","⠇","⠀"]], 50 | '8' => [["⠰","⣉","⠆","⠀"],["⠘","⠤","⠃","⠀"]], 51 | '~' => [["⠠","⠢","⠂","⠀"],["⠀","⠀","⠀","⠀"]], 52 | '3' => [["⠈","⣩","⠃","⠀"],["⠐","⠤","⠃","⠀"]], 53 | '^' => [["⠔","⡗","⠄","⠀"],["⠀","⠇","⠀","⠀"]], 54 | '"' => [["⠠","⠣","⠃","⠀"],["⠀","⠀","⠀","⠀"]], 55 | '#' => [["⢰","⠤","⡆","⠀"],["⠸","⠒","⠇","⠀"]], 56 | 't' => [["⢸","⠤","⠀","⠀"],["⠘","⠤","⠂","⠀"]], 57 | 'z' => [["⠠","⢤","⠀","⠀"],["⠰","⠥","⠀","⠀"]], 58 | ')' => [["⠈","⢢","⠀","⠀"],["⠠","⠊","⠀","⠀"]], 59 | 'B' => [["⢸","⣉","⠆","⠀"],["⠸","⠤","⠃","⠀"]], 60 | ';' => [["⠀","⡄","⠀","⠀"],["⢀","⠆","⠀","⠀"]], 61 | 'k' => [["⢸","⢀","⠄","⠀"],["⠸","⠑","⠄","⠀"]], 62 | 'h' => [["⢸","⠤","⡀","⠀"],["⠸","⠀","⠇","⠀"]], 63 | '$' => [["⠠","⡚","⠄","⠀"],["⠐","⡬","⠂","⠀"]], 64 | 'D' => [["⢸","⠉","⡆","⠀"],["⠸","⠤","⠃","⠀"]], 65 | ':' => [["⠀","⡄","⠀","⠀"],["⠀","⠆","⠀","⠀"]], 66 | '\\' => [["⠈","⢆","⠀","⠀"],["⠀","⠈","⠆","⠀"]], 67 | 'E' => [["⢸","⣉","⠁","⠀"],["⠸","⠤","⠄","⠀"]], 68 | '(' => [["⢠","⠊","⠀","⠀"],["⠈","⠢","⠀","⠀"]], 69 | '=' => [["⠠","⠤","⠄","⠀"],["⠈","⠉","⠁","⠀"]], 70 | '@' => [["⢰","⡩","⡆","⠀"],["⠘","⠬","⠅","⠀"]], 71 | 'm' => [["⡠","⡠","⡀","⠀"],["⠇","⠁","⠇","⠀"]], 72 | '-' => [["⢀","⣀","⡀","⠀"],["⠀","⠀","⠀","⠀"]], 73 | 'p' => [["⢠","⠤","⡀","⠀"],["⢸","⠒","⠁","⠀"]], 74 | 'N' => [["⢸","⠢","⡇","⠀"],["⠸","⠀","⠇","⠀"]], 75 | 'o' => [["⢀","⠤","⡀","⠀"],["⠘","⠤","⠃","⠀"]], 76 | '/' => [["⠀","⡰","⠁","⠀"],["⠰","⠁","⠀","⠀"]], 77 | 'F' => [["⢸","⣉","⠁","⠀"],["⠸","⠀","⠀","⠀"]], 78 | 'M' => [["⡗","⡔","⡇","⠀"],["⠇","⠀","⠇","⠀"]], 79 | 'l' => [["⠈","⡇","⠀","⠀"],["⠀","⠧","⠀","⠀"]], 80 | '[' => [["⢸","⠉","⠀","⠀"],["⠸","⠤","⠀","⠀"]], 81 | 'c' => [["⢀","⠤","⡀","⠀"],["⠘","⠤","⠂","⠀"]], 82 | '_' => [["⠀","⠀","⠀","⠀"],["⣀","⣀","⡀","⠀"]], 83 | 'L' => [["⢸","⠀","⠀","⠀"],["⠸","⠤","⠄","⠀"]], 84 | '?' => [["⠐","⢉","⠆","⠀"],["⠀","⠅","⠀","⠀"]], 85 | 'info' => ' Area51 Overwide font from https://damieng.com/zx-origins','W' => [["⡇","⡀","⡇","⠀"],["⠗","⠑","⠇","⠀"]], 86 | 'R' => [["⢸","⣉","⠆","⠀"],["⠸","⠈","⠆","⠀"]], 87 | 'n' => [["⢠","⠤","⡀","⠀"],["⠸","⠀","⠇","⠀"]], 88 | 'C' => [["⢰","⠉","⠂","⠀"],["⠘","⠤","⠂","⠀"]], 89 | '}' => [["⠈","⢆","⠀","⠀"],["⠠","⠃","⠀","⠀"]], 90 | '1' => [["⠐","⡇","⠀","⠀"],["⠠","⠧","⠀","⠀"]], 91 | 'S' => [["⠰","⣉","⠁","⠀"],["⠠","⠤","⠃","⠀"]], 92 | '*' => [["⢐","⣔","⠀","⠀"],["⠐","⠑","⠀","⠀"]], 93 | 'f' => [["⢰","⣉","⠂","⠀"],["⠸","⠀","⠀","⠀"]], 94 | 'K' => [["⢸","⡠","⠃","⠀"],["⠸","⠈","⠆","⠀"]], 95 | '\'' => [["⠠","⠃","⠀","⠀"],["⠀","⠀","⠀","⠀"]], 96 | "©" => [["⣎","⠍","⡆","⠀"],["⢏","⣂","⠇","⠀"]]} 97 | -------------------------------------------------------------------------------- /examples/fonts/Area51 Serif Bold.grf: -------------------------------------------------------------------------------- 1 | {':' => [["⠀","⣤","⠀","⠀"],["⠀","⠶","⠀","⠀"]], 2 | '\\' => [["⠘","⣧","⠀","⠀"],["⠀","⠹","⠆","⠀"]], 3 | '=' => [["⠠","⠤","⠄","⠀"],["⠈","⠉","⠁","⠀"]], 4 | 'E' => [["⢸","⣏","⠃","⠀"],["⠸","⠧","⠆","⠀"]], 5 | '(' => [["⢠","⡎","⠀","⠀"],["⠈","⠣","⠀","⠀"]], 6 | '-' => [["⢀","⣀","⡀","⠀"],["⠀","⠀","⠀","⠀"]], 7 | 'm' => [["⢠","⣀","⡄","⠀"],["⠸","⠇","⠇","⠀"]], 8 | '@' => [["⢰","⡯","⡆","⠀"],["⠘","⠯","⠅","⠀"]], 9 | 'o' => [["⢀","⡤","⡀","⠀"],["⠘","⠧","⠃","⠀"]], 10 | 'N' => [["⢸","⡦","⡇","⠀"],["⠸","⠇","⠇","⠀"]], 11 | '/' => [["⠀","⣼","⠃","⠀"],["⠰","⠏","⠀","⠀"]], 12 | 'p' => [["⢠","⡤","⡀","⠀"],["⢸","⡗","⠁","⠀"]], 13 | 'c' => [["⢀","⡤","⡄","⠀"],["⠘","⠧","⠄","⠀"]], 14 | '_' => [["⠀","⠀","⠀","⠀"],["⣀","⣀","⡀","⠀"]], 15 | '?' => [["⠘","⣹","⠆","⠀"],["⠨","⠅","⠀","⠀"]], 16 | 'L' => [["⢸","⡇","⠀","⠀"],["⠸","⠧","⠆","⠀"]], 17 | 'F' => [["⢸","⣏","⠃","⠀"],["⠸","⠇","⠀","⠀"]], 18 | 'M' => [["⢸","⡖","⡇","⠀"],["⠸","⠇","⠇","⠀"]], 19 | 'l' => [["⠈","⣿","⠀","⠀"],["⠠","⠿","⠄","⠀"]], 20 | '[' => [["⢸","⡏","⠀","⠀"],["⠸","⠧","⠀","⠀"]], 21 | 'W' => [["⢸","⡇","⡇","⠀"],["⠸","⠓","⠇","⠀"]], 22 | 'n' => [["⢠","⡤","⡀","⠀"],["⠸","⠇","⠇","⠀"]], 23 | 'R' => [["⢸","⣏","⠆","⠀"],["⠸","⠇","⠇","⠀"]], 24 | 'info' => ' Area51 Serif Bold font from https://damieng.com/zx-origins','}' => [["⠈","⢷","⡀","⠀"],["⠠","⠟","⠀","⠀"]], 25 | '1' => [["⠐","⣿","⠀","⠀"],["⠠","⠿","⠄","⠀"]], 26 | 'C' => [["⢰","⡏","⠃","⠀"],["⠘","⠧","⠆","⠀"]], 27 | 'K' => [["⢸","⡧","⠃","⠀"],["⠸","⠏","⠆","⠀"]], 28 | '\'' => [["⠰","⠃","⠀","⠀"],["⠀","⠀","⠀","⠀"]], 29 | "©" => [["⣎","⡭","⢱","⠀"],["⢏","⣓","⡸","⠀"]], 30 | 'S' => [["⠰","⣏","⠃","⠀"],["⠰","⠼","⠃","⠀"]], 31 | '*' => [["⢐","⣴","⡂","⠀"],["⠐","⠋","⠂","⠀"]], 32 | 'f' => [["⢰","⣏","⠃","⠀"],["⠸","⠇","⠀","⠀"]], 33 | '+' => [["⢀","⣶","⡀","⠀"],["⠀","⠛","⠀","⠀"]], 34 | 'x' => [["⢠","⡄","⡄","⠀"],["⠰","⠹","⠆","⠀"]], 35 | 's' => [["⢀","⡤","⠄","⠀"],["⠠","⠽","⠂","⠀"]], 36 | 'u' => [["⢠","⢠","⡄","⠀"],["⠘","⠼","⠇","⠀"]], 37 | '{' => [["⢀","⡾","⠁","⠀"],["⠀","⠻","⠄","⠀"]], 38 | 'j' => [["⠀","⢬","⡅","⠀"],["⢠","⣸","⠇","⠀"]], 39 | 'd' => [["⢀","⢽","⡇","⠀"],["⠘","⠼","⠇","⠀"]], 40 | 'T' => [["⠈","⣿","⠁","⠀"],["⠀","⠿","⠀","⠀"]], 41 | '5' => [["⢸","⣏","⠃","⠀"],["⠐","⠼","⠃","⠀"]], 42 | '3' => [["⠘","⣹","⠇","⠀"],["⠐","⠼","⠃","⠀"]], 43 | 'y' => [["⢠","⢠","⡄","⠀"],["⢠","⠟","⠀","⠀"]], 44 | '8' => [["⠰","⣏","⠆","⠀"],["⠘","⠼","⠃","⠀"]], 45 | '~' => [["⠰","⠢","⠆","⠀"],["⠀","⠀","⠀","⠀"]], 46 | '#' => [["⢰","⡦","⡆","⠀"],["⠸","⠗","⠇","⠀"]], 47 | '"' => [["⠰","⠳","⠃","⠀"],["⠀","⠀","⠀","⠀"]], 48 | ')' => [["⠈","⣦","⠀","⠀"],["⠠","⠋","⠀","⠀"]], 49 | 't' => [["⢸","⡧","⠀","⠀"],["⠘","⠧","⠆","⠀"]], 50 | 'z' => [["⠠","⣤","⠀","⠀"],["⠰","⠧","⠀","⠀"]], 51 | '^' => [["⠐","⣿","⠂","⠀"],["⠀","⠿","⠀","⠀"]], 52 | 'B' => [["⢸","⣏","⠆","⠀"],["⠸","⠧","⠃","⠀"]], 53 | 'k' => [["⢸","⣇","⠄","⠀"],["⠸","⠗","⠄","⠀"]], 54 | ';' => [["⠀","⣤","⠀","⠀"],["⠀","⡴","⠀","⠀"]], 55 | '$' => [["⠠","⣞","⠆","⠀"],["⠰","⡽","⠂","⠀"]], 56 | 'D' => [["⢸","⡏","⡆","⠀"],["⠸","⠧","⠃","⠀"]], 57 | 'h' => [["⢸","⡧","⡀","⠀"],["⠸","⠇","⠇","⠀"]], 58 | 'g' => [["⢀","⢤","⡄","⠀"],["⠈","⣺","⠇","⠀"]], 59 | ' ' => [["⠀","⠀","⠀","⠀"],["⠀","⠀","⠀","⠀"]], 60 | 'V' => [["⢸","⡇","⡇","⠀"],["⠸","⠗","⠁","⠀"]], 61 | 'Y' => [["⠸","⣸","⡇","⠀"],["⠀","⠿","⠀","⠀"]], 62 | 'P' => [["⢸","⣏","⠆","⠀"],["⠸","⠇","⠀","⠀"]], 63 | 'G' => [["⢰","⡏","⡃","⠀"],["⠘","⠧","⠇","⠀"]], 64 | 'v' => [["⢠","⡄","⣤","⠀"],["⠀","⠻","⠃","⠀"]], 65 | 'O' => [["⢰","⡏","⡆","⠀"],["⠘","⠧","⠃","⠀"]], 66 | '!' => [["⠀","⣿","⠀","⠀"],["⠀","⠭","⠀","⠀"]], 67 | '.' => [["⠀","⠀","⠀","⠀"],["⠰","⠆","⠀","⠀"]], 68 | 'q' => [["⢀","⢤","⡄","⠀"],["⠈","⢺","⡇","⠀"]], 69 | '&' => [["⠰","⣏","⡃","⠀"],["⠘","⠯","⠆","⠀"]], 70 | '4' => [["⢀","⢾","⡇","⠀"],["⠈","⠽","⠇","⠀"]], 71 | 'A' => [["⢰","⣏","⡆","⠀"],["⠸","⠇","⠇","⠀"]], 72 | 'H' => [["⢸","⣇","⡇","⠀"],["⠸","⠇","⠇","⠀"]], 73 | ']' => [["⠈","⣿","⠀","⠀"],["⠠","⠿","⠀","⠀"]], 74 | 'Z' => [["⠘","⣹","⠇","⠀"],["⠸","⠧","⠆","⠀"]], 75 | 'e' => [["⢀","⡤","⡀","⠀"],["⠘","⠯","⠅","⠀"]], 76 | '6' => [["⢠","⣞","⠀","⠀"],["⠘","⠧","⠃","⠀"]], 77 | 'b' => [["⢸","⡧","⡀","⠀"],["⠸","⠧","⠃","⠀"]], 78 | 'Q' => [["⢰","⡏","⡆","⠀"],["⠘","⠷","⡅","⠀"]], 79 | 'J' => [["⠀","⢹","⡇","⠀"],["⠸","⠼","⠃","⠀"]], 80 | '0' => [["⢰","⡏","⡆","⠀"],["⠘","⠧","⠃","⠀"]], 81 | 'w' => [["⢠","⡄","⡄","⠀"],["⠸","⠓","⠇","⠀"]], 82 | 'i' => [["⠠","⣭","⠀","⠀"],["⠠","⠿","⠄","⠀"]], 83 | "£" => [["⢰","⣏","⠃","⠀"],["⠸","⠧","⠆","⠀"]], 84 | '%' => [["⠰","⣠","⠆","⠀"],["⠰","⠋","⠆","⠀"]], 85 | 'I' => [["⠈","⣿","⠁","⠀"],["⠠","⠿","⠄","⠀"]], 86 | 'X' => [["⠸","⣇","⠇","⠀"],["⠸","⠸","⠇","⠀"]], 87 | 'r' => [["⢠","⣄","⡄","⠀"],["⠸","⠇","⠀","⠀"]], 88 | 'U' => [["⢸","⡇","⡇","⠀"],["⠘","⠧","⠃","⠀"]], 89 | '|' => [["⠀","⣿","⠀","⠀"],["⠀","⠿","⠀","⠀"]], 90 | '<' => [["⢀","⡴","⠂","⠀"],["⠀","⠙","⠂","⠀"]], 91 | '9' => [["⠰","⣹","⡆","⠀"],["⠀","⠾","⠁","⠀"]], 92 | '7' => [["⢘","⣹","⠇","⠀"],["⠰","⠏","⠀","⠀"]], 93 | 'a' => [["⠀","⢤","⡀","⠀"],["⠐","⠽","⠇","⠀"]], 94 | ',' => [["⠀","⠀","⠀","⠀"],["⢠","⠆","⠀","⠀"]], 95 | '>' => [["⠐","⢦","⡀","⠀"],["⠐","⠋","⠀","⠀"]], 96 | '2' => [["⠘","⣹","⠆","⠀"],["⠸","⠧","⠄","⠀"]]} 97 | -------------------------------------------------------------------------------- /examples/fonts/Area51.grf: -------------------------------------------------------------------------------- 1 | {'C' => [["⢰","⠉","⠂","⠀"],["⠘","⠤","⠂","⠀"]], 2 | '}' => [["⠈","⢆","⠀","⠀"],["⠠","⠃","⠀","⠀"]], 3 | '1' => [["⠐","⡇","⠀","⠀"],["⠠","⠧","⠀","⠀"]], 4 | 'S' => [["⠰","⣉","⠁","⠀"],["⠠","⠤","⠃","⠀"]], 5 | '*' => [["⢐","⣔","⠀","⠀"],["⠐","⠑","⠀","⠀"]], 6 | 'f' => [["⢰","⣉","⠂","⠀"],["⠸","⠀","⠀","⠀"]], 7 | '\'' => [["⠠","⠃","⠀","⠀"],["⠀","⠀","⠀","⠀"]], 8 | 'K' => [["⢸","⡠","⠃","⠀"],["⠸","⠈","⠆","⠀"]], 9 | "©" => [["⣎","⠍","⡆","⠀"],["⢏","⣂","⠇","⠀"]], 10 | 'F' => [["⢸","⣉","⠁","⠀"],["⠸","⠀","⠀","⠀"]], 11 | 'M' => [["⢸","⠒","⡇","⠀"],["⠸","⠀","⠇","⠀"]], 12 | 'l' => [["⠈","⡇","⠀","⠀"],["⠀","⠧","⠀","⠀"]], 13 | '[' => [["⢸","⠉","⠀","⠀"],["⠸","⠤","⠀","⠀"]], 14 | '_' => [["⠀","⠀","⠀","⠀"],["⣀","⣀","⡀","⠀"]], 15 | 'c' => [["⢀","⠤","⡀","⠀"],["⠘","⠤","⠂","⠀"]], 16 | 'L' => [["⢸","⠀","⠀","⠀"],["⠸","⠤","⠄","⠀"]], 17 | '?' => [["⠐","⢉","⠆","⠀"],["⠀","⠅","⠀","⠀"]], 18 | 'info' => ' Area51 font from https://damieng.com/zx-origins','R' => [["⢸","⣉","⠆","⠀"],["⠸","⠈","⠆","⠀"]], 19 | 'W' => [["⢸","⠀","⡇","⠀"],["⠸","⠒","⠇","⠀"]], 20 | 'n' => [["⢠","⠤","⡀","⠀"],["⠸","⠀","⠇","⠀"]], 21 | '@' => [["⢰","⡩","⡆","⠀"],["⠘","⠬","⠅","⠀"]], 22 | 'm' => [["⢠","⣀","⡄","⠀"],["⠸","⠀","⠇","⠀"]], 23 | '-' => [["⢀","⣀","⡀","⠀"],["⠀","⠀","⠀","⠀"]], 24 | 'p' => [["⢠","⠤","⡀","⠀"],["⢸","⠒","⠁","⠀"]], 25 | 'N' => [["⢸","⠢","⡇","⠀"],["⠸","⠀","⠇","⠀"]], 26 | 'o' => [["⢀","⠤","⡀","⠀"],["⠘","⠤","⠃","⠀"]], 27 | '/' => [["⠀","⡰","⠁","⠀"],["⠰","⠁","⠀","⠀"]], 28 | '\\' => [["⠈","⢆","⠀","⠀"],["⠀","⠈","⠆","⠀"]], 29 | ':' => [["⠀","⡄","⠀","⠀"],["⠀","⠆","⠀","⠀"]], 30 | 'E' => [["⢸","⣉","⠁","⠀"],["⠸","⠤","⠄","⠀"]], 31 | '(' => [["⢠","⠊","⠀","⠀"],["⠈","⠢","⠀","⠀"]], 32 | '=' => [["⠠","⠤","⠄","⠀"],["⠈","⠉","⠁","⠀"]], 33 | ';' => [["⠀","⡄","⠀","⠀"],["⢀","⠆","⠀","⠀"]], 34 | 'k' => [["⢸","⢀","⠄","⠀"],["⠸","⠑","⠄","⠀"]], 35 | 'h' => [["⢸","⠤","⡀","⠀"],["⠸","⠀","⠇","⠀"]], 36 | '$' => [["⠠","⡚","⠄","⠀"],["⠐","⡬","⠂","⠀"]], 37 | 'D' => [["⢸","⠉","⡆","⠀"],["⠸","⠤","⠃","⠀"]], 38 | '^' => [["⠐","⡗","⠀","⠀"],["⠀","⠇","⠀","⠀"]], 39 | '"' => [["⠠","⠣","⠃","⠀"],["⠀","⠀","⠀","⠀"]], 40 | '#' => [["⢰","⠤","⡆","⠀"],["⠸","⠒","⠇","⠀"]], 41 | ')' => [["⠈","⢢","⠀","⠀"],["⠠","⠊","⠀","⠀"]], 42 | 'z' => [["⠠","⢤","⠀","⠀"],["⠰","⠥","⠀","⠀"]], 43 | 't' => [["⢸","⠤","⠀","⠀"],["⠘","⠤","⠂","⠀"]], 44 | 'B' => [["⢸","⣉","⠆","⠀"],["⠸","⠤","⠃","⠀"]], 45 | 'y' => [["⢠","⠀","⡄","⠀"],["⠈","⣒","⠇","⠀"]], 46 | '8' => [["⠰","⣉","⠆","⠀"],["⠘","⠤","⠃","⠀"]], 47 | '~' => [["⠠","⠢","⠂","⠀"],["⠀","⠀","⠀","⠀"]], 48 | '3' => [["⠈","⣩","⠃","⠀"],["⠐","⠤","⠃","⠀"]], 49 | '{' => [["⢀","⠎","⠀","⠀"],["⠀","⠣","⠀","⠀"]], 50 | 'u' => [["⢠","⠀","⡄","⠀"],["⠘","⠤","⠇","⠀"]], 51 | 'x' => [["⢠","⠀","⡄","⠀"],["⠰","⠉","⠆","⠀"]], 52 | '+' => [["⢀","⣆","⠀","⠀"],["⠀","⠃","⠀","⠀"]], 53 | 's' => [["⢀","⠤","⠄","⠀"],["⠠","⠭","⠂","⠀"]], 54 | '5' => [["⢸","⣉","⠁","⠀"],["⠠","⠤","⠃","⠀"]], 55 | 'j' => [["⠀","⠀","⡅","⠀"],["⠰","⣀","⠇","⠀"]], 56 | 'd' => [["⢀","⠤","⡇","⠀"],["⠘","⠤","⠇","⠀"]], 57 | 'T' => [["⠈","⡏","⠀","⠀"],["⠀","⠇","⠀","⠀"]], 58 | 'A' => [["⢰","⣉","⡆","⠀"],["⠸","⠀","⠇","⠀"]], 59 | 'H' => [["⢸","⣀","⡇","⠀"],["⠸","⠀","⠇","⠀"]], 60 | ']' => [["⠈","⢹","⠀","⠀"],["⠠","⠼","⠀","⠀"]], 61 | '6' => [["⢠","⣊","⠀","⠀"],["⠘","⠤","⠃","⠀"]], 62 | 'b' => [["⢸","⠤","⡀","⠀"],["⠸","⠤","⠃","⠀"]], 63 | 'Z' => [["⠈","⡩","⠃","⠀"],["⠸","⠤","⠄","⠀"]], 64 | 'e' => [["⢀","⠤","⡀","⠀"],["⠘","⠭","⠅","⠀"]], 65 | '4' => [["⢀","⠎","⡇","⠀"],["⠈","⠉","⠇","⠀"]], 66 | '&' => [["⠰","⣉","⡄","⠀"],["⠘","⠤","⠇","⠀"]], 67 | '!' => [["⠀","⡇","⠀","⠀"],["⠀","⠅","⠀","⠀"]], 68 | '.' => [["⠀","⠀","⠀","⠀"],["⠀","⠆","⠀","⠀"]], 69 | 'O' => [["⢰","⠉","⡆","⠀"],["⠘","⠤","⠃","⠀"]], 70 | 'q' => [["⢀","⠤","⡄","⠀"],["⠈","⠒","⡇","⠀"]], 71 | 'V' => [["⢸","⠀","⡇","⠀"],["⠸","⠔","⠁","⠀"]], 72 | 'g' => [["⢀","⠤","⡄","⠀"],["⠈","⣒","⠇","⠀"]], 73 | ' ' => [["⠀","⠀","⠀","⠀"],["⠀","⠀","⠀","⠀"]], 74 | 'Y' => [["⠸","⣀","⡇","⠀"],["⠠","⠤","⠃","⠀"]], 75 | 'P' => [["⢸","⣉","⠆","⠀"],["⠸","⠀","⠀","⠀"]], 76 | 'G' => [["⢰","⢉","⡁","⠀"],["⠘","⠤","⠇","⠀"]], 77 | 'v' => [["⢠","⠀","⡄","⠀"],["⠸","⠔","⠁","⠀"]], 78 | ',' => [["⠀","⠀","⠀","⠀"],["⢀","⠆","⠀","⠀"]], 79 | 'a' => [["⠀","⠤","⡀","⠀"],["⠐","⠭","⠇","⠀"]], 80 | '2' => [["⠈","⢉","⠆","⠀"],["⠰","⠥","⠄","⠀"]], 81 | '>' => [["⠐","⢄","⠀","⠀"],["⠐","⠁","⠀","⠀"]], 82 | '|' => [["⠀","⡇","⠀","⠀"],["⠀","⠇","⠀","⠀"]], 83 | '<' => [["⢀","⠔","⠀","⠀"],["⠀","⠑","⠀","⠀"]], 84 | 'I' => [["⠈","⡏","⠀","⠀"],["⠠","⠧","⠀","⠀"]], 85 | 'X' => [["⠸","⣀","⠇","⠀"],["⠸","⠀","⠇","⠀"]], 86 | 'r' => [["⢠","⡠","⠄","⠀"],["⠸","⠀","⠀","⠀"]], 87 | 'U' => [["⢸","⠀","⡇","⠀"],["⠘","⠤","⠃","⠀"]], 88 | '9' => [["⠰","⣉","⡆","⠀"],["⠀","⠔","⠁","⠀"]], 89 | '7' => [["⠈","⢉","⠇","⠀"],["⠀","⠎","⠀","⠀"]], 90 | 'w' => [["⢠","⠀","⡄","⠀"],["⠸","⠒","⠇","⠀"]], 91 | "£" => [["⢰","⣉","⠂","⠀"],["⠸","⠤","⠄","⠀"]], 92 | 'i' => [["⠠","⡅","⠀","⠀"],["⠀","⠧","⠀","⠀"]], 93 | 'J' => [["⠀","⠀","⡇","⠀"],["⠘","⠤","⠃","⠀"]], 94 | '0' => [["⢰","⠉","⡆","⠀"],["⠘","⠤","⠃","⠀"]], 95 | '%' => [["⠰","⢀","⠆","⠀"],["⠰","⠁","⠆","⠀"]], 96 | 'Q' => [["⢰","⠉","⡆","⠀"],["⠘","⠔","⠅","⠀"]]} 97 | -------------------------------------------------------------------------------- /examples/fonts/Cinema Bold.grf: -------------------------------------------------------------------------------- 1 | {'l' => [["⠀","⢹","⣿","⠀"],["⠀","⠸","⠿","⠀"]], 2 | '[' => [["⢸","⣿","⠉","⠀"],["⠸","⠿","⠤","⠀"]], 3 | 'M' => [["⣿","⣦","⣴","⡇"],["⠿","⠏","⠹","⠇"]], 4 | 'F' => [["⣿","⣏","⡉","⠁"],["⠿","⠇","⠀","⠀"]], 5 | 'L' => [["⣿","⡇","⠀","⠀"],["⠿","⠧","⠤","⠄"]], 6 | '?' => [["⠚","⢉","⣿","⠆"],["⠀","⠶","⠆","⠀"]], 7 | '_' => [["⠀","⠀","⠀","⠀"],["⣀","⣀","⣀","⣀"]], 8 | 'c' => [["⣠","⡤","⢤","⡀"],["⠻","⠧","⠴","⠂"]], 9 | 'info' => ' Cinema Bold font ','R' => [["⣿","⣏","⣹","⠆"],["⠿","⠇","⠹","⠆"]], 10 | 'n' => [["⣤","⡤","⢤","⡀"],["⠿","⠇","⠸","⠇"]], 11 | 'W' => [["⣿","⣇","⣸","⡇"],["⠿","⠋","⠙","⠇"]], 12 | 'C' => [["⣾","⡏","⠙","⠂"],["⠻","⠧","⠴","⠂"]], 13 | '}' => [["⠉","⢿","⣆","⡀"],["⠤","⠿","⠃","⠀"]], 14 | '1' => [["⠐","⣿","⡇","⠀"],["⠠","⠿","⠧","⠀"]], 15 | 'S' => [["⢾","⣏","⣙","⠂"],["⠲","⠦","⠼","⠃"]], 16 | 'f' => [["⣰","⣿","⡙","⠂"],["⠸","⠿","⠀","⠀"]], 17 | '*' => [["⢤","⣶","⣦","⠄"],["⠉","⠛","⠋","⠁"]], 18 | '\'' => [["⠠","⠿","⠃","⠀"],["⠀","⠀","⠀","⠀"]], 19 | 'K' => [["⣿","⣇","⡼","⠃"],["⠿","⠇","⠹","⠆"]], 20 | "©" => [["⡔","⣩","⡭","⢢"],["⠣","⣙","⣓","⠜"]], 21 | '\\' => [["⠻","⣷","⣄","⠀"],["⠀","⠈","⠻","⠷"]], 22 | ':' => [["⠀","⣤","⡄","⠀"],["⠀","⠶","⠆","⠀"]], 23 | '(' => [["⢠","⣾","⠋","⠁"],["⠈","⠻","⠦","⠄"]], 24 | 'E' => [["⣿","⣏","⡉","⠁"],["⠿","⠧","⠤","⠄"]], 25 | '=' => [["⠠","⠤","⠤","⠄"],["⠈","⠉","⠉","⠁"]], 26 | '@' => [["⣾","⡏","⣽","⡆"],["⠻","⠧","⠭","⠅"]], 27 | 'm' => [["⣤","⣄","⣤","⡀"],["⠿","⠸","⠇","⠇"]], 28 | '-' => [["⠀","⠀","⠀","⠀"],["⠈","⠉","⠉","⠁"]], 29 | 'p' => [["⣤","⡤","⢤","⡀"],["⣿","⡗","⠚","⠁"]], 30 | '/' => [["⠀","⣠","⣾","⠟"],["⠾","⠟","⠁","⠀"]], 31 | 'o' => [["⣠","⡤","⢤","⡀"],["⠻","⠧","⠼","⠃"]], 32 | 'N' => [["⣿","⣦","⣸","⡇"],["⠿","⠏","⠻","⠇"]], 33 | '^' => [["⣠","⣾","⣦","⡀"],["⠀","⠿","⠇","⠀"]], 34 | '"' => [["⠿","⠃","⠿","⠃"],["⠀","⠀","⠀","⠀"]], 35 | '#' => [["⢾","⡷","⣿","⠆"],["⠻","⠟","⠿","⠃"]], 36 | 'z' => [["⠤","⢤","⣤","⠄"],["⠴","⠿","⠥","⠄"]], 37 | ')' => [["⠈","⠙","⣷","⡄"],["⠠","⠴","⠟","⠁"]], 38 | 't' => [["⢼","⣿","⠤","⠀"],["⠘","⠿","⠤","⠄"]], 39 | 'B' => [["⣿","⣏","⣹","⠆"],["⠿","⠧","⠼","⠃"]], 40 | ';' => [["⠀","⣤","⡄","⠀"],["⢀","⣶","⠆","⠀"]], 41 | 'k' => [["⣿","⡇","⣠","⠄"],["⠿","⠏","⠳","⠄"]], 42 | 'h' => [["⣿","⡧","⢤","⡀"],["⠿","⠇","⠸","⠇"]], 43 | 'D' => [["⣿","⡏","⢳","⡄"],["⠿","⠧","⠞","⠁"]], 44 | '$' => [["⢴","⣞","⣓","⠂"],["⠒","⠶","⠛","⠁"]], 45 | '{' => [["⣀","⣾","⠏","⠁"],["⠀","⠻","⠧","⠄"]], 46 | 'u' => [["⣤","⠀","⣤","⡄"],["⠻","⠤","⠿","⠇"]], 47 | 's' => [["⣠","⡤","⠤","⠄"],["⠬","⠭","⠿","⠂"]], 48 | 'x' => [["⢤","⣄","⣠","⠄"],["⠴","⠛","⠷","⠄"]], 49 | '+' => [["⣀","⣶","⣆","⡀"],["⠀","⠛","⠃","⠀"]], 50 | '5' => [["⠿","⠭","⣭","⡁"],["⠲","⠤","⠿","⠃"]], 51 | 'T' => [["⠉","⣿","⡏","⠁"],["⠀","⠿","⠇","⠀"]], 52 | 'd' => [["⣠","⠤","⣿","⡇"],["⠻","⠤","⠿","⠇"]], 53 | 'j' => [["⠀","⠀","⣭","⡅"],["⢤","⣀","⣿","⠇"]], 54 | '~' => [["⠞","⠻","⠶","⠃"],["⠀","⠀","⠀","⠀"]], 55 | 'y' => [["⣤","⠀","⣤","⡄"],["⢙","⣒","⣿","⠇"]], 56 | '8' => [["⢾","⣏","⣹","⠆"],["⠻","⠧","⠼","⠃"]], 57 | '3' => [["⠚","⣉","⣿","⠆"],["⠲","⠤","⠿","⠃"]], 58 | '4' => [["⣴","⢻","⣿","⠀"],["⠉","⠹","⠿","⠁"]], 59 | '&' => [["⢾","⣏","⣷","⡀"],["⠻","⠧","⠿","⠄"]], 60 | ']' => [["⠈","⢹","⣿","⠀"],["⠠","⠼","⠿","⠀"]], 61 | 'A' => [["⣴","⡟","⢳","⡄"],["⠿","⠏","⠹","⠇"]], 62 | 'H' => [["⣿","⣇","⣸","⡇"],["⠿","⠇","⠸","⠇"]], 63 | 'b' => [["⣿","⡧","⢤","⡀"],["⠿","⠧","⠼","⠃"]], 64 | '6' => [["⣴","⣟","⣁","⠀"],["⠻","⠧","⠼","⠃"]], 65 | 'e' => [["⣠","⡤","⢤","⡀"],["⠻","⠯","⠭","⠅"]], 66 | 'Z' => [["⠉","⣩","⡿","⠃"],["⠾","⠯","⠤","⠄"]], 67 | 'V' => [["⣿","⡇","⢸","⡇"],["⠙","⠷","⠞","⠁"]], 68 | ' ' => [["⠀","⠀","⠀","⠀"],["⠀","⠀","⠀","⠀"]], 69 | 'g' => [["⣠","⠤","⣤","⡄"],["⢙","⣒","⣿","⠇"]], 70 | 'Y' => [["⢿","⣇","⣸","⠇"],["⠠","⠟","⠁","⠀"]], 71 | 'G' => [["⣾","⡏","⣉","⡁"],["⠻","⠧","⠿","⠇"]], 72 | 'P' => [["⣿","⡏","⢹","⡆"],["⠿","⠏","⠉","⠀"]], 73 | 'v' => [["⣤","⡄","⢠","⡄"],["⠙","⠷","⠟","⠁"]], 74 | '!' => [["⠀","⣿","⡇","⠀"],["⠀","⠶","⠆","⠀"]], 75 | '.' => [["⠀","⠀","⠀","⠀"],["⠀","⠶","⠆","⠀"]], 76 | 'O' => [["⣾","⡏","⢹","⡆"],["⠻","⠧","⠼","⠃"]], 77 | 'q' => [["⣠","⠤","⣤","⡄"],["⠙","⠒","⣿","⡇"]], 78 | '<' => [["⢀","⣴","⠟","⠁"],["⠀","⠙","⠷","⠄"]], 79 | '|' => [["⠀","⣿","⡇","⠀"],["⠀","⠿","⠇","⠀"]], 80 | 'r' => [["⣤","⡤","⢤","⡀"],["⠿","⠇","⠀","⠀"]], 81 | 'U' => [["⣿","⡇","⢸","⡇"],["⠻","⠧","⠼","⠃"]], 82 | 'I' => [["⠈","⣿","⡏","⠀"],["⠠","⠿","⠧","⠀"]], 83 | 'X' => [["⠻","⣧","⡼","⠃"],["⠾","⠉","⠿","⠆"]], 84 | '9' => [["⢾","⣉","⣿","⡆"],["⠠","⠾","⠋","⠀"]], 85 | '7' => [["⠉","⢉","⣿","⠇"],["⠀","⠿","⠇","⠀"]], 86 | ',' => [["⠀","⠀","⠀","⠀"],["⢀","⣶","⠆","⠀"]], 87 | 'a' => [["⠠","⠤","⣤","⡀"],["⠺","⠭","⠿","⠇"]], 88 | '2' => [["⠚","⢉","⣿","⠆"],["⠴","⠿","⠥","⠄"]], 89 | '>' => [["⠈","⠻","⣦","⡀"],["⠠","⠾","⠋","⠀"]], 90 | 'Q' => [["⣾","⡏","⢹","⡆"],["⠻","⠧","⠻","⠆"]], 91 | 'w' => [["⣤","⢀","⢠","⡄"],["⠻","⠟","⠿","⠃"]], 92 | 'i' => [["⠀","⣭","⡅","⠀"],["⠀","⠿","⠇","⠀"]], 93 | "£" => [["⣰","⣿","⣙","⠂"],["⠼","⠿","⠤","⠄"]], 94 | '0' => [["⣴","⡏","⢳","⡄"],["⠙","⠧","⠞","⠁"]], 95 | 'J' => [["⠀","⠀","⣿","⡇"],["⠲","⠤","⠿","⠃"]], 96 | '%' => [["⠺","⣢","⡾","⠂"],["⠺","⠋","⠺","⠂"]]} 97 | -------------------------------------------------------------------------------- /examples/fonts/Computer.grf: -------------------------------------------------------------------------------- 1 | {'W' => [["⣿","⠘","⠃","⡇"],["⠿","⠊","⠑","⠇"]], 2 | 'm' => [["⢠","⢄","⢤","⡄"],["⠸","⠰","⠸","⠇"]], 3 | '@' => [["⢠","⢊","⡭","⡆"],["⠘","⠬","⠭","⠁"]], 4 | '\\' => [["⠘","⢦","⡀","⠀"],["⠀","⠀","⠙","⠆"]], 5 | '"' => [["⠘","⠇","⠻","⠀"],["⠀","⠀","⠀","⠀"]], 6 | ']' => [["⠀","⠉","⣹","⠀"],["⠀","⠤","⠿","⠀"]], 7 | ' ' => [["⠀","⠀","⠀","⠀"],["⠀","⠀","⠀","⠀"]], 8 | 'q' => [["⢠","⠤","⣤","⠀"],["⠘","⠒","⣿","⠀"]], 9 | 'f' => [["⢀","⣿","⣉","⠀"],["⠀","⠿","⠀","⠀"]], 10 | 'N' => [["⢸","⠑","⢄","⡇"],["⠸","⠰","⠆","⠇"]], 11 | '9' => [["⢸","⣉","⣹","⠀"],["⠀","⠀","⠿","⠀"]], 12 | 'G' => [["⢸","⡏","⣙","⡃"],["⠸","⠧","⠤","⠇"]], 13 | 'J' => [["⠀","⠀","⢸","⡀"],["⠰","⠦","⠼","⠇"]], 14 | '7' => [["⠘","⠉","⡹","⠀"],["⠀","⠸","⠀","⠀"]], 15 | '6' => [["⢸","⠙","⠀","⠀"],["⠸","⠭","⠽","⠀"]], 16 | 'C' => [["⢸","⠉","⠿","⠀"],["⠸","⠤","⠼","⠀"]], 17 | 'h' => [["⢸","⡧","⢤","⠀"],["⠸","⠇","⠸","⠀"]], 18 | 'y' => [["⢠","⠀","⣤","⠀"],["⢘","⣒","⣿","⠀"]], 19 | 'n' => [["⢠","⡤","⠤","⡄"],["⠸","⠇","⠆","⠇"]], 20 | '*' => [["⠠","⣴","⡤","⠀"],["⠈","⠙","⠉","⠀"]], 21 | 'H' => [["⢸","⣇","⣀","⡇"],["⠸","⠇","⠀","⠇"]], 22 | '0' => [["⢰","⢉","⡉","⡇"],["⠸","⠬","⠥","⠃"]], 23 | 'I' => [["⠀","⣸","⠀","⠀"],["⠀","⠿","⠀","⠀"]], 24 | 'x' => [["⢠","⡄","⢠","⠀"],["⠰","⠉","⠷","⠀"]], 25 | 'X' => [["⠘","⢧","⡠","⠃"],["⠰","⠁","⠹","⠆"]], 26 | '3' => [["⠈","⣉","⣇","⠀"],["⠠","⠤","⠿","⠀"]], 27 | 'E' => [["⢸","⣉","⣉","⠀"],["⠸","⠧","⠤","⠀"]], 28 | '-' => [["⢀","⣀","⣀","⡀"],["⠀","⠀","⠀","⠀"]], 29 | 'L' => [["⢀","⡇","⠀","⠀"],["⠸","⠧","⠤","⠀"]], 30 | '|' => [["⠀","⢸","⡇","⠀"],["⠀","⠸","⠇","⠀"]], 31 | 'R' => [["⢸","⣉","⣹","⠀"],["⠸","⠇","⠸","⠇"]], 32 | ':' => [["⠀","⠰","⠆","⠀"],["⠀","⠰","⠆","⠀"]], 33 | '!' => [["⠀","⢸","⡇","⠀"],["⠀","⠨","⠅","⠀"]], 34 | 'T' => [["⠈","⢹","⡏","⠁"],["⠀","⠸","⠇","⠀"]], 35 | 'l' => [["⠀","⢸","⡇","⠀"],["⠀","⠸","⠇","⠀"]], 36 | '^' => [["⢠","⡼","⢤","⠀"],["⠈","⠁","⠈","⠀"]], 37 | 'M' => [["⡏","⠢","⢺","⡇"],["⠇","⠶","⠸","⠇"]], 38 | ';' => [["⠀","⠰","⠆","⠀"],["⠀","⠰","⡆","⠀"]], 39 | 'r' => [["⢠","⡤","⢤","⠀"],["⠸","⠇","⠀","⠀"]], 40 | 't' => [["⠠","⣧","⠄","⠀"],["⠀","⠿","⠀","⠀"]], 41 | 'g' => [["⢠","⠤","⣤","⠀"],["⢘","⣒","⣿","⠀"]], 42 | 'i' => [["⠀","⣭","⠀","⠀"],["⠀","⠿","⠀","⠀"]], 43 | '5' => [["⢸","⣉","⣉","⠀"],["⠠","⠤","⠿","⠀"]], 44 | 'F' => [["⢸","⣏","⣉","⠀"],["⠀","⠇","⠀","⠀"]], 45 | 'U' => [["⢸","⠀","⢸","⡇"],["⠸","⠤","⠼","⠇"]], 46 | 'Z' => [["⠘","⢉","⠝","⠀"],["⠰","⠥","⠶","⠀"]], 47 | '8' => [["⢀","⣏","⣹","⡀"],["⠸","⠧","⠼","⠇"]], 48 | '?' => [["⠸","⢉","⣿","⠀"],["⠀","⠰","⠆","⠀"]], 49 | 'k' => [["⢸","⣇","⣔","⠀"],["⠸","⠇","⠸","⠀"]], 50 | 'S' => [["⢸","⣉","⣙","⠀"],["⠰","⠦","⠼","⠀"]], 51 | '(' => [["⠀","⣴","⠋","⠀"],["⠀","⠙","⠦","⠀"]], 52 | '%' => [["⠿","⢇","⠔","⠁"],["⠔","⠁","⠿","⠇"]], 53 | "£" => [["⢀","⣸","⣙","⠀"],["⠠","⠿","⠤","⠄"]], 54 | '#' => [["⠠","⣶","⢴","⠄"],["⠐","⠗","⠿","⠂"]], 55 | 'j' => [["⠀","⢨","⡅","⠀"],["⢀","⣸","⡇","⠀"]], 56 | 'z' => [["⠠","⠤","⣤","⠀"],["⠰","⠯","⠥","⠀"]], 57 | 'a' => [["⠠","⠤","⣤","⠀"],["⠸","⠭","⠿","⠀"]], 58 | 'B' => [["⢸","⣏","⣹","⡀"],["⠸","⠧","⠤","⠇"]], 59 | 'p' => [["⢠","⡤","⢤","⠀"],["⢸","⡗","⠚","⠀"]], 60 | '}' => [["⠈","⢿","⣀","⠀"],["⠠","⠿","⠀","⠀"]], 61 | '&' => [["⠀","⣯","⠽","⠀"],["⠸","⠧","⠵","⠅"]], 62 | '=' => [["⠠","⠤","⠤","⠀"],["⠈","⠉","⠉","⠀"]], 63 | 'u' => [["⢠","⠀","⣤","⠀"],["⠸","⠤","⠿","⠀"]], 64 | '2' => [["⠀","⣉","⣹","⠀"],["⠀","⠧","⠤","⠀"]], 65 | 'v' => [["⢠","⠀","⣤","⠀"],["⠈","⠢","⠟","⠀"]], 66 | '1' => [["⠀","⢹","⠀","⠀"],["⠀","⠿","⠿","⠀"]], 67 | 'Y' => [["⠘","⢄","⡠","⠃"],["⠀","⠸","⠇","⠀"]], 68 | 'Q' => [["⢸","⡍","⠉","⡇"],["⠸","⠧","⢧","⠇"]], 69 | '[' => [["⠀","⣏","⠉","⠀"],["⠀","⠿","⠤","⠀"]], 70 | 'D' => [["⢸","⡏","⠉","⡆"],["⠸","⠧","⠤","⠃"]], 71 | 'K' => [["⢸","⣠","⣊","⡀"],["⠸","⠇","⠸","⠇"]], 72 | ',' => [["⠀","⠀","⠀","⠀"],["⠀","⠰","⡆","⠀"]], 73 | 'c' => [["⢠","⠤","⢤","⠀"],["⠸","⠧","⠴","⠀"]], 74 | 'e' => [["⢠","⡤","⢤","⠀"],["⠸","⠯","⠭","⠀"]], 75 | 'A' => [["⢀","⣏","⣉","⡇"],["⠸","⠇","⠀","⠇"]], 76 | '>' => [["⠀","⠲","⣄","⠀"],["⠀","⠚","⠁","⠀"]], 77 | 'o' => [["⢠","⡤","⢤","⠀"],["⠸","⠧","⠼","⠀"]], 78 | '<' => [["⢀","⡴","⠂","⠀"],["⠀","⠙","⠂","⠀"]], 79 | '{' => [["⢀","⣸","⠏","⠀"],["⠀","⠸","⠧","⠀"]], 80 | '$' => [["⢰","⣞","⣒","⠀"],["⠐","⠲","⠚","⠀"]], 81 | 'd' => [["⢠","⠤","⣿","⠀"],["⠸","⠤","⠿","⠀"]], 82 | 'b' => [["⢸","⡧","⢤","⠀"],["⠸","⠧","⠼","⠀"]], 83 | 'w' => [["⢠","⡄","⡄","⡄"],["⠸","⠧","⠢","⠇"]], 84 | "©" => [["⡏","⣭","⢭","⢹"],["⣇","⣛","⣒","⣸"]], 85 | '\'' => [["⠀","⠘","⠇","⠀"],["⠀","⠀","⠀","⠀"]], 86 | '.' => [["⠀","⠀","⠀","⠀"],["⠀","⠰","⠆","⠀"]], 87 | '+' => [["⢀","⣰","⣆","⡀"],["⠀","⠘","⠃","⠀"]], 88 | '4' => [["⢸","⡇","⣀","⠀"],["⠈","⠉","⠿","⠀"]], 89 | 's' => [["⢠","⡤","⠤","⠀"],["⠨","⠭","⠽","⠀"]], 90 | 'P' => [["⢸","⣉","⣹","⠀"],["⠸","⠇","⠀","⠀"]], 91 | 'V' => [["⢸","⠀","⢸","⡇"],["⠈","⠳","⠋","⠀"]], 92 | '/' => [["⠀","⢀","⡴","⠃"],["⠰","⠋","⠀","⠀"]], 93 | 'O' => [["⢸","⠉","⢉","⡇"],["⠸","⠤","⠼","⠇"]], 94 | 'info' => ' Computer font ',')' => [["⠀","⠙","⣦","⠀"],["⠀","⠴","⠋","⠀"]], 95 | '_' => [["⠀","⠀","⠀","⠀"],["⣀","⣀","⣀","⣀"]], 96 | '~' => [["⠸","⠻","⠾","⠀"],["⠀","⠀","⠀","⠀"]]} 97 | -------------------------------------------------------------------------------- /examples/fonts/Coolant.grf: -------------------------------------------------------------------------------- 1 | {'C' => [["⢀","⠔","⠉","⠂"],["⠘","⠤","⠤","⠄"]], 2 | 'a' => [["⠀","⡠","⠤","⠀"],["⠘","⠤","⠚","⠤"]], 3 | 'k' => [["⠀","⡜","⣀","⠀"],["⠼","⠙","⠤","⠤"]], 4 | 'Z' => [["⠉","⢉","⡩","⠃"],["⠮","⠥","⠤","⠤"]], 5 | 'X' => [["⠉","⢆","⡠","⠊"],["⠔","⠁","⠘","⠤"]], 6 | 'o' => [["⠀","⡠","⠤","⡀"],["⠘","⠤","⠔","⠁"]], 7 | 'P' => [["⠉","⣍","⡩","⠂"],["⠜","⠀","⠀","⠀"]], 8 | '$' => [["⠀","⢀","⢴","⠤"],["⠤","⡴","⠕","⠀"]], 9 | '%' => [["⠠","⠖","⡠","⠒"],["⠤","⠊","⠴","⠂"]], 10 | '9' => [["⢠","⠊","⣉","⠆"],["⠤","⠭","⠊","⠀"]], 11 | 'w' => [["⢤","⠀","⡀","⢠"],["⠸","⠊","⠗","⠁"]], 12 | 'x' => [["⠠","⡀","⢀","⠤"],["⠤","⠊","⠑","⠄"]], 13 | 'm' => [["⠀","⣄","⢀","⡄"],["⠼","⠈","⠹","⠤"]], 14 | '>' => [["⠀","⠢","⡀","⠀"],["⠀","⠊","⠀","⠀"]], 15 | '0' => [["⢀","⠔","⠉","⡆"],["⠘","⠤","⠊","⠀"]], 16 | 'r' => [["⠀","⣄","⠤","⡄"],["⠴","⠁","⠒","⠥"]], 17 | 'O' => [["⡠","⠊","⠑","⡄"],["⠣","⠤","⠔","⠁"]], 18 | '*' => [["⠀","⣄","⣆","⠄"],["⠈","⠘","⠈","⠀"]], 19 | '.' => [["⠀","⠀","⠀","⠀"],["⠀","⠰","⠀","⠀"]], 20 | '=' => [["⠠","⠤","⠤","⠀"],["⠈","⠉","⠉","⠀"]], 21 | 'z' => [["⠠","⠤","⢤","⠤"],["⠤","⠮","⠥","⠄"]], 22 | 'I' => [["⠈","⢩","⠂","⠀"],["⠀","⠣","⠤","⠄"]], 23 | 'j' => [["⠀","⠀","⡨","⠤"],["⠢","⠔","⠁","⠀"]], 24 | 'A' => [["⠀","⢠","⢻","⠀"],["⠤","⠋","⠉","⠆"]], 25 | '2' => [["⠀","⠊","⣉","⠆"],["⠰","⠭","⠤","⠀"]], 26 | '|' => [["⠀","⢠","⠃","⠀"],["⠀","⠎","⠀","⠀"]], 27 | '&' => [["⠀","⢎","⡕","⡀"],["⠐","⠥","⠚","⠄"]], 28 | 'h' => [["⠀","⣜","⠤","⡀"],["⠼","⠀","⠰","⠥"]], 29 | '7' => [["⠀","⠉","⡩","⠃"],["⠠","⠊","⠀","⠀"]], 30 | "£" => [["⣀","⣰","⡉","⠂"],["⠴","⠥","⠤","⠄"]], 31 | '{' => [["⠠","⢜","⠉","⠁"],["⠰","⠥","⠄","⠀"]], 32 | 'Q' => [["⡠","⠊","⠑","⡄"],["⠣","⠬","⠕","⠥"]], 33 | '@' => [["⢠","⢒","⢦","⠀"],["⠣","⠤","⠭","⠤"]], 34 | 'G' => [["⡠","⠊","⢑","⣀"],["⠣","⠤","⠬","⠆"]], 35 | 'E' => [["⠩","⣋","⡉","⠀"],["⠪","⠤","⠤","⠤"]], 36 | "©" => [["⢠","⢊","⢭","⢢"],["⠣","⣓","⡢","⠊"]], 37 | '?' => [["⠉","⢉","⡱","⠀"],["⠀","⠂","⠀","⠀"]], 38 | 'T' => [["⠉","⢉","⠏","⠉"],["⠀","⠎","⠀","⠀"]], 39 | '[' => [["⠀","⡜","⠉","⠁"],["⠰","⠥","⠄","⠀"]], 40 | 'J' => [["⠀","⠀","⢰","⠉"],["⠢","⠤","⠃","⠀"]], 41 | '3' => [["⠀","⠩","⡝","⠁"],["⠢","⠤","⠜","⠀"]], 42 | 'V' => [["⠉","⡇","⠀","⡜"],["⠀","⠧","⠊","⠀"]], 43 | 'S' => [["⠐","⢍","⠉","⠉"],["⠤","⠤","⠕","⠀"]], 44 | '4' => [["⢀","⠎","⡰","⠀"],["⠈","⠩","⠋","⠀"]], 45 | 'b' => [["⠀","⣜","⠤","⡀"],["⠬","⠤","⠔","⠁"]], 46 | 'K' => [["⠈","⣇","⠔","⠁"],["⠜","⠀","⠑","⠤"]], 47 | 'Y' => [["⠉","⢆","⡠","⠊"],["⠔","⠁","⠀","⠀"]], 48 | ';' => [["⠀","⠠","⠀","⠀"],["⠀","⠔","⠀","⠀"]], 49 | '\'' => [["⠀","⠜","⠀","⠀"],["⠀","⠀","⠀","⠀"]], 50 | '8' => [["⠀","⢎","⡩","⠂"],["⠐","⠥","⠜","⠀"]], 51 | 'F' => [["⠠","⣊","⡉","⠉"],["⠜","⠀","⠀","⠀"]], 52 | '+' => [["⢀","⣀","⣆","⡀"],["⠀","⠘","⠀","⠀"]], 53 | 'f' => [["⠀","⣀","⣔","⠒"],["⠤","⠜","⠀","⠀"]], 54 | 'g' => [["⠀","⡠","⠤","⠤"],["⢀","⣑","⡪","⠃"]], 55 | 'info' => ' Coolant font ','i' => [["⠤","⠤","⡌","⠀"],["⠀","⠪","⠤","⠤"]], 56 | 'd' => [["⠀","⡠","⢄","⡜"],["⠘","⠤","⠤","⠅"]], 57 | 'n' => [["⠀","⣄","⠤","⡀"],["⠼","⠀","⠰","⠥"]], 58 | ':' => [["⠀","⠠","⠀","⠀"],["⠀","⠐","⠀","⠀"]], 59 | '-' => [["⢀","⣀","⣀","⠀"],["⠀","⠀","⠀","⠀"]], 60 | 'R' => [["⠉","⣍","⡩","⠂"],["⠜","⠀","⠑","⠤"]], 61 | 't' => [["⠤","⢤","⠧","⠄"],["⠀","⠣","⠤","⠤"]], 62 | '"' => [["⠠","⠃","⠜","⠀"],["⠀","⠀","⠀","⠀"]], 63 | ' ' => [["⠀","⠀","⠀","⠀"],["⠀","⠀","⠀","⠀"]], 64 | 'l' => [["⠉","⢉","⠆","⠀"],["⠀","⠪","⠤","⠤"]], 65 | 's' => [["⠀","⢀","⠤","⠤"],["⠤","⠤","⠕","⠀"]], 66 | 'v' => [["⠤","⡀","⢀","⠤"],["⠀","⠗","⠁","⠀"]], 67 | '#' => [["⠠","⡴","⢤","⠦"],["⠙","⠉","⠋","⠁"]], 68 | ']' => [["⠀","⠈","⢩","⠃"],["⠠","⠤","⠎","⠀"]], 69 | '6' => [["⠀","⣔","⠭","⡉"],["⠘","⠤","⠔","⠁"]], 70 | 'p' => [["⠤","⠤","⠤","⡀"],["⡸","⠢","⠔","⠁"]], 71 | '}' => [["⠀","⠈","⠩","⣃"],["⠠","⠤","⠎","⠀"]], 72 | 'W' => [["⢩","⢃","⠆","⡜"],["⠣","⠺","⠜","⠀"]], 73 | 'N' => [["⢉","⢆","⢀","⠎"],["⠜","⠀","⠙","⠤"]], 74 | 'c' => [["⠀","⡠","⢄","⠀"],["⠘","⠤","⠤","⠤"]], 75 | 'B' => [["⠉","⡍","⣩","⠂"],["⠸","⠤","⠔","⠁"]], 76 | '5' => [["⠀","⠼","⢍","⠉"],["⠢","⠤","⠊","⠀"]], 77 | ',' => [["⠀","⠀","⠀","⠀"],["⠀","⡰","⠀","⠀"]], 78 | 'u' => [["⠤","⡄","⠀","⡄"],["⠘","⠤","⠲","⠁"]], 79 | 'D' => [["⠉","⡍","⠑","⡄"],["⠸","⠤","⠔","⠁"]], 80 | '(' => [["⠀","⡔","⠉","⠀"],["⠘","⠤","⠀","⠀"]], 81 | '\\' => [["⠑","⢄","⠀","⠀"],["⠀","⠀","⠑","⠄"]], 82 | '/' => [["⠀","⢀","⠔","⠁"],["⠔","⠁","⠀","⠀"]], 83 | 'e' => [["⠀","⡠","⢄","⠀"],["⠘","⠮","⠥","⠤"]], 84 | '<' => [["⠀","⡠","⠂","⠀"],["⠀","⠈","⠂","⠀"]], 85 | '~' => [["⠀","⠖","⠴","⠀"],["⠀","⠀","⠀","⠀"]], 86 | '_' => [["⠀","⠀","⠀","⠀"],["⣀","⣀","⣀","⡀"]], 87 | '1' => [["⠀","⠠","⡞","⠀"],["⠀","⠰","⠁","⠀"]], 88 | 'y' => [["⠀","⡠","⠀","⡠"],["⣀","⣑","⡪","⠃"]], 89 | 'U' => [["⢩","⠃","⠀","⡜"],["⠣","⠤","⠔","⠁"]], 90 | '^' => [["⢀","⢴","⢄","⠀"],["⠀","⠸","⠀","⠀"]], 91 | 'H' => [["⢉","⣆","⣀","⠎"],["⠜","⠀","⠘","⠤"]], 92 | 'M' => [["⢉","⠦","⡠","⡎"],["⠎","⠘","⠘","⠤"]], 93 | '!' => [["⠀","⢠","⠃","⠀"],["⠀","⠆","⠀","⠀"]], 94 | ')' => [["⠀","⠈","⡱","⠀"],["⠠","⠔","⠁","⠀"]], 95 | 'q' => [["⢀","⠤","⠤","⠤"],["⠣","⢔","⠎","⠀"]], 96 | 'L' => [["⠉","⡕","⠀","⠀"],["⠘","⠤","⠤","⠤"]]} 97 | -------------------------------------------------------------------------------- /examples/fonts/Everest Bold.grf: -------------------------------------------------------------------------------- 1 | {'Q' => [["⣴","⠋","⢳","⡄"],["⠻","⣔","⢮","⡃"]], 2 | '%' => [["⢴","⡢","⡠","⠀"],["⠠","⠪","⣖","⠄"]], 3 | 'J' => [["⠀","⠀","⢸","⡇"],["⠰","⣀","⣸","⠇"]], 4 | '0' => [["⣴","⢋","⢳","⡄"],["⠻","⣌","⡼","⠃"]], 5 | 'w' => [["⣤","⢀","⢠","⡄"],["⠹","⡾","⡾","⠁"]], 6 | "£" => [["⣰","⣏","⡉","⠂"],["⣸","⣇","⣀","⡀"]], 7 | 'i' => [["⠀","⢨","⡅","⠀"],["⠀","⢸","⡇","⠀"]], 8 | '7' => [["⠈","⠉","⣹","⠇"],["⠀","⢸","⡇","⠀"]], 9 | '9' => [["⢰","⡏","⢹","⡆"],["⠠","⣉","⣹","⠇"]], 10 | 'X' => [["⠻","⣄","⡼","⠃"],["⣴","⠋","⢳","⡄"]], 11 | 'I' => [["⠈","⢹","⡏","⠁"],["⢀","⣸","⣇","⡀"]], 12 | 'U' => [["⢸","⡇","⢸","⡇"],["⠸","⣇","⣸","⠇"]], 13 | 'r' => [["⢠","⣄","⢤","⡀"],["⢸","⡇","⠀","⠀"]], 14 | '|' => [["⠀","⢸","⡇","⠀"],["⠀","⢸","⡇","⠀"]], 15 | '<' => [["⠀","⣠","⠖","⠀"],["⠀","⠈","⠓","⠀"]], 16 | '>' => [["⠀","⠲","⣄","⠀"],["⠀","⠚","⠁","⠀"]], 17 | '2' => [["⠐","⠉","⣹","⠆"],["⢠","⣞","⣁","⡀"]], 18 | 'a' => [["⠀","⠤","⢤","⡀"],["⠰","⣏","⣹","⡇"]], 19 | ',' => [["⠀","⠀","⠀","⠀"],["⠀","⢘","⠇","⠀"]], 20 | 'G' => [["⢠","⡞","⠉","⠂"],["⠘","⢧","⣹","⡇"]], 21 | 'v' => [["⢠","⡄","⢠","⡄"],["⢸","⡷","⠋","⠀"]], 22 | 'P' => [["⢸","⡏","⢹","⡆"],["⢸","⡏","⠉","⠀"]], 23 | 'Y' => [["⠸","⣇","⣸","⠇"],["⠀","⢸","⡇","⠀"]], 24 | 'g' => [["⢀","⡤","⢤","⡄"],["⠈","⣓","⣹","⠇"]], 25 | ' ' => [["⠀","⠀","⠀","⠀"],["⠀","⠀","⠀","⠀"]], 26 | 'V' => [["⢻","⡄","⣼","⠃"],["⠈","⢷","⠏","⠀"]], 27 | 'q' => [["⢀","⡤","⣠","⡄"],["⠈","⠓","⢺","⡇"]], 28 | 'O' => [["⣴","⠋","⢳","⡄"],["⠻","⣄","⡼","⠃"]], 29 | '!' => [["⠀","⣾","⡆","⠀"],["⠀","⢨","⠀","⠀"]], 30 | '.' => [["⠀","⠀","⠀","⠀"],["⠀","⠰","⠆","⠀"]], 31 | '&' => [["⢾","⣹","⠆","⠀"],["⢿","⣈","⡷","⡁"]], 32 | '4' => [["⢀","⡴","⣋","⠀"],["⠘","⠓","⣿","⠂"]], 33 | 'Z' => [["⠉","⢉","⡽","⠃"],["⣴","⣋","⣀","⡀"]], 34 | 'e' => [["⢀","⡤","⢤","⡀"],["⠸","⣏","⣉","⠅"]], 35 | '6' => [["⢰","⣏","⣉","⠂"],["⠸","⣇","⣸","⠇"]], 36 | 'b' => [["⢸","⣇","⢤","⡀"],["⢸","⣇","⣸","⠇"]], 37 | 'H' => [["⢸","⣇","⣸","⡇"],["⢸","⡇","⢸","⡇"]], 38 | 'A' => [["⢠","⡟","⣧","⠀"],["⣾","⠉","⢹","⡆"]], 39 | ']' => [["⠀","⠉","⣿","⠀"],["⠀","⣀","⣿","⠀"]], 40 | 'd' => [["⢀","⡤","⣸","⡇"],["⠸","⣇","⣸","⡇"]], 41 | 'j' => [["⠀","⠀","⢨","⡅"],["⠠","⣀","⣸","⠇"]], 42 | 'T' => [["⠈","⢹","⡏","⠁"],["⠀","⢸","⡇","⠀"]], 43 | '5' => [["⢸","⣏","⣉","⠁"],["⠠","⣀","⣸","⠇"]], 44 | 'x' => [["⠠","⣄","⣠","⠄"],["⢠","⡞","⢳","⡄"]], 45 | '+' => [["⢀","⣰","⣆","⡀"],["⠀","⠘","⠃","⠀"]], 46 | 's' => [["⢀","⡤","⠤","⡀"],["⠠","⣙","⣳","⠄"]], 47 | 'u' => [["⢠","⡄","⢠","⡄"],["⠸","⣇","⢼","⡇"]], 48 | '{' => [["⢀","⣰","⠏","⠁"],["⠀","⠸","⣇","⡀"]], 49 | '3' => [["⠈","⢉","⣽","⠃"],["⠠","⣀","⣸","⠇"]], 50 | '8' => [["⠰","⣏","⣹","⠆"],["⠸","⣇","⣸","⠇"]], 51 | 'y' => [["⢠","⡄","⢠","⡄"],["⠈","⣓","⣹","⠇"]], 52 | '~' => [["⠰","⠛","⠶","⠃"],["⠀","⠀","⠀","⠀"]], 53 | 'B' => [["⢸","⣏","⣹","⠆"],["⢸","⣇","⣸","⠇"]], 54 | ')' => [["⠀","⠙","⣦","⠀"],["⠀","⣠","⠟","⠀"]], 55 | 't' => [["⠠","⣿","⠄","⠀"],["⠀","⢿","⣀","⠄"]], 56 | 'z' => [["⠠","⠤","⢤","⡄"],["⢀","⣴","⣋","⡀"]], 57 | '#' => [["⢼","⡧","⣿","⠄"],["⢺","⡗","⣿","⠂"]], 58 | '"' => [["⢘","⠇","⡻","⠀"],["⠀","⠀","⠀","⠀"]], 59 | '^' => [["⢠","⠞","⢦","⠀"],["⠀","⠀","⠀","⠀"]], 60 | '$' => [["⢠","⣖","⡓","⠄"],["⠐","⢬","⠽","⠃"]], 61 | 'D' => [["⢸","⡏","⢳","⡄"],["⢸","⣇","⡼","⠃"]], 62 | 'h' => [["⢸","⣇","⢤","⡀"],["⢸","⡇","⢸","⡇"]], 63 | 'k' => [["⢸","⡇","⣰","⠆"],["⢸","⡏","⢳","⡄"]], 64 | ';' => [["⠀","⠰","⠆","⠀"],["⠀","⢘","⠇","⠀"]], 65 | '=' => [["⠠","⠤","⠤","⠄"],["⠈","⠉","⠉","⠁"]], 66 | '(' => [["⠀","⣴","⠋","⠀"],["⠀","⠻","⣄","⠀"]], 67 | 'E' => [["⢸","⣏","⡉","⠁"],["⢸","⣇","⣀","⡀"]], 68 | '\\' => [["⠘","⣧","⠀","⠀"],["⠀","⠘","⣧","⠀"]], 69 | ':' => [["⠀","⠰","⠆","⠀"],["⠀","⠰","⠆","⠀"]], 70 | 'o' => [["⢀","⡤","⢤","⡀"],["⠸","⣇","⣸","⠇"]], 71 | 'N' => [["⢸","⣦","⣸","⡇"],["⢸","⡏","⠻","⡇"]], 72 | '/' => [["⠀","⢠","⡟","⠀"],["⢠","⡟","⠀","⠀"]], 73 | 'p' => [["⢠","⣄","⢤","⡀"],["⢸","⡗","⠚","⠁"]], 74 | '-' => [["⢀","⣀","⣀","⡀"],["⠀","⠀","⠀","⠀"]], 75 | 'm' => [["⣤","⣄","⣤","⡀"],["⣿","⢸","⢸","⡇"]], 76 | '@' => [["⣴","⣫","⢷","⡄"],["⠻","⣝","⣚","⡃"]], 77 | 'W' => [["⣿","⢠","⢸","⡇"],["⢿","⠟","⢿","⠇"]], 78 | 'R' => [["⢸","⡏","⢹","⡆"],["⢸","⡏","⢻","⡄"]], 79 | 'n' => [["⢠","⣄","⢤","⡀"],["⢸","⡇","⢸","⡇"]], 80 | 'info' => ' Everest Bold font ','c' => [["⢀","⡤","⠤","⡀"],["⠸","⣇","⣀","⠄"]], 81 | '_' => [["⠀","⠀","⠀","⠀"],["⣀","⣀","⣀","⣀"]], 82 | '?' => [["⠰","⠏","⣹","⠆"],["⠀","⢨","⡅","⠀"]], 83 | 'L' => [["⢸","⡇","⠀","⠀"],["⢸","⣇","⣀","⡀"]], 84 | 'M' => [["⣷","⣄","⣴","⡇"],["⣿","⠹","⢹","⡇"]], 85 | 'F' => [["⢸","⣏","⡉","⠁"],["⢸","⡇","⠀","⠀"]], 86 | '[' => [["⠀","⣿","⠉","⠀"],["⠀","⣿","⣀","⠀"]], 87 | 'l' => [["⠀","⠉","⣿","⠀"],["⠀","⠀","⣿","⠀"]], 88 | "©" => [["⡾","⣵","⠪","⣳"],["⢷","⡻","⢔","⡽"]], 89 | '\'' => [["⠀","⡻","⠀","⠀"],["⠀","⠀","⠀","⠀"]], 90 | 'K' => [["⢸","⣇","⡼","⠃"],["⢸","⡏","⢳","⡄"]], 91 | 'f' => [["⣀","⣾","⣉","⠂"],["⢄","⡿","⠀","⠀"]], 92 | '*' => [["⠠","⣴","⡤","⠀"],["⠈","⠙","⠉","⠀"]], 93 | 'S' => [["⢾","⣉","⠉","⠂"],["⢄","⣈","⣹","⠆"]], 94 | '1' => [["⠠","⢺","⡇","⠀"],["⢀","⣸","⣇","⡀"]], 95 | '}' => [["⠈","⠹","⣆","⡀"],["⢀","⣸","⠇","⠀"]], 96 | 'C' => [["⢠","⡞","⠉","⠂"],["⠘","⢧","⣀","⠄"]]} 97 | -------------------------------------------------------------------------------- /examples/fonts/Everest Condensed SemiBold.grf: -------------------------------------------------------------------------------- 1 | {'j' => [["⠀","⠀","⣭","⠀"],["⠠","⣀","⡿","⠀"]], 2 | 'd' => [["⢀","⠤","⣿","⠀"],["⠸","⣀","⣿","⠀"]], 3 | 'T' => [["⠈","⢹","⡏","⠁"],["⠀","⢸","⡇","⠀"]], 4 | '5' => [["⢸","⣏","⡉","⠀"],["⠠","⣀","⡿","⠀"]], 5 | 'x' => [["⢠","⡄","⢠","⠀"],["⢠","⠛","⣧","⠀"]], 6 | '+' => [["⢀","⣰","⣆","⡀"],["⠀","⠘","⠃","⠀"]], 7 | 's' => [["⢀","⡤","⢄","⠀"],["⠠","⣙","⡦","⠀"]], 8 | 'u' => [["⢠","⠀","⣤","⠀"],["⠸","⣀","⣿","⠀"]], 9 | '{' => [["⢀","⣰","⠏","⠀"],["⠀","⠸","⣇","⠀"]], 10 | '3' => [["⠈","⣉","⡟","⠀"],["⠠","⣀","⡿","⠀"]], 11 | 'y' => [["⢠","⠀","⣤","⠀"],["⠈","⣒","⡿","⠀"]], 12 | '8' => [["⠰","⣏","⡱","⠀"],["⠸","⣀","⡿","⠀"]], 13 | '~' => [["⠰","⠟","⠞","⠀"],["⠀","⠀","⠀","⠀"]], 14 | 'B' => [["⢸","⣏","⡱","⠀"],["⢸","⣇","⡸","⠀"]], 15 | '#' => [["⠠","⣿","⢼","⠄"],["⠐","⣿","⢺","⠂"]], 16 | '"' => [["⠠","⠟","⠼","⠃"],["⠀","⠀","⠀","⠀"]], 17 | ')' => [["⠀","⠙","⣦","⠀"],["⠀","⣠","⠟","⠀"]], 18 | 't' => [["⠠","⣿","⠄","⠀"],["⠀","⢿","⡠","⠀"]], 19 | 'z' => [["⠠","⠤","⣤","⠀"],["⢠","⣞","⣁","⠀"]], 20 | '^' => [["⢠","⠞","⢦","⠀"],["⠀","⠀","⠀","⠀"]], 21 | '$' => [["⠠","⣞","⠢","⠀"],["⠐","⢬","⠗","⠀"]], 22 | 'D' => [["⢸","⡏","⢢","⠀"],["⢸","⣇","⠜","⠀"]], 23 | 'h' => [["⢸","⡧","⢄","⠀"],["⢸","⡇","⢸","⠀"]], 24 | ';' => [["⠀","⢠","⡄","⠀"],["⠀","⣰","⠆","⠀"]], 25 | 'k' => [["⢸","⣇","⠔","⠀"],["⢸","⡗","⢄","⠀"]], 26 | '=' => [["⠠","⠤","⠤","⠀"],["⠈","⠉","⠉","⠀"]], 27 | 'E' => [["⢸","⣏","⡉","⠀"],["⢸","⣇","⣀","⠀"]], 28 | '(' => [["⠀","⣴","⠋","⠀"],["⠀","⠻","⣄","⠀"]], 29 | ':' => [["⠀","⢠","⡄","⠀"],["⠀","⠰","⠆","⠀"]], 30 | '\\' => [["⠘","⣧","⠀","⠀"],["⠀","⠘","⣧","⠀"]], 31 | 'o' => [["⢀","⡤","⢄","⠀"],["⠸","⣇","⡸","⠀"]], 32 | 'N' => [["⢸","⣧","⢸","⠀"],["⢸","⡇","⢻","⠀"]], 33 | '/' => [["⠀","⢠","⡟","⠀"],["⢠","⡟","⠀","⠀"]], 34 | 'p' => [["⢠","⡤","⢄","⠀"],["⢸","⡧","⠜","⠀"]], 35 | '-' => [["⢀","⣀","⣀","⠀"],["⠀","⠀","⠀","⠀"]], 36 | 'm' => [["⢠","⡤","⡠","⡀"],["⢸","⡇","⡇","⡇"]], 37 | '@' => [["⢠","⡞","⡩","⡆"],["⠘","⢧","⣑","⡃"]], 38 | 'R' => [["⢸","⡏","⢱","⠀"],["⢸","⡟","⢅","⠀"]], 39 | 'n' => [["⢠","⡤","⢄","⠀"],["⢸","⡇","⢸","⠀"]], 40 | 'W' => [["⢸","⡇","⡄","⡇"],["⠈","⣷","⢱","⠁"]], 41 | 'info' => ' Everest Condensed SemiBold font ','_' => [["⠀","⠀","⠀","⠀"],["⣀","⣀","⣀","⣀"]], 42 | 'c' => [["⢀","⡤","⢄","⠀"],["⠸","⣇","⡠","⠀"]], 43 | '?' => [["⠰","⢉","⡷","⠀"],["⠀","⣭","⠀","⠀"]], 44 | 'L' => [["⢸","⡇","⠀","⠀"],["⢸","⣇","⣀","⠀"]], 45 | 'M' => [["⢸","⡷","⡰","⡇"],["⢸","⡇","⠁","⡇"]], 46 | 'F' => [["⢸","⣏","⡉","⠀"],["⢸","⡇","⠀","⠀"]], 47 | 'l' => [["⠀","⢹","⡇","⠀"],["⠀","⢸","⡇","⠀"]], 48 | '[' => [["⠀","⣿","⠉","⠀"],["⠀","⣿","⣀","⠀"]], 49 | '\'' => [["⠀","⠼","⠃","⠀"],["⠀","⠀","⠀","⠀"]], 50 | 'K' => [["⢸","⡧","⠊","⠀"],["⢸","⡏","⢢","⠀"]], 51 | "©" => [["⡎","⣴","⠢","⢱"],["⢇","⠻","⠔","⡸"]], 52 | 'S' => [["⠰","⣏","⠑","⠀"],["⠠","⣈","⡷","⠀"]], 53 | 'f' => [["⢀","⣾","⣉","⠀"],["⢀","⡿","⠀","⠀"]], 54 | '*' => [["⠠","⣴","⡤","⠀"],["⠈","⠙","⠉","⠀"]], 55 | '}' => [["⠈","⢷","⣀","⠀"],["⢀","⡿","⠀","⠀"]], 56 | '1' => [["⠀","⢺","⡇","⠀"],["⠀","⣸","⣇","⠀"]], 57 | 'C' => [["⢰","⡏","⠑","⠀"],["⠸","⣇","⡠","⠀"]], 58 | 'Q' => [["⢰","⡏","⢱","⠀"],["⠸","⣗","⢜","⠀"]], 59 | '%' => [["⠰","⢝","⡴","⠀"],["⠰","⢫","⡲","⠀"]], 60 | 'J' => [["⠀","⠀","⣿","⠀"],["⠰","⣀","⡿","⠀"]], 61 | '0' => [["⢰","⡏","⢱","⠀"],["⠸","⣇","⡸","⠀"]], 62 | 'w' => [["⢠","⡄","⠀","⡄"],["⠘","⣧","⢣","⠃"]], 63 | "£" => [["⢀","⣾","⡑","⠀"],["⢀","⣿","⣀","⠀"]], 64 | 'i' => [["⠀","⢨","⡅","⠀"],["⠀","⢸","⡇","⠀"]], 65 | '9' => [["⢰","⠉","⣷","⠀"],["⠠","⣉","⡿","⠀"]], 66 | '7' => [["⠈","⢉","⡿","⠀"],["⠀","⣿","⠀","⠀"]], 67 | 'I' => [["⠈","⣿","⠁","⠀"],["⢀","⣿","⡀","⠀"]], 68 | 'X' => [["⠸","⣇","⡸","⠀"],["⢰","⠉","⣷","⠀"]], 69 | 'r' => [["⢠","⡤","⢄","⠀"],["⢸","⡇","⠀","⠀"]], 70 | 'U' => [["⢸","⡇","⢸","⠀"],["⠸","⣇","⡸","⠀"]], 71 | '<' => [["⢀","⡴","⠂","⠀"],["⠀","⠙","⠂","⠀"]], 72 | '|' => [["⠀","⢸","⡇","⠀"],["⠀","⢸","⡇","⠀"]], 73 | '>' => [["⠀","⠲","⣄","⠀"],["⠀","⠚","⠁","⠀"]], 74 | '2' => [["⠐","⢉","⡷","⠀"],["⢰","⣏","⣀","⠀"]], 75 | 'a' => [["⠀","⠤","⣄","⠀"],["⠰","⣉","⣿","⠀"]], 76 | ',' => [["⠀","⠀","⠀","⠀"],["⠀","⣰","⠆","⠀"]], 77 | 'Y' => [["⠸","⣇","⣀","⠇"],["⠀","⢸","⡇","⠀"]], 78 | 'G' => [["⢰","⡏","⠑","⠀"],["⠸","⣇","⣹","⠀"]], 79 | 'v' => [["⢠","⡄","⢠","⠀"],["⢸","⠗","⠁","⠀"]], 80 | 'P' => [["⢸","⡏","⢱","⠀"],["⢸","⡏","⠁","⠀"]], 81 | 'g' => [["⢀","⠤","⣤","⠀"],["⢈","⣒","⡿","⠀"]], 82 | ' ' => [["⠀","⠀","⠀","⠀"],["⠀","⠀","⠀","⠀"]], 83 | 'V' => [["⢸","⡇","⢸","⠀"],["⠀","⢻","⠃","⠀"]], 84 | 'q' => [["⢀","⠤","⣤","⠀"],["⠘","⠤","⣿","⠀"]], 85 | 'O' => [["⢰","⡏","⢱","⠀"],["⠸","⣇","⡸","⠀"]], 86 | '!' => [["⠀","⢸","⡇","⠀"],["⠀","⢨","⡅","⠀"]], 87 | '.' => [["⠀","⠀","⠀","⠀"],["⠀","⠰","⠆","⠀"]], 88 | '&' => [["⠰","⣏","⠆","⠀"],["⠸","⣇","⢏","⠀"]], 89 | '4' => [["⢀","⢴","⡇","⠀"],["⠘","⢺","⡗","⠀"]], 90 | 'Z' => [["⠈","⢉","⡿","⠀"],["⢰","⣏","⣀","⠀"]], 91 | 'e' => [["⢀","⡤","⢄","⠀"],["⠸","⣏","⡩","⠀"]], 92 | '6' => [["⢰","⣏","⡑","⠀"],["⠸","⣇","⡸","⠀"]], 93 | 'b' => [["⢸","⡧","⢄","⠀"],["⢸","⣇","⡸","⠀"]], 94 | 'A' => [["⢀","⡾","⢆","⠀"],["⢸","⡏","⢹","⠀"]], 95 | 'H' => [["⢸","⣇","⣸","⠀"],["⢸","⡇","⢸","⠀"]], 96 | ']' => [["⠀","⠉","⣿","⠀"],["⠀","⣀","⣿","⠀"]]} 97 | -------------------------------------------------------------------------------- /examples/fonts/Everest Condensed.grf: -------------------------------------------------------------------------------- 1 | {'$' => [["⠠","⣚","⠢","⠀"],["⠐","⢤","⠕","⠀"]], 2 | 'D' => [["⢸","⠉","⢢","⠀"],["⢸","⣀","⠜","⠀"]], 3 | 'h' => [["⢸","⡠","⢄","⠀"],["⢸","⠀","⢸","⠀"]], 4 | ';' => [["⠀","⠠","⠀","⠀"],["⠀","⡰","⠀","⠀"]], 5 | 'k' => [["⢸","⢀","⠔","⠀"],["⢸","⠑","⢄","⠀"]], 6 | 'B' => [["⢸","⣉","⡱","⠀"],["⢸","⣀","⡸","⠀"]], 7 | ')' => [["⠀","⠑","⡄","⠀"],["⠀","⡠","⠃","⠀"]], 8 | 't' => [["⠠","⡧","⠄","⠀"],["⠀","⢇","⡠","⠀"]], 9 | 'z' => [["⠠","⠤","⡤","⠀"],["⢠","⣊","⣀","⠀"]], 10 | '#' => [["⠠","⡧","⢼","⠄"],["⠐","⡗","⢺","⠂"]], 11 | '"' => [["⠠","⠃","⠜","⠀"],["⠀","⠀","⠀","⠀"]], 12 | '^' => [["⠠","⠊","⠢","⠀"],["⠀","⠀","⠀","⠀"]], 13 | '3' => [["⠈","⣉","⡝","⠀"],["⠠","⣀","⡸","⠀"]], 14 | '8' => [["⠰","⣉","⡱","⠀"],["⠸","⣀","⡸","⠀"]], 15 | 'y' => [["⢠","⠀","⢠","⠀"],["⠈","⣒","⡺","⠀"]], 16 | '~' => [["⠰","⠑","⠜","⠀"],["⠀","⠀","⠀","⠀"]], 17 | 'j' => [["⠀","⠀","⢨","⠀"],["⠠","⣀","⡸","⠀"]], 18 | 'd' => [["⢀","⠤","⣸","⠀"],["⠸","⣀","⢼","⠀"]], 19 | 'T' => [["⠈","⢹","⠉","⠀"],["⠀","⢸","⠀","⠀"]], 20 | '5' => [["⢸","⣉","⡉","⠀"],["⠠","⣀","⡸","⠀"]], 21 | 'x' => [["⠠","⡀","⡠","⠀"],["⢀","⠜","⢄","⠀"]], 22 | '+' => [["⢀","⣰","⣀","⠀"],["⠀","⠘","⠀","⠀"]], 23 | 's' => [["⢀","⠤","⢄","⠀"],["⠠","⣉","⡢","⠀"]], 24 | 'u' => [["⢠","⠀","⢠","⠀"],["⠸","⣀","⢼","⠀"]], 25 | '{' => [["⢀","⡰","⠉","⠀"],["⠀","⠸","⣀","⠀"]], 26 | "©" => [["⡎","⡔","⠢","⢱"],["⢇","⠣","⠔","⡸"]], 27 | 'K' => [["⢸","⡠","⠊","⠀"],["⢸","⠈","⢢","⠀"]], 28 | '\'' => [["⠀","⠜","⠀","⠀"],["⠀","⠀","⠀","⠀"]], 29 | '*' => [["⠠","⣰","⡠","⠀"],["⠈","⠘","⠈","⠀"]], 30 | 'f' => [["⢀","⣰","⣉","⠀"],["⢀","⡸","⠀","⠀"]], 31 | 'S' => [["⠰","⣉","⠑","⠀"],["⠠","⣀","⡱","⠀"]], 32 | '1' => [["⠀","⢺","⠀","⠀"],["⠀","⣸","⡀","⠀"]], 33 | '}' => [["⠈","⠱","⣀","⠀"],["⢀","⡸","⠀","⠀"]], 34 | 'C' => [["⢰","⠉","⠑","⠀"],["⠸","⣀","⡠","⠀"]], 35 | 'W' => [["⢸","⢠","⢸","⠀"],["⠈","⡎","⡎","⠀"]], 36 | 'R' => [["⢸","⠉","⢱","⠀"],["⢸","⠙","⢅","⠀"]], 37 | 'n' => [["⢠","⡠","⢄","⠀"],["⢸","⠀","⢸","⠀"]], 38 | 'info' => ' Everest Condensed font ','_' => [["⠀","⠀","⠀","⠀"],["⣀","⣀","⣀","⣀"]], 39 | 'c' => [["⢀","⠤","⢄","⠀"],["⠸","⣀","⡠","⠀"]], 40 | 'L' => [["⢸","⠀","⠀","⠀"],["⢸","⣀","⣀","⠀"]], 41 | '?' => [["⠰","⠉","⡱","⠀"],["⠀","⢨","⠀","⠀"]], 42 | 'F' => [["⢸","⣉","⡉","⠀"],["⢸","⠀","⠀","⠀"]], 43 | 'M' => [["⢸","⢆","⢾","⠀"],["⢸","⠈","⢸","⠀"]], 44 | '[' => [["⠀","⡏","⠁","⠀"],["⠀","⣇","⡀","⠀"]], 45 | 'l' => [["⠀","⠉","⡇","⠀"],["⠀","⠀","⡇","⠀"]], 46 | 'N' => [["⢸","⢆","⢸","⠀"],["⢸","⠈","⢾","⠀"]], 47 | 'o' => [["⢀","⠤","⢄","⠀"],["⠸","⣀","⡸","⠀"]], 48 | '/' => [["⠀","⠀","⡜","⠀"],["⠀","⡜","⠀","⠀"]], 49 | 'p' => [["⢠","⡠","⢄","⠀"],["⢸","⠢","⠜","⠀"]], 50 | '-' => [["⢀","⣀","⣀","⠀"],["⠀","⠀","⠀","⠀"]], 51 | 'm' => [["⢠","⢄","⢄","⠀"],["⢸","⢸","⢸","⠀"]], 52 | '@' => [["⢠","⢊","⢕","⡄"],["⠘","⢌","⣒","⡁"]], 53 | '=' => [["⠠","⠤","⠤","⠀"],["⠈","⠉","⠉","⠀"]], 54 | 'E' => [["⢸","⣉","⡉","⠀"],["⢸","⣀","⣀","⠀"]], 55 | '(' => [["⠀","⡔","⠁","⠀"],["⠀","⠣","⡀","⠀"]], 56 | '\\' => [["⠀","⢣","⠀","⠀"],["⠀","⠀","⢣","⠀"]], 57 | ':' => [["⠀","⠠","⠀","⠀"],["⠀","⠐","⠀","⠀"]], 58 | '>' => [["⠀","⠢","⡀","⠀"],["⠀","⠊","⠀","⠀"]], 59 | '2' => [["⠐","⠉","⡱","⠀"],["⢠","⣊","⣀","⠀"]], 60 | 'a' => [["⠀","⠤","⢄","⠀"],["⠰","⣉","⣹","⠀"]], 61 | ',' => [["⠀","⠀","⠀","⠀"],["⠀","⡰","⠀","⠀"]], 62 | '7' => [["⠈","⠉","⡹","⠀"],["⠀","⡎","⠀","⠀"]], 63 | '9' => [["⢰","⠉","⢱","⠀"],["⠠","⣉","⡹","⠀"]], 64 | 'X' => [["⠘","⢄","⠜","⠀"],["⢠","⠊","⢢","⠀"]], 65 | 'I' => [["⠈","⢹","⠉","⠀"],["⢀","⣸","⣀","⠀"]], 66 | 'U' => [["⢸","⠀","⢸","⠀"],["⠸","⣀","⡸","⠀"]], 67 | 'r' => [["⢠","⡠","⢄","⠀"],["⢸","⠀","⠀","⠀"]], 68 | '<' => [["⠀","⡠","⠂","⠀"],["⠀","⠈","⠂","⠀"]], 69 | '|' => [["⠀","⢸","⠀","⠀"],["⠀","⢸","⠀","⠀"]], 70 | '%' => [["⠠","⡢","⡠","⠀"],["⠠","⠪","⡢","⠀"]], 71 | 'J' => [["⠀","⠀","⢸","⠀"],["⠰","⣀","⡸","⠀"]], 72 | '0' => [["⢰","⢉","⢱","⠀"],["⠸","⣈","⡸","⠀"]], 73 | "£" => [["⢀","⣎","⡑","⠀"],["⢀","⣇","⣀","⠀"]], 74 | 'w' => [["⢠","⠀","⢠","⠀"],["⠘","⡜","⡜","⠀"]], 75 | 'i' => [["⠀","⢨","⠀","⠀"],["⠀","⢸","⠀","⠀"]], 76 | 'Q' => [["⢰","⠉","⢱","⠀"],["⠸","⣐","⢜","⠀"]], 77 | 'Z' => [["⠈","⢉","⠝","⠀"],["⢰","⣁","⣀","⠀"]], 78 | 'e' => [["⢀","⠤","⢄","⠀"],["⠸","⣉","⡩","⠀"]], 79 | '6' => [["⢰","⣉","⡑","⠀"],["⠸","⣀","⡸","⠀"]], 80 | 'b' => [["⢸","⡠","⢄","⠀"],["⢸","⢄","⡸","⠀"]], 81 | 'H' => [["⢸","⣀","⣸","⠀"],["⢸","⠀","⢸","⠀"]], 82 | 'A' => [["⢀","⠎","⢆","⠀"],["⢸","⠉","⢹","⠀"]], 83 | ']' => [["⠀","⠉","⡇","⠀"],["⠀","⣀","⡇","⠀"]], 84 | '&' => [["⠰","⣉","⠆","⠀"],["⠸","⣀","⢏","⠀"]], 85 | '4' => [["⢀","⠔","⡇","⠀"],["⠘","⠒","⡗","⠀"]], 86 | 'q' => [["⢀","⠤","⣠","⠀"],["⠘","⠤","⢺","⠀"]], 87 | 'O' => [["⢰","⠉","⢱","⠀"],["⠸","⣀","⡸","⠀"]], 88 | '!' => [["⠀","⢸","⠀","⠀"],["⠀","⢨","⠀","⠀"]], 89 | '.' => [["⠀","⠀","⠀","⠀"],["⠀","⠰","⠀","⠀"]], 90 | 'P' => [["⢸","⠉","⢱","⠀"],["⢸","⠉","⠁","⠀"]], 91 | 'v' => [["⢠","⠀","⢠","⠀"],["⢸","⠔","⠁","⠀"]], 92 | 'G' => [["⢰","⠉","⠑","⠀"],["⠸","⣀","⣹","⠀"]], 93 | 'Y' => [["⠸","⡀","⡸","⠀"],["⠀","⢸","⠀","⠀"]], 94 | 'g' => [["⢀","⠤","⢤","⠀"],["⢈","⣒","⡹","⠀"]], 95 | ' ' => [["⠀","⠀","⠀","⠀"],["⠀","⠀","⠀","⠀"]], 96 | 'V' => [["⢸","⠀","⢸","⠀"],["⠀","⢣","⠃","⠀"]]} 97 | -------------------------------------------------------------------------------- /examples/fonts/Everest.grf: -------------------------------------------------------------------------------- 1 | {'4' => [["⢀","⠔","⢁","⠀"],["⠘","⠒","⢺","⠂"]], 2 | '&' => [["⠰","⣉","⠆","⠀"],["⢎","⣀","⡱","⡁"]], 3 | ']' => [["⠀","⠉","⡇","⠀"],["⠀","⣀","⡇","⠀"]], 4 | 'A' => [["⢀","⠎","⢆","⠀"],["⡜","⠉","⠙","⡄"]], 5 | 'H' => [["⢸","⣀","⣀","⡇"],["⢸","⠀","⠀","⡇"]], 6 | 'b' => [["⢸","⡠","⠤","⡀"],["⢸","⢄","⣀","⠇"]], 7 | '6' => [["⢰","⣉","⣉","⠂"],["⠸","⣀","⣀","⠇"]], 8 | 'e' => [["⢀","⠤","⠤","⡀"],["⠸","⣉","⣉","⠅"]], 9 | 'Z' => [["⠉","⢉","⠝","⠁"],["⣔","⣁","⣀","⡀"]], 10 | 'V' => [["⢣","⠀","⢠","⠃"],["⠀","⢣","⠃","⠀"]], 11 | ' ' => [["⠀","⠀","⠀","⠀"],["⠀","⠀","⠀","⠀"]], 12 | 'g' => [["⢀","⠤","⠤","⡄"],["⠈","⣒","⣊","⠇"]], 13 | 'Y' => [["⠣","⡀","⡠","⠃"],["⠀","⢸","⠀","⠀"]], 14 | 'v' => [["⢠","⠀","⢀","⠄"],["⢸","⠔","⠁","⠀"]], 15 | 'G' => [["⢠","⠊","⠉","⠂"],["⠘","⢄","⣉","⡇"]], 16 | 'P' => [["⢸","⠉","⠉","⡆"],["⢸","⠉","⠉","⠀"]], 17 | '!' => [["⠀","⢸","⠀","⠀"],["⠀","⢨","⠀","⠀"]], 18 | '.' => [["⠀","⠀","⠀","⠀"],["⠀","⠰","⠀","⠀"]], 19 | 'O' => [["⡔","⠉","⠑","⡄"],["⠣","⣀","⡠","⠃"]], 20 | 'q' => [["⢀","⠤","⢄","⡄"],["⠘","⠤","⠔","⡇"]], 21 | '<' => [["⠀","⡠","⠂","⠀"],["⠀","⠈","⠂","⠀"]], 22 | '|' => [["⠀","⢸","⠀","⠀"],["⠀","⢸","⠀","⠀"]], 23 | 'r' => [["⢠","⡠","⠤","⡀"],["⢸","⠀","⠀","⠀"]], 24 | 'U' => [["⢸","⠀","⠀","⡇"],["⠸","⣀","⣀","⠇"]], 25 | 'I' => [["⠈","⢹","⠉","⠀"],["⢀","⣸","⣀","⠀"]], 26 | 'X' => [["⠑","⢄","⠔","⠁"],["⡠","⠊","⠢","⡀"]], 27 | '9' => [["⢰","⠉","⠉","⡆"],["⠠","⣉","⣉","⠇"]], 28 | '7' => [["⠈","⠉","⢉","⠇"],["⠀","⢰","⠁","⠀"]], 29 | ',' => [["⠀","⠀","⠀","⠀"],["⠀","⡰","⠂","⠀"]], 30 | 'a' => [["⠀","⠤","⠤","⡀"],["⠰","⣉","⣉","⡇"]], 31 | '2' => [["⠐","⠉","⡩","⠂"],["⢠","⣊","⣀","⡀"]], 32 | '>' => [["⠀","⠐","⢄","⠀"],["⠀","⠐","⠁","⠀"]], 33 | 'Q' => [["⡔","⠉","⠑","⡄"],["⠣","⣀","⡢","⡃"]], 34 | 'i' => [["⠀","⢨","⠀","⠀"],["⠀","⢸","⠀","⠀"]], 35 | 'w' => [["⡄","⠀","⠀","⡄"],["⠘","⡜","⡜","⠀"]], 36 | "£" => [["⣰","⣉","⠉","⠂"],["⣸","⣀","⣀","⡀"]], 37 | '0' => [["⡔","⢉","⠑","⡄"],["⠣","⣈","⡠","⠃"]], 38 | 'J' => [["⠀","⠀","⠀","⡇"],["⠰","⣀","⣀","⠇"]], 39 | '%' => [["⢔","⠄","⡠","⠀"],["⠠","⠊","⢔","⠄"]], 40 | 'l' => [["⠀","⠉","⡇","⠀"],["⠀","⠀","⡇","⠀"]], 41 | '[' => [["⠀","⡏","⠁","⠀"],["⠀","⣇","⡀","⠀"]], 42 | 'F' => [["⢸","⣉","⡉","⠁"],["⢸","⠀","⠀","⠀"]], 43 | 'M' => [["⣧","⠀","⢠","⡇"],["⡇","⢣","⠃","⡇"]], 44 | '?' => [["⠰","⠉","⢉","⠆"],["⠀","⠀","⡅","⠀"]], 45 | 'L' => [["⢸","⠀","⠀","⠀"],["⢸","⣀","⣀","⡀"]], 46 | '_' => [["⠀","⠀","⠀","⠀"],["⣀","⣀","⣀","⣀"]], 47 | 'c' => [["⢀","⠤","⠤","⡀"],["⠸","⣀","⣀","⠄"]], 48 | 'info' => ' Everest font ','R' => [["⢸","⠉","⠉","⡆"],["⢸","⠉","⠫","⡀"]], 49 | 'W' => [["⡇","⢠","⠀","⡇"],["⢣","⠃","⢣","⠃"]], 50 | 'n' => [["⢠","⡠","⠤","⡀"],["⢸","⠀","⠀","⡇"]], 51 | 'C' => [["⢠","⠊","⠉","⠂"],["⠘","⢄","⣀","⠄"]], 52 | '}' => [["⠈","⠉","⢆","⡀"],["⢀","⣀","⠇","⠀"]], 53 | '1' => [["⠀","⢺","⠀","⠀"],["⢀","⣸","⣀","⠀"]], 54 | 'S' => [["⢎","⣉","⠉","⠂"],["⢄","⣀","⣉","⠆"]], 55 | '*' => [["⠠","⣰","⡠","⠀"],["⠈","⠘","⠈","⠀"]], 56 | 'f' => [["⢀","⣰","⣉","⠂"],["⢄","⡸","⠀","⠀"]], 57 | '\'' => [["⠀","⠜","⠀","⠀"],["⠀","⠀","⠀","⠀"]], 58 | 'K' => [["⢸","⣀","⠔","⠁"],["⢸","⠀","⠑","⡄"]], 59 | "©" => [["⡎","⡔","⠢","⢱"],["⢇","⠣","⠔","⡸"]], 60 | '\\' => [["⠀","⢣","⠀","⠀"],["⠀","⠀","⢣","⠀"]], 61 | ':' => [["⠀","⠠","⠄","⠀"],["⠀","⠐","⠂","⠀"]], 62 | '(' => [["⠀","⡔","⠁","⠀"],["⠀","⠣","⡀","⠀"]], 63 | 'E' => [["⢸","⣉","⡉","⠁"],["⢸","⣀","⣀","⡀"]], 64 | '=' => [["⠠","⠤","⠤","⠄"],["⠈","⠉","⠉","⠁"]], 65 | '@' => [["⡔","⡩","⡕","⡄"],["⠣","⣑","⣓","⡃"]], 66 | '-' => [["⢀","⣀","⣀","⡀"],["⠀","⠀","⠀","⠀"]], 67 | 'm' => [["⡤","⢄","⠤","⡀"],["⡇","⢸","⠀","⡇"]], 68 | 'p' => [["⢠","⡠","⠤","⡀"],["⢸","⠢","⠤","⠃"]], 69 | '/' => [["⠀","⠀","⡜","⠀"],["⠀","⡜","⠀","⠀"]], 70 | 'o' => [["⢀","⠤","⠤","⡀"],["⠸","⣀","⣀","⠇"]], 71 | 'N' => [["⢸","⢣","⠀","⡇"],["⢸","⠀","⢣","⡇"]], 72 | '^' => [["⠠","⠊","⠢","⠀"],["⠀","⠀","⠀","⠀"]], 73 | '#' => [["⠠","⡧","⢼","⠄"],["⠐","⡗","⢺","⠂"]], 74 | '"' => [["⠠","⠃","⠜","⠀"],["⠀","⠀","⠀","⠀"]], 75 | ')' => [["⠀","⠑","⡄","⠀"],["⠀","⡠","⠃","⠀"]], 76 | 't' => [["⠠","⡧","⠄","⠀"],["⠀","⢇","⣀","⠄"]], 77 | 'z' => [["⠠","⠤","⢤","⠄"],["⢀","⣔","⣁","⡀"]], 78 | 'B' => [["⢸","⣉","⣉","⠆"],["⢸","⣀","⣀","⠇"]], 79 | ';' => [["⠀","⠠","⠄","⠀"],["⠀","⡰","⠂","⠀"]], 80 | 'k' => [["⢸","⠀","⡠","⠂"],["⢸","⠉","⠢","⡀"]], 81 | 'h' => [["⢸","⡠","⠤","⡀"],["⢸","⠀","⠀","⡇"]], 82 | 'D' => [["⢸","⠉","⠑","⡄"],["⢸","⣀","⡠","⠃"]], 83 | '$' => [["⠠","⣒","⠓","⠄"],["⠐","⢤","⠭","⠂"]], 84 | '{' => [["⢀","⡰","⠉","⠁"],["⠀","⠸","⣀","⡀"]], 85 | 'u' => [["⢠","⠀","⠀","⡄"],["⠸","⣀","⡠","⡇"]], 86 | 's' => [["⢀","⠤","⠤","⡀"],["⠠","⣉","⣒","⠄"]], 87 | '+' => [["⢀","⣰","⣀","⠀"],["⠀","⠘","⠀","⠀"]], 88 | 'x' => [["⠠","⡀","⢀","⠄"],["⢠","⠊","⠑","⡄"]], 89 | '5' => [["⢸","⣉","⣉","⠁"],["⠠","⣀","⣀","⠇"]], 90 | 'T' => [["⠉","⢹","⠉","⠁"],["⠀","⢸","⠀","⠀"]], 91 | 'j' => [["⠀","⠀","⠀","⡅"],["⠠","⣀","⣀","⠇"]], 92 | 'd' => [["⢀","⠤","⢄","⡇"],["⠸","⣀","⡠","⡇"]], 93 | '~' => [["⠰","⠙","⠦","⠃"],["⠀","⠀","⠀","⠀"]], 94 | 'y' => [["⢠","⠀","⠀","⡄"],["⠈","⣒","⣒","⠇"]], 95 | '8' => [["⠰","⣉","⣉","⠆"],["⠸","⣀","⣀","⠇"]], 96 | '3' => [["⠈","⢉","⣩","⠃"],["⠠","⣀","⣀","⠇"]]} 97 | -------------------------------------------------------------------------------- /examples/fonts/Mutual.grf: -------------------------------------------------------------------------------- 1 | {'=' => [["⠠","⠤","⠤","⠀"],["⠈","⠉","⠉","⠀"]], 2 | '(' => [["⢀","⠎","⠀","⠀"],["⠈","⢆","⠀","⠀"]], 3 | 'E' => [["⢹","⣉","⡍","⠃"],["⠼","⠤","⠥","⠆"]], 4 | '\\' => [["⠐","⢄","⠀","⠀"],["⠀","⠀","⠑","⠄"]], 5 | ':' => [["⠀","⠰","⠀","⠀"],["⠀","⠰","⠀","⠀"]], 6 | 'N' => [["⢹","⠢","⡈","⡏"],["⠼","⠄","⠈","⠧"]], 7 | 'o' => [["⢀","⠤","⠤","⡀"],["⠘","⠤","⠤","⠃"]], 8 | '/' => [["⠀","⠀","⡠","⠂"],["⠠","⠊","⠀","⠀"]], 9 | 'p' => [["⢤","⠤","⠤","⡀"],["⣸","⡒","⠒","⠁"]], 10 | '-' => [["⢀","⣀","⣀","⠀"],["⠀","⠀","⠀","⠀"]], 11 | 'm' => [["⢤","⢄","⢄","⠀"],["⠼","⠸","⠸","⠄"]], 12 | '@' => [["⢠","⢊","⠭","⡆"],["⠈","⠢","⠭","⠁"]], 13 | 'n' => [["⢤","⡠","⢄","⠀"],["⠼","⠄","⠼","⠄"]], 14 | 'R' => [["⢹","⣉","⣉","⠆"],["⠼","⠄","⠱","⠄"]], 15 | 'W' => [["⢹","⠁","⠈","⡏"],["⠸","⠊","⠑","⠇"]], 16 | 'info' => ' Mutual font ','c' => [["⢀","⠤","⠤","⡀"],["⠘","⠤","⠤","⠂"]], 17 | '_' => [["⠀","⠀","⠀","⠀"],["⣀","⣀","⣀","⣀"]], 18 | '?' => [["⠊","⢉","⣉","⠆"],["⠀","⠰","⠀","⠀"]], 19 | 'L' => [["⢹","⠁","⠀","⠀"],["⠼","⠤","⠤","⠆"]], 20 | 'F' => [["⢹","⣉","⡍","⠃"],["⠼","⠄","⠁","⠀"]], 21 | 'M' => [["⢹","⢆","⡰","⡏"],["⠼","⠄","⠠","⠧"]], 22 | 'l' => [["⠀","⢹","⠀","⠀"],["⠀","⠼","⠄","⠀"]], 23 | '[' => [["⠀","⡏","⠉","⠀"],["⠀","⣇","⣀","⠀"]], 24 | '\'' => [["⠀","⠨","⠃","⠀"],["⠀","⠀","⠀","⠀"]], 25 | 'K' => [["⢹","⣁","⡨","⠋"],["⠼","⠄","⠨","⠦"]], 26 | "©" => [["⡔","⡩","⣩","⢢"],["⠣","⣑","⣒","⠜"]], 27 | 'S' => [["⢎","⣉","⣑","⠃"],["⠖","⠤","⠤","⠃"]], 28 | 'f' => [["⢀","⣎","⡉","⠂"],["⠠","⠧","⠀","⠀"]], 29 | '*' => [["⠠","⣴","⡤","⠀"],["⠀","⠁","⠁","⠀"]], 30 | '}' => [["⠈","⠱","⣀","⠀"],["⠠","⠜","⠀","⠀"]], 31 | '1' => [["⠀","⢺","⠀","⠀"],["⠀","⠼","⠄","⠀"]], 32 | 'C' => [["⡔","⠉","⠉","⠂"],["⠑","⠤","⠤","⠂"]], 33 | 'd' => [["⡠","⠤","⣹","⠀"],["⠣","⠤","⠺","⠄"]], 34 | 'j' => [["⠀","⠀","⢬","⠀"],["⠀","⢄","⡸","⠀"]], 35 | 'T' => [["⠋","⢹","⠉","⠃"],["⠀","⠼","⠄","⠀"]], 36 | '5' => [["⣏","⣉","⣉","⠃"],["⠢","⠤","⠤","⠃"]], 37 | '+' => [["⢀","⣰","⣀","⠀"],["⠀","⠘","⠀","⠀"]], 38 | 'x' => [["⢤","⠄","⢤","⠄"],["⠴","⠍","⠵","⠄"]], 39 | 's' => [["⢀","⠤","⠤","⠄"],["⠠","⠭","⠭","⠂"]], 40 | '{' => [["⢀","⡰","⠉","⠀"],["⠀","⠘","⠤","⠀"]], 41 | 'u' => [["⢤","⠀","⠠","⡄"],["⠘","⠤","⠔","⠧"]], 42 | '3' => [["⠋","⢉","⣝","⠁"],["⠢","⠤","⠤","⠃"]], 43 | 'y' => [["⢤","⠄","⢤","⠄"],["⣄","⡱","⠁","⠀"]], 44 | '8' => [["⢎","⣉","⣉","⠆"],["⠣","⠤","⠤","⠃"]], 45 | '~' => [["⢀","⠤","⣀","⠄"],["⠀","⠀","⠀","⠀"]], 46 | 'B' => [["⢹","⣉","⣉","⠆"],["⠼","⠤","⠤","⠃"]], 47 | '"' => [["⠨","⠃","⠝","⠀"],["⠀","⠀","⠀","⠀"]], 48 | '#' => [["⠠","⡦","⢴","⠄"],["⠺","⠒","⠗","⠀"]], 49 | ')' => [["⠈","⢆","⠀","⠀"],["⢀","⠎","⠀","⠀"]], 50 | 'z' => [["⢠","⠤","⢤","⠄"],["⠠","⠮","⠥","⠆"]], 51 | 't' => [["⠠","⡧","⠄","⠀"],["⠀","⠣","⠤","⠂"]], 52 | '^' => [["⠠","⠊","⠢","⠀"],["⠀","⠀","⠀","⠀"]], 53 | '$' => [["⠠","⣒","⣗","⠂"],["⠐","⠺","⠒","⠁"]], 54 | 'D' => [["⢹","⠉","⠑","⡄"],["⠼","⠤","⠔","⠁"]], 55 | 'h' => [["⢹","⡠","⢄","⠀"],["⠼","⠄","⠼","⠄"]], 56 | ';' => [["⠀","⠀","⠆","⠀"],["⠀","⢐","⠆","⠀"]], 57 | 'k' => [["⢹","⠀","⢤","⠄"],["⠼","⠍","⠵","⠄"]], 58 | 'Y' => [["⠙","⡅","⡝","⠁"],["⠀","⠼","⠄","⠀"]], 59 | 'G' => [["⡔","⠉","⣉","⡂"],["⠑","⠤","⠔","⠇"]], 60 | 'P' => [["⢹","⣉","⣉","⠆"],["⠼","⠄","⠀","⠀"]], 61 | 'v' => [["⢤","⠄","⠠","⡤"],["⠀","⠱","⠎","⠀"]], 62 | 'g' => [["⡠","⠤","⢄","⠄"],["⡪","⠭","⠥","⡀"]], 63 | ' ' => [["⠀","⠀","⠀","⠀"],["⠀","⠀","⠀","⠀"]], 64 | 'V' => [["⠹","⡁","⢈","⠏"],["⠀","⠱","⠎","⠀"]], 65 | 'q' => [["⡠","⠤","⣠","⠄"],["⠑","⠒","⣺","⡀"]], 66 | 'O' => [["⡔","⠉","⠑","⡄"],["⠑","⠤","⠔","⠁"]], 67 | '!' => [["⠀","⢸","⠀","⠀"],["⠀","⠨","⠀","⠀"]], 68 | '.' => [["⠀","⠀","⠀","⠀"],["⠀","⠰","⠀","⠀"]], 69 | '&' => [["⢰","⡩","⢂","⡀"],["⠣","⠬","⠪","⠄"]], 70 | '4' => [["⡠","⠊","⡇","⠀"],["⠉","⠩","⠯","⠁"]], 71 | 'Z' => [["⠋","⢉","⠝","⠁"],["⠴","⠥","⠤","⠆"]], 72 | 'e' => [["⢀","⠤","⠤","⡀"],["⠘","⠭","⠭","⠅"]], 73 | '6' => [["⣠","⣊","⣁","⠀"],["⠣","⠤","⠤","⠃"]], 74 | 'b' => [["⢹","⡠","⠤","⡀"],["⠼","⠢","⠤","⠃"]], 75 | 'A' => [["⠀","⡜","⢣","⠀"],["⠼","⠍","⠩","⠧"]], 76 | 'H' => [["⢹","⣁","⣈","⡏"],["⠼","⠄","⠠","⠧"]], 77 | ']' => [["⠀","⠉","⡇","⠀"],["⠀","⣀","⡇","⠀"]], 78 | 'Q' => [["⡔","⠉","⠑","⡄"],["⠑","⠤","⢔","⡁"]], 79 | '%' => [["⠪","⢕","⠔","⠀"],["⠐","⠑","⠭","⠂"]], 80 | 'J' => [["⠀","⠉","⡏","⠁"],["⠣","⠤","⠃","⠀"]], 81 | '0' => [["⡔","⢉","⠕","⡄"],["⠑","⠥","⠔","⠁"]], 82 | 'w' => [["⢤","⠄","⢤","⠄"],["⠈","⠎","⠎","⠀"]], 83 | 'i' => [["⠀","⢬","⠀","⠀"],["⠀","⠼","⠄","⠀"]], 84 | "£" => [["⣰","⣉","⡉","⠂"],["⠼","⠤","⠤","⠆"]], 85 | '9' => [["⢎","⣉","⣉","⡆"],["⠀","⠤","⠊","⠀"]], 86 | '7' => [["⠋","⠉","⡩","⠃"],["⠀","⠸","⠀","⠀"]], 87 | 'I' => [["⠈","⢹","⠉","⠀"],["⠠","⠼","⠤","⠀"]], 88 | 'X' => [["⠙","⢅","⠝","⠁"],["⠴","⠅","⠵","⠄"]], 89 | 'r' => [["⢤","⡠","⠤","⡀"],["⠼","⠄","⠀","⠁"]], 90 | 'U' => [["⢹","⠁","⠈","⡏"],["⠘","⠤","⠤","⠃"]], 91 | '|' => [["⠀","⢸","⠀","⠀"],["⠀","⠸","⠀","⠀"]], 92 | '<' => [["⠀","⡠","⠊","⠀"],["⠀","⠈","⠢","⠀"]], 93 | '>' => [["⠀","⠑","⢄","⠀"],["⠀","⠔","⠁","⠀"]], 94 | '2' => [["⠊","⠉","⣉","⠆"],["⠴","⠭","⠤","⠆"]], 95 | 'a' => [["⠠","⠤","⢄","⠀"],["⠪","⠭","⠝","⠄"]], 96 | ',' => [["⠀","⠀","⠀","⠀"],["⠀","⡲","⠀","⠀"]]} 97 | -------------------------------------------------------------------------------- /examples/fonts/Reflections Dark.grf: -------------------------------------------------------------------------------- 1 | {'"' => [["⣯","⠇","⣯","⠇"],["⠀","⠀","⠀","⠀"]], 2 | 'f' => [["⣮","⣏","⡛","⠃"],["⠿","⠇","⠀","⠀"]], 3 | 'I' => [["⠀","⣯","⡇","⠀"],["⠀","⠿","⠇","⠀"]], 4 | '?' => [["⠛","⣋","⣯","⡇"],["⠀","⠶","⠆","⠀"]], 5 | 'c' => [["⣔","⡖","⠶","⠆"],["⠿","⠧","⠶","⠆"]], 6 | '}' => [["⠉","⣯","⣇","⡀"],["⠤","⠿","⠇","⠀"]], 7 | 'G' => [["⣮","⡏","⣛","⡃"],["⠿","⠧","⠷","⠇"]], 8 | 'J' => [["⠀","⠀","⣯","⡇"],["⠶","⠦","⠿","⠇"]], 9 | 'd' => [["⣔","⡖","⣯","⡇"],["⠿","⠧","⠿","⠇"]], 10 | 'u' => [["⣖","⡆","⣖","⡆"],["⠻","⠧","⠿","⠇"]], 11 | 'm' => [["⣖","⣖","⣖","⡄"],["⠿","⠝","⠽","⠇"]], 12 | 'A' => [["⣮","⣏","⣯","⡇"],["⠿","⠇","⠿","⠇"]], 13 | ';' => [["⠀","⠯","⠇","⠀"],["⠀","⣯","⠇","⠀"]], 14 | 'H' => [["⣯","⣇","⣯","⡇"],["⠿","⠇","⠿","⠇"]], 15 | '7' => [["⠛","⢋","⣯","⠇"],["⠀","⠾","⠏","⠀"]], 16 | '4' => [["⣯","⡇","⣯","⡇"],["⠉","⠉","⠿","⠇"]], 17 | 'w' => [["⣖","⡦","⣖","⡆"],["⠻","⠿","⠿","⠃"]], 18 | ')' => [["⠈","⢹","⣵","⠀"],["⠠","⠼","⠟","⠀"]], 19 | '8' => [["⢮","⣏","⣯","⠇"],["⠿","⠧","⠿","⠇"]], 20 | 'x' => [["⢖","⣆","⣖","⠆"],["⠿","⠇","⠿","⠇"]], 21 | 'Q' => [["⣮","⡏","⣯","⡇"],["⠿","⠧","⣫","⡆"]], 22 | ',' => [["⠀","⠀","⠀","⠀"],["⠀","⣯","⠇","⠀"]], 23 | 'e' => [["⣔","⡖","⣖","⡆"],["⠿","⠯","⠭","⠅"]], 24 | '3' => [["⠛","⣋","⣯","⠇"],["⠶","⠦","⠿","⠇"]], 25 | 'O' => [["⣮","⡏","⣯","⡇"],["⠿","⠧","⠿","⠇"]], 26 | 'M' => [["⣯","⣗","⣯","⡇"],["⠿","⠝","⠽","⠇"]], 27 | 'R' => [["⣯","⣏","⡯","⠃"],["⠿","⠇","⠿","⠆"]], 28 | 'Z' => [["⠛","⣫","⡯","⠃"],["⠾","⠯","⠶","⠆"]], 29 | 'y' => [["⣖","⡆","⣖","⡆"],["⣙","⣓","⣿","⡇"]], 30 | 'D' => [["⣯","⡏","⣯","⡆"],["⠿","⠧","⠿","⠇"]], 31 | 'W' => [["⣯","⣗","⣯","⡇"],["⠿","⠟","⠿","⠇"]], 32 | '_' => [["⠀","⠀","⠀","⠀"],["⣖","⣶","⣶","⣶"]], 33 | '<' => [["⢀","⣴","⠝","⠀"],["⠀","⠙","⠷","⠀"]], 34 | '$' => [["⣖","⣞","⣓","⡂"],["⠶","⢦","⡷","⠇"]], 35 | 'E' => [["⣮","⣏","⡛","⠃"],["⠿","⠧","⠶","⠆"]], 36 | '@' => [["⣮","⡏","⣯","⡆"],["⠻","⠧","⠭","⠅"]], 37 | 'K' => [["⣯","⣧","⡯","⠃"],["⠿","⠏","⠿","⠆"]], 38 | 's' => [["⣖","⣖","⣒","⡂"],["⠶","⠦","⠷","⠇"]], 39 | '~' => [["⣮","⠷","⣮","⠇"],["⠀","⠀","⠀","⠀"]], 40 | '5' => [["⣯","⣏","⣛","⡃"],["⠶","⠦","⠿","⠃"]], 41 | 'j' => [["⠀","⠀","⡭","⡅"],["⣤","⣄","⣿","⡇"]], 42 | 'k' => [["⣯","⡇","⡤","⡄"],["⠿","⠏","⠿","⠆"]], 43 | '&' => [["⢮","⡏","⣍","⣄"],["⠿","⠧","⠷","⠇"]], 44 | "£" => [["⣮","⣏","⡛","⠃"],["⠿","⠧","⠶","⠆"]], 45 | 'U' => [["⣯","⡇","⣯","⡇"],["⠻","⠧","⠿","⠇"]], 46 | 'o' => [["⣔","⡖","⣖","⡆"],["⠿","⠧","⠿","⠃"]], 47 | '2' => [["⠛","⣫","⡯","⠃"],["⠾","⠯","⠶","⠆"]], 48 | '^' => [["⡠","⣞","⡦","⡀"],["⠀","⠿","⠇","⠀"]], 49 | 'i' => [["⠀","⡭","⡅","⠀"],["⠀","⠿","⠇","⠀"]], 50 | 'T' => [["⠉","⣯","⡏","⠁"],["⠀","⠿","⠇","⠀"]], 51 | 'p' => [["⣖","⡖","⣖","⡆"],["⣿","⡗","⠛","⠃"]], 52 | 't' => [["⣯","⡗","⠒","⠀"],["⠿","⠧","⠶","⠆"]], 53 | 'n' => [["⣖","⡖","⣖","⡄"],["⠿","⠇","⠿","⠇"]], 54 | '+' => [["⠀","⡤","⡄","⠀"],["⠉","⠿","⠏","⠁"]], 55 | '9' => [["⣯","⡏","⣯","⡇"],["⠭","⠭","⠿","⠃"]], 56 | 'F' => [["⣮","⣏","⡛","⠃"],["⠿","⠇","⠀","⠀"]], 57 | '0' => [["⣮","⡏","⣯","⡇"],["⠿","⠧","⠿","⠃"]], 58 | '\\' => [["⠻","⣽","⡀","⠀"],["⠀","⠈","⠿","⠆"]], 59 | '-' => [["⢠","⢤","⣤","⡄"],["⠈","⠉","⠉","⠁"]], 60 | 'X' => [["⢯","⣇","⣯","⠇"],["⠿","⠇","⠿","⠇"]], 61 | ':' => [["⠀","⠯","⠇","⠀"],["⠀","⠯","⠇","⠀"]], 62 | '(' => [["⢰","⣽","⠉","⠀"],["⠘","⠿","⠤","⠀"]], 63 | '1' => [["⠈","⣯","⡇","⠀"],["⠠","⠿","⠧","⠀"]], 64 | '\'' => [["⠀","⣯","⠇","⠀"],["⠀","⠀","⠀","⠀"]], 65 | 'N' => [["⣯","⡏","⣯","⡆"],["⠿","⠇","⠿","⠇"]], 66 | '%' => [["⠛","⣸","⡽","⠃"],["⠾","⠏","⠰","⠆"]], 67 | 'C' => [["⣮","⡏","⠛","⠃"],["⠿","⠧","⠶","⠆"]], 68 | 'z' => [["⠒","⣲","⡲","⠂"],["⠾","⠯","⠶","⠆"]], 69 | 'V' => [["⣯","⡇","⣯","⡇"],["⠙","⠷","⠟","⠁"]], 70 | '.' => [["⠀","⠀","⠀","⠀"],["⠀","⠯","⠇","⠀"]], 71 | 'v' => [["⣖","⡆","⣖","⡆"],["⠙","⠷","⠟","⠁"]], 72 | 'r' => [["⣖","⡖","⠶","⠆"],["⠿","⠇","⠀","⠀"]], 73 | 'B' => [["⣯","⣏","⣯","⠇"],["⠿","⠧","⠿","⠇"]], 74 | 'a' => [["⣔","⡖","⣖","⡆"],["⠿","⠧","⠻","⠇"]], 75 | ' ' => [["⠀","⠀","⠀","⠀"],["⠀","⠀","⠀","⠀"]], 76 | 'h' => [["⣯","⡗","⣖","⡆"],["⠿","⠇","⠿","⠇"]], 77 | '=' => [["⠠","⠤","⠤","⠄"],["⠈","⠉","⠉","⠁"]], 78 | '#' => [["⢴","⢴","⣴","⠄"],["⠺","⠻","⠺","⠂"]], 79 | '/' => [["⠀","⣸","⡽","⠃"],["⠾","⠏","⠀","⠀"]], 80 | ']' => [["⠈","⢹","⣽","⠀"],["⠠","⠼","⠿","⠀"]], 81 | 'P' => [["⣯","⡏","⣯","⡆"],["⠿","⠏","⠉","⠁"]], 82 | '6' => [["⣯","⡯","⡭","⡀"],["⠿","⠧","⠿","⠇"]], 83 | 'info' => ' Reflections Dark font ','!' => [["⠀","⣯","⡇","⠀"],["⠀","⠭","⠅","⠀"]], 84 | '[' => [["⢸","⣽","⠉","⠀"],["⠸","⠿","⠤","⠀"]], 85 | "©" => [["⡎","⡩","⡭","⢱"],["⢇","⣛","⣓","⡸"]], 86 | '{' => [["⣀","⣯","⡏","⠁"],["⠀","⠿","⠧","⠄"]], 87 | '>' => [["⠘","⢵","⣄","⠀"],["⠰","⠟","⠁","⠀"]], 88 | 'L' => [["⣯","⡇","⠀","⠀"],["⠿","⠧","⠶","⠆"]], 89 | 'S' => [["⣯","⣏","⣛","⡃"],["⠶","⠦","⠷","⠇"]], 90 | 'g' => [["⣖","⡖","⣖","⡆"],["⣙","⣓","⣿","⡇"]], 91 | 'q' => [["⣖","⡖","⣖","⡆"],["⠙","⠓","⣿","⡇"]], 92 | '*' => [["⢀","⡤","⣄","⠀"],["⠐","⠿","⠗","⠀"]], 93 | 'b' => [["⣯","⡗","⣖","⡆"],["⠿","⠧","⠿","⠃"]], 94 | 'l' => [["⠀","⢹","⣽","⠀"],["⠀","⠸","⠿","⠀"]], 95 | '|' => [["⠀","⣯","⡇","⠀"],["⠀","⠿","⠇","⠀"]], 96 | 'Y' => [["⢯","⣧","⣯","⠇"],["⠀","⠿","⠇","⠀"]]} 97 | -------------------------------------------------------------------------------- /examples/fonts/Reflections.grf: -------------------------------------------------------------------------------- 1 | {'O' => [["⣎","⡏","⣏","⡆"],["⠻","⠧","⠿","⠃"]], 2 | '3' => [["⠊","⣋","⣏","⠆"],["⠲","⠦","⠿","⠃"]], 3 | 'e' => [["⡔","⣖","⣖","⡄"],["⠻","⠧","⠶","⠂"]], 4 | 'Q' => [["⣎","⡏","⣏","⡆"],["⠻","⠧","⣿","⡇"]], 5 | 'x' => [["⢖","⣆","⣖","⠆"],["⠿","⠇","⠿","⠇"]], 6 | ',' => [["⠀","⠀","⠀","⠀"],["⠀","⣴","⠕","⠀"]], 7 | '8' => [["⢎","⣏","⣏","⠆"],["⠻","⠧","⠿","⠃"]], 8 | ')' => [["⠈","⢹","⣱","⠀"],["⠠","⠼","⠟","⠀"]], 9 | '4' => [["⢀","⢴","⣹","⠀"],["⠉","⠹","⠿","⠁"]], 10 | 'w' => [["⡆","⣆","⡆","⡆"],["⠻","⠗","⠿","⠃"]], 11 | '7' => [["⠋","⢋","⣏","⠇"],["⠀","⠾","⠏","⠀"]], 12 | ';' => [["⠀","⢠","⢤","⠀"],["⠀","⣴","⠕","⠀"]], 13 | 'H' => [["⣏","⣇","⣏","⡇"],["⠿","⠇","⠿","⠇"]], 14 | 'A' => [["⡎","⣏","⡏","⡆"],["⠿","⠇","⠿","⠇"]], 15 | 'd' => [["⡔","⡖","⡏","⡇"],["⠻","⠧","⠿","⠇"]], 16 | 'u' => [["⡖","⡆","⡖","⡆"],["⠻","⠧","⠿","⠇"]], 17 | 'm' => [["⡖","⡦","⡖","⡄"],["⠿","⠟","⠿","⠇"]], 18 | 'G' => [["⣎","⡏","⣋","⡃"],["⠻","⠧","⠿","⠇"]], 19 | 'J' => [["⠀","⠀","⡏","⡇"],["⠲","⠦","⠿","⠃"]], 20 | '}' => [["⠉","⡏","⣆","⡀"],["⠤","⠿","⠃","⠀"]], 21 | 'c' => [["⡔","⡖","⠖","⠄"],["⠻","⠧","⠶","⠂"]], 22 | '?' => [["⠊","⣋","⣏","⡆"],["⠀","⠶","⠆","⠀"]], 23 | 'I' => [["⠀","⡏","⡇","⠀"],["⠀","⠿","⠇","⠀"]], 24 | 'f' => [["⣎","⣏","⠋","⠂"],["⠿","⠇","⠀","⠀"]], 25 | '"' => [["⠮","⠆","⠮","⠆"],["⠀","⠀","⠀","⠀"]], 26 | 'o' => [["⡔","⡖","⡖","⡄"],["⠻","⠧","⠿","⠃"]], 27 | 'U' => [["⣏","⡇","⣏","⡇"],["⠻","⠧","⠿","⠃"]], 28 | "£" => [["⣎","⣏","⡋","⠂"],["⠿","⠧","⠶","⠆"]], 29 | '&' => [["⢎","⣏","⣋","⡂"],["⠻","⠧","⠿","⠇"]], 30 | 'k' => [["⡏","⡇","⡤","⡄"],["⠿","⠏","⠿","⠆"]], 31 | '5' => [["⣏","⣏","⣋","⠃"],["⠲","⠦","⠿","⠃"]], 32 | 'j' => [["⠀","⠀","⡮","⡆"],["⢤","⣄","⣷","⠇"]], 33 | 's' => [["⢔","⣖","⠖","⠄"],["⠲","⠮","⠿","⠂"]], 34 | 'K' => [["⡏","⡧","⡫","⠃"],["⠿","⠏","⠿","⠆"]], 35 | '~' => [["⣔","⡦","⣖","⠆"],["⠀","⠀","⠀","⠀"]], 36 | 'E' => [["⣎","⣏","⡋","⠃"],["⠻","⠧","⠶","⠆"]], 37 | '$' => [["⠯","⣏","⡏","⠃"],["⠦","⠿","⠯","⠇"]], 38 | '@' => [["⣎","⣏","⣏","⡆"],["⠻","⠧","⠭","⠅"]], 39 | '_' => [["⠀","⠀","⠀","⠀"],["⣀","⣀","⣀","⣀"]], 40 | '<' => [["⢀","⢔","⠝","⠀"],["⠀","⠙","⠷","⠀"]], 41 | 'W' => [["⣇","⡗","⣇","⡇"],["⠻","⠧","⠿","⠃"]], 42 | 'D' => [["⣏","⡏","⣏","⡆"],["⠿","⠧","⠿","⠃"]], 43 | 'y' => [["⡖","⡆","⡖","⡆"],["⣙","⣓","⣿","⠇"]], 44 | 'Z' => [["⠋","⣫","⡫","⠃"],["⠾","⠯","⠶","⠆"]], 45 | 'R' => [["⣏","⣯","⡫","⠃"],["⠿","⠏","⠻","⠆"]], 46 | 'M' => [["⡏","⡗","⡏","⡇"],["⠿","⠗","⠿","⠇"]], 47 | ' ' => [["⠀","⠀","⠀","⠀"],["⠀","⠀","⠀","⠀"]], 48 | 'B' => [["⡏","⣏","⣏","⠆"],["⠿","⠧","⠿","⠃"]], 49 | 'a' => [["⡔","⡖","⡔","⡆"],["⠻","⠧","⠻","⠇"]], 50 | 'r' => [["⡖","⡖","⠖","⠄"],["⠿","⠇","⠀","⠀"]], 51 | 'v' => [["⡖","⡆","⡖","⡆"],["⠙","⠧","⠟","⠁"]], 52 | 'V' => [["⣏","⡇","⣏","⡇"],["⠙","⠧","⠟","⠁"]], 53 | 'z' => [["⠖","⡲","⡲","⠂"],["⠾","⠯","⠶","⠆"]], 54 | '.' => [["⠀","⠀","⠀","⠀"],["⠀","⠾","⠆","⠀"]], 55 | 'C' => [["⣎","⡏","⠋","⠂"],["⠻","⠧","⠶","⠂"]], 56 | '%' => [["⠮","⢆","⢔","⠆"],["⠰","⠟","⠱","⠷"]], 57 | 'N' => [["⣏","⡏","⣏","⡆"],["⠿","⠇","⠿","⠇"]], 58 | '1' => [["⠐","⣏","⡇","⠀"],["⠠","⠿","⠧","⠀"]], 59 | '\'' => [["⢠","⡪","⠂","⠀"],["⠀","⠀","⠀","⠀"]], 60 | '(' => [["⢰","⣹","⠉","⠀"],["⠘","⠿","⠤","⠀"]], 61 | 'X' => [["⢏","⣇","⣏","⠇"],["⠿","⠇","⠿","⠇"]], 62 | ':' => [["⠀","⡤","⡄","⠀"],["⠀","⠾","⠆","⠀"]], 63 | '\\' => [["⠫","⣢","⡀","⠀"],["⠀","⠈","⠻","⠆"]], 64 | '-' => [["⢀","⣀","⣀","⡀"],["⠀","⠀","⠀","⠀"]], 65 | 'F' => [["⣎","⣏","⡋","⠃"],["⠿","⠇","⠀","⠀"]], 66 | '0' => [["⣎","⡏","⣏","⡆"],["⠻","⠧","⠿","⠃"]], 67 | '9' => [["⢎","⣏","⣏","⡆"],["⠲","⠦","⠿","⠃"]], 68 | '+' => [["⠀","⡤","⡄","⠀"],["⠉","⠿","⠏","⠁"]], 69 | 'n' => [["⡖","⡖","⡖","⡄"],["⠿","⠇","⠿","⠇"]], 70 | 't' => [["⡏","⡧","⠀","⠀"],["⠻","⠧","⠶","⠂"]], 71 | '^' => [["⡠","⣞","⡦","⡀"],["⠀","⠿","⠇","⠀"]], 72 | 'i' => [["⠀","⡮","⡆","⠀"],["⠀","⠿","⠇","⠀"]], 73 | 'T' => [["⠋","⣏","⡏","⠃"],["⠀","⠿","⠇","⠀"]], 74 | 'p' => [["⡖","⡖","⡖","⡄"],["⣿","⡧","⠿","⠃"]], 75 | '2' => [["⠊","⣫","⡫","⠂"],["⠾","⠯","⠶","⠆"]], 76 | 'Y' => [["⢏","⣇","⣏","⠇"],["⠀","⠿","⠇","⠀"]], 77 | '|' => [["⠀","⡏","⡇","⠀"],["⠀","⠿","⠇","⠀"]], 78 | 'l' => [["⠀","⢹","⣹","⠀"],["⠀","⠸","⠿","⠀"]], 79 | '*' => [["⢀","⡤","⣄","⠀"],["⠐","⠿","⠗","⠀"]], 80 | 'b' => [["⡏","⡗","⡖","⡄"],["⠿","⠧","⠿","⠃"]], 81 | 'q' => [["⡔","⡖","⡖","⡆"],["⠻","⠧","⣿","⡇"]], 82 | 'g' => [["⡔","⡖","⡖","⡆"],["⢬","⣍","⣿","⠇"]], 83 | 'L' => [["⡏","⡇","⠀","⠀"],["⠿","⠧","⠶","⠆"]], 84 | 'S' => [["⠯","⣏","⡋","⠃"],["⠶","⠦","⠿","⠇"]], 85 | "©" => [["⡎","⡩","⡭","⢱"],["⢇","⣙","⣓","⡸"]], 86 | '{' => [["⣀","⡎","⡏","⠁"],["⠀","⠻","⠧","⠄"]], 87 | '>' => [["⠘","⢕","⢄","⠀"],["⠰","⠟","⠁","⠀"]], 88 | '[' => [["⠀","⣏","⡏","⠀"],["⠀","⠿","⠧","⠀"]], 89 | '!' => [["⠀","⢸","⣹","⠀"],["⠀","⠰","⠷","⠀"]], 90 | 'info' => ' Reflections font ','6' => [["⣎","⣏","⣋","⠂"],["⠻","⠧","⠿","⠃"]], 91 | ']' => [["⠀","⢹","⣹","⠀"],["⠀","⠼","⠿","⠀"]], 92 | 'P' => [["⣏","⡏","⣏","⡆"],["⠿","⠏","⠉","⠀"]], 93 | '/' => [["⠀","⣠","⡪","⠃"],["⠾","⠋","⠀","⠀"]], 94 | '#' => [["⢴","⢴","⣴","⠄"],["⠺","⠻","⠺","⠂"]], 95 | '=' => [["⠠","⠤","⠤","⠄"],["⠈","⠉","⠉","⠁"]], 96 | 'h' => [["⡏","⡗","⡖","⡄"],["⠿","⠇","⠿","⠇"]]} 97 | -------------------------------------------------------------------------------- /examples/fonts/Tentacle Bold.grf: -------------------------------------------------------------------------------- 1 | {'0' => [["⢸","⣿","⠉","⡇"],["⠸","⠿","⠤","⠇"]], 2 | 'info' => ' Tentacle Bold font by DamienG https://damieng.com','@' => [["⣿","⡏","⡭","⡇"],["⠿","⠧","⠭","⠁"]], 3 | 'I' => [["⠉","⣿","⡏","⠁"],["⠤","⠿","⠧","⠄"]], 4 | 'h' => [["⢹","⣿","⠤","⡄"],["⠼","⠿","⠄","⠧"]], 5 | 'q' => [["⡤","⢤","⣤","⠄"],["⠓","⣹","⣿","⡀"]], 6 | 'C' => [["⣿","⡏","⠑","⠃"],["⠿","⠧","⠤","⠆"]], 7 | '7' => [["⠘","⣉","⣿","⡇"],["⠀","⠀","⠿","⠇"]], 8 | 'j' => [["⠀","⢬","⣭","⠀"],["⢀","⣸","⣿","⠀"]], 9 | '<' => [["⢀","⣴","⠟","⠁"],["⠀","⠙","⠷","⠄"]], 10 | 'U' => [["⢹","⠁","⣿","⡏"],["⠸","⠤","⠿","⠇"]], 11 | 'o' => [["⡤","⠤","⣤","⡄"],["⠧","⠤","⠿","⠇"]], 12 | 'n' => [["⢤","⣤","⠤","⡄"],["⠼","⠿","⠠","⠧"]], 13 | '6' => [["⢸","⣿","⣙","⡀"],["⠸","⠿","⠤","⠇"]], 14 | 'L' => [["⢹","⣿","⠁","⠀"],["⠼","⠿","⠤","⠆"]], 15 | 'P' => [["⢹","⣿","⢉","⡇"],["⠼","⠿","⠄","⠀"]], 16 | '|' => [["⠀","⣿","⡇","⠀"],["⠀","⠿","⠇","⠀"]], 17 | 'O' => [["⢸","⠉","⣿","⡇"],["⠸","⠤","⠿","⠇"]], 18 | '+' => [["⣀","⣶","⣆","⡀"],["⠀","⠛","⠃","⠀"]], 19 | 'd' => [["⡤","⠬","⣿","⡇"],["⠧","⠔","⠿","⠧"]], 20 | ' ' => [["⠀","⠀","⠀","⠀"],["⠀","⠀","⠀","⠀"]], 21 | ')' => [["⠀","⢻","⣦","⠀"],["⠀","⠾","⠋","⠀"]], 22 | '~' => [["⠛","⠙","⠚","⠃"],["⠀","⠀","⠀","⠀"]], 23 | 'V' => [["⠹","⣁","⣿","⠏"],["⠀","⠹","⠏","⠀"]], 24 | 'E' => [["⢹","⣿","⣉","⠃"],["⠼","⠿","⠤","⠆"]], 25 | '*' => [["⣀","⣴","⣄","⡀"],["⠘","⠋","⠛","⠀"]], 26 | 'D' => [["⢹","⠉","⣿","⡇"],["⠼","⠤","⠿","⠇"]], 27 | '4' => [["⣇","⣸","⣿","⡀"],["⠀","⠼","⠿","⠄"]], 28 | 'Y' => [["⢹","⣀","⣿","⡏"],["⠠","⠤","⠿","⠇"]], 29 | 'v' => [["⢤","⣤","⠠","⡤"],["⠸","⠿","⠊","⠀"]], 30 | '^' => [["⡠","⢾","⣦","⡀"],["⠀","⠀","⠀","⠀"]], 31 | '"' => [["⠀","⠇","⠿","⠇"],["⠀","⠀","⠀","⠀"]], 32 | '5' => [["⢸","⣉","⣙","⡀"],["⠰","⠤","⠿","⠇"]], 33 | '{' => [["⣀","⣿","⠏","⠁"],["⠀","⠿","⠧","⠄"]], 34 | '_' => [["⠀","⠀","⠀","⠀"],["⣀","⣀","⣀","⣀"]], 35 | 'K' => [["⢹","⣿","⡡","⠋"],["⠼","⠿","⠌","⠦"]], 36 | 'X' => [["⠹","⣿","⡸","⠁"],["⠼","⠿","⠸","⠄"]], 37 | 'y' => [["⢤","⠄","⣤","⡤"],["⣘","⣒","⣿","⡇"]], 38 | 'W' => [["⢹","⣡","⣌","⡏"],["⠸","⠟","⠻","⠇"]], 39 | '!' => [["⠀","⣿","⡇","⠀"],["⠀","⠭","⠅","⠀"]], 40 | '=' => [["⠠","⠤","⠤","⠀"],["⠈","⠉","⠉","⠀"]], 41 | 'p' => [["⢤","⣤","⠤","⡄"],["⣸","⣿","⡑","⠃"]], 42 | '}' => [["⠉","⢿","⣇","⡀"],["⠤","⠿","⠇","⠀"]], 43 | '[' => [["⢸","⣿","⠉","⠀"],["⠸","⠿","⠤","⠀"]], 44 | '?' => [["⠘","⣉","⣿","⡇"],["⠀","⠥","⠀","⠀"]], 45 | '8' => [["⠸","⣉","⣿","⠇"],["⠸","⠤","⠿","⠇"]], 46 | ';' => [["⠀","⠰","⠶","⠀"],["⠀","⣰","⣶","⠀"]], 47 | '\'' => [["⠀","⠼","⠿","⠀"],["⠀","⠀","⠀","⠀"]], 48 | 'M' => [["⡏","⡏","⣿","⡇"],["⠧","⠡","⠿","⠇"]], 49 | '(' => [["⢠","⣾","⠃","⠀"],["⠈","⠻","⠆","⠀"]], 50 | 'J' => [["⠀","⢹","⣿","⠁"],["⢀","⣸","⣿","⠀"]], 51 | 'i' => [["⠠","⣭","⡅","⠀"],["⠀","⠿","⠧","⠀"]], 52 | '%' => [["⠯","⢟","⠔","⠁"],["⠔","⠱","⠽","⠇"]], 53 | '#' => [["⢴","⢴","⣶","⠄"],["⠙","⠙","⠛","⠁"]], 54 | 'c' => [["⣤","⡤","⢤","⠀"],["⠿","⠧","⠤","⠆"]], 55 | '9' => [["⢸","⣉","⣿","⡇"],["⠀","⠦","⠿","⠇"]], 56 | 'T' => [["⠋","⣿","⡏","⠃"],["⠠","⠿","⠧","⠀"]], 57 | "©" => [["⡔","⣭","⡭","⢢"],["⠣","⣛","⣓","⠜"]], 58 | 'Q' => [["⣿","⡏","⠉","⡇"],["⠿","⠧","⢧","⠇"]], 59 | '1' => [["⠀","⢹","⣿","⠀"],["⠀","⠸","⠿","⠀"]], 60 | '.' => [["⠀","⠀","⠀","⠀"],["⠀","⠰","⠆","⠀"]], 61 | 'R' => [["⢹","⣿","⣉","⡇"],["⠼","⠿","⠈","⠦"]], 62 | '2' => [["⢀","⣋","⣿","⡇"],["⠸","⠤","⠤","⠆"]], 63 | 'm' => [["⡤","⡤","⣤","⡄"],["⠇","⠇","⠿","⠇"]], 64 | '>' => [["⠈","⠻","⣦","⡀"],["⠠","⠾","⠋","⠀"]], 65 | 'H' => [["⢹","⣿","⣈","⡏"],["⠼","⠿","⠠","⠧"]], 66 | 'a' => [["⠠","⢤","⣤","⠀"],["⠯","⠽","⠿","⠄"]], 67 | 'r' => [["⢤","⣄","⠤","⡄"],["⠼","⠿","⠄","⠀"]], 68 | ':' => [["⠀","⠶","⠆","⠀"],["⠀","⠶","⠆","⠀"]], 69 | 'Z' => [["⠋","⣩","⡿","⠃"],["⠾","⠿","⠤","⠆"]], 70 | '-' => [["⢀","⣀","⣀","⠀"],["⠀","⠀","⠀","⠀"]], 71 | '/' => [["⠀","⢀","⣴","⠟"],["⠴","⠟","⠁","⠀"]], 72 | 'x' => [["⣤","⡄","⢤","⡄"],["⠾","⠯","⠹","⠆"]], 73 | 'z' => [["⡤","⢤","⣤","⠄"],["⠴","⠿","⠥","⠆"]], 74 | '3' => [["⠀","⣋","⣿","⠇"],["⠰","⠤","⠿","⠇"]], 75 | 'u' => [["⢤","⠠","⣤","⡄"],["⠸","⠔","⠿","⠧"]], 76 | 'F' => [["⢹","⣿","⣉","⠃"],["⠼","⠿","⠄","⠀"]], 77 | 'S' => [["⢸","⣉","⣑","⡃"],["⠦","⠤","⠿","⠇"]], 78 | ']' => [["⠈","⢹","⣿","⠀"],["⠠","⠼","⠿","⠀"]], 79 | 'N' => [["⢹","⠉","⣿","⡇"],["⠼","⠠","⠿","⠧"]], 80 | 'w' => [["⢤","⢄","⡠","⡤"],["⠸","⠟","⠻","⠇"]], 81 | 'l' => [["⠈","⣿","⡇","⠀"],["⠀","⠿","⠧","⠀"]], 82 | '$' => [["⢰","⣶","⣚","⡂"],["⠐","⠖","⠛","⠃"]], 83 | 'f' => [["⢀","⣿","⣏","⡃"],["⠠","⠿","⠧","⠀"]], 84 | 'k' => [["⢹","⣿","⢀","⠤"],["⠼","⠿","⠑","⠤"]], 85 | 's' => [["⡤","⠤","⠤","⠀"],["⠭","⠭","⠿","⠇"]], 86 | 'G' => [["⣿","⡏","⠚","⠀"],["⠿","⠧","⠽","⠁"]], 87 | 'e' => [["⣤","⡤","⠤","⡄"],["⠿","⠯","⠭","⠅"]], 88 | 'A' => [["⢸","⣉","⣿","⡇"],["⠼","⠄","⠿","⠧"]], 89 | 't' => [["⢼","⣿","⠄","⠀"],["⠸","⠿","⠤","⠆"]], 90 | 'b' => [["⢹","⣿","⡠","⡄"],["⠸","⠿","⠤","⠇"]], 91 | '&' => [["⢿","⣏","⠇","⡀"],["⠿","⠧","⠽","⠥"]], 92 | ',' => [["⠀","⠀","⠀","⠀"],["⠀","⣰","⣶","⠀"]], 93 | '\\' => [["⠻","⣦","⡀","⠀"],["⠀","⠈","⠻","⠦"]], 94 | 'g' => [["⡤","⠤","⣠","⡤"],["⣓","⣊","⣿","⡇"]], 95 | 'B' => [["⢹","⢉","⣿","⠇"],["⠼","⠤","⠿","⠇"]], 96 | "£" => [["⣸","⣿","⡙","⠀"],["⠼","⠿","⠤","⠆"]]} 97 | -------------------------------------------------------------------------------- /examples/fonts/Tentacle.grf: -------------------------------------------------------------------------------- 1 | {'e' => [["⢠","⡤","⠤","⡄"],["⠸","⠯","⠭","⠅"]], 2 | '~' => [["⠘","⠙","⠚","⠀"],["⠀","⠀","⠀","⠀"]], 3 | '<' => [["⢀","⡴","⠋","⠀"],["⠀","⠙","⠦","⠀"]], 4 | '\\' => [["⠳","⣄","⠀","⠀"],["⠀","⠈","⠳","⠄"]], 5 | '/' => [["⠀","⢀","⡴","⠃"],["⠴","⠋","⠀","⠀"]], 6 | '1' => [["⠀","⢹","⡇","⠀"],["⠀","⠸","⠇","⠀"]], 7 | '_' => [["⠀","⠀","⠀","⠀"],["⣀","⣀","⣀","⣀"]], 8 | 'y' => [["⢤","⠄","⣤","⠄"],["⣘","⣒","⣿","⠀"]], 9 | '^' => [["⡠","⠺","⣦","⡀"],["⠀","⠀","⠀","⠀"]], 10 | 'U' => [["⢹","⠁","⢸","⡏"],["⠸","⠤","⠼","⠇"]], 11 | 'H' => [["⢹","⣇","⣹","⠁"],["⠼","⠇","⠼","⠄"]], 12 | 'M' => [["⢹","⢹","⢹","⡏"],["⠼","⠌","⠼","⠧"]], 13 | '!' => [["⠀","⢸","⡇","⠀"],["⠀","⠨","⠅","⠀"]], 14 | 'L' => [["⢹","⡏","⠀","⠀"],["⠼","⠧","⠴","⠀"]], 15 | ')' => [["⠀","⢳","⡄","⠀"],["⠀","⠞","⠁","⠀"]], 16 | 'q' => [["⢠","⠤","⢤","⡤"],["⠘","⠒","⣹","⣇"]], 17 | 'N' => [["⢹","⠉","⣿","⠀"],["⠼","⠠","⠿","⠄"]], 18 | 'W' => [["⢹","⢡","⡌","⡏"],["⠸","⠟","⠻","⠇"]], 19 | 'c' => [["⢠","⡤","⢤","⠀"],["⠸","⠧","⠤","⠆"]], 20 | '5' => [["⢸","⣉","⣙","⡀"],["⠰","⠤","⠼","⠇"]], 21 | 'B' => [["⢹","⢉","⣹","⠇"],["⠼","⠤","⠼","⠇"]], 22 | ',' => [["⠀","⠀","⠀","⠀"],["⠀","⣰","⡆","⠀"]], 23 | 'u' => [["⢤","⠀","⢤","⡄"],["⠸","⠤","⠺","⠧"]], 24 | 'D' => [["⢹","⠉","⢹","⡇"],["⠼","⠤","⠼","⠇"]], 25 | '(' => [["⠀","⣴","⠃","⠀"],["⠀","⠙","⠆","⠀"]], 26 | '"' => [["⠀","⠇","⠿","⠀"],["⠀","⠀","⠀","⠀"]], 27 | 't' => [["⠠","⣿","⠤","⠄"],["⠀","⠿","⠤","⠆"]], 28 | 's' => [["⢠","⠤","⠤","⠀"],["⠨","⠭","⠽","⠇"]], 29 | 'v' => [["⢤","⡄","⢤","⠄"],["⠸","⠗","⠁","⠀"]], 30 | ' ' => [["⠀","⠀","⠀","⠀"],["⠀","⠀","⠀","⠀"]], 31 | 'l' => [["⠀","⢹","⡇","⠀"],["⠀","⠸","⠧","⠀"]], 32 | '#' => [["⠠","⡦","⣶","⠄"],["⠈","⠋","⠛","⠁"]], 33 | ']' => [["⠀","⠉","⣿","⠀"],["⠀","⠤","⠿","⠀"]], 34 | '}' => [["⠈","⠹","⣇","⡀"],["⠠","⠼","⠇","⠀"]], 35 | 'p' => [["⢤","⡤","⠤","⡄"],["⣸","⣏","⠒","⠃"]], 36 | '6' => [["⢸","⣏","⣙","⡀"],["⠸","⠧","⠤","⠇"]], 37 | 'g' => [["⢠","⠤","⢄","⡤"],["⢘","⣒","⣹","⡇"]], 38 | 'i' => [["⠀","⢬","⡅","⠀"],["⠀","⠸","⠧","⠀"]], 39 | 'info' => ' Tentacle font by DamienG https://damieng.com','d' => [["⢠","⠤","⢽","⡇"],["⠸","⠤","⠺","⠧"]], 40 | 'n' => [["⢤","⡤","⢤","⠀"],["⠼","⠇","⠼","⠄"]], 41 | ':' => [["⠀","⠰","⠆","⠀"],["⠀","⠰","⠆","⠀"]], 42 | '-' => [["⢀","⣀","⣀","⠀"],["⠀","⠀","⠀","⠀"]], 43 | 'R' => [["⢹","⣏","⣉","⡇"],["⠼","⠇","⠵","⠄"]], 44 | 'T' => [["⠘","⢹","⡏","⠃"],["⠀","⠼","⠧","⠀"]], 45 | '?' => [["⠘","⣉","⣹","⡇"],["⠀","⠥","⠀","⠀"]], 46 | '[' => [["⠀","⣿","⠉","⠀"],["⠀","⠿","⠤","⠀"]], 47 | 'J' => [["⠀","⢹","⡏","⠀"],["⢀","⣸","⡇","⠀"]], 48 | '3' => [["⠀","⢋","⣹","⠇"],["⠰","⠤","⠼","⠇"]], 49 | 'K' => [["⢹","⣏","⠜","⠁"],["⠼","⠧","⠱","⠄"]], 50 | 'S' => [["⢸","⣉","⣑","⡃"],["⠦","⠤","⠼","⠇"]], 51 | 'V' => [["⠹","⣁","⡿","⠁"],["⠀","⠹","⠁","⠀"]], 52 | 'b' => [["⢹","⣇","⠤","⡄"],["⠸","⠧","⠤","⠇"]], 53 | '4' => [["⢸","⣀","⣿","⡀"],["⠀","⠠","⠿","⠄"]], 54 | 'Y' => [["⢹","⣀","⣸","⡏"],["⠠","⠤","⠼","⠇"]], 55 | ';' => [["⠀","⠰","⠆","⠀"],["⠀","⣰","⡆","⠀"]], 56 | '8' => [["⠸","⣉","⣹","⠇"],["⠸","⠤","⠼","⠇"]], 57 | 'F' => [["⢹","⣏","⣉","⠃"],["⠼","⠧","⠄","⠀"]], 58 | '\'' => [["⠀","⠼","⠇","⠀"],["⠀","⠀","⠀","⠀"]], 59 | '+' => [["⢀","⣰","⣆","⡀"],["⠀","⠘","⠃","⠀"]], 60 | 'f' => [["⢀","⣿","⣙","⠀"],["⠠","⠿","⠄","⠀"]], 61 | '|' => [["⠀","⢸","⡇","⠀"],["⠀","⠸","⠇","⠀"]], 62 | '2' => [["⢀","⣋","⣹","⡇"],["⠸","⠤","⠤","⠆"]], 63 | '&' => [["⢿","⡹","⢀","⠀"],["⠿","⠬","⠯","⠄"]], 64 | 'h' => [["⢹","⡧","⢤","⠀"],["⠼","⠧","⠸","⠄"]], 65 | '7' => [["⠘","⢉","⣹","⡇"],["⠀","⠀","⠸","⠇"]], 66 | "£" => [["⣸","⣏","⡙","⠀"],["⠼","⠧","⠤","⠆"]], 67 | '{' => [["⢀","⣸","⠏","⠁"],["⠀","⠸","⠧","⠄"]], 68 | '@' => [["⢸","⡏","⡭","⡇"],["⠸","⠧","⠭","⠁"]], 69 | 'Q' => [["⣿","⠉","⠉","⡇"],["⠿","⠤","⢧","⠇"]], 70 | 'E' => [["⢹","⣏","⣉","⠃"],["⠼","⠧","⠤","⠆"]], 71 | 'G' => [["⣿","⠉","⠚","⠀"],["⠿","⠤","⠽","⠁"]], 72 | "©" => [["⡔","⣭","⢭","⢢"],["⠣","⣛","⣒","⠜"]], 73 | '9' => [["⢸","⣉","⢽","⡇"],["⠀","⠤","⠞","⠁"]], 74 | 'w' => [["⢤","⠄","⢤","⠄"],["⠸","⠞","⠾","⠀"]], 75 | 'x' => [["⢤","⡄","⢠","⠄"],["⠴","⠏","⠱","⠄"]], 76 | '>' => [["⠈","⠳","⣄","⠀"],["⠠","⠞","⠁","⠀"]], 77 | 'm' => [["⢤","⢤","⢤","⡄"],["⠼","⠸","⠸","⠧"]], 78 | 'r' => [["⠠","⣄","⠤","⡄"],["⠠","⠿","⠄","⠀"]], 79 | '*' => [["⢀","⣰","⣀","⠀"],["⠀","⠋","⠃","⠀"]], 80 | 'O' => [["⢸","⠉","⢹","⡇"],["⠸","⠤","⠼","⠇"]], 81 | '0' => [["⢸","⡏","⠉","⡇"],["⠸","⠧","⠤","⠇"]], 82 | '=' => [["⠠","⠤","⠤","⠀"],["⠈","⠉","⠉","⠀"]], 83 | '.' => [["⠀","⠀","⠀","⠀"],["⠀","⠰","⠆","⠀"]], 84 | 'I' => [["⠈","⢹","⡏","⠁"],["⠠","⠼","⠧","⠄"]], 85 | 'z' => [["⢠","⠤","⣤","⠄"],["⠠","⠾","⠥","⠆"]], 86 | 'j' => [["⠀","⠠","⣭","⠀"],["⠀","⣀","⣿","⠀"]], 87 | 'A' => [["⢸","⣉","⣹","⡇"],["⠼","⠄","⠼","⠧"]], 88 | 'C' => [["⢸","⡏","⠑","⠃"],["⠸","⠧","⠤","⠆"]], 89 | 'k' => [["⢹","⡇","⡠","⠄"],["⠼","⠏","⠢","⠄"]], 90 | 'a' => [["⠀","⠤","⣤","⠀"],["⠸","⠭","⠿","⠄"]], 91 | 'Z' => [["⠘","⢉","⡽","⠃"],["⠰","⠿","⠤","⠆"]], 92 | 'X' => [["⠹","⣇","⡸","⠁"],["⠼","⠇","⠸","⠄"]], 93 | 'P' => [["⢹","⡏","⣉","⡇"],["⠼","⠧","⠀","⠀"]], 94 | 'o' => [["⢠","⠤","⢤","⡄"],["⠸","⠤","⠼","⠇"]], 95 | '%' => [["⠯","⢟","⠔","⠁"],["⠔","⠱","⠽","⠇"]], 96 | '$' => [["⠀","⣶","⣚","⠂"],["⠐","⠖","⠛","⠀"]]} 97 | -------------------------------------------------------------------------------- /examples/fonts/ZX Times.grf: -------------------------------------------------------------------------------- 1 | {'3' => [["⠐","⢋","⣹","⠆"],["⠐","⠦","⠼","⠃"]], 2 | 'o' => [["⢀","⡤","⢤","⡀"],["⠘","⠧","⠼","⠃"]], 3 | 'b' => [["⢹","⣇","⢤","⡀"],["⠸","⠧","⠼","⠃"]], 4 | '#' => [["⠠","⡴","⡴","⠄"],["⠲","⠳","⠓","⠀"]], 5 | '\'' => [["⠀","⠸","⠃","⠀"],["⠀","⠀","⠀","⠀"]], 6 | 'r' => [["⢤","⣄","⢤","⡀"],["⠼","⠧","⠀","⠀"]], 7 | 'M' => [["⢹","⢿","⡸","⡏"],["⠼","⠜","⠣","⠧"]], 8 | '{' => [["⢀","⣰","⠏","⠀"],["⠀","⠸","⣇","⠀"]], 9 | 'L' => [["⢹","⡏","⠀","⠀"],["⠼","⠧","⠤","⠂"]], 10 | ':' => [["⠀","⢠","⡄","⠀"],["⠀","⠰","⠆","⠀"]], 11 | '?' => [["⠐","⠋","⣹","⠆"],["⠀","⠬","⠀","⠀"]], 12 | '|' => [["⠀","⢸","⡇","⠀"],["⠀","⢸","⡇","⠀"]], 13 | '%' => [["⠺","⢽","⡢","⠃"],["⠰","⠑","⠯","⠗"]], 14 | '5' => [["⠰","⠯","⣉","⠁"],["⠐","⠤","⠼","⠃"]], 15 | 'q' => [["⣠","⢄","⣤","⠄"],["⠙","⢒","⣿","⡀"]], 16 | '1' => [["⠀","⢺","⡇","⠀"],["⠀","⠼","⠧","⠀"]], 17 | 'V' => [["⠹","⣇","⢈","⠏"],["⠀","⠹","⠎","⠀"]], 18 | "©" => [["⡔","⣩","⢭","⢢"],["⠣","⣙","⣒","⠜"]], 19 | '@' => [["⣴","⡩","⢵","⡄"],["⠻","⣑","⣚","⠃"]], 20 | 'P' => [["⢹","⡏","⢹","⠆"],["⠼","⠯","⠁","⠀"]], 21 | '/' => [["⠀","⢀","⡾","⠁"],["⢀","⡾","⠁","⠀"]], 22 | '$' => [["⠰","⣯","⣏","⠃"],["⠰","⢼","⠽","⠃"]], 23 | '"' => [["⠸","⠃","⠟","⠀"],["⠀","⠀","⠀","⠀"]], 24 | '~' => [["⠴","⠲","⠴","⠂"],["⠀","⠀","⠀","⠀"]], 25 | 'J' => [["⠀","⠈","⣿","⠁"],["⠘","⠧","⠋","⠀"]], 26 | '4' => [["⢀","⠔","⣿","⠀"],["⠈","⠉","⠿","⠁"]], 27 | '-' => [["⢀","⣀","⣀","⠀"],["⠀","⠀","⠀","⠀"]], 28 | 'm' => [["⢤","⣤","⣠","⡀"],["⠼","⠇","⠇","⠧"]], 29 | 'y' => [["⢤","⡄","⠠","⡤"],["⢀","⣹","⠎","⠀"]], 30 | 'K' => [["⢹","⣇","⠝","⠁"],["⠼","⠏","⠷","⠄"]], 31 | 'E' => [["⢹","⣏","⡍","⠃"],["⠼","⠧","⠥","⠂"]], 32 | 'C' => [["⣴","⠋","⠑","⠃"],["⠙","⠦","⠤","⠂"]], 33 | ']' => [["⠀","⠉","⣿","⠀"],["⠀","⣀","⣿","⠀"]], 34 | 'X' => [["⠙","⣧","⡜","⠁"],["⠴","⠉","⠷","⠄"]], 35 | '}' => [["⠀","⠹","⣆","⡀"],["⠀","⣸","⠇","⠀"]], 36 | 'G' => [["⣴","⠋","⣚","⡀"],["⠙","⠦","⠜","⠀"]], 37 | 'u' => [["⢤","⡄","⢤","⡄"],["⠘","⠧","⠺","⠧"]], 38 | '(' => [["⠀","⣴","⠁","⠀"],["⠀","⠻","⡀","⠀"]], 39 | 'Z' => [["⠘","⢉","⡽","⠃"],["⠰","⠯","⠤","⠆"]], 40 | 'A' => [["⠀","⡜","⣧","⠀"],["⠼","⠍","⠽","⠧"]], 41 | 'l' => [["⠀","⢹","⡇","⠀"],["⠀","⠼","⠧","⠀"]], 42 | '=' => [["⠠","⠤","⠤","⠀"],["⠈","⠉","⠉","⠀"]], 43 | ';' => [["⠀","⢠","⡄","⠀"],["⠀","⢰","⠆","⠀"]], 44 | 'f' => [["⢀","⣾","⡙","⠂"],["⠠","⠿","⠄","⠀"]], 45 | '.' => [["⠀","⠀","⠀","⠀"],["⠀","⠰","⠆","⠀"]], 46 | 'T' => [["⠘","⢹","⡏","⠃"],["⠀","⠼","⠧","⠀"]], 47 | 'W' => [["⢹","⣏","⣎","⡏"],["⠸","⠟","⠙","⠇"]], 48 | 'd' => [["⣠","⢌","⣿","⠀"],["⠻","⠤","⠿","⠄"]], 49 | 'x' => [["⠠","⣤","⡠","⠄"],["⠠","⠎","⠷","⠄"]], 50 | 'S' => [["⠰","⣯","⣑","⠃"],["⠰","⠢","⠽","⠃"]], 51 | ')' => [["⠀","⢱","⡄","⠀"],["⠀","⡸","⠃","⠀"]], 52 | 'Y' => [["⠙","⣯","⢨","⠋"],["⠀","⠼","⠧","⠀"]], 53 | '\\' => [["⠈","⢷","⡀","⠀"],["⠀","⠈","⢷","⡀"]], 54 | 'Q' => [["⣴","⠋","⢳","⡄"],["⠙","⠦","⣞","⠁"]], 55 | ',' => [["⠀","⠀","⠀","⠀"],["⠀","⢰","⠆","⠀"]], 56 | '7' => [["⠘","⠉","⣹","⠇"],["⠀","⠰","⠏","⠀"]], 57 | 'w' => [["⢤","⡤","⠠","⡤"],["⠈","⠷","⠱","⠁"]], 58 | '_' => [["⠀","⠀","⠀","⠀"],["⣀","⣀","⣀","⣀"]], 59 | 'N' => [["⢹","⣦","⣹","⠁"],["⠼","⠌","⠻","⠀"]], 60 | '+' => [["⢀","⣰","⣆","⡀"],["⠀","⠘","⠃","⠀"]], 61 | '8' => [["⠰","⣯","⣉","⠆"],["⠘","⠤","⠽","⠃"]], 62 | 't' => [["⠠","⣾","⠤","⠀"],["⠀","⠻","⠔","⠀"]], 63 | 'B' => [["⢹","⣏","⣹","⠆"],["⠼","⠧","⠼","⠃"]], 64 | '9' => [["⠰","⣏","⣹","⡆"],["⠀","⠤","⠞","⠁"]], 65 | '>' => [["⠐","⠦","⣄","⡀"],["⠐","⠋","⠁","⠀"]], 66 | 'h' => [["⢹","⣇","⢤","⡀"],["⠼","⠇","⠼","⠇"]], 67 | 'k' => [["⢹","⡇","⡤","⠀"],["⠼","⠏","⠷","⠄"]], 68 | '0' => [["⢠","⡎","⢱","⡄"],["⠈","⠣","⠜","⠁"]], 69 | "£" => [["⢀","⣾","⡙","⠂"],["⠠","⠿","⠤","⠆"]], 70 | '2' => [["⠐","⠋","⣹","⠆"],["⠠","⠾","⠥","⠆"]], 71 | 'U' => [["⢹","⡇","⠈","⡏"],["⠘","⠧","⠤","⠃"]], 72 | 'H' => [["⢹","⣏","⣸","⡏"],["⠼","⠇","⠼","⠧"]], 73 | 'g' => [["⢀","⡤","⢤","⡠"],["⠐","⠯","⢭","⡀"]], 74 | '&' => [["⢰","⣏","⢆","⣀"],["⠻","⠼","⠳","⠅"]], 75 | '6' => [["⢠","⣞","⣉","⠀"],["⠘","⠧","⠼","⠃"]], 76 | 'info' => ' ZX Times font by DamienG https://damieng.com','v' => [["⢤","⡄","⢤","⠄"],["⠀","⠻","⠃","⠀"]], 77 | 'D' => [["⢹","⡏","⢳","⡄"],["⠼","⠧","⠞","⠁"]], 78 | '<' => [["⢀","⣠","⠴","⠂"],["⠀","⠈","⠙","⠂"]], 79 | 'c' => [["⢀","⡤","⢤","⡀"],["⠘","⠧","⠤","⠂"]], 80 | 'a' => [["⢀","⠤","⢤","⡀"],["⠐","⠯","⠽","⠇"]], 81 | ' ' => [["⠀","⠀","⠀","⠀"],["⠀","⠀","⠀","⠀"]], 82 | 's' => [["⢀","⣤","⠤","⠄"],["⠠","⠭","⠿","⠂"]], 83 | 'I' => [["⠀","⢹","⡏","⠀"],["⠀","⠼","⠧","⠀"]], 84 | 'i' => [["⠀","⢬","⡅","⠀"],["⠀","⠼","⠧","⠀"]], 85 | '!' => [["⠀","⢿","⠇","⠀"],["⠀","⠬","⠄","⠀"]], 86 | 'e' => [["⢀","⡤","⢤","⡀"],["⠘","⠯","⠭","⠅"]], 87 | '^' => [["⢠","⠞","⢦","⠀"],["⠀","⠀","⠀","⠀"]], 88 | '[' => [["⠀","⣿","⠉","⠀"],["⠀","⣿","⣀","⠀"]], 89 | '*' => [["⠠","⣰","⡠","⠀"],["⠈","⠘","⠈","⠀"]], 90 | 'p' => [["⢤","⣄","⢤","⡀"],["⣸","⣗","⠚","⠁"]], 91 | 'O' => [["⣴","⠋","⢳","⡄"],["⠙","⠦","⠞","⠁"]], 92 | 'R' => [["⢹","⣏","⣹","⠆"],["⠼","⠧","⠹","⠦"]], 93 | 'F' => [["⢹","⣏","⡍","⠃"],["⠼","⠧","⠁","⠀"]], 94 | 'j' => [["⠀","⠠","⣭","⠀"],["⠀","⣀","⡿","⠀"]], 95 | 'z' => [["⢠","⠤","⣤","⠄"],["⠠","⠾","⠥","⠆"]], 96 | 'n' => [["⢤","⣄","⢤","⡀"],["⠼","⠧","⠸","⠧"]]} 97 | -------------------------------------------------------------------------------- /examples/fonts/ZX Venice.grf: -------------------------------------------------------------------------------- 1 | {'g' => [["⣠","⠤","⢤","⡄"],["⠙","⣒","⡹","⠃"]], 2 | 'q' => [["⣠","⠤","⢤","⡄"],["⠻","⠔","⢹","⡧"]], 3 | 'L' => [["⢰","⡏","⠁","⠀"],["⠼","⠧","⠤","⠊"]], 4 | 'S' => [["⠐","⢯","⡉","⠕"],["⠪","⠤","⠽","⠂"]], 5 | "©" => [["⡔","⣩","⢭","⢢"],["⠣","⣙","⣒","⠜"]], 6 | '>' => [["⠀","⠲","⣄","⠀"],["⠀","⠚","⠁","⠀"]], 7 | '{' => [["⢀","⣸","⠏","⠁"],["⠀","⢸","⣇","⡀"]], 8 | 'Y' => [["⢺","⡇","⢸","⡇"],["⠈","⣓","⡹","⠃"]], 9 | '|' => [["⠀","⢸","⡇","⠀"],["⠀","⢸","⡇","⠀"]], 10 | 'l' => [["⠀","⣾","⠁","⠀"],["⠀","⠿","⠔","⠁"]], 11 | '*' => [["⢀","⣢","⣔","⡀"],["⠀","⠊","⠑","⠀"]], 12 | 'b' => [["⢰","⣏","⠤","⣄"],["⠸","⠧","⠴","⠋"]], 13 | ']' => [["⠀","⠉","⣿","⠀"],["⠀","⣀","⣿","⠀"]], 14 | 'P' => [["⠎","⣿","⣉","⡷"],["⠤","⠟","⠀","⠀"]], 15 | '/' => [["⠀","⢀","⡴","⠃"],["⠰","⠋","⠀","⠀"]], 16 | '#' => [["⠠","⡼","⢤","⠧"],["⢲","⠓","⡞","⠂"]], 17 | '=' => [["⠠","⠤","⠤","⠄"],["⠈","⠉","⠉","⠁"]], 18 | 'h' => [["⢰","⡏","⣠","⡄"],["⠸","⠏","⠸","⠗"]], 19 | '!' => [["⠀","⢸","⡇","⠀"],["⠀","⠰","⠆","⠀"]], 20 | '[' => [["⠀","⣿","⠉","⠀"],["⠀","⣿","⣀","⠀"]], 21 | 'info' => ' ZX Venice font by DamienG https://damieng.com','6' => [["⢰","⣏","⢭","⡂"],["⠘","⠧","⠼","⠃"]], 22 | 'z' => [["⢀","⠤","⣤","⠄"],["⠠","⠾","⠥","⠔"]], 23 | 'V' => [["⢺","⡇","⢺","⡆"],["⠘","⠧","⠊","⠀"]], 24 | '.' => [["⠀","⠀","⠀","⠀"],["⠀","⠰","⠆","⠀"]], 25 | '%' => [["⢎","⡱","⡩","⠃"],["⠠","⠊","⢎","⡱"]], 26 | 'C' => [["⢰","⡏","⠩","⠂"],["⠘","⠧","⠔","⠁"]], 27 | '1' => [["⠀","⢺","⡇","⠀"],["⠀","⠼","⠧","⠀"]], 28 | '\'' => [["⠀","⠘","⠀","⠀"],["⠀","⠀","⠀","⠀"]], 29 | 'N' => [["⢹","⢦","⡀","⡏"],["⠜","⠀","⠙","⠇"]], 30 | ' ' => [["⠀","⠀","⠀","⠀"],["⠀","⠀","⠀","⠀"]], 31 | 'B' => [["⢺","⣏","⣹","⠆"],["⠼","⠧","⠤","⠟"]], 32 | 'a' => [["⣠","⠤","⣤","⠀"],["⠻","⠔","⠿","⠊"]], 33 | 'v' => [["⣠","⡀","⣠","⡄"],["⠸","⠧","⠊","⠀"]], 34 | 'r' => [["⢀","⣤","⡠","⠄"],["⠀","⠿","⠂","⠀"]], 35 | '+' => [["⢀","⣰","⣀","⠀"],["⠀","⠘","⠀","⠀"]], 36 | '9' => [["⢰","⡏","⣹","⡆"],["⠐","⠭","⠼","⠃"]], 37 | 'n' => [["⣠","⡄","⣠","⡄"],["⠸","⠏","⠸","⠗"]], 38 | 't' => [["⠠","⣾","⠄","⠀"],["⠀","⠿","⠔","⠁"]], 39 | '2' => [["⠠","⠊","⣹","⠆"],["⠠","⠾","⠥","⠔"]], 40 | 'i' => [["⢀","⣭","⠀","⠀"],["⠀","⠿","⠔","⠁"]], 41 | '^' => [["⠀","⡴","⡷","⡄"],["⠈","⠀","⠇","⠈"]], 42 | 'T' => [["⠎","⢹","⡏","⠁"],["⠠","⠼","⠃","⠀"]], 43 | 'p' => [["⣠","⣄","⠤","⣄"],["⢸","⡗","⠤","⠟"]], 44 | '(' => [["⠀","⣴","⠁","⠀"],["⠀","⠻","⡀","⠀"]], 45 | 'X' => [["⠞","⢦","⡴","⠋"],["⠴","⠋","⠙","⠞"]], 46 | ':' => [["⠀","⠰","⠆","⠀"],["⠀","⠰","⠆","⠀"]], 47 | '\\' => [["⠘","⢦","⡀","⠀"],["⠀","⠀","⠙","⠆"]], 48 | '-' => [["⢀","⣀","⣀","⡀"],["⠀","⠀","⠀","⠀"]], 49 | 'F' => [["⠎","⣿","⣉","⠃"],["⠤","⠟","⠀","⠀"]], 50 | '0' => [["⢰","⡏","⢹","⡆"],["⠘","⠧","⠼","⠃"]], 51 | 'k' => [["⢰","⣏","⠤","⡀"],["⠸","⠏","⠻","⠔"]], 52 | 'j' => [["⠀","⢀","⣭","⠀"],["⠀","⣁","⠟","⠀"]], 53 | '5' => [["⠸","⠯","⢭","⡑"],["⠐","⠤","⠞","⠁"]], 54 | 'K' => [["⢹","⣇","⡴","⠃"],["⠸","⠏","⠳","⠔"]], 55 | 's' => [["⢀","⡤","⠤","⡀"],["⠠","⠭","⠽","⠂"]], 56 | '~' => [["⠐","⠋","⠚","⠁"],["⠀","⠀","⠀","⠀"]], 57 | 'E' => [["⠎","⣿","⣉","⠃"],["⠤","⠿","⠤","⠔"]], 58 | '$' => [["⠠","⣖","⡗","⠢"],["⠐","⠤","⡯","⠗"]], 59 | '@' => [["⡴","⣫","⢽","⡆"],["⠳","⣝","⣊","⠄"]], 60 | "£" => [["⣠","⣞","⡉","⠆"],["⠼","⠧","⠤","⠂"]], 61 | 'U' => [["⢺","⡇","⢸","⡇"],["⠘","⠧","⠺","⠗"]], 62 | 'o' => [["⢀","⡤","⢤","⡀"],["⠘","⠧","⠞","⠁"]], 63 | '&' => [["⠰","⣩","⠗","⣀"],["⠺","⠥","⠝","⠬"]], 64 | 'Z' => [["⠎","⢉","⡽","⠃"],["⠴","⠯","⠤","⠊"]], 65 | 'y' => [["⣠","⡄","⢠","⡄"],["⠈","⣓","⡹","⠃"]], 66 | 'R' => [["⠎","⣿","⣉","⡷"],["⠤","⠏","⠳","⠔"]], 67 | 'M' => [["⢹","⢦","⢺","⡏"],["⠜","⠀","⠸","⠗"]], 68 | '_' => [["⠀","⠀","⠀","⠀"],["⣀","⣀","⣀","⣀"]], 69 | '<' => [["⠀","⣠","⠖","⠀"],["⠀","⠈","⠓","⠀"]], 70 | 'W' => [["⢺","⡇","⠐","⣷"],["⠸","⠗","⠧","⠋"]], 71 | 'D' => [["⠎","⣿","⠙","⣦"],["⠤","⠿","⠤","⠋"]], 72 | ')' => [["⠀","⢱","⡄","⠀"],["⠀","⡸","⠃","⠀"]], 73 | '8' => [["⠰","⣏","⣹","⠆"],["⠘","⠧","⠼","⠃"]], 74 | '4' => [["⢀","⠔","⣿","⢀"],["⠈","⠩","⠿","⠅"]], 75 | 'w' => [["⣠","⢠","⢀","⡄"],["⠿","⠼","⠜","⠁"]], 76 | '3' => [["⠀","⡊","⢽","⡋"],["⠘","⠤","⠤","⠟"]], 77 | 'O' => [["⣴","⠉","⢳","⡄"],["⠙","⠤","⠞","⠁"]], 78 | 'e' => [["⢀","⡤","⢤","⠀"],["⠘","⠷","⠥","⠊"]], 79 | 'Q' => [["⣴","⠉","⢳","⡄"],["⠙","⠬","⠾","⡁"]], 80 | 'x' => [["⡠","⣄","⣠","⠄"],["⠠","⠞","⠳","⠔"]], 81 | ',' => [["⠀","⠀","⠀","⠀"],["⠀","⢘","⠇","⠀"]], 82 | 'I' => [["⠰","⢹","⡇","⠀"],["⠀","⠸","⠧","⠊"]], 83 | '?' => [["⠐","⠉","⣹","⠆"],["⠀","⠀","⠅","⠀"]], 84 | 'f' => [["⠀","⣰","⣏","⠁"],["⢀","⡸","⠃","⠀"]], 85 | '"' => [["⠀","⠃","⠘","⠀"],["⠀","⠀","⠀","⠀"]], 86 | ';' => [["⠀","⠰","⠆","⠀"],["⠀","⢘","⠇","⠀"]], 87 | 'H' => [["⣺","⣇","⣰","⡏"],["⠼","⠃","⠸","⠗"]], 88 | '7' => [["⠐","⠉","⣩","⠟"],["⠀","⠰","⠏","⠀"]], 89 | 'd' => [["⣠","⠤","⣾","⠁"],["⠻","⠔","⠿","⠊"]], 90 | 'u' => [["⣠","⡄","⢠","⡄"],["⠘","⠧","⠺","⠗"]], 91 | 'm' => [["⣠","⣠","⣄","⣤"],["⠿","⠸","⠇","⠿"]], 92 | 'A' => [["⠐","⣩","⢿","⡆"],["⠽","⠋","⠙","⠧"]], 93 | 'G' => [["⣾","⢉","⣋","⠀"],["⠻","⠤","⠻","⠊"]], 94 | 'J' => [["⠀","⠎","⢹","⡇"],["⠀","⣀","⠜","⠁"]], 95 | 'c' => [["⢀","⡤","⠤","⡄"],["⠘","⠧","⠤","⠂"]], 96 | '}' => [["⠈","⠹","⣇","⡀"],["⢀","⣸","⡇","⠀"]]} 97 | -------------------------------------------------------------------------------- /examples/fonts/asm2grf.pl: -------------------------------------------------------------------------------- 1 | #! /usr/bin/env perl 2 | use strict; use warnings; 3 | use lib "../../lib/"; 4 | use open ":std", ":encoding(UTF-8)"; 5 | use Term::Graille qw/colour paint printAt clearScreen border block2braille pixelAt/; 6 | use Term::Graille::Font qw/convertDG saveGrf loadGrf/; 7 | use Time::HiRes qw/sleep/; 8 | use Data::Dumper; 9 | use utf8; 10 | 11 | #find all fonts 12 | opendir my $fontDir,"." or die "Unable to open directory $!;"; 13 | my @files=readdir $fontDir; 14 | @files=map{$_=~s/.z80.asm$//;$_} grep /z80.asm$/,@files; 15 | 16 | unless (scalar @files){ 17 | print "No unconverted font files found"; 18 | exit ; 19 | } 20 | foreach my $fontName(@files){ 21 | print "Converting font $fontName\n"; 22 | my $grf=convertDG("$fontName.z80.asm"); 23 | next unless $grf; 24 | saveGrf($grf,"$fontName.grf"); 25 | rename "$fontName.z80.asm","$fontName.z80.asm.done"or 26 | print "Unable to rename fontfile $fontName.z80.asm $!;\n" ; 27 | } 28 | 29 | -------------------------------------------------------------------------------- /examples/fonts/font2grf.pl: -------------------------------------------------------------------------------- 1 | #! /usr/bin/env perl 2 | use strict; use warnings; 3 | #use lib "../../lib/"; 4 | use open ":std", ":encoding(UTF-8)"; 5 | use Term::Graille qw/colour paint printAt clearScreen border block2braille pixelAt loadGrf/; 6 | use Time::HiRes qw/sleep/; 7 | use Data::Dumper; 8 | use utf8; 9 | 10 | #find all fonts 11 | opendir my $fontDir,"." or die "Unable to open directory $!;"; 12 | my @files=readdir $fontDir; 13 | @files=map{$_=~s/.z80.asm$//;$_} grep /z80.asm$/,@files; 14 | unless (scalar @files){ 15 | print "No unconverted font files found"; 16 | exit ; 17 | } 18 | foreach my $fontName(@files){ 19 | print "Converting font $fontName\n"; 20 | open my $zxFont,"<:utf8","$fontName.z80.asm" or 21 | print "Unable to open fontfile $fontName.z80.asm $!;\n" && next; 22 | my $font=""; 23 | $font.=$_ while(<$zxFont>); 24 | close $zxFont; 25 | 26 | $font=~s/^[\s\t]?;([^\n]+)/{/g; 27 | my $info=$1; 28 | $font=~s/defb &/[[0x/g; 29 | $font=~s/,&/],[0x/g; 30 | $font=~s/ ; /]],# /g; 31 | $font=~s/\s([^#\s]*)#\s(.)/ '$2'=>$1/g; 32 | $font=~s/\'\'\'/\"\'\"/g; 33 | $font=~s/\'\\\'/\'\\\\\'/g; 34 | $font.="\n}\n"; 35 | # print $font; 36 | my $binFont=eval($font); 37 | my $grlFont={}; 38 | for (keys %$binFont){ 39 | use utf8; 40 | $grlFont->{$_}=block2braille($binFont->{$_}) ; 41 | } 42 | $grlFont->{info}=$info||""; 43 | 44 | 45 | my $output=Dumper([$grlFont])=~ s/\\x\{([0-9a-f]{2,})\}/chr hex $1/ger; 46 | $output=~s/^\$VAR1 = \[\n\s+| \];\n?$//g; 47 | $output=~s/\[\n\s+\[/\[\[/g; 48 | $output=~s/\n\s+([^\s])/$1/g; 49 | $output=~s/\]\],/\]\],\n/g; 50 | if (length $output <100){ 51 | print "Conversion failed\n"; 52 | next; 53 | }; 54 | 55 | open my $dat,">:utf8","$fontName.grf" or 56 | print "Unable to save fontfile $fontName.grf $!;\n" && next; 57 | print $dat $output; 58 | close $dat; 59 | 60 | rename "$fontName.z80.asm","$fontName.z80.asm.done"or 61 | print "Unable to rename fontfile $fontName.z80.asm $!;\n" ; 62 | 63 | } 64 | 65 | -------------------------------------------------------------------------------- /examples/graillepiano.pl: -------------------------------------------------------------------------------- 1 | use strict; use warnings; 2 | use lib "../lib"; 3 | 4 | use Term::Graille qw/colour paint printAt cursorAt clearScreen border blockBlit block2braille pixelAt/; 5 | use Term::Graille::Interact; 6 | use Term::Graille::Menu; 7 | use Term::Graille::Audio; 8 | use Term::Graille::Font qw/loadGrf fontWrite/; 9 | 10 | my $beep=Term::Graille::Audio->new(); # TERM::Graille's Audio module 11 | my $io=Term::Graille::Interact->new(); # capture keyboard actions 12 | our $VERSION=0.01; 13 | 14 | my $canvas = Term::Graille->new( 15 | width => 140, 16 | height => 60, 17 | top=>4, 18 | left=>4, 19 | borderStyle => "double", 20 | borderColour => "blue", 21 | title => " A Graille Piano ", 22 | titleColour => "red", 23 | ); 24 | 25 | splashScreen(); 26 | $canvas->clear(); 27 | my ($keyboard,$key2note)=$beep->makeKeyboard; 28 | $beep->setupKeyboard($canvas); 29 | $beep->drawKeyboard(); 30 | $canvas->draw(); 31 | my $lastKey; 32 | 33 | $io->addAction("MAIN","others",{proc=>sub{ 34 | my ($pressed,$GV)=@_; 35 | if (exists $key2note->{$pressed}){ 36 | $beep->drawKey($lastKey); 37 | $beep->playSound(undef, $key2note->{$pressed}); 38 | $beep->drawKey($pressed,"red on_green"); 39 | $lastKey=$pressed; 40 | $canvas->draw(); 41 | }; 42 | } 43 | } 44 | ); 45 | 46 | $io->addAction("MAIN","esc",{proc=>sub{ 47 | exit 1; 48 | } 49 | } 50 | ); 51 | 52 | $io->run(); 53 | 54 | sub splashScreen{ 55 | $canvas->clear(); 56 | my $grf=loadGrf("./fonts/ZX Times.grf"); 57 | fontWrite($canvas,$grf,3,13,"Monophonic Piano"); 58 | fontWrite($canvas,$grf,3,10,"A simple demo of"); 59 | fontWrite($canvas,$grf,2,7,"Interactive Audio"); 60 | $canvas->textAt(30,20,"Press Esc then m to get menu; Enter to start","green"); 61 | $canvas->draw(); 62 | } 63 | -------------------------------------------------------------------------------- /examples/image2grl.pl: -------------------------------------------------------------------------------- 1 | #! /usr/bin/env perl 2 | use strict; use warnings; 3 | use Term::Graille qw/colour paint printAt clearScreen border/; 4 | use Image::Magick; 5 | 6 | # convert2grl--converts an image to .grl format. 7 | 8 | my %params=%ARGV; 9 | 10 | my $canvas = Term::Graille->new( 11 | width => $params{width}||72, 12 | height => $params{height}||60, 13 | top=>2, 14 | left=>10, 15 | borderStyle => "double", 16 | ); 17 | clearScreen(); 18 | my $inFile=$params{-i}||"perl2.jpg"; 19 | my $outFile=$params{-o}||($inFile=~s/\..+$//r).".grl"; 20 | loadImage($inFile); 21 | $canvas->exportCanvas($outFile); 22 | 23 | sub loadImage{ 24 | my $imgfile=shift; 25 | my $image = Image::Magick->new(); 26 | my $wh = $image->Read($imgfile); 27 | $image->Resize(geometry => "$canvas->{width}x$canvas->{height}", filter=>"Cubic"); 28 | warn $wh if $wh; 29 | $image->set('monochrome'=>'True','dither'=>'True','dither-method'=>"Floyd-Steinberg"); 30 | for (my $x =0;$x<$canvas->{width};$x++){ 31 | for (my $y=0;$y<$canvas->{height};$y++){ 32 | $canvas->set($x,$y) if $image->GetPixel("x"=>$x,"y",$canvas->{height}-$y); 33 | } 34 | } 35 | } 36 | 37 | $canvas->draw(); 38 | print "\n"; 39 | print " "x8,"Viewing $inFile, saved as $outFile\n"; 40 | printAt(18,0," ") 41 | ; 42 | -------------------------------------------------------------------------------- /examples/invaders.pl: -------------------------------------------------------------------------------- 1 | #! /usr/bin/env perl 2 | use strict; use warnings; 3 | use utf8; 4 | use lib "../lib"; 5 | use open ":std", ":encoding(UTF-8)"; 6 | use Term::Graille qw/colour paint printAt cursorAt clearScreen border blockBlit block2braille pixelAt/; 7 | use Term::Graille::Interact ; 8 | use Term::Graille::Audio; 9 | use Term::Graille::Font qw/loadGrf fontWrite/; 10 | 11 | my $beep=Term::Graille::Audio->new(); # TERM::Graille's Audio module 12 | my $grf=loadGrf("./fonts/ZX Times.grf"); 13 | 14 | my $spriteBank={}; 15 | my $dir="./sprites/"; 16 | my $scroll="left"; 17 | 18 | loadSprites(qw/alien1 alien2 alien3 defender spaceship/); 19 | $spriteBank->{patriot}=[[colour("blue")."↑".colour("reset")]]; 20 | $spriteBank->{bomb}=[[colour("red")."↧".colour("reset")]]; 21 | $spriteBank->{shield}=[[colour("yellow")."⢕".colour("reset")]]; 22 | my (@aliens,@missiles,@shields,@spaceships); 23 | my $defender={sprite=>"defender",x=>40,y=>1,}; 24 | my ($maxAlienX,$minAlienX,$alienDx,$alienDy,$alienMoveSkipStart, 25 | $alienSkipAccelerate,$aliensLanded,$stopUpdates,$alienMoveSkip, 26 | $level, $score, $lives, $highscore); 27 | 28 | my $canvas = Term::Graille->new( 29 | width => 150, 30 | height => 60, 31 | top=>4, 32 | left=>4, 33 | borderStyle => "double", 34 | title=>"Space Invaders", 35 | ); 36 | 37 | initialise(); 38 | 39 | my $io=Term::Graille::Interact->new(); 40 | $io->{debugCursor}=[22,45]; 41 | 42 | $io->addAction(undef,"[D",{note=>"Move Left ",proc=>sub{ 43 | $defender->{x}=$defender->{x}>=1?$defender->{x}-1:1 }} ); 44 | $io->addAction(undef,"[C",{note=>"Move Right ",proc=>sub{ 45 | $defender->{x}=$defender->{x}<=65?$defender->{x}+1:65 }} ); 46 | $io->addAction(undef,"[A",{note=>"Fire ", proc=>sub{ 47 | push @missiles,{sprite=>"patriot",x=>$defender->{x}+4,y=>1,dy=>1};; 48 | $beep->playSound(undef,"A#1");}} ); 49 | $io->addAction(undef,"p",{note=>"PaUSE ", proc=>sub{ 50 | $stopUpdates=$stopUpdates?0:1;}} ); 51 | 52 | $io->updateAction(\&update); 53 | $io->run(); 54 | 55 | sub update { 56 | victory() unless scalar @aliens; 57 | defeat("Aliens Landed") if $aliensLanded; 58 | return if $stopUpdates; 59 | updateScreen(); 60 | updateSprites(); 61 | } 62 | 63 | #draw all sprites and redraw screen 64 | sub updateScreen{ 65 | $canvas->clear(); 66 | @aliens=grep($_->{sprite},@aliens); 67 | @missiles=grep($_->{sprite},@missiles); 68 | @shields=grep($_->{sprite},@shields); 69 | @spaceships=grep($_->{sprite},@spaceships); 70 | foreach (@aliens,@missiles,$defender,@shields,@spaceships){ 71 | next unless defined $_->{sprite}; 72 | my ($spr,$x,$y)=@$_{qw/sprite x y/}; 73 | $canvas->blockBlit($spriteBank->{$spr},$x,$y); 74 | if ($spr=~/^a/){ #collect data for aliens closest to edges 75 | $maxAlienX=$x if $x>$maxAlienX; 76 | $minAlienX=$x if $x<$minAlienX; 77 | } 78 | } 79 | push @missiles,{sprite=>"bomb",x=>2+rand()*70,y=>16,dy=>-1} if rand()<0.01; 80 | push @spaceships,{sprite=>"spaceship",x=>3,y=>15,dx=>1} if rand()<0.005; 81 | $canvas->draw(); 82 | printAt(3,40,"Score: $score Level: $level, Lives ".colour("red")."♥ "x$lives.colour("reset") ); 83 | printAt(16,0," "); 84 | } 85 | 86 | sub updateSprites{ 87 | $alienMoveSkip--; 88 | unless ($alienMoveSkip>=0){ 89 | $alienDy=0; 90 | if ($maxAlienX>=67){$alienDx=-1;$alienDy=-1;$maxAlienX=0}; 91 | if ($minAlienX<=1){$alienDx=1;$alienDy=-1;$minAlienX=1000}; 92 | foreach (@aliens){ 93 | $_->{x}+=$alienDx; 94 | $_->{y}+=$alienDy; 95 | $aliensLanded=1 if $_->{y}<=2 96 | } 97 | $alienMoveSkipStart-=0.1; 98 | $alienMoveSkip=$alienMoveSkipStart; 99 | } 100 | foreach (@spaceships){ 101 | $_->{x}+=$_->{dx}; 102 | $_ ={} if $_->{x} >=67; 103 | push @missiles,{sprite=>"bomb",x=>$_->{x}+2,y=>16,dy=>-1} if ((rand()<0.5) and ($_->{x}==$defender->{x})); 104 | $_ ={} if $_->{x} >=67; 105 | } 106 | foreach my $missile (@missiles){ 107 | next unless $missile->{y}; 108 | $missile->{y}+=$missile->{dy}; 109 | if ($missile->{y}>=16|| $missile->{y}<=1){ 110 | $missile={}; 111 | }; 112 | next unless defined $missile->{sprite}; 113 | foreach my $alien (@aliens,@spaceships){ 114 | if ($missile->{sprite}&& $missile->{sprite} eq "patriot"&&(collide($missile,$alien))){ 115 | $score+=($alien->{sprite} eq "spaceship")?1000:10; 116 | $missile=$alien={}; 117 | $beep->playSound(undef,"D#1"); 118 | } 119 | } 120 | foreach my $shield (@shields){ 121 | if ($missile->{sprite}&&(collide($missile,$shield))){ 122 | $missile=$shield={}; 123 | $beep->playSound(undef,"c#1"); 124 | } 125 | } 126 | if ($missile->{sprite} && $missile->{sprite} eq "bomb"){ 127 | defeat("Defender Died") if collide($missile,$defender) 128 | } 129 | } 130 | } 131 | 132 | sub loadSprites{ 133 | my @sprites=@_; 134 | foreach my $spr (@sprites){ 135 | open my $grf,"<:utf8",$dir.$spr.".spr" or do { 136 | errorMessage( "Unable to load spritefile $spr.spr"); 137 | next; 138 | }; 139 | my $data=""; 140 | $data.=$_ while(<$grf>); 141 | close $grf; 142 | $spriteBank->{$spr}=[@{eval($data)}[0..1]] or return errorMessage("unable to load sprite $spr.spr\n $!"); 143 | $_->[0]=colour((qw/red yellow green magenta cyan white blue/)[rand()*7]).$_->[0] foreach (@{$spriteBank->{$spr}}) 144 | } 145 | } 146 | 147 | sub collide{ 148 | my ($sprA,$sprB) =@_; 149 | return unless $sprA->{sprite} && $sprB->{sprite}; 150 | my ($blkA,$blkB)=($spriteBank->{$sprA->{sprite}},$spriteBank->{$sprB->{sprite}}); 151 | my ($ax1,$ay1,$bx1,$by1)=($sprA->{x},$sprA->{y},$sprB->{x},$sprB->{y}); 152 | my ($ax2,$ay2,$bx2,$by2)=($ax1+@{$blkA->[0]},$ay1+@$blkA-1,$bx1+@{$blkB->[0]},$by1+@$blkB-1); 153 | return (($ax1 <= $bx2) && ($ax2 >= $bx1) && ($ay1 <= $by2) && ($ay2 >= $by1) ) ; 154 | } 155 | 156 | sub errorMessage{die $_[0];} 157 | 158 | sub setupAliens{ 159 | # build aliens dataset 160 | @aliens=(); 161 | $alienMoveSkip=$alienMoveSkipStart; 162 | $aliensLanded=0; 163 | my $row=15; # start at row 15 164 | for my $alien(keys %$spriteBank){ 165 | next unless $alien=~/alien/; # use alien sprites 166 | for (0..5){ 167 | push @aliens, {sprite=>$alien,x=>10+10*$_,y=>$row} 168 | } 169 | $row-=2; #next row down 170 | } 171 | } 172 | 173 | sub buildShields{ 174 | @shields=(); 175 | for my $y (2..3){ 176 | for my $x (0..4){ 177 | for my $z (0..6){ 178 | push @shields, {sprite=>"shield",x=>4+15*$x+$z,y=>$y} 179 | } 180 | } 181 | } 182 | } 183 | 184 | sub initialise{ 185 | clearScreen(); 186 | ($maxAlienX,$minAlienX,$alienDx,$alienDy,$alienMoveSkipStart, 187 | $alienSkipAccelerate,$aliensLanded,$level, $score, $lives, $highscore)= 188 | (0,200,1,0,8,0.01,0,1,0,3, 0); 189 | $stopUpdates=0; 190 | $alienMoveSkip=$alienMoveSkipStart; 191 | setupAliens(); 192 | buildShields(); 193 | splash(); 194 | sleep 3; 195 | } 196 | 197 | sub splash{ 198 | my $mainMessage=shift//"Space Invaders"; 199 | my $message=shift//"A Game for The PerlayStation Games Console 1"; 200 | my $format=shift//"blue"; 201 | $canvas->clear(); 202 | fontWrite($canvas,$grf,10,10,$mainMessage); 203 | $canvas->textAt(30,20,$message,$format); 204 | $canvas->textAt(40,12,"Left Right Arrow keys move defender","cyan bold"); 205 | $canvas->textAt(40,8,"Up Arrow fires missile, 'p' pauses","cyan bold"); 206 | $canvas->draw(); 207 | 208 | } 209 | 210 | sub victory{ 211 | $level++;$score+=500; 212 | splash("Level clear!!","Lives $lives Score: $score","green"); 213 | sleep 3; 214 | setupAliens(); 215 | } 216 | sub defeat{ 217 | my $fail=shift; 218 | $lives--; 219 | splash($fail,"Lives $lives Score: $score","green"); 220 | gameover() unless $lives; 221 | setupAliens(); 222 | sleep 4; 223 | } 224 | 225 | sub gameover{ 226 | $highscore=$score if $score>$highscore; 227 | splash(" Annihilated!","Score: $score High Score: $highscore ","green"); 228 | sleep 4; 229 | initialise(); 230 | } 231 | 232 | 233 | sub quit{ 234 | splash("Good Bye!!","Score: $score High Score: $highscore ","green"); 235 | sleep 4; 236 | exit; 237 | } 238 | 239 | -------------------------------------------------------------------------------- /examples/lineDemo.pl: -------------------------------------------------------------------------------- 1 | #! /usr/bin/env perl 2 | use strict; use warnings; 3 | use utf8; 4 | use open ":std", ":encoding(UTF-8)"; 5 | use Term::Graille qw/colour paint printAt clearScreen border/; 6 | 7 | my $canvas = Term::Graille->new( 8 | width => 120, 9 | height => 60, 10 | top=>4, 11 | left=>10, 12 | borderStyle => "double", 13 | title=>"Variable Thickness Line Demo", 14 | ); 15 | my @colours=qw/red blue green yellow cyan magenta white/; 16 | clearScreen(); 17 | $canvas->clear(); 18 | 19 | my $squiggles={ 20 | sin1=>sub{ 21 | my ($arg, $p, $length)=@_; 22 | return abs(4*sin ($p/$arg)); 23 | }, 24 | sin2=>sub{ 25 | my ($arg, $p, $length)=@_; 26 | return abs(4*sin (($p+4)/$arg)); 27 | }, 28 | zigzag=>sub{ 29 | my ($arg, $p, $length)=@_; 30 | return $p % $arg; 31 | }, 32 | fixed=>sub{ 33 | my ($arg, $p, $length)=@_; 34 | return $arg; 35 | }, 36 | linear=>sub{ 37 | my ($arg, $p, $length)=@_; 38 | return $arg*$p/$length; 39 | }, 40 | }; 41 | 42 | #for (2..9){ 43 | #$canvas->thick_line(60,30,60+sin($_*45*3.14159267/180)*20,30+cos($_*45*3.14159267/180)*20,3,$colours[$_% 7]); 44 | #} 45 | 46 | my ($x1,$y1,$x2,$y2)=(5,55,115,5); 47 | $canvas->varthick_line($x1,$y1,$x1,$y2,$squiggles->{sin1},3,$squiggles->{sin2},3,"green"); 48 | $canvas->varthick_line($x1,$y2,$x2,$y2,$squiggles->{sin1},3,$squiggles->{sin2},3,"green"); 49 | $canvas->varthick_line($x2,$y2,$x2,$y1,$squiggles->{sin1},3,$squiggles->{sin2},3,"green"); 50 | $canvas->varthick_line($x2,$y1,$x1,$y1,$squiggles->{sin1},3,$squiggles->{sin2},3,"green"); 51 | 52 | $canvas->varthick_line(20,12,20,48,$squiggles->{linear},4,$squiggles->{linear},4,"red"); 53 | 54 | $canvas->varthick_line(40,12,40,48,$squiggles->{zigzag},4,$squiggles->{fixed},2,"blue"); 55 | $canvas->varthick_line(60,12,60,48,$squiggles->{zigzag},4,$squiggles->{zigzag},6,"yellow"); 56 | $canvas->varthick_line(80,12,80,48,$squiggles->{sin1},4,$squiggles->{sin1},8,"magenta"); 57 | $canvas->varthick_line(100,12,100,48,$squiggles->{sin1},4,$squiggles->{sin1},4,"cyan"); 58 | 59 | $canvas->draw(); 60 | -------------------------------------------------------------------------------- /examples/perl2.grl: -------------------------------------------------------------------------------- 1 | ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ 2 | ⠀⠀⠀⠀⠀⠀⢀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣀⡀⣠⣀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ 3 | ⠀⠀⢀⡴⡤⣲⣾⣿⣗⡀⠀⠀⠀⠀⣠⣾⣿⣿⣽⣿⣟⡅⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ 4 | ⠀⠀⠀⠋⢳⣧⣫⣿⣿⣗⠀⠀⣀⣶⣿⣿⣿⣿⣿⡺⢽⣽⣆⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ 5 | ⠀⠀⠀⢀⣿⣿⣿⣯⣿⠏⣤⣾⣿⡟⢽⢛⢯⣫⡕⢍⢬⢪⡺⣆⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ 6 | ⠀⠀⠀⢰⣿⣶⢟⣿⢟⢰⣿⣿⣯⠃⠄⡢⠹⣓⡭⢯⢧⡝⡷⣛⣆⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ 7 | ⠀⠀⠀⠰⣿⣿⣏⡼⡣⢫⣿⣿⣿⠆⡐⡕⣣⠕⡭⡪⡄⣒⣺⣴⣿⣦⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀ 8 | ⠀⠀⠀⠀⠈⠿⣿⣎⣜⣴⣿⣿⣿⣯⣿⡞⣔⣿⣿⣷⣿⣾⣿⣿⡇⣻⡃⠀⠀⠀⠀⠀⠀⠀⠀⠀ 9 | ⠀⠀⠀⠀⠀⠀⠘⠛⠗⢛⣿⣿⣿⢿⣷⣮⣎⣾⣿⣿⣿⢻⣿⣿⠂⣸⠇⠀⠀⠀⠀⠀⠀⠀⠀⠀ 10 | ⠀⠀⠀⠀⠀⠀⠀⠀⠀⢠⣿⣿⠏⢠⣿⣿⡿⠂⣿⣿⠀⠀⢿⣿⡐⠋⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ 11 | ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠰⣿⡏⠀⣼⢿⠿⠁⠀⣿⣗⠀⠀⠘⣿⠂⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ 12 | ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠐⣿⠇⠀⠻⣎⠀⠀⠀⠎⠀⠀⠀⠀⣿⠂⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ 13 | ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⡟⠀⠀⠀⠀⠣⡐⠊⠀⠀⠀⠀⠀⡕⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ 14 | ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠨⠀⠀⠀⠀⢠⣴⡿⣮⠄⠀⠀⠀⠐⡃⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ 15 | ⠀⠀⠀⠀⠀⠀⠀⠰⠖⢕⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠾⠷⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ 16 | ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ 17 | -------------------------------------------------------------------------------- /examples/perl2.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/saiftynet/Term-Graille/c467644004a72a34c607ebdf6d8ec4c25afbac70/examples/perl2.jpg -------------------------------------------------------------------------------- /examples/pulseaudio.pl: -------------------------------------------------------------------------------- 1 | #!/usr/env perl 2 | ############### PulsAudio Utils ##################### 3 | #https://wiki.archlinux.org/title/PulseAudio/Examples# 4 | use strict; use warnings; 5 | use lib "../lib/"; 6 | 7 | use Term::Graille qw/colour paint printAt cursorAt clearScreen border blockBlit block2braille pixelAt/; 8 | use Term::Graille::Interact; 9 | use Term::Graille::Menu; 10 | use Term::Graille::Font qw/loadGrf fontWrite/; 11 | 12 | our $VERSION=0.01; 13 | 14 | my $canvas = Term::Graille->new( 15 | width => 140, 16 | height => 60, 17 | top=>4, 18 | left=>4, 19 | borderStyle => "double", 20 | borderColour => "blue", 21 | title => " PulseAudio ", 22 | titleColour => "red", 23 | ); 24 | 25 | splashScreen(); 26 | 27 | my $paList=paCtl(); 28 | my ($listMenu,$itemToObject)=makeListMenu(); 29 | 30 | my $io=Term::Graille::Interact->new(); # For interactions menu and keyboard 31 | $io->{debugCursor}=[22,45]; # Put key presses detected ny Interact on the screen 32 | 33 | my $menu=new Term::Graille::Menu( 34 | menu=>[["Global","Reset","Dump","Exit"], 35 | $listMenu, 36 | ["Sound","Record","Playback"], 37 | "About"], 38 | redraw=>\&refreshScreen, 39 | dispatcher=>\&menuActions, 40 | ); 41 | 42 | my %actions=( 43 | Reset=>sub{ 44 | }, 45 | Stop=>sub{ 46 | }, 47 | About=>sub{ 48 | splashScreen($canvas); 49 | } 50 | ); 51 | 52 | sub menuActions{ 53 | my ($action,$bc)=@_; 54 | return unless $action; 55 | if (exists $actions{$action}){ 56 | $actions{$action}->() 57 | } 58 | else{ 59 | if ($bc->[0] == 1){ # View Items 60 | my $mGroup=$bc->[1]; 61 | my $mIndex=$bc->[2]; 62 | #ie "$mGroup $mIndex $itemToObject"; 63 | objectDescribe(@{$itemToObject->[$mGroup]->[$mIndex]}) 64 | }; 65 | } 66 | }; 67 | 68 | $io->addObject(object => $menu,trigger=>"m"); 69 | $canvas->draw(); 70 | $io->run(); 71 | 72 | sub splashScreen{ 73 | $canvas->clear(); 74 | my $grf=loadGrf("./fonts/ZX Times.grf"); 75 | fontWrite($canvas,$grf,3,13,"PulseAudio Utils"); 76 | fontWrite($canvas,$grf,1,10,"Tool to Play with"); 77 | fontWrite($canvas,$grf,13,7,"Linux Audio"); 78 | $canvas->textAt(30,20,"An interactive shell for pacat pacmd pactl","green italic"); 79 | $canvas->textAt(38,16,"(parec paplay pamon padsp etc)","yellow italic"); 80 | $canvas->textAt(40,12,"Version $VERSION (GRAILLE ".$Term::Graille::VERSION.")","yellow italic"); 81 | $canvas->textAt(40,8,"(m key brings up the menu)","cyan bold"); 82 | $canvas->draw(); 83 | } 84 | 85 | sub refreshScreen{ 86 | clearScreen() unless (shift); 87 | $canvas->draw(); 88 | } 89 | 90 | sub paCtl{ 91 | my $paList={}; 92 | my $currentType;my $index;my $currentKey; 93 | foreach my $line (`pactl list`){ 94 | chomp $line; 95 | if ($line=~/^([A-Z][a-z]+) #(\d+)/){ 96 | $currentType=$1; $index=$2; 97 | $paList->{$currentType}//=[]; 98 | $paList->{$currentType}->[$index]={}; 99 | $currentKey=""; 100 | } 101 | next unless defined $index; 102 | if($line=~/^\t([A-Z\sa-z]+): (.+)/){ 103 | $currentKey=$1; 104 | $paList->{$currentType}->[$index]->{$1}=$2; 105 | } 106 | elsif($line=~/^\t([A-Z\sa-z]+):(\s)*$/){ 107 | $currentKey=$1; 108 | } 109 | elsif($line=~/^\t\t([A-Z\._a-z]+) = "(.+)"/){ 110 | $paList->{$currentType}->[$index]->{$currentKey}//={}; 111 | $paList->{$currentType}->[$index]->{$currentKey}->{$1}=$2; 112 | } 113 | elsif($line=~/^\t\t([A-Z\sa-z]+): (.+)/){ 114 | $paList->{$currentType}->[$index]->{$currentKey}//=[]; 115 | push @{$paList->{$currentType}->[$index]->{$currentKey}},{$1=>$2}; 116 | } 117 | else{ 118 | $paList->{$currentType}->[$index]->{$currentKey}.="\n".$line unless ref $paList->{$currentType}->[$index]->{$currentKey} 119 | } 120 | } 121 | return $paList 122 | } 123 | 124 | sub makeListMenu{ 125 | my $lM=["List"]; 126 | my $i2O=[];my $menuTypePosition=0; 127 | for my $type (reverse sort keys %$paList){ 128 | my @list=@{$paList->{$type}}; 129 | my $n=0;my $ind=0; my $splitList=0;my $start=0;my $end=0;my $typeList=[]; 130 | while ($ind<=$#list){ 131 | $i2O->[$menuTypePosition]//=[]; 132 | my $i=object2listItem($type,$ind); 133 | if ($i){ 134 | push @$typeList, $i; 135 | push @{$i2O->[$menuTypePosition]},[$type,$ind]; 136 | } 137 | if (@$typeList>=10){ # list is full so populate and start new list 138 | $splitList=1; 139 | $end=$ind; 140 | push @$lM, ["$type $start-$end",@$typeList]; 141 | $start=$end+1; 142 | $typeList=[]; 143 | $menuTypePosition++; 144 | } 145 | $ind++; 146 | } 147 | if ($splitList && @$typeList){ # list is split so needs new list 148 | $end=$ind; 149 | push @$lM, ["$type $start-$end",@$typeList]; 150 | } 151 | elsif(@$typeList){ # list is not split so submit as is 152 | push @$lM, [$type,@$typeList]; 153 | } 154 | else{ # list is empty so discard 155 | $menuTypePosition-- 156 | } 157 | $menuTypePosition++; 158 | } 159 | return $lM,$i2O; 160 | } 161 | 162 | sub object2listItem{ 163 | my ($type,$index)=@_; 164 | return undef unless $paList->{$type}->[$index]; 165 | return ($type eq "Client") ? 166 | substr($paList->{$type}->[$index]->{Properties}->{'application.name'},0,30): 167 | $paList->{$type}->[$index]->{Name}; 168 | 169 | } 170 | 171 | sub objectDescribe{ 172 | my ($type,$index)=@_; 173 | my $target= $paList->{$type}->[$index]; 174 | my ($x,$y)=(15,56); 175 | $canvas->clear(); 176 | $canvas->textAt($x,$y,object2listItem($type,$index),"green bold"); $y-=4; 177 | if ($type =~/^Sink|Source$/){ 178 | my @volume =split(/[,\n]\s+/,$target->{'Volume'}); 179 | $canvas->textAt($x,$y,"Description : ".$target->{Properties}->{'device.description'},"cyan"); $y-=4; 180 | $canvas->textAt($x,$y,"Device String : ".$target->{Properties}->{'device.string'},"cyan"); $y-=4; 181 | $canvas->textAt($x,$y,"Specification : ".$target->{'Sample Specification'},"cyan"); $y-=4; 182 | for (0..$#volume){ 183 | $canvas->textAt($x,$y,"Volume : ".$volume[$_],"cyan"); $y-=4; 184 | } 185 | $canvas->textAt($x,$y,"Card : ".$target->{Properties}->{'alsa.card_name'},"cyan"); $y-=4; 186 | } 187 | elsif ($type =~/^Module$/){ 188 | $canvas->textAt($x,$y,"Description : ".$target->{Properties}->{'module.description'},"cyan"); $y-=4; 189 | if ($target->{'Argument'}){ 190 | my @argument =split(/\s+/,$target->{'Argument'}); 191 | for (0..$#argument){ 192 | $canvas->textAt($x,$y,"Argument : ".$argument[$_],"cyan"); $y-=4; 193 | } 194 | } 195 | } 196 | elsif ($type =~/^Client$/){ 197 | $canvas->textAt($x,$y,"Driver : ".$target->{'Driver'},"cyan"); $y-=4; 198 | if ($target->{Properties}->{'application.process.id'}){ 199 | $canvas->textAt($x,$y,"Binary : ".$target->{Properties}->{'application.process.binary'},"cyan"); $y-=4; 200 | $canvas->textAt($x,$y,"PID : ".$target->{Properties}->{'application.process.id'},"cyan"); $y-=4; 201 | } 202 | } 203 | elsif ($type =~/^Sample$/){ 204 | $canvas->textAt($x,$y,"Duration : ".$target->{'Duration'},"cyan"); $y-=4; 205 | $canvas->textAt($x,$y,"Size : ".$target->{'Size'},"cyan"); $y-=4; 206 | $canvas->textAt($x,$y,"Spec : ".$target->{'Sample Specification'},"cyan"); $y-=4; 207 | my $filename=$target->{Properties}->{'media.filename'}//$target->{'Filename'}; 208 | if ($filename){ 209 | $canvas->textAt($x,$y,"Filename : ".$filename,"cyan"); $y-=4; 210 | } 211 | if ($target->{Properties}->{'application.process.id'}){ 212 | $canvas->textAt($x,$y,"Binary : ".$target->{Properties}->{'application.process.binary'},"cyan"); $y-=4; 213 | $canvas->textAt($x,$y,"PID : ".$target->{Properties}->{'application.process.id'},"cyan"); $y-=4; 214 | } 215 | } 216 | elsif ($type =~/^Card$/){ 217 | 218 | $canvas->textAt($x,$y,"Active Profile : ".$target->{'Active Profile'},"cyan"); $y-=4; 219 | for (["device.description" ,"Description : "], 220 | ["alsa.long_card_name","Card Name : "], 221 | ["device.bus" ,"Device Bus : "], 222 | ["device.vendor.name" ,"Vendor Name : "], 223 | ["device.product.name","Product Name : "],){ 224 | $canvas->textAt($x,$y,$_->[1].$target->{Properties}->{$_->[0]},"cyan"); $y-=4; 225 | 226 | } 227 | } 228 | $canvas->draw(); 229 | 230 | } 231 | -------------------------------------------------------------------------------- /examples/save.grl: -------------------------------------------------------------------------------- 1 | ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ 2 | ⠀⠀⠀⠀⠀⠀⠀⡀⠀⠀⠀⠀⠀⠀⠀⠀⣀⣀⣠⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ 3 | ⠀⠀⢠⡴⠄⣶⣯⡿⣶⡀⠀⠀⠀⠀⢠⣾⣿⣮⣾⣿⣏⠢⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ 4 | ⠀⠀⠀⠋⢳⣶⣍⣿⣿⡗⠀⠀⣀⣼⣿⣿⣿⣿⣯⡝⢽⣻⡆⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ 5 | ⠀⠀⠀⢀⣿⣿⢾⣭⣽⠏⣠⣾⣿⢻⠫⡚⡟⡣⡪⢌⢊⢕⡝⣆⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ 6 | ⠀⠀⠀⢰⣿⣲⠟⣿⢟⢠⣿⣻⣻⠁⢐⠑⠋⡟⠺⢺⠷⢪⢻⢫⣆⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ 7 | ⠀⠀⠀⠠⣿⣿⡇⣝⡖⢱⣿⣿⣷⡅⠂⡜⡌⣮⢫⢊⢂⢊⣼⢰⣻⣦⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀ 8 | ⠀⠀⠀⠀⠈⠿⣿⡌⡐⣰⣿⣿⣿⣎⢿⠗⢨⣳⣧⣿⣷⣿⣿⣯⡇⣽⡃⠀⠀⠀⠀⠀⠀⠀⠀⠀ 9 | ⠀⠀⠀⠀⠀⠀⠉⠛⠟⠛⣿⣿⣿⢿⣿⣮⣎⡽⣯⣿⡟⠹⣿⣿⠀⢸⡇⠀⠀⠀⠀⠀⠀⠀⠀⠀ 10 | ⠀⠀⠀⠀⠀⠀⠀⠀⠀⢠⣿⣿⠇⢠⣿⣿⡿⠀⣿⣿⠁⠀⢿⣿⠈⠋⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ 11 | ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠰⣿⡏⠀⣼⢿⠿⠁⠀⣟⡟⠀⠀⠘⣿⠂⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ 12 | ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣿⠇⠀⠻⣌⠀⠀⢀⠎⠈⠀⠀⠀⣿⠂⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ 13 | ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⡟⠀⠀⠀⠈⠢⡀⠃⠀⠀⠀⠀⠀⠪⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ 14 | ⠀⠀⠀⠀⠀⠀⠀⠀⠀⡜⠁⠀⠀⠀⣠⢴⡟⣾⡀⠀⠀⠀⠠⡇⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ 15 | ⠀⠀⠀⠀⠀⠀⠀⠤⠮⠰⠀⠀⠀⠀⠀⠀⠀⠈⠁⠀⠀⠀⠾⠷⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ 16 | ⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀ 17 | -------------------------------------------------------------------------------- /examples/sprites/File.spr: -------------------------------------------------------------------------------- 1 | [["⠀","⣀","⣖","⣒","⣒","⣒","⡒","⡆"],["⡖","⢓","⣒","⣒","⠒","⡆","⡇","⡇"],["⡇","⠸","⠗","⠂","⠀","⣇","⡧","⠇"],["⠉","⠉","⠉","⠉","⠉","⠁","⠀","⠀"]] 2 | -------------------------------------------------------------------------------- /examples/sprites/Hash.spr: -------------------------------------------------------------------------------- 1 | [["⠀","⠀","⠀","⠀","⠀","⠀","⠀","⠀"],["⣤","⣤","⣿","⣤","⣤","⣿","⣤","⣤"],["⣤","⣤","⣿","⣤","⣤","⣿","⣤","⣤"],["⠀","⠀","⠛","⠀","⠀","⠛","⠀","⠀"]] 2 | -------------------------------------------------------------------------------- /examples/sprites/Launcher.spr: -------------------------------------------------------------------------------- 1 | [["⠀","⠀","⣠","⠤","⠾","⠀","⠀","⠀"],["⣿","⣿","⣿","⠀","⢸","⢰","⠄","⠀"],["⠉","⠉","⠻","⠤","⣬","⠀","⠀","⠀"],["⠀","⠀","⠀","⠀","⠈","⠀","⠀","⠀"]] 2 | -------------------------------------------------------------------------------- /examples/sprites/bar.spr: -------------------------------------------------------------------------------- 1 | [["⠀","⠀","⣴","⠿","⢦","⠀","⠀","⠀"],["⠀","⠀","⣿","⠀","⢸","⠀","⠀","⠀"],["⠀","⠀","⣿","⠀","⢸","⠀","⠀","⠀"],["⠀","⠀","⠻","⣤","⡼","⠀","⠀","⠀"]] 2 | -------------------------------------------------------------------------------- /examples/sprites/boat.spr: -------------------------------------------------------------------------------- 1 | [["⠀","⠀","⠀","⠀","⢀","⠀","⠀","⠀"],["⠀","⠀","⢀","⣴","⣿","⢰","⡀","⠀"],["⣀","⣚","⣛","⣛","⣿","⣘","⣓","⣀"],["⠈","⠛","⠓","⠛","⠚","⠛","⠚","⠁"]] 2 | -------------------------------------------------------------------------------- /examples/sprites/cow.spr: -------------------------------------------------------------------------------- 1 | [["⠀","⠀","⠀","⠀","⠀","⠀","⠀","⠀"],["⠀","⠀","⢀","⣱","⣁","⡀","⠀","⠀"],["⠀","⡏","⠹","⠤","⡼","⠇","⠀","⠀"],["⡠","⠛","⡍","⠉","⡕","⠢","⠀","⠀"]] 2 | -------------------------------------------------------------------------------- /examples/sprites/crwoned.spr: -------------------------------------------------------------------------------- 1 | [["⠀","⠀","⠀","⠀","⠀","⠀","⠀","⠀"],["⠀","⠀","⠀","⠀","⠀","⠀","⠀","⠀"],["⠀","⠀","⠀","⠀","⠀","⠀","⠀","⠀"],["⠀","⠀","⠀","⠀","⠀","⠀","⠀","⠀"]] 2 | -------------------------------------------------------------------------------- /examples/sprites/default.spb: -------------------------------------------------------------------------------- 1 | { 2 | NewSprite=>[["⠀","⠀","⠀","⠀","⠀","⠀","⠀","⠀"],["⠀","⠀","⠀","⠀","⠀","⠀","⠀","⠀"],["⠀","⠀","⠀","⠀","⠀","⠀","⠀","⠀"],["⠀","⠀","⠀","⠀","⠀","⠀","⠀","⠀"]], 3 | } 4 | -------------------------------------------------------------------------------- /examples/sprites/die.spr: -------------------------------------------------------------------------------- 1 | [["⢰","⠪","⣛","⠍","⠩","⣛","⠝","⡆"],["⠈","⠑","⢤","⠰","⠆","⡤","⠋","⠀"],["⠛","⢧","⣀","⠉","⢉","⣡","⠞","⠛"],["⣤","⡴","⠚","⠋","⠙","⠓","⢤","⣤"]] 2 | -------------------------------------------------------------------------------- /examples/sprites/dummy.spb: -------------------------------------------------------------------------------- 1 | { 2 | cow=>[["⠀","⠀","⠀","⠀","⠀","⠀","⠀","⠀"],["⠀","⠀","⢀","⣱","⣁","⡀","⠀","⠀"],["⠀","⡏","⠹","⠤","⡼","⠇","⠀","⠀"],["⡠","⠛","⡍","⠉","⡕","⠢","⠀","⠀"]], 3 | empty=>[["⠀","⠀","⠀","⠀","⠀","⠀","⠀","⠀"],["⠀","⠀","⠀","⠀","⠀","⠀","⠀","⠀"],["⠀","⠀","⠀","⠀","⠀","⠀","⠀","⠀"],["⠀","⠀","⠀","⠀","⠀","⠀","⠀","⠀"]], 4 | man1=>[["⠀","⠀","⣠","⡤","⠤","⡀","⠀","⠀"],["⠀","⠀","⣙","⡳","⣒","⣁","⣤","⠄"],["⠀","⣼","⠃","⣿","⣇","⠀","⠀","⠀"],["⠀","⠀","⣴","⣋","⠈","⢳","⣤","⡀"]], 5 | spiral=>[["⡖","⣒","⣒","⣒","⣒","⣒","⢲","⠀"],["⡇","⡇","⡖","⣒","⢲","⢸","⢸","⠀"],["⡇","⣇","⣓","⣚","⣸","⢸","⢸","⠀"],["⣓","⣒","⣒","⣒","⣒","⣚","⣸","⠀"]], 6 | } 7 | -------------------------------------------------------------------------------- /examples/sprites/empty.spr: -------------------------------------------------------------------------------- 1 | [["⠀","⠀","⠀","⠀","⠀","⠀","⠀","⠀"],["⠀","⠀","⠀","⠀","⠀","⠀","⠀","⠀"],["⠀","⠀","⠀","⠀","⠀","⠀","⠀","⠀"],["⠀","⠀","⠀","⠀","⠀","⠀","⠀","⠀"]] 2 | -------------------------------------------------------------------------------- /examples/sprites/forbidden.spr: -------------------------------------------------------------------------------- 1 | [["⠀","⠀","⢀","⣀","⣀","⡀","⠀","⠀"],["⢠","⡞","⣉","⣀","⣀","⣉","⢳","⡄"],["⠘","⢧","⣉","⠉","⠉","⣉","⡼","⠃"],["⠀","⠀","⠈","⠉","⠉","⠁","⠀","⠀"]] 2 | -------------------------------------------------------------------------------- /examples/sprites/game1.spb: -------------------------------------------------------------------------------- 1 | { 2 | manhatwalk2=>[["⠀","⢤","⠿","⡿","⡤","⠀","⠀","⠀"],["⠀","⣈","⣲","⣖","⠋","⠀","⠀","⠀"],["⠸","⠀","⣇","⣸","⠑","⠒","⠂","⠀"],["⠰","⠖","⠃","⠘","⣇","⡀","⠀","⠀"]], 3 | manhatwalk4=>[["⠀","⢤","⠿","⡿","⡤","⠀","⠀","⠀"],["⠀","⠈","⣲","⣖","⠋","⠀","⠀","⠀"],["⠀","⡎","⣇","⣺","⠒","⠀","⠀","⠀"],["⠀","⢰","⣏","⡈","⠹","⠦","⠄","⠀"]], 4 | manhatwalk1=>[["⠀","⢤","⠿","⡿","⡤","⠀","⠀","⠀"],["⠀","⠈","⣲","⣖","⠋","⠀","⠀","⠀"],["⠀","⡎","⣇","⣺","⠒","⠀","⠀","⠀"],["⠀","⢰","⣏","⡈","⠹","⠦","⠄","⠀"]], 5 | manhatwalk3=>[["⠀","⢤","⠿","⡿","⡤","⠀","⠀","⠀"],["⠀","⠈","⣲","⣖","⠋","⠀","⠀","⠀"],["⠀","⠪","⣇","⡸","⠑","⠄","⠀","⠀"],["⠀","⠲","⢺","⣟","⠂","⠀","⠀","⠀"]], 6 | } 7 | -------------------------------------------------------------------------------- /examples/sprites/help.spr: -------------------------------------------------------------------------------- 1 | [["⠀","⠀","⠀","⣀","⡀","⠀","⠀","⠀"],["⠀","⠀","⠾","⠉","⠉","⣷","⠀","⠀"],["⠀","⠀","⠀","⢠","⡞","⠉","⠀","⠀"],["⠀","⠀","⠀","⢨","⡅","⠀","⠀","⠀"]] 2 | -------------------------------------------------------------------------------- /examples/sprites/info.spr: -------------------------------------------------------------------------------- 1 | [["⠀","⠐","⠿","⠿","⠂","⠀","⠀","⠀"],["⠀","⠠","⢶","⣶","⠀","⠀","⠀","⠀"],["⠀","⠀","⢸","⣿","⠀","⠀","⠀","⠀"],["⠀","⠀","⠘","⢿","⣤","⠼","⠃","⠀"]] 2 | -------------------------------------------------------------------------------- /examples/sprites/m.spr: -------------------------------------------------------------------------------- 1 | [["⠀","⠀","⠀","⠀","⠀","⠀","⠀","⠀"],["⠀","⠀","⠀","⠀","⠀","⠀","⠀","⠀"],["⠀","⠀","⠀","⠀","⠀","⠀","⠀","⠀"],["⠀","⠀","⠀","⠀","⠀","⠀","⠀","⠀"]] 2 | -------------------------------------------------------------------------------- /examples/sprites/man1.spr: -------------------------------------------------------------------------------- 1 | [["⠀","⠀","⣠","⡤","⠤","⡀","⠀","⠀"],["⠀","⠀","⣙","⡳","⣒","⣁","⣤","⠄"],["⠀","⣼","⠃","⣿","⣇","⠀","⠀","⠀"],["⠀","⠀","⣴","⣋","⠈","⢳","⣤","⡀"]] 2 | -------------------------------------------------------------------------------- /examples/sprites/manhatwalk1.spr: -------------------------------------------------------------------------------- 1 | [["⠀","⢤","⠿","⡿","⡤","⠀","⠀","⠀"],["⠀","⣈","⣲","⣖","⠋","⠀","⠀","⠀"],["⠪","⠀","⣇","⣸","⠑","⠄","⠀","⠀"],["⠠","⣞","⠁","⠈","⠳","⠔","⠂","⠀"]] 2 | -------------------------------------------------------------------------------- /examples/sprites/manhatwalk2.spr: -------------------------------------------------------------------------------- 1 | [["⠀","⢤","⠿","⡿","⡤","⠀","⠀","⠀"],["⠀","⣈","⣲","⣖","⠋","⠀","⠀","⠀"],["⠸","⠀","⣇","⣸","⠑","⠒","⠂","⠀"],["⠰","⠖","⠃","⠘","⣇","⡀","⠀","⠀"]] 2 | -------------------------------------------------------------------------------- /examples/sprites/manhatwalk3.spr: -------------------------------------------------------------------------------- 1 | [["⠀","⢤","⠿","⡿","⡤","⠀","⠀","⠀"],["⠀","⠈","⣲","⣖","⠋","⠀","⠀","⠀"],["⠀","⠪","⣇","⡸","⠑","⠄","⠀","⠀"],["⠀","⠲","⢺","⣟","⠂","⠀","⠀","⠀"]] 2 | -------------------------------------------------------------------------------- /examples/sprites/manhatwalk4.spr: -------------------------------------------------------------------------------- 1 | [["⠀","⢤","⠿","⡿","⡤","⠀","⠀","⠀"],["⠀","⠈","⣲","⣖","⠋","⠀","⠀","⠀"],["⠀","⠪","⣇","⡸","⠑","⠄","⠀","⠀"],["⠀","⠲","⢺","⣟","⠂","⠀","⠀","⠀"]] 2 | -------------------------------------------------------------------------------- /examples/sprites/mm.spr: -------------------------------------------------------------------------------- 1 | [["⠀","⠀","⠀","⠀","⠀","⠀","⠀","⠀"],["⠀","⠀","⠀","⠀","⠀","⠀","⠀","⠀"],["⠀","⠀","⠀","⠀","⠀","⠀","⠀","⠀"],["⠀","⠀","⠀","⠀","⠀","⠀","⠀","⠀"]] 2 | -------------------------------------------------------------------------------- /examples/sprites/oyster.spr: -------------------------------------------------------------------------------- 1 | [["⠀","⠀","⣀","⣠","⣤","⣀","⠀","⠀"],["⣴","⠋","⢁","⡤","⠤","⣌","⠙","⣦"],["⠻","⣄","⠙","⠦","⠤","⢾","⣥","⠞"],["⢴","⣞","⣓","⣀","⣀","⣒","⣲","⠦"]] 2 | -------------------------------------------------------------------------------- /examples/sprites/perly.spr: -------------------------------------------------------------------------------- 1 | [["⠀","⠀","⠀","⠀","⠀","⠀","⠀","⠀"],["⠀","⠀","⠀","⠀","⠀","⠀","⠀","⠀"],["⠀","⠀","⠀","⠀","⠀","⠀","⠀","⠀"],["⠀","⠀","⠀","⠀","⠀","⠀","⠀","⠀"]] 2 | -------------------------------------------------------------------------------- /examples/sprites/photos.spr: -------------------------------------------------------------------------------- 1 | [["⠀","⣀","⣖","⣒","⣒","⣒","⡒","⡆"],["⡖","⢓","⣒","⣒","⠒","⡆","⡇","⡇"],["⡇","⢇","⠢","⢂","⠇","⡇","⡇","⡇"],["⠧","⠤","⠭","⠥","⠤","⠗","⠋","⠁"]] 2 | -------------------------------------------------------------------------------- /examples/sprites/rocket.spr: -------------------------------------------------------------------------------- 1 | [["⠀","⠀","⡠","⠊","⠲","⣄","⠀","⠀"],["⠀","⠀","⡇","⠀","⠀","⣿","⠀","⠀"],["⠀","⠀","⡧","⡀","⣠","⢿","⡀","⠀"],["⠀","⡾","⠀","⠑","⠋","⠀","⠿","⡄"]] 2 | -------------------------------------------------------------------------------- /examples/sprites/spiral.spr: -------------------------------------------------------------------------------- 1 | [["⡖","⣒","⣒","⣒","⣒","⣒","⢲","⠀"],["⡇","⡇","⡖","⣒","⢲","⢸","⢸","⠀"],["⡇","⣇","⣓","⣚","⣸","⢸","⢸","⠀"],["⣓","⣒","⣒","⣒","⣒","⣚","⣸","⠀"]] 2 | -------------------------------------------------------------------------------- /examples/sprites/testmessage.spb: -------------------------------------------------------------------------------- 1 | { 2 | NewSprite=>[["⠀","⠀","⠀","⠀","⠀","⠀","⠀","⠀"],["⠀","⠀","⠀","⠀","⠀","⠀","⠀","⠀"],["⠀","⠀","⠀","⠀","⠀","⠀","⠀","⠀"],["⠀","⠀","⠀","⠀","⠀","⠀","⠀","⠀"]], 3 | } 4 | -------------------------------------------------------------------------------- /examples/sprites/warn.spr: -------------------------------------------------------------------------------- 1 | [["⠀","⠀","⠀","⢀","⡀","⠀","⠀","⠀"],["⠀","⠀","⠸","⣭","⣿","⠇","⠀","⠀"],["⠀","⠀","⠀","⠸","⠇","⠀","⠀","⠀"],["⠀","⠀","⠀","⢴","⡦","⠀","⠀","⠀"]] 2 | -------------------------------------------------------------------------------- /examples/sprites/warn2.spr: -------------------------------------------------------------------------------- 1 | [["⠀","⠀","⠀","⢀","⡀","⠀","⠀","⠀"],["⠀","⠀","⠸","⣭","⣿","⠇","⠀","⠀"],["⠀","⠀","⠀","⠸","⠇","⠀","⠀","⠀"],["⠀","⠀","⠀","⢴","⡦","⠀","⠀","⠀"]] 2 | -------------------------------------------------------------------------------- /examples/surmaphoto.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/saiftynet/Term-Graille/c467644004a72a34c607ebdf6d8ec4c25afbac70/examples/surmaphoto.png -------------------------------------------------------------------------------- /examples/testanimate.pl: -------------------------------------------------------------------------------- 1 | #! /usr/bin/env perl 2 | use strict; use warnings; 3 | #use lib "../lib/"; 4 | use Term::Graille qw/colour paint printAt clearScreen border/; 5 | use Image::Magick; 6 | use Time::HiRes "sleep"; 7 | 8 | # convert2grl--converts an image to .grv format. 9 | 10 | my $animationFolder=$ARGV[0]||"./animations/cheetah/"; 11 | opendir my $dir,$animationFolder or die; 12 | my @files=sort grep !/^(\.|\.\.|.*\.pl)$/,readdir $dir; 13 | 14 | my $canvas = Term::Graille->new( 15 | width => 120, 16 | height => 50, 17 | top=>4, 18 | left=>10, 19 | borderStyle => "double", 20 | borderColour => "yellow", 21 | title=>" Animation test ", 22 | ); 23 | my $animation=[]; 24 | 25 | my $frame=0; 26 | for my $image (@files){ 27 | $canvas->clear(); 28 | print "Loading image ",$animationFolder.$image," ",++$frame,"\n"; 29 | $animation->[$frame]=[]; 30 | loadImage($animationFolder.$image); 31 | #loadImage("./animations/chimp/chimp$frame.png"); 32 | foreach my $row (0..$#{$canvas->{grid}}){ 33 | foreach my $col(0..$#{$canvas->{grid}->[0]}){ 34 | $animation->[$frame]->[$row]->[$col]=$canvas->{grid}->[$row]->[$col]; 35 | } 36 | } 37 | } 38 | 39 | sleep 1; 40 | clearScreen(); 41 | 42 | 43 | foreach (0..100){ 44 | foreach my $frame (1..@$animation){ 45 | next unless ref $animation->[$frame]; 46 | $canvas->{grid}=$animation->[$frame]; 47 | $canvas->draw(); 48 | sleep 0.08; 49 | } 50 | } 51 | 52 | sub loadImage{ 53 | my $imgfile=shift; 54 | my $image = Image::Magick->new(); 55 | my $wh = $image->Read($imgfile); 56 | $image->Resize(geometry => "$canvas->{width}x$canvas->{height}"); 57 | die $wh if $wh; 58 | $image->set('monochrome'=>'True'); 59 | $image->Negate(); 60 | for (my $x =0;$x<$canvas->{width};$x++){ 61 | for (my $y=0;$y<$canvas->{height};$y++){ 62 | $canvas->set($x,$y) if $image->GetPixel("x"=>$x,"y",$canvas->{height}-$y); 63 | } 64 | } 65 | } 66 | 67 | -------------------------------------------------------------------------------- /examples/turtle.pl: -------------------------------------------------------------------------------- 1 | #! /usr/bin/env perl 2 | use strict; use warnings; 3 | use lib "../lib/"; 4 | use Term::Graille qw/colour paint printAt clearScreen border/; 5 | use Time::HiRes "sleep"; 6 | 7 | my $canvas = Term::Graille->new( 8 | width => 100, 9 | height => 64, 10 | top=>3, 11 | left=>10, 12 | title=>"Interactive Turtle Demo", 13 | borderStyle => "double", 14 | ); 15 | my $q="";my @commands; 16 | while ($q ne "quit"){ 17 | drawTurtle(); 18 | $canvas->draw(); 19 | $q=prompt(); 20 | push @commands, $q; 21 | clearScreen(); 22 | $canvas->clear(); 23 | $canvas->logo("ce;dir 0;"); 24 | $canvas->logo($_) foreach (@commands); 25 | 26 | printAt (5,70,[@commands]) 27 | } 28 | 29 | 30 | sub drawTurtle{ 31 | $canvas->logo("bk 2;lt 90;pu;fd 3;pd; pc white;rt 120;fd 6;rt 120;fd 6;rt 120;pu;fd 3;rt 90;pd"); 32 | } 33 | 34 | 35 | sub prompt{ 36 | printAt(20,0,""); 37 | print">>"; 38 | $_=<STDIN>; 39 | chomp; 40 | return $_; 41 | } 42 | -------------------------------------------------------------------------------- /examples/visualiser.pl: -------------------------------------------------------------------------------- 1 | #!/usr/env perl 2 | ########## Audio Analyser ########### 3 | use strict; use warnings; 4 | use lib "../lib/"; 5 | 6 | use Term::Graille qw/colour paint printAt cursorAt clearScreen border blockBlit block2braille pixelAt/; 7 | use Term::Graille::Interact; 8 | use Term::Graille::Menu; 9 | use Term::Graille::Font qw/loadGrf fontWrite/; 10 | 11 | my $io=Term::Graille::Interact->new(); # For interactions menu and keyboard 12 | $io->{debugCursor}=[22,45]; # Put key presses detected ny Interact on the screen 13 | 14 | my ($source,$recorder,$fh,$buffer,@data); 15 | 16 | my $canvas = Term::Graille->new( 17 | width => 120, 18 | height => 60, 19 | top=>4, 20 | left=>10, 21 | borderStyle => "double", 22 | borderColour => "blue", 23 | title => "Audio-Analyser", 24 | titleColour => "red", 25 | ); 26 | 27 | # draw the initial screen 28 | my $grf=loadGrf("./fonts/ZX Times.grf"); 29 | fontWrite($canvas,$grf,11,13,"Mic Input"); 30 | fontWrite($canvas,$grf,13,10,"Waveform"); 31 | fontWrite($canvas,$grf,13,7,"Analyser"); 32 | $canvas->textAt(15,20,"A not-so-realtime visual audio analyser for","green italic"); 33 | $canvas->textAt(40,16,"Microphone inputs","yellow italic"); 34 | $canvas->textAt(16,8," "); 35 | $canvas->textAt(24,4," "); 36 | 37 | my @listCaptures=`arecord -l`; 38 | my %devices=map{ /card (\d+):[^\[]+(\[[^\]]+\]), device (\d+)/ ;$2 =>"hw:$1,$3" } 39 | grep /card (\d+):[^\[]+(\[[^\]]+\]), device (\d+)/ , @listCaptures; 40 | 41 | my $menu=new Term::Graille::Menu( 42 | menu=>[["Monitor","Start","Stop"], 43 | ["Source",keys %devices], 44 | ["Play","Raw",["Filters","HiPass","LowPass"]], 45 | "About"], 46 | redraw=>\&refreshScreen, 47 | dispatcher=>\&menuActions, 48 | ); 49 | 50 | my %actions=( 51 | Start=>sub{ 52 | $io->{refreshRate}=1; 53 | $io->updateAction(sub {recordDisplay() } ); 54 | }, 55 | Stop=>sub{ 56 | $io->{refreshRate}=20; 57 | close $fh if $fh;; 58 | kill $recorder if $recorder; 59 | $io->stopUpdates(); 60 | }, 61 | ); 62 | 63 | sub recordDisplay{ 64 | return unless $source; 65 | my ($cmd,$rawData); 66 | 67 | # using arecord...not sure in arecord can record fractional seconds 68 | $cmd = "arecord -D $source -c 1 -d 1 -q"; 69 | $rawData = ''; 70 | $recorder = open ($fh, '-|:raw', $cmd )or die "Couldn't launch [$cmd] $!"; 71 | 72 | while (1) { 73 | my $success = read $fh, $rawData , 100, length($rawData); 74 | die $! if not defined $success; 75 | last if not $success; 76 | } 77 | 78 | @data= unpack 'C*', $rawData ; 79 | close $fh; 80 | kill $recorder; 81 | printAt(2,60,scalar @data); 82 | #$canvas->clear(); 83 | $canvas->scroll("l",undef,10); 84 | for my $pt (40..$#data){ 85 | next unless $pt%2; 86 | my $d=$data[$pt]; 87 | my $c=(qw/white cyan blue green yellow yellow magenta red/)[abs ($d-128)/16]; 88 | my $l=60*$d/2**8;$l+=$l>30?-30:30; 89 | $canvas->set(99+(20*$pt/(@data-40)),$l,colour($c)); 90 | } 91 | $canvas->draw(); 92 | } 93 | 94 | 95 | foreach my $dev(keys %devices){ 96 | $actions{$dev}=sub{ 97 | kill $recorder if $recorder; 98 | $recorder=undef; 99 | $source=$devices{$dev}; 100 | } 101 | } 102 | 103 | sub menuActions{ 104 | my ($action)=@_; 105 | if (exists $actions{$action}){ 106 | $actions{$action}->($io->{gV}) 107 | } 108 | }; 109 | 110 | $io->addObject(object => $menu,trigger=>"m"); 111 | $canvas->draw(); 112 | $io->run(); 113 | 114 | sub refreshScreen{ 115 | clearScreen() unless (shift); 116 | $canvas->draw(); 117 | } 118 | -------------------------------------------------------------------------------- /lib/Term/Graille/Chart.pm: -------------------------------------------------------------------------------- 1 | package Term::Graille::Chart; 2 | use strict; use warnings; 3 | use utf8; 4 | use Term::Graille qw/colour paint printAt clearScreen border blockBlit block2braille pixelAt/; 5 | use open ":std", ":encoding(UTF-8)"; 6 | 7 | our $VERSION="0.09"; 8 | 9 | my $chart=new Term::Graille::Chart( 10 | hOffset=>10, 11 | BottomAxis=>{tickSeparation=>7,tickLabels=>[" ",qw/ red green yellow violet indigo blue/," "]}, 12 | LeftAxis =>{tickSeparation=>2,tickLabels=>[0..5]}, 13 | RightAxis =>{tickSeparation=>3,tickLabels=>[0..3]}, 14 | TopAxis=>{tickSeparation=>7,tickLabels=>[" ",qw/ red green yellow violet blue/," "]}, 15 | ); 16 | print "\n\n", join("\n",@{$chart->mergeAxes()}); 17 | 18 | sub new{ 19 | my ($class, %params) = @_; 20 | my $self={}; 21 | $self->{hOffset}=$params{hOffset}//6; 22 | $self->{vOffset}=$params{vOffset}//6; 23 | $self->{Axes}={}; 24 | bless $self,$class; 25 | foreach my $side (qw/BottomAxis TopAxis LeftAxis RightAxis/){ 26 | next unless $params{$side}; 27 | $self->{Axes}->{$side}->{params}=$params{$side};; 28 | $self->makeAxis($side) ; 29 | } 30 | return $self; 31 | 32 | } 33 | 34 | sub makeAxis{ 35 | my ($self,$side)=@_; 36 | my $params=$self->{Axes}->{$side}->{params}; 37 | my $tickSeparation=$$params{tickSeparation}; 38 | my $tickLabels=$$params{tickLabels}; 39 | my $title=$$params{title}; 40 | my @axis=(); 41 | if ($side=~ /BottomAxis|TopAxis/){ 42 | my $gap="─"x( $tickSeparation-1); 43 | my $axis=(" "x$self->{hOffset})."┼".(($gap."┼")x(@{$tickLabels}-2)).$gap."┤"; 44 | my $axisWidth=$tickSeparation*$#{$tickLabels}+1; 45 | $self->{chartWidth}=$axisWidth if (!$self->{chartWidth} || $axisWidth >$self->{chartWidth}); 46 | my $tickLabelLine=(" "x$self->{hOffset}); 47 | foreach my $tick (0..$#{$tickLabels}){ 48 | substr ($tickLabelLine, ($self->{hOffset}+$tick*$tickSeparation+1)-length($tickLabels->[$tick])/2,)=$tickLabels->[$tick]; 49 | $tickLabelLine.=" "x($tickSeparation-1); 50 | } 51 | $self->{$side}=$side=~/^T/?[$tickLabelLine,$axis]:[$axis,$tickLabelLine]; 52 | 53 | } 54 | else{ 55 | 56 | my $axisHeight=$tickSeparation*$#{$tickLabels}+1; 57 | $self->{chartHeight}=$axisHeight if (!$self->{chartHeight} || $axisHeight >$self->{chartHeight}); 58 | if ($side=~/^L/){ 59 | my @gap=(" "x$self->{hOffset}."│")x($tickSeparation-1); 60 | foreach my $tick (0..$#{$tickLabels}){ 61 | my $row=" "x$self->{hOffset}."│"; 62 | substr ($row,$self->{hOffset}-(1+length $tickLabels->[$tick]),)=$tickLabels->[$tick]." ┼"; 63 | unshift @axis,@gap if $tick; 64 | unshift @axis,$row; 65 | } 66 | } 67 | else{ 68 | my @gap=("│")x($tickSeparation-1); 69 | foreach my $tick (0..$#{$tickLabels}){ 70 | my $row="┼ ".$tickLabels->[$tick]; 71 | unshift @axis,@gap if $tick; 72 | unshift @axis,$row; 73 | } 74 | } 75 | 76 | $self->{$side}=[@axis] 77 | } 78 | } 79 | 80 | sub mergeAxes{ 81 | my $self=shift; 82 | my @Axes; 83 | if (exists $self->{LeftAxis} && exists $self->{BottomAxis} ){ 84 | @Axes=@{$self->{LeftAxis}}; 85 | my $bA=$self->{BottomAxis}->[0]; 86 | $bA=~s/^ +//; 87 | substr ($Axes[-1],-1,)= $bA; 88 | push @Axes,$self->{BottomAxis}->[1]; 89 | } 90 | else { 91 | return ["unable to create axes"]; 92 | } 93 | if (exists $self->{RightAxis}){ 94 | my @rA=@{$self->{RightAxis}}; 95 | substr ($Axes[-2],-1,)= $rA[-1]; 96 | for (my $i=2;$i<=@rA;$i++){ 97 | $Axes[@Axes-$i-1].=(" "x($self->{chartWidth}-2)).$rA[@rA-$i]; 98 | } 99 | } 100 | if (exists $self->{TopAxis}){ 101 | my $tA=$self->{TopAxis}->[1]; 102 | $tA=~s/^ +//; 103 | substr ($Axes[0],$self->{hOffset},$self->{chartWidth})= $tA; 104 | unshift @Axes,$self->{TopAxis}->[0]; 105 | 106 | } 107 | return [@Axes]; 108 | } 109 | 110 | sub autoAxis{ 111 | my ($self,$side,$data,$nTicks,$axisLength)=@_; 112 | 113 | 114 | } 115 | 116 | 117 | 118 | 119 | 120 | # given a list of numbers determines whether the numbers cross zero; 121 | sub zeroCrossing{ 122 | my @list=sort @_; 123 | return ($list[0] <0 && $list[-1] >0)?1:0; 124 | } 125 | 126 | #given a list of data (as an array or arrayref, return minimum and maximum) 127 | sub minmax{ 128 | my @list=@_; 129 | @list=@{$list[0]} if (ref $list[0]); 130 | my $max=my $min = shift @list;; 131 | foreach my $v (@list){ 132 | $max=$v if $max<$v; 133 | $min=$v if $min>$v; 134 | } 135 | return ($min,$max) 136 | } 137 | 138 | # niceTicks 139 | # takes minimum value, maximum value and number of desired ticks; 140 | # returns list of ticks 141 | sub niceTicks{ 142 | my ($axisStart,$axisEnd,$nTicks)=@_; 143 | ($axisStart,$axisEnd,my $tick)=niceAxis($axisStart,$axisEnd,$nTicks); 144 | my @tickList=($axisStart); 145 | push @tickList,$tickList[-1]+$tick while ($tickList[-1]<=$axisEnd); 146 | return @tickList; 147 | } 148 | 149 | # niceAxis 150 | # takes minimum value, maximum value and number of desired ticks; 151 | # returns nice axis start, axis end and gap between ticks 152 | sub niceAxis{ 153 | my ($axisStart,$axisEnd,$nTicks)=@_; 154 | $nTicks//=5; 155 | my $axisWidth=$axisEnd-$axisStart; 156 | return undef unless $axisWidth>0; # error is start >= end 157 | 158 | my $niceRange=niceNumber($axisWidth,0); 159 | my $niceTick =niceNumber($niceRange/($nTicks-1) , 1); 160 | $axisStart=floor($axisStart/$niceTick)*$niceTick; 161 | $axisEnd = ceil($axisEnd/$niceTick)*$niceTick; 162 | return ($axisStart,$axisEnd,$niceTick) 163 | } 164 | 165 | # 166 | sub niceNumber{ 167 | my ($value,$round)=@_; 168 | 169 | my $exp= floor(log($value)/log(10)); 170 | my $fraction=$value/(10**$exp); 171 | 172 | if ($round){ 173 | for ($fraction){ 174 | $_ < 1.5 && return 1*(10**$exp); 175 | $_ < 3.0 && return 2*(10**$exp); 176 | $_ < 7.0 && return 5*(10**$exp); 177 | return 10*(10**$exp); 178 | } 179 | } 180 | else{ 181 | for ($fraction){ 182 | $_ < 1 && return 1*(10**$exp); 183 | $_ < 2 && return 2*(10**$exp); 184 | $_ < 4 && return 4*(10**$exp); 185 | return 10*(10**$exp); 186 | } 187 | } 188 | } 189 | 190 | 191 | # saves having to use POSIX 192 | sub floor{ 193 | my $v=shift; 194 | return int($v)-($v<0?1:0); 195 | } 196 | 197 | # saves having to use POSIX 198 | sub ceil{ 199 | my $v=shift; 200 | return int($v)+ ($v==int($v)?0:1); 201 | } 202 | 203 | 204 | 205 | 206 | 1; 207 | 208 | 209 | -------------------------------------------------------------------------------- /lib/Term/Graille/Dialog.pm: -------------------------------------------------------------------------------- 1 | package Term::Graille::Dialog; 2 | 3 | use strict;use warnings; 4 | use lib "../../../lib"; 5 | use Term::Graille qw/colour printAt clearScreen border cursorAt wrapText/; 6 | use base 'Exporter'; 7 | our @EXPORT_OK = qw/deathSentence confirm prompt caution/; 8 | use utf8; 9 | 10 | our $VERSION=0.10; 11 | 12 | =head3 C<my $dialog=Term::Graille::Dialog-E<gt>new(%params)> 13 | 14 | Creates a new dialog box; params are 15 | C<options> the possible options that may be selected 16 | C<redraw> This is a function to redraws the application screen. 17 | The dialog box may overwrite parts of the application screen, and this 18 | function needs to be provided to restore the screen. 19 | C<callback> The dialog does not call any functions itself, instead returns the 20 | button value pressed. if the $dialog->{entry} is set this is also sent. In 21 | prompts, the entry is populated by user. It is upto the main application 22 | to use this data (the callback function supplied) 23 | C<pos> Optional. The default position is [2,2], but setting this parameter allows 24 | the dialog to be placed elsewhere 25 | C<highlightColour> Optional. The selected item is highlighted default "black on_white" 26 | C<normalColour> Optional. The normal colour of text items "white on_black" 27 | 28 | 29 | =cut 30 | 31 | my %icons=( 32 | die=> [[colour("red")."⢰","⠪","⣛","⠍","⠩","⣛","⠝","⡆"], 33 | ["⠈","⠑","⢤","⠰","⠆","⡤","⠋","⠀"], 34 | ["⠛","⢧","⣀","⠉","⢉","⣡","⠞","⠛"], 35 | ["⣤","⡴","⠚","⠋","⠙","⠓","⢤","⣤".colour("reset")]], 36 | info=> [[colour("blue")."⠀","⠐","⠿","⠿","⠂","⠀","⠀","⠀"], 37 | ["⠀","⠠","⢶","⣶","⠀","⠀","⠀","⠀"], 38 | ["⠀","⠀","⢸","⣿","⠀","⠀","⠀","⠀"], 39 | ["⠀","⠀","⠘","⢿","⣤","⠼","⠃","⠀".colour("reset")]], 40 | error=> [[colour("yellow")."⠀","⠀","⠀","⢀","⡀","⠀","⠀","⠀"], 41 | ["⠀","⠀","⠸","⣭","⣿","⠇","⠀","⠀"], 42 | ["⠀","⠀","⠀","⠸","⠇","⠀","⠀","⠀"], 43 | ["⠀","⠀","⠀","⢴","⡦","⠀","⠀","⠀".colour("reset")]], 44 | forbidden=>[[colour("red")."⠀","⠀","⢀","⣀","⣀","⡀","⠀","⠀"], 45 | ["⢠","⡞","⣉","⣀","⣀","⣉","⢳","⡄"], 46 | ["⠘","⢧","⣉","⠉","⠉","⣉","⡼","⠃"], 47 | ["⠀","⠀","⠈","⠉","⠉","⠁","⠀","⠀".colour("reset")]], 48 | query=>[[colour("green")."⠀","⠀","⠀","⣀","⡀","⠀","⠀","⠀"], 49 | ["⠀","⠀","⠾","⠉","⠉","⣷","⠀","⠀"], 50 | ["⠀","⠀","⠀","⢠","⡞","⠉","⠀","⠀"], 51 | ["⠀","⠀","⠀","⢨","⡅","⠀","⠀","⠀".colour("reset")]], 52 | ); 53 | 54 | 55 | sub new{ 56 | my ($class,%params) = @_; 57 | my $self={}; 58 | bless $self,$class; 59 | 60 | $self->{buttons}=$params{buttons}//[]; 61 | $self->{selected}=$params{selected}// ""; #if a button is tob pres elected 62 | 63 | $self->{message}=$params{message}// "No Message"; 64 | 65 | $self->{redraw}=$params{redraw} if (exists $params{redraw}); # function to redraw application 66 | $self->{callback}=$params{callback} if (exists $params{callback}); # function to call after menu item selected 67 | $self->{transient}=$params{transient}//1; 68 | $self->{title}=$params{title}//"Dialog"; 69 | $self->{normalColour}=$params{titleColour}//"yellow"; 70 | $self->{pos}=$params{pos}//[2,2]; 71 | $self->{geometry}=$params{geometry}//[8,40]; 72 | $self->{param}=$params{param}; # optional parameter 73 | $self->{icon}=$params{icon}; 74 | $self->{highlightColour}=$params{highlightColour}//"black on_white"; 75 | $self->{normalColour}=$params{normalColour}//"white on_black"; 76 | $self->{keyAction}={ 77 | "[D" =>sub{$self->prevItem()}, 78 | "[C" =>sub{$self->nextItem()}, 79 | "enter"=>sub{$self->selectItem()}, 80 | "esc" =>sub{$self->{close}->()}, 81 | others =>sub{my ($obj,$pressed,$gV)=@_;$self->addChar($pressed)}, 82 | }; 83 | return $self; 84 | } 85 | 86 | sub mode{ 87 | my ($self,$mode,@etc)=@_; 88 | for ($mode){ 89 | /okc/i && do {$self->{buttons}=[qw/OK Cancel/]; last;}; 90 | /ok/i && do {$self->{buttons}=[qw/OK/]; last;}; 91 | /ync/i && do {$self->{buttons}=[qw/Yes No Cancel/]; last;}; 92 | /input/i && do {$self->{buttons}=[qw/Submit Cancel/]; 93 | $self->{entry}="";$self->{entryPos}=0; last}; 94 | 95 | } 96 | 97 | } 98 | 99 | sub draw{ 100 | my $self=shift; 101 | if (!ref $self->{message}){ 102 | $self->{message}=wrapText( $self->{message},$self->{geometry}->[1]-15); 103 | } 104 | my $entryLines=(defined $self->{entry})?2:0; 105 | my $buttons=""; 106 | foreach my $button (@{$self->{buttons}}){ 107 | $buttons.=" ".colour($self->{selected} && $self->{selected} eq $button?"black on_white":"white")."[$button]".colour("reset"); 108 | } 109 | border(@{$self->{pos}},$self->{pos}->[0]+max(($self->{icon}?6:0),@{$self->{message}}+$entryLines+1), 110 | $self->{geometry}->[1]+$self->{pos}->[1], 111 | "double","green", 112 | $self->{title},$self->{titleColour}); 113 | printAt($self->{pos}->[0]+1,6,[@{$self->{message}},($entryLines?($self->{entry},"-"x($self->{geometry}->[1]-4)):()),$buttons]); 114 | 115 | printAt($self->{pos}->[0]+2,$self->{geometry}->[1]-5,$icons{$self->{icon}}) if $self->{icon}; 116 | 117 | } 118 | 119 | 120 | sub addChar{ 121 | my ($self,$ch)=@_; 122 | return unless exists $self->{entry}; 123 | $self->{entry}.=$ch if (length $ch ==1 ); 124 | chop $self->{entry} if ($ch =~/back/ ); 125 | $self->draw(); 126 | 127 | } 128 | 129 | sub selectedPos{ 130 | my ($self)=@_; 131 | return 0 unless $self->{selected}; 132 | for my $p (0..$#{$self->{buttons}}){ 133 | return $p if ($self->{selected} eq $self->{buttons}->[$p]) 134 | } 135 | return 0; 136 | } 137 | 138 | sub nextItem{ 139 | my ($self)=@_; 140 | my $p=$self->selectedPos(); 141 | $p+=($p<$#{$self->{buttons}})?1:0; 142 | $self->{selected}= $self->{buttons}->[$p]; 143 | #printAt(2,50, " $p $self->{selected} ".$#{$self->{buttons}}." ".join("--",@{$self->{buttons}})); 144 | $self->draw(); 145 | } 146 | 147 | sub prevItem{ 148 | my ($self)=@_; 149 | my $p=$self->selectedPos(); 150 | $self->{selected}= $self->{buttons}->[$p-($p>0)?1:0]; 151 | #printAt(2,50, " $p $self->{selected} ".$#{$self->{buttons}}." ".join("--",@{$self->{buttons}})); 152 | $self->draw(); 153 | } 154 | 155 | sub selectItem{ 156 | my ($self)=@_; 157 | $self->{param}->{button}=$self->{selected}; 158 | $self->{param}->{entry}=$self->{entry}; 159 | if (exists $self->{callback}) {$self->{callback}->($self->{param})}; 160 | } 161 | 162 | 163 | sub close{ # what needs to be done before Interact de-activates widget 164 | my ($self)=@_; 165 | $self->{redraw}->(); 166 | } 167 | 168 | sub max{ 169 | return (sort{$a <=> $b}@_)[-1] 170 | } 171 | 172 | sub min{ 173 | return (sort{$a <=> $b}@_)[0] 174 | } 175 | 176 | sub prompt{ 177 | 178 | } 179 | 180 | 181 | sub deathSentence{ # waiting to be exported 182 | my $str=shift; 183 | my @epitaphs=( 184 | "'Forgive him Larry, for he knows not how to code'", 185 | "'I told you I was sick'", 186 | "'It is a far far better code that I do, than you have ever run'", 187 | "'Your references have been counted and found wanting'", 188 | "'Goodbye old friend, may the Schwartz be with you'", 189 | "'she believed she could, but she couldn\'t'", 190 | ); 191 | my $message=[@{wrapText(colour("red").$str.colour("reset"),45)},caller(), 192 | @{wrapText(colour("yellow").($epitaphs[rand(@epitaphs)]).colour("reset"),45)}]; 193 | 194 | border(10,4,11+@$message,64,"double","green","RIP","yellow"); 195 | printAt(11,6,$message); 196 | printAt(12,55,$icons{die}); 197 | 198 | printAt(18,0,""); 199 | sleep 10; 200 | exit; 201 | } 202 | 203 | sub fileSelector{ 204 | my ($dir,$filter,$callback,$redraw,$parameter)=@_; 205 | 206 | 207 | 208 | } 209 | 210 | 211 | 1; 212 | -------------------------------------------------------------------------------- /lib/Term/Graille/Font.pm: -------------------------------------------------------------------------------- 1 | package Term::Graille::Font; 2 | use strict; use warnings; 3 | use utf8; 4 | use Data::Dumper; 5 | use Term::Graille qw/block2braille/; 6 | use base 'Exporter'; 7 | our @EXPORT_OK = qw/convertDG saveGrf loadGrf fontWrite/; 8 | use open ":std", ":encoding(UTF-8)"; 9 | 10 | our $VERSION="0.09"; 11 | 12 | 13 | =head1 NAME 14 | 15 | Term::Graille::Font - convert and use ZX Spectrum fonts in Term::Graille 16 | 17 | =head1 SYNOPSIS 18 | 19 | use Term::Graille::Font qw/convertDG saveGrf fontWrite/; 20 | my $grf=convertDG("$fontName.z80.asm"); 21 | $canvas->blockBlit($grf->{A},$gridX,$gridY); # old method single character 22 | fontWrite($canvas,$grf,$gridX,$gridY,$text); # new method text string 23 | saveGrf($grf,"$fontName.grf"); 24 | 25 | =head1 DESCRIPTION 26 | 27 | This allows the creation, saving and usage of Braille Character to form 28 | Bitmap 8x8 font. It currently only accepts DameinGuards z80.asm files 29 | as found in damieng.com/typography/zx-origins/ where there a couple of 30 | hundred fonts available. 31 | 32 | =begin html 33 | 34 | <img src="https://user-images.githubusercontent.com/34284663/179078012-69f9f535-8d41-46b0-ba68-0a5dbe613cd9.gif"> 35 | 36 | =end html 37 | 38 | 39 | =head1 FUNCTIONS 40 | 41 | =cut 42 | 43 | =head3 C<my $grf=convertDG("$fontName.z80.asm");> 44 | 45 | Reads an ASM file as on Damien Guard's typography pages and returns a hashref 46 | containing the extracted 8x8 fonts converted into 4x2 Braille characters. 47 | 48 | =cut 49 | 50 | sub convertDG{ 51 | my $file=shift; 52 | my $font=""; 53 | open my $zxFont,"<:utf8",$file or die "Unable to open fontfile $file $!;\n"; 54 | $font.=$_ while(<$zxFont>); 55 | close $zxFont; 56 | 57 | $font=~s/^[\s\t]?;([^\n]+)/{/g; 58 | my $info=$1; 59 | $font=~s/defb &/[[0x/g; 60 | $font=~s/,&/],[0x/g; 61 | $font=~s/ ; /]],# /g; 62 | $font=~s/\s([^#\s]*)#\s(.)/ '$2'=>$1/g; 63 | $font=~s/\'\'\'/\"\'\"/g; 64 | $font=~s/\'\\\'/\'\\\\\'/g; 65 | $font.="\n}\n"; 66 | # print $font; 67 | my $binFont=eval($font); 68 | my $grlFont={}; 69 | for (keys %$binFont){ 70 | use utf8; 71 | $grlFont->{$_}=block2braille($binFont->{$_}) ; 72 | } 73 | $grlFont->{info}=$info||""; 74 | return $grlFont; 75 | } 76 | 77 | =head3 C<saveGrf($font,$file);> 78 | 79 | Uses Data::Dumper to produce convert a font hashref into a file that can 80 | be read using loadGrf 81 | 82 | =cut 83 | 84 | sub saveGrf{ 85 | my ($grf,$file)=@_; 86 | my $output=Dumper([$grf]); 87 | $output=~ s/\\x\{([0-9a-f]{2,})\}/chr hex $1/ge; 88 | $output=~s/^\$VAR1 = \[\n\s+| \];\n?$//g; 89 | $output=~s/\[\n\s+\[/\[\[/g; 90 | $output=~s/\n\s+([^\s])/$1/g; 91 | $output=~s/\]\],/\]\],\n/g; 92 | die "Conversion failed\n" if (length $output <100); 93 | open my $dat,">:utf8","$file" or die "Unable to save fontfile $file $!;\n"; 94 | print $dat $output; 95 | close $dat; 96 | } 97 | 98 | 99 | =head3 C<my $font=loadGrf($file);> 100 | 101 | Loads a font hashref from a file. The hashRef indexes 4x2 braille character 102 | blocks by character they represent; This may be used to represent any kind of 103 | character block, including tilemaps, sprites etc. 104 | 105 | =cut 106 | 107 | #loads a hashfile representing a 2D Array ref blocks of data 108 | #can be used to load fonts or other graphical blocks 109 | sub loadGrf{ 110 | my $file=shift; 111 | open my $grf,"<:utf8",$file or 112 | die "Unable to open file $file $!;\n" ; 113 | my $data=""; 114 | $data.=$_ while(<$grf>); 115 | close $grf; 116 | my $g=eval($data) or die "unable to load external data from $file $!";; 117 | return $g; 118 | } 119 | 120 | 121 | =head3 C<$fontWrite($canvas,$font,$gridX,$gridY,$text);> 122 | 123 | Writes a C<$text> string using a loaded font into a particular grid 124 | location in a C<$canvas> 125 | 126 | =cut 127 | 128 | sub fontWrite{ 129 | my ($canvas,$font,$gridX,$gridY,$text)=@_; 130 | for my $chPos(0..(length $text)-1){ 131 | $canvas->blockBlit($font->{substr($text,$chPos, 1)},$gridX+4*$chPos, $gridY) 132 | } 133 | } 134 | 135 | 1; 136 | -------------------------------------------------------------------------------- /lib/Term/Graille/Menu.pm: -------------------------------------------------------------------------------- 1 | =head1 NAME 2 | Term::Graille::Menu 3 | 4 | Modal hierarchical Menu system 5 | 6 | =head1 SYNOPSIS 7 | 8 | use Term::Graille::Interact; 9 | use Term::Graille::Menu ; # provides Term::Graille::Menu 10 | 11 | my $menu=new Term::Graille::Menu( 12 | menu=>[["File","New","Save","Load","Quit"], 13 | ["Edit","Clear",["Reformat","2x4","4x4"],["Scroll","left","right","up","down"]], 14 | "About"], 15 | redraw=>\&main::refreshScreen, 16 | dispatcher=>\&main::menuActions, 17 | ); 18 | 19 | 20 | =head1 DESCRIPTION 21 | 22 | Developed to allow user interaction using a hierarchical menu in command line 23 | applications. The menu is activated using a key press, and navigated 24 | typically using arrow keys. It does not handle or capture the key presses 25 | directly, and in Graille is used in conjunction with Term::Graille::Interact 26 | 27 | 28 | =begin html 29 | 30 | <img src="https://user-images.githubusercontent.com/34284663/185751328-f5b67fa4-c77d-40b0-ac3a-0c6c93239fae.gif"> 31 | 32 | =end html 33 | 34 | 35 | =head1 FUNCTIONS 36 | 37 | =cut 38 | 39 | package Term::Graille::Menu; 40 | 41 | use strict;use warnings; 42 | use Storable qw(dclone); 43 | use Term::Graille qw/colour printAt clearScreen/; 44 | use utf8; 45 | 46 | our $VERSION=0.11; 47 | 48 | =head3 C<my $menu=Term::Graille::Menu-E<gt>new(%params)> 49 | 50 | Creates a new $menu; params are 51 | C<menu> The menu tree as an Array ref containing strings and arrayrefs. 52 | Branches are Array refs, and end nodes are strings. See above example to 53 | visualise structure. 54 | C<redraw> This is a function that needs to be supplied to redraw the 55 | application screen. The menu will overwrite parts of the application screen, 56 | and this function needs to be provided to restore the screen. 57 | C<dispatcher> The menu does not call any functions, instead returns the 58 | leaf string selected. It is upto the main application to use this string to 59 | in a dispatch routine (the dispatcher function supplied) 60 | C<pos> Optional. The default position is [2,2], but setting this parameter allows 61 | the menu to be placed elsewhere 62 | C<highlightColour> Optional. The selected item is highlighted default "black on_white" 63 | C<normalColour> Optional. The normal colour of menu items "white on_black" 64 | 65 | 66 | =cut 67 | 68 | 69 | sub new{ 70 | my ($class,%params) = @_; 71 | my $self={}; 72 | bless $self,$class; 73 | $self->{menu}=$params{menu}//[]; 74 | $self->{redraw}=$params{redraw} if (exists $params{redraw}); # function to redraw application 75 | $self->{dispatcher}=$params{dispatcher} if (exists $params{dispatcher}); # function to call after menu item selected 76 | $self->{breadCrumbs}=[0]; 77 | $self->{pos}=$params{pos}//[2,2]; 78 | $self->{highlightColour}=$params{highlightColour}//"black on_white"; 79 | $self->{normalColour}=$params{normalColour}//"white on_black"; 80 | $self->{keyAction}={ 81 | "[A" =>sub{$self->upArrow()}, 82 | "[B" =>sub{$self->downArrow()}, 83 | "[C" =>sub{$self->rightArrow()}, 84 | "[D" =>sub{$self->leftArrow()}, 85 | "enter"=>sub{$self->openItem()}, 86 | "esc"=>sub{$self->{close}->()}, 87 | }; 88 | return $self; 89 | } 90 | 91 | 92 | =head3 C<$menu-E<gt>setMenu($menu,$reset)> 93 | 94 | Changes the menu. if reset is set then the menu "pointer" is set at the first item 95 | in menmu tree. 96 | 97 | =cut 98 | 99 | sub setMenu{ 100 | my ($self,$menu,$reset)=@_; 101 | $self->{menu}=$menu; 102 | $self->{breadCrumbs}=[0] if $reset; 103 | } 104 | 105 | 106 | =head3 C<$menu-E<gt>redraw()> 107 | 108 | Calls the application' s redraw function. This is required for the menu 109 | to be overwritten with application screen. 110 | 111 | =cut 112 | 113 | sub redraw{ 114 | my $self=shift; 115 | $self->{redraw}->() if (exists $self->{redraw}); 116 | 117 | } 118 | 119 | =head3 C<$menu-E<gt>nextItem()>, C<my $menu-E<gt>prevItem()>, 120 | C<my $menu-E<gt>closeItem()>, C<my $menu-E<gt>openItem()> 121 | 122 | Navigate the menu, select items. 123 | 124 | =cut 125 | 126 | sub nextItem{ 127 | my $self=shift; 128 | $self->{breadCrumbs}->[-1]++ ; 129 | $self->{breadCrumbs}->[-1]-- if ($self->drillDown() == 0); 130 | $self->draw(); 131 | } 132 | 133 | sub prevItem{ 134 | my $self=shift; 135 | $self->{breadCrumbs}->[-1]-- unless $self->{breadCrumbs}->[-1]==0; 136 | $self->draw(); 137 | } 138 | 139 | sub closeItem{ 140 | my $self=shift; 141 | if ($self->depth()>1){ 142 | pop @{$self->{breadCrumbs}}; 143 | $self->draw(); 144 | } 145 | else{ # if at top level close menu; 146 | $self->{close}->(); 147 | } 148 | } 149 | 150 | sub close{ 151 | my $self=shift; 152 | $self->{breadCrumbs}=[0]; 153 | $self->redraw(); 154 | 155 | } 156 | 157 | sub openItem{# enter submemnu if one exists, or "open" the item; 158 | my $self=shift; 159 | my ($label,$submenu)=@{$self->drillDown()}; 160 | if ($submenu) { 161 | $self->{breadCrumbs}=[@{$self->{breadCrumbs}},0]; 162 | $self->draw(); 163 | } 164 | else{ 165 | my $bc=$self->{breadCrumbs}; 166 | $self->{close}->(); 167 | $self->{dispatcher}->($label,$bc) if $self->{dispatcher}; 168 | } 169 | } 170 | 171 | sub upArrow{ 172 | my $self=shift; 173 | if ($self->depth()==1){ 174 | $self->closeItem(); 175 | } 176 | else{ 177 | $self->prevItem(); 178 | } 179 | } 180 | 181 | sub downArrow{ 182 | my $self=shift; 183 | if ($self->depth()==1){ 184 | $self->openItem(); 185 | } 186 | else{ 187 | $self->nextItem(); 188 | } 189 | } 190 | 191 | sub rightArrow{ 192 | my $self=shift; 193 | if ($self->depth()==1){ 194 | $self->nextItem(); 195 | } 196 | else{ 197 | $self->openItem(); 198 | } 199 | } 200 | 201 | sub leftArrow{ 202 | my $self=shift; 203 | if ($self->depth()==1){ 204 | $self->prevItem(); 205 | } 206 | else{ 207 | $self->closeItem(); 208 | } 209 | $self->redraw(); 210 | $self->draw(); 211 | } 212 | 213 | =head3 C<$menu-E<gt>drillDown()>, 214 | 215 | An internal routione that drills down the breadcrumbs to get the 216 | currently highlighted item, and whether it as any children. results 217 | returned an arrayRef containing two items [Label,Children?1:0] 218 | 219 | 220 | =cut 221 | 222 | sub drillDown{ # return curent item, and whether it has children; 223 | my $self=shift; 224 | my $tmp=dclone($self->{menu}); 225 | foreach my $level (0..$#{$self->{breadCrumbs}}){ 226 | return 0 unless $tmp->[$self->{breadCrumbs}->[$level]]; 227 | shift @{$tmp} unless ($level==0); 228 | $tmp=$tmp->[$self->{breadCrumbs}->[$level]]; 229 | } 230 | return ref $tmp?[$tmp->[0],1]:[$tmp,0]; 231 | } 232 | 233 | 234 | =head3 C<$menu-E<gt>drawMenu()>, 235 | 236 | Draws the menu tree, obviously. Overwrites parts of the canvas, therefore 237 | these may need to be redrawn after menu closed. 238 | 239 | =cut 240 | 241 | sub draw{ 242 | my $self=shift; 243 | my $pos=[@{$self->{pos}}]; # get a copy of contents of $self->{pos} 244 | foreach my $level (0..$#{$self->{breadCrumbs}}){ 245 | $pos = $self->drawLevel($level,$self->{breadCrumbs}->[$level],$pos) 246 | } 247 | } 248 | 249 | =head3 C<$menu-E<gt>drawLevel()>, 250 | 251 | Internal function to draw each level of the path to the selected item 252 | 253 | =cut 254 | 255 | sub drawLevel{ 256 | my ($self,$level,$ai,$pos)=@_; 257 | my $nextPos=$pos; 258 | my $tmp=dclone($self->{menu}); 259 | if (!$level){ 260 | foreach my $mi (0..$#$tmp){ 261 | my $label=((ref $tmp->[$mi])?$tmp->[$mi]->[0]:$tmp->[$mi]); 262 | my $active=($ai == $mi?1:0); 263 | if ($active){$nextPos=[$nextPos->[0]+1,$pos->[1]]} 264 | printAt(@$pos,$self->highlight($label,$active). " "); 265 | $pos->[1]+=(2+length $label); 266 | } 267 | print "\n"; 268 | } 269 | else{ 270 | my $l=0; 271 | while ($l<$level){ # walk down the tree until level to be printed 272 | $tmp=$tmp->[$self->{breadCrumbs}->[$l]]; 273 | shift @{$tmp} ; 274 | $l++ 275 | } 276 | my $longest=-1; 277 | foreach(@$tmp){ 278 | my $il=length(ref $_?$_->[0]:$_); 279 | $longest=$il if ($longest<$il); 280 | }; 281 | return if ($longest==-1); #empty list 282 | printAt(@$pos,"┌". ("─"x$longest)."┐"); 283 | $pos->[0]+=1; 284 | foreach my $mi (0..$#{$tmp}){ # skip first item which is label for list 285 | my $label=((ref $tmp->[$mi])?$tmp->[$mi]->[0]:$tmp->[$mi]); 286 | my $active=(($ai) == $mi?1:0); 287 | if ($active){$nextPos=[$pos->[0],$pos->[1]+$longest+2]} 288 | printAt(@$pos,$self->highlight($label,$active,$longest)); 289 | $pos->[0]+=1; 290 | } 291 | printAt(@$pos,"└". ("─"x$longest)."┘"); 292 | } 293 | return $nextPos; 294 | } 295 | 296 | 297 | =head3 C<$menu-E<gt>depth()>, 298 | 299 | Internal function to identify which level of the menu tree has been descended; 300 | I.e. the number of items in C<$menu->{breadCrumbs}> 301 | 302 | =cut 303 | 304 | sub depth{ 305 | my $self=shift; 306 | return scalar @{$self->{breadCrumbs}}; 307 | } 308 | 309 | 310 | =head3 C<$menu-E<gt>highlight()> 311 | 312 | Internal function to highlight selected items 313 | 314 | =cut 315 | 316 | sub highlight{ 317 | my ($self,$str,$hl,$padding)=@_; 318 | my $space=$padding?(" "x($padding-length $str)):" "; 319 | my $b=$padding?"│":""; 320 | return $b.colour($hl?$self->{highlightColour}:$self->{normalColour}).$str.$space.colour("reset").$b;; 321 | } 322 | 323 | 1; 324 | -------------------------------------------------------------------------------- /lib/Term/Graille/Selector.pm: -------------------------------------------------------------------------------- 1 | package Term::Graille::Selector; 2 | 3 | use strict;use warnings; 4 | use Term::Graille qw/colour printAt clearScreen border cursorAt/; 5 | use utf8; 6 | 7 | 8 | our $VERSION=0.10; 9 | 10 | =head3 C<my $selector=Term::Graille::Selector-E<gt>new(%params)> 11 | 12 | Creates a new $chooser; params are 13 | C<options> the possible options that may be selected 14 | C<redraw> This is a function to redraws the application screen. 15 | The chooser may overwrite parts of the application screen, and this 16 | function needs to be provided to restore the screen. 17 | C<callback> The chooser does not call any functions, instead returns the 18 | selected item(s). It is upto the main application to use this data (the 19 | callback function supplied) 20 | C<pos> Optional. The default position is [2,2], but setting this parameter allows 21 | the chooser to be placed elsewhere 22 | C<highlightColour> Optional. The selected item is highlighted default "black on_white" 23 | C<normalColour> Optional. The normal colour of menu items "white on_black" 24 | 25 | 26 | =cut 27 | 28 | 29 | sub new{ 30 | my ($class,%params) = @_; 31 | my $self={}; 32 | bless $self,$class; 33 | $self->{redraw}=$params{redraw} if (exists $params{redraw}); # function to redraw application 34 | $self->{callback}=$params{callback} if (exists $params{callback}); # function to call after menu item selected 35 | $self->{options}=$params{options}//[]; 36 | $self->{selected}=$params{selected}// ""; 37 | $self->{combo}=$params{combo}// 1; # combo mode...experimental 38 | $self->{entry}=$params{selected}// ""; 39 | $self->{entryPos}=0; 40 | $self->{pointer}=0; 41 | $self->{param}=$params{param}//{}; # this hashref may be passed for persistent data 42 | $self->{start}=$params{start}//0; 43 | $self->{title}=$params{title}//"Chooser"; 44 | $self->{normalColour}=$params{titleColour}//"yellow"; 45 | $self->{multi}=$params{multi}//0; 46 | $self->{pos}=$params{pos}//[2,2]; 47 | $self->{geometry}=$params{geometry}//[13,20]; 48 | $self->{highlightColour}=$params{highlightColour}//"black on_white"; 49 | $self->{normalColour}=$params{normalColour}//"white on_black"; 50 | $self->{keyAction}={ 51 | "[A" =>sub{$self->prevItem()}, 52 | "[B" =>sub{$self->nextItem()}, 53 | "[C" =>sub{$self->selectItem(1)}, # passes 1 if selected with right arrow 54 | "enter"=>sub{$self->selectItem(2)}, # passes 2 if selected with enter (the entry box is queried) 55 | "esc" =>sub{$self->{close}->()}, 56 | others =>sub{my ($obj,$pressed,$gV)=@_;$self->addChar($pressed)}, 57 | }; 58 | return $self; 59 | } 60 | 61 | sub draw{ 62 | my ($self)=@_; 63 | border($self->{pos}->[0],$self->{pos}->[1], 64 | $self->{pos}->[0]+$self->{geometry}->[0],$self->{pos}->[1]+$self->{geometry}->[1], 65 | "thick",$self->{focus}?$self->{focusColour}:$self->{blurColour}, 66 | $self->{title},$self->{titleColour}); 67 | 68 | $self->{start}++ while ($self->{pointer}>$self->{start}+$self->{geometry}->[0]-4); # the -4 user entry space for combo mode 69 | $self->{start}-- while ($self->{pointer}<$self->{start}); 70 | printAt($self->{pos}->[0]+1,$self->{pos}->[1]+1, $self->{entry}.(" "x($self->{geometry}->[1]-length $self->{entry}))); # combo mode input linethe 71 | printAt($self->{pos}->[0]+2,$self->{pos}->[1]+1, "-"x$self->{geometry}->[1]); # lower border for user entry space for combo mode 72 | 73 | foreach ($self->{start}..$self->{start}+$self->{geometry}->[0]-3){ 74 | if ($_<@{$self->{options}}){ 75 | my $colour=colour(isSelected($self,$self->{options}->[$_])?"black on_white":"white"); 76 | $colour.=colour(($_==$self->{pointer})?"underline":""); 77 | printAt($self->{pos}->[0]+$_+3-$self->{start},$self->{pos}->[1]+1, #+3 is for user entry space for combo mode 78 | $colour.$self->{options}->[$_].colour("reset")); 79 | } 80 | } 81 | } 82 | 83 | sub addChar{ 84 | my ($self,$ch)=@_; 85 | $self->{entry}.=$ch if (length $ch ==1 ); 86 | chop $self->{entry}if ($ch =~/back/ ); 87 | $self->draw(); 88 | 89 | } 90 | 91 | 92 | =head3 C<my $selector=setSelected($item)> 93 | makes an item highlighted in the selector...this is just as a 94 | visual cue...the cursor is at the underlined item 95 | 96 | =cut 97 | 98 | sub setSelected{ 99 | my ($self,$item)=@_; 100 | for my $o (0..$#{$self->{options}}){ 101 | if ($self->{options}->[$o] eq $item){ 102 | if ($self->{multi}==0){ 103 | $self->{selected}=[$o] 104 | } 105 | else{ 106 | # for multiselect 107 | } 108 | } 109 | } 110 | } 111 | 112 | 113 | =head3 C<my $selector=isSelected($item)> 114 | 115 | Test if an item has been preselected (used internallY 116 | 117 | =cut 118 | 119 | sub isSelected{ 120 | my ($self,$item)=@_; 121 | my $sel=ref($self->{selected})?$self->{selected}:[$self->{selected}]; 122 | for my $s (@{$sel}){ 123 | return 1 if ($s eq $item) 124 | } 125 | return 0 126 | } 127 | 128 | 129 | =head3 C<$selector->nextItem()/prevItem()/selectItem()> 130 | 131 | These are internal methods to choose the item from the list using keys asin $selector->{keyActions} above; 132 | 133 | =cut 134 | 135 | 136 | sub nextItem{ 137 | my ($self)=@_; 138 | $self->{pointer}++ unless ($self->{pointer} >=$#{$self->{options}}); 139 | $self->draw(); 140 | return $self->{options}->[$self->{pointer}]; 141 | } 142 | 143 | sub prevItem{ 144 | my ($self)=@_; 145 | $self->{pointer}-- unless ($self->{pointer} <=0); 146 | $self->draw(); 147 | return $self->{options}->[$self->{pointer}]; 148 | } 149 | 150 | sub selectItem{ 151 | my ($self,$submitMethod)=@_; 152 | if ($self->{multi}==0){ 153 | $self->{selected}=($self->{entry} and ($submitMethod == 2))?$self->{entry}:$self->{options}->[$self->{pointer}]; 154 | $self->{redraw}->(); 155 | $self->{callback}->({selected=>$self->{selected},method=>$submitMethod,%{$self->{param}}}) if $self->{callback}; 156 | return $self->{options}->[$self->{pointer}]; 157 | } 158 | else{ 159 | #for multiselect 160 | } 161 | } 162 | 163 | sub close{ # what needs to be done before Interact de-activates widget 164 | my ($self)=@_; 165 | $self->{redraw}->(); 166 | } 167 | 168 | 1; 169 | -------------------------------------------------------------------------------- /lib/Term/Graille/Textarea.pm: -------------------------------------------------------------------------------- 1 | =head1 NAME 2 | Term::Graille::Textarea 3 | 4 | Text editting area 5 | 6 | =head1 SYNOPSIS 7 | 8 | use Term::Graille::Interact; 9 | use Term::Graille::Textarea ; 10 | 11 | my $editor=new Term::Graille::Textarea( 12 | geometry=>[60,30], # [$Width, $Height] 13 | position=>[2,2], # [$row, $column] 14 | ); 15 | 16 | 17 | =head1 DESCRIPTION 18 | 19 | Allows an area for multiline text to be entered and editted. 20 | 21 | =head1 FUNCTIONS 22 | 23 | =cut 24 | 25 | package Term::Graille::Textarea; 26 | use lib "../lib"; 27 | use strict;use warnings; 28 | use Term::Graille qw/colour printAt clearScreen border/; 29 | use utf8; 30 | 31 | our $VERSION=0.10; 32 | 33 | =head3 C<my $menu=Term::Graille::Menu-E<gt>new(%params)> 34 | 35 | Creates a new Textarea; params are 36 | C<menu> The menu tree as an Array ref containing strings and arrayrefs. 37 | Branches are Array refs, and end nodes are strings. See above example to 38 | visualise structure. 39 | C<redraw> This is a function that needs to be supplied to redraw the 40 | application screen. The menu will overwrite parts of the application screen, 41 | and this function needs to be provided to restore the screen. 42 | C<callback> The menu does not call any functions, instead returns the 43 | leaf string selected. It is upto the main application to use this string to 44 | in a dispatch routine (the callback function supplied) 45 | C<pos> Optional. The default position is [2,2], but setting this parameter allows 46 | the menu to be placed elsewhere 47 | C<highlightColour> Optional. The selected item is highlighted default "black on_white" 48 | C<normalColour> Optional. The normal colour of menu items "white on_black" 49 | 50 | 51 | =cut 52 | 53 | 54 | sub new{ 55 | my ($class,%params) = @_; 56 | my $self={}; 57 | bless $self,$class; 58 | $self->{geometry}=$params{geometry}//[72,14]; # to do fugure out resize 59 | $self->{position}=$params{position}//[4,6]; # function to redraw application 60 | $self->{cursor}=[0,0], 61 | $self->{text}=$params{text}//[""]; 62 | $self->{windowStart}=$params{windowStart}//0; 63 | $self->{highlighted}=$params{highlighthed}//[[0,0],[0,0]];; 64 | $self->{keyAction}={ 65 | "[A" =>sub{$self->upArrow()}, 66 | "[B" =>sub{$self->downArrow()}, 67 | "[C" =>sub{$self->rightArrow()}, 68 | "[D" =>sub{$self->leftArrow()}, 69 | "enter" =>sub{$self->newline()}, 70 | "esc" =>sub{$self->exit()}, # this required for all interaction objects 71 | "delete" =>sub{$self->deleteChar()}, 72 | "backspace"=>sub{$self->backspace()}, 73 | others =>sub{my ($obj,$pressed,$gV)=@_;$self->addChar($pressed)}, 74 | }; 75 | 76 | $self->{border}=$params{border}//{style=>"double"}; 77 | $self->{title}=$params{title}//"Untitled"; 78 | 79 | 80 | 81 | return $self; 82 | } 83 | 84 | sub insertText{ 85 | my ($self,@text,$clear)=@_; 86 | my @newText=(); 87 | foreach (@text){ 88 | push @newText,(ref $_ eq "ARRAY")?@$_ :$_; 89 | } 90 | if ($clear){$self->{text}=[@newText]} 91 | else{ 92 | my @oldText=@{$self->{text}}; 93 | $self->{text}=[@oldText[0..$self->{cursor}->[0]],@newText,@oldText[$self->{cursor}->[0]..$#oldText]]; 94 | } 95 | } 96 | 97 | sub cursorMove{ 98 | my ($self,$movement)=@_; 99 | $self->{cursor}->[0]+=$movement->[0]; 100 | $self->{cursor}->[1]+=$movement->[1]; 101 | $self->{cursor}->[0]=0 if $self->{cursor}->[0] <0; 102 | $self->{cursor}->[0]=$#{$self->{text}} if ($#{$self->{text}} < $self->{cursor}->[0]); 103 | $self->{cursor}->[1]=0 if $self->{cursor}->[1] <0; 104 | $self->{cursor}->[1]=(length $self->{text}->[$self->{cursor}->[0]]) if ((length $self->{text}->[$self->{cursor}->[0]]) < $self->{cursor}->[1]); 105 | $self->{windowStart} = $self->{cursor}->[0] if ($self->{cursor}->[0] < $self->{windowStart}); 106 | $self->{windowStart} = $self->{cursor}->[0]-$self->{geometry}->[1] if ($self->{cursor}->[0]-$self->{geometry}->[1] > $self->{windowStart}); 107 | $self->draw(); 108 | my $winEnd=$self->{windowStart}+$self->{geometry}->[1]; 109 | if (@{$self->{text}}<$winEnd){$winEnd=@{$self->{text}}} ; 110 | printAt($self->{position}->[0]+$self->{geometry}->[1]+1,$self->{position}->[1]+$self->{geometry}->[0]-50, 111 | " Lines: $self->{windowStart}-$winEnd/".@{$self->{text}}." Curs: $self->{cursor}->[0],$self->{cursor}->[1] ". $self->charAt()." "); 112 | } 113 | 114 | sub draw{ 115 | my ($self)=@_; 116 | 117 | border($self->{position}->[0]-1,$self->{position}->[1]-1, 118 | $self->{position}->[0]+$self->{geometry}->[1],$self->{position}->[1]+$self->{geometry}->[0], 119 | $self->{border}->{style}//"double",$self->{border}->{colour}//"white",$self->{title},$self->{titleColour}//"white") if $self->{border}; 120 | for my $l ($self->{windowStart}..$self->{windowStart}+$self->{geometry}->[1]){ 121 | $self->drawLine($l) 122 | } 123 | } 124 | 125 | sub drawLine{ 126 | my ($self,$line)=@_; 127 | my ($row,$col)=@{$self->{position}}; # the position of the top left corner of textarea 128 | my $rowOffset=$row+$line-$self->{windowStart}; 129 | my $lineText=$self->{text}->[$line]//""; 130 | my $rowScroll=$self->{cursor}->[1]-$self->{geometry}->[0]; 131 | $rowScroll=0 if ($rowScroll<0); 132 | $lineText=(length $lineText > $rowScroll) ? substr($lineText,$rowScroll,($self->{geometry}->[0]+1)):""; 133 | printAt($rowOffset,$col," "x($self->{geometry}->[0]+1)); 134 | if ($line!=$self->{cursor}->[0]){ 135 | printAt($rowOffset,$col,$lineText); 136 | } 137 | else { 138 | # empty line= just blink character 139 | if (length $lineText<=1){ printAt($rowOffset,$col,colour("blink invert")." ".colour("reset"))} 140 | # end of line= line+blinkcharacter 141 | elsif (length $lineText==($self->{cursor}->[1]-$rowScroll)){ printAt($rowOffset,$col,$lineText.colour("blink invert")." ".colour("reset")) } 142 | else {substr ($lineText, $self->{cursor}->[1]+1-$rowScroll, 0)= colour("reset");substr ($lineText, $self->{cursor}->[1]-$rowScroll, 0)= colour("blink invert") ; 143 | printAt($rowOffset,$col,$lineText); 144 | }; 145 | } 146 | } 147 | 148 | sub upArrow{ 149 | my $self=shift; 150 | $self->cursorMove([-1,0]); 151 | } 152 | 153 | sub leftArrow{ 154 | my $self=shift; 155 | $self->cursorMove([0,-1]); 156 | } 157 | 158 | 159 | sub charAt{ 160 | my ($self,$pos)=@_; 161 | $pos//=$self->{cursor}; 162 | return "" if (length $self->{text}->[$pos->[0]] eq $pos->[1]); 163 | return substr($self->{text}->[$pos->[0]], $pos->[1], 1); 164 | 165 | } 166 | 167 | sub rightArrow{ 168 | my $self=shift; 169 | $self->cursorMove([0,1]); 170 | } 171 | 172 | sub downArrow{ 173 | my $self=shift; 174 | $self->cursorMove([1,0]); 175 | } 176 | 177 | sub getLine{ 178 | my ($self,$line)=@_; 179 | $line=$self->{cursor}->[0] unless defined $line; 180 | return $self->{text}->[$line]; 181 | } 182 | 183 | sub newline{ 184 | my ($self)=@_; 185 | my @textBefore=$self->{cursor}->[0]?(@{$self->{text}}[0..$self->{cursor}->[0]-1]):(); 186 | my @textAfter=($self->{cursor}->[0]<(scalar @{$self->{text}}))?@{$self->{text}}[$self->{cursor}->[0]+1..$#{$self->{text}}]:(); 187 | my $textAt=$self->{text}->[$self->{cursor}->[0]]; 188 | my @splitText= (substr($textAt, 0, $self->{cursor}->[1]), substr($textAt, $self->{cursor}->[1])); 189 | $self->{text}=[@textBefore,@splitText,@textAfter]; 190 | $self->cursorMove([1,-100]); 191 | } 192 | 193 | sub addChar{ 194 | my ($self,$ch)=@_; 195 | return unless defined $ch && (length $ch==1); 196 | my $line=$self->{text}->[$self->{cursor}->[0]]//""; 197 | if ($line){ substr($line, $self->{cursor}->[1], 0)=$ch;} 198 | else{ $line=$ch}; 199 | $self->{text}->[$self->{cursor}->[0]]=$line; 200 | $self->cursorMove([0,1]); 201 | 202 | } 203 | 204 | sub deleteChar{ 205 | my ($self)=@_; 206 | # delete character if not end of line 207 | if ($self->{cursor}->[1]<((length $self->{text}->[$self->{cursor}->[0]])-1)){ 208 | substr($self->{text}->[$self->{cursor}->[0]], $self->{cursor}->[1],1)=""; 209 | } 210 | # skip if no more lines at all 211 | elsif ($self->{cursor}->[0] == @{$self->{text}}-1){ 212 | 213 | } 214 | #get the next line and merge it with current line 215 | else{ 216 | $self->{text}->[$self->{cursor}->[0]].= $self->{text}->[$self->{cursor}->[0]+1]; 217 | splice( @{$self->{text}}, $self->{cursor}->[0]+1, 1 ); 218 | } 219 | $self->cursorMove([0,0]); 220 | } 221 | 222 | sub backspace{ 223 | my ($self)=@_; 224 | if ($self->{cursor}->[1]>0){ 225 | substr( $self->{text}->[$self->{cursor}->[0]], $self->{cursor}->[1]-1,1)=""; 226 | $self->cursorMove([0,-1]); 227 | } 228 | else{ 229 | return unless $self->{cursor}->[0]; # backspace at position[0,0] 230 | my $disp=length $self->{text}->[$self->{cursor}->[0]-1]; 231 | $self->{text}->[$self->{cursor}->[0]-1].= $self->{text}->[$self->{cursor}->[0]]; 232 | splice( @{$self->{text}}, $self->{cursor}->[0], 1 ); 233 | $self->cursorMove([-1,-100]); 234 | $self->cursorMove([0,$disp]); 235 | } 236 | } 237 | 238 | sub exit{ 239 | my $self=shift; 240 | $self->{close}->(); 241 | } 242 | 243 | sub text{ 244 | my $self=shift; 245 | return Join("\n",@{$self->{text}}); 246 | } 247 | 248 | 1; 249 | -------------------------------------------------------------------------------- /old_files/Readme.md: -------------------------------------------------------------------------------- 1 | # Old versions 2 | 3 | These are just the older versions as TYerm::Graille Evolved. 4 | New versions may break programs that depend on old infrastructure, hence these may be useful to somebody 5 | -------------------------------------------------------------------------------- /t/1.t: -------------------------------------------------------------------------------- 1 | #!/usr/bin/perl 2 | use strict;use warnings; 3 | use Test::More tests => 5; 4 | BEGIN { 5 | use_ok( 'Algorithm::Line::Bresenham 0.15'); 6 | use_ok( 'Term::Graille', qw/colour paint printAt clearScreen border block2braille pixelAt/); 7 | use_ok( 'Term::Graille::Font', qw/convertDG saveGrf loadGrf fontWrite/); 8 | use_ok( 'Term::Graille::IO'); 9 | use_ok( 'Term::Graille::Menu'); 10 | } 11 | --------------------------------------------------------------------------------