├── .gitattributes ├── .github └── ISSUE_TEMPLATE │ └── bug_report.md ├── .gitignore ├── CHANGELOG.md ├── CHGLOG.md ├── CODE_OF_CONDUCT.md ├── LICENSE ├── LISTS └── EXAMPLE │ ├── INFO │ ├── DOOM.MD │ └── UDOOM.MD │ ├── IN_PROGS │ ├── DOOM.BMP │ ├── DOOM2.BMP │ └── UDOOM.BMP │ ├── LIST.TXT │ └── TITLES │ ├── DOOM.BMP │ ├── DOOM2.BMP │ └── UDOOM.BMP ├── README.MD ├── RES ├── HELP.MD ├── RLOADER.CFG ├── SCREEN01.PNG ├── SCREEN02.PNG ├── SCREEN03.PNG ├── SCREEN04.PNG ├── SCREEN05.PNG ├── SPLASH.BMP ├── SPLASH40.ASC ├── SPLASH40.BIN ├── SPLASH80.ASC ├── SPLASH80.BIN └── SPLASH80.BMP ├── RLOADER ├── MAIN.ASM ├── MAKE-DBG.BAT ├── MAKE-REL.BAT └── TDCONFIG.TD ├── SRC ├── ANSIPIC.CPP ├── ANSIPIC.HPP ├── BITMAP.CPP ├── BITMAP.HPP ├── CONFIG.CPP ├── CONFIG.HPP ├── ENCODER.CPP ├── ENCODER.HPP ├── ENTRY.CPP ├── ENTRY.HPP ├── GRAPHICS.CPP ├── GRAPHICS.HPP ├── ISR.CPP ├── ISR.HPP ├── KEYBOARD.CPP ├── KEYBOARD.HPP ├── KEYB_ISR.ASM ├── LIST.CPP ├── LIST.HPP ├── MAIN.CPP ├── MARKDOWN.CPP ├── MARKDOWN.HPP ├── MATH.CPP ├── MATH.HPP ├── STRING.CPP ├── STRING.HPP ├── TUI │ ├── BUTTON.CPP │ ├── BUTTON.HPP │ ├── CONTROL.CPP │ ├── CONTROL.HPP │ ├── EDITBOX.CPP │ ├── EDITBOX.HPP │ ├── GRAPH.CPP │ ├── GRAPH.HPP │ ├── LABEL.CPP │ ├── LABEL.HPP │ ├── LISTBOX.CPP │ ├── LISTBOX.HPP │ ├── MSGBOX.CPP │ ├── MSGBOX.HPP │ ├── MSGQUEUE.CPP │ ├── MSGQUEUE.HPP │ ├── TEXTBOX.CPP │ ├── TEXTBOX.HPP │ ├── THEME.CPP │ ├── THEME.HPP │ ├── TUI.CPP │ ├── TUI.HPP │ ├── VIEW.CPP │ ├── VIEW.HPP │ ├── VIEWS │ │ ├── OPENSAVE.CPP │ │ └── OPENSAVE.HPP │ ├── WINDOW.CPP │ └── WINDOW.HPP ├── TYPES.HPP ├── UI.CPP ├── UI.HPP ├── VGA.CPP └── VGA.HPP ├── TESTS ├── KEYB │ ├── KEYTESTS.CPP │ └── KEYTESTS.HPP ├── MAIN.CPP ├── MARKDOWN │ ├── MD_TESTS.CPP │ └── MD_TESTS.HPP ├── TESTS.DSK ├── TESTS.PRJ ├── TUI │ ├── TESTVIEW.CPP │ ├── TESTVIEW.HPP │ ├── TUITESTS.CPP │ └── TUITESTS.HPP └── VGA │ ├── VGATESTS.CPP │ └── VGATESTS.HPP ├── UI.DSK └── UI.PRJ /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto 3 | 4 | .git* text eol=lf 5 | 6 | README.md text eol=lf 7 | 8 | *.c text 9 | *.h text 10 | *.cpp text 11 | *.hpp text 12 | 13 | *.dsk binary 14 | *.prj binary 15 | *.exe binary 16 | *.bmp binary 17 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/bug_report.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: Bug report 3 | about: Create a report to help improve RLoader 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | **Describe the bug** 11 | A clear and concise description of what the bug is. 12 | 13 | **To Reproduce** 14 | Steps to reproduce the behavior: 15 | 1. Go to '...' 16 | 2. Scroll down to '....' 17 | 3. See error 18 | 19 | **Expected behavior** 20 | A clear and concise description of what you expected to happen. 21 | 22 | **Screenshots** 23 | If applicable, add screenshots to help explain your problem. 24 | 25 | **Environment** 26 | - RLoader version: [v1.0.0, v1.0.3, ...] 27 | - Running on: [DOSBox, Virtual machine, Real hardware] 28 | - OS: [MS-DOS, FreeDOS, ...] 29 | 30 | **Additional context** 31 | Add any other context about the problem here. 32 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.exe 2 | *.obj 3 | *.bak 4 | *.bkp 5 | *.dst 6 | *.zip 7 | 8 | backup.bat 9 | package.bat 10 | 11 | lists/** 12 | 13 | !lists/example 14 | !lists/example/** 15 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | All notable changes to this project will be documented in this file. 3 | 4 | The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). 5 | 6 | ## [1.0.4] - 2022-10-30 7 | 8 | ### Changed 9 | - correct detection of the number of lines in text modes with fallback to 10 | standard documented modes. 11 | 12 | ## [1.0.3] - 2022-10-29 13 | 14 | ### Added 15 | - updated resident executable to check for `RLOADER_PATH` environment 16 | variabile and allow running RLoader from any position. 17 | 18 | ### Changed 19 | - list files parsing bugfix to allow empty lines. 20 | 21 | ## [1.0.2] - 2021-08-14 22 | 23 | ### Changed 24 | - rewritten the resident executable in assembly to minimize memory occupation 25 | and the allow to load it in upper memory. 26 | 27 | ## [1.0.1] - 2021-07-17 28 | 29 | ### Added 30 | - maintained user preference between `title` and `in_progs` thumbnails 31 | - made persistent the currently selected entry and the thumbnail display 32 | preference between RLoader sessions 33 | - added arguments `/batchpause` and `/batchmemfree` to simplify 34 | troubleshooting when launching programs 35 | - added check to verify that thumbnail bitmaps are uncompressed at 36 | 4-bits-per-pixel. 37 | -------------------------------------------------------------------------------- /CHGLOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | All notable changes to this project will be documented in this file. 3 | 4 | The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). 5 | 6 | ## [1.0.4] - 2022-10-30 7 | 8 | ### Changed 9 | - correct detection of the number of lines in text modes with fallback to 10 | standard documented modes. 11 | 12 | ## [1.0.3] - 2022-10-29 13 | 14 | ### Added 15 | - updated resident executable to check for `RLOADER_PATH` environment 16 | variabile and allow running RLoader from any position. 17 | 18 | ### Changed 19 | - list files parsing bugfix to allow empty lines. 20 | 21 | ## [1.0.2] - 2021-08-14 22 | 23 | ### Changed 24 | - rewritten the resident executable in assembly to minimize memory occupation 25 | and the allow to load it in upper memory. 26 | 27 | ## [1.0.1] - 2021-07-17 28 | 29 | ### Added 30 | - maintained user preference between `title` and `in_progs` thumbnails 31 | - made persistent the currently selected entry and the thumbnail display 32 | preference between RLoader sessions 33 | - added arguments `/batchpause` and `/batchmemfree` to simplify 34 | troubleshooting when launching programs 35 | - added check to verify that thumbnail bitmaps are uncompressed at 36 | 4-bits-per-pixel. 37 | -------------------------------------------------------------------------------- /CODE_OF_CONDUCT.md: -------------------------------------------------------------------------------- 1 | # Contributor Covenant Code of Conduct 2 | 3 | ## Our Pledge 4 | 5 | We as members, contributors, and leaders pledge to make participation in our 6 | community a harassment-free experience for everyone, regardless of age, body 7 | size, visible or invisible disability, ethnicity, sex characteristics, gender 8 | identity and expression, level of experience, education, socio-economic status, 9 | nationality, personal appearance, race, religion, or sexual identity 10 | and orientation. 11 | 12 | We pledge to act and interact in ways that contribute to an open, welcoming, 13 | diverse, inclusive, and healthy community. 14 | 15 | ## Our Standards 16 | 17 | Examples of behavior that contributes to a positive environment for our 18 | community include: 19 | 20 | * Demonstrating empathy and kindness toward other people 21 | * Being respectful of differing opinions, viewpoints, and experiences 22 | * Giving and gracefully accepting constructive feedback 23 | * Accepting responsibility and apologizing to those affected by our mistakes, 24 | and learning from the experience 25 | * Focusing on what is best not just for us as individuals, but for the 26 | overall community 27 | 28 | Examples of unacceptable behavior include: 29 | 30 | * The use of sexualized language or imagery, and sexual attention or 31 | advances of any kind 32 | * Trolling, insulting or derogatory comments, and personal or political attacks 33 | * Public or private harassment 34 | * Publishing others' private information, such as a physical or email 35 | address, without their explicit permission 36 | * Other conduct which could reasonably be considered inappropriate in a 37 | professional setting 38 | 39 | ## Enforcement Responsibilities 40 | 41 | Community leaders are responsible for clarifying and enforcing our standards of 42 | acceptable behavior and will take appropriate and fair corrective action in 43 | response to any behavior that they deem inappropriate, threatening, offensive, 44 | or harmful. 45 | 46 | Community leaders have the right and responsibility to remove, edit, or reject 47 | comments, commits, code, wiki edits, issues, and other contributions that are 48 | not aligned to this Code of Conduct, and will communicate reasons for moderation 49 | decisions when appropriate. 50 | 51 | ## Scope 52 | 53 | This Code of Conduct applies within all community spaces, and also applies when 54 | an individual is officially representing the community in public spaces. 55 | Examples of representing our community include using an official e-mail address, 56 | posting via an official social media account, or acting as an appointed 57 | representative at an online or offline event. 58 | 59 | ## Enforcement 60 | 61 | Instances of abusive, harassing, or otherwise unacceptable behavior may be 62 | reported to the community leaders responsible for enforcement at 63 | marco.sacchi.1979@gmail.com. 64 | All complaints will be reviewed and investigated promptly and fairly. 65 | 66 | All community leaders are obligated to respect the privacy and security of the 67 | reporter of any incident. 68 | 69 | ## Enforcement Guidelines 70 | 71 | Community leaders will follow these Community Impact Guidelines in determining 72 | the consequences for any action they deem in violation of this Code of Conduct: 73 | 74 | ### 1. Correction 75 | 76 | **Community Impact**: Use of inappropriate language or other behavior deemed 77 | unprofessional or unwelcome in the community. 78 | 79 | **Consequence**: A private, written warning from community leaders, providing 80 | clarity around the nature of the violation and an explanation of why the 81 | behavior was inappropriate. A public apology may be requested. 82 | 83 | ### 2. Warning 84 | 85 | **Community Impact**: A violation through a single incident or series 86 | of actions. 87 | 88 | **Consequence**: A warning with consequences for continued behavior. No 89 | interaction with the people involved, including unsolicited interaction with 90 | those enforcing the Code of Conduct, for a specified period of time. This 91 | includes avoiding interactions in community spaces as well as external channels 92 | like social media. Violating these terms may lead to a temporary or 93 | permanent ban. 94 | 95 | ### 3. Temporary Ban 96 | 97 | **Community Impact**: A serious violation of community standards, including 98 | sustained inappropriate behavior. 99 | 100 | **Consequence**: A temporary ban from any sort of interaction or public 101 | communication with the community for a specified period of time. No public or 102 | private interaction with the people involved, including unsolicited interaction 103 | with those enforcing the Code of Conduct, is allowed during this period. 104 | Violating these terms may lead to a permanent ban. 105 | 106 | ### 4. Permanent Ban 107 | 108 | **Community Impact**: Demonstrating a pattern of violation of community 109 | standards, including sustained inappropriate behavior, harassment of an 110 | individual, or aggression toward or disparagement of classes of individuals. 111 | 112 | **Consequence**: A permanent ban from any sort of public interaction within 113 | the community. 114 | 115 | ## Attribution 116 | 117 | This Code of Conduct is adapted from the [Contributor Covenant][homepage], 118 | version 2.0, available at 119 | https://www.contributor-covenant.org/version/2/0/code_of_conduct.html. 120 | 121 | Community Impact Guidelines were inspired by [Mozilla's code of conduct 122 | enforcement ladder](https://github.com/mozilla/diversity). 123 | 124 | [homepage]: https://www.contributor-covenant.org 125 | 126 | For answers to common questions about this code of conduct, see the FAQ at 127 | https://www.contributor-covenant.org/faq. Translations are available at 128 | https://www.contributor-covenant.org/translations. 129 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 Marco Sacchi 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /LISTS/EXAMPLE/INFO/DOOM.MD: -------------------------------------------------------------------------------- 1 | # Description of Doom 2 | 3 | *Doom* and its sequel, *Doom II*, are 4 | arguably among the most important 5 | games in the history of PC gaming. 6 | 7 | Though there were older games that 8 | involved the exploration of 3D 9 | rendered environments in first 10 | person, including 1990’s Castle 11 | Master, in which you hunted demons 12 | and found treasures in a fully 13 | 3D-rendered haunted castle, the first 14 | person shooter boom began in earnest 15 | with **Apogee Software** and **id** 16 | **Software’s** smash hit *Wolfenstein 3D*. 17 | -------------------------------------------------------------------------------- /LISTS/EXAMPLE/INFO/UDOOM.MD: -------------------------------------------------------------------------------- 1 | # Description of The Ultimate DOOM 2 | 3 | DOOM deservedly has a large base of 4 | fans. Its makers names are legend: 5 | *Carmack, Romero, McGee* and more. 6 | 1995’s *"The Ultimate DOOM"* provides 7 | a patched, complete version. 8 | 9 | Released by id Software in 1993, 10 | Computer Gaming World, PC Gamer UK, 11 | Dragon and GamesMaster all 12 | recommended it highly. DOOM changed 13 | the world of videogames. 14 | -------------------------------------------------------------------------------- /LISTS/EXAMPLE/IN_PROGS/DOOM.BMP: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/marco-sacchi/RLoader/b80fbbe1f108600be1fc0e191aca42f49443c153/LISTS/EXAMPLE/IN_PROGS/DOOM.BMP -------------------------------------------------------------------------------- /LISTS/EXAMPLE/IN_PROGS/DOOM2.BMP: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/marco-sacchi/RLoader/b80fbbe1f108600be1fc0e191aca42f49443c153/LISTS/EXAMPLE/IN_PROGS/DOOM2.BMP -------------------------------------------------------------------------------- /LISTS/EXAMPLE/IN_PROGS/UDOOM.BMP: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/marco-sacchi/RLoader/b80fbbe1f108600be1fc0e191aca42f49443c153/LISTS/EXAMPLE/IN_PROGS/UDOOM.BMP -------------------------------------------------------------------------------- /LISTS/EXAMPLE/LIST.TXT: -------------------------------------------------------------------------------- 1 | # example list 2 | # path executable setup cycles title 3 | 4 | e:\alleycat alleycat.exe - 9000 Alley cat 5 | 6 | e:\doom doom.exe setup.exe 30000 Doom 7 | f:\doom2 doom2.exe setup.exe 50000 Doom II 8 | c:\udoom doom.exe setup.exe 0 Ultimate doom 9 | -------------------------------------------------------------------------------- /LISTS/EXAMPLE/TITLES/DOOM.BMP: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/marco-sacchi/RLoader/b80fbbe1f108600be1fc0e191aca42f49443c153/LISTS/EXAMPLE/TITLES/DOOM.BMP -------------------------------------------------------------------------------- /LISTS/EXAMPLE/TITLES/DOOM2.BMP: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/marco-sacchi/RLoader/b80fbbe1f108600be1fc0e191aca42f49443c153/LISTS/EXAMPLE/TITLES/DOOM2.BMP -------------------------------------------------------------------------------- /LISTS/EXAMPLE/TITLES/UDOOM.BMP: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/marco-sacchi/RLoader/b80fbbe1f108600be1fc0e191aca42f49443c153/LISTS/EXAMPLE/TITLES/UDOOM.BMP -------------------------------------------------------------------------------- /README.MD: -------------------------------------------------------------------------------- 1 | # RLoader 2 | 3 | DOS programs launcher with customizable lists, search and thumbnails 4 | display. 5 | 6 | Developed entirely in Borland C++ 3.0 as a simple list viewer to launch DOS 7 | programs, it has been extended by integrating: 8 | - text search, 9 | - definition of multiple program lists, 10 | - display of thumbnails of the program title and in-program graphics, 11 | - simplified display of markdown files as help and information on the 12 | elements of the active list 13 | - a fully functional text-mode interface for low-end machines 14 | 15 | The interface designed for the arrow keys and only 5 other function keys 16 | makes it perfect to use in DOSBox even when user input is handled via a 17 | gamepad (and proper key mapping). 18 | 19 | ![SCREEN01](RES/SCREEN01.PNG?raw=true "SCREEN01") 20 | ![SCREEN02](RES/SCREEN02.PNG?raw=true "SCREEN02") 21 | ![SCREEN03](RES/SCREEN03.PNG?raw=true "SCREEN03") 22 | ![SCREEN04](RES/SCREEN04.PNG?raw=true "SCREEN04") 23 | ![SCREEN05](RES/SCREEN05.PNG?raw=true "SCREEN05") 24 | 25 | # Installation 26 | 27 | Simply copy the two executable files and the `res` and` lists` folders to 28 | a folder of your choice. 29 | 30 | # Configuration 31 | 32 | RLoader is configurable through the `RES/RLOADER.CFG` file. 33 | 34 | The foreground and background colors of the user interface and the mapping 35 | of the drives specified in the lists can be customized (see the comments in 36 | the configuration file for more info). 37 | 38 | This last feature allows the porting of the lists from one system to another 39 | without the need to modify the absolute paths. 40 | 41 | # Hardware requirements (for a real machine) 42 | 43 | RLoader was compiled using the 8086 instruction set and implements two user 44 | interfaces: one graphical and one in text mode. 45 | 46 | The former requires a compatible VGA graphics card while the latter makes it 47 | usable even on low-end machines with a compatible CGA graphics card. 48 | 49 | Thanks to [jsmolina](https://github.com/jsmolina) for pointing out a 50 | compatibility issue in some emulators when text mode is used on CGA graphics 51 | cards and to [mills32](https://github.com/mills32) for testing on 52 | [pcem](https://pcem-emulator.co.uk) and [dosbox-x](https://dosbox-x.com). 53 | 54 | Now RLoader works correctly even on CGA mono and composite, tandy, pcjr, 55 | amstrad, mcga, mda machines. 56 | 57 | # Execution 58 | 59 | You can run RLoader without arguments or with `/h` or `/?` to view a short 60 | help. 61 | 62 | To load the example list on real hardware or VM, type: 63 | 64 | ``` 65 | RLOADER example 66 | ``` 67 | 68 | if you are on DOSBox, type: 69 | 70 | ``` 71 | RLOADER /dosbox example 72 | ``` 73 | 74 | If you want to use the text-mode interface, type: 75 | 76 | ``` 77 | RLOADER /textmode example 78 | ``` 79 | 80 | ## Running from any location 81 | 82 | `RLOADER.EXE` is designed to search for `UI.EXE` in the current working 83 | directory, creating some problems in some particular execution scenarios, 84 | like: 85 | 86 | ``` 87 | E:\UTILS\RLOADER.EXE example 88 | ``` 89 | 90 | Since version 1.0.3 it is possible to define the environment variable 91 | `RLOADER_PATH` to specify the path of rloader, so that it can be executed from 92 | any location. 93 | 94 | The variable can contain the drive designator, it is treated in a 95 | case-insensitive way and **must not end** with the directory separator 96 | character (`\` or `/`). Example of valid paths: 97 | 98 | ``` 99 | set RLOADER_PATH=D:\UTILITIES\RLOADER 100 | set RLOADER_PATH=c:\rloader 101 | ``` 102 | 103 | # Loading RLoader in upper memory 104 | 105 | As pointed out by [cyberluke](https://github.com/cyberluke) the resident part 106 | of RLoader occupies an entire segment (64Kb) even if the executable is smaller 107 | in size. 108 | 109 | Now that it has been rewritten in assembly, a TSR-like behavior has been 110 | implemented whereby all unused memory paragraphs are released immediately 111 | after loading. This, together with the reduction of a tenth of the file size, 112 | makes it easier to load into upper memory, leaving the rest of the 113 | conventional memory available to the launched programs. 114 | 115 | ``` 116 | LH RLOADER example 117 | ``` 118 | 119 | # Troubleshooting 120 | 121 | RLoader also has arguments to simplify troubleshooting if the launch of the 122 | listed programs fails: 123 | 124 | - `/batchpause` pauses the dynamically generated batch file that launches the 125 | program just before returning to RLoader, in order to make any error 126 | messages readable; 127 | 128 | - `/batchmemfree` displays the memory available to the program that is 129 | launched using the `mem /free` command, available both in compatible MS-DOS 130 | environments and in the various flavors of DOSBox. 131 | 132 | # List and info navigation 133 | 134 | - **Arrow keys** Move to previous/next entry. 135 | - **PgUp**/**PgDown** Move ten lines up/down. 136 | - **Home**/**End** Move to the beginning/end of list. 137 | 138 | # Actions 139 | 140 | - **F1** Toggle this help screen 141 | - **F2** Loop between title and in-programs screen shots 142 | - **F3** Show program info 143 | - **F5** Run configuration program 144 | - **F10** Exit this program 145 | - **Enter** Launch selected list entry 146 | 147 | # Search & filtering 148 | 149 | Type text to automatically filter out entries that does not match search 150 | string in a case-insensitive manner. 151 | 152 | # Lists 153 | 154 | You can create lists by making a directory in `.\LISTS\` with the name 155 | you want for list: 156 | 157 | ``` 158 | .\LISTS\LIST_DIRECTORY 159 | LIST.TXT list entries definition 160 | TITLES\ programs titles screens 161 | IN_PROGS\ in-programs screen-shots 162 | INFO\ programs info files 163 | ``` 164 | 165 | ### LIST.TXT 166 | 167 | List of entries, one per line, with 168 | these columns: 169 | - absolute path with drive letter 170 | - executable filename 171 | - setup program filename (if exists) 172 | - number of cycles 173 | - program title 174 | 175 | Lines that starts with **#** character will be *recognized as comments*. 176 | 177 | Each column must be separated by one or more *white-space characters* 178 | (spaces or tabs). 179 | 180 | **Executable filename** is the executable or batch file to be used to launch 181 | the program. Extension is *mandatory*. 182 | 183 | **Setup filename** is the executable or batch file to be used to setup the 184 | program. When setup program not exists a - character must be specified. 185 | Extension is *mandatory*. 186 | 187 | The **cycles** will be used inside DOSBox to automatically set correct 188 | emulation speed. If zero is specified 'auto' emulation speed will be set. 189 | 190 | The **program title** will be read until end-of-line character or 191 | end-of-file, so it can contains any character. 192 | 193 | ### Thumbnails generation 194 | 195 | rloader thumbnails must be standard *4 bits-per-pixel uncompressed Windows* 196 | *bitmap*. Only first **14 colors** can be used, 2 colors are reserved by the 197 | user interface of this program. Width of the image *must be 320 pixels*. 198 | 199 | You can use any command line tool that supports image manipulation. For 200 | example using [ImageMagick](https://imagemagick.org/), which is a free and 201 | open source tool, you can write: 202 | 203 | ``` 204 | magick.exe input.png -resize 320 -dither Ordered -depth 4 -colors 14 -type palette bmp3:output.bmp 205 | ``` 206 | 207 | where: 208 | - `-resize 320` rescales the image to 320 pixels 209 | - `-dither Ordered` applies ordered dithering (optional) 210 | - `-depth 4` specifies 4 bits per pixel (16 colors) 211 | - `-colors 14` remaps using only 14 colors 212 | - `type color` ensures conversion to a palettized image 213 | - `bmp3` specifies an uncompressed Windows bitmap 214 | 215 | You can avoid bilinear filtering caused by downscaling by adding the 216 | `-filter` flag with the values ​​`Point` or `NearestNeighbor`. 217 | 218 | ### TITLES subdirectory 219 | 220 | This directory will contains optional programs titles screen-shots thumbnails. 221 | 222 | ### IN_PROGS subdirectory 223 | 224 | This directory will contains optional in-programs screen-shots thumbnails. 225 | 226 | ### INFO subdirectory 227 | 228 | This directory will contains optional simplified markdown files what will 229 | describe specific program usage, options, keyboard shortcuts or notes, 230 | with a **maximum line length of 38 characters**, markdown formatting excluded. 231 | 232 | 233 | **NOTE:** titles, in-programs and info filenames *must match the inner* 234 | *directory name* of the absolute path specified in LIST.TXT file. 235 | 236 | # How to compile 237 | 238 | You need the Borland C++ 3.0 development environment to be able to compile 239 | the program from source. At the moment the code is not fully compatible with 240 | other compilers, so executables are included. 241 | 242 | There are two projects, `UI.PRJ` and `RLOADER/RLOADER.PRJ`, both of which 243 | can be opened in the IDE and compiled. The `RLOADER.EXE` executable needs to 244 | be copied to the root folder and that's what needs to be run. 245 | 246 | # License 247 | 248 | MIT License, Copyright (c) 2021 Marco Sacchi 249 | -------------------------------------------------------------------------------- /RES/HELP.MD: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/marco-sacchi/RLoader/b80fbbe1f108600be1fc0e191aca42f49443c153/RES/HELP.MD -------------------------------------------------------------------------------- /RES/RLOADER.CFG: -------------------------------------------------------------------------------- 1 | # QMENU configuration file. 2 | # 3 | # Lines starting with # are comments. 4 | # White-space characters will be ignored and trailing 5 | # white-spaces will be stripped. 6 | # Key names cannot contains white-spaces and will be treated in 7 | # a case-insensitive manner. 8 | 9 | # User interface colors. 10 | # 11 | # Background and foreground colors in HTML format. 12 | # 13 | ui-bg-color = #072869 14 | ui-fg-color = #9db3df 15 | 16 | # Lists drives remapping 17 | # 18 | # Drives letters specified onto list files can be remapped to others 19 | # to simplify migrations to other systems configurations. 20 | # i.e.: 21 | # D = F 22 | # means that D drive letter on list file will be automatically remapped 23 | # to F when program is launched or setup without changing the list file. 24 | # Drives that do not needs remappings can be omitted. 25 | # 26 | 27 | -------------------------------------------------------------------------------- /RES/SCREEN01.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/marco-sacchi/RLoader/b80fbbe1f108600be1fc0e191aca42f49443c153/RES/SCREEN01.PNG -------------------------------------------------------------------------------- /RES/SCREEN02.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/marco-sacchi/RLoader/b80fbbe1f108600be1fc0e191aca42f49443c153/RES/SCREEN02.PNG -------------------------------------------------------------------------------- /RES/SCREEN03.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/marco-sacchi/RLoader/b80fbbe1f108600be1fc0e191aca42f49443c153/RES/SCREEN03.PNG -------------------------------------------------------------------------------- /RES/SCREEN04.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/marco-sacchi/RLoader/b80fbbe1f108600be1fc0e191aca42f49443c153/RES/SCREEN04.PNG -------------------------------------------------------------------------------- /RES/SCREEN05.PNG: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/marco-sacchi/RLoader/b80fbbe1f108600be1fc0e191aca42f49443c153/RES/SCREEN05.PNG -------------------------------------------------------------------------------- /RES/SPLASH.BMP: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/marco-sacchi/RLoader/b80fbbe1f108600be1fc0e191aca42f49443c153/RES/SPLASH.BMP -------------------------------------------------------------------------------- /RES/SPLASH40.ASC: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/marco-sacchi/RLoader/b80fbbe1f108600be1fc0e191aca42f49443c153/RES/SPLASH40.ASC -------------------------------------------------------------------------------- /RES/SPLASH40.BIN: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/marco-sacchi/RLoader/b80fbbe1f108600be1fc0e191aca42f49443c153/RES/SPLASH40.BIN -------------------------------------------------------------------------------- /RES/SPLASH80.ASC: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/marco-sacchi/RLoader/b80fbbe1f108600be1fc0e191aca42f49443c153/RES/SPLASH80.ASC -------------------------------------------------------------------------------- /RES/SPLASH80.BIN: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/marco-sacchi/RLoader/b80fbbe1f108600be1fc0e191aca42f49443c153/RES/SPLASH80.BIN -------------------------------------------------------------------------------- /RES/SPLASH80.BMP: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/marco-sacchi/RLoader/b80fbbe1f108600be1fc0e191aca42f49443c153/RES/SPLASH80.BMP -------------------------------------------------------------------------------- /RLOADER/MAKE-DBG.BAT: -------------------------------------------------------------------------------- 1 | @echo off 2 | tasm /ml /m4 /os /zi main.asm main.obj 3 | tlink /n /v /s main.obj, rloader.exe 4 | -------------------------------------------------------------------------------- /RLOADER/MAKE-REL.BAT: -------------------------------------------------------------------------------- 1 | @echo off 2 | tasm /ml /m4 /os /q main.asm main.obj 3 | tlink /n /s main.obj, rloader.exe 4 | -------------------------------------------------------------------------------- /RLOADER/TDCONFIG.TD: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/marco-sacchi/RLoader/b80fbbe1f108600be1fc0e191aca42f49443c153/RLOADER/TDCONFIG.TD -------------------------------------------------------------------------------- /SRC/ANSIPIC.CPP: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include "ansipic.hpp" 6 | 7 | ansi_picture::ansi_picture() { 8 | m_data = NULL; 9 | m_width = m_height = m_stride = 0; 10 | m_error = ANSIPIC_ERR_NONE; 11 | } 12 | 13 | ansi_picture::~ansi_picture() { 14 | this->unload(); 15 | } 16 | 17 | int ansi_picture::get_last_error() { 18 | return m_error; 19 | } 20 | 21 | uint16_t ansi_picture::get_width() { 22 | return m_width; 23 | } 24 | 25 | uint16_t ansi_picture::get_height() { 26 | return m_height; 27 | } 28 | 29 | const uint8_t *ansi_picture::get_data() { 30 | return m_data; 31 | } 32 | 33 | uint32_t ansi_picture::get_stride() { 34 | return m_stride; 35 | } 36 | 37 | bool_t ansi_picture::load(const char *filename, uint8_t width) { 38 | this->unload(); 39 | 40 | FILE *fp = fopen(filename, "rb"); 41 | if (!fp) { 42 | m_error = ANSIPIC_ERR_NOTFOUND; 43 | return FALSE; 44 | } 45 | 46 | fseek(fp, 0, SEEK_END); 47 | int file_size = (int)ftell(fp); 48 | 49 | if (((file_size >> 1) % width) != 0) { 50 | fclose(fp); 51 | m_error = ANSIPIC_ERR_INVALID; 52 | return FALSE; 53 | } 54 | 55 | m_width = width; 56 | m_stride = (uint16_t)width << 1; 57 | m_height = file_size / m_stride; 58 | 59 | m_data = (uint8_t *)malloc(file_size); 60 | if (m_data == NULL) { 61 | m_error = ANSIPIC_ERR_NOMEM; 62 | return FALSE; 63 | } 64 | 65 | rewind(fp); 66 | fread(m_data, m_stride, m_height, fp); 67 | fclose(fp); 68 | 69 | m_error = ANSIPIC_ERR_NONE; 70 | return TRUE; 71 | } 72 | 73 | void ansi_picture::unload() { 74 | if (m_data != NULL) 75 | free(m_data); 76 | 77 | m_error = ANSIPIC_ERR_NONE; 78 | m_stride = 0; 79 | m_data = NULL; 80 | } 81 | -------------------------------------------------------------------------------- /SRC/ANSIPIC.HPP: -------------------------------------------------------------------------------- 1 | #ifndef ANSIPIC_HPP 2 | #define ANSIPIC_HPP 3 | 4 | #include "types.hpp" 5 | 6 | #define ANSIPIC_ERR_NONE 0 /* No error. */ 7 | #define ANSIPIC_ERR_NOTFOUND -1 /* File not found. */ 8 | #define ANSIPIC_ERR_INVALID -2 /* Invalid/corrupted file. */ 9 | #define ANSIPIC_ERR_NOMEM -3 /* Not enough memory to allocate picture. */ 10 | 11 | /** Object to load ANSI/ASCII pictures. 12 | * 13 | * @note The supported format is binary 2-bytes per character. 14 | */ 15 | class ansi_picture { 16 | 17 | private: 18 | /** Last error value, see ANSIPIC_ERR_* defines. */ 19 | int m_error; 20 | 21 | /** Pointer to picture data. */ 22 | uint8_t *m_data; 23 | /** Width in characters. */ 24 | uint8_t m_width; 25 | /** Height in characters. */ 26 | uint8_t m_height; 27 | /** Stride in *bytes* between rows. */ 28 | uint16_t m_stride; 29 | 30 | /** Unload picture resources. */ 31 | void unload(); 32 | 33 | public: 34 | ansi_picture(); 35 | ~ansi_picture(); 36 | 37 | /** Get last error code after call to load method. 38 | * 39 | * @return Error code as ANSIPIC_ERR_* define. 40 | */ 41 | int get_last_error(); 42 | 43 | /** Get picture width. 44 | * 45 | * @return Width in pixels. 46 | */ 47 | uint16_t get_width(); 48 | /** Get picture height. 49 | * 50 | * @return Height in pixels. 51 | */ 52 | uint16_t get_height(); 53 | 54 | /** Get raw image data. 55 | * 56 | * @return Image data pointer. 57 | */ 58 | const uint8_t *get_data(); 59 | /** Get stride between image rows. 60 | * 61 | * @return Number of *bytes* to skip to the next row. 62 | */ 63 | uint32_t get_stride(); 64 | 65 | /** Load picture in memory. 66 | * 67 | * @param filename Fully-qualified path of ANSI file with extension. 68 | * @param width Picture width in characters. 69 | * @return TRUE on success, FALSE otherwise. 70 | */ 71 | bool_t load(const char *filename, uint8_t width); 72 | }; 73 | 74 | #endif 75 | -------------------------------------------------------------------------------- /SRC/BITMAP.CPP: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include "bitmap.hpp" 6 | 7 | bitmap::bitmap() { 8 | memset(&m_info, 0, sizeof(bitmap::info_header_t)); 9 | m_error = BMP_ERR_NONE; 10 | m_stride = 0; 11 | m_palette = NULL; 12 | m_image = NULL; 13 | } 14 | 15 | bitmap::~bitmap() { 16 | this->unload(); 17 | } 18 | 19 | int bitmap::get_last_error() { 20 | return m_error; 21 | } 22 | 23 | uint32_t bitmap::get_width() { 24 | return m_info.width; 25 | } 26 | 27 | uint32_t bitmap::get_height() { 28 | return m_info.height; 29 | } 30 | 31 | uint32_t *bitmap::get_palette(uint32_t *colors) { 32 | *colors = m_info.colors; 33 | return m_palette; 34 | } 35 | 36 | void *bitmap::get_image() { 37 | return m_image; 38 | } 39 | 40 | uint32_t bitmap::get_stride() { 41 | return m_stride; 42 | } 43 | 44 | bool_t bitmap::load(const char *filename) { 45 | this->unload(); 46 | 47 | FILE *fp = fopen(filename, "rb"); 48 | if (!fp) { 49 | m_error = BMP_ERR_NOTFOUND; 50 | return FALSE; 51 | } 52 | 53 | bitmap::file_header_t file_header; 54 | fread(&file_header, sizeof(bitmap::file_header_t), 1, fp); 55 | 56 | if (file_header.type != BMP_TYPE) { 57 | fclose(fp); 58 | m_error = BMP_ERR_INVALID; 59 | return FALSE; 60 | } 61 | 62 | fread(&(m_info.size), 4, 1, fp); 63 | fread(((uint8_t *)&m_info + 4), (size_t)m_info.size - 4, 1, fp); 64 | 65 | if (m_info.bpp != 4 || m_info.compression != 0) { 66 | fclose(fp); 67 | m_error = BMP_ERR_INVALID_FORMAT; 68 | return FALSE; 69 | } 70 | 71 | if (m_info.colors > 0) { 72 | m_palette = (uint32_t *)malloc((size_t)m_info.colors * 4); 73 | if (m_palette == NULL) { 74 | fclose(fp); 75 | this->unload(); 76 | m_error = BMP_ERR_NOMEM; 77 | return FALSE; 78 | } 79 | 80 | fread(m_palette, (size_t)m_info.colors * 4, 1, fp); 81 | } 82 | 83 | fseek(fp, file_header.offset, SEEK_SET); 84 | m_stride = (((m_info.bpp * m_info.width) >> 3) + 3) & ~0x03; 85 | 86 | m_image = malloc((size_t)(m_stride * m_info.height)); 87 | if (m_image == NULL) { 88 | fclose(fp); 89 | this->unload(); 90 | m_error = BMP_ERR_NOMEM; 91 | return FALSE; 92 | } 93 | 94 | uint32_t i; 95 | uint8_t *image_ptr = (uint8_t *)m_image; 96 | 97 | for (i = 0; i < m_info.height - 1; ++i) 98 | image_ptr += (uint16_t)m_stride; 99 | 100 | for (i = 0; i < m_info.height; ++i) { 101 | fread(image_ptr, (size_t)m_stride, 1, fp); 102 | image_ptr -= (uint16_t)m_stride; 103 | } 104 | 105 | fclose(fp); 106 | 107 | return TRUE; 108 | } 109 | 110 | void bitmap::unload() { 111 | if (m_palette != NULL) 112 | free(m_palette); 113 | 114 | if (m_image != NULL) 115 | free(m_image); 116 | 117 | memset(&m_info, 0x00, sizeof(bitmap::info_header_t)); 118 | m_error = BMP_ERR_NONE; 119 | m_stride = 0; 120 | m_palette = NULL; 121 | m_image = NULL; 122 | } 123 | -------------------------------------------------------------------------------- /SRC/BITMAP.HPP: -------------------------------------------------------------------------------- 1 | #ifndef BITMAP_HPP 2 | #define BITMAP_HPP 3 | 4 | #include "types.hpp" 5 | 6 | #define BMP_TYPE 0x4d42 7 | 8 | #define BMP_ERR_NONE 0 /* No error. */ 9 | #define BMP_ERR_NOTFOUND -1 /* File not found. */ 10 | #define BMP_ERR_INVALID -2 /* Invalid/corrupted bitmap file. */ 11 | #define BMP_ERR_NOMEM -3 /* Not enough memory to allocate bitmap. */ 12 | #define BMP_ERR_INVALID_FORMAT -4 /* Non 4-bit/pixel uncompressed bitmap.*/ 13 | 14 | #define BMP_CMP_NONE 0 /* None */ 15 | #define BMP_CMP_RLE8 1 /* RLE 8-bit/pixel, used only with 8-bit/pixel bitmaps */ 16 | #define BMP_CMP_RLE4 2 /* RLE 4-bit/pixel, used only with 4-bit/pixel bitmaps */ 17 | #define BMP_CMP_BITFIELDS 3 /* Huffman 1D, v2 RGB bit field masks, v3 RGBA */ 18 | #define BMP_CMP_JPEG 4 /* RLE-24, JPEG image for printing */ 19 | #define BMP_CMP_PNG 5 /* RLE-24, PNG image for printing */ 20 | #define BMP_CMP_ALPHABITFIELDS 6 /* RGBA bit field masks only Windows CE 5.0 with .NET 4.0 or later */ 21 | #define BMP_CMP_CMYK 11 /* None, only Windows Metafile CMYK */ 22 | #define BMP_CMP_CMYKRLE8 12 /* RLE-8, only Windows Metafile CMYK */ 23 | #define BMP_CMP_CMYKRLE4 13 /* RLE-4, only Windows Metafile CMYK */ 24 | 25 | #pragma pack(push, 1); 26 | 27 | /** Object to load uncompressed bitmap files. 28 | * 29 | * @note This is a simplified implementation, not all formats are supported. 30 | */ 31 | class bitmap { 32 | 33 | private: 34 | /** Bitmap file header. */ 35 | typedef struct { 36 | /** Type, must be BMP_TYPE. */ 37 | uint16_t type; 38 | /** Size of this header. */ 39 | uint32_t size; 40 | /** Reserved. */ 41 | uint16_t reserved, reserved2; 42 | /** Offset of image data from beginning of file. */ 43 | uint32_t offset; 44 | } file_header_t; 45 | 46 | /** Bitmap info header. */ 47 | typedef struct { 48 | /** Size of this header. */ 49 | uint32_t size; 50 | /** Bitmap width in pixels. */ 51 | int32_t width; 52 | /** Bitmap height in pixels. */ 53 | int32_t height; 54 | /** Number of color planes (must be 1). */ 55 | uint16_t planes; 56 | /** Number of bits per pixel. */ 57 | uint16_t bpp; 58 | /** Compression method being used. */ 59 | uint32_t compression; 60 | /** Size of the raw bitmap data; a dummy 0 can be given for BI_RGB bitmaps. */ 61 | uint32_t imgsize; 62 | /** Horizontal resolution of the image (pixel per meter). */ 63 | int32_t horiz_res; 64 | /** Vertical resolution of the image (pixel per meter). */ 65 | int32_t vert_res; 66 | /** Number of colors in the color palette, or 0 to default to 2n. */ 67 | uint32_t colors; 68 | /** Number of important colors used. */ 69 | uint32_t important_colors; 70 | } info_header_t; 71 | 72 | /** Last error value, see BMP_ERR_* defines. */ 73 | int m_error; 74 | /** Bitmap info header. */ 75 | info_header_t m_info; 76 | /** Image data stride in *bytes*. */ 77 | int32_t m_stride; 78 | /** Palette entries as packed 32-bit unsigned int 0x00BBGGRR. */ 79 | uint32_t *m_palette; 80 | /** Pointer to image. */ 81 | void *m_image; 82 | /** Unload bitmap resources. */ 83 | void unload(); 84 | 85 | public: 86 | bitmap(); 87 | ~bitmap(); 88 | 89 | /** Get last error code after call to load method. 90 | * 91 | * @return Error code as BMP_ERR_* define. 92 | */ 93 | int get_last_error(); 94 | 95 | /** Get bitmap width. 96 | * 97 | * @return Width in pixels. 98 | */ 99 | uint32_t get_width(); 100 | /** Get bitmap height. 101 | * 102 | * @return Height in pixels. 103 | */ 104 | uint32_t get_height(); 105 | 106 | /** Get pointer to palette data. 107 | * 108 | * @param colors Set to number of colors in the palette. 109 | * @return Pointer to first palette entry. 110 | */ 111 | uint32_t *get_palette(uint32_t *colors); 112 | /** Get raw image data. 113 | * 114 | * @return Image data pointer. 115 | */ 116 | void *get_image(); 117 | /** Get stride between image rows. 118 | * 119 | * @return Number of *bytes* to skip to the next row. 120 | */ 121 | uint32_t get_stride(); 122 | 123 | /** Load bitmap in memory. 124 | * 125 | * @param filename Fully-qualified path of bitmap file with extension. 126 | * @return TRUE on success, FALSE otherwise. 127 | */ 128 | bool_t load(const char *filename); 129 | }; 130 | 131 | #pragma pack(pop); 132 | 133 | #endif 134 | -------------------------------------------------------------------------------- /SRC/CONFIG.CPP: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include "string.hpp" 5 | #include "config.hpp" 6 | 7 | config::config() { 8 | for (int i = 0; i < 'Z' - 'A' + 1; ++i) 9 | m_drives_mapping[i] = 'A' + i; 10 | } 11 | 12 | config::~config() { 13 | 14 | } 15 | 16 | bool_t config::load(const char *filename) { 17 | FILE *fp = fopen(filename, "rt"); 18 | if (fp == NULL) 19 | return FALSE; 20 | 21 | char line[80], *key, *value; 22 | bool_t result = TRUE; 23 | int line_count = 1; 24 | while (fgets(line, 80, fp) != NULL) { 25 | if (*line == '#') { 26 | ++line_count; 27 | continue; 28 | } 29 | 30 | if (this->parse_line(line, &key, &value) == FALSE) { 31 | result = FALSE; 32 | break; 33 | } 34 | 35 | if (key == NULL && value == NULL) { 36 | ++line_count; 37 | continue; 38 | } 39 | 40 | strlwr(key); 41 | 42 | if (strcmp(key, "ui-bg-color") == 0) { 43 | strlwr(value); 44 | if (this->parse_color(value, &m_bg_color) == FALSE) { 45 | result = FALSE; 46 | break; 47 | } 48 | } else if (strcmp(key, "ui-fg-color") == 0) { 49 | strlwr(value); 50 | if (this->parse_color(value, &m_fg_color) == FALSE) { 51 | result = FALSE; 52 | break; 53 | } 54 | } else if (string::is_letter(*key) && strlen(key) == 1) { 55 | if (!string::is_letter(*value) || strlen(value) != 1) { 56 | result = FALSE; 57 | break; 58 | } 59 | 60 | strupr(key); 61 | strupr(value); 62 | if (*key != *value) 63 | m_drives_mapping[*key - 'A'] = *value; 64 | } else { 65 | result = FALSE; 66 | break; 67 | } 68 | ++line_count; 69 | } 70 | 71 | if (result == FALSE) 72 | printf("%s: Error parsing line %d\n", filename, line_count); 73 | 74 | fclose(fp); 75 | return result; 76 | } 77 | 78 | bool_t config::parse_line(char *line, char **key, char **value) { 79 | *key = NULL; 80 | *value = NULL; 81 | 82 | char *chr = line; 83 | 84 | // Strip line terminators and trailing spaces. 85 | char *new_line = strpbrk(line, "\n\r"); 86 | if (new_line != NULL) 87 | *new_line = NULL; 88 | 89 | while (string::is_white_space(*(--new_line)) && new_line > line) 90 | *new_line = NULL; 91 | 92 | // Skip white-spaces before key. 93 | while (string::is_white_space(*chr) && *chr != NULL) 94 | ++chr; 95 | 96 | // Empty line. 97 | if (*chr == NULL) 98 | return TRUE; 99 | 100 | // Set key pointer and go forward until white-space, 101 | // equal character or end of string. 102 | *key = chr; 103 | 104 | while (!string::is_white_space(*chr) && *chr != '=' && *chr != NULL) 105 | ++chr; 106 | 107 | if (*chr == NULL) 108 | return FALSE; 109 | 110 | if (*chr != '=') { 111 | *chr++ = NULL; 112 | while (string::is_white_space(*chr) && *chr != NULL) 113 | ++chr; 114 | 115 | if (*chr != '=') 116 | return FALSE; 117 | } 118 | 119 | *chr++ = NULL; 120 | 121 | // Skip white-spaces between equal character and start of value. 122 | while (string::is_white_space(*chr) && *chr != NULL) 123 | ++chr; 124 | 125 | // No value. 126 | if (*chr == NULL) 127 | return FALSE; 128 | 129 | // Set value. 130 | *value = chr; 131 | 132 | return TRUE; 133 | } 134 | 135 | bool_t config::parse_color(const char *value, vga::color_t *color) { 136 | int r, g, b; 137 | if (sscanf(value, "#%02x%02x%02x", &r, &g, &b) != 3) 138 | return FALSE; 139 | 140 | (*color)[0] = r; 141 | (*color)[1] = g; 142 | (*color)[2] = b; 143 | 144 | return TRUE; 145 | } 146 | 147 | char config::get_drive_mapping(char drive_letter) { 148 | if (!string::is_letter(drive_letter)) 149 | return NULL; 150 | 151 | if (drive_letter >= 'a') 152 | drive_letter -= 'a'; 153 | else 154 | drive_letter -= 'A'; 155 | 156 | if (drive_letter < 0 || drive_letter > ('Z' - 'A')) 157 | return NULL; 158 | 159 | return m_drives_mapping[drive_letter]; 160 | } 161 | -------------------------------------------------------------------------------- /SRC/CONFIG.HPP: -------------------------------------------------------------------------------- 1 | #ifndef CONFIG_HPP 2 | #define CONFIG_HPP 3 | 4 | #include "types.hpp" 5 | #include "vga.hpp" 6 | 7 | /** Configuration file reader and parser. */ 8 | class config { 9 | public: 10 | config(); 11 | ~config(); 12 | /** Read specified configuration file. 13 | * 14 | * @param filename Fully-qualified file path with extension. 15 | * @return TRUE on success, FALSE otherwise. 16 | */ 17 | bool_t load(const char *filename); 18 | /** Get background color 19 | * 20 | * @return Pointer to background color type. 21 | */ 22 | const vga::color_t *get_background_color() { return &m_bg_color; } 23 | /** Get foreground color 24 | * 25 | * @return Pointer to foreground color type. 26 | */ 27 | const vga::color_t *get_foreground_color() { return &m_fg_color; } 28 | /** Get mapping for specified drive. 29 | * 30 | * @param drive_letter Drive letter on list file. 31 | * @return Mapped drive letter. 32 | */ 33 | char get_drive_mapping(char drive_letter); 34 | 35 | private: 36 | /** Parse configuration file line. 37 | * 38 | * @note Line will be modified setting NULL characters to correctly 39 | * terminate key/values strings. 40 | * @param line Line to parse. 41 | * @param key Pointer to first character of key on passed line. 42 | * @param value Pointer to first character of value on passed line. 43 | */ 44 | bool_t parse_line(char *line, char **key, char **value); 45 | /** Parse HTML color to color type. 46 | * 47 | * @param value String representation in #RRGGBB format 48 | * @param color Valorized with parsed values. 49 | * @return TRUE on success, FALSE on parsing error. 50 | */ 51 | bool_t parse_color(const char *value, vga::color_t *color); 52 | 53 | /** Readed background and foregraound colors. */ 54 | vga::color_t m_bg_color, m_fg_color; 55 | /** Readed drives mapping. */ 56 | char m_drives_mapping['Z' - 'A' + 1]; 57 | }; 58 | 59 | #endif -------------------------------------------------------------------------------- /SRC/ENCODER.CPP: -------------------------------------------------------------------------------- 1 | /** Simulate low-end machine. */ 2 | //#define SLOW_MACHINE 3 | 4 | #ifdef SLOW_MACHINE 5 | #include 6 | #endif 7 | 8 | #include "encoder.hpp" 9 | 10 | #define NEW_ENCODER 1 11 | //#define OLD_ENCODER 1 12 | 13 | bitmap_encoder::bitmap_encoder(bitmap *bmp, uint8_t color_offset) { 14 | m_bitmap = bmp; 15 | m_image = (uint8_t *)bmp->get_image(); 16 | m_current_ptr = m_image; 17 | m_remaining_rows = (int)bmp->get_height(); 18 | m_color_offset = color_offset; 19 | m_color_offset |= m_color_offset << 4; 20 | m_plane_stride = (uint16_t)m_bitmap->get_width() >> 3; 21 | } 22 | 23 | bitmap_encoder::~bitmap_encoder() { 24 | m_bitmap = NULL; 25 | m_image = NULL; 26 | } 27 | 28 | bool_t bitmap_encoder::task() { 29 | if (m_bitmap == NULL || m_image == NULL || m_remaining_rows == 0) 30 | return FALSE; 31 | 32 | this->planar_4bits(m_current_ptr); 33 | 34 | m_current_ptr += (uint16_t)m_bitmap->get_stride(); 35 | --m_remaining_rows; 36 | 37 | #ifdef SLOW_MACHINE 38 | delay(10); 39 | #endif 40 | 41 | if (m_remaining_rows == 0) 42 | return TRUE; 43 | 44 | return FALSE; 45 | } 46 | 47 | void bitmap_encoder::planar_4bits(const uint8_t *row) { 48 | #if NEW_ENCODER 49 | uint16_t plane_stride = m_plane_stride; 50 | uint8_t *encoded = m_encode_buffer; 51 | uint8_t col_offset = (uint8_t)m_color_offset; 52 | 53 | asm { 54 | cli 55 | cld 56 | 57 | push es 58 | push ds 59 | 60 | lds si, [row] 61 | les di, [encoded] 62 | 63 | mov cx, plane_stride // compute end offset 64 | add cx, di 65 | 66 | mov ah, col_offset 67 | } 68 | next_packed: 69 | asm { 70 | push cx 71 | 72 | xor bx, bx // bl = plane0, bh = plane1 work bytes 73 | xor dx, dx // dl = plane2, dh = plane3 work bytes 74 | xor cl, cl // zero-based plane number 75 | mov ch, 6 // destination bit shift for packing 76 | } 77 | next_byte: 78 | asm { 79 | lodsb // al <- ds:si, two 4-bits pixels 80 | add al, ah // add color offset to the pixels 81 | 82 | mov cl, 3 // packing shifts count 83 | 84 | push ax // plane 0 85 | and al, 0x11 // mask two pixels to get plane 0 86 | mov ah, al // copy masked value 87 | shr ah, cl // shift copy to pack with original 88 | and al, 0x0f // mask pixel in the upper 4-bits 89 | or al, ah // merge copy and original bits 90 | 91 | xchg cl, ch // shift left on proper position 92 | shl al, cl 93 | xchg cl, ch 94 | 95 | or bl, al // OR to plane 0 working byte 96 | pop ax 97 | 98 | push ax // plane 1 99 | and al, 0x22 // mask two pixels to get plane 1 100 | mov ah, al // copy masked value 101 | shr ah, cl // shift copy to pack with original 102 | and al, 0x0f // mask pixel in the upper 4-bits 103 | or al, ah // merge copy and original bits 104 | shr al, 1 // shift to 000000xxb position 105 | 106 | xchg cl, ch // shift left on proper position 107 | shl al, cl 108 | xchg cl, ch 109 | 110 | or bh, al // OR to plane 1 working byte 111 | pop ax 112 | 113 | push ax // plane 2 114 | and al, 0x44 // mask two pixels to get plane 2 115 | mov ah, al // copy masked value 116 | shr ah, cl // shift copy to pack with original 117 | and al, 0x0f // mask pixel in the upper 4-bits 118 | or al, ah // merge copy and original bits 119 | dec cl // plane 2 120 | shr al, cl // shift to 000000xxb position 121 | 122 | xchg cl, ch // shift left on proper position 123 | shl al, cl 124 | xchg cl, ch 125 | 126 | or dl, al // OR to plane 2 working byte 127 | pop ax 128 | 129 | push ax // plane 3 130 | and al, 0x88 // mask two pixels to get plane 3 131 | mov ah, al // copy masked value 132 | inc cl // shift copy to pack with original 133 | shr ah, cl 134 | and al, 0x0f // mask pixel in the upper 4-bits 135 | or al, ah // merge copy and original bits 136 | shr al, cl // shift to 000000xxb position 137 | 138 | xchg cl, ch // shift left on proper position 139 | shl al, cl 140 | xchg cl, ch 141 | 142 | or dh, al // OR to plane 3 working byte 143 | pop ax 144 | 145 | sub ch, 2 // update shift value to next two bits 146 | jns next_byte // no sign, store on work bytes 147 | 148 | mov al, bl // write packed bits for plane 0 149 | stosb 150 | 151 | push di // store di for plane 0 152 | 153 | mov cx, plane_stride 154 | dec cx 155 | 156 | add di, cx // di is already incremented by stosb 157 | mov al, bh // write packed bits for plane 1 158 | stosb 159 | 160 | add di, cx // di is already incremented by stosb 161 | mov al, dl // write packed bits for plane 2 162 | stosb 163 | 164 | add di, cx // di is already incremented by stosb 165 | mov al, dh // write packed bits for plane 3 166 | stosb 167 | 168 | pop di // restore di for plane 0 next byte 169 | 170 | pop cx // restore plane stride 171 | 172 | cmp di, cx 173 | jge done // di == plane stride, done. 174 | 175 | jmp next_packed // ready to pack next 4 bytes 176 | } 177 | done: 178 | asm { 179 | lds si, [encoded] // reverse source and destination 180 | les di, [row] 181 | 182 | mov cx, plane_stride // compute buffer length 183 | shl cx, 1 184 | shl cx, 1 185 | 186 | mov bx, cx 187 | and bx, 1 188 | jz even // check for odd length 189 | 190 | movsb // move one byte 191 | } 192 | even: 193 | asm { 194 | shr cx, 1 195 | rep movsw // move a word at a time 196 | 197 | pop ds 198 | pop es 199 | 200 | sti 201 | } 202 | 203 | #elif OLD_ENCODER 204 | 205 | uint16_t width = (uint16_t)m_info.width; 206 | uint8_t *encoded = m_encode_buffer; 207 | uint8_t col_offset = (uint8_t)color_offset; 208 | 209 | asm { 210 | push ax 211 | push bx 212 | push cx 213 | push dx 214 | push si 215 | push di 216 | push ds 217 | push es 218 | 219 | lds si, [row] 220 | les di, [encoded] 221 | 222 | cld 223 | 224 | mov dh, col_offset 225 | mov dl, dh 226 | mov cl, 4 227 | shl dl, cl 228 | or dh, dl 229 | 230 | xor dl, dl /* plane index */ 231 | } 232 | plane_loop: 233 | asm { 234 | mov cx, width 235 | dec cx 236 | 237 | push si 238 | } 239 | row_loop: 240 | asm { 241 | mov bl, 4 /* shift to select pixel, alternate 0-4 */ 242 | mov bh, 8 /* 8 pixels block counter */ 243 | xor ax, ax 244 | 245 | push cx 246 | } 247 | block_loop_load: 248 | asm { 249 | lodsb 250 | add al, dh 251 | mov ch, al 252 | } 253 | block_loop_noload: 254 | asm { 255 | mov al, ch 256 | 257 | mov cl, bl 258 | shr al, cl 259 | mov cl, dl 260 | shr al, cl 261 | 262 | and al, 1 263 | mov cl, bh 264 | dec cl 265 | shl al, cl 266 | 267 | or ah, al 268 | 269 | xor bl, 4 270 | dec bh 271 | cmp bh, 0 272 | je block_done 273 | cmp bl, 0 274 | je block_loop_noload 275 | jmp block_loop_load 276 | } 277 | block_done: 278 | asm { 279 | mov al, ah 280 | stosb 281 | 282 | pop cx 283 | 284 | sub cx, 8 285 | jnc row_loop 286 | 287 | pop si 288 | 289 | inc dl 290 | cmp dl, 4 291 | jl plane_loop 292 | 293 | pop es 294 | pop ds 295 | pop di 296 | pop si 297 | pop dx 298 | pop cx 299 | pop bx 300 | pop ax 301 | } 302 | 303 | memcpy((void *)row, m_encode_buffer, (size_t)m_stride); 304 | 305 | #else 306 | 307 | uint8_t *pixel; 308 | uint8_t *encoded = m_encode_buffer; 309 | uint8_t block_size = (8 * m_info.bpp) >> 3; 310 | uint8_t pixel_shift = 4; 311 | 312 | pixel = (uint8_t *)row; 313 | for (int32_t x = 0; x < m_stride; ++x) 314 | *pixel++ = (uint8_t)(((*pixel & 0x0f) + color_offset) | 315 | (*pixel & 0xf0) + (color_offset << 4)); 316 | 317 | for (uint16_t plane = 0; plane < 4; ++plane) { 318 | pixel = (uint8_t *)row; 319 | for (int32_t i = 0; i < m_info.width; i += 8) { 320 | *encoded = 0; 321 | for (uint16_t j = 0; j < 8; ++j, pixel_shift ^= 4) 322 | *encoded |= 323 | (((pixel[j >> 1] /* select byte */ 324 | >> pixel_shift /* select 4-bit pixel */ 325 | ) >> plane) & 1) /* get pixel bit by plane */ 326 | << (7 - j); /* shift to set correct bit */ 327 | 328 | ++encoded; 329 | pixel += block_size; 330 | } 331 | } 332 | 333 | memcpy((void *)row, m_encode_buffer, (size_t)m_stride); 334 | #endif 335 | } 336 | -------------------------------------------------------------------------------- /SRC/ENCODER.HPP: -------------------------------------------------------------------------------- 1 | #ifndef ENCODER_HPP 2 | #define ENCODER_HPP 3 | 4 | #include "types.hpp" 5 | #include "bitmap.hpp" 6 | 7 | /** Object to encode bitmaps for displaying on 4-bits planar modes. 8 | * 9 | * @note This implementation currently support only 4-bpp bitmaps. 10 | */ 11 | class bitmap_encoder { 12 | public: 13 | /** Create and initialize the encoder. 14 | * 15 | * @param bmp Bitmap to be encoded. 16 | * @param color_offset Offset applied to color indices. 17 | * @param callback Callback to be called when encoding is complete. 18 | */ 19 | bitmap_encoder(bitmap *bmp, uint8_t color_offset = 0); 20 | ~bitmap_encoder(); 21 | 22 | /** Get bitmap encoder is working on. 23 | * 24 | * @return Pointer to bitmap. 25 | */ 26 | bitmap *get_bitmap() { return m_bitmap; } 27 | 28 | /** Do background encoding. 29 | * 30 | * @return TRUE when image encoding is complete, FALSE otherwise. 31 | */ 32 | bool_t task(); 33 | 34 | private: 35 | /** Bitmap to be encoded for planar display. */ 36 | bitmap *m_bitmap; 37 | /** Pointer to image to be encoded in-place. */ 38 | uint8_t *m_image; 39 | /** Encoding buffer. */ 40 | uint8_t m_encode_buffer[512]; 41 | /** Offset applied to color index. */ 42 | uint8_t m_color_offset; 43 | /** Plane stride in bytes. */ 44 | uint16_t m_plane_stride; 45 | /** Pointer to row to be encoded. */ 46 | uint8_t *m_current_ptr; 47 | /** Number of rows remaining to complete image encoding. */ 48 | int m_remaining_rows; 49 | 50 | /** Encode row to be displayed on a planar 4-bits mode. 51 | * 52 | * @param row Pointer to raw data image row. 53 | */ 54 | void planar_4bits(const uint8_t *row); 55 | }; 56 | 57 | #endif 58 | -------------------------------------------------------------------------------- /SRC/ENTRY.CPP: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include "string.hpp" 5 | #include "math.hpp" 6 | #include "entry.hpp" 7 | 8 | list_entry::list_entry() { 9 | m_path = m_executable = m_setup = m_title = NULL; 10 | m_cycles = 0; 11 | } 12 | 13 | list_entry::~list_entry() { 14 | if (m_path != NULL) 15 | free(m_path); 16 | 17 | if (m_executable != NULL) 18 | free(m_executable); 19 | 20 | if (m_setup != NULL) 21 | free(m_setup); 22 | 23 | if (m_title != NULL) 24 | free(m_title); 25 | 26 | m_path = m_executable = m_setup = m_title = NULL; 27 | m_cycles = 0; 28 | } 29 | 30 | bool_t list_entry::parse(const char *line) { 31 | if (line == NULL || *line == NULL) 32 | return FALSE; 33 | 34 | const char *src_ptr = line; 35 | char buffer[64]; 36 | 37 | int length = this->parse_field(src_ptr, buffer); 38 | if (length < 0) 39 | return FALSE; 40 | 41 | m_path = (char *)malloc(strlen(buffer) + 1); 42 | strcpy(m_path, buffer); 43 | 44 | src_ptr += length; 45 | length = this->parse_field(src_ptr, buffer); 46 | if (length < 0) 47 | return FALSE; 48 | 49 | m_executable = (char *)malloc(strlen(buffer) + 1); 50 | strcpy(m_executable, buffer); 51 | 52 | src_ptr += length; 53 | length = this->parse_field(src_ptr, buffer); 54 | if (length < 0) 55 | return FALSE; 56 | 57 | m_setup = (char *)malloc(strlen(buffer) + 1); 58 | strcpy(m_setup, buffer); 59 | 60 | src_ptr += length; 61 | length = this->parse_cycles(src_ptr, &m_cycles); 62 | if (length < 0) 63 | return FALSE; 64 | 65 | src_ptr += length; 66 | 67 | // Search for title string. 68 | while (string::is_white_space(*src_ptr)) 69 | ++src_ptr; 70 | 71 | length = strlen(src_ptr); 72 | char *new_line = strpbrk(src_ptr, "\n\r"); 73 | if (new_line != NULL) { 74 | length = min((int)(new_line - (char *)src_ptr), length); 75 | *new_line = NULL; 76 | } 77 | 78 | new_line = (char *)src_ptr + length - 1; 79 | while (string::is_white_space(*new_line) && length > 0) { 80 | *new_line-- = NULL; 81 | --length; 82 | } 83 | 84 | if (length < 1) 85 | return FALSE; 86 | 87 | m_title = (char *)malloc(length + 1); 88 | strcpy(m_title, src_ptr); 89 | 90 | char *chr = m_title; 91 | while (*chr != NULL) { 92 | if (string::is_white_space(*chr)) 93 | *chr = ' '; 94 | 95 | ++chr; 96 | } 97 | 98 | return TRUE; 99 | } 100 | 101 | int list_entry::parse_field(const char *source, const char *dest) { 102 | const char *src_ptr = source; 103 | while (string::is_white_space(*src_ptr)) { 104 | if (*src_ptr == NULL) 105 | return -1; 106 | 107 | ++src_ptr; 108 | } 109 | 110 | char *dest_ptr = (char *)dest; 111 | while (!string::is_white_space(*src_ptr)) { 112 | if (*src_ptr == NULL) 113 | return -1; 114 | 115 | *dest_ptr++ = *src_ptr++; 116 | } 117 | 118 | *dest_ptr = NULL; 119 | return (int)(src_ptr - source); 120 | } 121 | 122 | int list_entry::parse_cycles(const char *source, uint16_t *dest) { 123 | const char *src_ptr = source; 124 | while (string::is_white_space(*src_ptr)) { 125 | if (*src_ptr == NULL) 126 | return -1; 127 | 128 | ++src_ptr; 129 | } 130 | 131 | int digit_count = 0; 132 | *dest = 0; 133 | while (!string::is_white_space(*src_ptr)) { 134 | if (digit_count > 4 || *src_ptr == NULL || 135 | *src_ptr < '0' || *src_ptr > '9') 136 | return -1; 137 | 138 | *dest = (*dest * 10) + (*src_ptr++ - '0'); 139 | } 140 | 141 | return (int)(src_ptr - source); 142 | } 143 | 144 | bool_t list_entry::match(const char *search) { 145 | if (string::icase_match(this->get_folder(), search)) 146 | return TRUE; 147 | 148 | if (string::icase_match(m_title, search)) 149 | return TRUE; 150 | 151 | return FALSE; 152 | } 153 | 154 | const char *list_entry::get_folder() const { 155 | char *last_dir = max(strrchr(m_path, '\\'), strrchr(m_path, '/')); 156 | if (last_dir == NULL) 157 | last_dir = (char *)m_path; 158 | else 159 | ++last_dir; 160 | 161 | return last_dir; 162 | } 163 | -------------------------------------------------------------------------------- /SRC/ENTRY.HPP: -------------------------------------------------------------------------------- 1 | #ifndef ENTRY_HPP 2 | #define ENTRY_HPP 3 | 4 | #include "types.hpp" 5 | 6 | /** List entry object. */ 7 | class list_entry { 8 | public: 9 | list_entry(); 10 | ~list_entry(); 11 | 12 | /** Get entry index. 13 | * 14 | * @return Zero-based entry index into the list. 15 | */ 16 | uint16_t get_index() const { return m_index; } 17 | /** Set entry index. 18 | * 19 | * @param index Zero-based entry index into the list. 20 | */ 21 | void set_index(uint16_t index) { m_index = index; } 22 | 23 | /** Parse entry from list file. 24 | * 25 | * @param line Line of list file. 26 | * @return TRUE on successful parsing, FALSE otherwise. 27 | */ 28 | bool_t parse(const char *line); 29 | /** Test if this entry matches the search string. 30 | * 31 | * @param search Search string. 32 | * @return TRUE if entry matches, FALSE otherwise. 33 | */ 34 | bool_t match(const char *search); 35 | /** Get entry folder. 36 | * 37 | * @return The inner folder of entry path. 38 | */ 39 | const char *get_folder() const; 40 | 41 | /** Get entry path. 42 | * 43 | * @return Entry fully-qualified path. 44 | */ 45 | const char *get_path() const { return m_path; } 46 | /** Get entry executable. 47 | * 48 | * @return Executable filename with extension. 49 | */ 50 | const char *get_executable() const { return m_executable; } 51 | /** Get entry setup program. 52 | * 53 | * @return Setup program executable filename with extension. 54 | */ 55 | const char *get_setup() const { return m_setup; } 56 | /** Entry title. 57 | * 58 | * @return Entry descriptive title. 59 | */ 60 | const char *get_title() const { return m_title; } 61 | /** Get cycles required for emulation. 62 | * 63 | * @return The number of cycles to set when this program run on DOSBox. 64 | */ 65 | uint16_t get_cycles() const { return m_cycles; } 66 | /** Set cycles required for emulation. 67 | * 68 | * @param cycles The number of cycles to set when this program 69 | * run on DOSBox. 70 | */ 71 | void set_cycles(uint16_t cycles) { m_cycles = cycles; } 72 | 73 | private: 74 | /** Parse field. 75 | * 76 | * @param source List file line or part of it. 77 | * @param dest Valorized with readed field trimmed from whitespaces. 78 | * @return Number of characters parsed (whitespace included), -1 on error. 79 | */ 80 | int parse_field(const char *source, const char *dest); 81 | /** Parse cycles field as integer. 82 | * 83 | * @param source List file line or part of it. 84 | * @param dest Valorized with number of cycles. 85 | * @return Number of characters parsed (whitespace included), -1 on error. 86 | */ 87 | int parse_cycles(const char *source, uint16_t *dest); 88 | 89 | /** Zero-based entry index into the list. */ 90 | uint16_t m_index; 91 | /** Entry path, executable and setup program. */ 92 | char *m_path, *m_executable, *m_setup; 93 | /** Number of cycles to set when run entry on DOSBox. */ 94 | uint16_t m_cycles; 95 | /* Entry descriptive title. */ 96 | char *m_title; 97 | }; 98 | 99 | #endif -------------------------------------------------------------------------------- /SRC/GRAPHICS.HPP: -------------------------------------------------------------------------------- 1 | #ifndef GRAPHICS_HPP 2 | #define GRAPHICS_HPP 3 | 4 | #include "types.hpp" 5 | #include "vga.hpp" 6 | #include "ansipic.hpp" 7 | #include "bitmap.hpp" 8 | 9 | /** Graphics utility methods. */ 10 | class graphics { 11 | public: 12 | /** Convert unsigned 32-bit integer to rgb triplet. 13 | * 14 | * @param value Integer value. 15 | * @param color Valorized with rgb values. 16 | */ 17 | static void uint32_to_color(uint32_t value, vga::color_t *color); 18 | /** Linear interpolate two colors. 19 | * 20 | * @param from Source color. 21 | * @param to Destination color. 22 | * @param t Interpolation factor in range [0.0, 1.0]. 23 | * @param result Valorized with resulting interpolated color. 24 | */ 25 | static void lerp_color(const vga::color_t from, const vga::color_t to, 26 | float t, vga::color_t *result); 27 | /** Set palette contained on the bitmap on graphics card. 28 | * 29 | * @param bitmap Bitmap from which read the palette to set. 30 | */ 31 | static void set_palette(bitmap *bmp); 32 | /** Set palette contained in the bitmap on 16 color modes. 33 | * 34 | * @param bitmap Bitmap from which read the palette to set. 35 | * @param index_offset Offset to apply to palette index when color is set. 36 | */ 37 | static void set_ega_palette(bitmap *bmp, int index_offset = 0); 38 | /** Fade-in palette contained in the bitmap on 16 color modes. 39 | * 40 | * @param bmp Bitmap from which read the palette to fade in. 41 | * @param color Color from which fade. 42 | * @param duration Fading effect duration in milliseconds. 43 | */ 44 | static void ega_fade_in(bitmap *bmp, const vga::color_t color, 45 | int duration); 46 | /** Fade-out palette contained in the bitmap on 16 color modes. 47 | * 48 | * @param bmp Bitmap from which read the palette to fade out. 49 | * @param color Color to which fade. 50 | * @param duration Fading effect duration in milliseconds. 51 | */ 52 | static void ega_fade_out(bitmap *bmp, const vga::color_t color, 53 | int duration); 54 | /** Debug method to show palette on the firse row of pixels. */ 55 | static void show_palette(); 56 | 57 | /** Draw bitmap on specified position. 58 | * 59 | * @note This method is a specific implementation for 16 colors 60 | * planar modes and floor the x position the nearest multiple of 8. 61 | * @param bmp Bitmap to draw to the video memory. 62 | * @param x Horizontal position of top-left image corner. 63 | * @param y Vertical position of top-left image corner. 64 | */ 65 | static void draw(bitmap *bmp, uint16_t x, uint16_t y); 66 | 67 | /** Draw ANSI/ASCII picture on specified position. 68 | * 69 | * @param pic Picture to draw to the video memory. 70 | * @param row Zero-based character row of top-left picture corner. 71 | * @param column Zero-based character column of top-left picture corner. 72 | */ 73 | static void draw(ansi_picture *pic, uint8_t row, uint8_t column); 74 | 75 | /** Draw filled rectangle using text BIOS services. 76 | * 77 | * @param rect Rectangle to fill. 78 | * @param attrs Attrbutes of the drawed characters. 79 | */ 80 | static void draw_filled_rect(vga::text_rect_t *rect, uint8_t attrs); 81 | 82 | /** Draw frame using text BIOS services. 83 | * 84 | * @deprecated use uigraph::draw_frame 85 | * @param rect Rectangle into which draw the frame. 86 | * @param attrs Attrbutes of the drawed characters. 87 | */ 88 | static void draw_frame(vga::text_rect_t *rect, uint8_t attrs); 89 | 90 | /** Draw filled frame using text BIOS services. 91 | * 92 | * @deprecated use uigraph::draw_filled_frame 93 | * @param rect Rectangle into which draw the frame. 94 | * @param attrs Attrbutes of the drawed characters. 95 | */ 96 | static void draw_filled_frame(vga::text_rect_t *rect, uint8_t attrs); 97 | 98 | static void draw_outline(vga::text_rect_t *rect, uint8_t color); 99 | static void draw_focus_outline(vga::text_rect_t *rect, uint8_t color); 100 | 101 | /** Draw scroll bar using text BIOS services. 102 | * 103 | * @param rect Reference rectangle, the rendered scroll bar is of the 104 | * same height and positioned 1 character after the right 105 | * boundary of the rect. 106 | * @param attrs Attrbutes of the drawed characters. 107 | * @param count Number of entries represented by the scrollbar. 108 | * @param index Current entry that controls position of scrollbar thumb. 109 | */ 110 | static void draw_scroll_bar(vga::text_rect_t *rect, uint8_t attrs, 111 | int count, int index); 112 | 113 | /** Apply bold effect ORing the pixels of already rendered characters. 114 | * 115 | * @param row Row of first text character cell. 116 | * @param column Column of first text character cell. 117 | * @param chars Number of characters for which apply the effect. 118 | */ 119 | static void bold_effect(uint8_t row, uint8_t column, uint8_t chars); 120 | /** Apply italic effect shifting the pixels of already rendered characters. 121 | * 122 | * @param row Row of first text character cell. 123 | * @param column Column of first text character cell. 124 | * @param chars Number of characters for which apply the effect. 125 | */ 126 | static void italic_effect(uint8_t row, uint8_t column, uint8_t chars); 127 | 128 | private: 129 | /** Linear interpolate palette in the bitmap aganist specified color. 130 | * 131 | * @param bmp Bitmap from which read the palette to interpolate. 132 | * @param to_color Color into which interpolate. 133 | * @param t Interpolation factor in range [0.0, 1.0]. 134 | */ 135 | static void lerp_ega_palette(bitmap *bmp, const vga::color_t to_color, float t); 136 | 137 | static void draw_horiz_line(int x, int y, int length, uint8_t color); 138 | 139 | static void draw_vert_block(int x, int y, int length, uint8_t color, uint8_t bits); 140 | 141 | static void or_horiz_line(int x, int y, int length, uint8_t mask, uint8_t bits); 142 | 143 | static void or_vert_block(int x, int y, int length, uint8_t mask, uint8_t bits); 144 | }; 145 | 146 | #endif 147 | -------------------------------------------------------------------------------- /SRC/ISR.CPP: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include "isr.hpp" 5 | 6 | isr::isrptr_t isr::get(int int_num) { 7 | return (isr::isrptr_t)getvect(int_num); 8 | } 9 | 10 | isr::isrptr_t isr::set(int int_num, isrptr_t new_isr) { 11 | isr::isrptr_t old_isr = isr::get(int_num); 12 | setvect(int_num, new_isr); 13 | 14 | return old_isr; 15 | } 16 | -------------------------------------------------------------------------------- /SRC/ISR.HPP: -------------------------------------------------------------------------------- 1 | #ifndef ISR_HPP 2 | #define ISR_HPP 3 | 4 | #ifdef __cplusplus 5 | #define __CPPARGS ... 6 | #else 7 | #define __CPPARGS 8 | #endif 9 | 10 | class isr { 11 | public: 12 | /** Interrupt service routine definition. */ 13 | typedef void interrupt (far *isrptr_t)(__CPPARGS); 14 | 15 | /** Get pointer to specified interrupt service routine. 16 | * 17 | * @param int_num Interrupt number. 18 | * @return Interrupt service routine address. 19 | */ 20 | static isrptr_t get(int int_num); 21 | 22 | /** Set new interrupt service routine for specified interrupt. 23 | * 24 | * @param int_num Interrupt number. 25 | * @param new_isr New interrupt service routine address. 26 | * @return Old interrupt service routine address. 27 | */ 28 | static isrptr_t set(int int_num, isrptr_t new_isr); 29 | }; 30 | 31 | #endif 32 | -------------------------------------------------------------------------------- /SRC/KEYBOARD.HPP: -------------------------------------------------------------------------------- 1 | #ifndef KEYBOARD_HPP 2 | #define KEYBOARD_HPP 3 | 4 | #include "types.hpp" 5 | #include "isr.hpp" 6 | 7 | /** Virtual shifts bits definitions. */ 8 | 9 | #define VS_LSHIFT (1 << 0) 10 | #define VS_RSHIFT (1 << 1) 11 | #define VS_SHIFT (VS_RSHIFT | VS_LSHIFT) 12 | #define VS_LCTRL (1 << 2) 13 | #define VS_RCTRL (1 << 3) 14 | #define VS_CTRL (VS_RCTRL | VS_LCTRL) 15 | #define VS_LALT (1 << 4) 16 | #define VS_RALT (1 << 5) 17 | #define VS_ALT (VS_RALT | VS_LALT) 18 | #define VS_META (1 << 6) 19 | #define VS_MENU (1 << 7) 20 | #define VS_PAUSE (1 << 8) 21 | #define VS_SCRLLOCK (1 << 9) 22 | #define VS_NUMLOCK (1 << 10) 23 | #define VS_CAPSLOCK (1 << 11) 24 | #define VS_INSERT (1 << 12) 25 | 26 | /** Virual keys definitions. 27 | * 28 | * @note If the virtual key codes are modified it si necessary to modify 29 | * also the map in the cpp file (@see vk_to_char_map). 30 | */ 31 | 32 | #define VK_ESC 0x01 33 | #define VK_1 0x02 34 | #define VK_2 0x03 35 | #define VK_3 0x04 36 | #define VK_4 0x05 37 | #define VK_5 0x06 38 | #define VK_6 0x07 39 | #define VK_7 0x08 40 | #define VK_8 0x09 41 | #define VK_9 0x0a 42 | #define VK_0 0x0b 43 | #define VK_BKSPC 0x0e 44 | 45 | #define VK_TAB 0x0f 46 | #define VK_Q 0x10 47 | #define VK_W 0x11 48 | #define VK_E 0x12 49 | #define VK_R 0x13 50 | #define VK_T 0x14 51 | #define VK_Y 0x15 52 | #define VK_U 0x16 53 | #define VK_I 0x17 54 | #define VK_O 0x18 55 | #define VK_P 0x19 56 | #define VK_ENTER 0x1c 57 | 58 | #define VK_A 0x1e 59 | #define VK_S 0x1f 60 | #define VK_D 0x20 61 | #define VK_F 0x21 62 | #define VK_G 0x22 63 | #define VK_H 0x23 64 | #define VK_J 0x24 65 | #define VK_K 0x25 66 | #define VK_L 0x26 67 | 68 | #define VK_Z 0x2c 69 | #define VK_X 0x2d 70 | #define VK_C 0x2e 71 | #define VK_V 0x2f 72 | #define VK_B 0x30 73 | #define VK_N 0x31 74 | #define VK_M 0x32 75 | 76 | #define VK_SPACE 0x39 77 | 78 | #define VK_F1 0x3b 79 | #define VK_F2 0x3c 80 | #define VK_F3 0x3d 81 | #define VK_F4 0x3e 82 | #define VK_F5 0x3f 83 | #define VK_F6 0x40 84 | #define VK_F7 0x41 85 | #define VK_F8 0x42 86 | #define VK_F9 0x43 87 | #define VK_F10 0x44 88 | #define VK_F11 0x45 89 | #define VK_F12 0x46 90 | 91 | #define VK_HOME 0x47 92 | #define VK_UP 0x48 93 | #define VK_PGUP 0x49 94 | #define VK_LEFT 0x4b 95 | #define VK_RIGHT 0x4d 96 | #define VK_END 0x4f 97 | #define VK_DOWN 0x50 98 | #define VK_PGDN 0x51 99 | #define VK_INS 0x52 100 | #define VK_DEL 0x53 101 | 102 | // From here virtual key codes do not matches scan codes. 103 | 104 | #define VK_NUM0 0xb0 105 | #define VK_NUM1 0xb1 106 | #define VK_NUM2 0xb2 107 | #define VK_NUM3 0xb3 108 | #define VK_NUM4 0xb4 109 | #define VK_NUM5 0xb5 110 | #define VK_NUM6 0xb6 111 | #define VK_NUM7 0xb7 112 | #define VK_NUM8 0xb8 113 | #define VK_NUM9 0xb9 114 | #define VK_NUMDOT 0xba 115 | #define VK_NUMDIV 0xbb 116 | #define VK_NUMMUL 0xbc 117 | #define VK_NUMSUB 0xbd 118 | #define VK_NUMADD 0xbe 119 | 120 | #define VK_LSHIFT 0xd0 121 | #define VK_RSHIFT 0xd1 122 | #define VK_LCTRL 0xd2 123 | #define VK_RCTRL 0xd3 124 | #define VK_LALT 0xd4 125 | #define VK_RALT 0xd5 126 | #define VK_META 0xd6 127 | #define VK_MENU 0xd7 128 | #define VK_SCRLLOCK 0xd8 129 | #define VK_NUMLOCK 0xd9 130 | #define VK_CAPSLOCK 0xda 131 | 132 | #pragma pack(push, 1); 133 | 134 | /** Keyboard handling. */ 135 | class keyboard { 136 | public: 137 | /** Keyboard events. */ 138 | enum key_events { 139 | /** Key press. */ 140 | KEV_MAKE = 1, 141 | /** Key typematic repeat. */ 142 | KEV_PRESS, 143 | /** Key depress. */ 144 | KEV_BREAK 145 | }; 146 | 147 | /** Structure filled with data read from keyboard. */ 148 | typedef struct { 149 | /** Event type. */ 150 | key_events type; 151 | /** ASCII value. */ 152 | uint8_t ascii; 153 | /** Raw scan code. */ 154 | uint8_t scan_code; 155 | /** Virtual key code (@see VK_* defines). */ 156 | uint8_t virt_key; 157 | /** Virtual shifts pressed states (@see VS_* defines). */ 158 | uint16_t shifts; 159 | /** Virtual shifts locked states (@see VS_* defines). */ 160 | uint16_t locked_shifts; 161 | } read_data_t; 162 | 163 | /** Initialize keyboard handling chaining old ISR with new one. 164 | * 165 | * @return TRUE on success, FALSE otherwise. 166 | */ 167 | static bool_t init(); 168 | /** Release keyboard resources restoring old ISR. 169 | * 170 | * @return TRUE on success, FALSE otherwise. 171 | */ 172 | static bool_t release(); 173 | 174 | /** Read an event from the keyboard. 175 | * 176 | * @param data Filled with info about keyboard event. 177 | * @return TRUE if an event is ready, FALSE otherwise. 178 | */ 179 | static bool_t read(read_data_t *data); 180 | 181 | /** Check if a key has changed its state from not pressed to pressed. 182 | * 183 | * @return TRUE if a key has changed its state to pressed, FALSE otherwise. 184 | */ 185 | static bool_t is_key_make_avail(); 186 | /** Check if a key has changed its state from pressed to not pressed. 187 | * 188 | * @return TRUE if a key has changed its state to not pressed, 189 | * FALSE otherwise. 190 | */ 191 | static bool_t is_key_break_avail(); 192 | /** Read data about key not pressed/pressed state change. 193 | * 194 | * @param data Filled with info about key that has changed state. 195 | */ 196 | static void read_make_break(read_data_t *data); 197 | 198 | /** Check if key is available to be read (pressed or typematic repeat). 199 | * 200 | * @return TRUE if key is available, FALSE otherwise. 201 | */ 202 | static bool_t is_key_avail(); 203 | /** Read data about key pressed/repeated. 204 | * 205 | * @param data Filled with info about key that has pressed/repeated. 206 | */ 207 | static void read_press(read_data_t *data); 208 | 209 | /** Convert virtual key code to uppercase ascii character. 210 | * 211 | * @return Uppercase ascii character, zero if not virtual key is not 212 | * representable as a non-extended ascii character. 213 | */ 214 | static char virtual_key_to_char(uint8_t virt_key); 215 | 216 | private: 217 | static isr::isrptr_t old_isr; 218 | 219 | /** Normalize read data adding virtual keys values. */ 220 | static void normalize(read_data_t *data); 221 | /** Update bits of keyboard shift keys states. */ 222 | static void update_shifts_state(); 223 | 224 | /** ISR routine that read data from the keyboard and call old isr. */ 225 | static void interrupt isr_handler(__CPPARGS); 226 | }; 227 | 228 | #pragma pack(pop); 229 | 230 | #endif 231 | -------------------------------------------------------------------------------- /SRC/KEYB_ISR.ASM: -------------------------------------------------------------------------------- 1 | .8086 2 | LOCALS 3 | 4 | _TEXT segment byte public use16 'CODE' 5 | ; 6 | ; void interrupt keyboard::isr_handler(__CPPARGS); 7 | ; 8 | assume cs:_TEXT, ds:DGROUP, es:NOTHING 9 | @keyboard@isr_handler$qve proc far 10 | cli 11 | 12 | push ax ; Placeholders to store 13 | push ax ; original ISR address popped 14 | ; by retf instruction. 15 | 16 | pushf ; Save context. 17 | push ax 18 | push bp 19 | push sp 20 | push ds 21 | 22 | mov bp, DGROUP 23 | mov ds, bp 24 | mov bp, sp 25 | 26 | mov ax, word ptr [@keyboard@old_isr] ; Store original handler SEG 27 | mov word ptr [bp + 10], ax ; to pushed ax, line 12, and 28 | mov ax, word ptr [@keyboard@old_isr + 2]; store original handler 29 | mov word ptr [bp + 12], ax ; OFFSET to pushed ax, line 13 30 | 31 | in al, 060h 32 | mov ah, al 33 | and al, 07fh 34 | cmp al, 058h 35 | ja short @@escape 36 | 37 | mov byte ptr [_key_make_break], ah 38 | 39 | @@escape: 40 | 41 | ifdef DEBUG_KEYB 42 | call debug_keyb_isr 43 | endif 44 | 45 | pop ds ; Restore context. 46 | pop sp 47 | pop bp 48 | pop ax 49 | popf 50 | 51 | retf ; retf pop the placeholders 52 | ; from the stack and do a far 53 | ; jump to original ISR. 54 | @keyboard@isr_handler$qve endp 55 | 56 | ifdef DEBUG_KEYB 57 | 58 | debug_keyb_isr proc far 59 | push ax 60 | push bx 61 | push cx 62 | push ds 63 | push es 64 | push si 65 | push di 66 | 67 | mov bl, al 68 | and al, 80h 69 | jnz @@key_break 70 | mov al, 02h 71 | jmp @@key_make 72 | 73 | @@key_break: 74 | mov al, 01h 75 | 76 | @@key_make: 77 | cld 78 | 79 | les di, dword ptr [_keys_pressed] 80 | xor bh, bh 81 | and bl, 7fh 82 | add di, bx 83 | stosb 84 | 85 | lds si, dword ptr [_keys_pressed] 86 | mov bx, 0b800h 87 | mov es, bx 88 | mov di, 80 * 2 * 4 89 | 90 | mov cx, 80h 91 | mov ah, 07h 92 | @@show_keys: 93 | lodsb 94 | stosw 95 | loopnz @@show_keys 96 | 97 | pop di 98 | pop si 99 | pop es 100 | pop ds 101 | pop cx 102 | pop bx 103 | pop ax 104 | debug_keyb_isr endp 105 | 106 | endif 107 | 108 | _TEXT ends 109 | 110 | DGROUP group _DATA, _BSS 111 | 112 | _DATA segment word public use16 'DATA' 113 | extrn @keyboard@old_isr:dword 114 | extrn _old_key_make_break:byte 115 | extrn _key_make_break:byte 116 | 117 | ifdef DEBUG_KEYB 118 | extrn _keys_pressed:dword 119 | endif 120 | _DATA ends 121 | 122 | _BSS segment word public use16 'BSS' 123 | _BSS ends 124 | 125 | public @keyboard@isr_handler$qve 126 | 127 | end 128 | -------------------------------------------------------------------------------- /SRC/LIST.CPP: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include "list.hpp" 6 | #include "string.hpp" 7 | 8 | #define LINE_BUFFER_SIZE 128 9 | 10 | list::list() { 11 | m_entries = NULL; 12 | m_entry_list = NULL; 13 | m_entry_count = 0; 14 | } 15 | 16 | list::~list() { 17 | this->unload(); 18 | } 19 | 20 | bool_t list::load(const char *name) { 21 | char path[LIST_PATH_SIZE]; 22 | strcpy(path, "lists/"); 23 | strcat(path, name); 24 | strcpy(m_list_path, path); 25 | strcat(path, "/list.txt"); 26 | 27 | FILE *fp = fopen(path, "rt"); 28 | 29 | if (!fp) { 30 | this->unload(); 31 | return FALSE; 32 | } 33 | 34 | m_entry_count = this->count_entries(fp); 35 | if (m_entry_count <= 0) { 36 | fclose(fp); 37 | this->unload(); 38 | return FALSE; 39 | } 40 | 41 | m_entries = new list_entry[m_entry_count]; 42 | m_entry_list = (list_entry **)malloc(sizeof(list_entry *) * m_entry_count); 43 | if (m_entries == NULL || m_entry_list == NULL) { 44 | fclose(fp); 45 | this->unload(); 46 | return FALSE; 47 | } 48 | 49 | this->clear_entry_list(); 50 | 51 | if (this->read_entries(fp) == FALSE) { 52 | fclose(fp); 53 | this->unload(); 54 | return FALSE; 55 | } 56 | 57 | return TRUE; 58 | } 59 | 60 | int list::count_entries(FILE *fp) { 61 | int count = 0; 62 | char *line_buffer = (char *)malloc(LINE_BUFFER_SIZE); 63 | if (line_buffer == NULL) 64 | return -1; 65 | 66 | fseek(fp, 0, SEEK_SET); 67 | char *newline = NULL; 68 | while (fgets(line_buffer, LINE_BUFFER_SIZE, fp) != NULL) { 69 | line_buffer[LINE_BUFFER_SIZE - 1] = '\0'; 70 | newline = strchr(line_buffer, '\n'); 71 | if (newline != NULL) 72 | *newline = '\0'; 73 | 74 | if (line_buffer[0] == '#' || string::is_empty(line_buffer)) 75 | continue; 76 | 77 | ++count; 78 | } 79 | 80 | free(line_buffer); 81 | 82 | return count; 83 | } 84 | 85 | bool_t list::read_entries(FILE *fp) { 86 | int count = 0; 87 | char *line_buffer = (char *)malloc(LINE_BUFFER_SIZE); 88 | if (line_buffer == NULL) 89 | return FALSE; 90 | 91 | fseek(fp, 0, SEEK_SET); 92 | char *newline = NULL; 93 | while (fgets(line_buffer, LINE_BUFFER_SIZE, fp) != NULL) { 94 | line_buffer[LINE_BUFFER_SIZE - 1] = NULL; 95 | newline = strchr(line_buffer, '\n'); 96 | if (newline != NULL) 97 | *newline = '\0'; 98 | 99 | if (line_buffer[0] == '#' || string::is_empty(line_buffer)) 100 | continue; 101 | 102 | if (m_entries[count].parse(line_buffer) == FALSE) { 103 | free(line_buffer); 104 | return FALSE; 105 | } 106 | m_entries[count].set_index(count); 107 | 108 | ++count; 109 | } 110 | 111 | free(line_buffer); 112 | 113 | return TRUE; 114 | } 115 | 116 | list_entry *list::get_entry(int index) { 117 | if (m_entries == NULL || index >= m_entry_count) 118 | return NULL; 119 | 120 | return &(m_entries[index]); 121 | } 122 | 123 | list_entry *list::get_list() { 124 | return m_entries; 125 | } 126 | 127 | list_entry **list::filter(const char *search, int *filtered_count) { 128 | *filtered_count = 0; 129 | if (m_entry_count == 0) 130 | return NULL; 131 | 132 | this->clear_entry_list(); 133 | 134 | list_entry **pptr = m_entry_list; 135 | 136 | for (int i = 0; i < m_entry_count; ++i) { 137 | if (search != NULL && strlen(search) > 0 && 138 | !m_entries[i].match(search)) 139 | continue; 140 | 141 | *pptr = &(m_entries[i]); 142 | ++pptr; 143 | ++(*filtered_count); 144 | } 145 | 146 | *pptr = NULL; 147 | 148 | return m_entry_list; 149 | } 150 | 151 | void list::clear_entry_list() { 152 | memset(m_entry_list, 0x00, sizeof(list_entry *) * m_entry_count); 153 | } 154 | 155 | void list::unload() { 156 | if (m_entries != NULL) 157 | delete m_entries; 158 | 159 | if (m_entry_list != NULL) 160 | free(m_entry_list); 161 | 162 | m_entries = NULL; 163 | m_entry_list = NULL; 164 | m_entry_count = 0; 165 | } -------------------------------------------------------------------------------- /SRC/LIST.HPP: -------------------------------------------------------------------------------- 1 | #ifndef LIST_HPP 2 | #define LIST_HPP 3 | 4 | #include 5 | 6 | #include "types.hpp" 7 | #include "entry.hpp" 8 | 9 | #define LIST_PATH_SIZE 40 10 | 11 | /** List object. */ 12 | class list { 13 | 14 | public: 15 | list(); 16 | ~list(); 17 | 18 | /** Load list file. 19 | * 20 | * @param path Fully-qualified path with extension. 21 | * @return TRUE on success, FALSE otherwise. 22 | */ 23 | bool_t load(const char *path); 24 | /** Release list resources. */ 25 | void unload(); 26 | 27 | /** Get list file path. 28 | * 29 | * @return Fully-qualified list file path. 30 | */ 31 | const char *get_path() { return m_list_path; } 32 | 33 | /** Get list entry by index. 34 | * 35 | * @param index Entry zero-based index. 36 | * @return Entry object at specified index. 37 | */ 38 | list_entry *get_entry(int index); 39 | /** Get list entries. 40 | * 41 | * @return Pointer to first entry. 42 | */ 43 | list_entry *get_list(); 44 | /** Get list entry count. 45 | * 46 | * @return Number of entries in list. 47 | */ 48 | int get_entry_count() { return m_entry_count; }; 49 | 50 | /** Filter list by search string. 51 | * 52 | * @param search Text to search in entries. 53 | * @param filtered_count Valorized with number of matching items. 54 | * @return Array of pointers to matching entries. 55 | */ 56 | list_entry **filter(const char *search, int *filtered_count); 57 | 58 | private: 59 | /** Count entries skipping comments and empty lines. 60 | * 61 | * @param fp List file pointer. 62 | * @return Number of entries. 63 | */ 64 | int count_entries(FILE *fp); 65 | /** Read entries on list file. 66 | * 67 | * @param fp List file pointer. 68 | * @return TRUE on succes, FALSE on parsing error. 69 | */ 70 | bool_t read_entries(FILE *fp); 71 | /** Clear array of pointers to list entries (used for filtering). */ 72 | void clear_entry_list(); 73 | 74 | /** Array of list entries. */ 75 | list_entry *m_entries; 76 | /** Array of pointers to list entries (used for filtering). */ 77 | list_entry **m_entry_list; 78 | /** List entries count. */ 79 | int m_entry_count; 80 | /** List path. */ 81 | char m_list_path[LIST_PATH_SIZE]; 82 | }; 83 | 84 | #endif -------------------------------------------------------------------------------- /SRC/MARKDOWN.HPP: -------------------------------------------------------------------------------- 1 | #ifndef MARKDOWN_HPP 2 | #define MARKDOWN_HPP 3 | 4 | #include 5 | 6 | #include "types.hpp" 7 | 8 | class md_token; 9 | 10 | /** Simple markdown parser. 11 | * 12 | * This pre-parser support a subset of availabile markdown features. 13 | * Supported syntax: 14 | * - ATX headings using 1 to 6 hash characters followed by space: # to ###### 15 | * - paragraphs ending with 2 consecutive eol: \n\n, or between other blocks 16 | * - ordered lists using number followed by dot: 1. 17 | * - unordered lists using dashes: - 18 | * - fenced code blocks using 3+ consecutive backticks or tildes: ``` or ~~~ 19 | * - italic using one asterisk: * 20 | * - bold using two consecutive asterisks: ** 21 | * - bold and italic using three consecutive asterisks: *** 22 | * - code/keys spans using backtick: ` 23 | * - inside code/keys spans italic and bold styles will be ignored 24 | */ 25 | class markdown { 26 | public: 27 | enum block_type { 28 | B_PARAGRAPH, 29 | B_HEADING, 30 | B_OLIST, 31 | B_ULIST, 32 | B_LIST_ITEM, 33 | B_CODE, 34 | B_COUNT // Count if block types. 35 | }; 36 | 37 | enum styles { 38 | S_NONE = 0, 39 | S_EMPHASIS = 1 << 0, 40 | S_STRONG = 1 << 1, 41 | S_CODE = 1 << 2 42 | }; 43 | 44 | enum token_type { 45 | T_BLOCK_START, 46 | T_BLOCK_END, 47 | T_SPAN, 48 | T_NEWLINE 49 | }; 50 | 51 | typedef struct { 52 | token_type type; 53 | block_type block; 54 | styles style; 55 | char *text; 56 | } token_t; 57 | 58 | typedef uint8_t indents_t[B_COUNT]; 59 | typedef bool_t (*token_receiver_t)(token_t *); 60 | 61 | markdown(int width, token_receiver_t f, indents_t indents); 62 | ~markdown(); 63 | 64 | bool_t set_source(const char *filename); 65 | 66 | int get_lines(); 67 | 68 | bool_t parse(); 69 | 70 | private: 71 | enum block_delim { 72 | PARAGRAPH_DELIM, 73 | HEADING_DELIM, 74 | OLIST_DELIM, 75 | ULIST_DELIM, 76 | CODE_DELIM 77 | }; 78 | 79 | typedef struct delim_info { 80 | block_delim type; 81 | int size; 82 | char ch; 83 | int level; 84 | }; 85 | 86 | typedef struct style_delim { 87 | styles style; 88 | bool_t left; 89 | }; 90 | 91 | bool_t is_block_delim(delim_info *info); 92 | bool_t is_style_delim(char prev, char curr, style_delim *info); 93 | 94 | bool_t parse_heading(); 95 | bool_t parse_olist_block(); 96 | bool_t parse_ulist_block(); 97 | bool_t parse_code_block(); 98 | bool_t parse_paragraph(); 99 | 100 | bool_t parse_text(); 101 | 102 | bool_t read_char(char *ch, int *count); 103 | bool_t unread_chars(int num_chars, int *count); 104 | 105 | bool_t write_char(char ch); 106 | bool_t unwrite_chars(int num_chars); 107 | 108 | void new_block_token(block_type block); 109 | void new_block_end_token(block_type block); 110 | void new_style_token(styles style); 111 | void new_line_token(); 112 | void emit_curr_token(); 113 | void reset_token_buffer(); 114 | 115 | void swap_tokens(); 116 | 117 | indents_t m_indents; 118 | 119 | FILE *m_input; 120 | int m_lines; 121 | int m_width; 122 | int m_curr_line_len; 123 | 124 | token_receiver_t m_token_receiver; 125 | token_t m_curr_token; 126 | char *m_curr_token_ptr; 127 | 128 | delim_info m_curr_delim; 129 | block_type m_curr_block; 130 | styles m_curr_style; 131 | 132 | md_token *m_token; 133 | }; 134 | 135 | class md_token { 136 | public: 137 | md_token(int buffer_size, markdown::token_receiver_t f); 138 | ~md_token(); 139 | 140 | void reset(); 141 | void set_block(markdown::block_type block); 142 | void set_indent(int indent); 143 | void new_style(markdown::styles style); 144 | void new_line(); 145 | 146 | void emit_tokens(); 147 | 148 | int len(); 149 | int width(); 150 | 151 | bool_t write_char(char ch); 152 | bool_t unwrite_chars(int num_chars); 153 | 154 | private: 155 | typedef struct { 156 | markdown::styles style; 157 | int offset; 158 | } style_change_t; 159 | 160 | int m_buffer_size; 161 | markdown::token_receiver_t m_token_receiver; 162 | 163 | markdown::block_type m_block; 164 | 165 | char *m_text; 166 | char *m_ptr; 167 | char *m_ws_ptr; 168 | 169 | style_change_t *m_styles; 170 | int m_styles_count; 171 | 172 | int m_indent; 173 | }; 174 | 175 | #endif 176 | -------------------------------------------------------------------------------- /SRC/MATH.CPP: -------------------------------------------------------------------------------- 1 | #include "math.hpp" 2 | -------------------------------------------------------------------------------- /SRC/MATH.HPP: -------------------------------------------------------------------------------- 1 | #ifndef MATH_HPP 2 | #define MATH_HPP 3 | 4 | /** Compute minimum of two values of same type. 5 | * 6 | * @param a First value. 7 | * @param b Second value. 8 | * @return Minimum value. 9 | */ 10 | template 11 | T min(T a, T b) { 12 | return ((a < b) ? a : b); 13 | } 14 | 15 | /** Compute maximum of two values of same type. 16 | * 17 | * @param a First value. 18 | * @param b Second value. 19 | * @return Maximum value. 20 | */ 21 | template 22 | T max(T a, T b) { 23 | return ((a > b) ? a : b); 24 | } 25 | 26 | /** Clamp value to specified boundaries inclusively. 27 | * 28 | * @param v Value. 29 | * @param min Minimum inclusive value. 30 | * @param max Maximum inclusive value. 31 | * @return Clamped value. 32 | */ 33 | template 34 | T clamp(T v, T min, T max) { 35 | if (max < min) 36 | max = min; 37 | 38 | if (v < min) return min; 39 | if (v > max) return max; 40 | 41 | return v; 42 | } 43 | 44 | /** Linear interpolate from v0 to v1. 45 | * 46 | * @param v0 First value. 47 | * @param v1 Second value. 48 | * @param t Interpolation factor in range [0.0, 1.0]. 49 | * @return Interpolated value of same type as v0 and v1. 50 | */ 51 | template 52 | T lerp(T v0, T v1, float t) { 53 | return (T)((1 - t) * v0 + t * v1); 54 | } 55 | 56 | #endif -------------------------------------------------------------------------------- /SRC/STRING.CPP: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include "string.hpp" 4 | 5 | bool_t string::is_eol(char c) { 6 | return (c == '\r' || c == '\n') ? TRUE : FALSE; 7 | } 8 | 9 | bool_t string::is_white_space(char c) { 10 | return (c == ' ' || c == '\t') ? TRUE : FALSE; 11 | } 12 | 13 | bool_t string::is_punctuation(char c) { 14 | return ((c >= 0x21 && c <= 0x2f) || 15 | (c >= 0x3a && c <= 0x40) || 16 | (c >= 0x5b && c <= 0x60) || 17 | (c >= 0x7b && c <= 0x7e)) ? TRUE : FALSE; 18 | } 19 | 20 | bool_t string::is_digit(char c) { 21 | return (c >= '0' && c <= '9') ? TRUE : FALSE; 22 | } 23 | 24 | bool_t string::is_letter(char c) { 25 | return ((c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z')) ? TRUE : FALSE; 26 | } 27 | 28 | bool_t string::icase_compare(char a, char b) { 29 | if (a == b) return TRUE; 30 | 31 | if (a >= 'a' && a <= 'z') a -= 32; 32 | if (b >= 'a' && b <= 'z') b -= 32; 33 | 34 | return (a == b) ? TRUE : FALSE; 35 | } 36 | 37 | bool_t string::is_empty(const char *str) { 38 | const char *ptr = str; 39 | while (string::is_white_space(*ptr)) 40 | ++ptr; 41 | 42 | return (*ptr == NULL) ? TRUE : FALSE; 43 | } 44 | 45 | int string::icase_index_of(const char *str, char search) { 46 | char search_str[2] = { search, NULL }; 47 | const char *ptr = string::icase_match(str, search_str); 48 | if (ptr == NULL) 49 | return -1; 50 | 51 | return (int)(ptr - str); 52 | } 53 | 54 | char string::last_char(const char *str) { 55 | int length = strlen(str); 56 | if (length == 0) 57 | return '\0'; 58 | 59 | return *(str + length - 1); 60 | } 61 | 62 | char *string::icase_match(const char *str, const char *search) { 63 | const char *source = str; 64 | const char *search_ptr = search; 65 | 66 | while (*source != NULL) { 67 | while (*source != NULL && !string::icase_compare(*source, *search)) 68 | source++; 69 | 70 | if (*source == NULL) 71 | return NULL; 72 | 73 | while (*source != NULL && *search_ptr != NULL && 74 | string::icase_compare(*source, *search_ptr)) { 75 | source++; 76 | search_ptr++; 77 | } 78 | 79 | if (*search_ptr == NULL) 80 | return (char *)(source - strlen(search)); 81 | 82 | search_ptr = search; 83 | } 84 | 85 | return NULL; 86 | } 87 | 88 | int string::icase_match_all(const char *str, const char *search, const char **matches) { 89 | int match_count = 0; 90 | const char *end = str + strlen(str) - 1; 91 | size_t search_len = strlen(search); 92 | if (end < str || search_len == 0) 93 | return 0; 94 | 95 | char *match = (char *)str; 96 | while (match <= end && 97 | (match = string::icase_match(match, search)) != NULL) { 98 | if (*match == NULL) 99 | break; 100 | 101 | matches[match_count++] = match; 102 | match += search_len; 103 | } 104 | 105 | return match_count; 106 | } -------------------------------------------------------------------------------- /SRC/STRING.HPP: -------------------------------------------------------------------------------- 1 | #ifndef STRING_HPP 2 | #define STRING_HPP 3 | 4 | #include "types.hpp" 5 | 6 | /** String utilites methods. */ 7 | class string { 8 | public: 9 | /** Test for end of line character. 10 | * 11 | * @param c Character to test. 12 | * @return TRUE if character is \r or \n, FALSE otherwise. 13 | */ 14 | static bool_t is_eol(char c); 15 | /** Test for whitespace character. 16 | * 17 | * @param c Character to test. 18 | * @return TRUE if character is whitespace, FALSE otherwise. 19 | */ 20 | static bool_t is_white_space(char c); 21 | /** Test for punctuation character. 22 | * 23 | * @param c Character to test. 24 | * @return TRUE if character is punctuation, FALSE otherwise. 25 | */ 26 | static bool_t is_punctuation(char c); 27 | /** Test for digit character. 28 | * 29 | * @param c Character to test. 30 | * @return TRUE if character is digit, FALSE otherwise. 31 | */ 32 | static bool_t is_digit(char c); 33 | /** Test for letter character. 34 | * 35 | * @note This method do not support extended ASCII characters. 36 | * @param c Character to test. 37 | * @return TRUE if character is letter, FALSE otherwise. 38 | */ 39 | static bool_t is_letter(char c); 40 | /** Compare two character case-insensitively. 41 | * 42 | * @note This method do not support extended ASCII characters. 43 | * @param a First character. 44 | * @param b Second character. 45 | * @return TRUE if characters match, FALSE otherwise. 46 | */ 47 | static bool_t icase_compare(char a, char b); 48 | /** Test for empty string. 49 | * 50 | * @param str String to test. 51 | * @return TRUE if string is made only of whitespace characters. 52 | */ 53 | static bool_t is_empty(const char *str); 54 | /** Find index of first occurence of searched characted. 55 | * 56 | * @param str String to search in. 57 | * @param search Character to search. 58 | * 59 | * @return Index of first occurence, -1 if not found. 60 | */ 61 | static int icase_index_of(const char *str, char search); 62 | /** Get last characted or a string. 63 | * 64 | * @param String for which return last character. 65 | * @return Last character or NULL if string has zero lenght. 66 | */ 67 | static char last_char(const char *str); 68 | /** Test for substring match case-insensitively. 69 | * 70 | * @param str String to search in. 71 | * @param search String to be searched. 72 | * @return Pointer to first match, NULL if no match are found. 73 | */ 74 | static char *icase_match(const char *str, const char *search); 75 | /** Return all case-insesitive matches of a substring. 76 | * 77 | * @note matches must be already allocated. 78 | * @param str String to search in. 79 | * @param search String to be searched. 80 | * @param matches Valorized with pointers to first character of matches. 81 | * @return Number of matches. 82 | */ 83 | static int icase_match_all(const char *str, const char *search, const char **matches); 84 | }; 85 | 86 | #endif 87 | -------------------------------------------------------------------------------- /SRC/TUI/BUTTON.CPP: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include "tui/button.hpp" 5 | #include "tui/graph.hpp" 6 | #include "math.hpp" 7 | #include "string.hpp" 8 | 9 | uibutton::uibutton(uint16_t id, int top, int left, int width, 10 | const char *caption) 11 | : uicontrol(id) { 12 | int caption_length = strlen(caption); 13 | m_caption = new char[caption_length + 1]; 14 | strcpy(m_caption, caption); 15 | 16 | m_pressed = FALSE; 17 | 18 | width = max(width, caption_length); 19 | m_rect.top = m_rect.bottom = top; 20 | m_rect.left = left; 21 | m_rect.right = left + width - 1; 22 | 23 | this->set_back_color_internal(uitheme::button::back); 24 | this->set_fore_color_internal(uitheme::button::fore); 25 | } 26 | 27 | uibutton::~uibutton() { 28 | delete m_caption; 29 | m_caption = NULL; 30 | } 31 | 32 | void uibutton::lose_focus() { 33 | // If button is pressed when lose focus, revert it to normal state 34 | // to avoid unwanted activation. 35 | if (m_pressed == TRUE) { 36 | m_pressed = FALSE; 37 | this->repaint(); 38 | } 39 | 40 | uicontrol::lose_focus(); 41 | } 42 | 43 | void uibutton::repaint() { 44 | if (this->can_repaint() == FALSE) { 45 | m_repaint_pending = TRUE; 46 | return; 47 | } 48 | 49 | vga::text_rect_t rect; 50 | this->get_paint_rect(&rect); 51 | 52 | vga::set_cursor_pos(rect.top, rect.left); 53 | vga::write_char(' ', m_paint_attrs, rect.right - rect.left + 1); 54 | 55 | int offset = ((rect.right - rect.left + 1) >> 1) - 56 | (strlen(m_caption) >> 1); 57 | vga::set_cursor_pos(rect.top, rect.left + offset); 58 | vga::write_string(m_caption, m_paint_attrs, 59 | vga::USE_ATTRS_NO_UPDATE_CURSOR); 60 | 61 | if (m_focused == TRUE) 62 | uigraph::apply_focus(&rect, m_paint_attrs, uigraph::FOCUS_SURFACE); 63 | else 64 | uigraph::remove_focus(&rect, m_paint_attrs, uigraph::FOCUS_SURFACE); 65 | 66 | if (m_accelerator != NULL && m_caption != NULL) { 67 | char *ch = m_caption; 68 | int accel_index = 0; 69 | while (*ch != NULL) { 70 | if (string::icase_compare(*ch, m_accelerator) == TRUE) { 71 | offset += accel_index + rect.left; 72 | uigraph::draw_accelerator(rect.top, offset, m_paint_attrs); 73 | break; 74 | } 75 | 76 | ++accel_index; 77 | ++ch; 78 | } 79 | } 80 | 81 | if (m_pressed == TRUE) 82 | uigraph::apply_active(&rect, m_paint_attrs); 83 | 84 | uicontrol::repaint(); 85 | } 86 | 87 | void uibutton::on_accelerator(tui::action_t *action) { 88 | m_pressed = TRUE; 89 | this->repaint(); 90 | 91 | delay(250); 92 | m_pressed = FALSE; 93 | this->repaint(); 94 | 95 | action->type = tui::TUIA_CTRL_ACTIVATED; 96 | action->id = m_id; 97 | action->control = this; 98 | } 99 | 100 | void uibutton::on_message(uiqueue::message_t *msg, tui::action_t *action) { 101 | if (msg->type == uiqueue::TUIM_KEYDOWN && m_pressed == FALSE) { 102 | keyboard::read_data_t *data = (keyboard::read_data_t *)msg->data; 103 | bool_t triggered = (data->virt_key == VK_SPACE || 104 | data->virt_key == VK_ENTER); 105 | 106 | if (triggered && data->shifts == 0) { 107 | m_pressed = TRUE; 108 | this->repaint(); 109 | } 110 | } else if (msg->type == uiqueue::TUIM_KEYUP && m_pressed == TRUE) { 111 | keyboard::read_data_t *data = (keyboard::read_data_t *)msg->data; 112 | bool_t triggered = (data->virt_key == VK_SPACE || 113 | data->virt_key == VK_ENTER || 114 | data->virt_key == VK_ESC); 115 | 116 | if (triggered && data->shifts == 0) { 117 | m_pressed = FALSE; 118 | this->repaint(); 119 | 120 | if (data->virt_key != VK_ESC) { 121 | action->type = tui::TUIA_CTRL_ACTIVATED; 122 | action->id = m_id; 123 | action->control = this; 124 | } 125 | } 126 | } 127 | } 128 | -------------------------------------------------------------------------------- /SRC/TUI/BUTTON.HPP: -------------------------------------------------------------------------------- 1 | #ifndef TUI_BUTTON_HPP 2 | #define TUI_BUTTON_HPP 3 | 4 | #include "types.hpp" 5 | 6 | #include "tui/control.hpp" 7 | 8 | /** Button control. */ 9 | class uibutton : public uicontrol { 10 | public: 11 | /** Button constructor. 12 | * 13 | * @param id Control unique identifier. 14 | * @param top Zero-based vertical position in characters. 15 | * @param left Zero-based horizontal position in characters. 16 | * @param width Button width in characters. 17 | * @param caption Button caption. 18 | */ 19 | uibutton(uint16_t id, int top, int left, int width, const char *caption); 20 | ~uibutton(); 21 | 22 | virtual void repaint(); 23 | 24 | virtual void on_accelerator(tui::action_t *action); 25 | virtual void on_message(uiqueue::message_t *msg, tui::action_t *action); 26 | 27 | virtual void lose_focus(); 28 | 29 | private: 30 | /** Button caption. */ 31 | char *m_caption; 32 | /** Button pressed flag. */ 33 | bool_t m_pressed; 34 | }; 35 | 36 | #endif 37 | -------------------------------------------------------------------------------- /SRC/TUI/CONTROL.HPP: -------------------------------------------------------------------------------- 1 | #ifndef TUI_CONTROL_HPP 2 | #define TUI_CONTROL_HPP 3 | 4 | #include "types.hpp" 5 | #include "vga.hpp" 6 | 7 | #include "tui/tui.hpp" 8 | 9 | /** Control base class. */ 10 | class uicontrol { 11 | public: 12 | /** Control constructor. 13 | * 14 | * @param id Control unique identifier. 15 | */ 16 | uicontrol(uint16_t id); 17 | /** Control destructor. 18 | * 19 | * @note This destructor also deallocate resources of all 20 | * descendant controls. 21 | */ 22 | virtual ~uicontrol(); 23 | 24 | /** Set view linked to this control. 25 | * 26 | * @note This method is used internally. 27 | * @param view View object. 28 | */ 29 | void set_view(uiview *view) { m_view = view; } 30 | 31 | /** Get control unique identifier. 32 | * 33 | * @return Control unique identifier. 34 | */ 35 | uint16_t get_id() { return m_id; } 36 | 37 | /** Get control accelerator character triggered by ALT+[char]. 38 | * 39 | * @return The uppercase accelerator ASCII character. 40 | */ 41 | char get_accelerator() { return m_accelerator; } 42 | /** Set control accelerator character. 43 | * 44 | * @see get_accelerator 45 | * @param accelerator Set the accelerator character as uppercase ASCII 46 | * character. 47 | */ 48 | void set_accelerator(char accelerator); 49 | 50 | /** Get control text/foreground color. 51 | * 52 | * @return Color index. 53 | */ 54 | uint8_t get_fore_color(); 55 | /** Set control text/foreground color and repaint control. 56 | * 57 | * @param color Color index. 58 | */ 59 | void set_fore_color(uint8_t color); 60 | /** Get control background color. 61 | * 62 | * @return Color index. 63 | */ 64 | uint8_t get_back_color(); 65 | /** Set control background color and repaint control. 66 | * 67 | * @param color Color index. 68 | */ 69 | void set_back_color(uint8_t color); 70 | 71 | /** Get control selected text/foreground color. 72 | * 73 | * @return Color index. 74 | */ 75 | uint8_t get_selected_fore_color(); 76 | /** Set control selected text/foreground color and repaint control. 77 | * 78 | * @param color Color index. 79 | */ 80 | void set_selected_fore_color(uint8_t color); 81 | /** Get control selected background color. 82 | * 83 | * @return Color index. 84 | */ 85 | uint8_t get_selected_back_color(); 86 | /** Set control selected background color and repaint control. 87 | * 88 | * @param color Color index. 89 | */ 90 | void set_selected_back_color(uint8_t color); 91 | 92 | /** Get transparency flag. 93 | * 94 | * @return TRUE if transparency is enabled, false otherwise. 95 | */ 96 | bool_t get_transparency(); 97 | /** Set transparency flag. 98 | * 99 | * @param enabled TRUE to enable transparency, false otherwise. 100 | */ 101 | void set_transparency(bool_t enabled); 102 | 103 | /** Recompute painting color attributes when transparency is enabled. */ 104 | void update_transparency_attrs(); 105 | 106 | /** Check if control can got focus. 107 | * 108 | * @return TRUE if control can got focus, FALSE otherwise. 109 | */ 110 | bool_t is_focusable() { return m_focusable; } 111 | 112 | /** Get paint rect in local coordinate respect to the parent. 113 | * 114 | * @param rect Buffer filled with vga::text_rect_t structure. 115 | */ 116 | void get_rect(vga::text_rect_t *rect); 117 | 118 | /** Pause repaints until resume_repaint is called. */ 119 | void pause_repaint(); 120 | /** Resume repaints and do a repaint if needed. */ 121 | void resume_repaint(); 122 | 123 | /** Get parent control. 124 | * 125 | * @return Parent control pointer, NULL if not defined. 126 | */ 127 | uicontrol *get_parent() { return m_parent; } 128 | /** Get next sibling control. 129 | * 130 | * @return Next control, NULL if not defined. 131 | */ 132 | uicontrol *get_next() { return m_next; } 133 | /** Get previous sibling control. 134 | * 135 | * @return Previous control, NULL if not defined. 136 | */ 137 | uicontrol *get_previous() { return m_previous; } 138 | /** Get first children control. 139 | * 140 | * @return First children control, NULL if not defined. 141 | */ 142 | uicontrol *get_first_child() { return m_first_child; } 143 | /** Get last children control. 144 | * 145 | * @return Last children control, NULL if not defined. 146 | */ 147 | uicontrol *get_last_child(); 148 | 149 | /** Set next control updating internal double linked list. 150 | * 151 | * @param ctrl Control to insert after this one. 152 | */ 153 | void set_next(uicontrol *ctrl); 154 | /** Set previous control updating internal double linked list. 155 | * 156 | * @param ctrl Control to insert before this one. 157 | */ 158 | void set_previous(uicontrol *ctrl); 159 | 160 | /** Append control to the children list. 161 | * 162 | * @param ctrl Control to append. 163 | * @return TRUE on success, FALSE if control is already children 164 | * of this one. 165 | */ 166 | bool_t add_control(uicontrol *ctrl); 167 | /** Remove control to the children list. 168 | * 169 | * @param ctrl Control to remove. 170 | * @return TRUE on success, FALSE if control is not children of this one. 171 | */ 172 | bool_t remove_control(uicontrol *ctrl); 173 | /** Check if control is children of this one. 174 | * 175 | * @param ctrl Control to test. 176 | * @return TRUE if control is children of this one, FALSE otherwise. 177 | */ 178 | bool_t is_child_control(uicontrol *ctrl); 179 | 180 | /** Called when control got focus. */ 181 | virtual void got_focus(); 182 | /** Called when control lose focus. */ 183 | virtual void lose_focus(); 184 | /** Fully repaint the control and all descendants. 185 | * 186 | * @note uicontrol::repaint must be called on overloaded method. 187 | */ 188 | virtual void repaint(); 189 | 190 | /** Called when accelerator for this control is triggered. 191 | * 192 | * @param action Filled with action data to return to main UI task. 193 | */ 194 | virtual void on_accelerator(tui::action_t *action) { } 195 | /** Called when the control receives a message. 196 | * 197 | * @param msg Received message. 198 | * @param action Filled with action data to return to main UI task. 199 | */ 200 | virtual void on_message(uiqueue::message_t *msg, tui::action_t *action) { } 201 | 202 | protected: 203 | /** Set control text/foreground color. 204 | * 205 | * @param color Color index. 206 | */ 207 | void set_fore_color_internal(uint8_t color); 208 | /** Set control background color. 209 | * 210 | * @param color Color index. 211 | */ 212 | void set_back_color_internal(uint8_t color); 213 | /** Set control selected text/foreground color. 214 | * 215 | * @param color Color index. 216 | */ 217 | void set_selected_fore_color_internal(uint8_t color); 218 | /** Set control selected background color. 219 | * 220 | * @param color Color index. 221 | */ 222 | void set_selected_back_color_internal(uint8_t color); 223 | 224 | /** Compute parent control rectangle in absolute coordinate system. 225 | * 226 | * @param rect Filled with computed rectangle. 227 | */ 228 | void get_parent_rect(vga::text_rect_t *rect); 229 | /** Compute this control paint rectangle in absolute coordinate system. 230 | * 231 | * @param rect Filled with computed rectangle. 232 | */ 233 | void get_paint_rect(vga::text_rect_t *rect); 234 | 235 | /** Check if this control can be repainted. 236 | * 237 | * A control can be repainted when is descendant of a window control 238 | * linked to a view that is in uiview::STATE_OPENED and repainting is 239 | * not paused. 240 | * @return TRUE if control can be repainted, FALSE otherwise. 241 | */ 242 | bool_t can_repaint(); 243 | 244 | /** Unique identifier. */ 245 | uint16_t m_id; 246 | /** Accelerator as uppercase ASCII character. */ 247 | char m_accelerator; 248 | /** Control drawing area in local coordinate respect to the parent. */ 249 | vga::text_rect_t m_rect; 250 | /** Focused flag. */ 251 | bool_t m_focused; 252 | /** Flag to track if this control is focusable. */ 253 | bool_t m_focusable; 254 | /** Repaint paused flag. */ 255 | bool_t m_repaint_paused; 256 | /** Flag to track pending repaints due to calls to pause_repaint. */ 257 | bool_t m_repaint_pending; 258 | 259 | /** Control foreground and background colors paint attributes. */ 260 | uint8_t m_paint_attrs; 261 | /** Control blank characters attributes. */ 262 | uint8_t m_blank_attrs; 263 | /** Selection foreground and background colors paint attributes. */ 264 | uint8_t m_selected_attrs; 265 | /** Scrollbars foreground and background colors paint attributes. */ 266 | uint8_t m_scroll_bar_attrs; 267 | /** Transparency flag. 268 | * 269 | * When true this control inherits background color from first ancestor 270 | * control with this flag disabled. 271 | */ 272 | bool_t m_transparent; 273 | private: 274 | /** View this control is part of. */ 275 | uiview *m_view; 276 | /** Parent control. */ 277 | uicontrol *m_parent; 278 | /** Next sibling control. */ 279 | uicontrol *m_next; 280 | /** Parent sibling control. */ 281 | uicontrol *m_previous; 282 | /** First children control. */ 283 | uicontrol *m_first_child; 284 | }; 285 | 286 | #endif 287 | -------------------------------------------------------------------------------- /SRC/TUI/EDITBOX.CPP: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/marco-sacchi/RLoader/b80fbbe1f108600be1fc0e191aca42f49443c153/SRC/TUI/EDITBOX.CPP -------------------------------------------------------------------------------- /SRC/TUI/EDITBOX.HPP: -------------------------------------------------------------------------------- 1 | #ifndef TUI_EDITBOX_HPP 2 | #define TUI_EDITBOX_HPP 3 | 4 | #include "types.hpp" 5 | 6 | #include "tui/control.hpp" 7 | 8 | /** Editbox control. */ 9 | class uieditbox : public uicontrol { 10 | public: 11 | /** Editbox constructor. 12 | * 13 | * @param id Control unique identifier. 14 | * @param top Zero-based vertical position in characters. 15 | * @param left Zero-based horizontal position in characters. 16 | * @param width List width in characters, scrollbar included. 17 | * @param max_length Maximum allwed text length. 18 | */ 19 | uieditbox(uint16_t id, int top, int left, uint16_t width, 20 | uint16_t max_length); 21 | ~uieditbox(); 22 | 23 | /** Get displayed text. 24 | * 25 | * @return Displayed text. 26 | */ 27 | const char *get_text(); 28 | 29 | /** Set displayed text. 30 | * 31 | * @param text Text to display. 32 | */ 33 | void set_text(const char *text); 34 | 35 | virtual void repaint(); 36 | 37 | virtual void got_focus(); 38 | virtual void lose_focus(); 39 | 40 | virtual void on_message(uiqueue::message_t *msg, tui::action_t *action); 41 | 42 | private: 43 | /** Render caret at specified position. 44 | * 45 | * @param pos Caret position as zero-based character index. 46 | */ 47 | void render_caret(uint16_t pos); 48 | 49 | /** Displayed text. */ 50 | char *m_text; 51 | /** Maximum allowed text length. */ 52 | uint16_t m_max_length; 53 | /** Current text display offset in characters. */ 54 | uint16_t m_text_offset; 55 | /** Current caret position. */ 56 | uint16_t m_caret_pos; 57 | /** True when a selection range is active. */ 58 | bool_t m_selected; 59 | /** Position of selection range oppsed to the caret position. */ 60 | uint16_t m_selection_pos; 61 | }; 62 | 63 | #endif 64 | -------------------------------------------------------------------------------- /SRC/TUI/GRAPH.CPP: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include "tui/tui.hpp" 4 | #include "tui/graph.hpp" 5 | #include "tui/theme.hpp" 6 | 7 | #include "math.hpp" 8 | 9 | bool_t uigraph::custom_frame = FALSE; 10 | uint8_t uigraph::previous_chars[FRAME_CHARS * 16]; 11 | int uigraph::previous_char_height = 0; 12 | 13 | #define FRAME_TOP_LEFT 0 14 | #define FRAME_TOP 1 15 | #define FRAME_TOP_RIGHT 2 16 | #define FRAME_LEFT 3 17 | #define FRAME_RIGHT 4 18 | #define FRAME_BOTTOM_LEFT 5 19 | #define FRAME_BOTTOM 6 20 | #define FRAME_BOTTOM_RIGHT 7 21 | 22 | uint8_t uigraph::frame_chars[FRAME_CHARS] = { 23 | 0xc9, 0xcd, 0xbb, 24 | 0xba, 0xba, 25 | 0xc8, 0xcd, 0xbc 26 | }; 27 | 28 | bool_t uigraph::setup_frame_chars() { 29 | if (tui::text_mode == FALSE) 30 | return TRUE; 31 | 32 | vga::font_info_t info; 33 | vga::get_font_info(vga::ROM_8x8_2DOT_FONT, &info); 34 | int char_height = info.scanlines_per_char; 35 | 36 | switch (char_height) { 37 | case 8: 38 | // CGA 39 | vga::get_font_info(vga::ROM_8x8_2DOT_FONT, &info); 40 | break; 41 | 42 | case 14: 43 | // EGA 44 | vga::get_font_info(vga::ROM_8x14_FONT, &info); 45 | break; 46 | 47 | case 16: 48 | // VGA 49 | vga::get_font_info(vga::ROM_8x16_FONT, &info); 50 | break; 51 | 52 | default: 53 | return FALSE; 54 | } 55 | 56 | memcpy(uigraph::previous_chars, 57 | (uint8_t far *)info.pointer + 0xba * char_height, 58 | 1 * char_height); 59 | 60 | memcpy(uigraph::previous_chars + char_height, 61 | (uint8_t far *)info.pointer + 0xc8 * char_height, 62 | (FRAME_CHARS - 1) * char_height); 63 | 64 | uint8_t new_chars[FRAME_CHARS * 16]; 65 | uint8_t *char_line = new_chars; 66 | 67 | // Left side 68 | memset(char_line, 0xc0, char_height); 69 | char_line += char_height; 70 | 71 | // Right side 72 | memset(char_line, 0x01, char_height); 73 | char_line += char_height; 74 | 75 | // Top side 76 | *char_line++ = 0xff; 77 | *char_line++ = 0xff; 78 | memset(char_line, 0x00, char_height - 2); 79 | char_line += char_height - 2; 80 | 81 | // Bottom side 82 | memset(char_line, 0x00, char_height - 2); 83 | char_line += char_height - 2; 84 | *char_line++ = 0xff; 85 | *char_line++ = 0xff; 86 | 87 | // Top-left corner 88 | *char_line++ = 0xff; 89 | *char_line++ = 0xff; 90 | memset(char_line, 0xc0, char_height - 2); 91 | char_line += char_height - 2; 92 | 93 | // Top-right corner 94 | *char_line++ = 0xff; 95 | *char_line++ = 0xff; 96 | memset(char_line, 0x01, char_height - 2); 97 | char_line += char_height - 2; 98 | 99 | // Bottom-left corner 100 | memset(char_line, 0xc0, char_height - 2); 101 | char_line += char_height - 2; 102 | *char_line++ = 0xff; 103 | *char_line++ = 0xff; 104 | 105 | // Bottom-right corner 106 | memset(char_line, 0x01, char_height - 2); 107 | char_line += char_height - 2; 108 | *char_line++ = 0xff; 109 | *char_line++ = 0xff; 110 | 111 | vga::load_alpha_font(new_chars, char_height, 1, 0xba, 0); 112 | vga::load_alpha_font(new_chars + char_height, char_height, 113 | FRAME_CHARS - 1, 0xc8, 0); 114 | 115 | uigraph::previous_char_height = char_height; 116 | uigraph::custom_frame = TRUE; 117 | 118 | uigraph::frame_chars[FRAME_TOP_LEFT] = 0xcb; 119 | uigraph::frame_chars[FRAME_TOP] = 0xc9; 120 | uigraph::frame_chars[FRAME_TOP_RIGHT] = 0xcc; 121 | uigraph::frame_chars[FRAME_LEFT] = 0xba; 122 | uigraph::frame_chars[FRAME_RIGHT] = 0xc8; 123 | uigraph::frame_chars[FRAME_BOTTOM_LEFT] = 0xcd; 124 | uigraph::frame_chars[FRAME_BOTTOM] = 0xca; 125 | uigraph::frame_chars[FRAME_BOTTOM_RIGHT] = 0xce; 126 | 127 | return TRUE; 128 | } 129 | 130 | void uigraph::restore_chars() { 131 | if (uigraph::custom_frame == FALSE) 132 | return; 133 | 134 | vga::load_alpha_font(uigraph::previous_chars, 135 | uigraph::previous_char_height, 1, 0xba, 0); 136 | vga::load_alpha_font(uigraph::previous_chars + 137 | uigraph::previous_char_height, 138 | uigraph::previous_char_height, 139 | FRAME_CHARS - 1, 0xc8, 0); 140 | } 141 | 142 | void uigraph::enable_caret(bool_t enable) { 143 | if (tui::text_mode == TRUE) 144 | vga::enable_cursor(enable); 145 | } 146 | 147 | void uigraph::draw_frame(vga::text_rect_t *rect, uint8_t attrs) { 148 | uint8_t width = rect->right - rect->left + 1; 149 | uint8_t *ch = uigraph::frame_chars; 150 | 151 | vga::set_cursor_pos(rect->top, rect->left); 152 | vga::write_char(ch[FRAME_TOP], attrs, width); 153 | vga::write_char(ch[FRAME_TOP_LEFT], attrs, 1); 154 | 155 | vga::set_cursor_pos(rect->top, rect->right); 156 | vga::write_char(ch[FRAME_TOP_RIGHT], attrs, 1); 157 | 158 | for (int i = rect->top + 1; i < rect->bottom; ++i) { 159 | vga::set_cursor_pos(i, rect->left); 160 | vga::write_char(ch[FRAME_LEFT], attrs, 1); 161 | 162 | vga::set_cursor_pos(i, rect->right); 163 | vga::write_char(ch[FRAME_RIGHT], attrs, 1); 164 | } 165 | 166 | vga::set_cursor_pos(rect->bottom, rect->left); 167 | vga::write_char(ch[FRAME_BOTTOM], attrs, width); 168 | vga::write_char(ch[FRAME_BOTTOM_LEFT], attrs, 1); 169 | 170 | vga::set_cursor_pos(rect->bottom, rect->right); 171 | vga::write_char(ch[FRAME_BOTTOM_RIGHT], attrs, 1); 172 | } 173 | 174 | void uigraph::draw_filled_frame(vga::text_rect_t *rect, uint8_t attrs) { 175 | graphics::draw_filled_rect(rect, attrs); 176 | uigraph::draw_frame(rect, attrs); 177 | } 178 | 179 | void uigraph::draw_accelerator(uint8_t row, uint8_t col, uint8_t attrs) { 180 | uigraph::highlight_chars(row, col, attrs & 0x0f, 1); 181 | } 182 | 183 | void uigraph::highlight_chars(uint8_t row, uint8_t col, uint8_t attrs, 184 | uint16_t count) { 185 | if (tui::text_mode) { 186 | if (attrs & 0x80) 187 | vga::and_attrs(row, col, 0xf0, count); 188 | else 189 | vga::or_attrs(row, col, 0x0f, count); 190 | } else { 191 | vga::set_cursor_pos(row, col); 192 | vga::write_char('_', 0x80 | (attrs & 0x0f), count); 193 | } 194 | } 195 | 196 | void uigraph::reverse_item(uint8_t row, uint8_t col, uint8_t attrs, 197 | uint16_t count) { 198 | if (tui::text_mode == TRUE) { 199 | uint8_t reverse = (attrs & 0x0f) ^ 200 | ((attrs & 0xf0) >> 4); 201 | reverse |= reverse << 4; 202 | vga::xor_attrs(row, col, reverse, count); 203 | } else { 204 | vga::set_cursor_pos(row, col); 205 | vga::write_char(0xdb, 0x80 | (attrs & 0x0f), count); 206 | } 207 | } 208 | 209 | void uigraph::apply_focus(uint8_t row, uint8_t col, uint8_t attrs, 210 | uint8_t count) { 211 | vga::text_rect_t rect; 212 | rect.top = rect.bottom = row; 213 | rect.left = col; 214 | rect.right = col + count - 1; 215 | uigraph::apply_focus(&rect, attrs, FOCUS_SURFACE); 216 | } 217 | 218 | void uigraph::apply_focus(vga::text_rect_t *rect, uint8_t attr, 219 | focus_style style) { 220 | if (tui::text_mode == FALSE) { 221 | graphics::draw_focus_outline(rect, attr); 222 | return; 223 | } 224 | 225 | if (style & FOCUS_SURFACE) { 226 | uint16_t count = rect->right - rect->left + 1; 227 | for (uint8_t row = rect->top; row <= rect->bottom; ++row) 228 | vga::write_attrs(row, rect->left, attr ^ 0x88, count); 229 | } 230 | } 231 | 232 | void uigraph::apply_active(uint8_t row, uint8_t col, uint8_t attrs, 233 | uint8_t count) { 234 | vga::text_rect_t rect; 235 | rect.top = rect.bottom = row; 236 | rect.left = col; 237 | rect.right = col + count - 1; 238 | uigraph::apply_active(&rect, attrs); 239 | } 240 | 241 | void uigraph::apply_active(vga::text_rect_t *rect, uint8_t attrs) { 242 | uint16_t count = rect->right - rect->left + 1; 243 | if (tui::text_mode == TRUE) { 244 | /*if (attrs & 0x80) { 245 | attrs = 0x80 | ((attrs & 0x0f) ^ 0x0f); 246 | for (uint8_t row = rect->top; row <= rect->bottom; ++row) 247 | vga::xor_attrs(row, rect->left, attrs, count); 248 | } else*/ 249 | for (uint8_t row = rect->top; row <= rect->bottom; ++row) 250 | vga::or_attrs(row, rect->left, 0x8f, count); 251 | } else { 252 | for (uint8_t row = rect->top; row <= rect->bottom; ++row) { 253 | vga::set_cursor_pos(row, rect->left); 254 | vga::write_char(0xdb, 0x80 | (attrs & 0x07), count); 255 | } 256 | } 257 | } 258 | 259 | void uigraph::remove_focus(vga::text_rect_t *rect, uint8_t attrs, 260 | focus_style style) { 261 | if (tui::text_mode == FALSE) { 262 | graphics::draw_focus_outline(rect, 0); 263 | graphics::draw_outline(rect, attrs); 264 | return; 265 | } 266 | } 267 | 268 | void uigraph::draw_shadow(vga::text_rect_t *rect) { 269 | const vga::state_t *state = vga::get_current_state(); 270 | if (tui::text_mode == TRUE) { 271 | uint8_t attrs = vga::encode_attrs(uitheme::shadow::back, 272 | uitheme::shadow::fore, 0); 273 | 274 | uint8_t left = rect->left, right = rect->right; 275 | uint8_t bottom = rect->bottom; 276 | bool_t left_column = FALSE, right_column = FALSE; 277 | bool_t bottom_row = FALSE; 278 | 279 | if (left > 1) { 280 | --left; 281 | left_column = TRUE; 282 | } 283 | 284 | if (right < state->columns - 1) { 285 | ++right; 286 | right_column = TRUE; 287 | } 288 | 289 | if (bottom < state->rows - 1) { 290 | ++bottom; 291 | bottom_row = TRUE; 292 | } else 293 | bottom = state->rows - 1; 294 | 295 | if (left_column) { 296 | for (uint8_t i = rect->top + 1; i <= bottom; ++i) 297 | vga::write_attrs(i, left, attrs, 1); 298 | } 299 | 300 | if (right_column) { 301 | for (uint8_t i = rect->top + 1; i <= bottom; ++i) 302 | vga::write_attrs(i, right, attrs, 1); 303 | } 304 | 305 | if (bottom_row) 306 | vga::write_attrs(bottom, left, attrs, right - left + 1); 307 | } else { 308 | // TODO: render shadow on graphics mode. 309 | } 310 | } 311 | -------------------------------------------------------------------------------- /SRC/TUI/GRAPH.HPP: -------------------------------------------------------------------------------- 1 | #ifndef TUI_GRAPH_HPP 2 | #define TUI_GRAPH_HPP 3 | 4 | #include "graphics.hpp" 5 | #include "tui/theme.hpp" 6 | 7 | #define FRAME_CHARS 8 8 | 9 | /** Utility methods to abstract graphics/text-mode UI elements drawing. */ 10 | class uigraph { 11 | public: 12 | /** Focus graphics style. */ 13 | enum focus_style { 14 | /** Focus drawed as an outline, only in graphic mode. */ 15 | FOCUS_OUTLINE = 0x01, 16 | /** Focus drawed as full control surface colors highlight. */ 17 | FOCUS_SURFACE = 0x02 18 | }; 19 | 20 | /** Modify characters generator bitmap to render customized frame. 21 | * 22 | * @return True on success, false on unrecognized font height. 23 | */ 24 | static bool_t setup_frame_chars(); 25 | /** Restore modified charaters generator bitmap. */ 26 | static void restore_chars(); 27 | 28 | /** Enable hardware cursor, only when in text-mode. 29 | * 30 | * @param enable TRUE to enable caret, FALSE otherwise. 31 | */ 32 | static void enable_caret(bool_t enable); 33 | 34 | /** Draw accelerator character. 35 | * 36 | * @param row Zero-based text row. 37 | * @param col Zero-based text column. 38 | * @param attrs Painting color attributes of text for which hightlight 39 | * accelerator character. 40 | */ 41 | static void draw_accelerator(uint8_t row, uint8_t col, uint8_t attrs); 42 | 43 | /** Draw frame accounting for customized characters. 44 | * 45 | * @deprecated use uigraph::draw_frame 46 | * @param rect Rectangle into which draw the frame. 47 | * @param attrs Attrbutes of the drawed characters. 48 | */ 49 | static void draw_frame(vga::text_rect_t *rect, uint8_t attrs); 50 | /** Draw filled frame accounting for customized characters. 51 | * 52 | * @deprecated use uigraph::draw_filled_frame 53 | * @param rect Rectangle into which draw the frame. 54 | * @param attrs Attrbutes of the drawed characters. 55 | */ 56 | static void draw_filled_frame(vga::text_rect_t *rect, uint8_t attrs); 57 | 58 | /** Highlight (underline in graphic mode) characters. 59 | * 60 | * @param row Zero-based text row. 61 | * @param col Zero-based text column. 62 | * @param attrs Painting color attributes of text to hightlight. 63 | */ 64 | static void highlight_chars(uint8_t row, uint8_t col, uint8_t attrs, 65 | uint16_t count); 66 | 67 | /** Reverse item colors. 68 | * 69 | * @param row Zero-based text row. 70 | * @param col Zero-based text column. 71 | * @param attrs Painting color attributes. 72 | * @param count Item width as characters count. 73 | */ 74 | static void reverse_item(uint8_t row, uint8_t col, uint8_t attrs, 75 | uint16_t count); 76 | 77 | /** Apply focus style to an item. 78 | * 79 | * @param row Zero-based text row. 80 | * @param col Zero-based text column. 81 | * @param attrs Painting color attributes. 82 | * @param count Item width as characters count. 83 | */ 84 | static void apply_focus(uint8_t row, uint8_t col, uint8_t attrs, 85 | uint8_t count); 86 | 87 | /** Apply focus style to specified rect. 88 | * 89 | * @param rect Rect for which apply style. 90 | * @param attrs Painting color attributes of control. 91 | * @param style Focus style. 92 | */ 93 | static void apply_focus(vga::text_rect_t *rect, uint8_t attrs, 94 | focus_style style); 95 | 96 | /** Remove applied focus style to specified rect. 97 | * 98 | * @note Only for graphics UI, no effect on text-mode UI. 99 | * @param rect Rect for which apply style. 100 | * @param attrs Painting color attributes of control. 101 | * @param style Previously specified focus style. 102 | */ 103 | static void remove_focus(vga::text_rect_t *rect, uint8_t attrs, 104 | focus_style style); 105 | 106 | /** Apply active style to an item. 107 | * 108 | * @param row Zero-based text row. 109 | * @param col Zero-based text column. 110 | * @param attrs Painting color attributes. 111 | * @param count Item width as characters count. 112 | */ 113 | static void apply_active(uint8_t row, uint8_t col, uint8_t attrs, 114 | uint8_t count); 115 | 116 | /** Apply active style to specified rect. 117 | * 118 | * @param rect Rect for which apply style. 119 | * @param attr Painting color attributes of control. 120 | */ 121 | static void apply_active(vga::text_rect_t *rect, uint8_t attr); 122 | 123 | /** Render a shadow around specified rectangle clipping it if necessary. 124 | * 125 | * @param rect Reference rectangle. 126 | */ 127 | static void draw_shadow(vga::text_rect_t *rect); 128 | 129 | private: 130 | /** True when character generator is customized, false otherwise. */ 131 | static bool_t custom_frame; 132 | /** Original character generator bitmap data. */ 133 | static uint8_t previous_chars[FRAME_CHARS * 16]; 134 | /** Original character generator char height. */ 135 | static int previous_char_height; 136 | /** Frame charaters indices. */ 137 | static uint8_t uigraph::frame_chars[FRAME_CHARS]; 138 | }; 139 | 140 | #endif 141 | -------------------------------------------------------------------------------- /SRC/TUI/LABEL.CPP: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include "tui/label.hpp" 4 | #include "tui/graph.hpp" 5 | #include "math.hpp" 6 | #include "string.hpp" 7 | 8 | uilabel::uilabel(uint16_t id, int top, int left, const char *text) 9 | : uicontrol(id) { 10 | m_focusable = FALSE; 11 | 12 | m_autosize = TRUE; 13 | m_multiline = FALSE; 14 | 15 | m_rect.top = m_rect.bottom = top; 16 | m_rect.left = left; 17 | 18 | m_text = NULL; 19 | m_lines = NULL; 20 | 21 | this->set_text(text); 22 | 23 | this->set_fore_color_internal(uitheme::label::fore); 24 | 25 | // Label is transparent by default. 26 | m_transparent = TRUE; 27 | } 28 | 29 | uilabel::uilabel(uint16_t id, int top, int left, int width, int height, 30 | const char *text) 31 | : uicontrol(id) { 32 | m_focusable = FALSE; 33 | 34 | m_multiline = TRUE; 35 | 36 | m_rect.bottom = m_rect.top = top; 37 | m_rect.left = left; 38 | m_rect.right = left + width - 1; 39 | 40 | if (height > 0) { 41 | m_autosize = FALSE; 42 | m_rect.bottom += height - 1; 43 | } else { 44 | m_autosize = TRUE; 45 | } 46 | 47 | m_text = NULL; 48 | m_lines = NULL; 49 | 50 | this->set_text(text); 51 | 52 | m_paint_attrs = vga::encode_attrs(0, uitheme::label::fore, 0); 53 | 54 | // Label is transparent by default. 55 | m_transparent = TRUE; 56 | } 57 | 58 | uilabel::~uilabel() { 59 | if (m_lines != NULL) 60 | delete m_lines; 61 | 62 | delete m_text; 63 | m_text = NULL; 64 | } 65 | 66 | bool_t uilabel::set_text(const char *text) { 67 | int text_length = strlen(text); 68 | char *new_text = new char[text_length + 1]; 69 | if (new_text == NULL) 70 | return FALSE; 71 | 72 | if (m_multiline == TRUE) { 73 | int height; 74 | if (m_autosize == TRUE) { 75 | const vga::state_t *state = vga::get_current_state(); 76 | height = state->rows; 77 | } else { 78 | height = m_rect.bottom - m_rect.top + 1; 79 | } 80 | 81 | char **new_lines = new char *[height]; 82 | if (new_lines == NULL) { 83 | delete new_text; 84 | return FALSE; 85 | } 86 | 87 | if (m_text != NULL) 88 | delete m_text; 89 | if (m_lines != NULL) 90 | delete m_lines; 91 | 92 | m_text = new_text; 93 | strcpy(m_text, text); 94 | 95 | m_lines = new_lines; 96 | m_lines_count = 0; 97 | 98 | m_effective_width = this->wrap_text(height); 99 | m_rect.bottom = m_rect.top + m_lines_count - 1; 100 | } else { 101 | if (m_text != NULL) 102 | delete m_text; 103 | if (m_lines != NULL) 104 | delete m_lines; 105 | 106 | m_text = new_text; 107 | strcpy(m_text, text); 108 | 109 | m_lines = NULL; 110 | m_lines_count = 0; 111 | 112 | m_rect.right = m_rect.left + text_length - 1; 113 | } 114 | 115 | this->repaint(); 116 | 117 | return TRUE; 118 | } 119 | 120 | int uilabel::wrap_text(int max_lines) { 121 | int effective_width = 0; 122 | int length = strlen(m_text); 123 | int max_width = m_rect.right - m_rect.left + 1; 124 | 125 | m_lines_count = 0; 126 | char *ptr = m_text; 127 | 128 | while (m_lines_count < max_lines && (ptr + max_width - m_text) < length) { 129 | char *split_ptr = ptr + max_width; 130 | while (split_ptr > ptr && !string::is_white_space(*split_ptr)) 131 | --split_ptr; 132 | 133 | while (split_ptr > ptr && string::is_white_space(*split_ptr)) 134 | --split_ptr; 135 | 136 | *(split_ptr + 1) = NULL; 137 | split_ptr += 2; 138 | while (string::is_white_space(*split_ptr)) 139 | ++split_ptr; 140 | 141 | effective_width = max(effective_width, (int)strlen(ptr)); 142 | 143 | m_lines[m_lines_count++] = ptr; 144 | ptr = split_ptr; 145 | } 146 | 147 | if (m_lines_count < max_lines) 148 | m_lines[m_lines_count++] = ptr; 149 | 150 | return effective_width; 151 | } 152 | 153 | void uilabel::repaint() { 154 | if (this->can_repaint() == FALSE) { 155 | m_repaint_pending = TRUE; 156 | return; 157 | } 158 | 159 | this->update_transparency_attrs(); 160 | 161 | vga::text_rect_t rect; 162 | this->get_paint_rect(&rect); 163 | 164 | graphics::draw_filled_rect(&rect, m_paint_attrs); 165 | 166 | int accel_index = -1, accel_line = 0; 167 | 168 | if (m_multiline == TRUE) { 169 | int row = rect.top; 170 | for (int i = 0; i < m_lines_count; ++i, ++row) { 171 | vga::set_cursor_pos(row, rect.left); 172 | vga::write_string(m_lines[i], m_paint_attrs, 173 | vga::USE_ATTRS_NO_UPDATE_CURSOR); 174 | 175 | if (m_accelerator != NULL && accel_index == -1) { 176 | accel_index = string::icase_index_of(m_lines[i], 177 | m_accelerator); 178 | accel_line = i; 179 | } 180 | } 181 | } else { 182 | vga::set_cursor_pos(rect.top, rect.left); 183 | vga::write_string(m_text, m_paint_attrs, 184 | vga::USE_ATTRS_NO_UPDATE_CURSOR); 185 | 186 | if (m_accelerator != NULL) 187 | accel_index = string::icase_index_of(m_text, m_accelerator); 188 | } 189 | 190 | if (accel_index >= 0) { 191 | accel_line += rect.top; 192 | accel_index += rect.left; 193 | uigraph::draw_accelerator(accel_line, accel_index, m_paint_attrs); 194 | } 195 | 196 | uicontrol::repaint(); 197 | } 198 | -------------------------------------------------------------------------------- /SRC/TUI/LABEL.HPP: -------------------------------------------------------------------------------- 1 | #ifndef TUI_LABEL_HPP 2 | #define TUI_LABEL_HPP 3 | 4 | #include "types.hpp" 5 | 6 | #include "tui/control.hpp" 7 | 8 | /** Label control. */ 9 | class uilabel : public uicontrol { 10 | public: 11 | /** Label constructor. 12 | * 13 | * @param id Control unique identifier. 14 | * @param top Zero-based vertical position in characters. 15 | * @param left Zero-based horizontal position in characters. 16 | * @param text Label displayed text. 17 | */ 18 | uilabel(uint16_t id, int top, int left, const char *text); 19 | /** Label constructor. 20 | * 21 | * @param id Control unique identifier. 22 | * @param top Zero-based vertical position in characters. 23 | * @param left Zero-based horizontal position in characters. 24 | * @param width Width in characters. 25 | * @param height Height in characters, it can be zero to limit 26 | * the width only. 27 | * @param text Label displayed text. 28 | */ 29 | uilabel(uint16_t id, int top, int left, int width, int height, 30 | const char *text); 31 | ~uilabel(); 32 | 33 | /** Set or update displayed text. 34 | * 35 | * @param text Text to be displayed. 36 | * @return True on success, false on allocation error. 37 | */ 38 | bool_t set_text(const char *text); 39 | 40 | virtual void repaint(); 41 | 42 | private: 43 | /** Word wrap the text when width and height are specified. 44 | * 45 | * @param max_lines Maximum allowed lines. 46 | * @return Effective maximum text width in charaters. 47 | */ 48 | int wrap_text(int max_lines); 49 | 50 | /** Label displayed text. */ 51 | char *m_text; 52 | /** Pointers to first character of each line of wrapped text. */ 53 | char **m_lines; 54 | /** Line count of wrapped text. */ 55 | int m_lines_count; 56 | /** Width of longest line of text in characters when not autosized. */ 57 | int m_effective_width; 58 | /** Flag to track multiline or single line label. */ 59 | bool_t m_multiline; 60 | /** Flag to track autosize. */ 61 | bool_t m_autosize; 62 | }; 63 | 64 | #endif 65 | -------------------------------------------------------------------------------- /SRC/TUI/LISTBOX.HPP: -------------------------------------------------------------------------------- 1 | #ifndef TUI_LISTBOX_HPP 2 | #define TUI_LISTBOX_HPP 3 | 4 | #include "types.hpp" 5 | 6 | #include "tui/control.hpp" 7 | 8 | /** Maximum number of columns in which items are arranged. */ 9 | #define LISTBOX_MAX_COLS 10 10 | 11 | /** Listbox control. 12 | */ 13 | class uilistbox : public uicontrol { 14 | public: 15 | /** Items sorting modes. */ 16 | enum sorting { 17 | /** No sorting. */ 18 | SORT_NONE, 19 | /** Ascending sorting. */ 20 | SORT_ASCENDING, 21 | /** Descending sorting. */ 22 | SORT_DESCENDING 23 | }; 24 | 25 | /** Listbox constructor. 26 | * 27 | * @param id Control unique identifier. 28 | * @param top Zero-based vertical position in characters. 29 | * @param left Zero-based horizontal position in characters. 30 | * @param width List width in characters, scrollbar included. 31 | * @param height List height in characters. 32 | */ 33 | uilistbox(uint16_t id, int top, int left, int width, int height); 34 | ~uilistbox(); 35 | 36 | /** Remove all items. */ 37 | void clear(); 38 | 39 | /** Add an item to the list. 40 | * 41 | * @param text Item text. 42 | * @return True on success, false otherwise. 43 | */ 44 | bool_t add_item(const char *text); 45 | 46 | /** Select specified item by index. 47 | * 48 | * @param index Item zero-based index. 49 | * @return True on item selection success, false otherwise. 50 | */ 51 | bool_t set_selected_item(int index); 52 | 53 | /** Select specified item by text. 54 | * 55 | * @param text Text to search for. 56 | * @return True on item selection success, false otherwise. 57 | */ 58 | bool_t set_selected_item(const char *text); 59 | 60 | /** Get selected item index. 61 | * 62 | * @return Index of selected item, -1 if no item is selected. 63 | */ 64 | int get_selected_item(); 65 | 66 | /** Get selected item text. 67 | * 68 | * @return Text of selected item, NULL if no item is selected. 69 | */ 70 | const char *get_selected_item_text(); 71 | 72 | /** Get current number of columns in which items are arranged. 73 | * 74 | * @return Number of columns. 75 | */ 76 | int get_columns() { return m_columns; } 77 | 78 | /** Set number of columns in which items are arranged. 79 | * 80 | * @param columns Number of columns in range [1, LISTBOX_MAX_COLS]. 81 | */ 82 | void set_columns(int columns); 83 | 84 | /** Get current sorting mode. 85 | * 86 | * @return Current sorting mode. 87 | */ 88 | sorting get_sort_mode(); 89 | 90 | /** Set sorting mode. 91 | * 92 | * @param mode Sorting mode. 93 | */ 94 | void set_sort_mode(sorting mode); 95 | 96 | /** Set highlight string. 97 | * 98 | * @param string Text to be highlighted in all items. 99 | */ 100 | void set_highlight(const char *string); 101 | 102 | virtual void repaint(); 103 | 104 | virtual void on_message(uiqueue::message_t *msg, tui::action_t *action); 105 | 106 | protected: 107 | /** Find first item beginning with search string. 108 | * 109 | * @param search String to search for. 110 | * @return First found item index, -1 if no match is found. 111 | */ 112 | int find_first_item(char *search); 113 | 114 | /** Repaint item. 115 | * 116 | * @param index Index of item to be repainted. 117 | * @param rect Repaint rectangle. 118 | */ 119 | virtual void repaint_item(int index, vga::text_rect_t *rect); 120 | 121 | /** List offset as index of first visible item. */ 122 | int m_offset; 123 | /** Index of selected list item. */ 124 | int m_selected_item; 125 | /** List items count. */ 126 | int m_items_count; 127 | 128 | /** Capacity change step when list items will be added/removed. */ 129 | int m_items_capacity_step; 130 | /** Internal list capacity. */ 131 | int m_items_capacity; 132 | 133 | /** Number of columns in which items are arranged, default 1. */ 134 | int m_columns; 135 | /** Horizontal offset in local coordinates from control left edge. */ 136 | int m_columns_offsets[LISTBOX_MAX_COLS + 1]; 137 | 138 | /** Items sorting mode. */ 139 | sorting m_sort_mode; 140 | 141 | /** Mnemonic search string. */ 142 | char *m_mnemonic_string; 143 | /** Mnemonic current character pointer into search string. */ 144 | char *m_mnemonic_ptr; 145 | /** Maximum mnemonic string length. */ 146 | int m_mnemonic_maxlen; 147 | 148 | /** Highlight string. */ 149 | char *m_highlight_string; 150 | /** Maximum highlight string length. */ 151 | int m_highlight_maxlen; 152 | 153 | /** Array of items string pointers. */ 154 | char **m_items; 155 | 156 | private: 157 | /** Sort items in-place when sorting is not SORT_NONE. */ 158 | void sort_items(); 159 | 160 | /** Append character to the end of mnemonic string. 161 | * 162 | * @param c Character to append. 163 | */ 164 | void mnemonic_push(char c); 165 | /** Remove last character from the mnemonic string. */ 166 | void mnemonic_pop(); 167 | /** Reset mnemonic string to empty string. */ 168 | void mnemonic_reset(); 169 | 170 | /** Scroll list by specified offset. 171 | * 172 | * @param offset Scroll offset, negative value are allowed. 173 | * @return True if selected item is changed, false otherwise. 174 | */ 175 | bool_t scroll(int offset); 176 | /** Jump to the beginning of the list. 177 | * 178 | * @return True if selected item is changed, false otherwise. 179 | */ 180 | bool_t jump_to_begin(); 181 | /** Jump to the end of the list. 182 | * 183 | * @return True if selected item is changed, false otherwise. 184 | */ 185 | bool_t jump_to_end(); 186 | 187 | /** Calculate list offset. 188 | * 189 | * @param selected_item Zero-based index of selected item. 190 | * @param Zero based index of top item to be displayed. 191 | */ 192 | int calc_list_offset(int selected_item); 193 | /** Calculate list repaint rectangle. 194 | * 195 | * @param rect Filled with calculated values. 196 | */ 197 | void calc_list_rect(vga::text_rect_t *rect); 198 | 199 | /** Repaint scrollbar one column beyond the rectangle right side. 200 | * 201 | * @param rect Reference rectangle. 202 | */ 203 | void repaint_scroll_bar(vga::text_rect_t *rect); 204 | 205 | /** Track last sent action to handle item selecting/selected. */ 206 | tui::action_types m_last_action; 207 | }; 208 | 209 | #endif 210 | -------------------------------------------------------------------------------- /SRC/TUI/MSGBOX.CPP: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include "tui/msgbox.hpp" 4 | #include "math.hpp" 5 | #include "string.hpp" 6 | #include "tui/button.hpp" 7 | #include "tui/label.hpp" 8 | #include "tui/window.hpp" 9 | 10 | #define MSGBOX_MAX_BUTTONS 8 11 | #define MSGBOX_MAX_LINES 20 12 | #define MSGBOX_MIN_TEXT_WIDTH 30 13 | #define MSGBOX_MAX_TEXT_WIDTH 50 14 | 15 | #define ID_WINDOW 1 16 | 17 | const char *uimsgbox::m_captions[] = { 18 | "Ok", 19 | "Cancel", 20 | "Yes", 21 | "No", 22 | "Retry", 23 | "Abort", 24 | "Ignore" 25 | }; 26 | 27 | const char uimsgbox::m_accelerators[] = { 28 | 'O', 'C', 'Y', 'N', 'R', 'A', 'I' 29 | }; 30 | 31 | uimsgbox::uimsgbox(const char *caption, const char *message) 32 | : uiview() { 33 | m_is_modal = TRUE; 34 | 35 | m_caption = new char [strlen(caption) + 1]; 36 | strcpy(m_caption, caption); 37 | 38 | m_message = new char [strlen(message) + 1]; 39 | strcpy(m_message, message); 40 | 41 | m_buttons_count = 0; 42 | memset(m_buttons_infos, 0x00, MSGBOX_MAX_BUTTONS * sizeof(button_info_t)); 43 | 44 | m_selected_button_id = 0; 45 | } 46 | 47 | uimsgbox::~uimsgbox() { 48 | delete m_message; 49 | delete m_caption; 50 | 51 | for (int i = 0; i < MSGBOX_MAX_BUTTONS; ++i) 52 | if (m_buttons_infos[i].id >= BTN_CUSTOM_FIRST && 53 | m_buttons_infos[i].caption != NULL) 54 | delete m_buttons_infos[i].caption; 55 | } 56 | 57 | bool_t uimsgbox::add_button(int id) { 58 | if (m_buttons_count >= MSGBOX_MAX_BUTTONS) 59 | return FALSE; 60 | 61 | button_info_t *info = &m_buttons_infos[m_buttons_count++]; 62 | info->id = id; 63 | info->caption = (char *)uimsgbox::m_captions[id]; 64 | info->accelerator = uimsgbox::m_accelerators[id]; 65 | 66 | return TRUE; 67 | } 68 | 69 | bool_t uimsgbox::add_button(int id, const char *caption, char accelerator) { 70 | if (m_buttons_count >= MSGBOX_MAX_BUTTONS) 71 | return FALSE; 72 | 73 | button_info_t *info = &m_buttons_infos[m_buttons_count++]; 74 | info->id = id; 75 | info->caption = new char[strlen(caption) + 1]; 76 | strcpy(info->caption, caption); 77 | info->accelerator = accelerator; 78 | 79 | return TRUE; 80 | } 81 | 82 | int uimsgbox::calc_buttons_props() { 83 | int width = 0; 84 | button_info_t *info; 85 | for (int i = 0; i < m_buttons_count; ++i) { 86 | info = &m_buttons_infos[i]; 87 | info->left = width; 88 | info->width = strlen(info->caption) + 4; 89 | width += info->width + 2; 90 | } 91 | 92 | if (m_buttons_count > 0) 93 | width -= 2; 94 | 95 | return width; 96 | } 97 | 98 | bool_t uimsgbox::init_controls() { 99 | if (m_buttons_count == 0) { 100 | if (this->add_button(BTN_OK) == FALSE) 101 | return FALSE; 102 | } 103 | 104 | int buttons_width = this->calc_buttons_props(); 105 | 106 | int message_width = max(buttons_width, MSGBOX_MIN_TEXT_WIDTH); 107 | int window_width = max(buttons_width, message_width) + 4; 108 | 109 | uilabel *label = new uilabel(0, 2, 2, message_width, 0, m_message); 110 | if (label == NULL) 111 | return FALSE; 112 | 113 | vga::text_rect_t rect; 114 | label->get_rect(&rect); 115 | 116 | int window_height = rect.bottom - rect.top + 6; 117 | m_window = new uiwindow(ID_WINDOW, 0, 0, window_width, window_height, 118 | uiwindow::FRAMED | uiwindow::CAPTIONED | 119 | uiwindow::CENTERED, 120 | m_caption); 121 | if (m_window == NULL) 122 | return FALSE; 123 | 124 | m_window->add_control(label); 125 | 126 | int buttons_offset = (window_width - buttons_width) >> 1; 127 | 128 | for (int i = 0; i < m_buttons_count; ++i) { 129 | button_info_t *info = &m_buttons_infos[i]; 130 | uibutton *button = new uibutton(info->id, window_height - 2, 131 | info->left + buttons_offset, 132 | info->width, info->caption); 133 | 134 | if (button == NULL) 135 | return FALSE; 136 | 137 | if (info->accelerator) 138 | button->set_accelerator(info->accelerator); 139 | 140 | m_window->add_control(button); 141 | } 142 | 143 | return TRUE; 144 | } 145 | 146 | bool_t uimsgbox::on_action(tui::action_t *action) { 147 | switch (action->type) { 148 | case tui::TUIA_CTRL_DESTROY: 149 | if (action->id == ID_WINDOW) { 150 | this->queue_result(m_selected_button_id); 151 | return TRUE; 152 | } 153 | break; 154 | 155 | case tui::TUIA_CTRL_ACTIVATED: 156 | m_selected_button_id = action->id; 157 | this->queue_close_view(); 158 | break; 159 | } 160 | 161 | return TRUE; 162 | } 163 | -------------------------------------------------------------------------------- /SRC/TUI/MSGBOX.HPP: -------------------------------------------------------------------------------- 1 | #ifndef MSGBOX_HPP 2 | #define MSGBOX_HPP 3 | 4 | #include "tui/view.hpp" 5 | 6 | #define MSGBOX_MAX_BUTTONS 8 7 | 8 | // Forward declarations. 9 | class uibutton; 10 | 11 | class uimsgbox : public uiview { 12 | public: 13 | /** Standard buttons id. */ 14 | enum buttons { 15 | BTN_OK, 16 | BTN_CANCEL, 17 | BTN_YES, 18 | BTN_NO, 19 | BTN_RETRY, 20 | BTN_ABORT, 21 | BTN_IGNORE, 22 | /** First available id for custom buttons. */ 23 | BTN_CUSTOM_FIRST 24 | }; 25 | 26 | uimsgbox(const char *caption, const char *message); 27 | virtual ~uimsgbox(); 28 | 29 | bool_t add_button(int id); 30 | bool_t add_button(int id, const char *caption, char accelerator); 31 | 32 | virtual bool_t init_controls(); 33 | 34 | virtual bool_t on_action(tui::action_t *action); 35 | 36 | int get_selected_button_id() { return m_selected_button_id; } 37 | 38 | private: 39 | /** Structure to track buttons added to the message box. */ 40 | typedef struct { 41 | /** Button id, @see uimsgbox::buttons for standard button id. */ 42 | int id; 43 | /** Button caption, NULL for standard buttons. */ 44 | char *caption; 45 | /** Button accelerator, 0 for standard buttons. */ 46 | char accelerator; 47 | /** Left offset from column calculated for buttons positioning. */ 48 | int left; 49 | /** Button width. */ 50 | int width; 51 | } button_info_t; 52 | 53 | /** Calculate button properties like position and size. 54 | * 55 | * @return Total buttons width in characters, from left edge 56 | * of the first one to right edge of the last one. 57 | */ 58 | int calc_buttons_props(); 59 | 60 | int wrap_message(int max_width); 61 | 62 | /** Captions of standard buttons. */ 63 | static const char *m_captions[]; 64 | /** Accelerators of standard buttons. */ 65 | static const char m_accelerators[]; 66 | 67 | /** Message box caption. */ 68 | char *m_caption; 69 | /** Message box message text. */ 70 | char *m_message; 71 | /** Number of buttons in message box. */ 72 | int m_buttons_count; 73 | /** Buttons infos, @see uimsgbox::button_info_t */ 74 | button_info_t m_buttons_infos[MSGBOX_MAX_BUTTONS]; 75 | /** Selected button id, set before closing the messagebox. */ 76 | int m_selected_button_id; 77 | }; 78 | 79 | #endif 80 | -------------------------------------------------------------------------------- /SRC/TUI/MSGQUEUE.CPP: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include "tui/msgqueue.hpp" 4 | 5 | uiqueue::uiqueue() { 6 | m_first = m_last = NULL; 7 | } 8 | 9 | uiqueue::~uiqueue() { 10 | if (m_first == NULL) 11 | return; 12 | 13 | item_t *item = m_first, *next; 14 | while (item->next != NULL) { 15 | next = item->next; 16 | delete item; 17 | item = next; 18 | } 19 | 20 | m_first = m_last = NULL; 21 | } 22 | 23 | bool_t uiqueue::get_first(message_t *message) { 24 | if (m_first == NULL) 25 | return FALSE; 26 | 27 | memcpy(message, &(m_first->data), sizeof(message_t)); 28 | return TRUE; 29 | } 30 | 31 | bool_t uiqueue::queue(message_t *message) { 32 | item_t *item = new item_t(); 33 | if (item == NULL) 34 | return FALSE; 35 | 36 | memcpy(&(item->data), message, sizeof(message_t)); 37 | item->previous = m_last; 38 | item->next = NULL; 39 | 40 | if (m_first == NULL) { 41 | m_first = m_last = item; 42 | return TRUE; 43 | } 44 | 45 | m_last->next = item; 46 | m_last = item; 47 | 48 | return TRUE; 49 | } 50 | 51 | bool_t uiqueue::dequeue(message_t *dest) { 52 | if (m_first == NULL) 53 | return FALSE; 54 | 55 | memcpy(dest, &(m_first->data), sizeof(message_t)); 56 | 57 | item_t *next = m_first->next; 58 | delete m_first; 59 | 60 | if (next != NULL) 61 | next->previous = NULL; 62 | 63 | m_first = next; 64 | 65 | if (m_first == NULL) 66 | m_last = NULL; 67 | 68 | return TRUE; 69 | } 70 | -------------------------------------------------------------------------------- /SRC/TUI/MSGQUEUE.HPP: -------------------------------------------------------------------------------- 1 | #ifndef MSGQUEUE_HPP 2 | #define MSGQUEUE_HPP 3 | 4 | #include "types.hpp" 5 | 6 | class uiqueue { 7 | public: 8 | /** Message types enumeration. */ 9 | enum message_types { 10 | /** Keyboard key down. */ 11 | TUIM_KEYDOWN = 1, 12 | /** Keyboard repeat while key down. */ 13 | TUIM_KEYPRESS, 14 | /** Keyboard key release. */ 15 | TUIM_KEYUP, 16 | 17 | /** Critical error handler has been triggered. */ 18 | TUIM_CRIT_ERR, 19 | 20 | /** Open new view. */ 21 | TUIM_OPEN_VIEW, 22 | /** Close view. */ 23 | TUIM_CLOSE_VIEW, 24 | /** Closed view result. */ 25 | TUIM_VIEW_RESULT 26 | }; 27 | 28 | /** Message sent from low-level code to controls. */ 29 | typedef struct message { 30 | /** Message type. */ 31 | message_types type; 32 | /** Message parameter. */ 33 | int param; 34 | /** Message additional data. */ 35 | void *data; 36 | } message_t; 37 | 38 | uiqueue(); 39 | ~uiqueue(); 40 | 41 | /** Return first message without removing if from the queue. 42 | * 43 | * @return TRUE on success, FALSE if queue is empty. 44 | */ 45 | bool_t get_first(message_t *message); 46 | 47 | /** Queue message to be dispatched from TUI main loop. 48 | * 49 | * @param message Message to be copied into the queue. 50 | * @return TRUE on success, FALSE if queue is full. 51 | */ 52 | bool_t queue(message_t *message); 53 | /** Dequeue message from view internal queue. 54 | * 55 | * @param dest Message on top of the queue will be copied here. 56 | * @return TRUE on success, FALSE if queue is empty. 57 | */ 58 | bool_t dequeue(message_t *dest); 59 | 60 | private: 61 | typedef struct item { 62 | message_t data; 63 | item *previous, *next; 64 | } item_t; 65 | 66 | item_t *m_first; 67 | item_t *m_last; 68 | }; 69 | 70 | #endif MSGQUEUE_HPP 71 | -------------------------------------------------------------------------------- /SRC/TUI/TEXTBOX.CPP: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include "tui/textbox.hpp" 5 | #include "tui/graph.hpp" 6 | #include "math.hpp" 7 | #include "string.hpp" 8 | #include "markdown.hpp" 9 | 10 | uitextbox::uitextbox(uint16_t id, int top, int left, int width, int height) 11 | : uicontrol(id) { 12 | m_rect.top = top; 13 | m_rect.left = left; 14 | m_rect.right = left + width - 1; 15 | m_rect.bottom = top + height - 1; 16 | 17 | m_preparsed = NULL; 18 | m_type = uitextbox::TEXT; 19 | m_lines = 0; 20 | m_lines_offsets = NULL; 21 | m_offset = 0; 22 | 23 | this->set_back_color_internal(uitheme::textbox::back); 24 | this->set_fore_color_internal(uitheme::textbox::fore); 25 | } 26 | 27 | uitextbox::~uitextbox() { 28 | this->discard_preparsed(); 29 | } 30 | 31 | bool_t uitextbox::set_source(const char *filename, 32 | uitextbox::source_type type) { 33 | if (filename == NULL) 34 | return FALSE; 35 | 36 | switch (type) { 37 | case uitextbox::TEXT: 38 | m_lines = 0; 39 | m_offset = 0; 40 | m_type = type; 41 | return wrap_text(filename); 42 | 43 | case uitextbox::MARKDOWN: 44 | m_lines = 0; 45 | m_offset = 0; 46 | m_type = type; 47 | return preparse(filename); 48 | } 49 | 50 | return FALSE; 51 | } 52 | 53 | bool_t uitextbox::wrap_text(const char *filename) { 54 | this->discard_preparsed(); 55 | 56 | FILE *input = fopen(filename, "rb"); 57 | if (input == NULL) 58 | return FALSE; 59 | 60 | m_preparsed = tmpfile(); 61 | if (m_preparsed == NULL) { 62 | fclose(input); 63 | return FALSE; 64 | } 65 | 66 | vga::text_rect_t rect; 67 | this->calc_text_rect(&rect); 68 | int columns = rect.right - rect.left + 1; 69 | 70 | char *buffer = (char *)malloc(columns + 2); 71 | if (buffer == NULL) { 72 | fclose(input); 73 | fclose(m_preparsed); 74 | m_preparsed = NULL; 75 | return FALSE; 76 | } 77 | 78 | int lines = 0; 79 | // Dummy value. 80 | fwrite(&lines, sizeof(lines), 1, m_preparsed); 81 | 82 | while (fgets(buffer, columns + 2, input) != NULL) { 83 | int length = strlen(buffer); 84 | char *end = buffer + length - 1; 85 | // The end of buffer is in the middle of a word. 86 | if (length == (columns + 1) && !string::is_white_space(*end)) { 87 | // Go back to nearest whitespace and seek back, so that each 88 | // buffer starts at the beginning of a word. 89 | while (end > buffer && !string::is_white_space(*end)) 90 | --end; 91 | 92 | // The buffer contains only part of one word longer than the 93 | // buffer itself. 94 | if (end == buffer) 95 | end = buffer + columns - 1; 96 | 97 | fseek(input, (end - buffer + 1) - length, SEEK_CUR); 98 | // Skip white-spaces on line end. 99 | while (end > buffer && string::is_white_space(*end)) 100 | --end; 101 | } else { 102 | while (end > buffer && 103 | (string::is_white_space(*end) || string::is_eol(*end))) 104 | --end; 105 | } 106 | 107 | length = (int)(end - buffer + 1); 108 | 109 | ++lines; 110 | fputc(length, m_preparsed); 111 | if (length > 0) 112 | fwrite(buffer, length, 1, m_preparsed); 113 | } 114 | 115 | free(buffer); 116 | 117 | fclose(input); 118 | 119 | fseek(m_preparsed, 0, SEEK_SET); 120 | fwrite(&lines, sizeof(int), 1, m_preparsed); 121 | 122 | fflush(m_preparsed); 123 | 124 | return this->update_lines_offsets(lines); 125 | } 126 | 127 | bool_t uitextbox::preparse(const char *filename) { 128 | this->discard_preparsed(); 129 | 130 | m_preparsed = tmpfile(); 131 | if (m_preparsed == NULL) 132 | return FALSE; 133 | 134 | vga::text_rect_t rect; 135 | this->calc_text_rect(&rect); 136 | 137 | markdown *parser = new markdown(); 138 | parser->set_source(filename); 139 | parser->set_width(rect.right - rect.left + 1); 140 | parser->set_output(m_preparsed); 141 | 142 | if (parser->parse() == FALSE) { 143 | delete parser; 144 | return FALSE; 145 | } 146 | 147 | int lines = parser->get_lines(); 148 | delete parser; 149 | 150 | return this->update_lines_offsets(lines); 151 | } 152 | 153 | bool_t uitextbox::update_lines_offsets(int lines_count) { 154 | if (m_lines_offsets != NULL) 155 | delete m_lines_offsets; 156 | 157 | m_lines = lines_count; 158 | m_lines_offsets = new uint32_t[m_lines]; 159 | if (m_lines_offsets == NULL) { 160 | m_lines = 0; 161 | return FALSE; 162 | } 163 | 164 | fseek(m_preparsed, sizeof(int), SEEK_SET); 165 | 166 | uint32_t offset = sizeof(int); 167 | for (int i = 0; i < lines_count; ++i) { 168 | int length = fgetc(m_preparsed); 169 | m_lines_offsets[i] = offset; 170 | offset += length + 1; 171 | fseek(m_preparsed, length, SEEK_CUR); 172 | } 173 | 174 | fseek(m_preparsed, 0, SEEK_SET); 175 | 176 | return TRUE; 177 | } 178 | 179 | void uitextbox::discard_preparsed() { 180 | if (m_preparsed != NULL) { 181 | // Automatically deleted on close because is a temporary file. 182 | fclose(m_preparsed); 183 | m_preparsed = NULL; 184 | } 185 | 186 | if (m_lines_offsets != NULL) { 187 | delete m_lines_offsets; 188 | m_lines_offsets = NULL; 189 | } 190 | } 191 | 192 | void uitextbox::scroll(int offset) { 193 | if (m_lines < 1) 194 | return; 195 | 196 | vga::text_rect_t rect; 197 | this->calc_text_rect(&rect); 198 | 199 | int displayed_lines = rect.bottom - rect.top + 1; 200 | int new_offset = max(0, min(m_lines - displayed_lines, m_offset + offset)); 201 | 202 | if (new_offset == m_offset) 203 | return; 204 | 205 | int delta = new_offset - m_offset; 206 | int start = 0; 207 | 208 | m_offset = new_offset; 209 | 210 | if (delta > 0) { 211 | vga::scroll_page_up((uint8_t)delta, m_blank_attrs, &rect); 212 | start = displayed_lines - delta; 213 | } else { 214 | delta = -delta; 215 | vga::scroll_page_down((uint8_t)delta, m_blank_attrs, &rect); 216 | } 217 | 218 | for (int i = start; i < start + delta; ++i) 219 | this->repaint_line(m_offset + i, &rect); 220 | 221 | this->repaint_scroll_bar(&rect); 222 | } 223 | 224 | void uitextbox::jump_to_begin() { 225 | if (m_offset == 0) 226 | return; 227 | 228 | m_offset = 0; 229 | this->repaint(); 230 | } 231 | 232 | void uitextbox::jump_to_end() { 233 | vga::text_rect_t rect; 234 | this->calc_text_rect(&rect); 235 | int displayed_lines = rect.bottom - rect.top + 1; 236 | 237 | int new_offset = max(0, m_lines - displayed_lines); 238 | if (m_offset == new_offset) 239 | return; 240 | 241 | m_offset = new_offset; 242 | this->repaint(); 243 | } 244 | 245 | void uitextbox::calc_text_rect(vga::text_rect_t *rect) { 246 | this->get_paint_rect(rect); 247 | // Exclude the vertical scrollbar. 248 | --rect->right; 249 | } 250 | 251 | void uitextbox::repaint_line(int index, vga::text_rect_t *rect) { 252 | fseek(m_preparsed, m_lines_offsets[index], SEEK_SET); 253 | 254 | int length = fgetc(m_preparsed); 255 | if (length == 0) 256 | return; 257 | 258 | int max_length = rect->right - rect->left + 1; 259 | length = min(length, max_length); 260 | 261 | char buffer[90]; 262 | if (fread(buffer, length, 1, m_preparsed) == 0) 263 | return; 264 | 265 | buffer[length] = '\0'; 266 | 267 | vga::set_cursor_pos(rect->top + index - m_offset, rect->left); 268 | vga::write_string(buffer, m_paint_attrs, vga::USE_ATTRS_UPDATE_CURSOR); 269 | } 270 | 271 | void uitextbox::repaint_scroll_bar(vga::text_rect_t *rect) { 272 | graphics::draw_scroll_bar(rect, m_scroll_bar_attrs, 273 | m_lines, m_offset); 274 | } 275 | 276 | void uitextbox::repaint() { 277 | if (this->can_repaint() == FALSE) { 278 | m_repaint_pending = TRUE; 279 | return; 280 | } 281 | 282 | vga::text_rect_t rect; 283 | this->calc_text_rect(&rect); 284 | 285 | int lines = rect.bottom - rect.top + 1; 286 | vga::scroll_page_up((uint8_t)lines, m_blank_attrs, &rect); 287 | 288 | ++rect.right; 289 | 290 | if (m_focused == TRUE) 291 | uigraph::apply_focus(&rect, m_paint_attrs, uigraph::FOCUS_OUTLINE); 292 | else 293 | uigraph::remove_focus(&rect, m_paint_attrs, uigraph::FOCUS_OUTLINE); 294 | 295 | --rect.right; 296 | 297 | this->repaint_scroll_bar(&rect); 298 | 299 | if (m_lines < 1) 300 | return; 301 | 302 | for (int i = m_offset; i < m_offset + lines; ++i) { 303 | if (i >= m_lines) 304 | break; 305 | 306 | this->repaint_line(i, &rect); 307 | } 308 | 309 | uicontrol::repaint(); 310 | } 311 | 312 | void uitextbox::on_message(uiqueue::message_t *msg, tui::action_t *action) { 313 | if (m_lines == 0 || msg->type != uiqueue::TUIM_KEYPRESS) 314 | return; 315 | 316 | keyboard::read_data_t *data = (keyboard::read_data_t *)msg->data; 317 | 318 | if (msg->type == uiqueue::TUIM_KEYPRESS) { 319 | vga::text_rect_t rect; 320 | this->calc_text_rect(&rect); 321 | int displayed_lines = rect.bottom - rect.top + 1; 322 | 323 | switch (data->virt_key) { 324 | case VK_HOME: 325 | this->jump_to_begin(); 326 | break; 327 | 328 | case VK_END: 329 | this->jump_to_end(); 330 | break; 331 | 332 | case VK_UP: 333 | case VK_LEFT: 334 | this->scroll(-1); 335 | break; 336 | 337 | case VK_DOWN: 338 | case VK_RIGHT: 339 | this->scroll(1); 340 | break; 341 | 342 | case VK_PGUP: 343 | this->scroll(-displayed_lines); 344 | break; 345 | 346 | case VK_PGDN: 347 | this->scroll(displayed_lines); 348 | break; 349 | } 350 | } 351 | } 352 | -------------------------------------------------------------------------------- /SRC/TUI/TEXTBOX.HPP: -------------------------------------------------------------------------------- 1 | #ifndef TUI_TEXTBOX_HPP 2 | #define TUI_TEXTBOX_HPP 3 | 4 | #include 5 | 6 | #include "types.hpp" 7 | 8 | #include "tui/control.hpp" 9 | 10 | /** Textbox control. */ 11 | class uitextbox : public uicontrol { 12 | public: 13 | enum source_type { 14 | TEXT, 15 | MARKDOWN 16 | }; 17 | 18 | uitextbox(uint16_t id, int top, int left, int width, int height); 19 | ~uitextbox(); 20 | 21 | bool_t set_source(const char *filename, source_type type); 22 | 23 | private: 24 | bool_t wrap_text(const char *filename); 25 | bool_t preparse(const char *filename); 26 | bool_t update_lines_offsets(int lines_count); 27 | void discard_preparsed(); 28 | 29 | void scroll(int offset); 30 | void jump_to_begin(); 31 | void jump_to_end(); 32 | 33 | void calc_text_rect(vga::text_rect_t *rect); 34 | 35 | void repaint_line(int index, vga::text_rect_t *rect); 36 | void repaint_scroll_bar(vga::text_rect_t *rect); 37 | void repaint(); 38 | 39 | void on_message(uiqueue::message_t *msg, tui::action_t *action); 40 | 41 | FILE *m_preparsed; 42 | source_type m_type; 43 | int m_lines; 44 | uint32_t *m_lines_offsets; 45 | int m_offset; 46 | }; 47 | 48 | #endif 49 | -------------------------------------------------------------------------------- /SRC/TUI/THEME.CPP: -------------------------------------------------------------------------------- 1 | #include "tui/theme.hpp" 2 | 3 | uint8_t uitheme::button::fore = 0x0b; 4 | uint8_t uitheme::button::back = 0x03; 5 | 6 | uint8_t uitheme::editbox::fore = 0x00; 7 | uint8_t uitheme::editbox::back = 0x07; 8 | uint8_t uitheme::editbox::selected_fore = 0x0b; 9 | uint8_t uitheme::editbox::selected_back = 0x03; 10 | 11 | uint8_t uitheme::label::fore = 0x07; 12 | 13 | uint8_t uitheme::listbox::fore = 0x00; 14 | uint8_t uitheme::listbox::back = 0x07; 15 | uint8_t uitheme::listbox::selected_fore = 0x0b; 16 | uint8_t uitheme::listbox::selected_back = 0x03; 17 | 18 | uint8_t uitheme::scroll_bar::fore = 0x07; 19 | uint8_t uitheme::scroll_bar::back = 0x00; 20 | 21 | uint8_t uitheme::textbox::fore = 0x00; 22 | uint8_t uitheme::textbox::back = 0x07; 23 | 24 | uint8_t uitheme::window::fore = 0x07; 25 | uint8_t uitheme::window::back = 0x08; 26 | uint8_t uitheme::window::caption_fore = 0x0f; 27 | uint8_t uitheme::window::caption_back = 0x07; 28 | 29 | uint8_t uitheme::shadow::fore = 0x08; 30 | uint8_t uitheme::shadow::back = 0x00; 31 | 32 | void uitheme::init(bool_t text_mode) { 33 | // Already initialized values for text-mode UI. 34 | if (text_mode == TRUE) 35 | return; 36 | 37 | // 1-bit per pixel theme for graphics UI. 38 | uint8_t back = 0x00, fore = 0x07; 39 | uitheme::button::fore = fore; 40 | uitheme::button::back = back; 41 | 42 | uitheme::editbox::fore = fore; 43 | uitheme::editbox::back = back; 44 | uitheme::editbox::selected_fore = back; 45 | uitheme::editbox::selected_back = fore; 46 | 47 | uitheme::label::fore = fore; 48 | 49 | uitheme::listbox::fore = fore; 50 | uitheme::listbox::back = back; 51 | uitheme::listbox::selected_fore = back; 52 | uitheme::listbox::selected_back = fore; 53 | 54 | uitheme::scroll_bar::fore = fore; 55 | uitheme::scroll_bar::back = back; 56 | 57 | uitheme::window::fore = fore; 58 | uitheme::window::back = back; 59 | uitheme::window::caption_fore = back; 60 | uitheme::window::caption_back = fore; 61 | } 62 | -------------------------------------------------------------------------------- /SRC/TUI/THEME.HPP: -------------------------------------------------------------------------------- 1 | #ifndef THEME_HPP 2 | #define THEME_HPP 3 | 4 | #include "types.hpp" 5 | 6 | class uitheme { 7 | public: 8 | struct button { 9 | static uint8_t fore; 10 | static uint8_t back; 11 | }; 12 | 13 | struct editbox { 14 | static uint8_t fore; 15 | static uint8_t back; 16 | static uint8_t selected_fore; 17 | static uint8_t selected_back; 18 | }; 19 | 20 | struct label { 21 | static uint8_t fore; 22 | }; 23 | 24 | struct listbox { 25 | static uint8_t fore; 26 | static uint8_t back; 27 | static uint8_t selected_fore; 28 | static uint8_t selected_back; 29 | }; 30 | 31 | struct scroll_bar { 32 | static uint8_t fore; 33 | static uint8_t back; 34 | }; 35 | 36 | struct textbox { 37 | static uint8_t fore; 38 | static uint8_t back; 39 | }; 40 | 41 | struct window { 42 | static uint8_t fore; 43 | static uint8_t back; 44 | static uint8_t caption_fore; 45 | static uint8_t caption_back; 46 | }; 47 | 48 | struct shadow { 49 | static uint8_t fore; 50 | static uint8_t back; 51 | }; 52 | 53 | static void init(bool_t text_mode); 54 | }; 55 | 56 | #endif 57 | -------------------------------------------------------------------------------- /SRC/TUI/TUI.CPP: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include "vga.hpp" 4 | #include "tui/tui.hpp" 5 | #include "tui/theme.hpp" 6 | #include "tui/graph.hpp" 7 | #include "tui/view.hpp" 8 | #include "tui/window.hpp" 9 | 10 | bool_t tui::text_mode; 11 | uiqueue *tui::messages; 12 | 13 | tui::tui(bool_t text_mode) { 14 | m_curr_view = NULL; 15 | m_view_closing = FALSE; 16 | 17 | tui::text_mode = text_mode; 18 | tui::messages = new uiqueue(); 19 | 20 | uigraph::setup_frame_chars(); 21 | 22 | uitheme::init(text_mode); 23 | 24 | vga::set_blinking(FALSE); 25 | vga::enable_cursor(FALSE); 26 | } 27 | 28 | tui::~tui() { 29 | uiview *view = (uiview *)m_curr_view; 30 | while (view != NULL) { 31 | uiview *previous = (uiview *)view->get_previous(); 32 | delete view; 33 | view = previous; 34 | } 35 | 36 | delete tui::messages; 37 | tui::messages = NULL; 38 | 39 | m_curr_view = NULL; 40 | 41 | vga::set_blinking(TRUE); 42 | vga::enable_cursor(TRUE); 43 | 44 | uigraph::restore_chars(); 45 | } 46 | 47 | bool_t tui::task() { 48 | if (m_curr_view == NULL) 49 | return FALSE; 50 | 51 | if (!keyboard::read(&(this->keyb_data))) 52 | return TRUE; 53 | 54 | uiwindow *curr_window = m_curr_view->get_window(); 55 | uicontrol *focused = curr_window->get_focused_control(); 56 | if (focused == NULL) 57 | return FALSE; 58 | 59 | action_t action; 60 | memset(&action, 0x00, sizeof(tui::action_t)); 61 | 62 | if (m_curr_view->is_modal() == FALSE && 63 | this->keyb_data.type == keyboard::KEV_MAKE && 64 | this->keyb_data.virt_key == VK_F3 && 65 | ((this->keyb_data.shifts & VS_ALT) && 66 | ((this->keyb_data.shifts & ~VS_ALT) == 0))) { 67 | 68 | this->close_view(m_curr_view); 69 | 70 | return TRUE; 71 | } else if (this->keyb_data.type == keyboard::KEV_PRESS && 72 | this->keyb_data.virt_key == VK_TAB) { 73 | if (this->keyb_data.shifts & VS_SHIFT && 74 | (this->keyb_data.shifts & ~VS_SHIFT) == 0) 75 | curr_window->focus_previous_control(); 76 | else 77 | curr_window->focus_next_control(); 78 | 79 | focused = curr_window->get_focused_control(); 80 | 81 | action.type = TUIA_CTRL_FOCUS; 82 | action.id = focused->get_id(); 83 | action.control = focused; 84 | 85 | m_curr_view->on_action(&action); 86 | 87 | return TRUE; 88 | } 89 | 90 | if (this->keyb_data.type == keyboard::KEV_MAKE && 91 | this->keyb_data.shifts == VS_LALT) { 92 | uicontrol *accel_control = NULL; 93 | char ch = keyboard::virtual_key_to_char(this->keyb_data.virt_key); 94 | if (ch > 0) 95 | accel_control = curr_window->get_control_by_accelerator(ch); 96 | 97 | if (accel_control != NULL) { 98 | curr_window->focus_control(accel_control); 99 | accel_control->on_accelerator(&action); 100 | m_curr_view->on_action(&action); 101 | return TRUE; 102 | } 103 | } 104 | 105 | uiqueue::message_t msg; 106 | memset(&msg, 0x00, sizeof(uiqueue::message_t)); 107 | 108 | switch (this->keyb_data.type) { 109 | case keyboard::KEV_MAKE: 110 | msg.type = uiqueue::TUIM_KEYDOWN; 111 | msg.data = &(this->keyb_data); 112 | break; 113 | 114 | case keyboard::KEV_PRESS: 115 | msg.type = uiqueue::TUIM_KEYPRESS; 116 | msg.data = &(this->keyb_data); 117 | break; 118 | 119 | case keyboard::KEV_BREAK: 120 | msg.type = uiqueue::TUIM_KEYUP; 121 | msg.data = &(this->keyb_data); 122 | break; 123 | } 124 | 125 | focused->on_message(&msg, &action); 126 | m_curr_view->on_action(&action); 127 | 128 | // TODO: need better implementation. 129 | m_view_closing = FALSE; 130 | while (m_view_closing == FALSE && tui::messages->dequeue(&msg) == TRUE) { 131 | switch (msg.type) { 132 | case uiqueue::TUIM_OPEN_VIEW: 133 | this->open_view((uiview *)msg.data); 134 | break; 135 | 136 | case uiqueue::TUIM_CLOSE_VIEW: 137 | this->close_view((uiview *)msg.data); 138 | m_view_closing = TRUE; 139 | break; 140 | 141 | case uiqueue::TUIM_CRIT_ERR: 142 | int pippo = 3; 143 | break; 144 | } 145 | } 146 | 147 | return TRUE; 148 | } 149 | 150 | int tui::modal_loop() { 151 | while (this->task()) 152 | if (m_view_closing == TRUE) 153 | break; 154 | 155 | int result = 0; 156 | uiqueue::message msg; 157 | tui::messages->get_first(&msg); 158 | if (msg.type == uiqueue::TUIM_VIEW_RESULT) { 159 | result = msg.param; 160 | tui::messages->dequeue(&msg); 161 | } 162 | 163 | m_view_closing = FALSE; 164 | return result; 165 | } 166 | 167 | bool_t tui::open_view(uiview *view) { 168 | if (view->open(m_curr_view) == FALSE) { 169 | delete view; 170 | return FALSE; 171 | } 172 | 173 | m_curr_view = view; 174 | 175 | return TRUE; 176 | } 177 | 178 | bool_t tui::close_view(uiview *view) { 179 | action_t action; 180 | action.type = TUIA_CTRL_DESTROY; 181 | action.id = view->get_window()->get_id(); 182 | 183 | if (view->on_action(&action) != TRUE) 184 | return FALSE; 185 | 186 | uiview *previous = NULL; 187 | if (view->close(&previous) == TRUE) 188 | m_curr_view = previous; 189 | 190 | if (previous != NULL) { 191 | memset(&action, 0x00, sizeof(action_t)); 192 | action.type = TUIA_VIEW_CLOSED; 193 | action.view = view; 194 | previous->on_action(&action); 195 | } 196 | 197 | delete view; 198 | return TRUE; 199 | } 200 | -------------------------------------------------------------------------------- /SRC/TUI/TUI.HPP: -------------------------------------------------------------------------------- 1 | #ifndef TUI_HPP 2 | #define TUI_HPP 3 | 4 | #include "types.hpp" 5 | 6 | #include "keyboard.hpp" 7 | 8 | #include "tui/msgqueue.hpp" 9 | 10 | // Forward declarations. 11 | class uicontrol; 12 | class uiview; 13 | 14 | /** Text-based and graphics user interface. */ 15 | class tui { 16 | public: 17 | /** Control actions enumeration. */ 18 | enum action_types { 19 | /** A view is closed. */ 20 | TUIA_VIEW_CLOSED = 1, 21 | /** Control activated by ENTER key. */ 22 | TUIA_CTRL_ACTIVATED, 23 | /** New control is focused on current view. */ 24 | TUIA_CTRL_FOCUS, 25 | /** Control is destroyed. */ 26 | TUIA_CTRL_DESTROY, 27 | /** Control value is changed. */ 28 | TUIA_CTRL_VALUE_CHANGED, 29 | /** Control item selection is changing. */ 30 | TUIA_ITEM_SELECTING, 31 | /** Control item is selected, triggered on keyboard key release. */ 32 | TUIA_ITEM_SELECTED, 33 | /** Control item activated by ENTER key. */ 34 | TUIA_ITEM_ACTIVATED 35 | }; 36 | 37 | /** Action returned by controls when a message is processed. */ 38 | typedef struct { 39 | /** Action type. */ 40 | action_types type; 41 | /** Control id. */ 42 | uint16_t id; 43 | /** Item id, if the action is on a control item. */ 44 | uint16_t item_id; 45 | /** Pointer to control that has generated the action. */ 46 | uicontrol *control; 47 | /** Pointer to view that has generated the action, for view actions. */ 48 | uiview *view; 49 | } action_t; 50 | 51 | /** User-interface initialization. 52 | * 53 | * @param text_mode TRUE for text-mode UI, FALSE for graphics UI. 54 | */ 55 | tui(bool_t text_mode); 56 | ~tui(); 57 | 58 | /** Run message and actions handling task. 59 | * 60 | * @return TRUE if event is processed, FALSE if loop must terminate. 61 | */ 62 | bool_t task(); 63 | 64 | /** Loop modally until last opened view is closed then return. 65 | * 66 | * @return View result value if view queue TUIM_VIEW_RESULT event, 67 | * zero otherwise. 68 | */ 69 | int modal_loop(); 70 | 71 | /** Open specified view. 72 | * 73 | * @param view View to be opened and made current. 74 | * @return TRUE on correct initialization and open, FALSE otherwise. 75 | */ 76 | bool_t open_view(uiview *view); 77 | /** Close specified view. 78 | * 79 | * @param view View to be closed, previous one becomes current. 80 | * @return TRUE on correct initialization and open, FALSE otherwise. 81 | */ 82 | bool_t close_view(uiview *view); 83 | 84 | /** Used on TUI uigraph. 85 | * 86 | * @note It must only be read. 87 | */ 88 | static bool_t text_mode; 89 | 90 | static uiqueue *messages; 91 | private: 92 | /** Currently active view. */ 93 | uiview *m_curr_view; 94 | /** Data read from keyboard. */ 95 | keyboard::read_data_t keyb_data; 96 | /** Track if topmost view is closed for modal_loop method. */ 97 | bool_t m_view_closing; 98 | }; 99 | 100 | #endif 101 | -------------------------------------------------------------------------------- /SRC/TUI/VIEW.CPP: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include "tui/view.hpp" 4 | #include "tui/window.hpp" 5 | 6 | #define MAX_QUEUE_SIZE 8 7 | 8 | uiview::uiview() { 9 | m_is_modal = FALSE; 10 | 11 | m_window = NULL; 12 | m_state = STATE_INITIALIZING; 13 | m_previous = NULL; 14 | } 15 | 16 | uiview::~uiview() { 17 | if (m_window != NULL) { 18 | delete m_window; 19 | m_window = NULL; 20 | } 21 | 22 | m_state = STATE_DESTROYED; 23 | m_previous = NULL; 24 | } 25 | 26 | bool_t uiview::open(uiview *previous) { 27 | if (m_state != STATE_INITIALIZING) 28 | return FALSE; 29 | 30 | if (this->init_controls() == FALSE) 31 | return FALSE; 32 | 33 | m_previous = previous; 34 | 35 | m_window->set_view(this); 36 | m_state = STATE_OPENED; 37 | 38 | this->got_focus(); 39 | 40 | return TRUE; 41 | } 42 | 43 | bool_t uiview::close(uiview **previous) { 44 | if (m_state != STATE_OPENED) 45 | return FALSE; 46 | 47 | m_state = STATE_CLOSED; 48 | 49 | m_window->close(); 50 | 51 | if (m_previous != NULL) 52 | m_previous->got_focus(); 53 | 54 | *previous = m_previous; 55 | 56 | return TRUE; 57 | } 58 | 59 | bool_t uiview::got_focus() { 60 | if (m_state != STATE_OPENED) 61 | return FALSE; 62 | 63 | m_window->repaint(); 64 | m_window->got_focus(); 65 | 66 | return TRUE; 67 | } 68 | 69 | bool_t uiview::queue_open_view(uiview *view) { 70 | uiqueue::message_t msg; 71 | msg.type = uiqueue::TUIM_OPEN_VIEW; 72 | msg.data = view; 73 | return tui::messages->queue(&msg); 74 | } 75 | 76 | bool_t uiview::queue_close_view() { 77 | return this->queue_close_view(this); 78 | } 79 | 80 | bool_t uiview::queue_close_view(uiview *view) { 81 | uiqueue::message_t msg; 82 | msg.type = uiqueue::TUIM_CLOSE_VIEW; 83 | msg.data = view; 84 | return tui::messages->queue(&msg); 85 | } 86 | 87 | bool_t uiview::queue_result(int result) { 88 | uiqueue::message_t msg; 89 | msg.type = uiqueue::TUIM_VIEW_RESULT; 90 | msg.param = result; 91 | msg.data = NULL; 92 | return tui::messages->queue(&msg); 93 | } 94 | -------------------------------------------------------------------------------- /SRC/TUI/VIEW.HPP: -------------------------------------------------------------------------------- 1 | #ifndef TUI_VIEW_HPP 2 | #define TUI_VIEW_HPP 3 | 4 | #include "tui/tui.hpp" 5 | 6 | // Forward declaration. 7 | class uiwindow; 8 | 9 | /** View to present controls and receive action form TUI main loop. */ 10 | class uiview { 11 | public: 12 | /** View state. */ 13 | enum state { 14 | /** View is allocated but not visible. */ 15 | STATE_INITIALIZING, 16 | /** View is visible on the screen. */ 17 | STATE_OPENED, 18 | /** View is closed but not deallocated. */ 19 | STATE_CLOSED, 20 | /** View, linked window and all descendant control are deallocated. */ 21 | STATE_DESTROYED 22 | }; 23 | 24 | uiview(); 25 | virtual ~uiview(); 26 | 27 | /** Get view state. 28 | * 29 | * @return View state. 30 | */ 31 | state get_state() { return m_state; } 32 | 33 | /** Modal view flag status. 34 | * 35 | * @return True if view is modal, false otherwise. 36 | */ 37 | bool_t is_modal() { return m_is_modal; } 38 | 39 | /** Get window control linked to this view. 40 | * 41 | * @return Window object. 42 | */ 43 | uiwindow *get_window() { return m_window; } 44 | 45 | /** Get previous view. 46 | * 47 | * @return View that opened this one. 48 | */ 49 | uiview *get_previous() { return m_previous; } 50 | 51 | /** Called to allocate and initialize al controls. 52 | * 53 | * @return TRUE on success, FALSE otherwise. 54 | */ 55 | virtual bool_t init_controls() = 0; 56 | 57 | /** Called when the view receive an action. 58 | * 59 | * @param action Received action. 60 | * @return TRUE if action is processed, FALSE otherwise. 61 | */ 62 | virtual bool_t on_action(tui::action_t *action) = 0; 63 | 64 | /** Open the view displaying it on screen. 65 | * 66 | * @param previous Previous focused view. 67 | * @return TRUE on success, FALSE otherwise. 68 | */ 69 | bool_t open(uiview *previous); 70 | /** Close the view repainting the previous one. 71 | * 72 | * @param previous Filled with previous view. 73 | * @return TRUE on success, FALSE otherwise. 74 | */ 75 | bool_t close(uiview **previous); 76 | /** Called when the view got focus. 77 | * 78 | * @return TRUE on success, FALSE if state is not STATE_OPENED. 79 | */ 80 | bool_t got_focus(); 81 | 82 | protected: 83 | /** Queue message to open specified view. 84 | * 85 | * @param view View to open. 86 | * @return TRUE on queue success, FALSE if queue is full. 87 | */ 88 | bool_t queue_open_view(uiview *view); 89 | /** Queue message to close this view. 90 | * 91 | * @return TRUE on queue success, FALSE if queue is full. 92 | */ 93 | bool_t queue_close_view(); 94 | /** Queue message to close specified view. 95 | * 96 | * @param view View to close. 97 | * @return TRUE on queue success, FALSE if queue is full. 98 | */ 99 | bool_t queue_close_view(uiview *view); 100 | /** Queue message to store result on view close. 101 | * 102 | * @param result View result. 103 | * @return TRUE on queue success, FALSE if queue is full. 104 | */ 105 | bool_t queue_result(int result); 106 | 107 | /** View state. */ 108 | state m_state; 109 | /** Modal view flag. */ 110 | bool_t m_is_modal; 111 | /** Window linked to this view. */ 112 | uiwindow *m_window; 113 | /** Previous view. */ 114 | uiview *m_previous; 115 | }; 116 | 117 | #endif 118 | -------------------------------------------------------------------------------- /SRC/TUI/VIEWS/OPENSAVE.HPP: -------------------------------------------------------------------------------- 1 | #ifndef OPENSAVE_HPP 2 | #define OPENSAVE_HPP 3 | 4 | #include 5 | 6 | #include "types.hpp" 7 | #include "tui/view.hpp" 8 | 9 | // Forward declarations. 10 | class uilabel; 11 | class uieditbox; 12 | class uilistbox; 13 | class uibutton; 14 | class uimsgbox; 15 | 16 | /** Open/Save file view. */ 17 | class uiopensave : public uiview { 18 | public: 19 | /** Working modes. */ 20 | enum mode { 21 | /** Open/Select file. */ 22 | MODE_OPEN, 23 | /** Save existing or new file. */ 24 | MODE_SAVE 25 | }; 26 | 27 | /** Open/Save file view. 28 | * 29 | * @see uiopensave::mode 30 | * @param mode Open or save working mode. 31 | * @param filepath Start fully qualified path or filename, 32 | * null for current working directory. 33 | * @param Wildcard to filter displayed files. 34 | */ 35 | uiopensave(uiopensave::mode mode, const char *filepath, 36 | const char *wildcard); 37 | 38 | /** Get selected file. 39 | * 40 | * @param buffer Filled with fully qualified path to selected filename 41 | * @return TRUE if file has been selected, FALSE otherwise. 42 | */ 43 | bool_t get_selected_fullpath(char *buffer); 44 | 45 | virtual bool_t init_controls(); 46 | virtual bool_t on_action(tui::action_t *action); 47 | 48 | private: 49 | enum CTRL_ID { 50 | ID_WINDOW = 1, 51 | ID_LBL_FILENAME, 52 | ID_EDIT_FILENAME, 53 | ID_LBL_FILE_FULLPATH, 54 | ID_LBL_FILE_INFO, 55 | ID_LBL_FILES, 56 | ID_LIST_FILES, 57 | ID_LBL_DIRS, 58 | ID_LIST_DIRS, 59 | ID_BTN_OK, 60 | ID_BTN_CANCEL 61 | }; 62 | 63 | /** Normalize current path to uppercase and backslash as separators. */ 64 | void uiopensave::normalize_curr_path(); 65 | 66 | /** Check if specified path is a valid root path on existing drive. 67 | * 68 | * @param path Path to test. 69 | * @return TRUE if valid root path (X:\), FALSE otherwise. 70 | */ 71 | bool_t uiopensave::is_valid_root_path(const char *path); 72 | 73 | /** Append directory to m_curr_path. 74 | * 75 | * @param dir directory to append. 76 | */ 77 | void push_directory(const char *dir); 78 | /** Remove inner directory from path. 79 | * 80 | * @return Removed directory, NULL when on root directory. 81 | */ 82 | const char *pop_directory(); 83 | 84 | /** Populate files listbox control. 85 | * 86 | * @return TRUE on success, FALSE on allocation error. 87 | */ 88 | bool_t populate_files(); 89 | /** Populate directories and drives listbox control. 90 | * 91 | * @return TRUE on success, FALSE on allocation error. 92 | */ 93 | bool_t populate_dirs(); 94 | 95 | /** Get and format file info. 96 | * 97 | * @param filename Fully qualified filename. 98 | * @param buffer Filled with formatted info. 99 | */ 100 | void get_file_info(const char *filename, char *buffer); 101 | 102 | /** Check if given filename is valid. 103 | * 104 | * @param filename Filename to test. 105 | * @return TRUE if valid, FALSE otherwise. 106 | */ 107 | bool_t is_valid_filename(const char *filename); 108 | /** Check if current selected file is valid. 109 | * 110 | * @return TRUE if file have a valid filename and is in an existing 111 | * path, FALSE otherwise. 112 | */ 113 | bool_t valid_file_selected(); 114 | 115 | /** Create the messagebox to ask the user whether to overwrite the file. 116 | * 117 | * @return Message box object, NULL on allocation error. 118 | */ 119 | uimsgbox *create_overwrite_msgbox(); 120 | 121 | /** Current working mode. */ 122 | mode m_mode; 123 | 124 | //TODO: char *m_caption; 125 | /** Previous path to be restored after a critical error. */ 126 | char m_prev_path[MAXPATH]; 127 | /** Path for which files are displayed. */ 128 | char m_curr_path[MAXPATH]; 129 | /** Wildcard to filter displayed files. */ 130 | char m_curr_wildcard[MAXFILE + MAXEXT - 1]; 131 | /** TRUE if cancel button is pressed, FALSE otherwise. */ 132 | bool_t m_canceled; 133 | char m_start_filename[MAXFILE + MAXEXT - 1]; 134 | 135 | uilabel *m_lbl_filename; 136 | uieditbox *m_edit_filename; 137 | uilabel *m_lbl_file_fullpath; 138 | uilabel *m_lbl_file_info; 139 | uilabel *m_lbl_files; 140 | uilistbox *m_list_files; 141 | uilabel *m_lbl_dirs; 142 | uilistbox *m_list_dirs; 143 | uibutton *m_btn_ok; 144 | uibutton *m_btn_cancel; 145 | 146 | uimsgbox *m_msgbox; 147 | }; 148 | 149 | #endif 150 | -------------------------------------------------------------------------------- /SRC/TUI/WINDOW.CPP: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | #include "tui/window.hpp" 4 | #include "tui/graph.hpp" 5 | 6 | #include "graphics.hpp" 7 | #include "math.hpp" 8 | 9 | uiwindow::uiwindow(uint16_t id, int top, int left, int width, int height, 10 | uiwindow::style win_style, const char *caption) 11 | : uicontrol(id) { 12 | m_style = win_style; 13 | 14 | if (m_style & uiwindow::CAPTIONED) { 15 | int caption_len = strlen(caption); 16 | m_caption = new char[caption_len + 3]; 17 | *m_caption = ' '; 18 | strcpy(m_caption + 1, caption); 19 | *(m_caption + caption_len + 1) = ' '; 20 | *(m_caption + caption_len + 2) = NULL; 21 | } else 22 | m_caption = NULL; 23 | 24 | if (m_style & uiwindow::CENTERED) { 25 | const vga::state_t *state = vga::get_current_state(); 26 | top = (state->rows - height) >> 1; 27 | left = (80 - width) >> 1; 28 | } 29 | 30 | m_rect.top = top; 31 | m_rect.left = left; 32 | m_rect.right = left + width - 1; 33 | m_rect.bottom = top + height - 1; 34 | m_focused_control = NULL; 35 | 36 | this->set_back_color_internal(uitheme::window::back); 37 | this->set_fore_color_internal(uitheme::window::fore); 38 | 39 | this->set_caption_back_color_internal(uitheme::window::caption_back); 40 | this->set_caption_fore_color_internal(uitheme::window::caption_fore); 41 | } 42 | 43 | uiwindow::~uiwindow() { 44 | if (m_caption != NULL) { 45 | delete m_caption; 46 | m_caption = NULL; 47 | } 48 | } 49 | 50 | void uiwindow::close() { 51 | 52 | } 53 | 54 | void uiwindow::got_focus() { 55 | uicontrol::got_focus(); 56 | 57 | if (m_focused_control == NULL) 58 | this->focus_next_control(); 59 | } 60 | 61 | void uiwindow::lose_focus() { 62 | m_focused_control->lose_focus(); 63 | m_focused_control = NULL; 64 | 65 | uicontrol::lose_focus(); 66 | } 67 | 68 | void uiwindow::focus_next_control() { 69 | if (m_focused_control != NULL) { 70 | m_focused_control->lose_focus(); 71 | 72 | do { 73 | m_focused_control = m_focused_control->get_next(); 74 | } while (m_focused_control != NULL && 75 | m_focused_control->is_focusable() == FALSE); 76 | } 77 | 78 | if (m_focused_control == NULL) { 79 | m_focused_control = this->get_first_child(); 80 | 81 | while (m_focused_control != NULL && 82 | m_focused_control->is_focusable() == FALSE) 83 | m_focused_control = m_focused_control->get_next(); 84 | } 85 | 86 | if (m_focused_control != NULL) 87 | m_focused_control->got_focus(); 88 | } 89 | 90 | void uiwindow::focus_previous_control() { 91 | if (m_focused_control != NULL) 92 | m_focused_control->lose_focus(); 93 | 94 | do { 95 | m_focused_control = m_focused_control->get_previous(); 96 | } while (m_focused_control != NULL && 97 | m_focused_control->is_focusable() == FALSE); 98 | 99 | if (m_focused_control == NULL) { 100 | m_focused_control = this->get_last_child(); 101 | 102 | while (m_focused_control != NULL && 103 | m_focused_control->is_focusable() == FALSE) 104 | m_focused_control = m_focused_control->get_previous(); 105 | } 106 | 107 | if (m_focused_control != NULL) 108 | m_focused_control->got_focus(); 109 | } 110 | 111 | bool_t uiwindow::focus_control(uicontrol *ctrl) { 112 | if (this->is_child_control(ctrl) == FALSE) 113 | return FALSE; 114 | 115 | if (m_focused_control != NULL) 116 | m_focused_control->lose_focus(); 117 | 118 | m_focused_control = ctrl; 119 | m_focused_control->got_focus(); 120 | return TRUE; 121 | } 122 | 123 | uicontrol *uiwindow::get_control_by_accelerator(char accelerator) { 124 | uicontrol *ctrl = this->get_first_child(); 125 | char accel; 126 | 127 | do { 128 | accel = ctrl->get_accelerator(); 129 | if (ctrl->is_focusable() && accel > 0 && accel == accelerator) 130 | return ctrl; 131 | 132 | } while ((ctrl = ctrl->get_next()) != NULL); 133 | 134 | return NULL; 135 | } 136 | 137 | uint8_t uiwindow::get_caption_fore_color() { 138 | uint8_t back_color, fore_color, blink; 139 | vga::decode_attrs(m_caption_attrs, &back_color, &fore_color, &blink); 140 | return fore_color; 141 | } 142 | 143 | void uiwindow::set_caption_fore_color(uint8_t color) { 144 | // Cannot change color on 1-bit graphics UI. 145 | if (tui::text_mode == FALSE) 146 | return; 147 | 148 | this->set_caption_fore_color_internal(color); 149 | this->repaint(); 150 | } 151 | 152 | void uiwindow::set_caption_fore_color_internal(uint8_t color) { 153 | uint8_t back_color, fore_color, blink; 154 | vga::decode_attrs(m_caption_attrs, &back_color, &fore_color, &blink); 155 | m_caption_attrs = vga::encode_attrs(back_color, color, 0); 156 | } 157 | 158 | uint8_t uiwindow::get_caption_back_color() { 159 | uint8_t back_color, fore_color, blink; 160 | vga::decode_attrs(m_caption_attrs, &back_color, &fore_color, &blink); 161 | return back_color; 162 | } 163 | 164 | void uiwindow::set_caption_back_color(uint8_t color) { 165 | // Cannot change color on 1-bit graphics UI. 166 | if (tui::text_mode == FALSE) 167 | return; 168 | 169 | this->set_caption_back_color_internal(color); 170 | this->repaint(); 171 | } 172 | 173 | void uiwindow::set_caption_back_color_internal(uint8_t color) { 174 | uint8_t back_color, fore_color, blink; 175 | vga::decode_attrs(m_caption_attrs, &back_color, &fore_color, &blink); 176 | m_caption_attrs = vga::encode_attrs(color, fore_color, 0); 177 | } 178 | 179 | void uiwindow::repaint() { 180 | if (this->can_repaint() == FALSE) { 181 | m_repaint_pending = TRUE; 182 | return; 183 | } 184 | 185 | vga::text_rect_t rect; 186 | this->get_paint_rect(&rect); 187 | 188 | uigraph::draw_shadow(&rect); 189 | 190 | graphics::draw_filled_rect(&rect, m_paint_attrs); 191 | 192 | if (m_style & uiwindow::FRAMED) { 193 | if (tui::text_mode == TRUE) 194 | uigraph::draw_frame(&rect, m_paint_attrs); 195 | else 196 | graphics::draw_outline(&rect, m_paint_attrs); 197 | } 198 | 199 | if (m_style & uiwindow::CAPTIONED) { 200 | uint8_t rect_width = rect.right - rect.left + 1; 201 | uint8_t caption_length = min(rect_width, (uint8_t)strlen(m_caption)); 202 | uint8_t caption_start = (rect_width >> 1) - (caption_length >> 1); 203 | 204 | vga::set_cursor_pos(rect.top, rect.left); 205 | vga::write_char(' ', m_paint_attrs, rect_width); 206 | 207 | vga::set_cursor_pos(rect.top, rect.left + caption_start); 208 | vga::write_string(m_caption, caption_length, m_paint_attrs, 209 | vga::USE_ATTRS_UPDATE_CURSOR); 210 | 211 | uigraph::reverse_item(rect.top, rect.left, m_paint_attrs, rect_width); 212 | } 213 | 214 | // Repaint descendants. 215 | uicontrol::repaint(); 216 | } 217 | -------------------------------------------------------------------------------- /SRC/TUI/WINDOW.HPP: -------------------------------------------------------------------------------- 1 | #ifndef TUI_WINDOW_HPP 2 | #define TUI_WINDOW_HPP 3 | 4 | #include "types.hpp" 5 | 6 | #include "tui/control.hpp" 7 | /** Window control */ 8 | class uiwindow : public uicontrol { 9 | public: 10 | enum style { 11 | CAPTIONED = 0x01, 12 | FRAMED = 0x02, 13 | 14 | CENTERED = 0x10 15 | }; 16 | 17 | /** Window constructor. 18 | * 19 | * @see uiwindow::style 20 | * @param id Control unique identifier. 21 | * @param top Zero-based vertical position in characters. 22 | * @param left Zero-based horizontal position in characters. 23 | * @param width Window width in characters. 24 | * @param height Window height in characters. 25 | * @param win_style Window style. 26 | * @param caption Window caption. 27 | */ 28 | uiwindow(uint16_t id, int top, int left, int width, int height, 29 | style win_style, const char *caption); 30 | ~uiwindow(); 31 | 32 | /** Close the window and dispose all contained controls. */ 33 | void close(); 34 | 35 | void got_focus(); 36 | void lose_focus(); 37 | 38 | /** Focus next or first focusable contained control. */ 39 | void focus_next_control(); 40 | 41 | /** Focus previous focusable contained control. */ 42 | void focus_previous_control(); 43 | 44 | /** Focus specific control. 45 | * 46 | * @param ctrl Control to be focused. 47 | */ 48 | bool_t focus_control(uicontrol *ctrl); 49 | 50 | /** Get window caption text/foreground color. 51 | * 52 | * @return Color index. 53 | */ 54 | uint8_t get_caption_fore_color(); 55 | /** Set window caption text/foreground color and repaint control. 56 | * 57 | * @param color Color index. 58 | */ 59 | void set_caption_fore_color(uint8_t color); 60 | /** Get window caption background color. 61 | * 62 | * @return Color index. 63 | */ 64 | uint8_t get_caption_back_color(); 65 | /** Set window caption background color and repaint control. 66 | * 67 | * @param color Color index. 68 | */ 69 | void set_caption_back_color(uint8_t color); 70 | 71 | virtual void repaint(); 72 | 73 | /** Get current focused control. 74 | * 75 | * @return Pointer to current focused control. 76 | */ 77 | uicontrol *get_focused_control() { return m_focused_control; } 78 | 79 | /** Get control by accelerator character. 80 | * 81 | * @param accelerator Accelerator character. 82 | * @param First matching control. 83 | */ 84 | uicontrol *get_control_by_accelerator(char accelerator); 85 | 86 | protected: 87 | /** Set window caption text/foreground color. 88 | * 89 | * @param color Color index. 90 | */ 91 | void set_caption_fore_color_internal(uint8_t color); 92 | /** Set window caption background color. 93 | * 94 | * @param color Color index. 95 | */ 96 | void set_caption_back_color_internal(uint8_t color); 97 | 98 | private: 99 | /** Window style. */ 100 | style m_style; 101 | /** Window caption. */ 102 | char *m_caption; 103 | /** Window caption text attributes. */ 104 | uint8_t m_caption_attrs; 105 | /** Currently focused control. */ 106 | uicontrol *m_focused_control; 107 | }; 108 | 109 | #endif 110 | -------------------------------------------------------------------------------- /SRC/TYPES.HPP: -------------------------------------------------------------------------------- 1 | #ifndef TYPES_H 2 | #define TYPES_H 3 | 4 | #include 5 | 6 | #define TRUE ((bool_t)1) 7 | #define FALSE ((bool_t)0) 8 | 9 | /** Boolean. */ 10 | typedef unsigned char bool_t; 11 | 12 | /** Signed 8-bit integer. */ 13 | typedef char int8_t; 14 | /** Signed 16-bit integer. */ 15 | typedef short int16_t; 16 | /** Signed 32-bit integer. */ 17 | typedef long int32_t; 18 | 19 | /** Unsigned 8-bit integer. */ 20 | typedef unsigned char uint8_t; 21 | /** Unsigned 16-bit integer. */ 22 | typedef unsigned short uint16_t; 23 | /** Unsigned 32-bit integer. */ 24 | typedef unsigned long uint32_t; 25 | 26 | #endif -------------------------------------------------------------------------------- /SRC/UI.HPP: -------------------------------------------------------------------------------- 1 | #ifndef UI_HPP 2 | #define UI_HPP 3 | 4 | #include "list.hpp" 5 | #include "vga.hpp" 6 | #include "encoder.hpp" 7 | 8 | #define UI_THUMBNAIL_PATH_SIZE 80 9 | #define UI_FILTER_STR_SIZE 80 10 | 11 | /** User interface handling object. */ 12 | class ui { 13 | 14 | public: 15 | /** Initialize user interface. 16 | * 17 | * @param list List to be displayed. 18 | * @param text_mode True for text-base UI, false for graphical UI. 19 | * @param back_color Background color used to render UI elements. 20 | * @param fore_color Foreground color used to render UI elements. 21 | */ 22 | ui(list *list, bool_t text_mode, uint8_t back_color, uint8_t fore_color); 23 | ~ui(); 24 | 25 | /** Run background tasks. */ 26 | void task(); 27 | 28 | /** Set and clear the rectangle that defines the UI area. 29 | * 30 | * @param rect Rectangle of UI area. 31 | */ 32 | void set_panel_rect(vga::text_rect_t *rect); 33 | /** Draw panel graphics. */ 34 | void draw_panel(); 35 | 36 | /** Filter rendered list displaying only matching elements. 37 | * 38 | * @param search String to search in list entries. 39 | */ 40 | void filter_list(const char *search); 41 | /** Scroll displayed list or info file. 42 | * 43 | * @param offset Number of rows to scroll up (negative) or down (positive) 44 | * from current position. 45 | */ 46 | void scroll(int offset); 47 | /** Jump to the beginning of displayed list or info file. */ 48 | void jump_to_begin(); 49 | /** Jump to the end of displayed list or info file. */ 50 | void jump_to_end(); 51 | 52 | /** Get selected list entry. 53 | * 54 | * @return Pointer to selected entry, NULL if no entry is selected. 55 | */ 56 | const list_entry *get_selected_entry(); 57 | /** Set seleted list entry. 58 | * 59 | * @param entry Entry to select, if not in list first entry 60 | * will be selected. 61 | */ 62 | void set_selected_entry(list_entry *entry); 63 | /** Loop between titile and in-program thumbnails. */ 64 | void loop_thumbnail(); 65 | /** Get thumbnail loop index. 66 | * 67 | * @return Index of current thumbnail loop directory. 68 | */ 69 | int get_thumbnail_loop() { return m_thumbnail_loop; } 70 | /** Set thumbnail loop index. 71 | */ 72 | void set_thumbnail_loop(int index); 73 | 74 | /** Test if info file is displayed. 75 | * 76 | * @return TRUE if info file is displayed, FALSE otherwise. 77 | */ 78 | bool_t is_info_displayed() { return m_info_displayed; } 79 | /** Display help for this program. 80 | * 81 | * @return TRUE on success, FALSE otherwise. 82 | */ 83 | bool_t display_help(); 84 | /** Display info file for selected entry (if exists). 85 | * 86 | * @return TRUE on success, FALSE otherwise. 87 | */ 88 | bool_t display_selected_entry_info(); 89 | /** Hide info file panel and restore list view. */ 90 | void hide_info(); 91 | 92 | /** Display message for entry that cannot be launch. 93 | * 94 | * @param entry Entry to launch. 95 | */ 96 | void display_cannot_launch(const list_entry *entry); 97 | /** Display message for entry that cannot be setup. 98 | * 99 | * @param entry Entry to setup. 100 | */ 101 | void display_cannot_setup(const list_entry *entry); 102 | /** Display message to inform user that entry has no setup program. */ 103 | void display_no_setup(); 104 | 105 | int show_cycles_changed_dialog(); 106 | private: 107 | /** Scroll entry list. 108 | * 109 | * @param offset Number of entries to scroll up (negative) or down 110 | * (positive) from current position. 111 | */ 112 | void scroll_list(int offset); 113 | 114 | /** Draw search box with current search string. */ 115 | void draw_search_box(); 116 | /** Draw a shortcut on the bottom-right corner of panel rectangle. 117 | * 118 | * @param key String representation of key. 119 | * @param text Descriptive text. 120 | */ 121 | void draw_shortcut(const char *key, const char *text); 122 | /** Draw list at current position .*/ 123 | void draw_list(); 124 | /** Draw list entry. 125 | * 126 | * @param line Line position inside list rectangle. 127 | */ 128 | void draw_list_entry(int line); 129 | /** Draw list scroll bar. */ 130 | void draw_list_scroll_bar(); 131 | /** XOR selected list entry to reverse colors. */ 132 | void xor_selected_entry(); 133 | /** Calculate list offset from selected entry index. 134 | * 135 | * @param selected_entry Selected entry index. 136 | * @return Index of first list entry visible on top of list rectangle. 137 | */ 138 | int calc_list_offset(int selected_entry); 139 | 140 | /** Display specified info file. 141 | * 142 | * @param filename Fully-qualified filename with extension. 143 | * @return TRUE on success, FALSE otherwise. 144 | */ 145 | bool_t display_info(const char *filename); 146 | /** Draw info panel content. */ 147 | void draw_info(); 148 | /** Draw info content line. 149 | * 150 | * @param row Line position inside info rectangle. 151 | */ 152 | void draw_info_line(int row); 153 | /** Test for a formatting marker boundary character. 154 | * 155 | * @param chr Character to test. 156 | * @return TRUE if charater is a marker boundary, FALSE otherwise. 157 | */ 158 | bool_t is_marker_boundary(char chr); 159 | /** Release info file resources. */ 160 | void release_info_resources(); 161 | /** Scroll info file. 162 | * 163 | * @param offset Number of lines to scroll up (negative) or down 164 | * (positive) from current position. 165 | */ 166 | void scroll_info(int offset); 167 | 168 | /** Draw thumbnail for specified entry. 169 | * 170 | * @param entry Entry for which draw thumbnail. 171 | */ 172 | void draw_thumbnail(const list_entry *entry); 173 | 174 | /** Display message in thumbnail area clearing it. 175 | * 176 | * @note Message length cannot exceed thumbnail area width. 177 | * @param message String messsage to display. 178 | */ 179 | void draw_thumbnail_message(const char *message); 180 | /** Clear thumbnail area with UI background color. */ 181 | void clear_thumbnail_area(); 182 | 183 | /** XOR charaters in video memory (graphics or text mode). 184 | * 185 | * @param row Zero-based row character coordinate. 186 | * @param col Zero-based column character coordinate. 187 | * @param count Number of characters to XOR. 188 | */ 189 | void xor_chars(uint8_t row, uint8_t col, uint16_t count); 190 | 191 | /** When true Text-based UI will be used, graphical one otherwise. */ 192 | bool_t m_text_mode; 193 | 194 | /** UI panel recteangle. */ 195 | vga::text_rect_t m_panel_rect; 196 | /** UI panel characters attributes. */ 197 | uint8_t m_panel_attrs; 198 | 199 | /** UI blank characters attributes (used when scroll list/info views). */ 200 | uint8_t m_blank_attrs; 201 | 202 | /** Array of list entries. */ 203 | list *m_list; 204 | /** Array of pointers to list entries to filter the ones that matches. */ 205 | list_entry **m_filtered; 206 | /** Number of matching list entries. */ 207 | int m_filtered_count; 208 | /** Search string to search in list entries. */ 209 | char m_filtered_string[UI_FILTER_STR_SIZE]; 210 | /** List offset as index of first visible entry. */ 211 | int m_offset; 212 | /** Index of selected list entry. */ 213 | int m_selected_entry; 214 | /** List displaying rectangle. */ 215 | vga::text_rect_t m_list_rect; 216 | 217 | /** Flag to track if info file is displayed. */ 218 | bool_t m_info_displayed; 219 | /** Info offset as index of first visible line. */ 220 | int m_info_offset; 221 | /** Total number of lines of current displayed info file. */ 222 | int m_info_lines; 223 | /** Array of offsets of info file lines from the beginning of file. */ 224 | long *m_info_lines_offset; 225 | /** Info file pointer. */ 226 | FILE *m_info_file; 227 | /** Info file displaying rectangle. */ 228 | vga::text_rect_t m_info_rect; 229 | 230 | /** Path of current thumbnail. */ 231 | char m_thumbnail_path[UI_THUMBNAIL_PATH_SIZE]; 232 | /** Thumbnail loop index. */ 233 | int m_thumbnail_loop; 234 | 235 | /** Encode bitmap for planar VGA modes. */ 236 | bitmap_encoder *m_encoder; 237 | }; 238 | 239 | #endif 240 | -------------------------------------------------------------------------------- /TESTS/KEYB/KEYTESTS.CPP: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include "keyb/keytests.hpp" 5 | 6 | #include "vga.hpp" 7 | #include "keyboard.hpp" 8 | 9 | #define FLAG_TO_CHAR(value) ((value) ? 'X' : '-') 10 | 11 | void print_keyb_read(keyboard::read_data_t *data); 12 | void print_keyb_make_break(keyboard::read_data_t *data); 13 | 14 | bool_t keyb_tests() { 15 | bool_t result = TRUE; 16 | 17 | keyboard::read_data_t data; 18 | 19 | clrscr(); 20 | 21 | printf("Press CTRL+ESC to exit.\n"); 22 | 23 | keyboard::init(); 24 | 25 | bool_t loop = TRUE; 26 | while (loop == TRUE) { 27 | if (keyboard::read(&data) == FALSE) 28 | continue; 29 | 30 | switch (data.type) { 31 | case keyboard::KEV_MAKE: 32 | print_keyb_make_break(&data); 33 | break; 34 | 35 | case keyboard::KEV_PRESS: 36 | print_keyb_read(&data); 37 | 38 | if (data.virt_key == VK_ESC && (data.shifts & VS_CTRL)) { 39 | loop = FALSE; 40 | result = TRUE; 41 | printf("\nCTRL+ESC pressed, exiting.\n"); 42 | } 43 | break; 44 | 45 | case keyboard::KEV_BREAK: 46 | print_keyb_make_break(&data); 47 | break; 48 | } 49 | } 50 | 51 | keyboard::release(); 52 | 53 | return result; 54 | } 55 | 56 | void print_keyb_make_break(keyboard::read_data_t *data) { 57 | printf("make:[%c] scan:%02x - vkey:%02x\n", 58 | FLAG_TO_CHAR(data->ascii), 59 | data->scan_code, data->virt_key); 60 | } 61 | 62 | void print_keyb_read(keyboard::read_data_t *data) { 63 | printf("ascii:'%c' %02x - scan:%02x - vkey:%02x\n", 64 | data->ascii, data->ascii, data->scan_code, data->virt_key); 65 | 66 | printf(" pressed: SHIFT(L,R):%c,%c CTRL(L,R):%c,%c ALT(L,R):%c,%c\n" 67 | " INS: %c CAPS:%c NUM:%c SCROLL:%c PAUSE:%c\n", 68 | FLAG_TO_CHAR(data->shifts & VS_LSHIFT), 69 | FLAG_TO_CHAR(data->shifts & VS_RSHIFT), 70 | FLAG_TO_CHAR(data->shifts & VS_LCTRL), 71 | FLAG_TO_CHAR(data->shifts & VS_RCTRL), 72 | FLAG_TO_CHAR(data->shifts & VS_LALT), 73 | FLAG_TO_CHAR(data->shifts & VS_RALT), 74 | FLAG_TO_CHAR(data->shifts & VS_INSERT), 75 | FLAG_TO_CHAR(data->shifts & VS_CAPSLOCK), 76 | FLAG_TO_CHAR(data->shifts & VS_NUMLOCK), 77 | FLAG_TO_CHAR(data->shifts & VS_SCRLLOCK), 78 | FLAG_TO_CHAR(data->shifts & VS_PAUSE)); 79 | 80 | printf(" locked: INS: %c CAPS:%c NUM:%c SCROLL:%c\n", 81 | FLAG_TO_CHAR(data->locked_shifts & VS_INSERT), 82 | FLAG_TO_CHAR(data->locked_shifts & VS_CAPSLOCK), 83 | FLAG_TO_CHAR(data->locked_shifts & VS_NUMLOCK), 84 | FLAG_TO_CHAR(data->locked_shifts & VS_SCRLLOCK)); 85 | } 86 | -------------------------------------------------------------------------------- /TESTS/KEYB/KEYTESTS.HPP: -------------------------------------------------------------------------------- 1 | #ifndef KEYTESTS_HPP 2 | #define KEYTESTS_HPP 3 | 4 | #include "types.hpp" 5 | 6 | bool_t keyb_tests(); 7 | 8 | #endif 9 | -------------------------------------------------------------------------------- /TESTS/MAIN.CPP: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include "keyb/keytests.hpp" 5 | #include "critical/critests.hpp" 6 | #include "vga/vgatests.hpp" 7 | #include "tui/tuitests.hpp" 8 | #include "markdown/md_tests.hpp" 9 | 10 | #include "string.hpp" 11 | #include "vga.hpp" 12 | 13 | int main(int argc, const char *argv[]) { 14 | int selection = 0; 15 | 16 | if (argc > 1 && string::is_digit(*argv[1])) { 17 | selection = *argv[1] - '0'; 18 | } else { 19 | printf("Select test to run:\n" 20 | "1 - Keyboard\n" 21 | "2 - Custom critical error handler\n" 22 | "3 - VGA\n" 23 | "4 - Text-mode user interface\n" 24 | "5 - Graphics-mode user interface\n" 25 | "6 - Text-mode markdown\n" 26 | "7 - Graphics-mode markdown\n"); 27 | 28 | selection = getch() - '0'; 29 | } 30 | 31 | printf("Test %d selected.\n\n", selection); 32 | 33 | vga::init(); 34 | 35 | bool_t result; 36 | switch (selection) { 37 | case 1: 38 | result = keyb_tests(); 39 | break; 40 | 41 | case 2: 42 | result = critical_tests(); 43 | break; 44 | 45 | case 3: 46 | result = vga_tests(); 47 | break; 48 | 49 | case 4: 50 | result = tui_tests(TRUE); 51 | break; 52 | 53 | case 5: 54 | result = tui_tests(FALSE); 55 | break; 56 | 57 | case 6: 58 | result = md_tests(TRUE); 59 | break; 60 | 61 | case 7: 62 | result = md_tests(FALSE); 63 | break; 64 | 65 | default: 66 | printf("\nInvalid choice: %d\nExiting.", selection); 67 | return 1; 68 | } 69 | 70 | if (result == TRUE) 71 | printf("\n Test PASSED.\n"); 72 | else 73 | printf("\n Test FAILED.\n"); 74 | 75 | return (result == FALSE); 76 | } 77 | 78 | -------------------------------------------------------------------------------- /TESTS/MARKDOWN/MD_TESTS.CPP: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include "markdown/md_tests.hpp" 5 | 6 | #include "markdown.hpp" 7 | #include "vga.hpp" 8 | 9 | bool_t receiver(markdown::token_t *token); 10 | 11 | static markdown::indents_t indents; 12 | 13 | bool_t md_tests(bool_t text_mode) { 14 | if (text_mode == FALSE) 15 | vga::set_mode(0x12); 16 | else 17 | clrscr(); 18 | 19 | FILE *output = fopen("preparse.md$", "wb"); 20 | if (output == NULL) { 21 | printf("Cannot create preparse.md$ file.\n"); 22 | return FALSE; 23 | } 24 | 25 | indents[markdown::B_PARAGRAPH] = 0; 26 | indents[markdown::B_HEADING] = 0; 27 | indents[markdown::B_OLIST] = 0; 28 | indents[markdown::B_ULIST] = 0; 29 | indents[markdown::B_LIST_ITEM] = 4; 30 | indents[markdown::B_CODE] = 0; 31 | 32 | markdown *parser = new markdown(40, receiver, indents); 33 | parser->set_source("../readme.md"); 34 | 35 | clrscr(); 36 | 37 | if (parser->parse() == FALSE) { 38 | fclose(output); 39 | delete parser; 40 | return FALSE; 41 | } 42 | 43 | int lines = parser->get_lines(); 44 | delete parser; 45 | 46 | fclose(output); 47 | 48 | return TRUE; 49 | } 50 | 51 | bool_t receiver(markdown::token_t *token) { 52 | textbackground(BLACK); 53 | textcolor(LIGHTGRAY); 54 | 55 | int x = wherex(), y = wherey(); 56 | gotoxy(41, y); 57 | putch('|'); 58 | gotoxy(x, y); 59 | 60 | textbackground(token->block); 61 | /* 62 | B_PARAGRAPH, 63 | B_HEADING, 64 | B_OLIST, 65 | B_ULIST, 66 | B_LIST_ITEM, 67 | B_CODE 68 | */ 69 | textcolor(LIGHTGRAY); 70 | 71 | if (token->style & markdown::S_EMPHASIS) 72 | textcolor(MAGENTA); 73 | 74 | if (token->style & markdown::S_STRONG) 75 | textcolor(WHITE); 76 | 77 | if (token->style & markdown::S_CODE) { 78 | textcolor(BLACK); 79 | textbackground(LIGHTGRAY); 80 | } 81 | 82 | switch (token->type) { 83 | case markdown::T_BLOCK_START: 84 | textbackground(BLACK); 85 | if (indents[token->block] > 0) 86 | cprintf("\r\n%*c", indents[token->block], ' '); 87 | break; 88 | 89 | case markdown::T_BLOCK_END: 90 | textbackground(BLACK); 91 | cprintf("\r\n\r\n"); 92 | break; 93 | 94 | case markdown::T_SPAN: 95 | if (token->block == markdown::B_CODE) { 96 | int row = wherey(); 97 | for (int i = 0; i < 40; ++i) 98 | putch(' '); 99 | gotoxy(1, row); 100 | } 101 | 102 | cputs(token->text); 103 | break; 104 | 105 | case markdown::T_NEWLINE: 106 | textbackground(BLACK); 107 | cprintf("\r\n"); 108 | if (indents[token->block] > 0) 109 | cprintf("%*c", indents[token->block], ' '); 110 | break; 111 | } 112 | 113 | return TRUE; 114 | } -------------------------------------------------------------------------------- /TESTS/MARKDOWN/MD_TESTS.HPP: -------------------------------------------------------------------------------- 1 | #ifndef MD_TESTS_HPP 2 | #define MD_TESTS_HPP 3 | 4 | #include "types.hpp" 5 | 6 | bool_t md_tests(bool_t text_mode); 7 | 8 | #endif 9 | -------------------------------------------------------------------------------- /TESTS/TESTS.DSK: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/marco-sacchi/RLoader/b80fbbe1f108600be1fc0e191aca42f49443c153/TESTS/TESTS.DSK -------------------------------------------------------------------------------- /TESTS/TESTS.PRJ: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/marco-sacchi/RLoader/b80fbbe1f108600be1fc0e191aca42f49443c153/TESTS/TESTS.PRJ -------------------------------------------------------------------------------- /TESTS/TUI/TESTVIEW.CPP: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #include "tui/testview.hpp" 5 | #include "tui/msgbox.hpp" 6 | #include "tui/views/opensave.hpp" 7 | 8 | static const char *list_items[] = { 9 | "Item 0", 10 | "Item 1", 11 | "Long text for item 2", 12 | "Shorter item 3", 13 | "Very long text for item 4 that overflow", 14 | "Other item 5", 15 | "Another item 6", 16 | "Item 7", 17 | "Item 8" 18 | }; 19 | 20 | testview::testview(int top, int left, int width, int height) 21 | : uiview() { 22 | m_top = top; 23 | m_left = left; 24 | m_width = width; 25 | m_height = height; 26 | } 27 | 28 | bool_t testview::init_controls() { 29 | m_window = new uiwindow(ID_WINDOW, m_top, m_left, m_width, m_height, 30 | uiwindow::FRAMED | uiwindow::CAPTIONED, 31 | "Test caption"); 32 | if (m_window == NULL) 33 | return FALSE; 34 | 35 | m_button1 = new uibutton(ID_BUTTON_OPEN, 3, 4, 11, "Open view"); 36 | if (m_button1 == NULL) 37 | return FALSE; 38 | 39 | m_button1->set_accelerator('O'); 40 | m_window->add_control(m_button1); 41 | 42 | m_button2 = new uibutton(ID_BUTTON_CLOSE, 3, 17, 12, "Close view"); 43 | if (m_button2 == NULL) 44 | return FALSE; 45 | 46 | m_button2->set_fore_color(0x0c); 47 | m_button2->set_back_color(4); 48 | m_button2->set_accelerator('V'); 49 | m_window->add_control(m_button2); 50 | 51 | m_button3 = new uibutton(ID_BUTTON_MSGBOX, 3, 31, 9, "Msg box"); 52 | if (m_button3 == NULL) 53 | return FALSE; 54 | 55 | m_button3->set_fore_color(0x0a); 56 | m_button3->set_back_color(2); 57 | m_button3->set_accelerator('M'); 58 | m_window->add_control(m_button3); 59 | 60 | m_button4 = new uibutton(ID_BUTTON_DLGOPEN, 5, 4, 13, "Open dialog"); 61 | if (m_button4 == NULL) 62 | return FALSE; 63 | 64 | m_button4->set_accelerator('P'); 65 | m_window->add_control(m_button4); 66 | 67 | m_button5 = new uibutton(ID_BUTTON_DLGSAVE, 5, 19, 13, "Save dialog"); 68 | 69 | if (m_button5 == NULL) 70 | return FALSE; 71 | 72 | m_button5->set_accelerator('S'); 73 | m_window->add_control(m_button5); 74 | 75 | m_label = new uilabel(ID_LABEL, 7, 4, "Label text:"); 76 | if (m_label == NULL) 77 | return FALSE; 78 | 79 | m_label->set_accelerator('E'); 80 | m_window->add_control(m_label); 81 | 82 | m_edit = new uieditbox(ID_EDIT, 7, 16, 22, 64); 83 | if (m_edit == NULL) 84 | return FALSE; 85 | 86 | m_edit->set_text("Long string to test editbox scrolling."); 87 | m_edit->set_text("D:\\CPROGS\\RLOADER\\"); 88 | m_edit->set_accelerator('E'); 89 | m_window->add_control(m_edit); 90 | 91 | m_list = new uilistbox(ID_LIST, 9, 16, 22, 6); 92 | if (m_list == NULL) 93 | return FALSE; 94 | 95 | m_list->set_accelerator('L'); 96 | 97 | bool_t populate_ok = TRUE; 98 | for (int i = 0; i < 9; ++i) 99 | populate_ok = populate_ok && m_list->add_item(list_items[i]); 100 | 101 | m_window->add_control(m_list); 102 | if (populate_ok == FALSE) 103 | return FALSE; 104 | 105 | m_text = new uitextbox(ID_TEXT, 3, 42, 32, 8); 106 | if (m_text == NULL) 107 | return FALSE; 108 | 109 | m_text->set_source("../chglog.md", uitextbox::TEXT); 110 | m_window->add_control(m_text); 111 | 112 | m_markdown = new uitextbox(ID_MARKDOWN, 12, 42, 32, 8); 113 | if (m_markdown == NULL) 114 | return FALSE; 115 | 116 | m_markdown->set_source("../readme.md", uitextbox::MARKDOWN); 117 | m_window->add_control(m_markdown); 118 | 119 | return TRUE; 120 | } 121 | 122 | const char *testview::get_field_text() { 123 | return m_edit->get_text(); 124 | } 125 | 126 | bool_t testview::get_list_item(int *index, const char **text) { 127 | *index = m_list->get_selected_item(); 128 | *text = m_list->get_selected_item_text(); 129 | return (*index < 0) ? FALSE : TRUE; 130 | } 131 | 132 | uimsgbox *testview::create_messagebox() { 133 | uimsgbox *msgbox = new uimsgbox("Message caption", 134 | "Long message text to test " 135 | "word-wrapping, correct line counting, " 136 | "window sizing and buttons rendering."); 137 | if (msgbox == NULL) 138 | return NULL; 139 | 140 | if (!msgbox->add_button(uimsgbox::BTN_OK)) { 141 | delete msgbox; 142 | return NULL; 143 | } 144 | 145 | if (!msgbox->add_button(uimsgbox::BTN_CANCEL)) { 146 | delete msgbox; 147 | return NULL; 148 | } 149 | 150 | if (!msgbox->add_button(uimsgbox::BTN_CUSTOM_FIRST, "Custom", 'S')) { 151 | delete msgbox; 152 | return NULL; 153 | } 154 | 155 | return msgbox; 156 | } 157 | 158 | uiopensave *testview::create_opensave(uiopensave::mode mode) { 159 | return new uiopensave(mode, m_edit->get_text(), "*.*"); 160 | } 161 | 162 | bool_t testview::on_action(tui::action_t *action) { 163 | switch (action->type) { 164 | case tui::TUIA_CTRL_DESTROY: 165 | if (action->id == ID_WINDOW) 166 | return TRUE; 167 | break; 168 | 169 | case tui::TUIA_VIEW_CLOSED: 170 | if (action->view == m_dialog) { 171 | const char *field_text = m_dialog->get_field_text(); 172 | int item_index; 173 | const char *item_text; 174 | if (m_dialog->get_list_item(&item_index, &item_text) == TRUE) 175 | m_list->set_selected_item(item_index); 176 | 177 | m_edit->set_text(field_text); 178 | m_dialog = NULL; 179 | } else if (action->view == m_opensave) { 180 | char buffer[MAXPATH]; 181 | if (m_opensave->get_selected_fullpath(buffer) == TRUE) 182 | m_edit->set_text(buffer); 183 | } 184 | 185 | break; 186 | 187 | case tui::TUIA_CTRL_ACTIVATED: 188 | switch (action->id) { 189 | case ID_BUTTON_OPEN: 190 | m_dialog = new testview(5, 16, 50, 16); 191 | if (m_dialog != NULL) 192 | this->queue_open_view(m_dialog); 193 | break; 194 | 195 | case ID_BUTTON_CLOSE: 196 | this->queue_close_view(); 197 | break; 198 | 199 | case ID_BUTTON_MSGBOX: 200 | uimsgbox *msgbox = this->create_messagebox(); 201 | if (msgbox != NULL) 202 | this->queue_open_view(msgbox); 203 | break; 204 | 205 | case ID_BUTTON_DLGOPEN: 206 | m_opensave = this->create_opensave( 207 | uiopensave::MODE_OPEN 208 | ); 209 | if (m_opensave != NULL) 210 | this->queue_open_view(m_opensave); 211 | break; 212 | 213 | case ID_BUTTON_DLGSAVE: 214 | m_opensave = this->create_opensave( 215 | uiopensave::MODE_SAVE 216 | ); 217 | if (m_opensave != NULL) 218 | this->queue_open_view(m_opensave); 219 | break; 220 | } 221 | break; 222 | 223 | case tui::TUIA_CTRL_VALUE_CHANGED: 224 | if (action->id == ID_EDIT) 225 | m_list->set_highlight(m_edit->get_text()); 226 | break; 227 | } 228 | 229 | return TRUE; 230 | } 231 | -------------------------------------------------------------------------------- /TESTS/TUI/TESTVIEW.HPP: -------------------------------------------------------------------------------- 1 | #ifndef TUI_TESTVIEW_HPP 2 | #define TUI_TESTVIEW_HPP 3 | 4 | #include "tui/view.hpp" 5 | #include "tui/window.hpp" 6 | #include "tui/msgbox.hpp" 7 | #include "tui/button.hpp" 8 | #include "tui/label.hpp" 9 | #include "tui/editbox.hpp" 10 | #include "tui/listbox.hpp" 11 | #include "tui/textbox.hpp" 12 | 13 | #include "tui/views/opensave.hpp" 14 | 15 | class testview : public uiview { 16 | public: 17 | testview(int top, int left, int width, int height); 18 | 19 | const char *get_field_text(); 20 | bool_t get_list_item(int *index, const char **text); 21 | 22 | virtual bool_t init_controls(); 23 | virtual bool_t on_action(tui::action_t *action); 24 | 25 | private: 26 | enum CTRL_ID { 27 | ID_WINDOW = 1, 28 | ID_BUTTON_OPEN, 29 | ID_BUTTON_CLOSE, 30 | ID_BUTTON_MSGBOX, 31 | ID_BUTTON_DLGOPEN, 32 | ID_BUTTON_DLGSAVE, 33 | ID_LABEL, 34 | ID_EDIT, 35 | ID_LIST, 36 | ID_TEXT, 37 | ID_MARKDOWN 38 | }; 39 | 40 | uimsgbox *create_messagebox(); 41 | uiopensave *create_opensave(uiopensave::mode mode); 42 | 43 | int m_top, m_left; 44 | int m_width, m_height; 45 | 46 | uibutton *m_button1, *m_button2, *m_button3, *m_button4, *m_button5; 47 | uilabel *m_label; 48 | uieditbox *m_edit; 49 | uilistbox *m_list; 50 | uitextbox *m_text; 51 | uitextbox *m_markdown; 52 | uiopensave *m_opensave; 53 | testview *m_dialog; 54 | }; 55 | 56 | #endif 57 | -------------------------------------------------------------------------------- /TESTS/TUI/TUITESTS.CPP: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | #include "keyboard.hpp" 6 | 7 | #include "tui/tui.hpp" 8 | #include "tui/tuitests.hpp" 9 | #include "tui/testview.hpp" 10 | 11 | bool_t tui_tests(bool_t text_mode) { 12 | if (keyboard::init() == FALSE) { 13 | printf("\nCannot initialize keyboard.\n"); 14 | return FALSE; 15 | } 16 | 17 | if (text_mode == FALSE) 18 | vga::set_mode(0x12); 19 | else 20 | clrscr(); 21 | 22 | tui *ui = new tui(text_mode); 23 | if (ui == NULL) { 24 | printf("Cannot allocate user interface."); 25 | keyboard::release(); 26 | return FALSE; 27 | } 28 | 29 | uiview *test = new testview(2, 2, 76, 21); 30 | if (test == NULL) { 31 | printf("Cannot allocate test view."); 32 | delete ui; 33 | keyboard::release(); 34 | return FALSE; 35 | } 36 | 37 | ui->open_view(test); 38 | 39 | while (ui->task() == TRUE) { 40 | /* 41 | tui::action_t *action; 42 | if (action->type == 0) 43 | continue; 44 | 45 | int x = wherex(), y = wherey(); 46 | gotoxy(1, 1); 47 | 48 | printf("action:%d, id:%d, item:%d control:%p", action->type, 49 | action->id, action->item_id, action->control); 50 | 51 | gotoxy(x, y); 52 | */ 53 | } 54 | 55 | delete ui; 56 | 57 | keyboard::release(); 58 | 59 | return TRUE; 60 | } 61 | -------------------------------------------------------------------------------- /TESTS/TUI/TUITESTS.HPP: -------------------------------------------------------------------------------- 1 | #ifndef TUITESTS_HPP 2 | #define TUITESTS_HPP 3 | 4 | #include "types.hpp" 5 | 6 | bool_t tui_tests(bool_t text_mode); 7 | 8 | #endif -------------------------------------------------------------------------------- /TESTS/VGA/VGATESTS.CPP: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | #include "vga/vgatests.hpp" 7 | 8 | #include "vga.hpp" 9 | #include "graphics.hpp" 10 | #include "bitmap.hpp" 11 | #include "encoder.hpp" 12 | 13 | /** Do graphics or text-mode tests. 14 | * 15 | * @param graphics TRUE for graphics tests, FALSE for text-mode tests. 16 | * @return TRUE on success, FALSE otherwise. 17 | */ 18 | bool_t tests(bool_t graphics); 19 | 20 | bool_t vga_tests() { 21 | if (tests(FALSE) == FALSE) 22 | return FALSE; 23 | 24 | bool_t result = tests(TRUE); 25 | vga::set_mode(0x03); 26 | 27 | return result; 28 | } 29 | 30 | bool_t display_bitmap() { 31 | bitmap *bmp = new bitmap(); 32 | 33 | if (bmp->load("../lists/games/titles/doom.bmp") == FALSE) { 34 | delete bmp; 35 | printf("Cannot allocate bitmap.\n"); 36 | return FALSE; 37 | } 38 | 39 | bitmap_encoder *encoder = new bitmap_encoder(bmp, 2); 40 | if (encoder == NULL) { 41 | delete bmp; 42 | printf("Cannot allocate encoder.\n"); 43 | return FALSE; 44 | } 45 | 46 | printf("Encoding"); 47 | while (encoder->task() == FALSE) 48 | printf("."); 49 | 50 | graphics::set_ega_palette(bmp, 2); 51 | graphics::draw(bmp, 320, (480 >> 1) - (bmp->get_height() >> 1)); 52 | delete bmp; 53 | bmp = NULL; 54 | 55 | delete encoder; 56 | encoder = NULL; 57 | 58 | return TRUE; 59 | } 60 | 61 | bool_t draw_font_bitmap(vga::font_info_ptr pointer, vga::font_info_t *info) { 62 | uint8_t far *bitmap = (uint8_t far *)info->pointer; 63 | int height = 0; 64 | switch (pointer) { 65 | case vga::INT_1F_VECTOR: 66 | case vga::INT_43_VECTOR: 67 | return TRUE; 68 | 69 | case vga::ROM_8x14_FONT: 70 | case vga::ROM_9x14_ALT_FONT: 71 | height = 14; 72 | break; 73 | 74 | case vga::ROM_8x8_2DOT_FONT: 75 | case vga::ROM_8x8_2DOT_FONT_TOP: 76 | height = 8; 77 | break; 78 | 79 | case vga::ROM_8x16_FONT: 80 | case vga::ROM_9x16_ALT_FONT: 81 | height = 16; 82 | break; 83 | 84 | default: 85 | return FALSE; 86 | } 87 | 88 | for (int ch = 0; ch < 10; ++ch) { 89 | for (int j = 0; j < height; ++j) { 90 | uint8_t line = *bitmap++; 91 | 92 | for (int i = 0; i < 8; ++i) { 93 | vga::set_cursor_pos(j + 2, 7 - i + ch * 8); 94 | vga::write_char(((line >> i) & 1) ? 0xdb : ' ', 0x07, 1); 95 | } 96 | } 97 | } 98 | 99 | return TRUE; 100 | } 101 | 102 | void display_font_info(vga::font_info_ptr pointer) { 103 | vga::font_info_t info; 104 | vga::get_font_info(pointer, &info); 105 | 106 | clrscr(); 107 | printf("Pointer %d: %p, scanlines: %d, rows: %d\n", pointer, 108 | info.pointer, info.scanlines_per_char, info.chars_rows); 109 | 110 | draw_font_bitmap(pointer, &info); 111 | 112 | getch(); 113 | } 114 | 115 | void custom_alpha_chars() { 116 | const int num_chars = 8, first_char = 0xc8; 117 | 118 | uint8_t previous[num_chars * 16]; 119 | uint8_t new_chars[num_chars * 16] = { 120 | 0xff, 0xff, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 121 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 122 | 123 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 124 | 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xff, 0xff, 125 | 126 | 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 127 | 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 128 | 129 | 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 130 | 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 131 | 132 | 0xff, 0xff, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 133 | 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 134 | 135 | 0xff, 0xff, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 136 | 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 137 | 138 | 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 139 | 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xff, 0xff, 140 | 141 | 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 142 | 0x01, 0x01, 0x01, 0x01, 0x01, 0x01, 0xff, 0xff 143 | }; 144 | 145 | vga::font_info_t info; 146 | vga::get_font_info(vga::ROM_8x16_FONT, &info); 147 | memcpy(previous, (uint8_t far *)info.pointer + first_char * 16, 148 | num_chars * 16); 149 | 150 | uint8_t chars[num_chars + 1]; 151 | for (int i = 0; i < num_chars; ++i) 152 | chars[i] = i + first_char; 153 | 154 | chars[i] = NULL; 155 | 156 | clrscr(); 157 | printf("Next characters will be modified: %s", chars); 158 | getch(); 159 | 160 | vga::load_alpha_font(new_chars, 16, num_chars, first_char, 0); 161 | 162 | getch(); 163 | 164 | vga::load_alpha_font(previous, 16, num_chars, first_char, 0); 165 | 166 | getch(); 167 | } 168 | 169 | bool_t tests(bool_t graphics) { 170 | if (graphics == TRUE) 171 | vga::set_mode(0x12); 172 | 173 | display_font_info(vga::INT_1F_VECTOR); 174 | display_font_info(vga::INT_43_VECTOR); 175 | display_font_info(vga::ROM_8x14_FONT); 176 | display_font_info(vga::ROM_8x8_2DOT_FONT); 177 | display_font_info(vga::ROM_8x8_2DOT_FONT_TOP); 178 | display_font_info(vga::ROM_9x14_ALT_FONT); 179 | display_font_info(vga::ROM_8x16_FONT); 180 | display_font_info(vga::ROM_9x16_ALT_FONT); 181 | 182 | if (graphics == FALSE) 183 | custom_alpha_chars(); 184 | 185 | vga::set_cursor_type(0, 15); 186 | vga::set_cursor_pos(3, 16); 187 | 188 | uint8_t attrs = vga::encode_attrs(0, 1, 0); 189 | 190 | vga::write_char('c', attrs, 40); 191 | 192 | getch(); 193 | 194 | vga::text_rect_t rect; 195 | rect.top = 0; 196 | rect.left = 0; 197 | rect.bottom = 24; 198 | rect.right = 39; 199 | vga::scroll_page_up(1, attrs, &rect); 200 | 201 | vga::set_cursor_pos(24, 16); 202 | vga::write_char('a', attrs, 2); 203 | vga::write_string("Test string", attrs, vga::USE_ATTRS_UPDATE_CURSOR); 204 | 205 | getch(); 206 | 207 | if (graphics == TRUE) { 208 | if (display_bitmap() == FALSE) 209 | return FALSE; 210 | 211 | getch(); 212 | } 213 | 214 | rect.top = 0; 215 | rect.left = 40; 216 | rect.bottom = 39; 217 | rect.right = 79; 218 | vga::set_map_mask_reg(0x0f); 219 | 220 | attrs = vga::encode_attrs(0, 0, 0); 221 | 222 | int i = 15; 223 | while (i-- > 0) { 224 | vga::scroll_page_up(1, attrs, &rect); 225 | delay(10); 226 | } 227 | 228 | getch(); 229 | 230 | i = 15; 231 | while (i-- > 0) { 232 | vga::scroll_page_up(1, attrs, &rect); 233 | delay(10); 234 | } 235 | 236 | getch(); 237 | 238 | return TRUE; 239 | } 240 | 241 | -------------------------------------------------------------------------------- /TESTS/VGA/VGATESTS.HPP: -------------------------------------------------------------------------------- 1 | #ifndef VGATESTS_HPP 2 | #define VGATESTS_HPP 3 | 4 | #include "types.hpp" 5 | 6 | bool_t vga_tests(); 7 | 8 | #endif 9 | -------------------------------------------------------------------------------- /UI.DSK: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/marco-sacchi/RLoader/b80fbbe1f108600be1fc0e191aca42f49443c153/UI.DSK -------------------------------------------------------------------------------- /UI.PRJ: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/marco-sacchi/RLoader/b80fbbe1f108600be1fc0e191aca42f49443c153/UI.PRJ --------------------------------------------------------------------------------