Compare commits

...

3 Commits
v1.7 ... main

Author SHA1 Message Date
Michel Pollet
11cdb8b209 Version 1.9: See Changelog for details
List is too long...

Signed-off-by: Michel Pollet <buserror@gmail.com>
2024-05-13 16:40:51 +01:00
Michel Pollet
d60bc9bee9
ui: Handshake! Dislexia Induced Typo, fixed
Fix the Hardware Handshake label :-)

Signed-off-by: Michel Pollet <buserror@gmail.com>
2024-03-13 09:50:05 +00:00
Michel Pollet
e231ecb762
Version 1.8, see changelog for details
Tons of changes, see changelog

Signed-off-by: Michel Pollet <buserror@gmail.com>
2024-03-13 08:45:11 +00:00
132 changed files with 14894 additions and 3310 deletions

2
.gitignore vendored
View File

@ -6,3 +6,5 @@ cachegrind.out.*
callgrind.out.*
.cache
*.miov
hezner
*_scrot.png

127
CHANGELOG.md Normal file
View File

@ -0,0 +1,127 @@
<p align="center">
<img src="contrib/mii-icon-64.png" alt="MII Logo">
</p>
# MII Version Changelog
## 1.9
#### Video
* Video driver now only draw when the apple II video *really* changes. It keeps track of touched lines across the screen, and only updates when needed. This saves a considerable amount of CPU time.
* Redid the monochrome video rendering, low-res, double low-res and dhires
now have luminance based on the color, and not just a fixed value. This
makes the monochrome rendering a lot more accurate (and attractive).
* Added 2 other RGB color palettes, an alternate NTSC and the Mega2 (apple IIgs) one.
| NTSC Palette | Mega2 Palette |
|--------------------------------------|--------------------------------------|
| ![NTSC](docs/screen/v19ntsc.png) | ![Mega2](docs/screen/v19mega2.png) |
|--------------------------------------|--------------------------------------|
* Added AVX/SSE/generic vectorial versions of some code paths, for speed.
* Added some more 'artifacts' color frindges to the HIRES rendering as well.
<p align="center">
<img src="docs/screen/v19artifacts.png" alt="Artifacts">
</p>
#### Other bits
* Fixed a bug in the floppy stepper motor code that was preventing some floppies (demos, but also some prodos disks) from booting.
* Reworked the SmartPort code a bit, should be more compatible with some prodos programs. Also fixed a crash in block reading code.
* Joystick can now be plugged after starting the emulator, it will be detected.
* Changed the way the 65c02 core emulator is interfaced to the rest of the emulation. It was made to be 'pretty' but was also quite suboptimal, so I made it a bit less pretty, but it is now easily twice as fast as before.
* While before the emulator was using perhaps 25% of a core on my current machine, now it uses about 9% CPU for the same task.
* The 'floppy' and 'hard drive' file selector dialog now has a small popup menu with the last few directory used, so you can quickly switch between them.
* Added support for 'pasting' text in the emulator. Paste your pet BASIC programs, it works now. (Control-SHIFT-V -- not in a menu yet).
* There is a non-functional Mockingboard driver in the code, it's not hooked up to the UI, it still needs work (mostly make the audio output work).
* UI library got a massive overhaul, since it was released separately it had to get a serious cleanup. Some files are duplicated unfortunately, but that is needed to be able to use it as a standalone library.
## 1.8
* Changed the floppy disk view. *It now rotates*, and the heat map is now a
'trail' of the head, showing where it's been. It looks awesome!
<div align="center">
<img src="docs/screen/v18new_display.gif" alt="New Floppy display">
</div>
<center><i>Poor quality gif, It is a LOT smoother at 60fps in the program!</i>
</center>
* Added support for a *Ramworks III card*, with 1MB of RAM. It could have more,
but I thought and extra whole friggin MEGABYTE was enough for anyone.
* Added support for flashing text in text mode. I know, it was a bit of a
glaring omission, but it's there now.
* Internal changes to the UI, I've split the whole codebase into a few more files,
split the 'XORG/GLX' code from the 'Pure GL' code from the 'MII UI' code, so it should be a lot easier to port to other platforms.
* Redid the *DHRES rendering*, it's now a lot more accurate, has the correct
artifacts. It's not as optimized as the previous version, but it looks better.
* Now remap the *joystick* coodinates to a 'square' -- my current 8bitdo joystick
has a circular deadzone, and it was a bit annoying to use. I might make that
a setting, but for now, it's hardwired.
* *Emulator now passes a2audit*. There is only one kludge to do it, regarding the
'phantom read' of the indirect addressing.
* *Working Super Serial Card Driver*, it can bridge to a Linux /dev device for the moment, or to
a 'fake' loopback device. IN#x and PR#x works, and I can bootstrap using
ADTPro. It's all in there so I can run Mastodon!
<div align="center">
<img src="docs/screen/v18ssc_dialog.png" alt="SSC Config">
</div>
<center><i>Super Serial Card config dialog</i></center>
### libmui
* Standard file picker now shows *floppy icons*.
* Added a *Color Apple Menu*, in pure Macintosh II style.
<div align="center">
<img src="docs/screen/v18colorapple.png" alt="Color Apple">
</div>
* Fixed a few minor memory leaks.
* Tons more stuff in libmui, inc new font styles (bold, underline, condensed). There is also a text edit control now (still prototype).
### Internals
* Made an architecture document, see [Compiling](docs/Compiling.md) for a top-down view.
* Ported the support for VCD (Value Change Dump) from simavr, so I can now
record and playback the whole simulation of the floppy driver. No real use for the user.
* Split the video rendering into bits with a 'main' line rendering function pointer that is set only when video mode changes. This is a bit faster, and that gets rid of the Giant Function.
## 1.7
* New animated about box, because, everyone loves a good about box.
* Added support for Write Protect of floppy disks; disk can be write protected manually, or if the file lacks write permissions, OR if the file format (NIB, DSK) doesn't support writes.
* New fancypants 'bit view' of the floppy are they are read/written, with a
heat map to show where the head was last. Drive 1 appears left of the screen,
drive 2 on the right. It fades out after the drive motor stops.
<div align="center">
<img src="docs/screen/v17heatmap.png" alt="Heat map disk view">
</div>
<center><i>DOS 3.3 Disk 'bitstream view' on the left, the green trace shows what's just be read.</i></center>
## 1.6
* Another big update, yanked the old DiskII driver, and replaced it with a
homebrew one, low level, with native support for WOZ (1 & 2) files (*read AND write!*) as well as NIB and DSK (read only).
* This is big news, and MII can now boot all kind of copy protected disks, in
fact I tried a few hundreds, and they all worked so far!
* There is currently no way to create a new disk, but you can use a tool like
[CiderPress](https://a2ciderpress.com/) to create them, and then use them in MII. Or just copy your DOS 3.3.woz file and reformat it!
* There were a few other minor changes, mostly added some timing measurement
tooling, and a couple of (necessary for disk to work) tweaks to the emulator
itself as it was not counting cycles correctly for a couple of instructions.
* The UI now has support for pure power-of-two textures, for really old OpenGL
implementations, it is turned off by default, but could work with some
old hardware. Also updated *libmui* to make it less linux-centric.
* Fixed some more color issues, mostly DHIRES.
* Added 'typeahead' for when you select files in the dialog, like on old Macs
## 1.5
* BIG update, loads of changes, fixes, improvements.
* New super UI, using home-made libmui, channeling both GS/OS and MacOS 7.x!
* New emulation fixes, way more accurate. Video redone, audio redone.
* New front-end program using XLib and OpenGL 'low level'.
* New Icon.
## 1.0
* Fixed a few graphics rendering bugs/color swapped
* Fixed a few Makefile issues involving pathnamed with 'spaces' in them.
* More tweaks to the emulation, added a few cycles here and there.
## 0.9
* Added a 'debugger' shell, accessible via telnet.
* Added a mini-assembler, used to compile the drivers and the CPU unit tests.
* Added a 'Titan Accelerator IIe' simulation, to turn on/off fast mode.
## 0.5
* Initial release

View File

@ -1,5 +1,11 @@
# Makefile
#
# Copyright (C) 2024 Michel Pollet <buserror@gmail.com>
#
# SPDX-License-Identifier: MIT
# GCC is default -- simply because it's faster!
# GCC is default -- simply because it's faster to compile!
# From cursory tests, clang doesn't really add anything in terms of perfs.
CC = gcc
SHELL = /bin/bash
# This is where (g)make looks for the source files for implicit rules
@ -9,17 +15,26 @@ VPATH += ui_gl
CPPFLAGS += -Isrc -Isrc/format -Isrc/roms -Isrc/drivers
CPPFLAGS += -Icontrib
CPPFLAGS += -Ilibmish/src
CPPFLAGS += -Ilibmui/mui
CPPFLAGS += -Ilibmui/src
OPTIMIZE ?= -O2 -march=native
OPTIMIZE ?= -O2 -march=native -ffast-math
CFLAGS += --std=gnu99 -Wall -Wextra -g
CFLAGS += -fno-omit-frame-pointer
#CFLAGS += -fno-omit-frame-pointer
CFLAGS += $(OPTIMIZE)
CFLAGS += -Wno-unused-parameter -Wno-unused-function
LDLIBS += -lX11 -lGL -lGLU
LDLIBS += -lpthread -lutil -lm
VERSION := ${shell git log -1 --date=short --pretty="%h %cd"}
# better/faster linker
HAS_MOLD := $(shell which mold && echo 1)
ifeq ($(HAS_MOLD),1)
LDFLAGS += -B/usr/libexec/mold
endif
VERSION := ${shell \
echo $$(git describe --tags --abbrev=0 2>/dev/null || \
echo "(dev)") \
$$(git log -1 --date=short --pretty="%h %cd")}
CPPFLAGS += -DMII_VERSION="\"$(VERSION)\""
HAS_ALSA := $(shell pkg-config --exists alsa && echo 1)
@ -47,6 +62,13 @@ ALL_OBJ := ${patsubst %, ${OBJ}/%, ${notdir ${SRC:.c=.o}}}
CPPFLAGS += ${shell pkg-config --cflags pixman-1}
LDLIBS += ${shell pkg-config --libs pixman-1}
# this requires 64 bits ints, as it's used by xorg
# This uses tinycc, which is handy to run that sort of tools
ui_gl/mii_icon64.h : contrib/mii-icon-64.png
tcc -run libmui/utils/png2raw.c -t "unsigned long" -n mii_icon64 -o $@ $<
ui_gl/mii_mui_apple_logo.h : docs/Apple_logo_rainbow_version2_28x28.png
tcc -run libmui/utils/png2raw.c -n mii_mui_apple_logo -o $@ $<
$(BIN)/mii_emu_gl : $(ALL_OBJ) | mui mish
$(BIN)/mii_emu_gl : $(LIB)/libmish.a
$(BIN)/mii_emu_gl : $(LIB)/libmui.a
@ -60,7 +82,7 @@ $(LIB)/libmish.a : ${wildcard libmish/src/*} | $(LIB) $(OBJ) $(BIN)
LDLIBS += $(LIB)/libmui.a
mui : $(LIB)/libmui.a
$(LIB)/libmui.a : ${wildcard libmui/mui/*} | $(LIB) $(OBJ) $(BIN)
$(LIB)/libmui.a : ${wildcard libmui/src/*} | $(LIB) $(OBJ) $(BIN)
mkdir -p $(OBJ)/libmui && \
make -j -C libmui BUILD_DIR="../" CC="$(CC)" \
V="$(V)" OPTIMIZE="$(OPTIMIZE)" static
@ -78,23 +100,33 @@ clean :
# everytime a file is modified.
watch :
while true; do \
clear; $(MAKE) -j all tests; \
clear; $(MAKE) -j all ; \
inotifywait -qre close_write src src/format ui_gl test \
libmui libmui/mui; \
libmui libmui/src; \
done
tests : $(BIN)/mii_test $(BIN)/mii_cpu_test $(BIN)/mii_asm
# Just the library for mii, not any of the UI stuff
TEST_OBJ := ${patsubst %, ${OBJ}/%, ${notdir ${MII_SRC:.c=.o}}}
VPATH += test
# Base test without the UI, for performance testing
$(BIN)/mii_test : $(TEST_OBJ)
$(BIN)/mii_test : $(OBJ)/mii_test.o $(OBJ)/mii_mish.o
$(OBJ)/mii_test.o : CFLAGS := -O0 -Og ${filter-out -O%, $(CFLAGS)}
$(OBJ)/mii_cpu_test.o : CFLAGS := -O0 -Og ${filter-out -O%, $(CFLAGS)}
# Base test without the UI -- this re-include all C source in one big
# executable, ignoring the existing .o files, just so we can have custom flags
# for the test
$(BIN)/mii_test : test/mii_test.c ${MII_SRC}
$(BIN)/mii_test : CFLAGS = --std=gnu99 -Wall -Wextra -g -O0 -Og \
-Wno-unused-parameter -Wno-unused-function
$(BIN)/mii_test : CPPFLAGS = -DMII_TEST \
-Isrc -Isrc/format -Isrc/roms -Isrc/drivers -Icontrib \
-Ilibmish/src
$(BIN)/mii_test :
@echo " TEST" ${filter -O%, $(CPPFLAGS) $(CFLAGS)} $@
$(Q)$(CC) $(CPPFLAGS) $(CFLAGS) -o $@ $^ $(LIB)/libmish.a
VPATH += test
$(BIN)/mii_cpu_test : $(OBJ)/mii_cpu_test.o $(TEST_OBJ)
$(OBJ)/mii_cpu_test.o : CFLAGS := -O0 -Og ${filter-out -O%, $(CFLAGS)}
$(OBJ)/mii_cpu_test.o : CPPFLAGS += -DMII_TEST -DMII_65C02_DIRECT_ACCESS=0
# Assembler for the 6502
$(BIN)/mii_asm : $(OBJ)/mii_asm.o $(TEST_OBJ)
@ -123,7 +155,7 @@ compile_commands.json: lsp
lsp:
{ $$(which gmake) CC=gcc V=1 --always-make --dry-run all tests; \
$$(which gmake) CC=gcc V=1 --always-make --dry-run -C libmish ; \
$$(which gmake) CC=gcc V=1 --always-make --dry-run -C libmui ; } | \
$$(which gmake) CC=gcc V=1 --always-make --dry-run -C libmui all tests ; } | \
sh utils/clangd_gen.sh >compile_commands.json
-include $(O)/*.d
@ -139,3 +171,5 @@ avail:
mkdir -p $(DESTDIR)/bin
rm -f $(DESTDIR)/bin/mii_emu_gl && \
ln -sf $(realpath $(BIN)/mii_emu_gl) $(DESTDIR)/bin/mii_emu_gl
-include Makefile-extras*.local

101
README.md
View File

@ -2,58 +2,21 @@
<img src="contrib/mii-icon-64.png" alt="MII Logo">
</p>
# MII Version Changelog
## 1.7
* New animated about box, because, everyone loves a good about box.
* Added support for Write Protect of floppy disks; disk can be write protected manually, or if the file lacks write permissions, OR if the file format (NIB, DSK) doesn't support writes.
* New fancypants 'bit view' of the floppy are they are read/written, with a
heat map to show where the head was last. Drive 1 appears left of the screen,
drive 2 on the right. It fades out after the drive motor stops.
![Heat map disk viewq](docs/heat_map.png)
*DOS 3.3 Disk 'bitstream view' on the left, the green trace shows what's just be read.*
## 1.6
* Another big update, yanked the old DiskII driver, and replaced it with a
homebrew one, low level, with native support for WOZ (1 & 2) files (*read AND write!*) as well as NIB and DSK (read only).
* This is big news, and MII can now boot all kind of copy protected disks, in
fact I tried a few hundreds, and they all worked so far!
* There is currently no way to create a new disk, but you can use a tool like
[CiderPress](https://a2ciderpress.com/) to create them, and then use them in MII. Or just copy your DOS 3.3.woz file and reformat it!
* There were a few other minor changes, mostly added some timing measurement
tooling, and a couple of (necessary for disk to work) tweaks to the emulator
itself as it was not counting cycles correctly for a couple of instructions.
* The UI now has support for pure power-of-two textures, for really old OpenGL
implementations, it is turned off by default, but could work with some
old hardware. Also updated *libmui* to make it less linux-centric.
* Fixed some more color issues, mostly DHIRES.
* Added 'typeahead' for when you select files in the dialog, like on old Macs
## 1.5
* BIG update, loads of changes, fixes, improvements.
* New super UI, using home-made libmui, channeling both GS/OS and MacOS 7.x!
* New emulation fixes, way more accurate. Video redone, audio redone.
* New front-end program using XLib and OpenGL 'low level'.
* New Icon.
## 1.0
* Fixed a few graphics rendering bugs/color swapped
* Fixed a few Makefile issues involving pathnamed with 'spaces' in them.
* More tweaks to the emulation, added a few cycles here and there.
## 0.9
* Added a 'debugger' shell, accessible via telnet.
* Added a mini-assembler, used to compile the drivers and the CPU unit tests.
* Added a 'Titan Accelerator IIe' simulation, to turn on/off fast mode.
## 0.5
* Initial release
# MII Apple //e Emulator
*Note:* New versions and Changelog are at [CHANGELOG.md](CHANGELOG.md)
*Note2:* UI Library has moved to [libmui](https://github.com/buserror/libmui)
I know there are many out there, but none of them were ticking my fancy, so I decide to write my own. To start with it was "How hard can it be really?" then it snowballed as more and more things were fixed & added. It's been shelved for a while because well, it lacked documentation, headers, licence and stuff, so I spent some time cleaning it up for release.
One primary reason for this project was that linapple (or -pie) codebase is really horrible. It dates back from 2000's or before, with loads of Windows crud leftover, some SDL crud added, the audio just doesn't really work, and overall if you want to hack around the codebase, it's pretty dreadful.
![Quick how to load and boot](docs/video_main.gif)
*Quick Howto Load & Boot*
<center>
<img src="docs/screen/video_main.gif" alt="Quick how to load and boot">
<i>Quick Howto Load & Boot</i>
</center>
I wanted something:
@ -64,32 +27,37 @@ I wanted something:
* No gigantic config file.
* I didn't need II+ or unenhanced IIe, just 65c02 //e.
![Glorious NTSC colors](docs/screen_color.png)
*Double hires in color*
<center>
<img src="docs/screen/screen_color.png" alt="Glorious NTSC colors">
<i>Double hires in color</i>
</center>
## What can it do?
* 65c02 //e with 128K of ram.
* 65c02 //e with 128K of ram (RAM exensible to 2MB).
* Support all known graphic modes:
* Double-hires in mono and color, with automatic switch
* All the other modes in color/mono
* Color, Green, Amber rendering
* Simulated 'scanlines' to make it look vintage
* Color (NTSC/RGB), Green, Amber rendering
* Simulated 'scanlines' & artifacts to make it look vintage
* Speaker audio. ALSA For playback.
* Adds a small 'attack' filter when playing back to soften the often annoying 'click' of typical audio effects from the apple II.
* Mouse Card -- mouse isn't captured like in some other emulators.
* Super Serial Card -- can open a /dev/tty device, or a 'loopback' device.
* No Slot Clock
* Joystick Support
* Smartport DMA 'hard drive' card
* RAMWorks III card, with 1MB of RAM
* "Titan Accelerator //e" simulation, to turn on/off fast mode.
* Terence's J Boldt [1MB ROM card](https://github.com/tjboldt/ProDOS-ROM-Drive), also because I own a couple!
* Floppy Drive [more on that later]
* Floppy Drive with WOZ 1/2 in read/write, NIB and DSK in read only.
* No dependencies (X11) OpenGL rendering
* Built-in debugger (using telnet access)
* Super cool looking UI!
![Phosphorescent Green](docs/screen_green.png)
*Good old green monitor style. Theres Amber too.*
<center>
<img src="docs/screen/screen_green.png" alt="Phosphorescent Green">
<i>Good old green monitor style. Theres Amber too.</i>
</center>
## How to I compile it and run it?
* You need a C compiler, make, and a few libraries:
@ -102,13 +70,16 @@ I wanted something:
* For more details on development, see [Compiling](docs/Compiling.md)
* Then just type `make` and it should compile.
* To run it, just type `build-x86_64-linux-gnu/bin/mii_emu_gl` and it should start.
* If you want to install it, I suggest 'make avail' this will install a symlink into /usr/local/bin pointing back to the source tree, so you can run it from anywhere.
## Command line options
If you run it with no options, and there are no config file, it will present
you with a dialog to select the ROMs and the drives.
![Config dialog](docs/screen_config.png)
*Main slot configuration dialog*
<center>
<img src="docs/screen/screen_config.png" alt="Config dialog">
<i>Main slot configuration dialog</i>
</center>
You can also use the command line to specify them, and other options.
@ -161,9 +132,10 @@ There are just a few keys that are mapped for anything useful. List is not exaus
* **Control-F6** 'steps' the emulator, ie one instruction at a time.
* **Control-F7** 'next' instruction, ie step over a JSR instruction.
![Telnet into mii_emu](docs/screen_mish.png)
*The built-in shell, telnet into the emulator!*
<center>
<img src="docs/screen/screen_mish.png" alt="Telnet into mii_emu">
<i>The built-in shell, telnet into the emulator!</i>
</center>
## Anything else?
* Well it has it's own command line shell, using my own [libmish](https://github.com/buserror/libmish) so there's loads you can do by... *telnet into* the emulator!
@ -191,13 +163,16 @@ There are just a few keys that are mapped for anything useful. List is not exaus
## What it could do with
* The main window is 1280x720 on purpose, so it could do Full Screen.
* Port it to Raspbery Pi. I don't expect compiling issues, just video issues with GLes
* ~~Port it to Raspbery Pi~~. This was reported to work on a raspi 5.
* Make a tool to 'flatten' overlay files back into the primary image.
* Make a UI for the debugger, instead of telnet.
* A mockinbird sound card emulation [In Progress].
<div align="center">
<img src="docs/screen/screen_total.png" alt="Total Replay">
</div>
<center><i>Obligatory View of Total Replay</i></center>
![Total Replay](docs/screen_total.png)
*Obligatory View of [Total Replay](https://github.com/a2-4am/4cade), from legend [4am](https://github.com/a2-4am)*
## Inspiration, Licence etc
* MIT Licence, I think this is the most permissive, and this work is a derivative and has a lot of inpsiration from too many projects to claim anything more restrictive.
* The CPU Emulation was inspired by a few other implementations:

View File

@ -1,584 +0,0 @@
/* this file is auto-generated by icon-convert-tcc.c */
#define MII_ICON64_SIZE 4098
extern const unsigned long mii_icon64[MII_ICON64_SIZE];
#ifdef MII_ICON64_DEFINE
const unsigned long mii_icon64[MII_ICON64_SIZE] = {
64,64,
0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x01000000,
0x01000000, 0x039e9ea1, 0x04bebfc1, 0x04c9cacd, 0x04c9cacd, 0x05ccced0, 0x05ccced0, 0x05cfcfd1,
0x05d0d1d3, 0x06d2d3d6, 0x06d3d4d6, 0x06d2d3d6, 0x06d4d5d7, 0x0778a88e, 0x07499b67, 0x07499b67,
0x07499b67, 0x07499b67, 0x07499b67, 0x074a9b67, 0x06499b65, 0x064a9c65, 0x064a9c64, 0x064a9c63,
0x064a9b61, 0x064a9c5f, 0x064b9d5e, 0x054b9c5d, 0x054b9c5c, 0x054c9c55, 0x044d9a48, 0x01000000,
0x01000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
0x00000000, 0x00000000, 0x00000000, 0x2ff1f3f7, 0x7bf4f6fa, 0xaff4f7fb, 0xd0f4f7fb, 0xf2f5f7fb,
0xfdf5f8fc, 0xfff5f8fc, 0xfff5f8fc, 0xfff5f8fc, 0xfff5f8fc, 0xfff5f8fc, 0xfff5f8fc, 0xfff5f8fc,
0xfff5f8fc, 0xfff5f8fc, 0xfff5f8fc, 0xfff5f8fc, 0xfff5f8fc, 0xffc2ddc8, 0xff5fba4f, 0xff60ba4e,
0xff60ba4e, 0xff60ba4e, 0xff60ba4e, 0xff60ba4e, 0xff60ba4e, 0xff60ba4e, 0xff60ba4e, 0xff60ba4e,
0xff60ba4e, 0xff60ba4e, 0xff60ba4e, 0xff60ba4e, 0xff60ba4e, 0xff60ba4e, 0xff60ba4f, 0xfe5fba52,
0xf65fba53, 0xd35fba52, 0xb15eb955, 0x7c5db85c, 0x315ab663, 0x00000000, 0x00000000, 0x00000000,
0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
0x00000000, 0x51f3f5f9, 0xcef5f7fb, 0xfff5f8fc, 0xfff5f8fc, 0xfff5f8fc, 0xfff5f8fc, 0xfff5f8fc,
0xfff5f7fb, 0xfff5f8fc, 0xfff5f8fc, 0xfff5f7fb, 0xfff5f8fc, 0xfff5f8fc, 0xfff5f8fc, 0xfff5f8fc,
0xfff5f8fc, 0xfff5f7fb, 0xfff5f8fc, 0xfff5f8fc, 0xfff5f7fb, 0xfff1f5f7, 0xff70be68, 0xff61bb46,
0xff61bb46, 0xff61bb46, 0xff61bb46, 0xff61bb46, 0xff61bb46, 0xff61bb46, 0xff61bb46, 0xff61bb46,
0xff61bb46, 0xff61bb46, 0xff61bb46, 0xff61bb46, 0xff61bb46, 0xff61bb46, 0xff61bb46, 0xff61bb46,
0xff61bb46, 0xff61bb46, 0xff61bb46, 0xff61bb46, 0xff60bb4b, 0xcd5eb954, 0x515bb75f, 0x00000000,
0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x13edeff3,
0xb4f4f7fb, 0xfff5f8fc, 0xfff5f8fc, 0xfff5f8fc, 0xfff5f8fc, 0xfff5f8fc, 0xfff5f8fc, 0xfff5f8fc,
0xfff5f8fc, 0xfff5f8fc, 0xfff5f8fc, 0xfff5f8fc, 0xfff5f8fc, 0xfff5f8fc, 0xfff5f8fc, 0xfff5f8fc,
0xfff5f8fc, 0xfff5f8fc, 0xfff5f8fc, 0xfff5f8fc, 0xfff5f8fc, 0xfff5f8fc, 0xffbad9be, 0xff61bb47,
0xff61bb46, 0xff61bb46, 0xff61bb46, 0xff61bb46, 0xff61bb46, 0xff61bb46, 0xff61bb46, 0xff61bb46,
0xff61bb46, 0xff61bb46, 0xff61bb46, 0xff61bb46, 0xff61bb46, 0xff61bb46, 0xff61bb46, 0xff61bb46,
0xff61bb46, 0xff61bb46, 0xff61bb46, 0xff61bb46, 0xff61bb46, 0xff61bb46, 0xff60bb49, 0xb25db959,
0x1358b361, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x20eef1f4, 0xdff5f7fb,
0xfff5f8fc, 0xfff5f8fc, 0xfff5f7fb, 0xfff5f8fc, 0xfff5f8fc, 0xfff5f7fb, 0xfff5f8fc, 0xfff5f8fc,
0xfff5f7fb, 0xfff5f8fc, 0xfff5f8fc, 0xfff5f7fb, 0xfff5f8fc, 0xfff5f8fc, 0xfff5f7fb, 0xfff5f8fc,
0xfff5f8fc, 0xfff5f7fb, 0xfff5f8fc, 0xfff5f8fc, 0xfff5f7fb, 0xfff5f8fc, 0xffecf3f2, 0xff61ba52,
0xff61bb46, 0xff61bb46, 0xff61bb46, 0xff61bb46, 0xff61bb46, 0xff61bb46, 0xff61bb46, 0xff61bb46,
0xff61bb46, 0xff61bb46, 0xff61bb46, 0xff61bb46, 0xff61bb46, 0xff61bb46, 0xff61bb46, 0xff61bb46,
0xff61bb46, 0xff61bb46, 0xff61bb46, 0xff61bb46, 0xff61bb46, 0xff61bb46, 0xff61bb46, 0xff61bb46,
0xdd5eb953, 0x1f59b465, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x14eaecf0, 0xdff5f7fb, 0xfff5f7fb,
0xfff5f7fb, 0xfff5f7fb, 0xfff5f7fb, 0xfff5f7fb, 0xfff5f7fb, 0xfff5f7fb, 0xfff5f7fb, 0xfff5f7fb,
0xfff5f7fb, 0xfff5f7fb, 0xfff5f7fb, 0xfff5f7fb, 0xfff5f7fb, 0xfff5f7fb, 0xfff5f7fb, 0xfff5f7fb,
0xfff5f7fb, 0xfff5f7fb, 0xfff5f7fb, 0xfff5f7fb, 0xfff5f7fb, 0xfff5f7fb, 0xfff5f8fb, 0xffa2cfa3,
0xff61bb46, 0xff61bb46, 0xff61bb46, 0xff61bb46, 0xff61bb46, 0xff61bb46, 0xff61bb46, 0xff61bb46,
0xff61bb46, 0xff61bb46, 0xff61bb46, 0xff61bb46, 0xff61bb46, 0xff61bb46, 0xff61bb46, 0xff61bb46,
0xff61bb46, 0xff61bb46, 0xff61bb46, 0xff61bb46, 0xff61bb46, 0xff61bb46, 0xff61bb46, 0xff61bb46,
0xff61bb46, 0xdd5eb954, 0x1256b062, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x01000000, 0xb4f4f6fa, 0xfff5f7fb, 0xfff5f7fb,
0xfff5f7fb, 0xfff5f7fb, 0xfff5f7fb, 0xfff5f7fb, 0xfff5f7fb, 0xfff5f7fb, 0xfff5f7fb, 0xfff5f7fb,
0xfff5f7fb, 0xfff5f7fb, 0xfff5f7fb, 0xfff5f7fb, 0xfff5f7fb, 0xfff5f7fb, 0xfff5f7fb, 0xfff5f7fb,
0xfff5f7fb, 0xfff5f7fb, 0xfff5f7fb, 0xfff5f7fb, 0xfff5f7fb, 0xfff5f7fb, 0xfff5f7fb, 0xffd6e6dc,
0xff60bb48, 0xff61bb46, 0xff61bb46, 0xff61bb46, 0xff61bb46, 0xff61bb46, 0xff61bb46, 0xff61bb46,
0xff61bb46, 0xff61bb46, 0xff61bb46, 0xff61bb46, 0xff61bb46, 0xff61bb46, 0xff61bb46, 0xff61bb46,
0xff61bb46, 0xff61bb46, 0xff61bb46, 0xff61bb46, 0xff61bb46, 0xff61bb46, 0xff61bb46, 0xff61bb46,
0xff61bb46, 0xff61bb46, 0xb05db858, 0x01000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x4feff1f5, 0xfff5f7fa, 0xfff5f7fb, 0xfff4f7fa,
0xfff5f7fa, 0xfff5f7fa, 0xfff5f7fb, 0xfff5f7fa, 0xfff5f7fa, 0xfff5f7fb, 0xfff5f7fa, 0xfff5f7fb,
0xfff4f7fa, 0xfff5f7fa, 0xfff5f7fa, 0xfff5f7fb, 0xfff5f7fa, 0xfff5f7fa, 0xfff5f7fb, 0xfff5f7fa,
0xfff5f7fb, 0xfff4f7fa, 0xfff5f7fa, 0xfff5f7fa, 0xfff5f7fb, 0xfff5f7fa, 0xfff5f7fa, 0xfff5f7fa,
0xff75bf6e, 0xff61bb46, 0xff61bb46, 0xff61bb46, 0xff61bb46, 0xff61bb46, 0xff61bb46, 0xff61bb46,
0xff61bb46, 0xff61bb46, 0xff61bb46, 0xff61bb46, 0xff61bb46, 0xff61bb46, 0xff61bb46, 0xff61bb46,
0xff61bb46, 0xff61bb46, 0xff61bb46, 0xff61bb46, 0xff61bb46, 0xff61bb46, 0xff61bb46, 0xff61bb46,
0xff61bb46, 0xff61bb46, 0xff60bb4a, 0x4959b460, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
0x00000000, 0x00000000, 0x00000000, 0x01000000, 0xccf3f5f9, 0xfff5f7fa, 0xfff5f7fa, 0xfff5f7fa,
0xfff5f7fa, 0xfff5f7fa, 0xfff5f7fa, 0xfff5f7fa, 0xfff5f7fa, 0xfff5f6fa, 0xfff5f7fa, 0xfff5f7fa,
0xfff5f7fa, 0xfff5f7fa, 0xfff5f7fa, 0xfff5f7fa, 0xfff5f7fa, 0xfff5f7fa, 0xfff5f6fa, 0xfff5f7fa,
0xfff5f7fa, 0xfff5f7fa, 0xfff5f7fa, 0xfff5f7fa, 0xfff5f7fa, 0xfff5f7fa, 0xfff5f7fa, 0xfff5f7fa,
0xffb3d6b5, 0xff61bb46, 0xff61bb46, 0xff61bb46, 0xff61bb46, 0xff61bb46, 0xff61bb46, 0xff61bb46,
0xff61bb46, 0xff61bb46, 0xff61bb46, 0xff61bb46, 0xff61bb46, 0xff61bb46, 0xff61bb46, 0xff61bb46,
0xff61bb46, 0xff61bb46, 0xff61bb46, 0xff61bb46, 0xff61bb46, 0xff61bb46, 0xff61bb46, 0xff61bb46,
0xff61bb46, 0xff61bb46, 0xff61bb46, 0xc85db854, 0x01000000, 0x00000000, 0x00000000, 0x00000000,
0x00000000, 0x00000000, 0x00000000, 0x2ce8e9ed, 0xfff5f6fa, 0xfff5f6fa, 0xfff5f6fa, 0xfff4f6fa,
0xfff5f6fa, 0xfff5f6fa, 0xfff5f6fa, 0xfff5f6fa, 0xfff5f6fa, 0xfff5f6fa, 0xfff5f6fa, 0xfff5f6fa,
0xfff4f6fa, 0xfff5f6fa, 0xfff5f6fa, 0xfff5f6fa, 0xfff5f6fa, 0xfff5f6fa, 0xfff5f6fa, 0xfff5f6fa,
0xfff5f6fa, 0xfff4f6fa, 0xfff5f6fa, 0xfff5f6fa, 0xfff5f6fa, 0xfff5f6fa, 0xfff5f6fa, 0xfff5f6fa,
0xffdbe9e1, 0xff60ba49, 0xff61bb47, 0xff61bb47, 0xff61bb47, 0xff61bb47, 0xff61bb47, 0xff61bb47,
0xff61bb47, 0xff61bb47, 0xff61bb47, 0xff61bb47, 0xff61bb47, 0xff61bb47, 0xff61bb47, 0xff61bb47,
0xff61bb47, 0xff61bb47, 0xff61bb47, 0xff61bb47, 0xff61bb47, 0xff61bb47, 0xff61bb47, 0xff61bb47,
0xff61bb47, 0xff61bb47, 0xff61bb47, 0xff60ba4c, 0x2854ae62, 0x00000000, 0x00000000, 0x00000000,
0x00000000, 0x00000000, 0x00000000, 0x76f0f1f4, 0xfff5f6f9, 0xfff5f6f9, 0xfff5f6f9, 0xfff5f6f9,
0xfff5f6f9, 0xfff5f6f9, 0xfff5f6f9, 0xfff5f6f9, 0xfff5f6f9, 0xfff5f6f9, 0xfff5f6f9, 0xfff5f6f9,
0xfff5f6f9, 0xfff5f6f9, 0xfff5f6f9, 0xfff5f6f9, 0xfff5f6f9, 0xfff5f6f9, 0xfff5f6f9, 0xfff5f6f9,
0xfff5f6f9, 0xfff5f6f9, 0xfff5f6f9, 0xfff5f6f9, 0xfff5f6f9, 0xfff5f6f9, 0xfff5f6f9, 0xfff5f6f9,
0xfff5f6f9, 0xffc8b863, 0xffcbb440, 0xffccb540, 0xffccb540, 0xffccb540, 0xffccb540, 0xffccb540,
0xffccb540, 0xffccb540, 0xffccb540, 0xffccb540, 0xffccb540, 0xffccb540, 0xffccb540, 0xffccb540,
0xffccb540, 0xffccb540, 0xffccb540, 0xffccb540, 0xffccb540, 0xffccb540, 0xffccb540, 0xffccb540,
0xffccb540, 0xffccb540, 0xffccb540, 0xffccb541, 0x70cdaf48, 0x00000000, 0x00000000, 0x00000000,
0x00000000, 0x00000000, 0x01000000, 0xa7f1f2f5, 0xfff5f6f9, 0xfff5f6f9, 0xfff5f6f9, 0xfff5f6f9,
0xfff5f6f9, 0xfff5f6f9, 0xfff5f6f9, 0xfff5f6f9, 0xfff5f6f9, 0xfff5f6f9, 0xfff5f6f9, 0xfff5f6f9,
0xfff5f6f9, 0xfff5f6f9, 0xfff5f6f9, 0xfff5f6f9, 0xfff5f6f9, 0xfff5f6f9, 0xfff5f6f9, 0xfff5f6f9,
0xfff5f6f9, 0xfff5f6f9, 0xfff5f6f9, 0xfff5f6f9, 0xfff5f6f9, 0xfff5f6f9, 0xfff5f6f9, 0xfff5f6f9,
0xfff5f6f9, 0xfffad0a0, 0xfffdb827, 0xfffdb827, 0xfffdb827, 0xfffdb827, 0xfffdb827, 0xfffdb827,
0xfffdb827, 0xfffdb827, 0xfffdb827, 0xfffdb827, 0xfffdb827, 0xfffdb827, 0xfffdb827, 0xfffdb827,
0xfffdb827, 0xfffdb827, 0xfffdb827, 0xfffdb827, 0xfffdb827, 0xfffdb827, 0xfffdb827, 0xfffdb827,
0xfffdb827, 0xfffdb827, 0xfffdb827, 0xfffdb827, 0xa2f9b325, 0x01000000, 0x00000000, 0x00000000,
0x00000000, 0x00000000, 0x02000000, 0xcef2f3f6, 0xfff4f5f9, 0xfff4f5f9, 0xfff4f5f9, 0xfff4f5f8,
0xfff4f5f9, 0xfff4f5f9, 0xfff4f5f9, 0xfff4f5f9, 0xfff4f5f9, 0xfff4f5f9, 0xfff4f5f9, 0xfff4f5f9,
0xfff4f5f8, 0xfff4f5f9, 0xfff4f5f9, 0xfff4f5f9, 0xfff4f5f9, 0xfff4f5f9, 0xfff4f5f9, 0xfff4f5f9,
0xfff4f5f9, 0xfff4f5f8, 0xfff4f5f9, 0xfff4f5f9, 0xfff4f5f9, 0xfff4f5f9, 0xfff4f5f9, 0xfff4f5f9,
0xfff4f5f9, 0xfff8e3d0, 0xfffdb827, 0xfffdb827, 0xfffdb827, 0xfffdb827, 0xfffdb827, 0xfffdb827,
0xfffdb827, 0xfffdb827, 0xfffdb827, 0xfffdb827, 0xfffdb827, 0xfffdb827, 0xfffdb827, 0xfffdb827,
0xfffdb827, 0xfffdb827, 0xfffdb827, 0xfffdb827, 0xfffdb827, 0xfffdb827, 0xfffdb827, 0xfffdb827,
0xfffdb827, 0xfffdb827, 0xfffdb827, 0xfffdb827, 0xcafbb425, 0x02000000, 0x00000000, 0x00000000,
0x00000000, 0x00000000, 0x04000000, 0xdef2f3f6, 0xfff4f5f8, 0xfff4f5f8, 0xfff4f5f8, 0xfff4f5f8,
0xfff4f5f8, 0xfff4f5f8, 0xfff4f5f8, 0xfff4f5f8, 0xfff4f5f8, 0xfff4f5f8, 0xfff4f5f8, 0xfff4f5f8,
0xfff4f5f8, 0xfff4f5f8, 0xfff4f5f8, 0xfff4f5f8, 0xfff4f5f8, 0xfff4f5f8, 0xfff4f5f8, 0xfff4f5f8,
0xfff4f5f8, 0xfff4f5f8, 0xfff4f5f8, 0xfff4f5f8, 0xfff4f5f8, 0xfff4f5f8, 0xfff4f5f8, 0xfff4f5f8,
0xfff4f5f8, 0xfff6f2f0, 0xfffcb625, 0xfffdb827, 0xfffdb827, 0xfffdb827, 0xfffdb827, 0xfffdb827,
0xfffdb827, 0xfffdb827, 0xfffdb827, 0xfffdb827, 0xfffdb827, 0xfffdb827, 0xfffdb827, 0xfffdb827,
0xfffdb827, 0xfffdb827, 0xfffdb827, 0xfffdb827, 0xfffdb827, 0xfffdb827, 0xfffdb827, 0xfffdb827,
0xfffdb827, 0xfffdb827, 0xfffdb827, 0xfffdb827, 0xd7fab425, 0x03000000, 0x00000000, 0x00000000,
0x00000000, 0x00000000, 0x05000000, 0xeff2f3f6, 0xfff4f5f8, 0xfff4f5f8, 0xfff4f5f8, 0xfff4f5f8,
0xfff4f5f8, 0xfff4f5f8, 0xfff4f5f8, 0xfff4f5f8, 0xfff4f5f8, 0xfff4f5f8, 0xfff4f5f8, 0xfff4f5f8,
0xfff4f5f8, 0xfff4f5f8, 0xfff4f5f8, 0xfff4f5f8, 0xfff4f5f8, 0xfff4f5f8, 0xfff4f5f8, 0xfff4f5f8,
0xfff4f5f8, 0xfff4f5f8, 0xfff4f5f8, 0xfff4f5f8, 0xfff4f5f8, 0xfff4f5f8, 0xfff4f5f8, 0xfff4f5f8,
0xfff4f5f8, 0xfff5f5f8, 0xfffbc376, 0xfffdb827, 0xfffdb827, 0xfffdb827, 0xfffdb827, 0xfffdb827,
0xfffdb827, 0xfffdb827, 0xfffdb827, 0xfffdb827, 0xfffdb827, 0xfffdb827, 0xfffdb827, 0xfffdb827,
0xfffdb827, 0xfffdb827, 0xfffdb827, 0xfffdb827, 0xfffdb827, 0xfffdb827, 0xfffdb827, 0xfffdb827,
0xfffdb827, 0xfffdb827, 0xfffdb827, 0xfffdb827, 0xe6fbb526, 0x05000000, 0x00000000, 0x00000000,
0x00000000, 0x00000000, 0x07000000, 0xf8f3f4f7, 0xfff4f5f8, 0xfff4f5f8, 0xfff4f5f8, 0xfff4f5f8,
0xfff4f5f8, 0xfff4f5f8, 0xfff4f5f8, 0xfff4f5f8, 0xfff4f5f8, 0xfff4f5f8, 0xfff4f5f8, 0xfff4f5f8,
0xfff4f5f8, 0xfff4f5f8, 0xfff4f5f8, 0xfff4f5f8, 0xfff4f5f8, 0xfff4f5f8, 0xfff4f5f8, 0xfff4f5f8,
0xfff4f5f8, 0xfff4f5f8, 0xfff4f5f8, 0xfff4f5f8, 0xfff4f5f8, 0xfff4f5f8, 0xfff4f5f8, 0xfff4f5f8,
0xfff4f5f8, 0xfff4f5f8, 0xfff9d4ad, 0xfffdb827, 0xfffdb827, 0xfffdb827, 0xfffdb827, 0xfffdb827,
0xfffdb827, 0xfffdb827, 0xfffdb827, 0xfffdb827, 0xfffdb827, 0xfffdb827, 0xfffdb827, 0xfffdb827,
0xfffdb827, 0xfffdb827, 0xfffdb827, 0xfffdb827, 0xfffdb827, 0xfffdb827, 0xfffdb827, 0xfffdb827,
0xfffdb827, 0xfffdb827, 0xfffdb827, 0xfffdb827, 0xf3fcb525, 0x06000000, 0x00000000, 0x00000000,
0x00000000, 0x00000000, 0x07000000, 0xfdf3f5f7, 0xfff3f4f7, 0xfff3f5f8, 0xfff3f5f8, 0xfff3f4f7,
0xfff4f5f8, 0xfff3f5f8, 0xfff3f4f7, 0xfff3f5f8, 0xfff3f5f8, 0xfff3f4f7, 0xfff3f5f8, 0xfff3f5f8,
0xfff3f4f7, 0xfff4f5f8, 0xfff3f5f8, 0xfff3f4f7, 0xfff3f5f8, 0xfff3f5f8, 0xfff3f4f7, 0xfff3f5f8,
0xfff3f5f8, 0xfff3f4f7, 0xfff4f5f8, 0xfff3f5f8, 0xfff3f4f7, 0xfff3f5f8, 0xfff3f5f8, 0xfff3f4f7,
0xfff3f5f8, 0xfff3f5f8, 0xfff7e2cf, 0xfffdb827, 0xfffdb827, 0xfffdb827, 0xfffdb827, 0xfffdb827,
0xfffdb827, 0xfffdb827, 0xfffdb827, 0xfffdb827, 0xfffdb827, 0xfffdb726, 0xfffdb827, 0xfffdb827,
0xfffdb827, 0xfffdb827, 0xfffdb827, 0xfffdb827, 0xfffdb827, 0xfffdb827, 0xfffdb827, 0xfffdb827,
0xfffdb827, 0xfffdb827, 0xfffdb827, 0xfffdb827, 0xf9fcb526, 0x06000000, 0x00000000, 0x00000000,
0x00000000, 0x00000000, 0x08000000, 0xfff3f5f8, 0xfff3f5f8, 0xfff3f5f8, 0xfff3f5f8, 0xfff3f5f7,
0xfff3f5f7, 0xfff3f5f7, 0xfff3f5f8, 0xfff3f5f8, 0xfff3f5f8, 0xfff3f5f8, 0xfff3f5f8, 0xfff3f5f8,
0xfff3f5f7, 0xfff3f5f7, 0xffe7e8eb, 0xff88898a, 0xff939496, 0xffeeeff2, 0xfff3f5f8, 0xfff3f5f8,
0xfff3f5f8, 0xfff3f5f7, 0xfff3f5f7, 0xfff3f5f7, 0xfff3f5f8, 0xfff3f5f8, 0xfff3f5f8, 0xfff3f5f8,
0xfff3f5f8, 0xfff3f5f8, 0xfff5efea, 0xfffcb726, 0xfffdb827, 0xfffdb827, 0xfffdb827, 0xfffdb827,
0xfffdb827, 0xfffdb827, 0xfffdb827, 0xfffdb827, 0xffc58919, 0xff82540b, 0xffd7981d, 0xfffdb827,
0xfffdb827, 0xfffdb827, 0xfffdb827, 0xfffdb827, 0xfffdb827, 0xfffdb827, 0xfffdb827, 0xfffdb827,
0xfffdb827, 0xfffdb827, 0xfffdb827, 0xfffdb827, 0xfcfdb626, 0x07000000, 0x00000000, 0x00000000,
0x00000000, 0x00000000, 0x08000000, 0xfff3f5f8, 0xfff3f5f7, 0xfff3f5f7, 0xfff3f5f7, 0xfff3f5f7,
0xfff3f5f7, 0xfff3f5f7, 0xfff3f5f7, 0xfff3f5f7, 0xfff3f5f7, 0xfff3f5f7, 0xfff3f5f7, 0xfff3f5f7,
0xfff3f5f7, 0xfff3f5f7, 0xffc9cbcd, 0xff616162, 0xff616162, 0xffd9dbdd, 0xfff3f5f7, 0xfff3f5f7,
0xfff3f5f7, 0xfff3f5f7, 0xfff3f5f7, 0xfff3f5f7, 0xfff3f5f7, 0xfff3f5f7, 0xfff3f5f7, 0xfff3f5f7,
0xfff3f5f7, 0xfff3f5f7, 0xfff4f6f8, 0xfffbbb53, 0xfffdb827, 0xfffdb827, 0xfffdb827, 0xfffdb827,
0xfffdb827, 0xfffdb827, 0xfffdb827, 0xfffdb726, 0xff784a0b, 0xff653b08, 0xff9d6912, 0xfffdb827,
0xfffdb827, 0xfffdb827, 0xfffdb827, 0xfffdb827, 0xfffdb827, 0xfffdb827, 0xfffdb827, 0xfffdb827,
0xfffdb827, 0xfffdb827, 0xfffdb827, 0xfffdb827, 0xfffdb626, 0x07000000, 0x00000000, 0x00000000,
0x00000000, 0x00000000, 0x08000000, 0xfff3f4f7, 0xfff3f4f7, 0xfff3f4f7, 0xfff3f4f7, 0xfff3f4f7,
0xfff3f4f7, 0xfff3f4f7, 0xfff3f4f7, 0xfff3f4f7, 0xfff3f4f7, 0xfff3f4f7, 0xfff3f4f7, 0xfff3f4f7,
0xfff3f4f7, 0xfff3f4f7, 0xffc9cbcd, 0xff616162, 0xff616162, 0xffd7d9db, 0xfff3f4f7, 0xfff3f4f7,
0xfff3f4f7, 0xfff3f4f7, 0xfff3f4f7, 0xfff3f4f7, 0xfff3f4f7, 0xfff3f4f7, 0xfff3f4f7, 0xfff3f4f7,
0xfff3f4f7, 0xfff3f4f7, 0xfff3f4f7, 0xfff9c88d, 0xfffdb626, 0xfffdb626, 0xfffdb626, 0xfffdb626,
0xfffdb626, 0xfffdb626, 0xfffdb626, 0xfffdb425, 0xfe76460f, 0xfe65370d, 0xfe9a6414, 0xfffdb525,
0xfffdb626, 0xfffdb626, 0xfffdb626, 0xfffdb626, 0xfffdb626, 0xfffdb626, 0xfffdb626, 0xfffdb626,
0xfffdb626, 0xfffdb626, 0xfffdb626, 0xfffdb626, 0xfffdb424, 0x08000000, 0x00000000, 0x00000000,
0x00000000, 0x00000000, 0x08000000, 0xfff3f4f6, 0xfff2f4f6, 0xfff2f4f6, 0xfff2f4f6, 0xfff2f4f6,
0xfff3f4f7, 0xfff2f4f6, 0xfff2f4f6, 0xfff2f4f6, 0xfff2f4f6, 0xfff2f4f6, 0xfff2f4f6, 0xfff2f4f6,
0xfff2f4f6, 0xfff3f4f7, 0xffc9cacc, 0xff606162, 0xff606162, 0xffd7d9db, 0xfff2f4f6, 0xfff2f4f6,
0xfff2f4f6, 0xfff2f4f6, 0xfff3f4f7, 0xfff2f4f6, 0xfff2f4f6, 0xfff2f4f6, 0xfff2f4f6, 0xfff2f4f6,
0xfff2f4f6, 0xfff2f4f6, 0xfff2f4f6, 0xfff2bcb3, 0xfff86c3f, 0xfffd6e41, 0xfffe6e42, 0xfffe6e42,
0xfffe6e42, 0xfffe6e42, 0xfffe6e42, 0xfffe6e42, 0xfe723618, 0xfe653113, 0xfe974425, 0xfffe6e42,
0xfffe6e42, 0xfffe6e42, 0xfffe6e42, 0xfffe6e42, 0xfffe6e42, 0xfffe6e42, 0xfffe6e42, 0xfffe6e42,
0xfffe6e42, 0xfffe6e42, 0xfffe6e42, 0xfffe6e42, 0xfffe6e43, 0x08000000, 0x00000000, 0x00000000,
0x00000000, 0x00000000, 0x08000000, 0xfff2f4f6, 0xfff2f4f6, 0xfff2f4f6, 0xfff2f4f6, 0xfff2f4f6,
0xfff2f4f6, 0xfff2f4f6, 0xfff2f4f6, 0xfff2f4f6, 0xfff2f4f6, 0xfff2f4f6, 0xfff2f4f6, 0xfff2f4f6,
0xfff2f4f6, 0xfff2f4f6, 0xffc9cacc, 0xff606162, 0xff606162, 0xffd7d8da, 0xfff2f4f6, 0xfff2f4f6,
0xfff2f4f6, 0xfff2f4f6, 0xfff2f4f6, 0xfff2f4f6, 0xfff2f4f6, 0xfff2f4f6, 0xfff2f4f6, 0xfff2f4f6,
0xfff2f4f6, 0xfff2f4f6, 0xfff2f4f6, 0xfff2cdcb, 0xfff56240, 0xfffd6542, 0xfffe6643, 0xfffe6643,
0xfffe6643, 0xfffe6643, 0xfffe6643, 0xfffe6744, 0xff72311c, 0xff652c17, 0xff973f27, 0xfffe6643,
0xfffe6643, 0xfffe6643, 0xfffe6643, 0xfffe6643, 0xfffe6643, 0xfffe6643, 0xfffe6643, 0xfffe6643,
0xfffe6643, 0xfffe6643, 0xfffe6643, 0xfffe6643, 0xfffe6745, 0x08000000, 0x00000000, 0x00000000,
0x00000000, 0x00000000, 0x08000000, 0xfff2f3f6, 0xfff2f3f6, 0xfff2f3f6, 0xfff2f3f6, 0xfff2f3f6,
0xfff2f4f6, 0xfff2f3f6, 0xfff2f3f6, 0xfff2f4f6, 0xfff2f3f6, 0xfff2f3f6, 0xfff2f3f6, 0xfff2f3f6,
0xfff2f3f6, 0xfff2f4f6, 0xffd1d2d4, 0xff606162, 0xff606162, 0xffe1e3e5, 0xfff2f3f6, 0xfff2f3f6,
0xfff2f3f6, 0xfff2f3f6, 0xfff2f4f6, 0xfff2f3f6, 0xfff2f3f6, 0xfff2f4f6, 0xfff2f3f6, 0xfff2f3f6,
0xfff2f3f6, 0xfff2f3f6, 0xfff2f3f6, 0xfff3e0e0, 0xfff36140, 0xfffc6542, 0xfffe6643, 0xfffe6643,
0xfffe6643, 0xfffe6643, 0xfffe6643, 0xfffe6643, 0xff8d3c27, 0xff65291a, 0xffac4931, 0xfffe6643,
0xfffe6643, 0xfffe6643, 0xfffe6643, 0xfffe6643, 0xfffe6643, 0xfffe6643, 0xfffe6643, 0xfffe6643,
0xfffe6643, 0xfffe6643, 0xfffe6643, 0xfffe6643, 0xfffe6745, 0x08000000, 0x00000000, 0x00000000,
0x00000000, 0x00000000, 0x08000000, 0xfff2f3f5, 0xfff2f3f5, 0xfff2f3f5, 0xfff2f3f5, 0xfff2f3f5,
0xfff2f3f5, 0xfff2f3f5, 0xfff2f3f5, 0xfff2f3f5, 0xfff2f3f5, 0xfff2f3f5, 0xfff2f3f5, 0xfff2f3f5,
0xfff2f3f5, 0xfff2f3f5, 0xfff2f3f5, 0xffd2d3d5, 0xffd8d9db, 0xfff2f3f5, 0xfff2f3f5, 0xfff2f3f5,
0xfff2f3f5, 0xfff2f3f5, 0xfff2f3f5, 0xfff2f3f5, 0xfff2f3f5, 0xfff2f3f5, 0xfff2f3f5, 0xfff2f3f5,
0xfff2f3f5, 0xfff2f3f5, 0xfff2f3f5, 0xfff4eff0, 0xfff0603f, 0xfffb6442, 0xfffe6643, 0xfffe6643,
0xfffe6643, 0xfffe6643, 0xfffe6643, 0xfffe6643, 0xfff16646, 0xffd35d42, 0xfff86847, 0xfffe6643,
0xfffe6643, 0xfffe6643, 0xfffe6643, 0xfffe6643, 0xfffe6643, 0xfffe6643, 0xfffe6643, 0xfffe6643,
0xfffe6643, 0xfffe6643, 0xfffe6643, 0xfffe6643, 0xfffe6745, 0x08000000, 0x00000000, 0x00000000,
0x00000000, 0x00000000, 0x08000000, 0xfff2f3f5, 0xfff1f3f5, 0xfff1f3f5, 0xfff2f3f5, 0xfff1f3f5,
0xfff2f3f5, 0xfff1f3f5, 0xfff2f3f5, 0xfff2f3f5, 0xfff2f3f5, 0xfff1f3f5, 0xfff1f3f5, 0xfff2f3f5,
0xfff1f3f5, 0xfff2f3f5, 0xfff1f3f5, 0xfff2f3f5, 0xfff2f3f5, 0xfff2f3f5, 0xfff1f3f5, 0xfff1f3f5,
0xfff2f3f5, 0xfff1f3f5, 0xfff2f3f5, 0xfff1f3f5, 0xfff2f3f5, 0xfff2f3f5, 0xfff2f3f5, 0xfff1f3f5,
0xfff1f3f5, 0xfff2f3f5, 0xfff1f3f5, 0xfff3f5f6, 0xffef6b51, 0xfffa6441, 0xfffe6643, 0xfffe6643,
0xfffe6643, 0xfffe6643, 0xfffe6643, 0xfffe6643, 0xfffe6643, 0xfffe6643, 0xfffe6643, 0xfffe6643,
0xfffe6643, 0xfffe6643, 0xfffe6643, 0xfffe6643, 0xfffe6643, 0xfffe6643, 0xfffe6643, 0xfffe6643,
0xfffe6643, 0xfffe6643, 0xfffe6643, 0xfffe6643, 0xfffe6745, 0x08000000, 0x00000000, 0x00000000,
0x00000000, 0x00000000, 0x08000000, 0xfff2f3f5, 0xfff1f3f5, 0xfff1f3f5, 0xfff2f3f5, 0xfff1f3f5,
0xfff1f3f5, 0xfff2f3f5, 0xfff1f3f5, 0xfff1f3f5, 0xfff2f3f5, 0xfff1f3f5, 0xfff1f3f5, 0xfff2f3f5,
0xfff1f3f5, 0xfff1f3f5, 0xfff2f3f5, 0xfff1f3f5, 0xfff1f3f5, 0xfff2f3f5, 0xfff1f3f5, 0xfff1f3f5,
0xfff2f3f5, 0xfff1f3f5, 0xfff1f3f5, 0xfff2f3f5, 0xfff1f3f5, 0xfff1f3f5, 0xfff2f3f5, 0xfff1f3f5,
0xfff1f3f5, 0xfff2f3f5, 0xfff1f3f5, 0xfff2f3f5, 0xfff08f82, 0xfff96441, 0xfffe6643, 0xfffe6643,
0xfffe6643, 0xfffe6643, 0xfffe6643, 0xfffe6643, 0xfffe6643, 0xfffe6643, 0xfffe6643, 0xfffe6643,
0xfffe6643, 0xfffe6643, 0xfffe6643, 0xfffe6643, 0xfffe6643, 0xfffe6643, 0xfffe6643, 0xfffe6643,
0xfffe6643, 0xfffe6643, 0xfffe6643, 0xfffe6643, 0xfffe6745, 0x08000000, 0x00000000, 0x00000000,
0x00000000, 0x00000000, 0x08000000, 0xfff1f2f5, 0xfff1f2f4, 0xfff1f2f5, 0xfff1f2f5, 0xfff1f2f4,
0xfff1f2f5, 0xfff1f2f5, 0xfff1f2f4, 0xfff1f2f5, 0xfff1f2f5, 0xfff1f2f4, 0xfff1f2f5, 0xfff1f2f5,
0xfff1f2f4, 0xfff1f2f5, 0xfff1f2f5, 0xfff1f2f4, 0xfff1f2f5, 0xfff1f2f5, 0xfff1f2f4, 0xfff1f2f5,
0xfff1f2f5, 0xfff1f2f4, 0xfff1f2f5, 0xfff1f2f5, 0xfff1f2f4, 0xfff1f2f5, 0xfff1f2f5, 0xfff1f2f4,
0xfff1f2f5, 0xfff1f2f5, 0xfff1f2f4, 0xfff2f3f5, 0xffef9f95, 0xfff86341, 0xfffd6643, 0xfffe6643,
0xfffe6643, 0xfffe6643, 0xfffe6643, 0xfffe6643, 0xfffe6643, 0xfffe6643, 0xfffe6643, 0xfffe6643,
0xfffe6643, 0xfffe6643, 0xfffe6643, 0xfffe6643, 0xfffe6643, 0xfffe6643, 0xfffe6643, 0xfffe6643,
0xfffe6643, 0xfffe6643, 0xfffe6643, 0xfffe6643, 0xfffe6745, 0x08000000, 0x00000000, 0x00000000,
0x00000000, 0x00000000, 0x08000000, 0xfff1f2f5, 0xfff1f2f5, 0xfff1f2f5, 0xfff1f2f5, 0xfff1f2f5,
0xfff1f2f5, 0xfff1f2f5, 0xfff1f2f5, 0xfff1f2f5, 0xfff1f2f5, 0xfff1f2f5, 0xfff1f2f5, 0xfff1f2f5,
0xfff1f2f5, 0xfff1f2f5, 0xfff1f2f5, 0xfff1f2f5, 0xfff1f2f5, 0xfff1f2f5, 0xfff1f2f5, 0xfff1f2f5,
0xfff1f2f5, 0xfff1f2f5, 0xfff1f2f5, 0xfff1f2f5, 0xfff1f2f5, 0xfff1f2f5, 0xfff1f2f5, 0xfff1f2f5,
0xfff1f2f5, 0xfff1f2f5, 0xfff1f2f5, 0xfff1f2f5, 0xfff0b1ab, 0xfff66340, 0xfffd6542, 0xfffe6643,
0xfffe6643, 0xfffe6643, 0xfffe6643, 0xfffe6643, 0xfffe6643, 0xfffe6643, 0xfffe6643, 0xfffe6643,
0xfffe6643, 0xfffe6643, 0xfffe6643, 0xfffe6643, 0xfffe6643, 0xfffe6643, 0xfffe6643, 0xfffe6643,
0xfffe6643, 0xfffe6643, 0xfffe6643, 0xfffe6643, 0xfffe6745, 0x08000000, 0x00000000, 0x00000000,
0x00000000, 0x00000000, 0x08000000, 0xfff1f2f4, 0xfff1f2f4, 0xfff1f2f4, 0xfff1f2f4, 0xfff1f2f4,
0xfff1f2f4, 0xfff1f2f4, 0xfff1f2f4, 0xfff1f2f4, 0xfff1f2f4, 0xfff1f2f4, 0xfff1f2f4, 0xfff1f2f4,
0xfff1f2f4, 0xfff1f2f4, 0xfff1f2f4, 0xfff1f2f4, 0xfff1f2f4, 0xfff1f2f4, 0xfff1f2f4, 0xfff1f2f4,
0xfff1f2f4, 0xfff1f2f4, 0xfff1f2f4, 0xfff1f2f4, 0xfff1f2f4, 0xfff1f2f4, 0xfff1f2f4, 0xfff1f2f4,
0xfff1f2f4, 0xfff1f2f4, 0xfff1f2f4, 0xfff1f2f4, 0xffefbab5, 0xfff56240, 0xfffd6542, 0xfffe6643,
0xfffe6643, 0xfffe6643, 0xfffe6643, 0xfffe6643, 0xfffe6643, 0xfffe6643, 0xfffe6643, 0xfffe6643,
0xfffe6643, 0xfffe6643, 0xfffe6643, 0xfffe6643, 0xfffe6643, 0xfffe6643, 0xfffe6643, 0xfffe6643,
0xfffe6643, 0xfffe6643, 0xfffe6643, 0xfffe6643, 0xfffe6745, 0x08000000, 0x00000000, 0x00000000,
0x00000000, 0x00000000, 0x08000000, 0xfff1f2f4, 0xfff1f1f4, 0xfff1f2f4, 0xfff1f2f4, 0xfff1f1f4,
0xfff1f2f4, 0xfff1f2f4, 0xfff1f2f4, 0xfff1f2f4, 0xfff1f2f4, 0xfff1f1f4, 0xfff1f2f4, 0xfff1f2f4,
0xfff1f1f4, 0xfff1f2f4, 0xfff1f2f4, 0xfff1f2f4, 0xfff1f2f4, 0xfff1f2f4, 0xfff1f1f4, 0xfff1f2f4,
0xfff1f2f4, 0xfff1f1f4, 0xfff1f2f4, 0xfff1f2f4, 0xfff1f2f4, 0xfff1f2f4, 0xfff1f2f4, 0xfff1f1f4,
0xfff1f2f4, 0xfff1f2f4, 0xfff1f1f4, 0xfff1f2f4, 0xfeedc8c6, 0xfdeb5a41, 0xfdf35e43, 0xfdf55f44,
0xfdf55f44, 0xfdf55f44, 0xfdf55f44, 0xfdf55f44, 0xfdf55f44, 0xfdf55f44, 0xfdf55f44, 0xfdf55f44,
0xfdf55f44, 0xfdf55f44, 0xfdf55f44, 0xfdf55f44, 0xfdf55f44, 0xfdf55f44, 0xfdf55f44, 0xfdf55f44,
0xfdf55f44, 0xfdf55f44, 0xfdf55f44, 0xfdf55f44, 0xfef56046, 0x08000000, 0x00000000, 0x00000000,
0x00000000, 0x00000000, 0x08000000, 0xfff1f1f4, 0xfff1f1f3, 0xfff1f1f4, 0xfff1f1f3, 0xfff1f1f4,
0xfff1f1f4, 0xfff1f1f4, 0xfff1f1f3, 0xfff1f1f4, 0xfff1f1f4, 0xfff1f1f3, 0xfff1f1f4, 0xfff1f1f3,
0xfff1f1f4, 0xfff1f1f4, 0xfff1f1f4, 0xfff1f1f3, 0xfff1f1f4, 0xfff1f1f4, 0xfff1f1f3, 0xfff1f1f4,
0xfff1f1f3, 0xfff1f1f4, 0xfff1f1f4, 0xfff1f1f4, 0xfff1f1f3, 0xfff1f1f4, 0xfff1f1f4, 0xfff1f1f3,
0xfff1f1f4, 0xfff1f1f3, 0xfff1f1f4, 0xfff1f1f4, 0xffe0c5c8, 0xffc6313f, 0xffce3341, 0xffd03442,
0xffd03442, 0xffd03442, 0xffd03442, 0xffd03442, 0xffd03442, 0xffd03442, 0xffd03442, 0xffd03442,
0xffd03442, 0xffd03442, 0xffd03442, 0xffd03442, 0xffd03442, 0xffd03442, 0xffd03442, 0xffd03442,
0xffd03442, 0xffd03442, 0xffd03442, 0xffd03442, 0xffd03644, 0x08000000, 0x00000000, 0x00000000,
0x00000000, 0x00000000, 0x08000000, 0xfff1f1f3, 0xfff1f1f3, 0xfff1f1f3, 0xfff1f1f3, 0xfff1f1f3,
0xfff1f1f3, 0xfff1f1f3, 0xfff1f1f3, 0xfff1f1f3, 0xfff1f1f3, 0xfff1f1f3, 0xfff1f1f3, 0xfff1f1f3,
0xfff1f1f3, 0xfff1f1f3, 0xfff1f1f3, 0xfff1f1f3, 0xfff1f1f3, 0xfff1f1f3, 0xfff1f1f3, 0xfff1f1f3,
0xfff1f1f3, 0xfff1f1f3, 0xfff1f1f3, 0xfff1f1f3, 0xfff1f1f3, 0xfff1f1f3, 0xfff1f1f3, 0xfff1f1f3,
0xfff1f1f3, 0xfff1f1f3, 0xfff1f1f3, 0xfff1f1f3, 0xffe5cfd1, 0xffc5313e, 0xffcd3341, 0xffd03442,
0xffd03442, 0xffd03442, 0xffd03442, 0xffd03442, 0xffd03442, 0xffd03442, 0xffd03442, 0xffd03442,
0xffd03442, 0xffd03442, 0xffd03442, 0xffd03442, 0xffd03442, 0xffd03442, 0xffd03442, 0xffd03442,
0xffd03442, 0xffd03442, 0xffd03442, 0xffd03442, 0xffd03644, 0x08000000, 0x00000000, 0x00000000,
0x00000000, 0x00000000, 0x08000000, 0xfff1f1f3, 0xfff1f1f3, 0xfff1f1f3, 0xfff0f1f3, 0xfff1f1f3,
0xfff1f1f3, 0xfff1f1f3, 0xfff1f1f3, 0xfff0f1f3, 0xfff1f1f3, 0xfff1f1f3, 0xfff1f1f3, 0xfff0f1f3,
0xfff1f1f3, 0xfff1f1f3, 0xfff1f1f3, 0xfff1f1f3, 0xfff0f1f3, 0xfff1f1f3, 0xfff1f1f3, 0xfff1f1f3,
0xfff0f1f3, 0xfff1f1f3, 0xfff1f1f3, 0xfff1f1f3, 0xfff1f1f3, 0xfff0f1f3, 0xfff1f1f3, 0xfff1f1f3,
0xfff1f1f3, 0xfff0f1f3, 0xfff1f1f3, 0xfff1f1f3, 0xffe5d0d2, 0xffc4313e, 0xffcd3341, 0xffd03442,
0xffd03442, 0xffd03442, 0xffd03442, 0xffd03442, 0xffd03442, 0xffd03442, 0xffd03442, 0xffd03442,
0xffd03442, 0xffd03442, 0xffd03442, 0xffd03442, 0xffd03442, 0xffd03442, 0xffd03442, 0xffd03442,
0xffd03442, 0xffd03442, 0xffd03442, 0xffd03442, 0xffd03644, 0x08000000, 0x00000000, 0x00000000,
0x00000000, 0x00000000, 0x08000000, 0xfff0f1f2, 0xfff0f1f2, 0xfff0f1f2, 0xfff0f1f2, 0xfff0f1f2,
0xfff0f1f2, 0xfff0f1f2, 0xfff0f1f2, 0xfff0f1f2, 0xfff0f1f2, 0xfff0f1f2, 0xfff0f1f2, 0xfff0f1f2,
0xfff0f1f2, 0xfff0f1f2, 0xfff0f1f2, 0xfff0f1f2, 0xfff0f1f2, 0xfff0f1f2, 0xfff0f1f2, 0xfff0f1f2,
0xfff0f1f2, 0xfff0f1f2, 0xfff0f1f2, 0xfff0f1f2, 0xfff0f1f2, 0xfff0f1f2, 0xfff0f1f2, 0xfff0f1f2,
0xfff0f1f2, 0xfff0f1f2, 0xfff0f1f2, 0xfff1f2f3, 0xffd09da0, 0xffc4313e, 0xffcd3341, 0xffd03442,
0xffd03442, 0xffd03442, 0xffd03442, 0xffd03442, 0xffd03442, 0xffd03442, 0xffd03442, 0xffd03442,
0xffd03442, 0xffd03442, 0xffd03442, 0xffd03442, 0xffd03442, 0xffd03442, 0xffd03442, 0xffd03442,
0xffd03442, 0xffd03442, 0xffd03442, 0xffd03442, 0xfed03644, 0x08000000, 0x00000000, 0x00000000,
0x00000000, 0x00000000, 0x08000000, 0xfff0f1f2, 0xfff0f1f2, 0xfff0f1f2, 0xfff0f1f2, 0xfff0f1f2,
0xfff0f1f2, 0xfff0f1f2, 0xfff0f1f2, 0xfff0f1f2, 0xfff0f1f2, 0xfff0f1f2, 0xfff0f1f2, 0xfff0f1f2,
0xfff0f1f2, 0xfff0f1f2, 0xfff0f1f2, 0xfff0f1f2, 0xfff0f1f2, 0xfff0f1f2, 0xfff0f1f2, 0xfff0f1f2,
0xfff0f1f2, 0xfff0f1f2, 0xfff0f1f2, 0xfff1f2f3, 0xffc9a8aa, 0xffbb898c, 0xffb98587, 0xffb88386,
0xffb88386, 0xffb88285, 0xffb87e81, 0xffb6696e, 0xffb62d39, 0xffc5313e, 0xffcd3341, 0xffd03442,
0xffd03442, 0xffd03442, 0xffd03442, 0xffd03442, 0xffd03442, 0xffd03442, 0xffd03442, 0xffd03442,
0xffd03442, 0xffd03442, 0xffd03442, 0xffd03442, 0xffd03442, 0xffd03442, 0xffd03442, 0xffd03442,
0xffd03442, 0xffd03442, 0xffd03442, 0xffd03442, 0xfcd03644, 0x07000000, 0x00000000, 0x00000000,
0x00000000, 0x00000000, 0x08000000, 0xfff0f1f2, 0xfff0f1f2, 0xfff0f1f2, 0xfff0f1f2, 0xfff0f1f2,
0xfff0f1f2, 0xfff0f1f2, 0xfff0f1f2, 0xfff0f1f2, 0xfff0f1f2, 0xfff0f1f2, 0xfff0f1f2, 0xfff0f1f2,
0xfff0f1f2, 0xfff0f1f2, 0xfff0f1f2, 0xfff0f1f2, 0xfff0f1f2, 0xfff0f1f2, 0xfff0f1f2, 0xfff0f1f2,
0xfff0f1f2, 0xfff0f1f2, 0xfff0f1f2, 0xfff2f1f2, 0xffa42934, 0xffa72935, 0xffa82a35, 0xffa82a35,
0xffa82a35, 0xffa82a35, 0xffaa2a36, 0xffb12c38, 0xffbc2f3b, 0xffc8313f, 0xffce3341, 0xffd03442,
0xffd03442, 0xffd03442, 0xffd03442, 0xffd03442, 0xffd03442, 0xffd03442, 0xffd03442, 0xffd03442,
0xffd03442, 0xffd03442, 0xffd03442, 0xffd03442, 0xffd03442, 0xffd03442, 0xffd03442, 0xffd03442,
0xffd03442, 0xffd03442, 0xffd03442, 0xffd03442, 0xfbd03644, 0x07000000, 0x00000000, 0x00000000,
0x00000000, 0x00000000, 0x08000000, 0xfff0f0f1, 0xffeff0f1, 0xffeff0f1, 0xfff0f0f1, 0xffeff0f1,
0xfff0f0f1, 0xffeff0f1, 0xfff0f0f1, 0xffeff0f1, 0xffeff0f1, 0xffeff0f1, 0xffeff0f1, 0xfff0f0f1,
0xffeff0f1, 0xfff0f0f1, 0xffeff0f1, 0xfff0f0f1, 0xffeff0f1, 0xffeff0f1, 0xffeff0f1, 0xffeff0f1,
0xfff0f0f1, 0xffeff0f1, 0xfff0f0f1, 0xfff2f2f3, 0xffae2b37, 0xffb52d39, 0xffb82e3a, 0xffb82e3a,
0xffb82e3a, 0xffb82e3a, 0xffba2e3b, 0xffbe2f3c, 0xffc5313e, 0xffcb3240, 0xffcf3341, 0xffd03442,
0xffd03442, 0xffd03442, 0xffd03442, 0xffd03442, 0xffd03442, 0xffd03442, 0xffd03442, 0xffd03442,
0xffd03442, 0xffd03442, 0xffd03442, 0xffd03442, 0xffd03442, 0xffd03442, 0xffd03442, 0xffd03442,
0xffd03442, 0xffd03442, 0xffd03442, 0xffd03442, 0xfad03644, 0x07000000, 0x00000000, 0x00000000,
0x00000000, 0x00000000, 0x08000000, 0xffeff0f1, 0xffeff0f1, 0xffeff0f1, 0xffeff0f1, 0xffeff0f1,
0xffeff0f1, 0xffeff0f1, 0xffeff0f1, 0xffeff0f1, 0xffeff0f1, 0xffeff0f1, 0xffeff0f1, 0xffe4e5e6,
0xffedeeef, 0xffeff0f1, 0xffeff0f1, 0xffeff0f1, 0xffeff0f1, 0xffeff0f1, 0xffeff0f1, 0xffeff0f1,
0xffeff0f1, 0xffeff0f1, 0xffeff0f1, 0xfff2f3f4, 0xffb92f3b, 0xffc3303d, 0xffc6313f, 0xffc7313f,
0xffc7313f, 0xffc7313f, 0xffc8313f, 0xffca323f, 0xffcc3340, 0xffce3341, 0xffd03442, 0xffd03442,
0xffd03442, 0xffd03442, 0xffd03442, 0xffd03442, 0xffd03442, 0xffd03442, 0xffd03442, 0xffd03442,
0xffd03442, 0xffcf3442, 0xffd03442, 0xffd03442, 0xffd03442, 0xffd03442, 0xffd03442, 0xffd03442,
0xffd03442, 0xffd03442, 0xffd03442, 0xffd03442, 0xf8d03643, 0x07000000, 0x00000000, 0x00000000,
0x00000000, 0x00000000, 0x08000000, 0xffeff0f1, 0xffeff0f1, 0xffeff0f1, 0xffeff0f1, 0xffeff0f1,
0xffeff0f1, 0xffeff0f1, 0xffeff0f1, 0xffeff0f1, 0xffeff0f1, 0xffeff0f1, 0xffdadbdc, 0xff626363,
0xff919292, 0xffebeced, 0xffeff0f1, 0xffeff0f1, 0xffeff0f1, 0xffeff0f1, 0xffeff0f1, 0xffeff0f1,
0xffeff0f1, 0xffeff0f1, 0xffeff0f1, 0xfff2f2f4, 0xffbe3241, 0xffc83245, 0xffcc3347, 0xffcc3347,
0xffcc3347, 0xffcc3347, 0xffcd3348, 0xffcd3448, 0xffcd3448, 0xffce3448, 0xffce3448, 0xffce3448,
0xffce3448, 0xffce3448, 0xffce3448, 0xffce3448, 0xffce3448, 0xffce3448, 0xffce3448, 0xffce3446,
0xfe892439, 0xfe461832, 0xffb83145, 0xffce3549, 0xffce3549, 0xffce3549, 0xffce3549, 0xffce3549,
0xffce3549, 0xffce3549, 0xffce3549, 0xffce3549, 0xf7cd384c, 0x07000000, 0x00000000, 0x00000000,
0x00000000, 0x00000000, 0x08000000, 0xffeff0f1, 0xffeff0f1, 0xffeff0f1, 0xffeff0f1, 0xffefeff1,
0xffeff0f1, 0xffeff0f1, 0xffeff0f1, 0xffefeff1, 0xffeff0f1, 0xffeff0f1, 0xffe3e3e5, 0xff6a6b6b,
0xff5f5f60, 0xff949495, 0xffeaebec, 0xffeff0f1, 0xffefeff1, 0xffeff0f1, 0xffeff0f1, 0xffeff0f1,
0xffeff0f1, 0xffefeff1, 0xffeff0f1, 0xfff2f2f3, 0xff933e9a, 0xff953d98, 0xff953d98, 0xff953d98,
0xff953d98, 0xff953d98, 0xff953d98, 0xff953d98, 0xff953d98, 0xff953d98, 0xff953d98, 0xff953d98,
0xff953d98, 0xff953d98, 0xff953d98, 0xff953d98, 0xff953d98, 0xff953d98, 0xff943d99, 0xfe672d75,
0xfe36193d, 0xfe391c41, 0xff823f8f, 0xff953f99, 0xff953f99, 0xff953f99, 0xff953f99, 0xff953f99,
0xff953f99, 0xff953f99, 0xff953f99, 0xff953f99, 0xfd92449d, 0x07000000, 0x00000000, 0x00000000,
0x00000000, 0x00000000, 0x08000000, 0xffefeff1, 0xffeeeff0, 0xffefeff1, 0xffefeff1, 0xffefeff1,
0xffefeff1, 0xffefeff1, 0xffefeff1, 0xffefeff1, 0xffefeff1, 0xffeeeff0, 0xffefeff1, 0xffd6d6d7,
0xff686969, 0xff5f5f60, 0xff878788, 0xffe2e2e4, 0xffefeff1, 0xffefeff1, 0xffeeeff0, 0xffefeff1,
0xffefeff1, 0xffefeff1, 0xffefeff1, 0xfff2f2f3, 0xff953d98, 0xff963d97, 0xff963d97, 0xff963d97,
0xff963d97, 0xff963d97, 0xff963d97, 0xff963d97, 0xff963d97, 0xff963d97, 0xff963d97, 0xff963d97,
0xff963d97, 0xff963d97, 0xff963d97, 0xff963d97, 0xff963d97, 0xff933c96, 0xff632b70, 0xfe2e1b49,
0xfe321e4c, 0xff783a85, 0xff963e98, 0xff963d97, 0xff963d97, 0xff963d97, 0xff963d97, 0xff963d97,
0xff963d97, 0xff963d97, 0xff963d97, 0xff963d97, 0xfe944099, 0x07000000, 0x00000000, 0x00000000,
0x00000000, 0x00000000, 0x08000000, 0xffeeeff0, 0xffeeeff0, 0xffeeeff0, 0xffeeeff0, 0xffeeeff0,
0xffeeeff0, 0xffeeeff0, 0xffeeeff0, 0xffeeeff0, 0xffeeeff0, 0xffeeeff0, 0xffeeeff0, 0xffeeeff0,
0xffd6d7d8, 0xff6d6e6e, 0xff5f5f60, 0xff6c6c6d, 0xffc4c4c5, 0xffeeeef0, 0xffeeeff0, 0xffeeeff0,
0xffeeeff0, 0xffeeeff0, 0xffeeeff0, 0xfff0eff1, 0xff953d98, 0xff963d97, 0xff963d97, 0xff963d97,
0xff963d97, 0xff963d97, 0xff963d97, 0xff963d97, 0xff963d97, 0xff963d97, 0xff963d97, 0xff963d97,
0xff963d97, 0xff963d97, 0xff963d97, 0xff963d97, 0xff86388e, 0xff4d2564, 0xff281c4f, 0xff332055,
0xff7a3b89, 0xff963e98, 0xff963d97, 0xff963d97, 0xff963d97, 0xff963d97, 0xff963d97, 0xff963d97,
0xff963d97, 0xff963d97, 0xff963d97, 0xff963d97, 0xfe944099, 0x07000000, 0x00000000, 0x00000000,
0x00000000, 0x00000000, 0x08000000, 0xfdeeeff0, 0xffeeeff0, 0xffeeeff0, 0xffeeeff0, 0xffeeeff0,
0xffeeeff0, 0xffeeeff0, 0xffeeeff0, 0xffeeeff0, 0xffeeeff0, 0xffeeeff0, 0xffeeeff0, 0xffeeeff0,
0xffeeeff0, 0xffe0e1e2, 0xff868787, 0xff5f5f60, 0xff5f5f60, 0xff888889, 0xffcdcdce, 0xffedeeef,
0xffeeeff0, 0xffeeeff0, 0xffeeeff0, 0xffeae8ed, 0xff963d97, 0xff963d97, 0xff963d97, 0xff963d97,
0xff963d97, 0xff963d97, 0xff963d97, 0xff963d97, 0xff963d97, 0xff963d97, 0xff963d97, 0xff963d97,
0xff963d97, 0xff963d97, 0xff8a3991, 0xff632c74, 0xff2e1d54, 0xff261c51, 0xff442863, 0xff843e90,
0xff963e97, 0xff963d97, 0xff963d97, 0xff963d97, 0xff963d97, 0xff963d97, 0xff963d97, 0xff963d97,
0xff963d97, 0xff963d97, 0xff963d97, 0xff963d97, 0xfd953f99, 0x07000000, 0x00000000, 0x00000000,
0x00000000, 0x00000000, 0x07000000, 0xf9edeeef, 0xffeeeeef, 0xffeeeef0, 0xffeeeef0, 0xffeeeeef,
0xffeeeef0, 0xffeeeeef, 0xffeeeeef, 0xffeeeeef, 0xffeeeeef, 0xffeeeeef, 0xffeeeef0, 0xffeeeef0,
0xffeeeeef, 0xffeeeef0, 0xffebeced, 0xffb7b7b8, 0xff666667, 0xff5f5f5f, 0xff5f5f5f, 0xff7a7a7a,
0xffb4b5b6, 0xffd6d6d8, 0xffededef, 0xffe2dde5, 0xff963d97, 0xff963d97, 0xff963d97, 0xff963d97,
0xff963d97, 0xff963d97, 0xff963d97, 0xff963d97, 0xff963d97, 0xff963d97, 0xff953d98, 0xff8e3b93,
0xff7a3485, 0xff58296d, 0xff2e1d55, 0xff261c52, 0xff302057, 0xff67357c, 0xff904097, 0xff963d97,
0xff963d97, 0xff963d97, 0xff963d97, 0xff963d97, 0xff963d97, 0xff963d97, 0xff963d97, 0xff963d97,
0xff963d97, 0xff963d97, 0xff963d97, 0xff963d97, 0xfb93429a, 0x06000000, 0x00000000, 0x00000000,
0x00000000, 0x00000000, 0x07000000, 0xf0ececed, 0xffeeeeef, 0xffeeeeef, 0xffeeeeef, 0xffeeeeef,
0xffeeeeef, 0xffeeeeef, 0xffeeeeef, 0xffeeeeef, 0xffeeeeef, 0xffeeeeef, 0xffeeeeef, 0xffeeeeef,
0xffeeeeef, 0xffeeeeef, 0xffeeeeef, 0xffeeeeef, 0xffe2e2e3, 0xffababab, 0xff676767, 0xff5f5f5f,
0xff5e5e5f, 0xff5a5a5e, 0xff5b5a64, 0xff7f7888, 0xff71377d, 0xff7a3584, 0xff7f3688, 0xff82368a,
0xff85378b, 0xff83378a, 0xff803688, 0xff7a3485, 0xff70307d, 0xff612b73, 0xff492464, 0xff2f1e56,
0xff261c52, 0xff261c52, 0xff332159, 0xff623378, 0xff893f93, 0xff963e98, 0xff963d97, 0xff963d97,
0xff963d97, 0xff963d97, 0xff963d97, 0xff963d97, 0xff963d97, 0xff963d97, 0xff963d97, 0xff963d97,
0xff963d97, 0xff963d97, 0xff963d97, 0xff963d97, 0xe7934199, 0x06000000, 0x00000000, 0x00000000,
0x00000000, 0x00000000, 0x06000000, 0xdfebebec, 0xffeeeeef, 0xffeeeeef, 0xffeeeeef, 0xffeeeeef,
0xffeeeeef, 0xffeeeeef, 0xffedeeef, 0xffeeeeef, 0xffeeeeef, 0xffeeeeef, 0xffeeeeef, 0xffeeeeef,
0xffeeeeef, 0xffeeeeef, 0xffeeeeef, 0xffedeeef, 0xffeeeeef, 0xffeeeeef, 0xffe7e7e7, 0xffc0c0c1,
0xff939394, 0xff5b5b5f, 0xff514f5b, 0xff444057, 0xff362f54, 0xff2c2352, 0xff281e51, 0xff271c52,
0xff281c53, 0xff271c53, 0xff271c52, 0xff261c52, 0xff261c52, 0xff261c52, 0xff281d53, 0xff35225a,
0xff552e6f, 0xff753985, 0xff8e4096, 0xff963e98, 0xff963d97, 0xff963d97, 0xff963d97, 0xff963d97,
0xff963d97, 0xff963d97, 0xff963d97, 0xff963d97, 0xff963d97, 0xff963d97, 0xff963d97, 0xff963d97,
0xff963d97, 0xff963d97, 0xff963d97, 0xff963d97, 0xdb924098, 0x05000000, 0x00000000, 0x00000000,
0x00000000, 0x00000000, 0x04000000, 0xd0eaeaeb, 0xffeeeeee, 0xffededee, 0xffeeeeee, 0xffededee,
0xffeeeeee, 0xffededee, 0xffeeeeee, 0xffededee, 0xffededee, 0xffeeeeee, 0xffededee, 0xffeeeeee,
0xffededee, 0xffeeeeee, 0xffededee, 0xffeeeeee, 0xffededee, 0xffededee, 0xffeeeeee, 0xffededee,
0xffeeeeee, 0xffe8e8e9, 0xffd1d1d3, 0xffa196ad, 0xff6f3d80, 0xff623577, 0xff583071, 0xff532f70,
0xff4f2b6b, 0xff532e6f, 0xff572f71, 0xff5e3175, 0xff6a357e, 0xff773986, 0xff853d8f, 0xff914098,
0xff953e98, 0xff963d97, 0xff963d97, 0xff963d97, 0xff963d97, 0xff963d97, 0xff963d97, 0xff963d97,
0xff963d97, 0xff963d97, 0xff963d97, 0xff963d97, 0xff963d97, 0xff963d97, 0xff963d97, 0xff963d97,
0xff963d97, 0xff963d97, 0xff963d97, 0xff963d97, 0xcf914199, 0x04000000, 0x00000000, 0x00000000,
0x00000000, 0x00000000, 0x03000000, 0xace7e7e8, 0xffeeeeee, 0xffeeeeee, 0xffeeeeee, 0xffeeeeee,
0xffeeeeee, 0xffeeeeee, 0xffeeeeee, 0xffeeeeee, 0xffeeeeee, 0xffeeeeee, 0xffeeeeee, 0xffeeeeee,
0xffeeeeee, 0xffeeeeee, 0xffeeeeee, 0xffeeeeee, 0xffeeeeee, 0xffeeeeee, 0xffeeeeee, 0xffeeeeee,
0xffeeeeee, 0xffeeeeee, 0xffeeeeee, 0xffbfa8c6, 0xff953f98, 0xff953f98, 0xff954098, 0xff954099,
0xff954099, 0xff954099, 0xff954098, 0xff954098, 0xff953f98, 0xff953f98, 0xff953f98, 0xff953f98,
0xff953f98, 0xff953f98, 0xff953f98, 0xff953f98, 0xff953f98, 0xff953f98, 0xff953f98, 0xff953f98,
0xff953f98, 0xff953f98, 0xff953f98, 0xff953f98, 0xff953f98, 0xff953f98, 0xff953f98, 0xff953f98,
0xff953f98, 0xff953f98, 0xff953f98, 0xff953f98, 0xa78e4398, 0x02000000, 0x00000000, 0x00000000,
0x00000000, 0x00000000, 0x01000000, 0x7ee2e2e3, 0xffeeeeee, 0xffededee, 0xffeeeeee, 0xffededed,
0xffeeeeee, 0xffeeeeee, 0xffededed, 0xffeeeeee, 0xffeeeeee, 0xffeeeeee, 0xffededee, 0xffeeeeee,
0xffededed, 0xffeeeeee, 0xffeeeeee, 0xffededed, 0xffeeeeee, 0xffeeeeee, 0xffeeeeee, 0xffededee,
0xffeeeeee, 0xffededed, 0xffeeeeee, 0xff8aacd9, 0xff4f8bcd, 0xff518acc, 0xff5289cc, 0xff5289cc,
0xff5289cc, 0xff5289cc, 0xff5289cc, 0xff5289cc, 0xff5289cc, 0xff5289cc, 0xff5289cc, 0xff5289cc,
0xff5289cc, 0xff5289cc, 0xff5289cc, 0xff5289cc, 0xff5289cc, 0xff5289cc, 0xff5289cc, 0xff5289cc,
0xff5289cc, 0xff5289cc, 0xff5289cc, 0xff5289cc, 0xff5289cc, 0xff5289cc, 0xff5289cc, 0xff5289cc,
0xff5289cc, 0xff5289cc, 0xff5289cc, 0xff5289cc, 0x7a5b7abd, 0x01000000, 0x00000000, 0x00000000,
0x00000000, 0x00000000, 0x00000000, 0x38cbcbcb, 0xffedeeee, 0xffedeeee, 0xffeeeeee, 0xffedeeee,
0xffeeeeee, 0xffedeeee, 0xffedeeee, 0xffedeeee, 0xffedeeee, 0xffedeeee, 0xffedeeee, 0xffeeeeee,
0xffedeeee, 0xffeeeeee, 0xffedeeee, 0xffedeeee, 0xffedeeee, 0xffedeeee, 0xffedeeee, 0xffedeeee,
0xffeeeeee, 0xffedeeee, 0xfff0f0f0, 0xff3da2dc, 0xff009ddc, 0xff009ddc, 0xff009ddc, 0xff009ddc,
0xff009ddc, 0xff009ddc, 0xff009ddc, 0xff009ddc, 0xff009ddc, 0xff009ddc, 0xff009ddc, 0xff009ddc,
0xff009ddc, 0xff009ddc, 0xff009ddc, 0xff009ddc, 0xff009ddc, 0xff009ddc, 0xff009ddc, 0xff009ddc,
0xff009ddc, 0xff009ddc, 0xff009ddc, 0xff009ddc, 0xff009ddc, 0xff009ddc, 0xff009ddc, 0xff009ddc,
0xff009ddc, 0xff009ddc, 0xff009ddc, 0xff129bdb, 0x343579b4, 0x00000000, 0x00000000, 0x00000000,
0x00000000, 0x00000000, 0x00000000, 0x0b000000, 0xd3e9e9e9, 0xffededee, 0xffededee, 0xffededee,
0xffededee, 0xffededee, 0xffededee, 0xffededee, 0xffededee, 0xffededee, 0xffededee, 0xffededee,
0xffededee, 0xffededee, 0xffededee, 0xffededee, 0xffededee, 0xffededee, 0xffededee, 0xffededee,
0xffededee, 0xffededee, 0xffe1e7ed, 0xff009ddc, 0xff009ddc, 0xff009ddc, 0xff009ddc, 0xff009ddc,
0xff009ddc, 0xff009ddc, 0xff009ddc, 0xff009ddc, 0xff009ddc, 0xff009ddc, 0xff009ddc, 0xff009ddc,
0xff009ddc, 0xff009ddc, 0xff009ddc, 0xff009ddc, 0xff009ddc, 0xff009ddc, 0xff009ddc, 0xff009ddc,
0xff009ddc, 0xff009ddc, 0xff009ddc, 0xff009ddc, 0xff009ddc, 0xff009ddc, 0xff009ddc, 0xff009ddc,
0xff009ddc, 0xff009ddc, 0xff009ddc, 0xcf2295d4, 0x0a000000, 0x00000000, 0x00000000, 0x00000000,
0x00000000, 0x00000000, 0x00000000, 0x03000000, 0x62d5d5d5, 0xffededed, 0xffededed, 0xffededed,
0xffededed, 0xffededed, 0xffededed, 0xffededed, 0xffededed, 0xffededed, 0xffededed, 0xffededed,
0xffededed, 0xffededed, 0xffededed, 0xffededed, 0xffededed, 0xffededed, 0xffededed, 0xffededed,
0xffededed, 0xffededed, 0xffc7d8ea, 0xff009ddc, 0xff009ddc, 0xff009ddc, 0xff009ddc, 0xff009ddc,
0xff009ddc, 0xff009ddc, 0xff009ddc, 0xff009ddc, 0xff009ddc, 0xff009ddc, 0xff009ddc, 0xff009ddc,
0xff009ddc, 0xff009ddc, 0xff009ddc, 0xff009ddc, 0xff009ddc, 0xff009ddc, 0xff009ddc, 0xff009ddc,
0xff009ddc, 0xff009ddc, 0xff009ddc, 0xff009ddc, 0xff009ddc, 0xff009ddc, 0xff009ddc, 0xff009ddc,
0xff009ddc, 0xff009ddc, 0xff099cdb, 0x5c2884c0, 0x02000000, 0x00000000, 0x00000000, 0x00000000,
0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x0e000000, 0xc0e5e5e6, 0xffededed, 0xffededed,
0xffededed, 0xffededed, 0xffededed, 0xffededed, 0xffededed, 0xffededed, 0xffededed, 0xffededed,
0xffededed, 0xffededed, 0xffededed, 0xffededed, 0xffededed, 0xffededed, 0xffededed, 0xffededed,
0xffededed, 0xffededed, 0xffa5c6e5, 0xff009ddc, 0xff009ddc, 0xff009ddc, 0xff009ddc, 0xff009ddc,
0xff009ddc, 0xff009ddc, 0xff009ddc, 0xff009ddc, 0xff009ddc, 0xff009ddc, 0xff009ddc, 0xff009ddc,
0xff009ddc, 0xff009ddc, 0xff009ddc, 0xff009ddc, 0xff009ddc, 0xff009ddc, 0xff009ddc, 0xff009ddc,
0xff009ddc, 0xff009ddc, 0xff009ddc, 0xff009ddc, 0xff009ddc, 0xff009ddc, 0xff009ddc, 0xff009ddc,
0xff009ddc, 0xff019ddc, 0xbd2093d2, 0x0d000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x02000000, 0x2ca4a4a4, 0xe5eaeaea, 0xffececec,
0xffededed, 0xffededed, 0xffececec, 0xffeceded, 0xffececed, 0xffececec, 0xffececed, 0xffeceded,
0xffececec, 0xffededed, 0xffededed, 0xffececec, 0xffeceded, 0xffececed, 0xffececec, 0xffececed,
0xffeceded, 0xffeeeeee, 0xff66acdf, 0xff009ddc, 0xff009ddc, 0xff009ddc, 0xff009ddc, 0xff009ddc,
0xff009ddc, 0xff009ddc, 0xff009ddc, 0xff009ddc, 0xff009ddc, 0xff009ddc, 0xff009ddc, 0xff009ddc,
0xff009ddc, 0xff009ddc, 0xff009ddc, 0xff009ddc, 0xff009ddc, 0xff009ddc, 0xff009ddc, 0xff009ddc,
0xff009ddc, 0xff009ddc, 0xff009ddc, 0xff009ddc, 0xff009ddc, 0xff009ddc, 0xff009ddc, 0xff009ddc,
0xff009ddc, 0xe41798d8, 0x29216392, 0x02000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x06000000, 0x3cb1b1b1, 0xe6eaeaea,
0xffececec, 0xffececec, 0xffececec, 0xffececec, 0xffececec, 0xffececec, 0xffececec, 0xffececec,
0xffececec, 0xffececec, 0xffececec, 0xffececec, 0xffececec, 0xffececec, 0xffececec, 0xffececec,
0xffececec, 0xffe3e8ed, 0xff009ddc, 0xff009ddc, 0xff009ddc, 0xff009ddc, 0xff009ddc, 0xff009ddc,
0xff009ddc, 0xff009ddc, 0xff009ddc, 0xff009ddc, 0xff009ddc, 0xff009ddc, 0xff009ddc, 0xff009ddc,
0xff009ddc, 0xff009ddc, 0xff009ddc, 0xff009ddc, 0xff009ddc, 0xff009ddc, 0xff009ddc, 0xff009ddc,
0xff009ddc, 0xff009ddc, 0xff009ddc, 0xff009ddc, 0xff009ddc, 0xff009ddc, 0xff009ddc, 0xff009ddc,
0xe51598d8, 0x391d6ea2, 0x05000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x07000000, 0x339f9f9f,
0xc4e4e4e4, 0xffededed, 0xffececec, 0xffececec, 0xffececec, 0xffececec, 0xffececec, 0xffececec,
0xffececec, 0xffececec, 0xffededed, 0xffececec, 0xffececec, 0xffececec, 0xffececec, 0xffececec,
0xffececec, 0xffbbd1e7, 0xff009ddc, 0xff009ddc, 0xff009ddc, 0xff009ddc, 0xff009ddc, 0xff009ddc,
0xff009ddc, 0xff009ddc, 0xff009ddc, 0xff009ddc, 0xff009ddc, 0xff009ddc, 0xff009ddc, 0xff009ddc,
0xff009ddc, 0xff009ddc, 0xff009ddc, 0xff009ddc, 0xff009ddc, 0xff009ddc, 0xff009ddc, 0xff009ddc,
0xff009ddc, 0xff009ddc, 0xff009ddc, 0xff009ddc, 0xff009ddc, 0xff009ddc, 0xff039ddc, 0xc31994d3,
0x301e6290, 0x06000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x05000000,
0x19020202, 0x73d0d0d0, 0xdce8e8e8, 0xffececec, 0xffececec, 0xffececec, 0xffececec, 0xffececec,
0xffececec, 0xffececec, 0xffececec, 0xffececec, 0xffececec, 0xffececec, 0xffececec, 0xffececec,
0xffededed, 0xff84b7e1, 0xff009ddc, 0xff009ddc, 0xff009ddc, 0xff009ddc, 0xff009ddc, 0xff009ddc,
0xff009ddc, 0xff009ddc, 0xff009ddc, 0xff009ddc, 0xff009ddc, 0xff009ddc, 0xff009ddc, 0xff009ddc,
0xff009ddc, 0xff009ddc, 0xff009ddc, 0xff009ddc, 0xff009ddc, 0xff009ddc, 0xff009ddc, 0xff009ddc,
0xff009ddc, 0xff009ddc, 0xff009ddc, 0xff009ddc, 0xff069cdc, 0xdc1697d6, 0x712084be, 0x18000101,
0x04000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
0x02000000, 0x0b000000, 0x1d000000, 0x55bdbdbd, 0x97dbdbdb, 0xc4e3e3e3, 0xdee7e7e7, 0xfaececec,
0xffececec, 0xffededed, 0xffededed, 0xffececec, 0xffececec, 0xffededed, 0xffececec, 0xffededed,
0xffe4e7ec, 0xff069cdb, 0xff009ddc, 0xff019ddc, 0xff089cdb, 0xff0b9cdb, 0xff0b9cdb, 0xff0b9cdb,
0xff0b9cdb, 0xff0b9cdb, 0xff0b9cdb, 0xff0b9cdb, 0xff0b9cdb, 0xff0b9cdb, 0xff0b9cdb, 0xff0b9cdb,
0xff0b9cdb, 0xff0b9cdb, 0xff0b9cdb, 0xff0c9cdb, 0xff0d9cdb, 0xff0e9cdb, 0xff0e9bdb, 0xff119bdb,
0xfc159ada, 0xe31598d7, 0xc61794d3, 0x971b8dc9, 0x572378af, 0x1c000000, 0x0b000000, 0x01000000,
0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
0x00000000, 0x00000000, 0x02000000, 0x07000000, 0x10000000, 0x18000000, 0x1e000000, 0x22000000,
0x261c1c1c, 0x29424242, 0x2a4f4f4f, 0x2b555555, 0x2b565656, 0x2b575757, 0x2b575757, 0x2b575757,
0x2b484c51, 0x2b06304a, 0x2b05324c, 0x2b07324c, 0x2b0a314c, 0x2b0b304b, 0x2b0b304b, 0x2b0b304b,
0x2b0b304b, 0x2b0b304b, 0x2b0b304b, 0x2b0b304b, 0x2b0b304b, 0x2b0b304b, 0x2b0b304b, 0x2b0b304b,
0x2b0b304b, 0x2b0b304b, 0x2b0b304b, 0x2b0c304b, 0x2b0c304b, 0x2b0c2f4a, 0x2a0c2e48, 0x27082134,
0x22000000, 0x1e000000, 0x17000000, 0x0f000000, 0x07000000, 0x02000000, 0x00000000, 0x00000000,
0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x01000000, 0x01000000, 0x02000000,
0x03000000, 0x03000000, 0x04000000, 0x04000000, 0x04000000, 0x04000000, 0x04000000, 0x04000000,
0x04000000, 0x04000000, 0x04000000, 0x04000000, 0x04000000, 0x04000000, 0x04000000, 0x04000000,
0x04000000, 0x04000000, 0x04000000, 0x04000000, 0x04000000, 0x04000000, 0x04000000, 0x04000000,
0x04000000, 0x04000000, 0x04000000, 0x04000000, 0x04000000, 0x04000000, 0x03000000, 0x03000000,
0x02000000, 0x01000000, 0x01000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000, 0x00000000,
};
#endif

View File

@ -1,3 +1,15 @@
## Top down view
<center>
<img src="mui_emulator.drawio.png" alt="Top down view">
<i>Here how it's supposed to work!</i>
</center>
The emulator was made to be portable on most things. It's plain C compiled with -Wextras, I run in regularly thru the static analyser, and I regulartly check it with valgrind. So it's pretty clean.
It evolved from the original mess, and I organized it into bits which would make porting it to other platform easier.
## How to I compile it and run it?
* You need a C compiler, make, and a few libraries:
* libasound2-dev [ optional, for audio ]
@ -26,9 +38,4 @@ I have pretty consistent code style across my projects.
## What needs doing?
* I'm sure there are bugs. I haven't tested it on a lot of hardware, and apart from quite a few games and a few known productivity app, it had had very little extensive testing. So testing!
* In the code there are a few bits which needs fixing
* The mouse card is not working properly. It works for some (most importantly A2 Desktop), but it's not working properly. I suspect the VBL Interrupt handling.
* In mii.c, the *mii_page_table_update()* does work, but it sub-optimal, it was handy to debug the countless combination of soft-switches, but it is not the ideal way to handle that problem; it needs a proper switch-case statement.
* The floppy drive emulation was borrowed from bobbin, and it works, and it
got it all working, but it's definitely not matching the style of the rest of the codebase. It needs to be replaced.
* Plenty of the most complicated piece of code (video, memory mapping) load a dozen of soft-switches, it probably should use a separately maintained bit field that is maintained by the on/off switches, and would make it a lot easier to test for bit combinations.
* The static array of memory 'banks' works, but it prevents me easily making a memory extension card. It should be refactored at some point.
* The floppy driver still has issues writing, most notably it fails to 'bit copy' stuff when using Copy II plus. It's a timing issue, but I haven't found it yet.

Binary file not shown.

After

Width:  |  Height:  |  Size: 81 KiB

View File

Before

Width:  |  Height:  |  Size: 621 KiB

After

Width:  |  Height:  |  Size: 621 KiB

View File

Before

Width:  |  Height:  |  Size: 64 KiB

After

Width:  |  Height:  |  Size: 64 KiB

View File

Before

Width:  |  Height:  |  Size: 279 KiB

After

Width:  |  Height:  |  Size: 279 KiB

View File

Before

Width:  |  Height:  |  Size: 81 KiB

After

Width:  |  Height:  |  Size: 81 KiB

View File

Before

Width:  |  Height:  |  Size: 210 KiB

After

Width:  |  Height:  |  Size: 210 KiB

View File

Before

Width:  |  Height:  |  Size: 88 KiB

After

Width:  |  Height:  |  Size: 88 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 9.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 MiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 37 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 36 KiB

BIN
docs/screen/v19mega2.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 287 KiB

BIN
docs/screen/v19ntsc.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 294 KiB

View File

Before

Width:  |  Height:  |  Size: 790 KiB

After

Width:  |  Height:  |  Size: 790 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 510 KiB

8
libmui/.gitignore vendored Normal file
View File

@ -0,0 +1,8 @@
.bash_history
build-*
.vscode
compile_commands.json
cachegrind.out.*
callgrind.out.*
.cache
*_scrot.png

15
libmui/CHANGELOG.md Normal file
View File

@ -0,0 +1,15 @@
# MUI Version Changelog
## 1.2
* More tweaks to the menus. Popup menus can be justified left/right/center. Removed quite a few fudge factors.
* Added a notion of a control (per window) having the 'focus'. Currently listboxes and text edit boxes can have the focus.
## 1.1 -- since the release of the original verison
* Added support for horizontal scrollbars.
* Added a faster (vectorized) version of an obvious 'cg' function.
* Fixed a problem with mui_timers. Also typed it now.
* Added a bunch of extra comments to mui.h
* Added a *fully justified* text style for text boxes.
## 1.0 -- original version
* Initial release. Doh.

21
libmui/LICENSE Normal file
View File

@ -0,0 +1,21 @@
MIT License
Copyright (c) 2024 Michel Pollet
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View File

@ -1,89 +1,62 @@
# Makefile
#
# Copyright (C) 2020 Michel Pollet <buserror@gmail.com>
#
# SPDX-License-Identifier: MIT
CC = gcc
CPPFLAGS += -Imui
# for bsd_queue.h
CPPFLAGS += -I../libmish/src
CPPFLAGS += -DUI_HAS_XCB=1 -DUI_HAS_XKB=1
OPTIMIZE ?= -O0 -g
CFLAGS += --std=gnu99 -Wall -Wextra
CFLAGS += $(OPTIMIZE)
CFLAGS += -Wno-unused-parameter -Wno-unused-function
# PIC is necessary for the shared library/plugin to work
CFLAGS += -fPIC
CPPFLAGS += ${shell pkg-config --cflags pixman-1}
LDLIBS += ${shell pkg-config --libs pixman-1}
LDLIBS += $(shell pkg-config --libs \
xcb xcb-shm xcb-randr \
xkbcommon-x11)
LDLIBS += -lm -ldl
LIBMUI =
MUI_SRC := $(wildcard $(LIBMUI)src/*.c)
O := $(BUILD_DIR)build-$(shell $(CC) -dumpmachine)
BIN := $(O)/bin
OBJ := $(O)/obj/libmui
LIB := $(O)/lib
vpath %.c $(LIBMUI)src $(LIBMUI)mui_shell
MUI_SRC := $(wildcard mui/*.c)
SRC := $(MUI_SRC)
MUI_OBJ := ${patsubst %, $(OBJ)/%, ${notdir ${SRC:.c=.o}}}
# this is just so we compile the tests if not a submodule
IS_SUBMODULE := ${wildcard ../ui_gl/mii_mui.h}
SRC_VPATH := mui tests
SRC_VPATH += ../ui_gl
vpath %.c $(SRC_VPATH)
all : static mui_shell
CPPFLAGS += -I../contrib
include $(LIBMUI)Makefile.common
VERSION := ${shell git log -1 --date=short --pretty="%h %cd"}
CPPFLAGS += -DUI_VERSION="\"$(VERSION)\""
ifeq ($(IS_SUBMODULE),)
all : tests
endif
MUI_OBJ := ${patsubst %, $(OBJ)/%, ${notdir ${MUI_SRC:.c=.o}}}
TARGET_LIB := $(LIB)/libmui.a
all : $(BIN)/mui_playground $(LIB)/ui_tests.so
.PHONY : static
.PHONY : mui_shell static tests
mui_shell : $(BIN)/mui_shell
static : $(TARGET_LIB)
ifeq ($(V),1)
Q :=
else
Q := @
endif
tests : | $(TARGET_LIB)
$(MAKE) -j -C tests
@echo " ** To launch the demo, run "
@echo " $(BIN)/mui_shell -f $(LIB)/mui_widgets_demo.so"
$(TARGET_LIB) : $(MUI_OBJ) | $(LIB)
@echo " AR $@"
$(Q)$(AR) rcs $@ $^
$(OBJ)/ui_tests.o : CPPFLAGS += -Itests -I../ui_gl
$(LIB)/ui_tests.so : $(OBJ)/mii_mui_slots.o
$(LIB)/ui_tests.so : $(OBJ)/mii_mui_loadbin.o
$(LIB)/ui_tests.so : $(OBJ)/mii_mui_1mb.o
$(LIB)/ui_tests.so : $(OBJ)/mii_mui_2dsk.o
$(LIB)/ui_tests.so : $(OBJ)/mii_mui_about.o
#
# The shell program is used to test the UI library using plugins
# It is made using XCB and XKB libraries to have a minimal dependency
# on X11. Also, allows partial updates to be tested properly
#
$(OBJ)/mui_shell.o : CPPFLAGS += -DUI_HAS_XCB=1 -DUI_HAS_XKB=1
ifeq ($(shell uname),NetBSD)
# NetBSD requirements
$(OBJ)/mui_shell.o : CPPFLAGS += $(shell pkg-config --cflags xorg-server xkbcommon)
$(BIN)/mui_shell : LDLIBS += $(shell pkg-config --libs xorg-server)
endif
$(OBJ)/mii_mui_about.o : CPPFLAGS+=-DMII_ICON64_DEFINE
# use a .temp file, otherwise the playground tries to reload before the file
# is fully written, and it fails.
# the ${filter} are there to make the sure object files are linked before the .a
$(LIB)/ui_tests.so : $(OBJ)/ui_tests.o $(LIB)/libmui.a | $(O)
@echo " LDSO $@"
$(Q)$(CC) $(CPPFLAGS) $(CFLAGS) -shared -fPIC -o $@.temp \
${filter %.o, $^} ${filter %.a, $^} $(LDLIBS) && \
mv $@.temp $@
$(BIN)/mui_playground : $(OBJ)/mui_playground.o $(LIB)/libmui.a
$(OBJ)/%.o : %.c | $(OBJ)
@echo " CC" ${filter -O%, $(CPPFLAGS) $(CFLAGS)} "$<"
$(Q)$(CC) -MMD $(CPPFLAGS) $(CFLAGS) -c -o $@ $<
$(BIN)/% : | $(BIN)
@echo " LD $@"
$(Q)$(CC) $(LDFLAGS) -o $@ $^ $(LDLIBS)
$(O) $(OBJ) $(BIN) $(LIB):
@mkdir -p $@
$(BIN)/mui_shell : LDLIBS += $(shell pkg-config --libs \
xcb xcb-shm xcb-image \
xkbcommon xkbcommon-x11)
$(BIN)/mui_shell : LDLIBS += -lm
ifeq ($(shell uname),Linux)
$(BIN)/mui_shell : LDLIBS += -ldl
endif
$(BIN)/mui_shell : $(OBJ)/mui_shell.o $(LIB)/libmui.a
clean :
rm -rf $(O)
@ -92,13 +65,13 @@ clean :
# everytime a file is modified.
watch :
while true; do \
clear; $(MAKE) -j all; \
inotifywait -qre close_write mui tests ../ui_gl; \
clear; $(MAKE) -j all tests; \
inotifywait -qre close_write src mui_shell tests/* ../ui_gl; \
done
compile_commands.json: lsp
lsp:
{ $$(which gmake) CC=gcc V=1 --always-make --dry-run all ; } | \
{ $$(which gmake) CC=gcc V=1 --always-make --dry-run all tests ; } | \
sh ../utils/clangd_gen.sh >compile_commands.json
-include $(OBJ)/*.d

73
libmui/Makefile.common Normal file
View File

@ -0,0 +1,73 @@
# Makefile
#
# Copyright (C) 2024 Michel Pollet <buserror@gmail.com>
#
# SPDX-License-Identifier: MIT
LIBMUI ?= ../
# for bsd_queue.h and incbin.h
#MII ?= $(LIBMUI)..
#CPPFLAGS += -I$(MII)/libmish/src -I$(MII)/contrib
CPPFLAGS += -I$(LIBMUI)contrib
BUILD_DIR ?= $(LIBMUI)
O := $(BUILD_DIR)build-$(shell $(CC) -dumpmachine)
BIN := $(O)/bin
OBJ := $(O)/obj/libmui
LIB := $(O)/lib
CPPFLAGS += -I$(LIBMUI)src
CPPFLAGS += -I$(LIBMUI)mui_shell
CPPFLAGS += ${shell pkg-config --cflags pixman-1}
LDLIBS += ${shell pkg-config --libs pixman-1}
MUI_VERSION := ${shell \
echo $$(git describe --tags --abbrev=0 2>/dev/null || \
echo "(dev)") \
$$(git log -1 --date=short --pretty="%h %cd")}
CPPFLAGS += -DMUI_VERSION="\"$(MUI_VERSION)\""
OPTIMIZE ?= -O0 -g
CFLAGS += --std=gnu99 -Wall -Wextra
CFLAGS += $(OPTIMIZE)
CFLAGS += -Wno-unused-parameter -Wno-unused-function
# PIC is necessary for the shared library/plugin to work
CFLAGS += -fPIC
#CFLAGS += -fsanitize=address
#LDFLAGS += -fsanitize=address
#LDLIBS +=
ifeq ($(V),1)
Q :=
else
Q := @
endif
# use a .temp file, otherwise the mui_shell tries to reload before the file
# is fully written, and it fails.
# the ${filter} are there to make the sure object files are linked before the .a
$(LIB)/%.so : $(OBJ)/%.o $(LIB)/libmui.a | $(O)
ifeq ($(V),)
@echo " LDSO $@"
endif
$(Q)$(CC) $(CPPFLAGS) $(CFLAGS) -shared -fPIC -o $@.temp \
${filter %.o, $^} ${filter %.a, $^} $(LDLIBS) && \
mv $@.temp $@
$(OBJ)/%.o : %.c | $(OBJ)
ifeq ($(V),)
@echo " CC" ${filter -O%, $(CPPFLAGS) $(CFLAGS)} "$<"
endif
$(Q)$(CC) -MMD $(CPPFLAGS) $(CFLAGS) -c -o $@ $<
$(BIN)/% : | $(BIN)
ifeq ($(V),)
@echo " LD $@"
endif
$(Q)$(CC) $(LDFLAGS) -o $@ $^ $(LDLIBS)
$(O) $(OBJ) $(BIN) $(LIB):
@mkdir -p $@

View File

@ -1,21 +1,33 @@
# What is this?
This is a contender for the World Record for Feature Creep Side Project. It is pretty high in the contender list as it's a bolt on to *another* contender for the World Record for Feature Creep Side Project (the MII Apple //e emulator).
# What the hell is this?
This is a contender for the World Record for Feature Creep Side Project. It is pretty high in the contender list as it's a bolt on to *another* contender for the World Record for Feature Creep Side Project (the [MII Apple //e emulator](http://github.com/buserror/mii_emu)).
It is a library that duplicate a lot of a Macintosh Classic "Toolbox" APIs. It is not a complete implementation, but it is enough to make a few simple applications, also, all the bits I needed for the MII emulator.
<center>
<img src="doc/widgets.gif" alt="10 seconds demo">
<i>10 seconds demo</i>
</center>
# Why?
Well I wanted a UI library for MII -- something without tons of dependencies, and I didn't want the typical 'game like' style with "Arrow Keys + Return + Escape" sort of menus.
I started with Nuklear immediate mode UI, but it both look super ugly, AND is very limited as soon as you want to do something 'custom', and I didn't see myself hacking into that codebase. One thing I particularly dislike is the 'layout' engine that decide to put stuff where it wants, and it's *never* in the 'right' place, like a hard case of 'computer say so' -- typicaly result into
something like Programmer's Art. That's why Linux On The Deskop is famous for it's Pixel Perfection polished UIs. *Cough*.
something like Programmer's Art, just worse. That's why Linux On The Deskop is famous for it's Pixel Perfection polished UIs. *Cough*.
The other things I don't like with the trendy IM UIs is that they promise you that you don't have to keep a separate state around blah blah, however they forget to mention that there IS a state kept for you, based on hash values, and if you are unlucky enough to have a hash clash, you are screwed. I've seen that happen in real life, and it's not pretty to debug.
I miss the days were UIs were /crafted/ not just decided for you bad a bad 'layouting' engine with huge rectangular flat buttons and no sense whatsoever of 'design' or usability.
Also, I realized I had quite a few bits that I could use to make my own library anyway, so I started to do that.
Also, I like making my own toys. There, happy now?
<center>
<img src="doc/control_demo.png" alt="Basic Controls">
<i>Control Demo</i>
</center>
# What can it do?
Well, it has a few of the classic 'managers' that the Macintosh Toolbox had. Or the later GS/OS. However it behaves more like a modern system, it's not 'synchronous' like the old toolbox. Stuff gets redrawn even if you are clicking/dragging etc without having to 'do it yourself'.
@ -29,6 +41,12 @@ luckily, you only have to draw/convert the 'dirty' regions, so it's not too bad.
It could be possible to 'vectorize' the rendering to vertice buffers and stuff, but really, it's not needed, it's fast enough as it is and it would fall back the 'lets redraw everything' behaviour of the IMmediate UI libraries.
<center>
<img src="doc/static_text.png" alt="Basic text boxes">
<i>Some basic text boxes</i>
</center>
# How does it differ from the original?
Well, in terms of looks, it's kinda like I started with MacOS8/9, but removed all the grayscale bits.
Bizarelly, I think System 7 'flat' looks has aged better than the 'grayscale' look of 8/9, so I went with that. Most of the 'visible' difference are really related to the dialog's popup menus, which are a lot more 'OS8' than 'OS7'. The Scrollbar is definitely more GS/OS though, never understood why it took so long for MacOS to adopt that.
@ -52,7 +70,7 @@ It can create windows, and it can draw into them. Has up to 15 'layers', and can
I deliberately limited the number of coordinate systems to 2 -- like the old one; so you get the 'screen coordinates' and the 'window content coordinates'. I was half tempted to create a fully hierarchical system, but realized it was really not neeeded, and would just make things more complicated.
It's a 'smart' window manager, it keeps track of an 'invalid' list of rectangles, and clips to that when redrawing, so it doesn't redraw the whole window every time, yeah, like the original. None of that 'lets redraw absolutely everything every frame' stuff.
It's a 'smart' window manager, it keeps track of an 'invalid' list of rectangles, and clips to that when redrawing, so it doesn't redraw the whole window every time, yeah, like the original. None of that 'lets redraw absolutely everything every frame' stuff like Immediate Mode UIs.
- It's missing bits like 'zooming' (TODO), and 'resizing' (TODO).
- It doesn't do transparent windows. It is by design, it draws windows 'top down' to optimize clipping -- adding transparency wouldn't be hard, but I'd have to draw the windows 'bottom up' to handle blending, and we'd revert back to drawing a lot of stuff for very little return.
- Also, you can always alpha blend the whole *ui* 'screen' to wherever you want, so it's not like you can't have transparency.
@ -66,19 +84,31 @@ Menubar, menus, checkmarks, keyboard shortcuts, all that stuff. Made to looks li
## Control Manager
Buttons, checkboxes, radio buttons, scrollbars (vertical), wrapping textboxes, all that stuff.
- It's missing bits like Edit Field (TODO), and a Slider.
- It's missing bits like Edit Field (Work in Progress), and a Slider.
- There IS a prototype version of a text edit control, but it's not quite right yet -- works fine for a one liner etc, but not for a multi line text box. Not far off tho.
## List Manager
More or less hard coded to display filenames so far, but plain lists are actually easier than this so. Handle arrow keys/page up/down, scroll wheel, etc.
- It's missing a way to 'compress' the font and/or use ellipsis abreviations (TODO) when the item text is too long.
+ You CAN use 'typeahead' to find the item you want, like the original.
## Alerts
It has the typical 'Cancel'+'OK' alert.
- Could do with more types of alerts (TODO).
<center>
<img src="doc/alert.png" alt="Basic Alert">
<i>Basic Alert Dialog</i>
</center>
## Standard File
It has the classic 'Open' a file dialog. Haven't needed the other one. yet.
It has the classic 'Open' a file dialog. Haven't needed the other one. yet. This one one of the primary goal of the UI library to start with, so I spent quite a bit of time getting it 'right', quite happy about how it turned out.
- Could do with a 'Save' dialog (TODO).
- Maybe a 'period correct' way to handle previously visited folders... Currently it can same the last folder you visited *per file type*.
- You can use arrow keys, page/up down, and you can even typehead to the file you want, like in the old days.
+ It has an extra popup with the last few used directories.
- You can use arrow keys, page/up down, and you can even typehead to the file you want, like in the old MacOS!
<center>
<img src="doc/standard_get_file.png" alt="Standard Get File">
<i>Standard Get File</i>
</center>
## Resource Manager
Nope! Not there; I'd need some sort of ResEdit and stuff -- and now that is *ONE* Feature Creep Too Far thank you very much.
I have a vague idea of making some sort of MessagePack format for resources, but that's for another day.
@ -97,17 +127,17 @@ That's it, all the other bits I already had 'in stock' -- the 2D geometry bits I
# How do I build it?
It's a simple Makefile, so you just need to do a 'make' in the root directory. It will build the library, and the tests/demos/samples.
To build the tests/demos/samples, you'll need:
* xcb xcb-shm xcb-randr xkbcommon-x11 -- this is just to run the 'playground' demo, which is a simple window with a few buttons and stuff.
* xcb xcb-shm xcb-randr xkbcommon-x11 -- this is just to run the 'mui_shell' demo, which is a simple window with a few buttons and stuff.
## Nvidia Driver gotcha
* *Note* that if you use the nvidia binary driver (I do), you will need to add a flag to your config, otherwise the playground won't work.
* *Note* that if you use the nvidia binary driver (I do), you will need to add a flag to your config, otherwise the mui_shell won't work.
- Add: 'Option \"AllowSHMPixmaps\" \"1\"' to the "Device" In your /etc/X11/xorg.conf file.
# How do I use it?
Well the best way is to hack around *mui_playground.c* and *ui_tests.c*. It's a simple window with a few buttons and stuff, and it's a good place to start.
Well the best way is to hack around *mui_shell.c* and *mui_widgets_demo.c*. It's a simple window with a few buttons and stuff, and it's a good place to start.
The cool thing about ui_playground is that it loads ui_test.so as a *plugin* and auto-reload it if it detects a change. So you can hack around ui_test.c, and it will reload it and run it again! You can code a new dialog insanely fast with that, pretty must as fast as you would with a resource editor.
The cool thing about ui_mui_shell is that it loads mui_widgets_demo.so as a *plugin* and auto-reload it if it detects a change. So you can hack around mui_widgets_demo.c, and it will reload it and run it again! You can code a new dialog insanely fast with that, pretty must as fast as you would with a resource editor.
A good trick is to use 'make watch' on the *libmui* directory in a terminal tab, and it will rebuild the library and the playground automatically when you change something, that with the 'auto save' of your editor, and you will have a constantly building/running playground as you hack around.
A good trick is to use 'make watch' on the *libmui* directory in a terminal tab, and it will rebuild the library and the mui_shell automatically when you change something, that with the 'auto save' of your editor, and you will have a constantly building/running mui_shell as you hack around.
Have fun!
@ -132,3 +162,4 @@ Have fun!
- nope
* And Wayland then? Wayland is The Future after all!
- nope
<center><h1>Now, GET OFF MY LAWN!</h1></center>

860
libmui/contrib/bsd_queue.h Normal file
View File

@ -0,0 +1,860 @@
/*-
* SPDX-License-Identifier: BSD-3-Clause
*
* Copyright (c) 1991, 1993
* The Regents of the University of California. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* 3. Neither the name of the University nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*
* @(#)queue.h 8.5 (Berkeley) 8/20/94
* $FreeBSD$
*/
#ifndef _SYS_QUEUE_H_
#define _SYS_QUEUE_H_
//#include <sys/cdefs.h>
/*
* This file defines four types of data structures: singly-linked lists,
* singly-linked tail queues, lists and tail queues.
*
* A singly-linked list is headed by a single forward pointer. The elements
* are singly linked for minimum space and pointer manipulation overhead at
* the expense of O(n) removal for arbitrary elements. New elements can be
* added to the list after an existing element or at the head of the list.
* Elements being removed from the head of the list should use the explicit
* macro for this purpose for optimum efficiency. A singly-linked list may
* only be traversed in the forward direction. Singly-linked lists are ideal
* for applications with large datasets and few or no removals or for
* implementing a LIFO queue.
*
* A singly-linked tail queue is headed by a pair of pointers, one to the
* head of the list and the other to the tail of the list. The elements are
* singly linked for minimum space and pointer manipulation overhead at the
* expense of O(n) removal for arbitrary elements. New elements can be added
* to the list after an existing element, at the head of the list, or at the
* end of the list. Elements being removed from the head of the tail queue
* should use the explicit macro for this purpose for optimum efficiency.
* A singly-linked tail queue may only be traversed in the forward direction.
* Singly-linked tail queues are ideal for applications with large datasets
* and few or no removals or for implementing a FIFO queue.
*
* A list is headed by a single forward pointer (or an array of forward
* pointers for a hash table header). The elements are doubly linked
* so that an arbitrary element can be removed without a need to
* traverse the list. New elements can be added to the list before
* or after an existing element or at the head of the list. A list
* may be traversed in either direction.
*
* A tail queue is headed by a pair of pointers, one to the head of the
* list and the other to the tail of the list. The elements are doubly
* linked so that an arbitrary element can be removed without a need to
* traverse the list. New elements can be added to the list before or
* after an existing element, at the head of the list, or at the end of
* the list. A tail queue may be traversed in either direction.
*
* For details on the use of these macros, see the queue(3) manual page.
*
* Below is a summary of implemented functions where:
* + means the macro is available
* - means the macro is not available
* s means the macro is available but is slow (runs in O(n) time)
*
* SLIST LIST STAILQ TAILQ
* _HEAD + + + +
* _CLASS_HEAD + + + +
* _HEAD_INITIALIZER + + + +
* _ENTRY + + + +
* _CLASS_ENTRY + + + +
* _INIT + + + +
* _EMPTY + + + +
* _FIRST + + + +
* _NEXT + + + +
* _PREV - + - +
* _LAST - - + +
* _FOREACH + + + +
* _FOREACH_FROM + + + +
* _FOREACH_SAFE + + + +
* _FOREACH_FROM_SAFE + + + +
* _FOREACH_REVERSE - - - +
* _FOREACH_REVERSE_FROM - - - +
* _FOREACH_REVERSE_SAFE - - - +
* _FOREACH_REVERSE_FROM_SAFE - - - +
* _INSERT_HEAD + + + +
* _INSERT_BEFORE - + - +
* _INSERT_AFTER + + + +
* _INSERT_TAIL - - + +
* _CONCAT s s + +
* _REMOVE_AFTER + - + -
* _REMOVE_HEAD + - + -
* _REMOVE s + s +
* _SWAP + + + +
*
*/
#ifdef QUEUE_MACRO_DEBUG
#warn Use QUEUE_MACRO_DEBUG_TRACE and/or QUEUE_MACRO_DEBUG_TRASH
#define QUEUE_MACRO_DEBUG_TRACE
#define QUEUE_MACRO_DEBUG_TRASH
#endif
#ifdef QUEUE_MACRO_DEBUG_TRACE
/* Store the last 2 places the queue element or head was altered */
struct qm_trace {
unsigned long lastline;
unsigned long prevline;
const char *lastfile;
const char *prevfile;
};
#define TRACEBUF struct qm_trace trace;
#define TRACEBUF_INITIALIZER { __LINE__, 0, __FILE__, NULL } ,
#define QMD_TRACE_HEAD(head) do { \
(head)->trace.prevline = (head)->trace.lastline; \
(head)->trace.prevfile = (head)->trace.lastfile; \
(head)->trace.lastline = __LINE__; \
(head)->trace.lastfile = __FILE__; \
} while (0)
#define QMD_TRACE_ELEM(elem) do { \
(elem)->trace.prevline = (elem)->trace.lastline; \
(elem)->trace.prevfile = (elem)->trace.lastfile; \
(elem)->trace.lastline = __LINE__; \
(elem)->trace.lastfile = __FILE__; \
} while (0)
#else /* !QUEUE_MACRO_DEBUG_TRACE */
#define QMD_TRACE_ELEM(elem)
#define QMD_TRACE_HEAD(head)
#define TRACEBUF
#define TRACEBUF_INITIALIZER
#endif /* QUEUE_MACRO_DEBUG_TRACE */
#ifdef QUEUE_MACRO_DEBUG_TRASH
#define TRASHIT(x) do {(x) = (void *)-1;} while (0)
#define QMD_IS_TRASHED(x) ((x) == (void *)(intptr_t)-1)
#else /* !QUEUE_MACRO_DEBUG_TRASH */
#define TRASHIT(x)
#define QMD_IS_TRASHED(x) 0
#endif /* QUEUE_MACRO_DEBUG_TRASH */
#if defined(QUEUE_MACRO_DEBUG_TRACE) || defined(QUEUE_MACRO_DEBUG_TRASH)
#define QMD_SAVELINK(name, link) void **name = (void *)&(link)
#else /* !QUEUE_MACRO_DEBUG_TRACE && !QUEUE_MACRO_DEBUG_TRASH */
#define QMD_SAVELINK(name, link)
#endif /* QUEUE_MACRO_DEBUG_TRACE || QUEUE_MACRO_DEBUG_TRASH */
#ifdef __cplusplus
/*
* In C++ there can be structure lists and class lists:
*/
#define QUEUE_TYPEOF(type) type
#else
#define QUEUE_TYPEOF(type) struct type
#endif
/*
* Singly-linked List declarations.
*/
#define SLIST_HEAD(name, type) \
struct name { \
struct type *slh_first; /* first element */ \
}
#define SLIST_CLASS_HEAD(name, type) \
struct name { \
class type *slh_first; /* first element */ \
}
#define SLIST_HEAD_INITIALIZER(head) \
{ NULL }
#define SLIST_ENTRY(type) \
struct { \
struct type *sle_next; /* next element */ \
}
#define SLIST_CLASS_ENTRY(type) \
struct { \
class type *sle_next; /* next element */ \
}
/*
* Singly-linked List functions.
*/
#if (defined(_KERNEL) && defined(INVARIANTS))
#define QMD_SLIST_CHECK_PREVPTR(prevp, elm) do { \
if (*(prevp) != (elm)) \
panic("Bad prevptr *(%p) == %p != %p", \
(prevp), *(prevp), (elm)); \
} while (0)
#else
#define QMD_SLIST_CHECK_PREVPTR(prevp, elm)
#endif
#define SLIST_CONCAT(head1, head2, type, field) do { \
QUEUE_TYPEOF(type) *curelm = SLIST_FIRST(head1); \
if (curelm == NULL) { \
if ((SLIST_FIRST(head1) = SLIST_FIRST(head2)) != NULL) \
SLIST_INIT(head2); \
} else if (SLIST_FIRST(head2) != NULL) { \
while (SLIST_NEXT(curelm, field) != NULL) \
curelm = SLIST_NEXT(curelm, field); \
SLIST_NEXT(curelm, field) = SLIST_FIRST(head2); \
SLIST_INIT(head2); \
} \
} while (0)
#define SLIST_EMPTY(head) ((head)->slh_first == NULL)
#define SLIST_FIRST(head) ((head)->slh_first)
#define SLIST_FOREACH(var, head, field) \
for ((var) = SLIST_FIRST((head)); \
(var); \
(var) = SLIST_NEXT((var), field))
#define SLIST_FOREACH_FROM(var, head, field) \
for ((var) = ((var) ? (var) : SLIST_FIRST((head))); \
(var); \
(var) = SLIST_NEXT((var), field))
#define SLIST_FOREACH_SAFE(var, head, field, tvar) \
for ((var) = SLIST_FIRST((head)); \
(var) && ((tvar) = SLIST_NEXT((var), field), 1); \
(var) = (tvar))
#define SLIST_FOREACH_FROM_SAFE(var, head, field, tvar) \
for ((var) = ((var) ? (var) : SLIST_FIRST((head))); \
(var) && ((tvar) = SLIST_NEXT((var), field), 1); \
(var) = (tvar))
#define SLIST_FOREACH_PREVPTR(var, varp, head, field) \
for ((varp) = &SLIST_FIRST((head)); \
((var) = *(varp)) != NULL; \
(varp) = &SLIST_NEXT((var), field))
#define SLIST_INIT(head) do { \
SLIST_FIRST((head)) = NULL; \
} while (0)
#define SLIST_INSERT_AFTER(slistelm, elm, field) do { \
SLIST_NEXT((elm), field) = SLIST_NEXT((slistelm), field); \
SLIST_NEXT((slistelm), field) = (elm); \
} while (0)
#define SLIST_INSERT_HEAD(head, elm, field) do { \
SLIST_NEXT((elm), field) = SLIST_FIRST((head)); \
SLIST_FIRST((head)) = (elm); \
} while (0)
#define SLIST_NEXT(elm, field) ((elm)->field.sle_next)
#define SLIST_REMOVE(head, elm, type, field) do { \
QMD_SAVELINK(oldnext, (elm)->field.sle_next); \
if (SLIST_FIRST((head)) == (elm)) { \
SLIST_REMOVE_HEAD((head), field); \
} \
else { \
QUEUE_TYPEOF(type) *curelm = SLIST_FIRST(head); \
while (SLIST_NEXT(curelm, field) != (elm)) \
curelm = SLIST_NEXT(curelm, field); \
SLIST_REMOVE_AFTER(curelm, field); \
} \
TRASHIT(*oldnext); \
} while (0)
#define SLIST_REMOVE_AFTER(elm, field) do { \
SLIST_NEXT(elm, field) = \
SLIST_NEXT(SLIST_NEXT(elm, field), field); \
} while (0)
#define SLIST_REMOVE_HEAD(head, field) do { \
SLIST_FIRST((head)) = SLIST_NEXT(SLIST_FIRST((head)), field); \
} while (0)
#define SLIST_REMOVE_PREVPTR(prevp, elm, field) do { \
QMD_SLIST_CHECK_PREVPTR(prevp, elm); \
*(prevp) = SLIST_NEXT(elm, field); \
TRASHIT((elm)->field.sle_next); \
} while (0)
#define SLIST_SWAP(head1, head2, type) do { \
QUEUE_TYPEOF(type) *swap_first = SLIST_FIRST(head1); \
SLIST_FIRST(head1) = SLIST_FIRST(head2); \
SLIST_FIRST(head2) = swap_first; \
} while (0)
/*
* Singly-linked Tail queue declarations.
*/
#define STAILQ_HEAD(name, type) \
struct name { \
struct type *stqh_first;/* first element */ \
struct type **stqh_last;/* addr of last next element */ \
}
#define STAILQ_CLASS_HEAD(name, type) \
struct name { \
class type *stqh_first; /* first element */ \
class type **stqh_last; /* addr of last next element */ \
}
#define STAILQ_HEAD_INITIALIZER(head) \
{ NULL, &(head).stqh_first }
#define STAILQ_ENTRY(type) \
struct { \
struct type *stqe_next; /* next element */ \
}
#define STAILQ_CLASS_ENTRY(type) \
struct { \
class type *stqe_next; /* next element */ \
}
/*
* Singly-linked Tail queue functions.
*/
#define STAILQ_CONCAT(head1, head2) do { \
if (!STAILQ_EMPTY((head2))) { \
*(head1)->stqh_last = (head2)->stqh_first; \
(head1)->stqh_last = (head2)->stqh_last; \
STAILQ_INIT((head2)); \
} \
} while (0)
#define STAILQ_EMPTY(head) ((head)->stqh_first == NULL)
#define STAILQ_FIRST(head) ((head)->stqh_first)
#define STAILQ_FOREACH(var, head, field) \
for((var) = STAILQ_FIRST((head)); \
(var); \
(var) = STAILQ_NEXT((var), field))
#define STAILQ_FOREACH_FROM(var, head, field) \
for ((var) = ((var) ? (var) : STAILQ_FIRST((head))); \
(var); \
(var) = STAILQ_NEXT((var), field))
#define STAILQ_FOREACH_SAFE(var, head, field, tvar) \
for ((var) = STAILQ_FIRST((head)); \
(var) && ((tvar) = STAILQ_NEXT((var), field), 1); \
(var) = (tvar))
#define STAILQ_FOREACH_FROM_SAFE(var, head, field, tvar) \
for ((var) = ((var) ? (var) : STAILQ_FIRST((head))); \
(var) && ((tvar) = STAILQ_NEXT((var), field), 1); \
(var) = (tvar))
#define STAILQ_INIT(head) do { \
STAILQ_FIRST((head)) = NULL; \
(head)->stqh_last = &STAILQ_FIRST((head)); \
} while (0)
#define STAILQ_INSERT_AFTER(head, tqelm, elm, field) do { \
if ((STAILQ_NEXT((elm), field) = STAILQ_NEXT((tqelm), field)) == NULL)\
(head)->stqh_last = &STAILQ_NEXT((elm), field); \
STAILQ_NEXT((tqelm), field) = (elm); \
} while (0)
#define STAILQ_INSERT_HEAD(head, elm, field) do { \
if ((STAILQ_NEXT((elm), field) = STAILQ_FIRST((head))) == NULL) \
(head)->stqh_last = &STAILQ_NEXT((elm), field); \
STAILQ_FIRST((head)) = (elm); \
} while (0)
#define STAILQ_INSERT_TAIL(head, elm, field) do { \
STAILQ_NEXT((elm), field) = NULL; \
*(head)->stqh_last = (elm); \
(head)->stqh_last = &STAILQ_NEXT((elm), field); \
} while (0)
#define STAILQ_LAST(head, type, field) \
(STAILQ_EMPTY((head)) ? NULL : \
__containerof((head)->stqh_last, \
QUEUE_TYPEOF(type), field.stqe_next))
#define STAILQ_NEXT(elm, field) ((elm)->field.stqe_next)
#define STAILQ_REMOVE(head, elm, type, field) do { \
QMD_SAVELINK(oldnext, (elm)->field.stqe_next); \
if (STAILQ_FIRST((head)) == (elm)) { \
STAILQ_REMOVE_HEAD((head), field); \
} \
else { \
QUEUE_TYPEOF(type) *curelm = STAILQ_FIRST(head); \
while (STAILQ_NEXT(curelm, field) != (elm)) \
curelm = STAILQ_NEXT(curelm, field); \
STAILQ_REMOVE_AFTER(head, curelm, field); \
} \
TRASHIT(*oldnext); \
} while (0)
#define STAILQ_REMOVE_AFTER(head, elm, field) do { \
if ((STAILQ_NEXT(elm, field) = \
STAILQ_NEXT(STAILQ_NEXT(elm, field), field)) == NULL) \
(head)->stqh_last = &STAILQ_NEXT((elm), field); \
} while (0)
#define STAILQ_REMOVE_HEAD(head, field) do { \
if ((STAILQ_FIRST((head)) = \
STAILQ_NEXT(STAILQ_FIRST((head)), field)) == NULL) \
(head)->stqh_last = &STAILQ_FIRST((head)); \
} while (0)
#define STAILQ_SWAP(head1, head2, type) do { \
QUEUE_TYPEOF(type) *swap_first = STAILQ_FIRST(head1); \
QUEUE_TYPEOF(type) **swap_last = (head1)->stqh_last; \
STAILQ_FIRST(head1) = STAILQ_FIRST(head2); \
(head1)->stqh_last = (head2)->stqh_last; \
STAILQ_FIRST(head2) = swap_first; \
(head2)->stqh_last = swap_last; \
if (STAILQ_EMPTY(head1)) \
(head1)->stqh_last = &STAILQ_FIRST(head1); \
if (STAILQ_EMPTY(head2)) \
(head2)->stqh_last = &STAILQ_FIRST(head2); \
} while (0)
/*
* List declarations.
*/
#define LIST_HEAD(name, type) \
struct name { \
struct type *lh_first; /* first element */ \
}
#define LIST_CLASS_HEAD(name, type) \
struct name { \
class type *lh_first; /* first element */ \
}
#define LIST_HEAD_INITIALIZER(head) \
{ NULL }
#define LIST_ENTRY(type) \
struct { \
struct type *le_next; /* next element */ \
struct type **le_prev; /* address of previous next element */ \
}
#define LIST_CLASS_ENTRY(type) \
struct { \
class type *le_next; /* next element */ \
class type **le_prev; /* address of previous next element */ \
}
/*
* List functions.
*/
#if (defined(_KERNEL) && defined(INVARIANTS))
/*
* QMD_LIST_CHECK_HEAD(LIST_HEAD *head, LIST_ENTRY NAME)
*
* If the list is non-empty, validates that the first element of the list
* points back at 'head.'
*/
#define QMD_LIST_CHECK_HEAD(head, field) do { \
if (LIST_FIRST((head)) != NULL && \
LIST_FIRST((head))->field.le_prev != \
&LIST_FIRST((head))) \
panic("Bad list head %p first->prev != head", (head)); \
} while (0)
/*
* QMD_LIST_CHECK_NEXT(TYPE *elm, LIST_ENTRY NAME)
*
* If an element follows 'elm' in the list, validates that the next element
* points back at 'elm.'
*/
#define QMD_LIST_CHECK_NEXT(elm, field) do { \
if (LIST_NEXT((elm), field) != NULL && \
LIST_NEXT((elm), field)->field.le_prev != \
&((elm)->field.le_next)) \
panic("Bad link elm %p next->prev != elm", (elm)); \
} while (0)
/*
* QMD_LIST_CHECK_PREV(TYPE *elm, LIST_ENTRY NAME)
*
* Validates that the previous element (or head of the list) points to 'elm.'
*/
#define QMD_LIST_CHECK_PREV(elm, field) do { \
if (*(elm)->field.le_prev != (elm)) \
panic("Bad link elm %p prev->next != elm", (elm)); \
} while (0)
#else
#define QMD_LIST_CHECK_HEAD(head, field)
#define QMD_LIST_CHECK_NEXT(elm, field)
#define QMD_LIST_CHECK_PREV(elm, field)
#endif /* (_KERNEL && INVARIANTS) */
#define LIST_CONCAT(head1, head2, type, field) do { \
QUEUE_TYPEOF(type) *curelm = LIST_FIRST(head1); \
if (curelm == NULL) { \
if ((LIST_FIRST(head1) = LIST_FIRST(head2)) != NULL) { \
LIST_FIRST(head2)->field.le_prev = \
&LIST_FIRST((head1)); \
LIST_INIT(head2); \
} \
} else if (LIST_FIRST(head2) != NULL) { \
while (LIST_NEXT(curelm, field) != NULL) \
curelm = LIST_NEXT(curelm, field); \
LIST_NEXT(curelm, field) = LIST_FIRST(head2); \
LIST_FIRST(head2)->field.le_prev = &LIST_NEXT(curelm, field); \
LIST_INIT(head2); \
} \
} while (0)
#define LIST_EMPTY(head) ((head)->lh_first == NULL)
#define LIST_FIRST(head) ((head)->lh_first)
#define LIST_FOREACH(var, head, field) \
for ((var) = LIST_FIRST((head)); \
(var); \
(var) = LIST_NEXT((var), field))
#define LIST_FOREACH_FROM(var, head, field) \
for ((var) = ((var) ? (var) : LIST_FIRST((head))); \
(var); \
(var) = LIST_NEXT((var), field))
#define LIST_FOREACH_SAFE(var, head, field, tvar) \
for ((var) = LIST_FIRST((head)); \
(var) && ((tvar) = LIST_NEXT((var), field), 1); \
(var) = (tvar))
#define LIST_FOREACH_FROM_SAFE(var, head, field, tvar) \
for ((var) = ((var) ? (var) : LIST_FIRST((head))); \
(var) && ((tvar) = LIST_NEXT((var), field), 1); \
(var) = (tvar))
#define LIST_INIT(head) do { \
LIST_FIRST((head)) = NULL; \
} while (0)
#define LIST_INSERT_AFTER(listelm, elm, field) do { \
QMD_LIST_CHECK_NEXT(listelm, field); \
if ((LIST_NEXT((elm), field) = LIST_NEXT((listelm), field)) != NULL)\
LIST_NEXT((listelm), field)->field.le_prev = \
&LIST_NEXT((elm), field); \
LIST_NEXT((listelm), field) = (elm); \
(elm)->field.le_prev = &LIST_NEXT((listelm), field); \
} while (0)
#define LIST_INSERT_BEFORE(listelm, elm, field) do { \
QMD_LIST_CHECK_PREV(listelm, field); \
(elm)->field.le_prev = (listelm)->field.le_prev; \
LIST_NEXT((elm), field) = (listelm); \
*(listelm)->field.le_prev = (elm); \
(listelm)->field.le_prev = &LIST_NEXT((elm), field); \
} while (0)
#define LIST_INSERT_HEAD(head, elm, field) do { \
QMD_LIST_CHECK_HEAD((head), field); \
if ((LIST_NEXT((elm), field) = LIST_FIRST((head))) != NULL) \
LIST_FIRST((head))->field.le_prev = &LIST_NEXT((elm), field);\
LIST_FIRST((head)) = (elm); \
(elm)->field.le_prev = &LIST_FIRST((head)); \
} while (0)
#define LIST_NEXT(elm, field) ((elm)->field.le_next)
#define LIST_PREV(elm, head, type, field) \
((elm)->field.le_prev == &LIST_FIRST((head)) ? NULL : \
__containerof((elm)->field.le_prev, \
QUEUE_TYPEOF(type), field.le_next))
#define LIST_REMOVE(elm, field) do { \
QMD_SAVELINK(oldnext, (elm)->field.le_next); \
QMD_SAVELINK(oldprev, (elm)->field.le_prev); \
QMD_LIST_CHECK_NEXT(elm, field); \
QMD_LIST_CHECK_PREV(elm, field); \
if (LIST_NEXT((elm), field) != NULL) \
LIST_NEXT((elm), field)->field.le_prev = \
(elm)->field.le_prev; \
*(elm)->field.le_prev = LIST_NEXT((elm), field); \
TRASHIT(*oldnext); \
TRASHIT(*oldprev); \
} while (0)
#define LIST_SWAP(head1, head2, type, field) do { \
QUEUE_TYPEOF(type) *swap_tmp = LIST_FIRST(head1); \
LIST_FIRST((head1)) = LIST_FIRST((head2)); \
LIST_FIRST((head2)) = swap_tmp; \
if ((swap_tmp = LIST_FIRST((head1))) != NULL) \
swap_tmp->field.le_prev = &LIST_FIRST((head1)); \
if ((swap_tmp = LIST_FIRST((head2))) != NULL) \
swap_tmp->field.le_prev = &LIST_FIRST((head2)); \
} while (0)
/*
* Tail queue declarations.
*/
#define TAILQ_HEAD(name, type) \
struct name { \
struct type *tqh_first; /* first element */ \
struct type **tqh_last; /* addr of last next element */ \
TRACEBUF \
}
#define TAILQ_CLASS_HEAD(name, type) \
struct name { \
class type *tqh_first; /* first element */ \
class type **tqh_last; /* addr of last next element */ \
TRACEBUF \
}
#define TAILQ_HEAD_INITIALIZER(head) \
{ NULL, &(head).tqh_first, TRACEBUF_INITIALIZER }
#define TAILQ_ENTRY(type) \
struct { \
struct type *tqe_next; /* next element */ \
struct type **tqe_prev; /* address of previous next element */ \
TRACEBUF \
}
#define TAILQ_CLASS_ENTRY(type) \
struct { \
class type *tqe_next; /* next element */ \
class type **tqe_prev; /* address of previous next element */ \
TRACEBUF \
}
/*
* Tail queue functions.
*/
#if (defined(_KERNEL) && defined(INVARIANTS))
/*
* QMD_TAILQ_CHECK_HEAD(TAILQ_HEAD *head, TAILQ_ENTRY NAME)
*
* If the tailq is non-empty, validates that the first element of the tailq
* points back at 'head.'
*/
#define QMD_TAILQ_CHECK_HEAD(head, field) do { \
if (!TAILQ_EMPTY(head) && \
TAILQ_FIRST((head))->field.tqe_prev != \
&TAILQ_FIRST((head))) \
panic("Bad tailq head %p first->prev != head", (head)); \
} while (0)
/*
* QMD_TAILQ_CHECK_TAIL(TAILQ_HEAD *head, TAILQ_ENTRY NAME)
*
* Validates that the tail of the tailq is a pointer to pointer to NULL.
*/
#define QMD_TAILQ_CHECK_TAIL(head, field) do { \
if (*(head)->tqh_last != NULL) \
panic("Bad tailq NEXT(%p->tqh_last) != NULL", (head)); \
} while (0)
/*
* QMD_TAILQ_CHECK_NEXT(TYPE *elm, TAILQ_ENTRY NAME)
*
* If an element follows 'elm' in the tailq, validates that the next element
* points back at 'elm.'
*/
#define QMD_TAILQ_CHECK_NEXT(elm, field) do { \
if (TAILQ_NEXT((elm), field) != NULL && \
TAILQ_NEXT((elm), field)->field.tqe_prev != \
&((elm)->field.tqe_next)) \
panic("Bad link elm %p next->prev != elm", (elm)); \
} while (0)
/*
* QMD_TAILQ_CHECK_PREV(TYPE *elm, TAILQ_ENTRY NAME)
*
* Validates that the previous element (or head of the tailq) points to 'elm.'
*/
#define QMD_TAILQ_CHECK_PREV(elm, field) do { \
if (*(elm)->field.tqe_prev != (elm)) \
panic("Bad link elm %p prev->next != elm", (elm)); \
} while (0)
#else
#define QMD_TAILQ_CHECK_HEAD(head, field)
#define QMD_TAILQ_CHECK_TAIL(head, headname)
#define QMD_TAILQ_CHECK_NEXT(elm, field)
#define QMD_TAILQ_CHECK_PREV(elm, field)
#endif /* (_KERNEL && INVARIANTS) */
#define TAILQ_CONCAT(head1, head2, field) do { \
if (!TAILQ_EMPTY(head2)) { \
*(head1)->tqh_last = (head2)->tqh_first; \
(head2)->tqh_first->field.tqe_prev = (head1)->tqh_last; \
(head1)->tqh_last = (head2)->tqh_last; \
TAILQ_INIT((head2)); \
QMD_TRACE_HEAD(head1); \
QMD_TRACE_HEAD(head2); \
} \
} while (0)
#define TAILQ_EMPTY(head) ((head)->tqh_first == NULL)
#define TAILQ_FIRST(head) ((head)->tqh_first)
#define TAILQ_FOREACH(var, head, field) \
for ((var) = TAILQ_FIRST((head)); \
(var); \
(var) = TAILQ_NEXT((var), field))
#define TAILQ_FOREACH_FROM(var, head, field) \
for ((var) = ((var) ? (var) : TAILQ_FIRST((head))); \
(var); \
(var) = TAILQ_NEXT((var), field))
#define TAILQ_FOREACH_SAFE(var, head, field, tvar) \
for ((var) = TAILQ_FIRST((head)); \
(var) && ((tvar) = TAILQ_NEXT((var), field), 1); \
(var) = (tvar))
#define TAILQ_FOREACH_FROM_SAFE(var, head, field, tvar) \
for ((var) = ((var) ? (var) : TAILQ_FIRST((head))); \
(var) && ((tvar) = TAILQ_NEXT((var), field), 1); \
(var) = (tvar))
#define TAILQ_FOREACH_REVERSE(var, head, headname, field) \
for ((var) = TAILQ_LAST((head), headname); \
(var); \
(var) = TAILQ_PREV((var), headname, field))
#define TAILQ_FOREACH_REVERSE_FROM(var, head, headname, field) \
for ((var) = ((var) ? (var) : TAILQ_LAST((head), headname)); \
(var); \
(var) = TAILQ_PREV((var), headname, field))
#define TAILQ_FOREACH_REVERSE_SAFE(var, head, headname, field, tvar) \
for ((var) = TAILQ_LAST((head), headname); \
(var) && ((tvar) = TAILQ_PREV((var), headname, field), 1); \
(var) = (tvar))
#define TAILQ_FOREACH_REVERSE_FROM_SAFE(var, head, headname, field, tvar) \
for ((var) = ((var) ? (var) : TAILQ_LAST((head), headname)); \
(var) && ((tvar) = TAILQ_PREV((var), headname, field), 1); \
(var) = (tvar))
#define TAILQ_INIT(head) do { \
TAILQ_FIRST((head)) = NULL; \
(head)->tqh_last = &TAILQ_FIRST((head)); \
QMD_TRACE_HEAD(head); \
} while (0)
#define TAILQ_INSERT_AFTER(head, listelm, elm, field) do { \
QMD_TAILQ_CHECK_NEXT(listelm, field); \
if ((TAILQ_NEXT((elm), field) = TAILQ_NEXT((listelm), field)) != NULL)\
TAILQ_NEXT((elm), field)->field.tqe_prev = \
&TAILQ_NEXT((elm), field); \
else { \
(head)->tqh_last = &TAILQ_NEXT((elm), field); \
QMD_TRACE_HEAD(head); \
} \
TAILQ_NEXT((listelm), field) = (elm); \
(elm)->field.tqe_prev = &TAILQ_NEXT((listelm), field); \
QMD_TRACE_ELEM(&(elm)->field); \
QMD_TRACE_ELEM(&(listelm)->field); \
} while (0)
#define TAILQ_INSERT_BEFORE(listelm, elm, field) do { \
QMD_TAILQ_CHECK_PREV(listelm, field); \
(elm)->field.tqe_prev = (listelm)->field.tqe_prev; \
TAILQ_NEXT((elm), field) = (listelm); \
*(listelm)->field.tqe_prev = (elm); \
(listelm)->field.tqe_prev = &TAILQ_NEXT((elm), field); \
QMD_TRACE_ELEM(&(elm)->field); \
QMD_TRACE_ELEM(&(listelm)->field); \
} while (0)
#define TAILQ_INSERT_HEAD(head, elm, field) do { \
QMD_TAILQ_CHECK_HEAD(head, field); \
if ((TAILQ_NEXT((elm), field) = TAILQ_FIRST((head))) != NULL) \
TAILQ_FIRST((head))->field.tqe_prev = \
&TAILQ_NEXT((elm), field); \
else \
(head)->tqh_last = &TAILQ_NEXT((elm), field); \
TAILQ_FIRST((head)) = (elm); \
(elm)->field.tqe_prev = &TAILQ_FIRST((head)); \
QMD_TRACE_HEAD(head); \
QMD_TRACE_ELEM(&(elm)->field); \
} while (0)
#define TAILQ_INSERT_TAIL(head, elm, field) do { \
QMD_TAILQ_CHECK_TAIL(head, field); \
TAILQ_NEXT((elm), field) = NULL; \
(elm)->field.tqe_prev = (head)->tqh_last; \
*(head)->tqh_last = (elm); \
(head)->tqh_last = &TAILQ_NEXT((elm), field); \
QMD_TRACE_HEAD(head); \
QMD_TRACE_ELEM(&(elm)->field); \
} while (0)
#define TAILQ_LAST(head, headname) \
(*(((struct headname *)((head)->tqh_last))->tqh_last))
#define TAILQ_NEXT(elm, field) ((elm)->field.tqe_next)
#define TAILQ_PREV(elm, headname, field) \
(*(((struct headname *)((elm)->field.tqe_prev))->tqh_last))
#define TAILQ_REMOVE(head, elm, field) do { \
QMD_SAVELINK(oldnext, (elm)->field.tqe_next); \
QMD_SAVELINK(oldprev, (elm)->field.tqe_prev); \
QMD_TAILQ_CHECK_NEXT(elm, field); \
QMD_TAILQ_CHECK_PREV(elm, field); \
if ((TAILQ_NEXT((elm), field)) != NULL) \
TAILQ_NEXT((elm), field)->field.tqe_prev = \
(elm)->field.tqe_prev; \
else { \
(head)->tqh_last = (elm)->field.tqe_prev; \
QMD_TRACE_HEAD(head); \
} \
*(elm)->field.tqe_prev = TAILQ_NEXT((elm), field); \
TRASHIT(*oldnext); \
TRASHIT(*oldprev); \
QMD_TRACE_ELEM(&(elm)->field); \
} while (0)
#define TAILQ_SWAP(head1, head2, type, field) do { \
QUEUE_TYPEOF(type) *swap_first = (head1)->tqh_first; \
QUEUE_TYPEOF(type) **swap_last = (head1)->tqh_last; \
(head1)->tqh_first = (head2)->tqh_first; \
(head1)->tqh_last = (head2)->tqh_last; \
(head2)->tqh_first = swap_first; \
(head2)->tqh_last = swap_last; \
if ((swap_first = (head1)->tqh_first) != NULL) \
swap_first->field.tqe_prev = &(head1)->tqh_first; \
else \
(head1)->tqh_last = &(head1)->tqh_first; \
if ((swap_first = (head2)->tqh_first) != NULL) \
swap_first->field.tqe_prev = &(head2)->tqh_first; \
else \
(head2)->tqh_last = &(head2)->tqh_first; \
} while (0)
#endif /* !_SYS_QUEUE_H_ */

379
libmui/contrib/incbin.h Normal file
View File

@ -0,0 +1,379 @@
/**
* @file incbin.h
* @author Dale Weiler
* @brief Utility for including binary files
*
* Facilities for including binary files into the current translation unit and
* making use from them externally in other translation units.
*/
#ifndef INCBIN_HDR
#define INCBIN_HDR
#include <limits.h>
// Michel addition:
// Allow the included file to have an extra zero, to include text files
// as plain zero terminated strings
#ifdef INCBIN_TRAILING_ZERO
#define INCBIN_TRAIL INCBIN_BYTE "0\n"
#else
#define INCBIN_TRAIL
#endif
#if defined(__AVX512BW__) || \
defined(__AVX512CD__) || \
defined(__AVX512DQ__) || \
defined(__AVX512ER__) || \
defined(__AVX512PF__) || \
defined(__AVX512VL__) || \
defined(__AVX512F__)
# define INCBIN_ALIGNMENT_INDEX 6
#elif defined(__AVX__) || \
defined(__AVX2__)
# define INCBIN_ALIGNMENT_INDEX 5
#elif defined(__SSE__) || \
defined(__SSE2__) || \
defined(__SSE3__) || \
defined(__SSSE3__) || \
defined(__SSE4_1__) || \
defined(__SSE4_2__) || \
defined(__neon__)
# define INCBIN_ALIGNMENT_INDEX 4
#elif ULONG_MAX != 0xffffffffu
# define INCBIN_ALIGNMENT_INDEX 3
# else
# define INCBIN_ALIGNMENT_INDEX 2
#endif
/* Lookup table of (1 << n) where `n' is `INCBIN_ALIGNMENT_INDEX' */
#define INCBIN_ALIGN_SHIFT_0 1
#define INCBIN_ALIGN_SHIFT_1 2
#define INCBIN_ALIGN_SHIFT_2 4
#define INCBIN_ALIGN_SHIFT_3 8
#define INCBIN_ALIGN_SHIFT_4 16
#define INCBIN_ALIGN_SHIFT_5 32
#define INCBIN_ALIGN_SHIFT_6 64
/* Actual alignment value */
#define INCBIN_ALIGNMENT \
INCBIN_CONCATENATE( \
INCBIN_CONCATENATE(INCBIN_ALIGN_SHIFT, _), \
INCBIN_ALIGNMENT_INDEX)
/* Stringize */
#define INCBIN_STR(X) \
#X
#define INCBIN_STRINGIZE(X) \
INCBIN_STR(X)
/* Concatenate */
#define INCBIN_CAT(X, Y) \
X ## Y
#define INCBIN_CONCATENATE(X, Y) \
INCBIN_CAT(X, Y)
/* Deferred macro expansion */
#define INCBIN_EVAL(X) \
X
#define INCBIN_INVOKE(N, ...) \
INCBIN_EVAL(N(__VA_ARGS__))
/* Green Hills uses a different directive for including binary data */
#if defined(__ghs__)
# if (__ghs_asm == 2)
# define INCBIN_MACRO ".file"
/* Or consider the ".myrawdata" entry in the ld file */
# else
# define INCBIN_MACRO "\tINCBIN"
# endif
#else
# define INCBIN_MACRO ".incbin"
#endif
#ifndef _MSC_VER
# define INCBIN_ALIGN \
__attribute__((aligned(INCBIN_ALIGNMENT)))
#else
# define INCBIN_ALIGN __declspec(align(INCBIN_ALIGNMENT))
#endif
#if defined(__arm__) || /* GNU C and RealView */ \
defined(__arm) || /* Diab */ \
defined(_ARM) /* ImageCraft */
# define INCBIN_ARM
#endif
#ifdef __GNUC__
/* Utilize .balign where supported */
# define INCBIN_ALIGN_HOST ".balign " INCBIN_STRINGIZE(INCBIN_ALIGNMENT) "\n"
# define INCBIN_ALIGN_BYTE ".balign 1\n"
#elif defined(INCBIN_ARM)
/*
* On arm assemblers, the alignment value is calculated as (1 << n) where `n' is
* the shift count. This is the value passed to `.align'
*/
# define INCBIN_ALIGN_HOST ".align " INCBIN_STRINGIZE(INCBIN_ALIGNMENT_INDEX) "\n"
# define INCBIN_ALIGN_BYTE ".align 0\n"
#else
/* We assume other inline assembler's treat `.align' as `.balign' */
# define INCBIN_ALIGN_HOST ".align " INCBIN_STRINGIZE(INCBIN_ALIGNMENT) "\n"
# define INCBIN_ALIGN_BYTE ".align 1\n"
#endif
/* INCBIN_CONST is used by incbin.c generated files */
#if defined(__cplusplus)
# define INCBIN_EXTERNAL extern "C"
# define INCBIN_CONST extern const
#else
# define INCBIN_EXTERNAL extern
# define INCBIN_CONST const
#endif
/**
* @brief Optionally override the linker section into which data is emitted.
*
* @warning If you use this facility, you'll have to deal with platform-specific linker output
* section naming on your own
*
* Overriding the default linker output section, e.g for esp8266/Arduino:
* @code
* #define INCBIN_OUTPUT_SECTION ".irom.text"
* #include "incbin.h"
* INCBIN(Foo, "foo.txt");
* // Data is emitted into program memory that never gets copied to RAM
* @endcode
*/
#if !defined(INCBIN_OUTPUT_SECTION)
# if defined(__APPLE__)
# define INCBIN_OUTPUT_SECTION ".const_data"
# else
# define INCBIN_OUTPUT_SECTION ".rodata"
# endif
#endif
#if defined(__APPLE__)
/* The directives are different for Apple branded compilers */
# define INCBIN_SECTION INCBIN_OUTPUT_SECTION "\n"
# define INCBIN_GLOBAL(NAME) ".globl " INCBIN_MANGLE INCBIN_STRINGIZE(INCBIN_PREFIX) #NAME "\n"
# define INCBIN_INT ".long "
# define INCBIN_MANGLE "_"
# define INCBIN_BYTE ".byte "
# define INCBIN_TYPE(...)
#else
# define INCBIN_SECTION ".section " INCBIN_OUTPUT_SECTION "\n"
# define INCBIN_GLOBAL(NAME) ".global " INCBIN_STRINGIZE(INCBIN_PREFIX) #NAME "\n"
# if defined(__ghs__)
# define INCBIN_INT ".word "
# else
# define INCBIN_INT ".int "
# endif
# if defined(__USER_LABEL_PREFIX__)
# define INCBIN_MANGLE INCBIN_STRINGIZE(__USER_LABEL_PREFIX__)
# else
# define INCBIN_MANGLE ""
# endif
# if defined(INCBIN_ARM)
/* On arm assemblers, `@' is used as a line comment token */
# define INCBIN_TYPE(NAME) ".type " INCBIN_STRINGIZE(INCBIN_PREFIX) #NAME ", %object\n"
# elif defined(__MINGW32__) || defined(__MINGW64__)
/* Mingw doesn't support this directive either */
# define INCBIN_TYPE(NAME)
# else
/* It's safe to use `@' on other architectures */
# define INCBIN_TYPE(NAME) ".type " INCBIN_STRINGIZE(INCBIN_PREFIX) #NAME ", @object\n"
# endif
# define INCBIN_BYTE ".byte "
#endif
/* List of style types used for symbol names */
#define INCBIN_STYLE_CAMEL 0
#define INCBIN_STYLE_SNAKE 1
/**
* @brief Specify the prefix to use for symbol names.
*
* By default this is `g', producing symbols of the form:
* @code
* #include "incbin.h"
* INCBIN(Foo, "foo.txt");
*
* // Now you have the following symbols:
* // const unsigned char gFooData[];
* // const unsigned char *const gFooEnd;
* // const unsigned int gFooSize;
* @endcode
*
* If however you specify a prefix before including: e.g:
* @code
* #define INCBIN_PREFIX incbin
* #include "incbin.h"
* INCBIN(Foo, "foo.txt");
*
* // Now you have the following symbols instead:
* // const unsigned char incbinFooData[];
* // const unsigned char *const incbinFooEnd;
* // const unsigned int incbinFooSize;
* @endcode
*/
#if !defined(INCBIN_PREFIX)
# define INCBIN_PREFIX g
#endif
/**
* @brief Specify the style used for symbol names.
*
* Possible options are
* - INCBIN_STYLE_CAMEL "CamelCase"
* - INCBIN_STYLE_SNAKE "snake_case"
*
* Default option is *INCBIN_STYLE_CAMEL* producing symbols of the form:
* @code
* #include "incbin.h"
* INCBIN(Foo, "foo.txt");
*
* // Now you have the following symbols:
* // const unsigned char <prefix>FooData[];
* // const unsigned char *const <prefix>FooEnd;
* // const unsigned int <prefix>FooSize;
* @endcode
*
* If however you specify a style before including: e.g:
* @code
* #define INCBIN_STYLE INCBIN_STYLE_SNAKE
* #include "incbin.h"
* INCBIN(foo, "foo.txt");
*
* // Now you have the following symbols:
* // const unsigned char <prefix>foo_data[];
* // const unsigned char *const <prefix>foo_end;
* // const unsigned int <prefix>foo_size;
* @endcode
*/
#if !defined(INCBIN_STYLE)
# define INCBIN_STYLE INCBIN_STYLE_CAMEL
#endif
/* Style lookup tables */
#define INCBIN_STYLE_0_DATA Data
#define INCBIN_STYLE_0_END End
#define INCBIN_STYLE_0_SIZE Size
#define INCBIN_STYLE_1_DATA _data
#define INCBIN_STYLE_1_END _end
#define INCBIN_STYLE_1_SIZE _size
/* Style lookup: returning identifier */
#define INCBIN_STYLE_IDENT(TYPE) \
INCBIN_CONCATENATE( \
INCBIN_STYLE_, \
INCBIN_CONCATENATE( \
INCBIN_EVAL(INCBIN_STYLE), \
INCBIN_CONCATENATE(_, TYPE)))
/* Style lookup: returning string literal */
#define INCBIN_STYLE_STRING(TYPE) \
INCBIN_STRINGIZE( \
INCBIN_STYLE_IDENT(TYPE)) \
/* Generate the global labels by indirectly invoking the macro with our style
* type and concatenating the name against them. */
#define INCBIN_GLOBAL_LABELS(NAME, TYPE) \
INCBIN_INVOKE( \
INCBIN_GLOBAL, \
INCBIN_CONCATENATE( \
NAME, \
INCBIN_INVOKE( \
INCBIN_STYLE_IDENT, \
TYPE))) \
INCBIN_INVOKE( \
INCBIN_TYPE, \
INCBIN_CONCATENATE( \
NAME, \
INCBIN_INVOKE( \
INCBIN_STYLE_IDENT, \
TYPE)))
/**
* @brief Externally reference binary data included in another translation unit.
*
* Produces three external symbols that reference the binary data included in
* another translation unit.
*
* The symbol names are a concatenation of `INCBIN_PREFIX' before *NAME*; with
* "Data", as well as "End" and "Size" after. An example is provided below.
*
* @param NAME The name given for the binary data
*
* @code
* INCBIN_EXTERN(Foo);
*
* // Now you have the following symbols:
* // extern const unsigned char <prefix>FooData[];
* // extern const unsigned char *const <prefix>FooEnd;
* // extern const unsigned int <prefix>FooSize;
* @endcode
*/
#define INCBIN_EXTERN(NAME) \
INCBIN_EXTERNAL const INCBIN_ALIGN unsigned char \
INCBIN_CONCATENATE( \
INCBIN_CONCATENATE(INCBIN_PREFIX, NAME), \
INCBIN_STYLE_IDENT(DATA))[]; \
INCBIN_EXTERNAL const INCBIN_ALIGN unsigned char *const \
INCBIN_CONCATENATE( \
INCBIN_CONCATENATE(INCBIN_PREFIX, NAME), \
INCBIN_STYLE_IDENT(END)); \
INCBIN_EXTERNAL const unsigned int \
INCBIN_CONCATENATE( \
INCBIN_CONCATENATE(INCBIN_PREFIX, NAME), \
INCBIN_STYLE_IDENT(SIZE))
/**
* @brief Include a binary file into the current translation unit.
*
* Includes a binary file into the current translation unit, producing three symbols
* for objects that encode the data and size respectively.
*
* The symbol names are a concatenation of `INCBIN_PREFIX' before *NAME*; with
* "Data", as well as "End" and "Size" after. An example is provided below.
*
* @param NAME The name to associate with this binary data (as an identifier.)
* @param FILENAME The file to include (as a string literal.)
*
* @code
* INCBIN(Icon, "icon.png");
*
* // Now you have the following symbols:
* // const unsigned char <prefix>IconData[];
* // const unsigned char *const <prefix>IconEnd;
* // const unsigned int <prefix>IconSize;
* @endcode
*
* @warning This must be used in global scope
* @warning The identifiers may be different if INCBIN_STYLE is not default
*
* To externally reference the data included by this in another translation unit
* please @see INCBIN_EXTERN.
*/
#ifdef _MSC_VER
#define INCBIN(NAME, FILENAME) \
INCBIN_EXTERN(NAME)
#else
#define INCBIN(NAME, FILENAME) \
__asm__(INCBIN_SECTION \
INCBIN_GLOBAL_LABELS(NAME, DATA) \
INCBIN_ALIGN_HOST \
INCBIN_MANGLE INCBIN_STRINGIZE(INCBIN_PREFIX) #NAME INCBIN_STYLE_STRING(DATA) ":\n" \
INCBIN_MACRO " \"" FILENAME "\"\n" \
INCBIN_TRAIL \
INCBIN_GLOBAL_LABELS(NAME, END) \
INCBIN_ALIGN_BYTE \
INCBIN_MANGLE INCBIN_STRINGIZE(INCBIN_PREFIX) #NAME INCBIN_STYLE_STRING(END) ":\n" \
INCBIN_BYTE "1\n" \
INCBIN_GLOBAL_LABELS(NAME, SIZE) \
INCBIN_ALIGN_HOST \
INCBIN_MANGLE INCBIN_STRINGIZE(INCBIN_PREFIX) #NAME INCBIN_STYLE_STRING(SIZE) ":\n" \
INCBIN_INT INCBIN_MANGLE INCBIN_STRINGIZE(INCBIN_PREFIX) #NAME INCBIN_STYLE_STRING(END) " - " \
INCBIN_MANGLE INCBIN_STRINGIZE(INCBIN_PREFIX) #NAME INCBIN_STYLE_STRING(DATA) "\n" \
INCBIN_ALIGN_HOST \
".text\n" \
); \
INCBIN_EXTERN(NAME)
#endif
#endif

BIN
libmui/doc/alert.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 20 KiB

BIN
libmui/doc/control_demo.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 51 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 39 KiB

BIN
libmui/doc/static_text.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 44 KiB

BIN
libmui/doc/widgets.gif Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 384 KiB

View File

@ -22,7 +22,7 @@ OS2Version: 0
OS2_WeightWidthSlopeOnly: 0
OS2_UseTypoMetrics: 0
CreationTime: 830956166
ModificationTime: 1700234837
ModificationTime: 1711450964
PfmFamily: 81
TTFWeight: 400
TTFWidth: 5
@ -800,10 +800,10 @@ NameList: AGL For New Fonts
DisplaySize: -96
AntiAlias: 1
FitToEm: 1
WinInfo: 323 19 9
WinInfo: 230 23 12
BeginPrivate: 0
EndPrivate
BeginChars: 488 470
BeginChars: 489 471
StartChar: .notdef
Encoding: 409 -1 0
@ -857,7 +857,7 @@ EndChar
StartChar: .null
Encoding: 5 8 1
AltUni2: 00001d.ffffffff.0 002400.ffffffff.0 00001d.ffffffff.0 002400.ffffffff.0
AltUni2: 002400.ffffffff.0 00001d.ffffffff.0 002400.ffffffff.0 00001d.ffffffff.0
Width: 0
GlyphClass: 1
Flags: W
@ -2429,7 +2429,7 @@ EndChar
StartChar: hyphen
Encoding: 36 45 16
AltUni2: 0000ad.ffffffff.0 002010.ffffffff.0 0000ad.ffffffff.0 002010.ffffffff.0
AltUni2: 002010.ffffffff.0 0000ad.ffffffff.0 002010.ffffffff.0 0000ad.ffffffff.0
Width: 1190
GlyphClass: 1
Flags: W
@ -30762,24 +30762,24 @@ Refer: 15 44 N 1 0 0 1 350 -451 2
Validated: 1
EndChar
StartChar: uniE0B0
Encoding: 474 57520 456
StartChar: uni25B6
Encoding: 474 9654 456
Width: 1051
VWidth: 1000
Flags: W
LayerCount: 2
Fore
SplineSet
354.5 59 m 0,0,1
294.5 -1 294.5 -1 206.900390625 -2.2001953125 c 0,2,3
122.900390625 -2.2001953125 122.900390625 -2.2001953125 61.7001953125 59 c 128,-1,4
0.5 120.200195312 0.5 120.200195312 0.5 207.799804688 c 2,5,-1
0.5 1242.20019531 l 2,6,7
0.5 1328.59960938 0.5 1328.59960938 61.7001953125 1390.40039062 c 128,-1,8
122.900390625 1452.20019531 122.900390625 1452.20019531 206.900390625 1452.20019531 c 0,9,10
294.5 1452.20019531 294.5 1452.20019531 354.5 1391 c 2,11,-1
1038.5 725 l 1,12,13
628.099609375 325.400390625 628.099609375 325.400390625 354.5 59 c 0,0,1
354.5 59 m 4,0,1
294.5 -1 294.5 -1 206.900390625 -2.2001953125 c 4,2,3
122.900390625 -2.2001953125 122.900390625 -2.2001953125 61.7001953125 59 c 132,-1,4
0.5 120.200195312 0.5 120.200195312 0.5 207.799804688 c 6,5,-1
0.5 1242.20019531 l 6,6,7
0.5 1328.59960938 0.5 1328.59960938 61.7001953125 1390.40039062 c 132,-1,8
122.900390625 1452.20019531 122.900390625 1452.20019531 206.900390625 1452.20019531 c 4,9,10
294.5 1452.20019531 294.5 1452.20019531 354.5 1391 c 6,11,-1
1038.5 725 l 5,12,13
628.099609375 325.400390625 628.099609375 325.400390625 354.5 59 c 4,0,1
EndSplineSet
Validated: 1
EndChar
@ -31663,39 +31663,62 @@ StartChar: uniE0B1
Encoding: 487 57521 469
Width: 2331
VWidth: 1000
Flags: W
LayerCount: 2
Fore
SplineSet
2408.40039062 129 m 5,0,-1
2408.40039062 -105 l 5,1,-1
1948.79980469 -105 l 6,2,3
1765.20019531 -105 1765.20019531 -105 1632 6 c 132,-1,4
1498.79980469 117 1498.79980469 117 1498.79980469 298.200195312 c 4,5,6
1498.79980469 454.200195312 1498.79980469 454.200195312 1633.79980469 574.799804688 c 132,-1,7
1768.79980469 695.400390625 1768.79980469 695.400390625 1950 695.400390625 c 6,8,-1
2408.40039062 695.400390625 l 5,9,-1
2408.40039062 461.400390625 l 5,10,-1
2227.20019531 196.200195312 l 5,11,-1
1918.79980469 196.200195312 l 5,12,-1
2110.79980469 461.400390625 l 5,13,-1
1970.40039062 461.400390625 l 6,14,15
1892.40039062 461.400390625 1892.40039062 461.400390625 1844.40039062 416.400390625 c 132,-1,16
1796.40039062 371.400390625 1796.40039062 371.400390625 1796.40039062 301.799804688 c 4,17,18
1796.40039062 227.400390625 1796.40039062 227.400390625 1847.40039062 178.200195312 c 132,-1,19
1898.40039062 129 1898.40039062 129 1970.40039062 129 c 6,20,-1
2408.40039062 129 l 5,0,-1
1244.40039062 1611 m 5,21,-1
1777.20019531 1611 l 5,22,-1
1185.60058594 -111 l 5,23,24
1184.40039062 -111 1184.40039062 -111 654 -111 c 5,25,-1
1244.40039062 1611 l 5,21,-1
505.200195312 1611 m 5,26,-1
1039.20019531 1611 l 5,27,-1
447.600585938 -111 l 5,28,-1
-84 -111 l 5,29,-1
505.200195312 1611 l 5,26,-1
EndSplineSet
Validated: 524289
EndChar
StartChar: triagdn
Encoding: 488 9660 470
Width: 1467
VWidth: 1000
Flags: WO
LayerCount: 2
Fore
SplineSet
2408.40039062 72 m 1,0,-1
2408.40039062 -162 l 1,1,-1
1948.79980469 -162 l 2,2,3
1765.20019531 -162 1765.20019531 -162 1632 -51 c 128,-1,4
1498.79980469 60 1498.79980469 60 1498.79980469 241.200195312 c 0,5,6
1498.79980469 397.200195312 1498.79980469 397.200195312 1633.79980469 517.799804688 c 128,-1,7
1768.79980469 638.400390625 1768.79980469 638.400390625 1950 638.400390625 c 2,8,-1
2408.40039062 638.400390625 l 1,9,-1
2408.40039062 404.400390625 l 1,10,-1
2227.20019531 139.200195312 l 1,11,-1
1918.79980469 139.200195312 l 1,12,-1
2110.79980469 404.400390625 l 1,13,-1
1970.40039062 404.400390625 l 2,14,15
1892.40039062 404.400390625 1892.40039062 404.400390625 1844.40039062 359.400390625 c 128,-1,16
1796.40039062 314.400390625 1796.40039062 314.400390625 1796.40039062 244.799804688 c 0,17,18
1796.40039062 170.400390625 1796.40039062 170.400390625 1847.40039062 121.200195312 c 128,-1,19
1898.40039062 72 1898.40039062 72 1970.40039062 72 c 2,20,-1
2408.40039062 72 l 1,0,-1
1244.40039062 1554 m 1,21,-1
1777.20019531 1554 l 1,22,-1
1185.60058594 -168 l 1,23,24
1184.40039062 -168 1184.40039062 -168 654 -168 c 1,25,-1
1244.40039062 1554 l 1,21,-1
505.200195312 1554 m 1,26,-1
1039.20019531 1554 l 1,27,-1
447.600585938 -168 l 1,28,-1
-84 -168 l 1,29,-1
505.200195312 1554 l 1,26,-1
65.5 890 m 0,0,1
5.5 950 5.5 950 4.2998046875 1037.59960938 c 0,2,3
4.2998046875 1121.59960938 4.2998046875 1121.59960938 65.5 1182.79980469 c 128,-1,4
126.700195312 1244 126.700195312 1244 214.299804688 1244 c 2,5,-1
1248.70019531 1244 l 2,6,7
1335.09960938 1244 1335.09960938 1244 1396.90039062 1182.79980469 c 128,-1,8
1458.70019531 1121.59960938 1458.70019531 1121.59960938 1458.70019531 1037.59960938 c 0,9,10
1458.70019531 950 1458.70019531 950 1397.5 890 c 2,11,-1
731.5 206 l 1,12,13
331.900390625 616.400390625 331.900390625 616.400390625 65.5 890 c 0,0,1
EndSplineSet
Validated: 524289
EndChar
EndChars
EndSplineFont

Binary file not shown.

Binary file not shown.

File diff suppressed because it is too large Load Diff

View File

@ -1,366 +0,0 @@
/*
* mui_cdef_scrollbar.c
*
* Copyright (C) 2023 Michel Pollet <buserror@gmail.com>
*
* SPDX-License-Identifier: MIT
*/
#include <stdio.h>
#include <stdlib.h>
#include "mui.h"
#include "cg.h"
enum {
MUI_CONTROL_SCROLLBAR = FCC('s','b','a','r'),
};
extern const mui_control_color_t mui_control_color[MUI_CONTROL_STATE_COUNT];
enum mui_sb_part_e {
MUI_SB_PART_FRAME = 0,
MUI_SB_PART_UP,
MUI_SB_PART_DOWN,
MUI_SB_PART_PAGEUP,
MUI_SB_PART_PAGEDOWN,
MUI_SB_PART_THUMB,
MUI_SB_PART_COUNT,
};
typedef struct mui_scrollbar_control_t {
mui_control_t control;
uint32_t visible;
uint32_t max;
c2_pt_t drag_offset;
uint32_t saved_value; // to handle 'snapback'
} mui_scrollbar_control_t;
static void
mui_scrollbar_make_rects(
mui_control_t * c,
c2_rect_t * parts)
{
c2_rect_t f = c->frame;
c2_rect_offset(&f, c->win->content.l, c->win->content.t);
parts[MUI_SB_PART_FRAME] = f;
c2_rect_t part = f;
part.b = part.t + c2_rect_width(&part);
parts[MUI_SB_PART_UP] = part;
part = f;
part.t = part.b - c2_rect_width(&part);
parts[MUI_SB_PART_DOWN] = part;
mui_scrollbar_control_t *sb = (mui_scrollbar_control_t *)c;
if (sb->max <= sb->visible) {
c2_rect_t z = {};
parts[MUI_SB_PART_THUMB] = z;
parts[MUI_SB_PART_PAGEUP] = z;
parts[MUI_SB_PART_PAGEDOWN] = z;
return;
}
part = f;
part.t = parts[MUI_SB_PART_UP].b + 1;
part.b = parts[MUI_SB_PART_DOWN].t - 1;
float visible = sb->visible / (float)sb->max;
float thumb_size = visible * sb->visible;
if (thumb_size < 20)
thumb_size = 20;
float thumb_pos = c->value / ((float)sb->max- sb->visible);
float thumb_offset = 0.5 + thumb_pos * (c2_rect_height(&part) - thumb_size);
// printf("%s visible:%.2f ts: %.2f thumb_pos:%.2f thumb_offset:%.2f\n",
// __func__, visible, thumb_size, thumb_pos, thumb_offset);
part.b = part.t + thumb_size;
c2_rect_offset(&part, 0, thumb_offset);
if (part.b > parts[MUI_SB_PART_DOWN].t) {
c2_rect_offset(&part, 0, parts[MUI_SB_PART_DOWN].t - part.b);
}
parts[MUI_SB_PART_THUMB] = part;
part = f;
part.t = parts[MUI_SB_PART_UP].b + 1;
part.b = parts[MUI_SB_PART_THUMB].t - 1;
parts[MUI_SB_PART_PAGEUP] = part;
part = f;
part.t = parts[MUI_SB_PART_THUMB].b + 1;
part.b = parts[MUI_SB_PART_DOWN].t - 1;
parts[MUI_SB_PART_PAGEDOWN] = part;
}
static void
mui_scrollbar_draw(
mui_window_t * win,
mui_control_t * c,
mui_drawable_t *dr )
{
c2_rect_t f = c->frame;
c2_rect_offset(&f, win->content.l, win->content.t);
struct cg_ctx_t * cg = mui_drawable_get_cg(dr);
cg_set_line_width(cg, 1);
cg_rectangle(cg, f.l, f.t,
c2_rect_width(&f), c2_rect_height(&f));
cg_set_source_color(cg, &CG_COLOR(mui_control_color[c->state].fill));
cg_fill_preserve(cg);
cg_set_source_color(cg, &CG_COLOR(mui_control_color[c->state].frame));
cg_stroke(cg);
mui_font_t * icons = mui_font_find(win->ui, "icon_small");
c2_rect_t parts[MUI_SB_PART_COUNT];
mui_scrollbar_make_rects(c, parts);
mui_color_t contentFill = MUI_COLOR(0xa0a0a0ff);
mui_color_t decoColor = MUI_COLOR(0x666666ff);
c2_rect_t pf;
pf = parts[MUI_SB_PART_UP];
cg_rectangle(cg, pf.l, pf.t,
c2_rect_width(&pf), c2_rect_height(&pf));
cg_set_source_color(cg,
c->flags.hit_part == MUI_SB_PART_UP ?
&CG_COLOR(decoColor) :
&CG_COLOR(mui_control_color[c->state].fill));
cg_fill_preserve(cg);
cg_set_source_color(cg, &CG_COLOR(mui_control_color[c->state].frame));
cg_stroke(cg);
stb_ttc_measure m = {};
mui_font_text_measure(icons, "", &m);
pf.l = pf.l + (c2_rect_width(&pf) - m.x1 - m.x0) / 2;
mui_font_text_draw(icons, dr, pf.tl, "", 0,
mui_control_color[c->state].text);
pf = parts[MUI_SB_PART_DOWN];
cg_rectangle(cg, pf.l, pf.t,
c2_rect_width(&pf), c2_rect_height(&pf));
cg_set_source_color(cg,
c->flags.hit_part == MUI_SB_PART_DOWN ?
&CG_COLOR(decoColor) :
&CG_COLOR(mui_control_color[c->state].fill));
cg_fill_preserve(cg);
cg_set_source_color(cg, &CG_COLOR(mui_control_color[c->state].frame));
cg_stroke(cg);
mui_font_text_measure(icons, "", &m);
pf.l = pf.l + (c2_rect_width(&pf) - m.x1 - m.x0) / 2;
mui_font_text_draw(icons, dr, pf.tl, "", 0,
mui_control_color[c->state].text);
pf = parts[MUI_SB_PART_PAGEUP];
if (c2_rect_height(&pf) > 0) {
cg_rectangle(cg, pf.l, pf.t,
c2_rect_width(&pf), c2_rect_height(&pf));
cg_set_source_color(cg,
c->flags.hit_part == MUI_SB_PART_PAGEUP ?
&CG_COLOR(decoColor) :
&CG_COLOR(contentFill));
cg_fill(cg);
}
pf = parts[MUI_SB_PART_PAGEDOWN];
if (c2_rect_height(&pf) > 0) {
cg_rectangle(cg, pf.l, pf.t,
c2_rect_width(&pf), c2_rect_height(&pf));
cg_set_source_color(cg,
c->flags.hit_part == MUI_SB_PART_PAGEDOWN ?
&CG_COLOR(decoColor) :
&CG_COLOR(contentFill));
cg_fill(cg);
}
pf = parts[MUI_SB_PART_THUMB];
if (c2_rect_height(&pf) > 0) {
cg_rectangle(cg, pf.l, pf.t,
c2_rect_width(&pf), c2_rect_height(&pf));
cg_set_source_color(cg,
c->flags.hit_part == MUI_SB_PART_THUMB ?
&CG_COLOR(decoColor) :
&CG_COLOR(mui_control_color[c->state].fill));
cg_fill_preserve(cg);
cg_set_source_color(cg,
&CG_COLOR(mui_control_color[c->state].frame));
cg_stroke(cg);
}
}
static void
_mui_scrollbar_scroll(
mui_control_t * c,
int32_t delta )
{
mui_scrollbar_control_t *sb = (mui_scrollbar_control_t *)c;
int32_t v = (int32_t)c->value + delta;
if (v < 0)
v = 0;
if (v > ((int32_t)sb->max - (int32_t)sb->visible))
v = sb->max - sb->visible;
c->value = v;
mui_control_inval(c);
mui_control_action(c, MUI_CONTROL_ACTION_VALUE_CHANGED, NULL);
}
static bool
mui_scrollbar_mouse(
struct mui_control_t * c,
mui_event_t * ev)
{
mui_scrollbar_control_t *sb = (mui_scrollbar_control_t *)c;
c2_rect_t parts[MUI_SB_PART_COUNT];
mui_scrollbar_make_rects(c, parts);
switch (ev->type) {
case MUI_EVENT_BUTTONDOWN: {
for (int i = 1; i < MUI_SB_PART_COUNT; ++i) {
if (c2_rect_contains_pt(&parts[i], &ev->mouse.where)) {
c->flags.hit_part = i;
sb->drag_offset.x =
ev->mouse.where.x - parts[i].l;
sb->drag_offset.y =
ev->mouse.where.y - parts[i].t;
sb->saved_value = c->value;
break;
}
}
int part = c->flags.hit_part % MUI_SB_PART_COUNT;
switch (part) {
case MUI_SB_PART_DOWN:
case MUI_SB_PART_UP: {
_mui_scrollbar_scroll(c,
part == MUI_SB_PART_UP ? -30 : 30);
} break;
case MUI_SB_PART_PAGEUP:
case MUI_SB_PART_PAGEDOWN: {
int32_t offset = sb->visible;
_mui_scrollbar_scroll(c,
part == MUI_SB_PART_PAGEUP ?
-offset : offset);
} break;
case MUI_SB_PART_THUMB:
mui_control_inval(c);
break;
}
// printf("%s hit part %d\n", __func__, c->flags.hit_part);
} break;
case MUI_EVENT_DRAG: {
if (!c->flags.hit_part)
break;
int part = c->flags.hit_part % MUI_SB_PART_COUNT;
c2_rect_t test_rect = parts[part];
if (part == MUI_SB_PART_THUMB)
c2_rect_inset(&test_rect, -60, -60);
if (c2_rect_contains_pt(&test_rect, &ev->mouse.where)) {
c->flags.hit_part = part;
switch (part) {
case MUI_SB_PART_THUMB: {
c2_rect_t nt = parts[part];
c2_rect_offset(&nt, 0,
-(nt.t + parts[MUI_SB_PART_UP].b) +
ev->mouse.where.y - sb->drag_offset.y);
// printf("%s thumb %s\n", __func__, c2_rect_as_str(&nt));
if (nt.t < 0)
c2_rect_offset(&nt, 0, -nt.t);
if (nt.b > parts[MUI_SB_PART_DOWN].t)
c2_rect_offset(&nt, 0, parts[MUI_SB_PART_DOWN].t - nt.b);
int max_pixels = parts[MUI_SB_PART_DOWN].t -
parts[MUI_SB_PART_UP].b -
c2_rect_height(&nt);
uint32_t nv = nt.t * (sb->max - sb->visible) / max_pixels;
if (nv > (sb->max - sb->visible))
nv = sb->max - sb->visible;
c->value = nv;
// printf("v is %d vs %d max %d = %d new val %d\n",
// nt.t, max_pixels, sb->max, nv,
// mui_control_get_value(c));
mui_control_inval(c);
mui_control_action(c, MUI_CONTROL_ACTION_VALUE_CHANGED, NULL);
} break;
}
} else {
c->flags.hit_part = part + MUI_SB_PART_COUNT;
if (part == MUI_SB_PART_THUMB) {
c->value = sb->saved_value;
mui_control_inval(c);
mui_control_action(c, MUI_CONTROL_ACTION_VALUE_CHANGED, NULL);
}
}
} break;
case MUI_EVENT_BUTTONUP: {
if (!c->flags.hit_part)
break;
mui_control_inval(c);
c->flags.hit_part = 0;
} break;
}
return true;
}
static bool
mui_cdef_scrollbar(
struct mui_control_t * c,
uint8_t what,
void * param)
{
switch (what) {
case MUI_CDEF_INIT:
break;
case MUI_CDEF_DISPOSE:
break;
case MUI_CDEF_DRAW: {
mui_drawable_t * dr = param;
mui_scrollbar_draw(c->win, c, dr);
} break;
case MUI_CDEF_EVENT: {
// printf("%s event\n", __func__);
mui_event_t *ev = param;
switch (ev->type) {
case MUI_EVENT_BUTTONUP:
case MUI_EVENT_DRAG:
case MUI_EVENT_BUTTONDOWN: {
return mui_scrollbar_mouse(c, ev);
} break;
}
} break;
}
return false;
}
uint32_t
mui_scrollbar_get_max(
mui_control_t * c)
{
mui_scrollbar_control_t *sb = (mui_scrollbar_control_t *)c;
return sb->max;
}
void
mui_scrollbar_set_max(
mui_control_t * c,
uint32_t max)
{
mui_scrollbar_control_t *sb = (mui_scrollbar_control_t *)c;
sb->max = max;
mui_control_inval(c);
}
void
mui_scrollbar_set_page(
mui_control_t * c,
uint32_t page)
{
mui_scrollbar_control_t *sb = (mui_scrollbar_control_t *)c;
sb->visible = page;
mui_control_inval(c);
}
mui_control_t *
mui_scrollbar_new(
mui_window_t * win,
c2_rect_t frame,
uint32_t uid )
{
return mui_control_new(
win, MUI_CONTROL_SCROLLBAR, mui_cdef_scrollbar,
frame, NULL, uid,
sizeof(mui_scrollbar_control_t));
}

View File

@ -1,5 +1,5 @@
/*
* mui_playground.c
* mui_shell.c
*
* Copyright (C) 2023 Michel Pollet <buserror@gmail.com>
*
@ -9,18 +9,22 @@
#define MUI_HAS_XKB 1
#if MUI_HAS_XCB
#include <sys/stat.h>
#include <stdbool.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <libgen.h>
#include <ctype.h>
#include <fcntl.h>
#include <dlfcn.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <xcb/xcb.h>
#include <xcb/shm.h>
#include <xcb/xcb_image.h>
#include <xcb/randr.h>
//#include <xcb/randr.h>
#if MUI_HAS_XKB
#include <xcb/xkb.h>
@ -32,7 +36,9 @@ struct xkb_state;
#endif
#include "mui.h"
#include "mui_plugin.h"
#include "mui_shell_plugin.h"
typedef struct mui_xcb_t {
mui_t ui;
@ -42,10 +48,11 @@ typedef struct mui_xcb_t {
float ui_scale_x, ui_scale_y;
c2_pt_t size;
xcb_connection_t * xcb;
xcb_image_t * xcb_image; // if no shared memory
xcb_shm_segment_info_t shm;
xcb_window_t window;
xcb_pixmap_t xcb_pix;
xcb_gcontext_t xcb_context;
xcb_gcontext_t xcb_win_gc;
struct xkb_state * xkb_state;
int redraw;
@ -109,6 +116,7 @@ _mui_xcb_convert_keycode(
case XKB_KEY_Down: out->key.key = MUI_KEY_DOWN; break;
// XKB_KEY_Begin
case XKB_KEY_Insert: out->key.key = MUI_KEY_INSERT; break;
case XKB_KEY_Delete: out->key.key = MUI_KEY_DELETE; break;
case XKB_KEY_Home: out->key.key = MUI_KEY_HOME; break;
case XKB_KEY_End: out->key.key = MUI_KEY_END; break;
case XKB_KEY_Page_Up: out->key.key = MUI_KEY_PAGEUP; break;
@ -130,72 +138,6 @@ _mui_xcb_convert_keycode(
return true;
}
int
mui_xcb_list_physical_screens(
struct xcb_connection_t * xcb,
struct c2_rect_array_t *out)
{
if (!xcb || !out)
return -1;
c2_rect_array_clear(out);
xcb_screen_t *screen = xcb_setup_roots_iterator(
xcb_get_setup(xcb)).data;
xcb_randr_get_screen_resources_current_reply_t *reply =
xcb_randr_get_screen_resources_current_reply(
xcb,
xcb_randr_get_screen_resources_current(
xcb, screen->root),
NULL);
xcb_timestamp_t timestamp = reply->config_timestamp;
int len = xcb_randr_get_screen_resources_current_outputs_length(reply);
xcb_randr_output_t *randr_outputs =
xcb_randr_get_screen_resources_current_outputs(reply);
for (int i = 0; i < len; i++) {
xcb_randr_get_output_info_reply_t *output =
xcb_randr_get_output_info_reply(
xcb,
xcb_randr_get_output_info(
xcb, randr_outputs[i], timestamp),
NULL);
if (!output || output->crtc == XCB_NONE ||
output->connection == XCB_RANDR_CONNECTION_DISCONNECTED)
continue;
xcb_randr_get_crtc_info_reply_t *crtc =
xcb_randr_get_crtc_info_reply(xcb,
xcb_randr_get_crtc_info(
xcb, output->crtc, timestamp),
NULL);
c2_rect_t r = C2_RECT(crtc->x, crtc->y,
crtc->x +crtc->width, crtc->y + crtc->height);
c2_rect_array_add(out, r);
free(crtc);
free(output);
}
free(reply);
return 0;
}
static bool
_cui_match_physical_screen(
xcb_connection_t *xcb,
c2_pt_t want_size,
c2_pt_p found_pos )
{
bool res = false;
c2_rect_array_t sc = {};
mui_xcb_list_physical_screens(xcb, &sc);
for (unsigned int i = 0; i < sc.count; i++) {
if (c2_rect_width(&sc.e[i]) == want_size.x &&
c2_rect_height(&sc.e[i]) == want_size.y) {
*found_pos = sc.e[i].tl;
res = true;
}
}
return res;
}
struct mui_t *
mui_xcb_init(
struct mui_t *mui,
@ -219,12 +161,6 @@ mui_xcb_init(
ui->xcb = xcb_connect(NULL, NULL);
bool windowed = 1;
bool opaque = 1;
c2_pt_t found_position = {};
bool has_position = !windowed && _cui_match_physical_screen(
ui->xcb, ui->size, &found_position);
xcb_screen_iterator_t iter = xcb_setup_roots_iterator(
xcb_get_setup(ui->xcb));
printf("%s %d screens\n", __func__, iter.rem);
@ -238,46 +174,19 @@ mui_xcb_init(
}
printf("XCB Screen depth %d\n", screen->root_depth);
/*
* This walks thru the 'visual', looking for a true colour *32 bits* one
* which means it handles ARGB colors, which we can draw into. Also find
* one which color bit masks matches libcui & libpixman.
*/
xcb_visualtype_t *argb_visual = NULL;
xcb_depth_iterator_t depth_iter =
xcb_screen_allowed_depths_iterator(screen);
for (; depth_iter.rem; xcb_depth_next(&depth_iter)) {
xcb_visualtype_iterator_t visual_iter =
xcb_depth_visuals_iterator(depth_iter.data);
// printf("XCB Depth %d\n", depth_iter.data->depth);
if (depth_iter.data->depth != 32)
continue;
for (; visual_iter.rem; xcb_visualtype_next(&visual_iter)) {
if (visual_iter.data->_class == XCB_VISUAL_CLASS_TRUE_COLOR
&& visual_iter.data->red_mask == 0xff0000
&& visual_iter.data->green_mask == 0x00ff00
&& visual_iter.data->blue_mask == 0x0000ff) {
argb_visual = visual_iter.data;
break;
}
}
}
printf("XCB ARGB Transparency %s\n",
argb_visual ? "Supported" : "Not available");
if (windowed || opaque)
argb_visual = NULL;
xcb_shm_query_version_reply_t *xcb_shm_present;
xcb_shm_present = xcb_shm_query_version_reply(
ui->xcb, xcb_shm_query_version(ui->xcb), NULL);
bool slow_path = false;
if (!xcb_shm_present || !xcb_shm_present->shared_pixmaps) {
printf("xcb_shm error... %p\n", xcb_shm_present);
printf("If using nvidia driver, you need\n"
" Option \"AllowSHMPixmaps\" \"1\"\n"
" In your /etc/X11/xorg.conf file\n");
exit(0);
}
printf("XCB Shared memory present\n");
slow_path = true;
// exit(0);
} else
printf("XCB Shared memory present\n");
_mui_xcb_init_keyboard(ui);
@ -287,19 +196,14 @@ mui_xcb_init(
XCB_CW_EVENT_MASK;
xcb_colormap_t cmap = xcb_generate_id(ui->xcb);
/* required for having transparent windows */
if (argb_visual) {
xcb_create_colormap(ui->xcb, XCB_COLORMAP_ALLOC_NONE, cmap,
screen->root, argb_visual->visual_id);
value_mask |= XCB_CW_COLORMAP;
}
uint32_t w_mask[] = {
screen->black_pixel,
// Border Pixel; not really needed for anything, but needed
// for ARGB window otherwise it doesn't get created properly
0x88888888,
// if we found a screen of the exact size, remove the border
has_position ? 1 : 0,
0,
XCB_EVENT_MASK_EXPOSURE | XCB_EVENT_MASK_BUTTON_PRESS |
XCB_EVENT_MASK_BUTTON_RELEASE | XCB_EVENT_MASK_POINTER_MOTION |
XCB_EVENT_MASK_ENTER_WINDOW | XCB_EVENT_MASK_LEAVE_WINDOW |
@ -309,12 +213,12 @@ mui_xcb_init(
ui->window = xcb_generate_id(ui->xcb);
xcb_create_window(
ui->xcb,
argb_visual ? 32 : XCB_COPY_FROM_PARENT,
XCB_COPY_FROM_PARENT,
ui->window, screen->root,
found_position.x, found_position.y,
0,0,
pix->size.x, pix->size.y, 0,
XCB_WINDOW_CLASS_INPUT_OUTPUT,
argb_visual ? argb_visual->visual_id : screen->root_visual,
screen->root_visual,
value_mask, w_mask);
xcb_free_colormap(ui->xcb, cmap);
@ -322,42 +226,95 @@ mui_xcb_init(
xcb_change_property(ui->xcb, XCB_PROP_MODE_REPLACE,
ui->window, XCB_ATOM_WM_NAME, XCB_ATOM_STRING, 8,
strlen(title), title);
// create a graphic context
value_mask = XCB_GC_FOREGROUND | XCB_GC_GRAPHICS_EXPOSURES;
value_list[0] = screen->white_pixel;
value_list[1] = 0;
ui->xcb_context = xcb_generate_id(ui->xcb);
ui->xcb_win_gc = xcb_generate_id(ui->xcb);
xcb_create_gc(
ui->xcb, ui->xcb_context, ui->window, value_mask, value_list);
ui->xcb, ui->xcb_win_gc, ui->window, value_mask, value_list);
// map the window onto the screen
xcb_map_window(ui->xcb, ui->window);
// wont show unless I do this
xcb_flush(ui->xcb);
ui->shm.shmid = shmget(IPC_PRIVATE,
pix->size.x * pix->size.y * 4, IPC_CREAT | 0777);
ui->shm.shmaddr = shmat(ui->shm.shmid, 0, 0);
ui->shm.shmseg = xcb_generate_id(ui->xcb);
xcb_shm_attach(ui->xcb, ui->shm.shmseg, ui->shm.shmid, 0);
shmctl(ui->shm.shmid, IPC_RMID, 0);
ui->xcb_pix = xcb_generate_id(ui->xcb);
xcb_shm_create_pixmap(
ui->xcb, ui->xcb_pix, ui->window,
pix->size.x, pix->size.y,
argb_visual ? 32 : screen->root_depth,
ui->shm.shmseg, 0);
if (!slow_path) {
xcb_generic_error_t *error;
xcb_void_cookie_t cook;
ui->shm.shmid = shmget(IPC_PRIVATE,
pix->size.x * pix->size.y * 4, IPC_CREAT | 0666);
ui->shm.shmaddr = shmat(ui->shm.shmid, 0, 0);
ui->shm.shmseg = xcb_generate_id(ui->xcb);
cook = xcb_shm_attach_checked(ui->xcb, ui->shm.shmseg, ui->shm.shmid, 0);
error = xcb_request_check(ui->xcb, cook);
if (error) {
fprintf(stderr, "XCB: Error xcb_shm_attach_checked: %d\n",
error->error_code);
free(error);
// xcb_disconnect(ui->xcb);
// return 1;
}
shmctl(ui->shm.shmid, IPC_RMID, 0);
pix->pixels = ui->shm.shmaddr;
pix->row_bytes = pix->size.x * 4;
ui->xcb_pix = xcb_generate_id(ui->xcb);
cook = xcb_shm_create_pixmap_checked(
ui->xcb, ui->xcb_pix, ui->window,
pix->size.x, pix->size.y,
screen->root_depth,
ui->shm.shmseg, 0);
error = xcb_request_check(ui->xcb, cook);
if (error) {
fprintf(stderr, "XCB: Error xcb_shm_create_pixmap: %d\n",
error->error_code);
free(error);
} else
pix->pixels = ui->shm.shmaddr;
pix->row_bytes = pix->size.x * 4;
}
if (!pix->pixels) {
printf("XCB: Not using SHM, slow path\n");
pix->pixels = malloc(pix->size.x * pix->size.y * 4);
ui->xcb_pix = xcb_generate_id(ui->xcb);
xcb_create_pixmap(
ui->xcb, screen->root_depth, ui->xcb_pix, ui->window,
pix->size.x, pix->size.y);
ui->xcb_image = xcb_image_create_native(
ui->xcb, pix->size.x, pix->size.y, XCB_IMAGE_FORMAT_Z_PIXMAP,
screen->root_depth, pix->pixels,
pix->size.x * pix->size.y * 4, pix->pixels);
pix->row_bytes = pix->size.x * 4;
}
xcb_flush(ui->xcb);
// printf("%s pix is %p\n", __func__, pix->pixels);
ui->redraw = 1;
return &ui->ui;
}
static void
mui_read_clipboard(
struct mui_t *mui)
{
FILE *f = popen("xclip -selection clipboard -o", "r");
if (!f)
return;
mui_utf8_t clip = {};
char buf[1024];
size_t r = 0;
do {
r = fread(buf, 1, sizeof(buf), f);
if (r > 0)
mui_utf8_append(&clip, (uint8_t*)buf, r);
} while (r > 0);
pclose(f);
mui_utf8_free(&mui->clipboard);
mui->clipboard = clip;
}
int
mui_xcb_poll(
struct mui_t * mui,
mui_drawable_t * dr,
bool redrawn)
{
mui_xcb_t * ui = (mui_xcb_t *)mui;
@ -405,6 +362,11 @@ mui_xcb_poll(
key_ev.key.key <= MUI_KEY_MODIFIERS_LAST) {
mui->modifier_keys |= (1 << (key_ev.key.key - MUI_KEY_MODIFIERS));
}
if (toupper(key_ev.key.key) == 'V' &&
(mui->modifier_keys & MUI_MODIFIER_CTRL)) {
printf("Get CLIPBOARD\n");
mui_read_clipboard(mui);
}
key_ev.modifiers = mui->modifier_keys;
// key_ev.modifiers |= MUI_MODIFIER_EVENT_TRACE;
// gameover = key_ev.key.key == 'q';
@ -477,7 +439,12 @@ mui_xcb_poll(
break;
case XCB_EXPOSE: {
// xcb_expose_event_t *expose_event = (xcb_expose_event_t*) event;
ui->redraw++;
// ui->redraw++;
xcb_expose_event_t *x = (xcb_expose_event_t*) event;
xcb_copy_area(
ui->xcb, ui->xcb_pix,
ui->window, ui->xcb_win_gc,
x->x, x->y, x->x, x->y, x->width, x->height);
} break;
default:
// Handle other events
@ -492,17 +459,31 @@ mui_xcb_poll(
c2_rect_t *ra = (c2_rect_t*)pixman_region32_rectangles(&mui->redraw, &rc);
if (ui->redraw) {
ui->redraw = 0;
rc = 1;
rc = 1;
ra = &whole;
}
if (rc) {
// printf("XCB: %d rects to redraw\n", rc);
for (int i = 0; i < rc; i++) {
c2_rect_t r = ra[i];
// printf("XCB: %d,%d %dx%d\n", r.l, r.t, c2_rect_width(&r), c2_rect_height(&r));
// printf("XCB: %d,%d %dx%d\n", r.l, r.t, c2_rect_width(&r), c2_rect_height(&r));
// cannot update the exact rectangle, just window-wide strips
if (ui->xcb_image) {
mui_pixmap_t * pix = &dr->pix;
xcb_put_image(
ui->xcb, XCB_IMAGE_FORMAT_Z_PIXMAP,
ui->xcb_pix, ui->xcb_win_gc,
pix->size.x, c2_rect_height(&r),
0, r.t, 0, 24,
c2_rect_height(&r) * pix->row_bytes,
(uint8_t*)pix->pixels + (r.t * pix->row_bytes) /*+ r.l * 4*/);
}
xcb_copy_area(
ui->xcb, ui->xcb_pix, ui->window, ui->xcb_context,
r.l, r.t, r.l, r.t, c2_rect_width(&r), c2_rect_height(&r));
ui->xcb, ui->xcb_pix,
ui->window, ui->xcb_win_gc,
r.l, r.t, r.l, r.t,
c2_rect_width(&r), c2_rect_height(&r));
}
}
pixman_region32_clear(&mui->redraw);
@ -525,15 +506,29 @@ mui_xcb_terminate(
#endif
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <fcntl.h>
#include <stdio.h>
#include <dlfcn.h>
int main()
int
main(
int argc,
const char * argv[])
{
const char * filename = NULL;// "build-x86_64-linux-gnu/lib/ui_tests.so";
for (int i = 1; i < argc; i++) {
if (argv[i][0] == '-') {
switch (argv[i][1]) {
case 'f':
filename = argv[++i];
break;
}
} else {
filename = argv[i];
}
}
if (!filename) {
fprintf(stderr, "Usage: %s -f <filename>\n", basename((char*)argv[0]));
return 1;
}
mui_xcb_t xcb_ui = {};
mui_drawable_t dr = {};
@ -543,7 +538,6 @@ int main()
mui_t *mui = mui_xcb_init((struct mui_t *)&xcb_ui, &dr.pix);
mui_xcb_t * ui = &xcb_ui;
void * dynload = NULL;
char * filename = "build-x86_64-linux-gnu/lib/ui_tests.so";
struct stat st_current = {}, st = {};
mui_time_t stamp = 0;
@ -594,13 +588,13 @@ int main()
mui_run(mui);
if (ui->plug && ui->plug->draw)
draw = ui->plug->draw(mui, ui->plug_data, &dr, false);
if (mui_xcb_poll(mui, draw))
if (mui_xcb_poll(mui, &dr, draw))
break;
mui_time_t now = mui_get_time();
while (stamp < now)
stamp += (MUI_TIME_SECOND / 60);
usleep(stamp-now);
} while (1);
} while (!mui->quit_request);
if (dynload) {
if (ui->plug_data && ui->plug && ui->plug->dispose) {
ui->plug->dispose(ui->plug_data);
@ -608,9 +602,11 @@ int main()
ui->plug_data = NULL;
}
printf("Closed %s\n", filename);
dlclose(dynload);
// no need to dlclose, it prevents valgrind --leak-check=yes to find
// the symbols we want as they have been unloaded!
// dlclose(dynload);
}
mui_drawable_dispose(&dr);
mui_xcb_terminate(mui);
return 0;
}
}

View File

@ -0,0 +1,42 @@
/*
* mui_shell_plugin.h
*
* Copyright (C) 2023 Michel Pollet <buserror@gmail.com>
*
* SPDX-License-Identifier: MIT
*/
#pragma once
#include <stdint.h>
#include <stdbool.h>
/*
* Quck and dirty plugin interface for mui_shell, just allows a quick reload
* of a plugin without having to restart the whole shell.
* In combination with 'make watch' you have have 1s turnaround time for
* your plugin development, helps a ton when tweaking a dialog etc.
*/
struct mui_t;
struct mui_drawable_t;
typedef struct mui_plug_t {
const char * name; // 'Human' name of the plugin
const uint32_t * icon; // optional
// return a 'handle' to some data for this plugin.
void * (*init)(
struct mui_t * ui,
struct mui_plug_t * plug,
struct mui_drawable_t * dr );
void (*dispose)(
void * plug );
int (*draw)(
struct mui_t * ui,
void * plug,
struct mui_drawable_t * dr,
uint16_t all );
int (*event)(
struct mui_t * ui,
void * plug,
struct mui_event_t * event );
} mui_plug_t;

View File

@ -298,7 +298,7 @@ c2_polyline_clear(
int
c2_polyline_get_segment(
c2_polyline_p pl,
long ind,
unsigned long ind,
c2_segment_p o )
{
if (ind > pl->pt.count)
@ -444,7 +444,7 @@ c2_polyline_array_clip(
c2_rect_p clip,
c2_polyline_array_p outPoly )
{
for (long poly = 0; poly < pa->count; poly++) {
for (unsigned long poly = 0; poly < pa->count; poly++) {
c2_polyline_t *p = pa->e[poly];
if (!p)
break;

View File

@ -190,7 +190,7 @@ c2_rect_contains_pt(
C2_DECL c2_rect_p
c2_rect_union(
c2_rect_p r,
c2_rect_p u )
const c2_rect_p u )
{
if (!r || !u) return r;
if (c2_rect_isempty(r)) {

View File

@ -32,7 +32,7 @@ c2_polyline_clear(
int
c2_polyline_get_segment(
c2_polyline_p pl,
long ind,
unsigned long ind,
c2_segment_p o );
void

View File

@ -124,9 +124,9 @@ C_ARRAY_DECL C_ARRAY_INLINE \
C_ARRAY_DECL C_ARRAY_INLINE \
__name##_count_t __name##_insert(\
__name##_p a, __name##_count_t index, \
__name##_element_t * e, __name##_count_t count) \
const __name##_element_t * e, __name##_count_t count) \
{\
if (!a) return 0;\
if (!a || !e || !count) return 0;\
if (index > a->count) index = a->count;\
if (a->count + count >= a->size) \
__name##_realloc(a, (((a->count + count) / __name##_page_size)+1) * __name##_page_size);\
@ -137,6 +137,14 @@ C_ARRAY_DECL C_ARRAY_INLINE \
a->count += count;\
return a->count;\
}\
C_ARRAY_DECL C_ARRAY_INLINE \
__name##_count_t __name##_append(\
__name##_p a, \
const __name##_element_t * e, __name##_count_t count) \
{\
if (!a) return 0;\
return __name##_insert(a, a->count, e, count);\
}\
C_ARRAY_DECL C_ARRAY_INLINE \
__name##_count_t __name##_delete(\
__name##_p a, __name##_count_t index, __name##_count_t count) \

View File

@ -27,6 +27,22 @@
#include "cg.h"
// make this optional on darwin, where the is no weak linking
#if defined(__APPLE__) && defined(__MACH__)
#define CG_WEAK_LINKING 0
#else
#define CG_WEAK_LINKING 1
#endif
#if CG_WEAK_LINKING
#define _cg_stringify(_a) #_a
#define _cg_und(_b) _cg_stringify(__ ## _b)
#define cg_weak_alias(_name) \
extern __typeof(__##_name) _name __attribute__((weak, alias(_cg_und(_name))))
#else
#define cg_weak_alias(_name)
#endif
#define cg_array_init(array) \
do { \
array.data = NULL; \
@ -1431,12 +1447,36 @@ static inline uint32_t interpolate_pixel(uint32_t x, uint32_t a, uint32_t y, uin
return x;
}
#if defined(__AVX2__)
typedef uint32_t u32_v __attribute__((vector_size(32)));
#define VEC_ALIGN 31
#define VEC_ECOUNT 8
#else
typedef uint32_t u32_v __attribute__((vector_size(16)));
#define VEC_ALIGN 15
#define VEC_ECOUNT 4
#endif
static void __cg_memfill32(uint32_t * dst, uint32_t val, int len)
{
for(int i = 0; i < len; i++)
dst[i] = val;
// use extensions to do the first part, as long as it is aligned properly
while (len > 0 && ((uintptr_t)dst & VEC_ALIGN)) {
*dst++ = val;
len--;
}
u32_v v = val - (u32_v){};
while (len >= VEC_ECOUNT) {
*(u32_v *)dst = v;
dst += VEC_ECOUNT; len -= VEC_ECOUNT;
}
// do the last part
while (len > 0) {
*dst++ = val;
len--;
}
}
extern __typeof(__cg_memfill32) cg_memfill32 __attribute__((weak, alias("__cg_memfill32")));
cg_weak_alias(cg_memfill32);
static inline int gradient_clamp(struct cg_gradient_data_t * gradient, int ipos)
{
@ -1615,7 +1655,7 @@ static void __cg_comp_solid_source(uint32_t * dst, int len, uint32_t color, uint
dst[i] = color + CG_BYTE_MUL(dst[i], ialpha);
}
}
extern __typeof(__cg_comp_solid_source) cg_comp_solid_source __attribute__((weak, alias("__cg_comp_solid_source")));
cg_weak_alias(cg_comp_solid_source);
static void __cg_comp_solid_source_over(uint32_t * dst, int len, uint32_t color, uint32_t alpha)
{
@ -1632,7 +1672,7 @@ static void __cg_comp_solid_source_over(uint32_t * dst, int len, uint32_t color,
dst[i] = color + CG_BYTE_MUL(dst[i], ialpha);
}
}
extern __typeof(__cg_comp_solid_source_over) cg_comp_solid_source_over __attribute__((weak, alias("__cg_comp_solid_source_over")));
cg_weak_alias(cg_comp_solid_source_over);
static void __cg_comp_solid_destination_in(uint32_t * dst, int len, uint32_t color, uint32_t alpha)
{
@ -1642,7 +1682,7 @@ static void __cg_comp_solid_destination_in(uint32_t * dst, int len, uint32_t col
for(int i = 0; i < len; i++)
dst[i] = CG_BYTE_MUL(dst[i], a);
}
extern __typeof(__cg_comp_solid_destination_in) cg_comp_solid_destination_in __attribute__((weak, alias("__cg_comp_solid_destination_in")));
cg_weak_alias(cg_comp_solid_destination_in);
static void __cg_comp_solid_destination_out(uint32_t * dst, int len, uint32_t color, uint32_t alpha)
{
@ -1652,7 +1692,7 @@ static void __cg_comp_solid_destination_out(uint32_t * dst, int len, uint32_t co
for(int i = 0; i < len; i++)
dst[i] = CG_BYTE_MUL(dst[i], a);
}
extern __typeof(__cg_comp_solid_destination_out) cg_comp_solid_destination_out __attribute__((weak, alias("__cg_comp_solid_destination_out")));
cg_weak_alias(cg_comp_solid_destination_out);
static void __cg_comp_source(uint32_t * dst, int len, uint32_t * src, uint32_t alpha)
{
@ -1667,7 +1707,7 @@ static void __cg_comp_source(uint32_t * dst, int len, uint32_t * src, uint32_t a
dst[i] = interpolate_pixel(src[i], alpha, dst[i], ialpha);
}
}
extern __typeof(__cg_comp_source) cg_comp_source __attribute__((weak, alias("__cg_comp_source")));
cg_weak_alias(cg_comp_source);
static void __cg_comp_source_over(uint32_t * dst, int len, uint32_t * src, uint32_t alpha)
{
@ -1696,7 +1736,7 @@ static void __cg_comp_source_over(uint32_t * dst, int len, uint32_t * src, uint3
}
}
}
extern __typeof(__cg_comp_source_over) cg_comp_source_over __attribute__((weak, alias("__cg_comp_source_over")));
cg_weak_alias(cg_comp_source_over);
static void __cg_comp_destination_in(uint32_t * dst, int len, uint32_t * src, uint32_t alpha)
{
@ -1716,7 +1756,7 @@ static void __cg_comp_destination_in(uint32_t * dst, int len, uint32_t * src, ui
}
}
}
extern __typeof(__cg_comp_destination_in) cg_comp_destination_in __attribute__((weak, alias("__cg_comp_destination_in")));
cg_weak_alias(cg_comp_destination_in);
static void __cg_comp_destination_out(uint32_t * dst, int len, uint32_t * src, uint32_t alpha)
{
@ -1736,7 +1776,7 @@ static void __cg_comp_destination_out(uint32_t * dst, int len, uint32_t * src, u
}
}
}
extern __typeof(__cg_comp_destination_out) cg_comp_destination_out __attribute__((weak, alias("__cg_comp_destination_out")));
cg_weak_alias(cg_comp_destination_out);
typedef void (*cg_comp_solid_function_t)(uint32_t * dst, int len, uint32_t color, uint32_t alpha);
static const cg_comp_solid_function_t cg_comp_solid_map[] = {

View File

@ -22,8 +22,9 @@ mui_init(
//memset(ui, 0, sizeof(*ui));
ui->color.clear = MUI_COLOR(0xccccccff);
ui->color.highlight = MUI_COLOR(0xd6fcc0ff);
ui->timer.map = 0;
ui->carret_timer = 0xff;
TAILQ_INIT(&ui->windows);
TAILQ_INIT(&ui->zombies);
TAILQ_INIT(&ui->fonts);
mui_font_init(ui);
pixman_region32_init(&ui->redraw);
@ -37,6 +38,7 @@ mui_dispose(
{
pixman_region32_fini(&ui->inval);
pixman_region32_fini(&ui->redraw);
mui_utf8_free(&ui->clipboard);
mui_font_dispose(ui);
mui_window_t *w;
while ((w = TAILQ_FIRST(&ui->windows))) {
@ -121,7 +123,6 @@ mui_handle_event(
bool res = false;
if (!ev->when)
ev->when = mui_get_time();
ui->action_active++;
switch (ev->type) {
case MUI_EVENT_KEYUP:
case MUI_EVENT_KEYDOWN: {
@ -156,28 +157,56 @@ mui_handle_event(
printf("%s %d mouse %d %3dx%3d capture:%s\n", __func__,
ev->type, ev->mouse.button,
ev->mouse.where.x, ev->mouse.where.y,
ui->event_capture ?
ui->event_capture->title : "(none)");
if (ui->event_capture) {
res = mui_window_handle_mouse(ui->event_capture, ev);
ui->event_capture.window ?
ui->event_capture.window->title :
"(none)");
/* Handle double click detection */
if (ev->mouse.button < MUI_EVENT_BUTTON_MAX &&
ev->type == MUI_EVENT_BUTTONDOWN) {
int click_delta = ev->when - ui->last_click_stamp[ev->mouse.button];
if (ui->last_click_stamp[ev->mouse.button] &&
click_delta < (500 * MUI_TIME_MS)) {
ui->last_click_stamp[ev->mouse.button] = 0;
ev->mouse.count = 2;
} else {
ui->last_click_stamp[ev->mouse.button] = ev->when;
}
}
if (ui->event_capture.window) {
res = mui_window_handle_mouse(
ui->event_capture.window, ev);
break;
} else {
mui_window_t *w, *safe;
TAILQ_FOREACH_REVERSE_SAFE(w, &ui->windows, windows, self, safe) {
/* We can't use the REVERSE_SAFE macro here, as the window
* list can change quite a bit, especially when menus are
* involved */
mui_window_t *w;
w = TAILQ_LAST(&ui->windows, windows);
int done = 0;
while (w && !done) {
mui_window_lock(w);
// in case 'prev' gets deleted, keep a ref on it.
mui_window_ref_t prev = {};
mui_window_ref(&prev, TAILQ_PREV(w, windows, self),
FCC('H', 'O', 'L', 'D'));
if ((res = mui_window_handle_mouse(w, ev))) {
if (ev->modifiers & MUI_MODIFIER_EVENT_TRACE)
printf(" window:%s handled it\n",
w->title);
break;
done = 1;
}
mui_window_unlock(w); // COULD delete window here
w = prev.window; // this might have been NULLed
mui_window_deref(&prev);
}
}
if (ev->modifiers & MUI_MODIFIER_EVENT_TRACE)
if (!res)
printf(" no window handled it\n");
} break;
default:
break;
}
ui->action_active--;
return res;
}
@ -206,12 +235,13 @@ mui_event_match_key(
return false;
if (toupper(ev->key.key) != toupper(key_equ.key))
return false;
if (_mui_simplify_mods(ev->modifiers) != _mui_simplify_mods(key_equ.mod))
if (_mui_simplify_mods(ev->modifiers) !=
_mui_simplify_mods(key_equ.mod))
return false;
return true;
}
uint8_t
mui_timer_id_t
mui_timer_register(
mui_t *ui,
mui_timer_p cb,
@ -220,20 +250,21 @@ mui_timer_register(
{
if (ui->timer.map == (uint64_t)-1L) {
fprintf(stderr, "%s ran out of timers\n", __func__);
return -1;
return MUI_TIMER_NONE;
}
int ti = ffsl(~ui->timer.map) - 1;
mui_timer_id_t ti = __builtin_ffsl(~ui->timer.map) - 1;
// printf("%s:%d delay %d\n", __func__, ti, delay);
ui->timer.map |= 1 << ti;
ui->timer.timers[ti].cb = cb;
ui->timer.timers[ti].param = param;
ui->timer.timers[ti].when = mui_get_time() + delay;
return 0;
return ti;
}
mui_time_t
mui_timer_reset(
struct mui_t * ui,
uint8_t id,
mui_timer_id_t id,
mui_timer_p cb,
mui_time_t delay)
{
@ -241,7 +272,7 @@ mui_timer_reset(
return 0;
if (!(ui->timer.map & (1L << id)) ||
ui->timer.timers[id].cb != cb) {
printf("%s: timer %d not active\n", __func__, id);
// printf("%s:%d not active\n", __func__, id);
return 0;
}
mui_time_t res = 0;
@ -251,9 +282,8 @@ mui_timer_reset(
ui->timer.timers[id].when = now + delay;
if (delay == 0) {
ui->timer.map &= ~(1L << id);
printf("%s: timer %d removed\n", __func__, id);
// printf("%s: %d removed\n", __func__, id);
}
return res;
}
@ -264,12 +294,11 @@ mui_timers_run(
uint64_t now = mui_get_time();
uint64_t map = ui->timer.map;
while (map) {
int ti = ffsl(map) - 1;
int ti = __builtin_ffsl(map) - 1;
map &= ~(1 << ti);
if (ui->timer.timers[ti].when > now)
continue;
mui_time_t r = ui->timer.timers[ti].cb(
ui, now,
mui_time_t r = ui->timer.timers[ti].cb(ui, now,
ui->timer.timers[ti].param);
if (r == 0)
ui->timer.map &= ~(1 << ti);
@ -278,27 +307,11 @@ mui_timers_run(
}
}
void
_mui_window_free(
mui_window_t *win);
void
mui_garbage_collect(
mui_t * ui)
{
mui_window_t *win, *safe;
TAILQ_FOREACH_SAFE(win, &ui->zombies, self, safe) {
TAILQ_REMOVE(&ui->zombies, win, self);
_mui_window_free(win);
}
}
void
mui_run(
mui_t *ui)
{
mui_timers_run(ui);
mui_garbage_collect(ui);
}
bool
@ -313,3 +326,165 @@ mui_has_active_windows(
}
return false;
}
void
mui_clipboard_set(
mui_t * ui,
const uint8_t * utf8,
uint len)
{
len = len ? len : strlen((char*)utf8);
mui_utf8_clear(&ui->clipboard);
mui_utf8_insert(&ui->clipboard, 0, utf8, len);
mui_utf8_add(&ui->clipboard, 0);
mui_window_action(
ui->menubar.window, MUI_CLIPBOARD_CHANGED, NULL);
}
const uint8_t *
mui_clipboard_get(
mui_t * ui,
uint * len)
{
mui_window_action(
ui->menubar.window, MUI_CLIPBOARD_REQUEST, NULL);
if (len)
*len = ui->clipboard.count > 1 ? ui->clipboard.count - 1 : 0;
return ui->clipboard.count ? ui->clipboard.e : NULL;
}
void
mui_refqueue_init(
mui_refqueue_t *queue)
{
TAILQ_INIT(&queue->head);
}
uint
mui_refqueue_dispose(
mui_refqueue_t *queue)
{
uint res = 0;
struct mui_ref_t *ref, *safe;
TAILQ_FOREACH_SAFE(ref, &queue->head, self, safe) {
if (ref->count) {
ref->count--;
if (ref->count) {
// printf("%s: ref %4.4s count %2d\n", __func__,
// (char*)&ref->kind, ref->count);
res++;
continue;
}
}
TAILQ_REMOVE(&queue->head, ref, self);
ref->queue = NULL;
if (ref->deref)
ref->deref(ref);
}
return res;
}
/* Remove reference 'ref' from it's reference queue */
void
mui_ref_deref(
struct mui_ref_t * ref)
{
if (!ref)
return;
if (ref->queue)
TAILQ_REMOVE(&ref->queue->head, ref, self);
if (ref->alloc) {
free(ref);
return;
}
ref->queue = NULL;
ref->deref = NULL;
ref->count = 0;
}
static void
mui_ref_deref_control(
struct mui_ref_t * _ref)
{
mui_control_ref_t * ref = (mui_control_ref_t*)_ref;
ref->control = NULL;
}
mui_control_ref_t *
mui_control_ref(
mui_control_ref_t * ref,
struct mui_control_t * control,
uint32_t kind)
{
if (!control)
return NULL;
if (ref && ref->ref.queue) {
printf("%s Warning: ref %p %4.4s already in queue\n",
__func__, ref, (char*)&kind);
if (ref->control != control) {
printf("%s ERROR: ref %p control %p != %p\n",
__func__, ref, ref->control, control);
}
return NULL;
}
struct mui_ref_t * res = ref ? ref : calloc(1, sizeof(*ref));
res->alloc = !ref;
res->queue = &control->refs;
res->kind = kind;
res->deref = mui_ref_deref_control;
res->count = 1;
ref = (mui_control_ref_t*)res;
ref->control = control;
TAILQ_INSERT_TAIL(&control->refs.head, res, self);
return ref;
}
void
mui_control_deref(
mui_control_ref_t * ref)
{
ref->control = NULL;
mui_ref_deref(&ref->ref);
}
static void
mui_ref_deref_window(
struct mui_ref_t * _ref)
{
mui_window_ref_t * ref = (mui_window_ref_t*)_ref;
ref->window = NULL;
}
mui_window_ref_t *
mui_window_ref(
mui_window_ref_t * ref,
struct mui_window_t * win,
uint32_t kind)
{
if (!win)
return NULL;
if (ref && ref->ref.queue) {
printf("%s Warning: ref %p %4.4s already in queue\n",
__func__, ref, (char*)&kind);
return NULL;
}
struct mui_ref_t * res = ref ? ref : calloc(1, sizeof(*ref));
res->alloc = !ref;
res->queue = &win->refs;
res->kind = kind;
res->deref = mui_ref_deref_window;
res->count = 1;
ref = (mui_window_ref_t*)res;
ref->window = win;
TAILQ_INSERT_TAIL(&win->refs.head, res, self);
return ref;
}
void
mui_window_deref(
mui_window_ref_t * ref)
{
ref->window = NULL;
mui_ref_deref(&ref->ref);
}

File diff suppressed because it is too large Load Diff

View File

@ -72,11 +72,12 @@ mui_alert(
}
cf = C2_RECT_WH(0, 10, 540-140, 70);
c2_rect_left_of(&cf, c2_rect_width(&w->content), 20);
c = mui_textbox_new(w, cf, message, NULL, 0);
cf = C2_RECT_WH(10, 10, 80, 75);
c = mui_textbox_new(w, cf, message, NULL, MUI_TEXT_ALIGN_COMPACT);
cf = C2_RECT_WH(10, 10, 80, 85);
c = mui_textbox_new(w, cf,
"", "icon_large",
MUI_TEXT_ALIGN_CENTER | MUI_TEXT_ALIGN_MIDDLE);
MUI_TEXT_ALIGN_CENTER | MUI_TEXT_ALIGN_MIDDLE |
MUI_TEXT_ALIGN_COMPACT);
c = NULL;
TAILQ_FOREACH(c, &w->controls, self) {

View File

@ -22,7 +22,7 @@ enum {
typedef struct mui_textbox_control_t {
mui_control_t control;
mui_font_t * font;
uint16_t flags;
uint32_t flags;
} mui_textbox_control_t;
extern const mui_control_color_t mui_control_color[MUI_CONTROL_STATE_COUNT];
@ -155,7 +155,7 @@ mui_textbox_new(
c2_rect_t frame,
const char * text,
const char * font,
uint16_t flags )
uint32_t flags )
{
mui_control_t * c = mui_control_new(
win, MUI_CONTROL_TEXTBOX, mui_cdef_boxes,
@ -181,7 +181,7 @@ mui_groupbox_new(
mui_window_t * win,
c2_rect_t frame,
const char * title,
uint16_t flags )
uint32_t flags )
{
mui_control_t * c = mui_control_new(
win, MUI_CONTROL_GROUPBOX, mui_cdef_boxes,

View File

@ -40,17 +40,15 @@ mui_button_draw(
cg_stroke(cg);
c2_rect_inset(&f, BUTTON_INSET, BUTTON_INSET);
}
mui_font_t * main = TAILQ_FIRST(&win->ui->fonts);
mui_font_t * main = mui_font_find(win->ui, "main");
stb_ttc_measure m = {};
mui_font_text_measure(main, c->title, &m);
int title_width = m.x1 - m.x0;
c2_rect_t title = f;
title.r = title.l + title_width + 1;
title.b = title.t + m.ascent - m.descent;
c2_rect_offset(&title, -m.x0 +
(int)((c2_rect_width(&f) / 2) - (c2_rect_width(&title)) / 2),
(c2_rect_height(&f) / 2) - (c2_rect_height(&title) / 2));
int title_width = m.x1;// - m.x0;
c2_rect_t title = C2_RECT_WH(0, 0, title_width, m.ascent - m.descent);
c2_rect_offset(&title,
f.l + ((c2_rect_width(&f) / 2) - (c2_rect_width(&title)) / 2),
f.t + (c2_rect_height(&f) / 2) - (c2_rect_height(&title) / 2));
mui_drawable_clip_push(dr, &f);
cg = mui_drawable_get_cg(dr);
c2_rect_t inner = f;
@ -64,8 +62,9 @@ mui_button_draw(
// c2_rect_width(&title), c2_rect_height(&title));
cg_set_source_color(cg, &CG_COLOR(mui_control_color[c->state].frame));
cg_stroke(cg);
// offset for leading space
mui_font_text_draw(main, dr,
C2_PT(title.l, title.t), c->title, strlen(c->title),
C2_PT(title.l - m.x0, title.t), c->title, strlen(c->title),
mui_control_color[c->state].text);
mui_drawable_clip_pop(dr);
}
@ -83,14 +82,19 @@ mui_check_rad_draw(
c2_rect_t box = f;
box.r = box.l + (main->size * 0.95);
box.b = box.t + (main->size * 0.95);
c2_rect_offset(&box, 0, (c2_rect_height(&f) / 2) - (c2_rect_height(&box) / 2));
c2_rect_offset(&box, 1, (c2_rect_height(&f) / 2) - (c2_rect_height(&box) / 2));
c2_rect_t title = f;
title.l = box.r + 8;
// mui_drawable_clip_push(dr, &f);
// only do a a get_cg after the clip is set, as this is what converts
// the drawable clip rectangle list into a cg path
struct cg_ctx_t * cg = mui_drawable_get_cg(dr);
mui_drawable_clip_push(dr, &f);
if (0) { // debug draw the text rectangle as a box
cg_rectangle(cg, title.l, title.t,
c2_rect_width(&title), c2_rect_height(&title));
cg_stroke(cg);
}
// draw the box/circle
if (c->style == MUI_BUTTON_STYLE_RADIO) {
cg_circle(cg, box.l + (c2_rect_width(&box) / 2),
@ -131,8 +135,8 @@ mui_check_rad_draw(
c->state == MUI_CONTROL_STATE_DISABLED ?
mui_control_color[c->state].text :
mui_control_color[0].text,
MUI_TEXT_ALIGN_MIDDLE);
mui_drawable_clip_pop(dr);
MUI_TEXT_ALIGN_MIDDLE|MUI_TEXT_ALIGN_COMPACT);
// mui_drawable_clip_pop(dr);
}
@ -191,6 +195,8 @@ mui_button_mouse(
else
mui_control_set_state(c, MUI_CONTROL_STATE_NORMAL);
} break;
default:
break;
}
return true;
}
@ -232,6 +238,8 @@ mui_cdef_button(
case MUI_EVENT_BUTTONDOWN: {
return mui_button_mouse(c, ev);
} break;
default:
break;
}
} break;
case MUI_CDEF_SELECT: {

View File

@ -36,7 +36,7 @@ mui_drawable_draw(
mui_drawable_clip_push(dr, &f);
mui_drawable_control_t *dc = (mui_drawable_control_t *)c;
for (int i = 0; i < (int)dc->drawables.count; i++) {
for (uint i = 0; i < dc->drawables.count; i++) {
mui_drawable_t *d = dc->drawables.e[i];
if (!d->pix.pixels)
continue;
@ -75,7 +75,7 @@ mui_cdef_drawable(
switch (c->type) {
case MUI_CONTROL_DRAWABLE: {
mui_drawable_control_t *dc = (mui_drawable_control_t *)c;
for (int i = 0; i < (int)dc->drawables.count; i++) {
for (uint i = 0; i < dc->drawables.count; i++) {
mui_drawable_t *d = dc->drawables.e[i];
mui_drawable_dispose(d);
}

View File

@ -47,7 +47,7 @@ mui_listbox_draw(
c2_rect_offset(&f, win->content.l, win->content.t);
struct cg_ctx_t * cg = mui_drawable_get_cg(dr);
cg_set_line_width(cg, 1);
cg_set_line_width(cg, mui_control_has_focus(c) ? 2 : 1);
cg_set_source_color(cg, &CG_COLOR(mui_control_color[c->state].frame));
cg_rectangle(cg, f.l, f.t,
c2_rect_width(&f), c2_rect_height(&f));
@ -57,18 +57,19 @@ mui_listbox_draw(
c2_rect_inset(&clip, 1, 1);
mui_drawable_clip_push(dr, &clip);
}
cg = mui_drawable_get_cg(dr);
mui_listbox_control_t *lb = (mui_listbox_control_t *)c;
uint32_t top_element = lb->scroll / lb->elem_height;
uint32_t bottom_element = top_element + 1 +
(c2_rect_height(&f) / lb->elem_height);
uint32_t bottom_element = (lb->scroll + c2_rect_height(&f) ) /
lb->elem_height;
// printf("%s draw from %d to %d\n", __func__, top_element, bottom_element);
mui_font_t * icons = mui_font_find(win->ui, "icon_small");
mui_font_t * main = mui_font_find(win->ui, "main");
mui_color_t highlight = win->ui->color.highlight;
for (unsigned int ii = top_element;
ii < lb->elems.count && ii < bottom_element; ii++) {
for (uint ii = top_element;
ii < lb->elems.count && ii <= bottom_element; ii++) {
c2_rect_t ef = f;
ef.b = ef.t + lb->elem_height;
c2_rect_offset(&ef, 0, ii * lb->elem_height - lb->scroll);
@ -78,7 +79,10 @@ mui_listbox_draw(
cg_set_source_color(cg, &CG_COLOR(highlight));
cg_rectangle(cg, ef.l, ef.t,
c2_rect_width(&ef), c2_rect_height(&ef));
cg_fill(cg);
if (mui_control_has_focus(c))
cg_fill(cg);
else
cg_stroke(cg);
}
ef.l += 8;
mui_listbox_elem_t *e = &lb->elems.e[ii];
@ -137,12 +141,12 @@ mui_listbox_typehead(
// that find something that matches, we're good. If not, try to match
// the prefix in a non-case sensitive way in case the user doesn't know
// what he wants...
for (unsigned int ii = 0; ii < lb->elems.count; ii++) {
for (uint ii = 0; ii < lb->elems.count; ii++) {
mui_listbox_elem_t *e = &lb->elems.e[ii];
if (strncmp(e->elem, lb->typehead.buf, lb->typehead.index) == 0)
return ii - lb->control.value;
}
for (unsigned int ii = 0; ii < lb->elems.count; ii++) {
for (uint ii = 0; ii < lb->elems.count; ii++) {
mui_listbox_elem_t *e = &lb->elems.e[ii];
if (strncasecmp(e->elem, lb->typehead.buf, lb->typehead.index) == 0)
return ii - lb->control.value;
@ -172,6 +176,18 @@ mui_listbox_key(
case MUI_KEY_DOWN: delta = 1; break;
case MUI_KEY_PAGEUP: delta = -page_size; break;
case MUI_KEY_PAGEDOWN: delta = page_size; break;
case '\t':
mui_control_switch_focus(c->win,
ev->modifiers & MUI_MODIFIER_SHIFT ? -1 : 0);
break;
#if 0
case 13: // enter
mui_control_action(c, MUI_CONTROL_ACTION_SELECT,
&lb->elems.e[c->value]);
#endif
break;
default:
break;
}
if (!delta)
return false;
@ -207,13 +223,15 @@ mui_listbox_key(
}
static bool
mui_cdef_event(
mui_listbox_cdef_event(
struct mui_control_t * c,
mui_event_t *ev)
{
mui_listbox_control_t *lb = (mui_listbox_control_t *)c;
switch (ev->type) {
case MUI_EVENT_BUTTONDOWN: {
if (!mui_control_has_focus(c))
mui_control_set_focus(c);
c2_rect_t f = c->frame;
c2_rect_offset(&f, c->win->content.l, c->win->content.t);
// uint32_t page_size = (c2_rect_height(&f) / lb->elem_height)-1;
@ -244,9 +262,9 @@ mui_cdef_event(
}
return true;
} break;
case MUI_EVENT_KEYUP: {
if (mui_listbox_key(c, ev))
return true;
case MUI_EVENT_KEYDOWN: {
// printf("%s key %d\n", __func__, ev->key.key);
return mui_listbox_key(c, ev);
} break;
case MUI_EVENT_WHEEL: {
// printf("%s wheel delta %d\n", __func__, ev->wheel.delta);
@ -262,6 +280,8 @@ mui_cdef_event(
mui_control_inval(c);
return true;
} break;
default:
break;
}
return false;
}
@ -272,10 +292,16 @@ mui_cdef_listbox(
uint8_t what,
void * param)
{
mui_listbox_control_t *lb = (mui_listbox_control_t *)c;
switch (what) {
case MUI_CDEF_INIT:
if (mui_window_isfront(c->win) &&
c->win->control_focus.control == NULL)
mui_control_set_focus(c);
break;
case MUI_CDEF_DISPOSE:
// strings for the elements are not owned by the listbox
mui_listbox_elems_free(&lb->elems);
break;
case MUI_CDEF_DRAW: {
mui_drawable_t * dr = param;
@ -283,7 +309,14 @@ mui_cdef_listbox(
} break;
case MUI_CDEF_EVENT: {
mui_event_t *ev = param;
return mui_cdef_event(c, ev);
return mui_listbox_cdef_event(c, ev);
} break;
case MUI_CDEF_CAN_FOCUS: {
return true;
} break;
case MUI_CDEF_FOCUS: {
// int activate = *(int*)param;
// printf("%s activate %d\n", __func__, activate);
} break;
}
return false;
@ -327,7 +360,7 @@ mui_listbox_new(
win, MUI_CONTROL_LISTBOX, mui_cdef_listbox,
lbf, NULL, uid, sizeof(mui_listbox_control_t));
mui_listbox_control_t *lb = (mui_listbox_control_t *)c;
lb->scrollbar = mui_scrollbar_new(win, sb, 0);
lb->scrollbar = mui_scrollbar_new(win, sb, 0, 30, 0);
mui_control_set_action(lb->scrollbar, mui_listbox_sbar_action, c);
lb->elem_height = main->size + 2;
@ -355,7 +388,7 @@ mui_listbox_prepare(
mui_scrollbar_set_max(lb->scrollbar,
c2_rect_height(&content));
mui_control_set_value(lb->scrollbar, -lb->scroll);
mui_scrollbar_set_page(lb->scrollbar, c2_rect_height(&c->frame));
// mui_scrollbar_set_page(lb->scrollbar, c2_rect_height(&c->frame));
} else {
mui_scrollbar_set_max(lb->scrollbar, 0);
mui_control_set_value(lb->scrollbar, 0);

View File

@ -0,0 +1,517 @@
/*
* mui_cdef_scrollbar.c
*
* Copyright (C) 2023 Michel Pollet <buserror@gmail.com>
*
* SPDX-License-Identifier: MIT
*/
#include <stdio.h>
#include <stdlib.h>
#include "mui.h"
#include "cg.h"
enum {
MUI_CONTROL_H_SCROLL = 0,
MUI_CONTROL_V_SCROLL
};
extern const mui_control_color_t mui_control_color[MUI_CONTROL_STATE_COUNT];
enum mui_sb_part_e {
MUI_SB_PART_FRAME = 0,
MUI_SB_PART_UP, // aka MUI_SB_PART_LEFT
MUI_SB_PART_DOWN, // aka MUI_SB_PART_RIGHT
MUI_SB_PART_PAGEUP, // aka MUI_SB_PART_PAGELEFT
MUI_SB_PART_PAGEDOWN, // aka MUI_SB_PART_PAGERIGHT
MUI_SB_PART_THUMB,
MUI_SB_PART_THUMBROW,
MUI_SB_PART_COUNT,
};
typedef struct mui_scrollbar_control_t {
mui_control_t control;
uint32_t visible, page_step, line_step;
uint32_t max;
c2_pt_t drag_offset;
uint32_t saved_value; // to handle 'snapback'
uint8_t click_hold_timer;
c2_rect_t parts[MUI_SB_PART_COUNT];
} mui_scrollbar_control_t;
static void
mui_scrollbar_make_rects(
mui_control_t * c )
{
mui_scrollbar_control_t *sb = (mui_scrollbar_control_t *)c;
c2_rect_t * parts = sb->parts;
c2_rect_t f = c->frame;
c2_rect_offset(&f, c->win->content.l, c->win->content.t);
parts[MUI_SB_PART_FRAME] = f;
parts[MUI_SB_PART_THUMBROW] = f;
c2_rect_t part = f;
if (c->type == MUI_CONTROL_H_SCROLL) {
part.r = part.l + c2_rect_height(&part);
parts[MUI_SB_PART_UP] = part;
parts[MUI_SB_PART_THUMBROW].l = part.r;
part = f;
part.l = part.r - c2_rect_height(&part);
parts[MUI_SB_PART_DOWN] = part;
parts[MUI_SB_PART_THUMBROW].r = part.l;
} else {
part.b = part.t + c2_rect_width(&part);
parts[MUI_SB_PART_UP] = part;
parts[MUI_SB_PART_THUMBROW].t = part.b;
part = f;
part.t = part.b - c2_rect_width(&part);
parts[MUI_SB_PART_DOWN] = part;
parts[MUI_SB_PART_THUMBROW].b = part.t;
}
if (sb->max <= sb->visible) {
c2_rect_t z = {};
if (c->type == MUI_CONTROL_H_SCROLL)
printf("%s ZEROYING max %d visible %d\n", __func__, sb->max, sb->visible);
parts[MUI_SB_PART_THUMB] = z;
parts[MUI_SB_PART_PAGEUP] = z;
parts[MUI_SB_PART_PAGEDOWN] = z;
parts[MUI_SB_PART_THUMBROW] = z;
return;
}
// thumb rectangle
part = parts[MUI_SB_PART_THUMBROW];
uint32_t part_size;
if (c->type == MUI_CONTROL_H_SCROLL)
part_size = c2_rect_width(&part);
else
part_size = c2_rect_height(&part);
float visible = sb->visible / (float)sb->max;
float thumb_size = visible * part_size;
if (thumb_size < 20)
thumb_size = 20;
float thumb_pos = c->value / ((float)sb->max - sb->visible);
float thumb_offset = 0.5 + thumb_pos * (part_size - thumb_size);
if (c->type == MUI_CONTROL_H_SCROLL) {
// printf("%s visible:%.2f ts: %.2f thumb_pos:%.2f thumb_offset:%.2f\n",
// __func__, visible, thumb_size, thumb_pos, thumb_offset);
part.r = part.l + thumb_size;
c2_rect_offset(&part, thumb_offset, 0);
if (part.r > parts[MUI_SB_PART_DOWN].l)
c2_rect_offset(&part, parts[MUI_SB_PART_DOWN].l - part.r, 0);
parts[MUI_SB_PART_THUMB] = part;
part = f;
part.l = parts[MUI_SB_PART_UP].r + 1;
part.r = parts[MUI_SB_PART_THUMB].l - 1;
parts[MUI_SB_PART_PAGEUP] = part;
part = f;
part.l = parts[MUI_SB_PART_THUMB].r + 1;
part.r = parts[MUI_SB_PART_DOWN].l - 1;
parts[MUI_SB_PART_PAGEDOWN] = part;
} else {
part.b = part.t + thumb_size;
c2_rect_offset(&part, 0, thumb_offset);
if (part.b > parts[MUI_SB_PART_DOWN].t)
c2_rect_offset(&part, 0, parts[MUI_SB_PART_DOWN].t - part.b);
parts[MUI_SB_PART_THUMB] = part;
part = f;
part.t = parts[MUI_SB_PART_UP].b + 1;
part.b = parts[MUI_SB_PART_THUMB].t - 1;
parts[MUI_SB_PART_PAGEUP] = part;
part = f;
part.t = parts[MUI_SB_PART_THUMB].b + 1;
part.b = parts[MUI_SB_PART_DOWN].t - 1;
parts[MUI_SB_PART_PAGEDOWN] = part;
}
}
static void
mui_scrollbar_draw(
mui_window_t * win,
mui_control_t * c,
mui_drawable_t * dr )
{
mui_scrollbar_control_t *sb = (mui_scrollbar_control_t *)c;
c2_rect_t f = c->frame;
c2_rect_offset(&f, win->content.l, win->content.t);
mui_scrollbar_make_rects(c);
struct cg_ctx_t * cg = mui_drawable_get_cg(dr);
cg_set_line_width(cg, 2);
cg_rectangle(cg, f.l, f.t,
c2_rect_width(&f), c2_rect_height(&f));
cg_set_source_color(cg, &CG_COLOR(mui_control_color[c->state].fill));
cg_fill_preserve(cg);
cg_set_source_color(cg, &CG_COLOR(mui_control_color[c->state].frame));
cg_stroke(cg);
mui_font_t * icons = mui_font_find(win->ui, "icon_small");
// c2_rect_t parts[MUI_SB_PART_COUNT];
// mui_scrollbar_make_rects(c, parts);
c2_rect_t * parts = sb->parts;
mui_color_t contentFill = MUI_COLOR(0xa0a0a0ff);
mui_color_t decoColor = MUI_COLOR(0x666666ff);
c2_rect_t pf;
pf = parts[MUI_SB_PART_UP];
cg_rectangle(cg, pf.l, pf.t,
c2_rect_width(&pf), c2_rect_height(&pf));
cg_set_source_color(cg,
c->flags.hit_part == MUI_SB_PART_UP ?
&CG_COLOR(decoColor) :
&CG_COLOR(mui_control_color[c->state].fill));
cg_fill_preserve(cg);
cg_set_source_color(cg, &CG_COLOR(mui_control_color[c->state].frame));
cg_stroke(cg);
stb_ttc_measure m = {};
char * icon;
icon = c->type == MUI_CONTROL_H_SCROLL ? "" : "";
mui_font_text_measure(icons, icon, &m);
pf.l = pf.l + (c2_rect_width(&pf) - m.x1 - m.x0) / 2;
mui_font_text_draw(icons, dr, pf.tl, icon, 0,
mui_control_color[c->state].text);
pf = parts[MUI_SB_PART_DOWN];
cg_rectangle(cg, pf.l, pf.t,
c2_rect_width(&pf), c2_rect_height(&pf));
cg_set_source_color(cg,
c->flags.hit_part == MUI_SB_PART_DOWN ?
&CG_COLOR(decoColor) :
&CG_COLOR(mui_control_color[c->state].fill));
cg_fill_preserve(cg);
cg_set_source_color(cg, &CG_COLOR(mui_control_color[c->state].frame));
cg_stroke(cg);
icon = c->type == MUI_CONTROL_H_SCROLL ? "" : "";
mui_font_text_measure(icons, icon, &m);
pf.l = pf.l + (c2_rect_width(&pf) - m.x1 - m.x0) / 2;
mui_font_text_draw(icons, dr, pf.tl, icon, 0,
mui_control_color[c->state].text);
pf = parts[MUI_SB_PART_PAGEUP];
if (c2_rect_isempty(&pf) == 0) {
cg_rectangle(cg, pf.l, pf.t,
c2_rect_width(&pf), c2_rect_height(&pf));
cg_set_source_color(cg,
c->flags.hit_part == MUI_SB_PART_PAGEUP ?
&CG_COLOR(decoColor) :
&CG_COLOR(contentFill));
cg_fill(cg);
}
pf = parts[MUI_SB_PART_PAGEDOWN];
if (c2_rect_isempty(&pf) == 0) {
cg_rectangle(cg, pf.l, pf.t,
c2_rect_width(&pf), c2_rect_height(&pf));
cg_set_source_color(cg,
c->flags.hit_part == MUI_SB_PART_PAGEDOWN ?
&CG_COLOR(decoColor) :
&CG_COLOR(contentFill));
cg_fill(cg);
}
pf = parts[MUI_SB_PART_THUMB];
if (c2_rect_isempty(&pf) == 0) {
cg_rectangle(cg, pf.l, pf.t,
c2_rect_width(&pf), c2_rect_height(&pf));
cg_set_source_color(cg,
c->flags.hit_part == MUI_SB_PART_THUMB ?
&CG_COLOR(decoColor) :
&CG_COLOR(mui_control_color[c->state].fill));
cg_fill_preserve(cg);
cg_set_source_color(cg,
&CG_COLOR(mui_control_color[c->state].frame));
cg_stroke(cg);
}
}
static void
_mui_scrollbar_scroll(
mui_control_t * c,
int32_t delta )
{
mui_scrollbar_control_t *sb = (mui_scrollbar_control_t *)c;
int32_t v = (int32_t)c->value + delta;
if (v < 0)
v = 0;
if (v > ((int32_t)sb->max - (int32_t)sb->visible))
v = sb->max - sb->visible;
c->value = v;
mui_scrollbar_make_rects(c);
mui_control_inval(c);
mui_control_action(c, MUI_CONTROL_ACTION_VALUE_CHANGED, NULL);
}
static void
_mui_scrollbar_part_click(
mui_control_t * c )
{
mui_scrollbar_control_t *sb = (mui_scrollbar_control_t *)c;
int part = c->flags.hit_part % MUI_SB_PART_COUNT;
switch (part) {
case MUI_SB_PART_DOWN:
case MUI_SB_PART_UP: {
int32_t offset = sb->line_step ? sb->line_step : 30;
_mui_scrollbar_scroll(c,
part == MUI_SB_PART_UP ? -offset : offset);
} break;
case MUI_SB_PART_PAGEUP:
case MUI_SB_PART_PAGEDOWN: {
int32_t offset = sb->page_step ? sb->page_step : sb->visible;
_mui_scrollbar_scroll(c,
part == MUI_SB_PART_PAGEUP ? -offset : offset);
} break;
case MUI_SB_PART_THUMB:
mui_control_inval(c);
break;
}
}
static mui_time_t
_mui_scrollbar_click_hold_timer_cb(
struct mui_t * mui,
mui_time_t now,
void * param)
{
mui_control_t * c = param;
_mui_scrollbar_part_click(c);
return 100 * MUI_TIME_MS;
}
static void
mui_scrollbar_thumb_drag(
mui_control_t * c,
c2_pt_t * where )
{
mui_scrollbar_control_t *sb = (mui_scrollbar_control_t *)c;
c2_rect_t * parts = sb->parts;
int max_pixels;
uint32_t nv;
c2_rect_t nt = parts[MUI_SB_PART_THUMB];
if (c->type == MUI_CONTROL_H_SCROLL) {
c2_rect_offset(&nt,
-(nt.l + parts[MUI_SB_PART_UP].r) +
where->x - sb->drag_offset.x, 0);
if (nt.l < 0)
c2_rect_offset(&nt, -nt.l, 0);
if (nt.r > parts[MUI_SB_PART_DOWN].l)
c2_rect_offset(&nt, parts[MUI_SB_PART_DOWN].l - nt.r, 0);
max_pixels = parts[MUI_SB_PART_DOWN].l -
parts[MUI_SB_PART_UP].r -
c2_rect_width(&nt);
nv = nt.l * (sb->max - sb->visible) / max_pixels;
} else {
c2_rect_offset(&nt, 0,
-(nt.t + parts[MUI_SB_PART_UP].b) +
where->y - sb->drag_offset.y);
if (nt.t < 0)
c2_rect_offset(&nt, 0, -nt.t);
if (nt.b > parts[MUI_SB_PART_DOWN].t)
c2_rect_offset(&nt, 0, parts[MUI_SB_PART_DOWN].t - nt.b);
max_pixels = parts[MUI_SB_PART_DOWN].t -
parts[MUI_SB_PART_UP].b -
c2_rect_height(&nt);
nv = nt.t * (sb->max - sb->visible) / max_pixels;
}
if (nv > (sb->max - sb->visible))
nv = sb->max - sb->visible;
c->value = nv;
//printf("%s nv %3d visible %d max %d\n", __func__, nv, sb->visible, sb->max);
mui_control_inval(c);
mui_control_action(c, MUI_CONTROL_ACTION_VALUE_CHANGED, NULL);
}
static bool
mui_scrollbar_mouse(
struct mui_control_t * c,
mui_event_t * ev)
{
mui_scrollbar_control_t *sb = (mui_scrollbar_control_t *)c;
mui_scrollbar_make_rects(c);
c2_rect_t * parts = sb->parts;
switch (ev->type) {
case MUI_EVENT_BUTTONDOWN: {
for (int i = 1; i < MUI_SB_PART_COUNT; ++i) {
if (c2_rect_contains_pt(&parts[i], &ev->mouse.where)) {
c->flags.hit_part = i;
sb->drag_offset.x = ev->mouse.where.x - parts[i].l;
sb->drag_offset.y = ev->mouse.where.y - parts[i].t;
sb->saved_value = c->value;
break;
}
}
_mui_scrollbar_part_click(c);
if (c->flags.hit_part != MUI_SB_PART_THUMB) {
/*
* If the user clicks and holds, we want to keep scrolling,
* so keep a timer around that gets disabled later anyway
*/
sb->click_hold_timer = mui_timer_register(c->win->ui,
_mui_scrollbar_click_hold_timer_cb, c, 500 * MUI_TIME_MS);
}
// printf("%s hit part %d\n", __func__, c->flags.hit_part);
} break;
case MUI_EVENT_DRAG: {
if (!c->flags.hit_part)
break;
int part = c->flags.hit_part % MUI_SB_PART_COUNT;
c2_rect_t test_rect = parts[part];
if (part == MUI_SB_PART_THUMB)
c2_rect_inset(&test_rect, -60, -60);
if (c2_rect_contains_pt(&test_rect, &ev->mouse.where)) {
c->flags.hit_part = part;
switch (part) {
case MUI_SB_PART_THUMB:
mui_scrollbar_thumb_drag(c, &ev->mouse.where);
break;
default:
if (sb->click_hold_timer == 0xff)
sb->click_hold_timer = mui_timer_register(
c->win->ui,
_mui_scrollbar_click_hold_timer_cb,
c, 500 * MUI_TIME_MS);
break;
}
} else {
if (sb->click_hold_timer != 0xff) {
mui_timer_reset(c->win->ui,
sb->click_hold_timer,
_mui_scrollbar_click_hold_timer_cb, 0);
sb->click_hold_timer = 0xff;
}
c->flags.hit_part = part + MUI_SB_PART_COUNT;
if (part == MUI_SB_PART_THUMB) {
c->value = sb->saved_value;
mui_control_inval(c);
mui_control_action(c, MUI_CONTROL_ACTION_VALUE_CHANGED, NULL);
}
}
} break;
case MUI_EVENT_BUTTONUP: {
if (sb->click_hold_timer != 0xff) {
mui_timer_reset(c->win->ui,
sb->click_hold_timer,
_mui_scrollbar_click_hold_timer_cb, 0);
sb->click_hold_timer = 0xff;
}
if (!c->flags.hit_part)
break;
mui_control_inval(c);
c->flags.hit_part = 0;
} break;
default:
break;
}
return true;
}
static bool
mui_cdef_scrollbar(
struct mui_control_t * c,
uint8_t what,
void * param)
{
mui_scrollbar_control_t *sb = (mui_scrollbar_control_t *)c;
switch (what) {
case MUI_CDEF_INIT: {
mui_scrollbar_make_rects(c);
sb->visible = c->type == MUI_CONTROL_H_SCROLL ?
c2_rect_width(&sb->parts[MUI_SB_PART_FRAME]) :
c2_rect_height(&sb->parts[MUI_SB_PART_FRAME]);
// printf("%s visible %d\n", __func__, sb->visible);
} break;
case MUI_CDEF_DISPOSE:
if (sb->click_hold_timer != 0xff) {
mui_timer_reset(c->win->ui,
sb->click_hold_timer,
_mui_scrollbar_click_hold_timer_cb, 0);
sb->click_hold_timer = 0xff;
}
break;
case MUI_CDEF_SET_FRAME: // inval is done by the caller
mui_scrollbar_make_rects(c);
break;
case MUI_CDEF_DRAW: {
mui_drawable_t * dr = param;
mui_scrollbar_draw(c->win, c, dr);
} break;
case MUI_CDEF_SET_VALUE:
mui_scrollbar_make_rects(c);
mui_control_inval(c);
break;
case MUI_CDEF_EVENT: {
// printf("%s event\n", __func__);
mui_event_t *ev = param;
switch (ev->type) {
case MUI_EVENT_BUTTONUP:
case MUI_EVENT_DRAG:
case MUI_EVENT_BUTTONDOWN: {
return mui_scrollbar_mouse(c, ev);
} break;
case MUI_EVENT_WHEEL: {
int offset = sb->line_step ? sb->line_step : 30;
_mui_scrollbar_scroll(c, ev->wheel.delta * offset);
return true;
} break;
default:
break;
}
} break;
}
return false;
}
uint32_t
mui_scrollbar_get_max(
mui_control_t * c)
{
mui_scrollbar_control_t *sb = (mui_scrollbar_control_t *)c;
return sb->max;
}
void
mui_scrollbar_set_max(
mui_control_t * c,
uint32_t max)
{
mui_scrollbar_control_t *sb = (mui_scrollbar_control_t *)c;
sb->max = max;
// printf("%s max %3d visible %d\n", __func__, max, sb->visible);
mui_scrollbar_make_rects(c);
mui_control_inval(c);
}
void
mui_scrollbar_set_page(
mui_control_t * c,
uint32_t page)
{
mui_scrollbar_control_t *sb = (mui_scrollbar_control_t *)c;
sb->page_step = page;
mui_control_inval(c);
}
mui_control_t *
mui_scrollbar_new(
mui_window_t * win,
c2_rect_t frame,
uint32_t uid,
uint32_t line_step,
uint32_t page_step)
{
mui_control_t * c = mui_control_new(win,
c2_rect_width(&frame) < c2_rect_height(&frame) ?
MUI_CONTROL_V_SCROLL : MUI_CONTROL_H_SCROLL,
mui_cdef_scrollbar, frame, NULL, uid,
sizeof(mui_scrollbar_control_t));
mui_scrollbar_control_t *sb = (mui_scrollbar_control_t *)c;
sb->line_step = line_step;
sb->page_step = page_step;
return c;
}

File diff suppressed because it is too large Load Diff

View File

@ -10,7 +10,7 @@
#include <stdlib.h>
#include "mui.h"
#include "mui_priv.h"
const mui_control_color_t mui_control_color[MUI_CONTROL_STATE_COUNT] = {
[MUI_CONTROL_STATE_NORMAL] = {
@ -35,6 +35,10 @@ const mui_control_color_t mui_control_color[MUI_CONTROL_STATE_COUNT] = {
},
};
static void
mui_control_dispose_actions(
mui_control_t * c);
void
mui_control_draw(
mui_window_t * win,
@ -67,10 +71,13 @@ mui_control_new(
c->title = title ? strdup(title) : NULL;
c->win = win;
c->uid = uid;
mui_refqueue_init(&c->refs);
STAILQ_INIT(&c->actions);
TAILQ_INSERT_TAIL(&win->controls, c, self);
if (c->cdef)
c->cdef(c, MUI_CDEF_INIT, NULL);
// should we auto-focus the control? not sure..
// mui_control_set_focus(c);
return c;
}
@ -83,27 +90,29 @@ _mui_control_free(
if (c->title)
free(c->title);
c->title = NULL;
if (c->cdef)
c->cdef(c, MUI_CDEF_DISPOSE, NULL);
free(c);
}
void
mui_control_dispose(
mui_control_t * c )
{
if (!c)
return;
if (c->flags.zombie) {
printf("%s: DOUBLE delete %s\n", __func__, c->title);
if (c->win) {
TAILQ_REMOVE(&c->win->controls, c, self);
if (c->cdef)
c->cdef(c, MUI_CDEF_DISPOSE, NULL);
c->win = NULL;
mui_control_dispose_actions(c);
}
if (mui_refqueue_dispose(&c->refs) != 0) {
// fprintf(stderr, "%s Warning: control %s still has a lock\n",
// __func__, c->title);
return;
}
TAILQ_REMOVE(&c->win->controls, c, self);
if (c->win->flags.zombie || c->win->ui->action_active) {
c->flags.zombie = true;
TAILQ_INSERT_TAIL(&c->win->zombies, c, self);
} else
_mui_control_free(c);
_mui_control_free(c);
}
uint32_t
@ -146,13 +155,18 @@ _mui_control_highlight_timer_cb(
mui_time_t now,
void * param)
{
mui_control_t * c = param;
mui_control_ref_t *ref = param;
mui_control_t * c = ref->control;
if (!c) {
mui_control_deref(ref);
return 0;
}
// printf("%s: %s\n", __func__, c->title);
mui_control_set_state(c, MUI_CONTROL_STATE_NORMAL);
if (c->cdef)
c->cdef(c, MUI_CDEF_SELECT, NULL);
mui_control_action(c, MUI_CONTROL_ACTION_SELECT, NULL);
mui_control_deref(ref);
return 0;
}
@ -198,16 +212,37 @@ mui_control_event(
if (c->state != MUI_CONTROL_STATE_DISABLED &&
mui_event_match_key(ev, c->key_equ)) {
mui_control_set_state(c, MUI_CONTROL_STATE_CLICKED);
mui_control_ref_t * ref = mui_control_ref(NULL, c,
FCC('h', 'i', 'g', 'h'));
mui_timer_register(
c->win->ui, _mui_control_highlight_timer_cb,
c, MUI_TIME_SECOND / 10);
ref, MUI_TIME_SECOND / 10);
res = true;
}
break;
default: break;
}
return res;
}
void
mui_control_set_frame(
mui_control_t * c,
c2_rect_t * frame )
{
if (!c || !frame)
return;
c2_rect_t old = c->frame;
if (c2_rect_equal(&old, frame))
return;
mui_control_inval(c); // old position
c->frame = *frame;
if (c->cdef && c->cdef(c, MUI_CDEF_SET_FRAME, frame))
return;
mui_control_inval(c); // new position
}
void
mui_control_inval(
mui_control_t * c )
@ -267,6 +302,65 @@ mui_control_set_title(
mui_control_inval(c);
}
void
mui_control_lock(
mui_control_t *c)
{
if (!c)
return;
if (!c->lock.control) {
mui_control_ref(&c->lock, c, FCC('l', 'o', 'c', 'k'));
c->lock.ref.count = 10; // prevent it from being deleted
} else {
c->lock.ref.count += 10;
}
}
mui_control_t *
mui_control_unlock(
mui_control_t *c)
{
if (!c)
return NULL;
if (c->lock.control) {
if (c->lock.ref.trace)
printf("%s: control %s was locked\n",
__func__, c->title);
if (c->lock.ref.count > 10) {
c->lock.ref.count -= 10;
} else { // control was disposed of
int delete = c->lock.ref.count < 10;
// we are the last one, remove the lock
if (c->lock.ref.trace)
printf("%s: control %s unlocked delete %d\n",
__func__, c->title, delete);
mui_control_deref(&c->lock);
if (delete) {
mui_control_dispose(c);
c = NULL;
}
}
} else {
// if (c->lock.ref.trace)
printf("%s: control %s was not locked\n", __func__, c->title);
}
return c;
}
static void
mui_control_dispose_actions(
mui_control_t * c)
{
if (!c)
return;
mui_action_t *a;
while ((a = STAILQ_FIRST(&c->actions)) != NULL) {
STAILQ_REMOVE_HEAD(&c->actions, self);
free(a);
}
}
void
mui_control_action(
mui_control_t * c,
@ -275,14 +369,17 @@ mui_control_action(
{
if (!c)
return;
c->win->ui->action_active++;
mui_action_t *a;
STAILQ_FOREACH(a, &c->actions, self) {
// this prevents the callbacks from disposing of the control
// the control is locked until the last callback is done
// then it's disposed of
mui_control_lock(c);
mui_action_t *a, *safe;
STAILQ_FOREACH_SAFE(a, &c->actions, self, safe) {
if (!a->control_cb)
continue;
a->control_cb(c, a->cb_param, what, param);
}
c->win->ui->action_active--;
mui_control_unlock(c);
}
void
@ -313,3 +410,61 @@ mui_control_get_by_id(
}
return NULL;
}
bool
mui_control_set_focus(
mui_control_t * c )
{
mui_window_t * win = c->win;
if (!win || !c)
return false;
if (!c->cdef || !c->cdef(c, MUI_CDEF_CAN_FOCUS, NULL))
return false;
if (win->control_focus.control == c)
return true;
if (win->control_focus.control) {
win->control_focus.control->cdef(
win->control_focus.control, MUI_CDEF_FOCUS, &(int){0});
mui_control_inval(win->control_focus.control);
mui_control_deref(&win->control_focus);
}
mui_control_inval(c);
c->cdef(c, MUI_CDEF_FOCUS, &(int){1});
mui_control_ref(&c->win->control_focus, c, FCC('T','e','a','c'));
return true;
}
bool
mui_control_has_focus(
mui_control_t * c )
{
if (!c)
return false;
return c->win->control_focus.control == c;
}
mui_control_t *
mui_control_switch_focus(
mui_window_t * win,
int dir )
{
if (!win)
return NULL;
mui_control_t *c = win->control_focus.control;
if (!c)
c = TAILQ_FIRST(&win->controls);
if (!c)
return c;
mui_control_t * start = c;
do {
c = dir > 0 ? TAILQ_NEXT(c, self) : TAILQ_PREV(c, controls, self);
if (!c)
c = dir > 0 ? TAILQ_FIRST(&win->controls) :
TAILQ_LAST(&win->controls, controls);
if (c->cdef && c->cdef(c, MUI_CDEF_CAN_FOCUS, NULL))
break;
} while (c != start);
mui_control_set_focus(c);
printf("focus %4.4s %s\n", (char*)&c->type, c->title);
return c;
}

View File

@ -68,7 +68,7 @@ mui_drawable_clear(
if (dr->pixman)
pixman_image_unref(dr->pixman);
dr->pixman = NULL;
for (int i = 0; i < (int)dr->clip.count; i++)
for (uint i = 0; i < dr->clip.count; i++)
pixman_region32_fini(&dr->clip.e[i]);
mui_clip_stack_clear(&dr->clip);
if (dr->pix.pixels && dr->dispose_pixels)
@ -209,7 +209,7 @@ mui_drawable_set_clip(
{
if (!dr)
return;
for (int i = 0; i < (int)dr->clip.count; i++)
for (uint i = 0; i < dr->clip.count; i++)
pixman_region32_fini(&dr->clip.e[i]);
mui_clip_stack_clear(&dr->clip);
if (clip && clip->count) {

View File

@ -15,6 +15,7 @@
#include "stb_ttc.h"
//#ifndef __wasm__
#define INCBIN_STYLE INCBIN_STYLE_SNAKE
#define INCBIN_PREFIX mui_
#include "incbin.h"
@ -23,9 +24,16 @@ INCBIN(main_font, "fonts/Charcoal_mui.ttf");
INCBIN(icon_font, "fonts/typicon.ttf");
INCBIN(dingbat_font, "fonts/Dingbat.ttf");
INCBIN(geneva_font, "fonts/Geneva.ttf");
//#endif
#include "mui.h"
// "Narrow style" reduces the advance by this factor
// Not the 'space' characters are reduced even more (twice that)
#define MUI_NARROW_ADVANCE_FACTOR 0.92
// Interline factor for compact text
#define MUI_COMPACT_FACTOR 0.85
mui_font_t *
mui_font_find(
mui_t *ui,
@ -54,9 +62,9 @@ mui_font_t *
mui_font_from_mem(
mui_t *ui,
const char *name,
unsigned int size,
uint size,
const void *font_data,
unsigned int font_size )
uint font_size )
{
mui_font_t *f = calloc(1, sizeof(*f));
f->name = strdup(name);
@ -73,12 +81,14 @@ mui_font_init(
mui_t *ui)
{
// printf("%s: Loading fonts\n", __func__);
#ifndef __wasm__
mui_font_from_mem(ui, "main", 28,
mui_main_font_data, mui_main_font_size);
mui_font_from_mem(ui, "icon_large", 96,
mui_icon_font_data, mui_icon_font_size);
mui_font_from_mem(ui, "icon_small", 30,
mui_icon_font_data, mui_icon_font_size);
#endif
}
void
@ -89,6 +99,7 @@ mui_font_dispose(
while ((f = TAILQ_FIRST(&ui->fonts))) {
TAILQ_REMOVE(&ui->fonts, f, self);
stb_ttc_Free(&f->ttc);
mui_drawable_dispose(&f->font);
free(f->name);
free(f);
}
@ -113,15 +124,15 @@ mui_font_text_draw(
mui_drawable_t *dr,
c2_pt_t where,
const char *text,
unsigned int text_len,
uint text_len,
mui_color_t color)
{
struct stb_ttc_info * ttc = &font->ttc;
unsigned int state = 0;
uint state = 0;
float scale = stbtt_ScaleForPixelHeight(&ttc->font, font->size);
double xpos = 0;
unsigned int last = 0;
unsigned int cp = 0;
uint last = 0;
uint cp = 0;
if (!text_len)
text_len = strlen(text);
@ -132,7 +143,7 @@ mui_font_text_draw(
pixman_image_t * fill = pixman_image_create_solid_fill(&pc);
where.y += font->ttc.ascent * scale;
for (unsigned int ch = 0; text[ch] && ch < text_len; ch++) {
for (uint ch = 0; text[ch] && ch < text_len; ch++) {
if (stb_ttc__UTF8_Decode(&state, &cp, text[ch]) != UTF8_ACCEPT)
continue;
if (last) {
@ -171,40 +182,59 @@ mui_font_text_draw(
IMPLEMENT_C_ARRAY(mui_glyph_array);
IMPLEMENT_C_ARRAY(mui_glyph_line_array);
void
mui_font_measure(
mui_font_t *font,
c2_rect_t bbox,
const char *text,
unsigned int text_len,
uint text_len,
mui_glyph_line_array_t *lines,
uint16_t flags)
mui_text_e flags)
{
struct stb_ttc_info * ttc = &font->ttc;
unsigned int state = 0;
uint state = 0;
float scale = stbtt_ScaleForPixelHeight(&ttc->font, font->size);
unsigned int last = 0;
unsigned int cp = 0;
uint last = 0;
uint cp = 0;
int debug = flags & MUI_TEXT_DEBUG;
if (!text_len)
text_len = strlen(text);
//debug = !strncmp(text, "Titan", 5) || !strcmp(text, "Driver");
if (debug)
printf("Measure text %s\n", text);
lines->margin_left = c2_rect_width(&bbox);
lines->margin_right = 0;
lines->height = 0;
c2_pt_t where = {};
unsigned int ch = 0;
uint ch = 0;
int wrap_chi = 0;
int wrap_w = 0;
int wrap_count = 0;
float compact = flags & MUI_TEXT_ALIGN_COMPACT ?
MUI_COMPACT_FACTOR : 1.0;
float narrow = flags & MUI_TEXT_STYLE_NARROW ?
MUI_NARROW_ADVANCE_FACTOR : 1.0;
float narrow_space = flags & MUI_TEXT_STYLE_NARROW ?
narrow * 0.9 : 1.0;
mui_glyph_array_t * line = NULL;
do {
where.y += font->ttc.ascent * scale;
const mui_glyph_array_t zero = {};
mui_glyph_line_array_push(lines, zero);
mui_glyph_array_t * line = &lines->e[lines->count - 1];
line = &lines->e[lines->count - 1];
line->x = 0;
line->t = where.y;
where.y += (font->ttc.ascent * compact) * scale;
line->b = where.y - (font->ttc.descent * scale);
line->y = where.y;
line->w = 0;
wrap_chi = ch;
wrap_w = 0;
wrap_count = 0;
if (debug)
printf("line %d y:%3d ch:%3d\n", lines->count,
line->y, ch);
for (;text[ch]; ch++) {
if (stb_ttc__UTF8_Decode(&state, &cp, text[ch]) != UTF8_ACCEPT)
continue;
@ -213,8 +243,11 @@ mui_font_measure(
line->w += kern;
}
last = cp;
// printf("glyph %3d : %04x:%c\n", ch, cp, cp < 32 ? '.' : cp);
if (debug) printf(" glyph ch:%3d : %04x:%c S:%d L:%2d:%2d\n",
ch, cp, cp < 32 ? '.' : cp, state,
lines->count-1, line->count);
if (cp == '\n') {
line->line_break = true;
ch++;
break;
}
@ -231,7 +264,11 @@ mui_font_measure(
continue;
if (gc->p_y == (unsigned short) -1)
stb_ttc__ScaledGlyphRenderToCache(ttc, gc);
if (((line->w + gc->advance) * scale) > c2_rect_width(&bbox)) {
float advance = gc->advance * narrow;
// we make spaces even narrower (if narrow style is on)
if (cp == ' ')
advance *= narrow_space;
if (((line->w + advance) * scale) > c2_rect_width(&bbox)) {
if (wrap_count) {
ch = wrap_chi + 1;
line->count = wrap_count;
@ -239,33 +276,80 @@ mui_font_measure(
}
break;
}
line->w += gc->advance;
mui_glyph_array_push(line, gc->index);
mui_glyph_t g = {
.glyph = cp,
.pos = ch,
.index = gc->index,
.x = (line->w * scale) + gc->x0,
.w = advance * scale,
};
mui_glyph_array_push(line, g);
// printf(" PUSH[%2d] glyph %3d : %04x:%c x:%3d w:%3d\n",
// line->count - 1, g.pos, text[g.pos], text[g.pos],
// g.x, g.w);
line->w += advance;
};
if (line->line_break) {
// stuff a newline here
mui_glyph_t g = {
.glyph = 0,
.pos = ch,
.x = (line->w) * scale,
};
mui_glyph_array_push(line, g);
}
// zero terminate the line, so there is a marker at the end
mui_glyph_t g = {
.glyph = 0,
.pos = ch,
.x = (line->w) * scale,
};
mui_glyph_array_push(line, g);
line->count--;
where.y += -font->ttc.descent * scale;
} while (text[ch] && ch < text_len);
int bh = 0;
for (int i = 0; i < (int)lines->count; i++) {
/*
* Finalise the lines, calculate the total height, and the margins
* Margins are the minimal x and maximal x of the lines
*/
for (uint i = 0; i < lines->count; i++) {
mui_glyph_array_t * line = &lines->e[i];
bh = line->y - (font->ttc.descent * scale);
lines->height = line->y - (font->ttc.descent * scale);
line->w *= scale;
// printf(" line %d y %3d size %d width %d\n", i,
// line->y, line->count, line->w);
}
// printf("box height is %d/%d\n", bh, c2_rect_height(&bbox));
int ydiff = 0;
if (flags & MUI_TEXT_ALIGN_MIDDLE) {
ydiff = (c2_rect_height(&bbox) - bh) / 2;
ydiff = (c2_rect_height(&bbox) - (int)lines->height) / 2;
} else if (flags & MUI_TEXT_ALIGN_BOTTOM) {
ydiff = c2_rect_height(&bbox) - bh;
ydiff = c2_rect_height(&bbox) - (int)lines->height;
}
for (int i = 0; i < (int)lines->count; i++) {
if (debug)
printf("box height is %d/%d ydiff:%d\n",
lines->height, c2_rect_height(&bbox), ydiff);
for (uint i = 0; i < lines->count; i++) {
mui_glyph_array_t * line = &lines->e[i];
line->y += ydiff;
if (i == lines->count - 1) // last line is always a break
line->line_break = true;
if (flags & MUI_TEXT_ALIGN_RIGHT) {
line->x = c2_rect_width(&bbox) - line->w;
} else if (flags & MUI_TEXT_ALIGN_CENTER) {
line->x = (c2_rect_width(&bbox) - line->w) / 2;
} else if (flags & MUI_TEXT_ALIGN_FULL) {
line->x = 0;
if (line->count > 1 && !line->line_break) {
float space = (c2_rect_width(&bbox) - line->w) / (line->count - 1);
for (uint ci = 1; ci < line->count; ci++)
line->e[ci].x += ci * space;
}
}
if (line->x < (int)lines->margin_left)
lines->margin_left = line->x;
if (line->x + line->w > lines->margin_right) // last x
lines->margin_right = line->x + line->w;
if (debug)
printf(" line %d y:%3d size %3d width %.2f\n", i,
line->y, line->count, line->w);
}
}
@ -275,7 +359,7 @@ mui_font_measure_clear(
{
if (!lines)
return;
for (int i = 0; i < (int)lines->count; i++) {
for (uint i = 0; i < lines->count; i++) {
mui_glyph_array_t * line = &lines->e[i];
mui_glyph_array_free(line);
}
@ -290,39 +374,67 @@ mui_font_measure_draw(
c2_rect_t bbox,
mui_glyph_line_array_t *lines,
mui_color_t color,
uint16_t flags)
mui_text_e flags)
{
pixman_color_t pc = PIXMAN_COLOR(color);
pixman_image_t * fill = pixman_image_create_solid_fill(&pc);
struct stb_ttc_info * ttc = &font->ttc;
float scale = stbtt_ScaleForPixelHeight(&ttc->font, font->size);
mui_drawable_t * src = &font->font;
mui_drawable_t * dst = dr;
// all glyphs we need were loaded, update the pixman texture
_mui_font_pixman_prep(font);
for (int li = 0; li < (int)lines->count; li++) {
for (uint li = 0; li < lines->count; li++) {
mui_glyph_array_t * line = &lines->e[li];
int xpos = 0;//where.x / scale;
for (int ci = 0; ci < (int)line->count; ci++) {
unsigned int cache_index = line->e[ci];
int lastu = line->x;
for (uint ci = 0; ci < line->count; ci++) {
uint cache_index = line->e[ci].index;
if (line->e[ci].glyph < ' ')
continue;
stb_ttc_g *gc = &ttc->glyph[cache_index];
// int pxpos = gc->x0 + ((xpos + gc->lsb) * scale);
int pxpos = gc->x0 + ((xpos + 0) * scale);
float pxpos = line->e[ci].x;
int ph = gc->y1 - gc->y0;
int pw = gc->x1 - gc->x0;
pixman_image_composite32(
PIXMAN_OP_OVER,
fill,
pixman_image_composite32(PIXMAN_OP_OVER, fill,
mui_drawable_get_pixman(src),
mui_drawable_get_pixman(dst),
0, 0, gc->p_x, gc->p_y,
bbox.l + line->x + pxpos,
bbox.t + line->y + gc->y0, pw, ph);
xpos += gc->advance;
bbox.l + (line->x + pxpos),
bbox.t + (line->y + gc->y0), pw, ph);
/*
* For 'cheap' bold, we just draw the glyph again over the
* same position, but shifted by one pixel in x.
* Works surprisingly well!
*/
if (flags & MUI_TEXT_STYLE_BOLD) {
pixman_image_composite32(PIXMAN_OP_OVER, fill,
mui_drawable_get_pixman(src),
mui_drawable_get_pixman(dst),
0, 0, gc->p_x, gc->p_y,
bbox.l + line->x + pxpos + 1,
bbox.t + line->y + gc->y0, pw, ph);
}
/*
* Underline is very primitive, it just draws a line
* under the glyphs, but it's enough for now. Skips the
* ones with obvious descenders. This is far from perfect
* obviously but it's a start.
*/
if (flags & MUI_TEXT_STYLE_ULINE) {
// don't draw under glyphs like qpygj etc
bool draw_underline = gc->y1 <= 2;
if (draw_underline) {
c2_rect_t u = C2_RECT(
bbox.l + lastu,
bbox.t + line->y + 2,
bbox.l + line->x + pxpos + pw,
bbox.t + line->y + 3);
pixman_image_fill_boxes(PIXMAN_OP_OVER,
mui_drawable_get_pixman(dst),
&pc, 1, (pixman_box32_t*)&u);
}
lastu = line->x + pxpos + pw;
}
}
}
pixman_image_unref(fill);
@ -334,9 +446,9 @@ mui_font_textbox(
mui_drawable_t *dr,
c2_rect_t bbox,
const char *text,
unsigned int text_len,
uint text_len,
mui_color_t color,
uint16_t flags)
mui_text_e flags)
{
mui_glyph_line_array_t lines = {};
@ -344,8 +456,6 @@ mui_font_textbox(
text_len = strlen(text);
mui_font_measure(font, bbox, text, text_len, &lines, flags);
mui_font_measure_draw(font, dr, bbox, &lines, color, flags);
mui_font_measure_clear(&lines);
}

View File

@ -22,6 +22,7 @@ enum {
MUI_CONTROL_MENUITEM = FCC('m','i','t','m'),
MUI_CONTROL_SUBMENUITEM = FCC('s','m','i','t'),
MUI_CONTROL_POPUP = FCC('p','o','p','u'),
MUI_CONTROL_POPUP_MARK = FCC('p','o','p','m'),
};
/* These are *window action* -- parameter 'target' is a mui_menu_t* */
@ -37,33 +38,33 @@ enum mui_menu_action_e {
struct mui_menu_control_t;
struct mui_menubar_t;
typedef struct mui_menu_t {
mui_window_t win;
unsigned int click_inside : 1,
uint click_inside : 1,
drag_ev : 1,
closing: 1, // prevent double-delete
timer_call_count : 2; // used by mui_menu_close_timer_cb
mui_control_t * highlighted;
mui_control_ref_t highlighted; // mui_menuitem_control_t *
mui_time_t sub_open_stamp;
// currently open menu, if any
struct mui_menu_control_t * sub;
struct mui_menubar_t * menubar;
mui_control_ref_t sub; // mui_menu_control_t *
mui_window_ref_t menubar; // mui_menubar_t * window
} mui_menu_t;
typedef struct mui_menubar_t {
mui_window_t win;
unsigned int click_inside : 1,
uint click_inside : 1,
drag_ev : 1,
was_highlighted : 1,
timer_call_count : 2; // used by mui_menu_close_timer_cb
// currently open menu title
struct mui_menu_control_t * selected_title;
mui_control_ref_t selected_title; // mui_menu_control_t *
// keep track of the menus, and their own submenus as they are being opened
// this is to keep track of the 'hierarchy' of menus, so that we can close
// them all when the user clicks outside of them, or release the mouse.
mui_menu_t * open[8];
int open_count;
mui_window_ref_t open[8];
uint open_count;
bool delayed_closing;
} mui_menubar_t;
@ -111,20 +112,22 @@ mui_cdef_popup(
// any open menus (and their submenus, if any)
static bool
_mui_menubar_close_menu(
mui_menubar_t *mbar )
mui_menubar_t *mbar )
{
if (mbar->delayed_closing)
return false;
mbar->click_inside = false;
mui_control_set_state((mui_control_t*)mbar->selected_title, 0);
if (mbar->selected_title)
mbar->selected_title->menu_window = NULL;
mui_menu_control_t * m = (mui_menu_control_t*)mbar->selected_title.control;
D(printf("%s %s\n", __func__, m ? ((mui_control_t*)m)->title : "???");)
mui_control_set_state((mui_control_t*)m, 0);
if (m)
mui_window_deref(&m->menu_window);
if (!mbar->open_count)
return false;
mbar->selected_title = NULL;
for (int i = 0; i < mbar->open_count; i++) {
mui_menu_close(&mbar->open[i]->win);
mbar->open[i] = NULL;
mui_control_deref(&mbar->selected_title);
for (uint i = 0; i < mbar->open_count; i++) {
mui_menu_close(mbar->open[i].window);
mui_window_deref(&mbar->open[i]);
}
mbar->open_count = 0;
return true;
@ -133,17 +136,17 @@ _mui_menubar_close_menu(
// close the submenu from a hierarchical menu item
static bool
_mui_menu_close_submenu(
mui_menu_t * menu )
mui_menu_t * menu )
{
mui_menu_control_t * sub = menu->sub;
mui_menu_control_t * sub = (mui_menu_control_t*)menu->sub.control;
if (!sub)
return false;
menu->sub = NULL;
mui_control_deref(&menu->sub);
mui_control_set_state((mui_control_t*)sub, 0);
if (sub->menu_window) {
mui_menu_close(sub->menu_window);
if (sub->menu_window.window) {
mui_menu_close(sub->menu_window.window);
}
sub->menu_window = NULL;
mui_window_deref(&sub->menu_window);
return true;
}
@ -158,26 +161,27 @@ mui_menu_close_timer_cb(
void * param)
{
mui_menu_t * menu = param;
if (!menu->highlighted) {
printf("%s: no selected item, closing\n", __func__);
if (!menu->highlighted.control) {
D(printf("%s: no selected item, closing\n", __func__);)
mui_window_dispose(&menu->win);
return 0;
}
menu->timer_call_count++;
mui_control_set_state(menu->highlighted,
menu->highlighted->state == MUI_CONTROL_STATE_CLICKED ?
mui_control_set_state(menu->highlighted.control,
menu->highlighted.control->state == MUI_CONTROL_STATE_CLICKED ?
MUI_CONTROL_STATE_NORMAL : MUI_CONTROL_STATE_CLICKED);
if (menu->timer_call_count == 3) {
// we are done!
mui_menuitem_control_t * item = (mui_menuitem_control_t*)menu->highlighted;
mui_menuitem_control_t * item =
(mui_menuitem_control_t*)menu->highlighted.control;
mui_window_action(&menu->win, MUI_MENU_ACTION_SELECT, &item->item);
// mui_menu_close_all(&menu->win);
if (menu->menubar) {
mui_menubar_t * mbar = (mui_menubar_t*)menu->menubar;
if (menu->menubar.window) {
mui_menubar_t * mbar = (mui_menubar_t*)menu->menubar.window;
mui_window_action(&mbar->win,
MUI_MENUBAR_ACTION_SELECT, &item->item);
mbar->delayed_closing = false;
_mui_menubar_close_menu((mui_menubar_t*)menu->menubar);
_mui_menubar_close_menu(mbar);
} else
mui_menu_close(&menu->win);
return 0;
@ -260,12 +264,14 @@ mui_menubar_handle_mouse(
mui_window_t * win = &mbar->win;
bool inside = c2_rect_contains_pt(&win->frame, &ev->mouse.where);
mui_control_t * c = inside ? mui_control_locate(win, ev->mouse.where) : NULL;
mui_control_t * c = inside ?
mui_control_locate(win, ev->mouse.where) : NULL;
switch (ev->type) {
case MUI_EVENT_BUTTONUP: {
D(printf("%s up drag %d click in:%d high:%d was:%d\n", __func__,
mbar->drag_ev, mbar->click_inside,
mbar->selected_title ? 1 : 0, mbar->was_highlighted);)
mbar->selected_title.control ? 1 : 0,
mbar->was_highlighted);)
if (mbar->drag_ev == 0 && mbar->click_inside) {
if (mbar->was_highlighted) {
return _mui_menubar_close_menu(mbar);
@ -291,26 +297,32 @@ mui_menubar_handle_mouse(
D(printf("%s click inside %d\n", __func__, inside);)
mbar->drag_ev = 0;
mbar->click_inside = inside;
mbar->was_highlighted = mbar->selected_title != NULL;
mbar->was_highlighted = mbar->selected_title.control != NULL;
}
if (c && mui_control_get_state(c) != MUI_CONTROL_STATE_DISABLED) {
if (mbar->selected_title &&
c != (mui_control_t*)mbar->selected_title)
if (mbar->selected_title.control &&
c != mbar->selected_title.control) {
_mui_menubar_close_menu(mbar);
}
mbar->click_inside = true;
mui_control_set_state(c, MUI_CONTROL_STATE_CLICKED);
mui_menu_control_t *title = (mui_menu_control_t*)c;
mbar->selected_title = title;
mui_control_deref(&mbar->selected_title);
mui_control_ref(&mbar->selected_title, c, FCC('s','e','l','t'));
if (mui_control_get_type(c) == MUI_CONTROL_MENUTITLE) {
if (title->menu_window == NULL) {
title->menu_window = _mui_menu_create(
if (title->menu_window.window == NULL) {
mui_window_t *new = _mui_menu_create(
win->ui, mbar, C2_PT(c->frame.l, c->frame.b),
title->menu.e);
mui_window_ref(&title->menu_window, new,
FCC('m','e','n','u'));
}
}
return true;
}
} break;
default:
break;
}
return false;
}
@ -381,6 +393,13 @@ mui_wdef_menubar(
{
mui_menubar_t * mbar = (mui_menubar_t*)win;
switch (what) {
case MUI_WDEF_DISPOSE: {
mui_control_deref(&mbar->selected_title);
for (uint i = 0; i < mbar->open_count; i++) {
mui_menu_close(mbar->open[i].window);
mui_window_deref(&mbar->open[i]);
}
} break;
case MUI_WDEF_DRAW: {
mui_drawable_t * dr = param;
mui_wdef_menubar_draw(win, dr);
@ -401,6 +420,8 @@ mui_wdef_menubar(
if (mui_menubar_handle_keydown(mbar, ev))
return true;
} break;
default:
break;
}
} break;
}
@ -423,9 +444,9 @@ mui_menu_handle_mouse(
if (menu->drag_ev == 0) {
// return true;
}
mui_menubar_t * mbar = (mui_menubar_t*)menu->menubar;
if (menu->highlighted &&
menu->highlighted->type != MUI_CONTROL_SUBMENUITEM) {
mui_menubar_t * mbar = (mui_menubar_t*)menu->menubar.window;
if (menu->highlighted.control &&
menu->highlighted.control->type != MUI_CONTROL_SUBMENUITEM) {
/*
* This tells the normal closing code that we are
* taking care of the closing with the timer, and not *now*
@ -436,7 +457,7 @@ mui_menu_handle_mouse(
mui_timer_register(win->ui, mui_menu_close_timer_cb, menu,
MUI_MENU_CLOSE_BLINK_DELAY);
} else {
menu->highlighted = NULL;
mui_control_deref(&menu->highlighted);
if (mbar)
_mui_menubar_close_menu(mbar);
else
@ -460,37 +481,44 @@ mui_menu_handle_mouse(
}
// printf("%s in:%d c:%s\n", __func__, inside, c ? c->title : "");
if (c && mui_control_get_state(c) != MUI_CONTROL_STATE_DISABLED) {
if (menu->sub && c != (mui_control_t*)menu->sub)
if (menu->sub.control && c != menu->sub.control)
_mui_menu_close_submenu(menu);
if (menu->highlighted && c != menu->highlighted)
mui_control_set_state(menu->highlighted, 0);
if (menu->highlighted.control &&
c != menu->highlighted.control)
mui_control_set_state(menu->highlighted.control, 0);
mui_control_set_state(c, MUI_CONTROL_STATE_CLICKED);
menu->highlighted = c;
mui_control_deref(&menu->highlighted);
mui_control_ref(&menu->highlighted, c, FCC('h','i','g','h'));
if (c->type == MUI_CONTROL_SUBMENUITEM) {
mui_menu_control_t *title = (mui_menu_control_t*)c;
if (title->menu_window == NULL) {
if (title->menu_window.window == NULL) {
c2_pt_t where = C2_PT(c->frame.r, c->frame.t);
c2_pt_offset(&where, win->content.l, win->content.t);
title->menu_window = _mui_menu_create(
mui_window_t *new = _mui_menu_create(
win->ui,
(mui_menubar_t*)menu->menubar, where,
(mui_menubar_t*)menu->menubar.window, where,
title->menu.e);
menu->sub = title;
mui_window_ref(&title->menu_window, new,
FCC('m','e','n','u'));
mui_control_ref(&menu->sub, c,
FCC('s','u','b','m'));
menu->sub_open_stamp = mui_get_time();
mui_window_action(&menu->win, MUI_MENU_ACTION_OPEN,
title->menu_window);
mui_window_set_action(title->menu_window,
title->menu_window.window);
mui_window_set_action(title->menu_window.window,
mui_submenu_action_cb, menu);
}
}
} else {
if (!menu->sub) {
if (menu->highlighted)
mui_control_set_state(menu->highlighted, 0);
menu->highlighted = NULL;
if (!menu->sub.control) {
if (menu->highlighted.control)
mui_control_set_state(menu->highlighted.control, 0);
mui_control_deref(&menu->highlighted);
}
}
} break;
default:
break;
}
return false;
}
@ -504,6 +532,7 @@ mui_wdef_menu(
mui_menu_t * menu = (mui_menu_t*)win;
switch (what) {
case MUI_WDEF_DISPOSE: {
mui_window_deref(&menu->menubar);
_mui_menu_close_submenu(menu);
} break;
case MUI_WDEF_DRAW: {
@ -520,6 +549,8 @@ mui_wdef_menu(
if (mui_menu_handle_mouse(menu, ev))
return true;
} break;
default:
break;
}
} break;
}
@ -539,7 +570,8 @@ mui_menubar_new(
ui, mbf,
mui_wdef_menubar, MUI_WINDOW_MENUBAR_LAYER,
"Menubar", sizeof(*mbar));
ui->menubar = &mbar->win;
mbar->win.flags.style = MUI_MENU_STYLE_MBAR;
mui_window_ref(&ui->menubar, &mbar->win, FCC('m','b','a','r'));
return &mbar->win;
}
@ -547,7 +579,7 @@ mui_window_t *
mui_menubar_get(
mui_t * ui )
{
return ui ? ui->menubar : NULL;
return ui->menubar.window;
}
bool
@ -586,7 +618,7 @@ mui_menubar_add_simple(
//printf("%s title %s rect %s\n", __func__, title, c2_rect_as_str(&title_rect));
mui_menu_control_t *menu = (mui_menu_control_t*)c;
menu->menubar = win;
mui_window_ref(&menu->menubar, win, FCC('m','b','a','r'));
/*
* We do not clone the items, so they must be static
* from somewhere -- we do not free them either.
@ -601,6 +633,63 @@ mui_menubar_add_simple(
return c;
}
/* 'count' can be zero, in which case *requires* NULL termination */
mui_control_t *
mui_menubar_add_menu(
mui_window_t * win,
uint32_t menu_uid,
mui_menu_item_t * items,
uint count )
{
c2_rect_t parts[MUI_MENUTITLE_PART_COUNT];
mui_menutitle_get_part_locations(win->ui, NULL, items, parts);
int title_width = c2_rect_width(&parts[MUI_MENUTITLE_PART_ALL]);
c2_rect_t title_rect = { .t = 2 };
mui_control_t * last = TAILQ_LAST(&win->controls, controls);
if (last) {
c2_rect_offset(&title_rect, last->frame.r, 0);
} else
title_rect.l = 4;
title_rect.r = title_rect.l + title_width + 6;
title_rect.b = win->content.b + 0;// title_rect.t + m.ascent - m.descent;
mui_control_t * c = mui_control_new(
win, MUI_CONTROL_MENUTITLE, mui_cdef_popup,
title_rect, items[0].title, menu_uid,
sizeof(mui_menu_control_t));
//printf("%s title %s rect %s\n", __func__, title, c2_rect_as_str(&title_rect));
mui_menu_control_t *menu = (mui_menu_control_t*)c;
mui_window_ref(&menu->menubar, win, FCC('m','b','a','r'));
menu->item.item = items[0];
/*
* We do not clone the items, so they must be static
* from somewhere -- we do not free them either.
*/
int sub_count = count ? count - 1 : 0;
for (int ii = 1; items[ii].title; ii++)
sub_count++;
menu->menu.count = sub_count ;
menu->menu.e = items + 1;
menu->menu.read_only = 1;
return c;
}
#if 0
void
mui_menu_set_title(
mui_control_t * c,
const mui_menu_item_t *title )
{
mui_menu_control_t * menu = (mui_menu_control_t*)c;
mui_control_set_title(c, title);
mui_window_set_title(&menu->menu_window, title);
}
#endif
mui_window_t *
mui_menubar_highlight(
mui_window_t * win,
@ -624,13 +713,16 @@ mui_menu_get_enclosing_rect(
mui_menu_item_t * items)
{
c2_rect_t frame = {};
if (!items)
return frame;
frame.b = 1; // space for outside frame
mui_font_t * main = mui_font_find(ui, "main");
stb_ttc_measure m = {};
for (int i = 0; items[i].title; i++) {
items[i].location = frame.br;
items[i].location = frame.b;
if (items[i].title && items[i].title[0] != '-') {
mui_font_text_measure(main, items[i].title, &m);
m.x0 = 0;
int title_width = main->size + m.x1 - m.x0 ;
if (items[i].kcombo[0]) {
@ -641,11 +733,13 @@ mui_menu_get_enclosing_rect(
if (title_width > frame.r)
frame.r = title_width;
frame.b += main->size + 2;
items[i].height = main->size + 4;
} else {
frame.b += main->size / 4;
items[i].height = main->size / 4;
}
frame.b += items[i].height;
}
frame.b += 1; // space for outside frame
return frame;
}
@ -686,9 +780,12 @@ _mui_menu_create(
ui, on_screen,
mui_wdef_menu, MUI_WINDOW_MENU_LAYER,
items[0].title, sizeof(*menu));
menu->win.flags.style = MUI_MENU_STYLE_MENU;
if (mbar) {
mbar->open[mbar->open_count++] = menu;
menu->menubar = mbar;
mui_window_ref(&mbar->open[mbar->open_count], &menu->win,
FCC('m','e','n','u'));
mbar->open_count++;
mui_window_ref(&menu->menubar, &mbar->win, FCC('m','b','a','r'));
}
/* Walk all the items in out static structure, and create the controls
* for each of them with their own corresponding item */
@ -696,11 +793,8 @@ _mui_menu_create(
mui_menu_item_t * item = &items[i];
item->index = i;
c2_rect_t title_rect = frame;
title_rect.t = item->location.y - 1;
if (items[i+1].title)
title_rect.b = items[i+1].location.y;
else
title_rect.b = frame.b;
title_rect.t = item->location;
title_rect.b = title_rect.t + item->height;
mui_control_t * c = NULL;
if (item->submenu) {
@ -739,13 +833,15 @@ static void
mui_menu_close(
mui_window_t * win )
{
if (!win)
return;
mui_menu_t * menu = (mui_menu_t*)win;
mui_menubar_t * mbar = (mui_menubar_t*)menu->menubar; // can be NULL
mui_menubar_t * mbar = (mui_menubar_t*)menu->menubar.window; // can be NULL
menu->highlighted = NULL;
mui_control_deref(&menu->highlighted);
if (mbar && mbar->open_count) {
mbar->open[mbar->open_count-1] = NULL;
mbar->open_count--;
mui_window_deref(&mbar->open[mbar->open_count]);
}
mui_window_dispose(win);
}
@ -759,7 +855,7 @@ mui_popupmenu_action_cb(
void * param) // popup control here
{
mui_menu_control_t * pop = cb_param;
printf("%s %4.4s\n", __func__, (char*)&what);
D(printf("%s %4.4s\n", __func__, (char*)&what);)
switch (what) {
case MUI_MENU_ACTION_SELECT: {
mui_menu_item_t * item = param;
@ -780,10 +876,11 @@ mui_popupmenu_handle_mouse(
mui_control_t * c = &pop->item.control;
switch (ev->type) {
case MUI_EVENT_BUTTONUP: {
printf("%s up has popup %d\n", __func__, pop->menu_window != NULL);
if (pop->menu_window) {
D(printf("%s up has popup %d\n", __func__,
pop->menu_window.window != NULL);)
if (pop->menu_window.window) {
// mui_menu_close(pop->menu_window);
pop->menu_window = NULL;
mui_window_deref(&pop->menu_window);
}
mui_control_set_state(c, 0);
} break;
@ -791,20 +888,29 @@ mui_popupmenu_handle_mouse(
mui_control_set_state(c,
ev->type != MUI_EVENT_BUTTONUP ?
MUI_CONTROL_STATE_CLICKED : 0);
if (!pop->menu_window) {
if (!pop->menu_window.window && pop->menu.e) {
c2_pt_t loc = pop->menu_frame.tl;
c2_pt_offset(&loc, c->win->content.l, c->win->content.t);
c2_pt_offset(&loc, 0, -pop->menu.e[c->value].location.y);
pop->menu_window = _mui_menu_create(
if (c->type == MUI_CONTROL_POPUP)
c2_pt_offset(&loc, 0, -pop->menu.e[c->value].location);
else if (c->type == MUI_CONTROL_POPUP_MARK)
c2_pt_offset(&loc, 0, c2_rect_height(&c->frame));
mui_window_t *new = _mui_menu_create(
c->win->ui, NULL, loc,
pop->menu.e);
mui_window_set_action(pop->menu_window,
// override the default style to make it specific to a popup
new->flags.style = MUI_MENU_STYLE_POPUP;
mui_window_ref(&pop->menu_window, new,
FCC('m','e','n','u'));
mui_window_set_action(pop->menu_window.window,
mui_popupmenu_action_cb, pop);
// pass the mousedown to the new popup
// mui_window_handle_mouse(pop->menu_window, ev);
}
mui_control_inval(c);
} break;
default:
break;
}
return true;
}
@ -821,15 +927,24 @@ mui_cdef_popup(
case MUI_CDEF_DISPOSE:
switch (c->type) {
case MUI_CONTROL_POPUP:
case MUI_CONTROL_POPUP_MARK:
case MUI_CONTROL_MENUTITLE: {
mui_menu_control_t *pop = (mui_menu_control_t*)c;
if (pop->menu_window) {
mui_menu_close(pop->menu_window);
pop->menu_window = NULL;
if (pop->menu_window.window) {
mui_menu_close(pop->menu_window.window);
mui_window_deref(&pop->menu_window);
}
mui_menu_items_clear(&pop->menu);
if (!pop->menu.read_only)
mui_menu_items_free(&pop->menu);
if (pop->item.color_icon)
mui_drawable_dispose(pop->item.color_icon);
} break;
case MUI_CONTROL_MENUITEM:
case MUI_CONTROL_SUBMENUITEM: {
mui_menuitem_control_t *mic = (mui_menuitem_control_t*)c;
if (mic->color_icon)
mui_drawable_dispose(mic->color_icon);
} break;
}
break;
@ -839,6 +954,9 @@ mui_cdef_popup(
case MUI_CONTROL_POPUP:
mui_popuptitle_draw(c->win, c, dr);
break;
case MUI_CONTROL_POPUP_MARK:
mui_popupmark_draw(c->win, c, dr);
break;
case MUI_CONTROL_MENUTITLE:
mui_menutitle_draw(c->win, c, dr);
break;
@ -856,6 +974,7 @@ mui_cdef_popup(
case MUI_EVENT_DRAG:
case MUI_EVENT_BUTTONDOWN: {
switch (c->type) {
case MUI_CONTROL_POPUP_MARK:
case MUI_CONTROL_POPUP: {
mui_menu_control_t *pop = (mui_menu_control_t*)c;
return mui_popupmenu_handle_mouse(pop, ev);
@ -865,6 +984,8 @@ mui_cdef_popup(
break;
}
} break;
default:
break;
}
} break;
}
@ -876,11 +997,17 @@ mui_popupmenu_new(
mui_window_t * win,
c2_rect_t frame,
const char * title,
uint32_t uid )
uint32_t uid,
uint32_t flags )
{
return mui_control_new(
win, MUI_CONTROL_POPUP, mui_cdef_popup,
int kind = MUI_CONTROL_POPUP;
if (!title || !strcmp(title, MUI_GLYPH_POPMARK))
kind = MUI_CONTROL_POPUP_MARK;
mui_control_t *c = mui_control_new(
win, kind, mui_cdef_popup,
frame, title, uid, sizeof(mui_menu_control_t));
c->style = flags;
return c;
}
mui_menu_items_t *
@ -889,8 +1016,10 @@ mui_popupmenu_get_items(
{
if (!c)
return NULL;
if (c->type != MUI_CONTROL_POPUP && c->type != MUI_CONTROL_MENUTITLE) {
printf("%s: not a popup or menutitle\n", __func__);
if (c->type != MUI_CONTROL_POPUP &&
c->type != MUI_CONTROL_POPUP_MARK &&
c->type != MUI_CONTROL_MENUTITLE ) {
D(printf("%s: not a popup or menutitle\n", __func__);)
return NULL;
}
mui_menu_control_t *pop = (mui_menu_control_t*)c;
@ -902,17 +1031,29 @@ mui_popupmenu_prepare(
mui_control_t * c)
{
mui_menu_control_t *pop = (mui_menu_control_t*)c;
if (pop->menu_window) {
mui_window_dispose(pop->menu_window);
pop->menu_window = NULL;
if (pop->menu_window.window) {
mui_window_dispose(pop->menu_window.window);
mui_window_deref(&pop->menu_window);
}
c2_rect_t frame = mui_menu_get_enclosing_rect(c->win->ui, pop->menu.e);
pop->menu_frame = frame;
// pop->menu_frame = frame;
c2_rect_offset(&frame, c->frame.l, c->frame.t);
frame.r += 32; // add the popup symbol width
if (c2_rect_width(&frame) < c2_rect_width(&c->frame)) {
c2_rect_offset(&frame, (c2_rect_width(&c->frame) / 2) -
(c2_rect_width(&frame) / 2), 0);
switch (c->type) {
case MUI_CONTROL_POPUP:
frame.r += 32; // add the popup symbol width
break;
case MUI_CONTROL_POPUP_MARK:
// little tweak so the popup appears over the bottom border of button
c2_rect_offset(&frame, 0, -2);
break;
}
if (c->style & MUI_TEXT_ALIGN_CENTER) {
if (c2_rect_width(&frame) < c2_rect_width(&c->frame)) {
c2_rect_offset(&frame, (c2_rect_width(&c->frame) / 2) -
(c2_rect_width(&frame) / 2), 0);
}
} else if (c->style & MUI_TEXT_ALIGN_RIGHT) {
c2_rect_offset(&frame, c2_rect_width(&c->frame) - c2_rect_width(&frame), 0);
}
pop->menu_frame = frame;
c->value = 0;

View File

@ -24,7 +24,6 @@ mui_wdef_menubar_draw(
{
c2_rect_t content = win->frame;
win->content = content;
struct cg_ctx_t * cg = mui_drawable_get_cg(dr);
mui_color_t frameColor = MUI_COLOR(0x000000ff);
@ -41,6 +40,104 @@ mui_wdef_menubar_draw(
extern const mui_control_color_t mui_control_color[MUI_CONTROL_STATE_COUNT];
enum {
MUI_MENUITEM_PART_ICON = 0,
MUI_MENUITEM_PART_TITLE,
MUI_MENUITEM_PART_KCOMBO,
MUI_MENUITEM_PART_COUNT,
};
/* this return the l,t coordinates for parts */
static void
mui_menuitem_get_part_locations(
mui_t * ui,
c2_rect_t * frame,
mui_menu_item_t * item,
c2_rect_t out[MUI_MENUITEM_PART_COUNT])
{
mui_font_t * main = mui_font_find(ui, "main");
const int margin_right = main->size / 3;
const int margin_left = main->size;
stb_ttc_measure m = {};
mui_font_text_measure(main, item->title, &m);
for (int i = 0; i < MUI_MENUITEM_PART_COUNT; i++)
out[i] = C2_RECT_WH(0, 0, 0, 0);
c2_rect_t title = *frame;
title.b = title.t + m.ascent - m.descent;
// center it vertically.
c2_rect_offset(&title, 0,
(c2_rect_height(frame) / 2) - (c2_rect_height(&title) / 2));
/* An icon shifts the title right, a 'mark' doesn't */
if (item->icon[0]) {
mui_font_t * icons = mui_font_find(ui, "icon_small");
mui_font_text_measure(icons, item->icon, &m);
title.l += 6;
c2_pt_t loc = title.tl;
loc.x += (icons->size / 2) - ((m.x1 - m.x0) / 2);
out[MUI_MENUITEM_PART_ICON].tl = loc;
title.l += 6;
} else if (item->mark[0]) {
mui_font_text_measure(main, item->mark, &m);
c2_pt_t loc = title.tl;
loc.x += m.x0 + (main->size / 2) - ((m.x1 - m.x0) / 2);
out[MUI_MENUITEM_PART_ICON].tl = loc;
}
// this is the 'left margin' for the menu item
title.l += margin_left;
if (item->kcombo[0]) {
mui_font_text_measure(main, item->kcombo, &m);
c2_pt_t loc = C2_PT(title.r - m.x1 - m.x0 - margin_right, title.t);
out[MUI_MENUITEM_PART_KCOMBO] = (c2_rect_t){
.l = loc.x, .t = loc.y,
.r = title.r - margin_right,
.b = title.b };
title.r = loc.x;
}
out[MUI_MENUITEM_PART_TITLE] = title;
}
void
mui_menutitle_get_part_locations(
mui_t * ui,
c2_rect_t * frame, // optional!
mui_menu_item_t * item,
c2_rect_t * out)
{
mui_font_t * main = mui_font_find(ui, "main");
const int margin = main->size / 3;
for (int i = 0; i < MUI_MENUTITLE_PART_COUNT; i++)
out[i] = C2_RECT_WH(0, 0, 0, 0);
if (item->color_icon) {
out[MUI_MENUTITLE_PART_ICON] = C2_RECT_WH(0, 0,
item->color_icon[0], item->color_icon[1]);
}
if (item->title) {
stb_ttc_measure m = {};
mui_font_text_measure(main, item->title, &m);
out[MUI_MENUTITLE_PART_TITLE] =
C2_RECT_WH(out[MUI_MENUTITLE_PART_ICON].r,
0, m.x1 - 0, m.ascent - m.descent);
}
out[MUI_MENUTITLE_PART_ALL] = out[MUI_MENUTITLE_PART_ICON];
c2_rect_union(
&out[MUI_MENUTITLE_PART_ALL],
&out[MUI_MENUTITLE_PART_TITLE]);
out[MUI_MENUTITLE_PART_ALL].r += margin;
if (frame) {
// center them all vertically at least
for (int i = 0; i < MUI_MENUTITLE_PART_COUNT; i++) {
c2_rect_offset(&out[i],
frame->l + margin, frame->t +
(c2_rect_height(frame) / 2) - (c2_rect_height(&out[i]) / 2));
}
}
}
void
mui_menutitle_draw(
mui_window_t * win,
@ -50,79 +147,48 @@ mui_menutitle_draw(
c2_rect_t f = c->frame;
c2_rect_offset(&f, win->content.l, win->content.t);
struct cg_ctx_t * cg = mui_drawable_get_cg(dr);
mui_font_t * main = mui_font_find(win->ui, "main");
stb_ttc_measure m = {};
mui_font_text_measure(main, c->title, &m);
int title_width = m.x1 - m.x0;
c2_rect_t title = f;
title.r = title.l + title_width + 1;
title.b = title.t + m.ascent - m.descent;
c2_rect_offset(&title, //-m.x0 +
(int)((c2_rect_width(&f) / 2) - (c2_rect_width(&title)) / 2),
(c2_rect_height(&f) / 2) - (c2_rect_height(&title) / 2));
c2_rect_t loc[MUI_MENUTITLE_PART_COUNT];
mui_menuitem_control_t *mic = (mui_menuitem_control_t*)c;
if (!mic->item.title)
mic->item.title = mic->control.title;
mui_menutitle_get_part_locations(win->ui, &f, &mic->item, loc);
mui_drawable_clip_push(dr, &f);
struct cg_ctx_t * cg = mui_drawable_get_cg(dr);
uint32_t state = mui_control_get_state(c);
if (state) {
cg_set_source_color(cg, &CG_COLOR(mui_control_color[state].fill));
cg_rectangle(cg, f.l, f.t, c2_rect_width(&f), c2_rect_height(&f));
cg_fill(cg);
}
mui_font_text_draw(main, dr,
C2_PT(title.l, title.t), c->title, strlen(c->title),
mui_control_color[state].text);
if (mic->item.color_icon) {
if (!mic->color_icon) {
c2_pt_t size = C2_PT(mic->item.color_icon[0],
mic->item.color_icon[1]);
mic->color_icon = mui_drawable_new(size, 32,
(void*)(mic->item.color_icon + 2), size.x * 4);
}
pixman_image_composite32(PIXMAN_OP_OVER,
mui_drawable_get_pixman(mic->color_icon),
NULL,
mui_drawable_get_pixman(dr),
0, 0, 0, 0,
loc[MUI_MENUTITLE_PART_ICON].l, loc[MUI_MENUTITLE_PART_ICON].t,
c2_rect_width(&loc[MUI_MENUTITLE_PART_ICON]),
c2_rect_height(&loc[MUI_MENUTITLE_PART_ICON]));
}
if (mic->item.title)
mui_font_text_draw(main, dr,
loc[MUI_MENUTITLE_PART_TITLE].tl,
mic->item.title, strlen(mic->item.title),
mui_control_color[state].text);
mui_drawable_clip_pop(dr);
}
static void
mui_menuitem_get_locations(
mui_t * ui,
c2_rect_t * frame,
mui_menu_item_t * item,
c2_pt_t out[3] )
{
mui_font_t * main = mui_font_find(ui, "main");
stb_ttc_measure m = {};
mui_font_text_measure(main, item->title, &m);
c2_rect_t title = *frame;
title.b = title.t + m.ascent - m.descent;
c2_rect_offset(&title, 0,
(c2_rect_height(frame) / 2) - (c2_rect_height(&title) / 2));
if (item->icon[0]) {
title.l += 6;
mui_font_t * icons = mui_font_find(ui, "icon_small");
mui_font_text_measure(icons, item->icon, &m);
c2_pt_t loc = title.tl;
loc.x = loc.x + (icons->size / 2) - ((m.x1 - m.x0) / 2);
out[0] = loc;
title.l += 6;
} else if (item->mark[0]) {
mui_font_text_measure(main, item->mark, &m);
c2_pt_t loc = title.tl;
loc.x = loc.x + (main->size / 2) - ((m.x1 - m.x0) / 2);
out[0] = loc;
}
title.l += main->size;
out[1] = title.tl;
if (item->kcombo[0]) {
mui_font_text_measure(main, item->kcombo, &m);
c2_pt_t loc = C2_PT(title.r - m.x1 - m.x0 - (main->size/3), title.t);
out[2] = loc;
}
}
extern const mui_control_color_t mui_control_color[MUI_CONTROL_STATE_COUNT];
void
mui_menuitem_draw(
mui_window_t * win,
@ -132,14 +198,14 @@ mui_menuitem_draw(
c2_rect_t f = c->frame;
c2_rect_offset(&f, win->content.l, win->content.t);
struct cg_ctx_t * cg = mui_drawable_get_cg(dr);
mui_drawable_clip_push(dr, &f);
struct cg_ctx_t * cg = mui_drawable_get_cg(dr);
mui_font_t * main = TAILQ_FIRST(&win->ui->fonts);
if (c->title && c->title[0] != '-') {
c2_pt_t loc[3];
c2_rect_t loc[MUI_MENUITEM_PART_COUNT];
mui_menuitem_control_t *mic = (mui_menuitem_control_t*)c;
mui_menuitem_get_locations(win->ui, &f, &mic->item, loc);
mui_menuitem_get_part_locations(win->ui, &f, &mic->item, loc);
uint32_t state = mui_control_get_state(c);
if (state && state != MUI_CONTROL_STATE_DISABLED) {
@ -152,20 +218,20 @@ mui_menuitem_draw(
if (mic->item.icon[0]) {
mui_font_t * icons = mui_font_find(win->ui, "icon_small");
mui_font_text_draw(icons, dr,
loc[0], mic->item.icon, 0,
loc[0].tl, mic->item.icon, 0,
mui_control_color[state].text);
} else if (mic->item.mark[0]) {
mui_font_text_draw(main, dr,
loc[0], mic->item.mark, 0,
loc[0].tl, mic->item.mark, 0,
mui_control_color[state].text);
}
mui_font_text_draw(main, dr,
loc[1], mic->item.title, 0,
loc[1].tl, mic->item.title, 0,
mui_control_color[state].text);
if (mic->item.kcombo[0]) {
mui_font_text_draw(main, dr,
loc[2], mic->item.kcombo, 0,
loc[2].tl, mic->item.kcombo, 0,
mui_control_color[state].text);
}
} else {
@ -218,20 +284,63 @@ mui_popuptitle_draw(
if (pop->menu.count) {
mui_menu_item_t item = pop->menu.e[c->value];
c2_pt_t loc[3];
c2_rect_t loc[MUI_MENUITEM_PART_COUNT];
c2_rect_offset(&f, 0, -1);
mui_menuitem_get_locations(win->ui, &f, &item, loc);
mui_menuitem_get_part_locations(win->ui, &f, &item, loc);
if (item.icon[0])
mui_font_text_draw(icons, dr,
loc[0], item.icon, 0,
loc[0].tl, item.icon, 0,
mui_control_color[state].text);
mui_font_text_draw(main, dr,
loc[1], item.title, 0,
loc[1].tl, item.title, 0,
mui_control_color[state].text);
}
// up/down arrow
mui_font_text_draw(icons, dr,
C2_PT(inner.r - 32 + 8, inner.t + 2), "", 0,
C2_PT(inner.r - 32 + 8, inner.t + 2), MUI_ICON_POPUP_ARROWS, 0,
mui_control_color[state].text);
mui_drawable_clip_pop(dr);
}
void
mui_popupmark_draw(
mui_window_t * win,
mui_control_t * c,
mui_drawable_t *dr )
{
mui_menu_control_t *pop = (mui_menu_control_t*)c;
c2_rect_t f = c->frame;
if (c2_rect_width(&pop->menu_frame) &&
c2_rect_width(&pop->menu_frame) < c2_rect_width(&f)) {
f = pop->menu_frame;
f.b = c->frame.b;
}
c2_rect_offset(&f, win->content.l, win->content.t);
mui_font_t * main = mui_font_find(win->ui, "main");
uint32_t state = mui_control_get_state(c);
mui_drawable_clip_push(dr, &f);
struct cg_ctx_t * cg = mui_drawable_get_cg(dr);
c2_rect_t inner = f;
c2_rect_inset(&inner, 1, 1);
cg_set_line_width(cg, 2);
cg_round_rectangle(cg, inner.l, inner.t,
c2_rect_width(&inner), c2_rect_height(&inner), 4, 4);
cg_set_source_color(cg, &CG_COLOR(mui_control_color[state].fill));
cg_fill_preserve(cg);
cg_set_source_color(cg, &CG_COLOR(mui_control_color[state].frame));
cg_stroke(cg);
#if 0
cg_move_to(cg, inner.r - 32, inner.t + 2);
cg_line_to(cg, inner.r - 32, inner.b - 2);
mui_color_t decoColor = MUI_COLOR(0x666666ff);
cg_set_source_color(cg, &CG_COLOR(decoColor));
cg_stroke(cg);
#endif
mui_font_text_draw(main, dr,
C2_PT(inner.r - 32 + 8, inner.t + 2), c->title, 0,
mui_control_color[state].text);
mui_drawable_clip_pop(dr);
}

View File

@ -28,13 +28,24 @@ bool
mui_window_handle_keyboard(
mui_window_t *win,
mui_event_t *event);
void
mui_window_lock(
mui_window_t *win);
mui_window_t *
mui_window_unlock(
mui_window_t *win);
void
mui_control_draw(
mui_window_t * win,
mui_control_t * c,
mui_drawable_t *dr );
void
mui_control_lock(
mui_control_t *c);
mui_control_t *
mui_control_unlock(
mui_control_t *c);
bool
mui_control_event(
@ -49,6 +60,7 @@ mui_control_event(
*/
typedef struct mui_menuitem_control_t {
mui_control_t control;
mui_drawable_t * color_icon; // if one had been provided
mui_menu_item_t item;
} mui_menuitem_control_t;
@ -57,10 +69,17 @@ typedef struct mui_menu_control_t {
mui_menuitem_control_t item;
mui_menu_items_t menu;
c2_rect_t menu_frame;
mui_window_t * menubar;
mui_window_t * menu_window; // when open
mui_window_ref_t menubar;
mui_window_ref_t menu_window; // when open
} mui_menu_control_t;
// used by mui_wdef_menubar for frame and drawing.
enum mui_mbar_style_e {
MUI_MENU_STYLE_MBAR = 0,
MUI_MENU_STYLE_MENU,
MUI_MENU_STYLE_POPUP,
};
void
mui_wdef_menubar_draw(
struct mui_window_t * win,
@ -80,8 +99,34 @@ mui_menutitle_draw(
mui_window_t * win,
mui_control_t * c,
mui_drawable_t *dr );
void
mui_popupmark_draw(
mui_window_t * win,
mui_control_t * c,
mui_drawable_t *dr );
enum {
MUI_MENUTITLE_PART_ALL = 0,
MUI_MENUTITLE_PART_ICON,
MUI_MENUTITLE_PART_TITLE,
MUI_MENUTITLE_PART_COUNT,
};
void
mui_menutitle_get_part_locations(
mui_t * ui,
c2_rect_t * frame, // optional!
mui_menu_item_t * item,
c2_rect_t * out);
// return true if window win is the menubar
bool
mui_menubar_window(
mui_window_t * win);
void
mui_refqueue_init(
mui_refqueue_t *queue);
uint
mui_refqueue_dispose(
mui_refqueue_t *queue);

View File

@ -33,7 +33,7 @@ IMPLEMENT_C_ARRAY(string_array);
typedef struct mui_stdfile_t {
mui_window_t win;
mui_control_t * ok, *cancel, *home, *root;
mui_control_t * listbox, *popup;
mui_control_t * listbox, *popup, *recent;
char * pref_file; // pathname we put last path used
char * re_pattern;
struct {
@ -43,6 +43,10 @@ typedef struct mui_stdfile_t {
char * current_path;
char * selected_path;
string_array_t pop_path;
struct {
mui_control_t *save_name;
mui_control_t *create_folder;
} save;
#ifdef MUI_HAS_REGEXP
regex_t re;
#endif
@ -56,6 +60,9 @@ enum {
MUI_STD_FILE_PART_ROOT,
MUI_STD_FILE_PART_LISTBOX,
MUI_STD_FILE_PART_POPUP,
MUI_STD_FILE_PART_RECENT,
MUI_STD_FILE_PART_NEW,
MUI_STD_FILE_PART_SAVE_NAME,
MUI_STD_FILE_PART_COUNT,
};
@ -93,6 +100,27 @@ mui_hash_nocase(
hash += hash << 5;
return hash;
}
static int
_mii_stdfile_check_dir(
const char * path)
{
const char *home = getenv("HOME");
if (home && path[0] == '~' && path[1] == '/') {
char * p = NULL;
asprintf(&p, "%s%s", home, path + 1);
path = p;
} else
path = strdup(path);
struct stat st;
int res = -1;
if (stat(path, &st) == 0) {
if (S_ISDIR(st.st_mode))
res = 0;
}
free((void*)path);
return res;
}
static int
_mui_stdfile_populate(
mui_stdfile_t * std,
@ -100,7 +128,14 @@ _mui_stdfile_populate(
{
if (std->current_path && !strcmp(std->current_path, path))
return 0;
const char *home = getenv("HOME");
int dupped = 0;
if (home && path[0] == '~' && path[1] == '/') {
char * p = NULL;
asprintf(&p, "%s%s", home, path + 1);
path = p;
dupped = 1;
}
printf("%s %s\n", __func__, path);
errno = 0;
DIR * dir = opendir(path);
@ -117,18 +152,19 @@ _mui_stdfile_populate(
}
if (std->current_path)
free(std->current_path);
std->current_path = strdup(path);
std->current_path = dupped ? (char*)path : strdup(path);
path = NULL; // this COULD be in the list we are now deleting!
for (int i = 0; i < (int)std->pop_path.count; i++)
for (uint i = 0; i < std->pop_path.count; i++)
free(std->pop_path.e[i]);
std->pop_path.count = 0;
mui_control_t *pop = std->popup;
mui_menu_items_t * items = mui_popupmenu_get_items(pop);
for (uint i = 0; i < items->count; i++)
free(items->e[i].title);
mui_menu_items_clear(items);
char * p = strdup(std->current_path);
char * d;
const char *home = getenv("HOME");
int item_id = 1000;
while ((d = basename(p)) != NULL) {
mui_menu_item_t i = {
@ -155,6 +191,8 @@ _mui_stdfile_populate(
mui_control_t * lb = std->listbox;
mui_listbox_elems_t * elems = mui_listbox_get_elems(lb);
for (uint i = 0; i < elems->count; i++)
free(elems->e[i].elem); // free all the strings
mui_listbox_elems_clear(elems);
struct dirent * ent;
while ((ent = readdir(dir))) {
@ -180,9 +218,9 @@ _mui_stdfile_populate(
// we enable all the files by default.
if (e.disabled && !std->re_pattern)
e.disabled = std->suffix[0].s[0] ? 1 : 0;
char *suf = strrchr(ent->d_name, '.');
// handle the case we have a list of dot suffixes to filter
if (e.disabled) {
char *suf = strrchr(ent->d_name, '.');
if (std->suffix[0].s[0] && suf) {
suf++;
uint32_t hash = mui_hash_nocase(suf);
@ -199,8 +237,19 @@ _mui_stdfile_populate(
e.elem = strdup(ent->d_name);
if (S_ISDIR(st.st_mode))
strcpy(e.icon, MUI_ICON_FOLDER);
else
else {
strcpy(e.icon, MUI_ICON_FILE);
if (suf) {
if (!strcasecmp(suf, ".woz") || !strcasecmp(suf, ".nib") ||
!strcasecmp(suf, ".do"))
strcpy(e.icon, MUI_ICON_FLOPPY5);
else if ((!strcasecmp(suf, ".dsk") ||
!strcasecmp(suf, ".po"))) {
if (st.st_size == 143360)
strcpy(e.icon, MUI_ICON_FLOPPY5);
}
}
}
mui_listbox_elems_push(elems, e);
}
qsort(elems->e, elems->count,
@ -211,6 +260,82 @@ _mui_stdfile_populate(
return 0;
}
static int
_mui_stdfile_load_pref(
mui_stdfile_t * std)
{
int res = 1;
FILE * f = fopen(std->pref_file, "r");
char * path = NULL;
size_t len = 0;
if (!f)
return res; // populate
mui_control_t *pop = std->recent;
mui_menu_items_t * items = mui_popupmenu_get_items(pop);
for (uint i = 0; i < items->count; i++)
free(items->e[i].title);
mui_menu_items_clear(items);
int line_count = 0;
const char *home = getenv("HOME");
do {
errno = 0;
if (getline(&path, &len, f) == -1)
break;
char *nl = strrchr(path, '\n');
if (nl)
*nl = 0;
if (line_count == 0) {
if (_mui_stdfile_populate(std, path) == 0) {
printf("%s last path[%s]: %s\n", __func__,
std->re_pattern, path);
res = 0;
}
}
if (home && !strncmp(home, path, strlen(home)) &&
path[strlen(home)] == '/') {
path[0] = '~';
memmove(path + 1, path + strlen(home),
strlen(path) - strlen(home) + 1);
}
int add = 1;
for (uint ii = 0; ii < items->count; ii++) {
if (!strcmp(items->e[ii].title, path)) {
add = 0;
break;
}
}
if (add)
add = !_mii_stdfile_check_dir(path);
if (add && items->count > 10) // limit to 10 recent paths
add = 0;
if (add) {
int item_id = 10000 + line_count;
mui_menu_item_t i = {
.title = strdup(path),
.uid = item_id,
};
char *d = path;
if (!strcmp(d, "/"))
strcpy(i.icon, MUI_ICON_ROOT);
else if (home && !strcmp(d, home))
strcpy(i.icon, MUI_ICON_HOME);
else
strcpy(i.icon, MUI_ICON_FOLDER);
mui_menu_items_push(items, i);
}
line_count++;
} while (!feof(f) && line_count < 6);
mui_menu_item_t z = {};
mui_menu_items_push(items, z);
mui_popupmenu_prepare(pop);
fclose(f);
if (path)
free(path);
return res;
}
static int
_mui_stdfile_window_action(
mui_window_t * win,
@ -223,7 +348,7 @@ _mui_stdfile_window_action(
switch (what) {
case MUI_WINDOW_ACTION_CLOSE: {
// dispose of anything we had allocated
printf("%s close\n", __func__);
// printf("%s close\n", __func__);
if (std->pref_file)
free(std->pref_file);
if (std->re_pattern)
@ -232,9 +357,21 @@ _mui_stdfile_window_action(
free(std->current_path);
if (std->selected_path)
free(std->selected_path);
#ifdef MUI_HAS_REGEXP
regfree(&std->re);
for (int i = 0; i < (int)std->pop_path.count; i++)
#endif
mui_control_t *pop = std->popup;
mui_menu_items_t * items = mui_popupmenu_get_items(pop);
for (uint i = 0; i < items->count; i++)
free(items->e[i].title);
for (uint i = 0; i < std->pop_path.count; i++)
free(std->pop_path.e[i]);
// free all the strings for all teh elems, its our responsibility
mui_listbox_elems_t * elems = mui_listbox_get_elems(std->listbox);
for (uint i = 0; i < elems->count; i++)
free(elems->e[i].elem); // free all the strings
string_array_free(&std->pop_path);
std->pop_path.count = 0;
} break;
}
@ -263,6 +400,17 @@ _mui_stdfile_control_action(
FILE * f = fopen(std->pref_file, "w");
if (f) {
fprintf(f, "%s\n", std->current_path);
// write recent paths popup back in
const char *home = getenv("HOME");
mui_menu_items_t * items = mui_popupmenu_get_items(std->recent);
for (uint i = 0; i < items->count; i++) {
if (items->e[i].title &&
strcmp(items->e[i].title, std->current_path) &&
strcmp(items->e[i].title, home) &&
strcmp(items->e[i].title, "/")) {
fprintf(f, "%s\n", items->e[i].title);
}
}
fclose(f);
}
}
@ -313,19 +461,32 @@ _mui_stdfile_control_action(
_mui_stdfile_populate(std, std->pop_path.e[idx]);
}
break;
case MUI_STD_FILE_PART_RECENT:
// printf("%s POPUP\n", __func__);
if (what == MUI_CONTROL_ACTION_VALUE_CHANGED) {
int idx = mui_control_get_value(c);
mui_menu_item_t * items = mui_popupmenu_get_items(c)->e;
printf("Recent Selected: %s\n", items[idx].title);
_mui_stdfile_populate(std, items[idx].title);
}
break;
}
return 0;
}
mui_window_t *
mui_stdfile_get(
mui_stdfile_make_window(
struct mui_t * ui,
c2_pt_t where,
const char * prompt,
const char * pattern,
const char * start_path,
const char * save_filename,
uint16_t flags )
{
float base_size = mui_font_find(ui, "main")->size;
float margin = base_size * 0.7;
c2_rect_t wpos = C2_RECT_WH(where.x, where.y, 700, 400);
if (where.x == 0 && where.y == 0)
c2_rect_offset(&wpos,
@ -372,19 +533,22 @@ mui_stdfile_get(
std->suffix[di].hash = hash;
di++;
}
free(dup);
}
bool save_box = false;
mui_control_t * c = NULL;
c2_rect_t cf;
cf = C2_RECT_WH(0, 0, 120, 40);
c2_rect_left_of(&cf, c2_rect_width(&w->content), 20);
c2_rect_top_of(&cf, c2_rect_height(&w->content), 20);
c2_rect_left_of(&cf, c2_rect_width(&w->content), margin);
c2_rect_top_of(&cf, c2_rect_height(&w->content), margin);
std->cancel = c = mui_button_new(w,
cf, MUI_BUTTON_STYLE_NORMAL,
"Cancel", MUI_STD_FILE_PART_CANCEL);
c2_rect_top_of(&cf, cf.t, 20);
c2_rect_top_of(&cf, cf.t, margin);
std->ok = c = mui_button_new(w,
cf, MUI_BUTTON_STYLE_DEFAULT,
"Select", MUI_STD_FILE_PART_OK);
save_box ? "Save" : "Select", MUI_STD_FILE_PART_OK);
std->ok->key_equ = MUI_KEY_EQU(0, 13); // return
std->cancel->key_equ = MUI_KEY_EQU(0, 27); // ESC
@ -394,57 +558,67 @@ mui_stdfile_get(
c2_rect_top_of(&t, cf.t, 25);
c = mui_separator_new(w, t);
int button_spacer = save_box ? margin * 0.7 : margin;
int listbox_height = save_box ? 250 : 300;
c2_rect_top_of(&cf, cf.t, 40);
std->home = c = mui_button_new(w,
cf, MUI_BUTTON_STYLE_NORMAL,
"Home", MUI_STD_FILE_PART_HOME);
c->key_equ = MUI_KEY_EQU(MUI_MODIFIER_ALT, 'h');
c2_rect_top_of(&cf, cf.t, 20);
c2_rect_top_of(&cf, cf.t, button_spacer);
std->root = c = mui_button_new(w,
cf, MUI_BUTTON_STYLE_NORMAL,
"Root", MUI_STD_FILE_PART_ROOT);
c->key_equ = MUI_KEY_EQU(MUI_MODIFIER_ALT, '/');
cf = C2_RECT_WH(15, 45, 700-185, 300);
if (save_box) {
c2_rect_top_of(&cf, cf.t, button_spacer);
std->save.create_folder = c = mui_button_new(w,
cf, MUI_BUTTON_STYLE_NORMAL,
"New…", MUI_STD_FILE_PART_ROOT);
c->key_equ = MUI_KEY_EQU(MUI_MODIFIER_ALT, 'n');
c->uid = MUI_STD_FILE_PART_NEW;
}
cf = C2_RECT_WH(margin, 0, c2_rect_width(&wpos)-185, 35);
c2_rect_top_of(&cf, c2_rect_height(&w->content), margin);
if (save_box) {
std->save.save_name = c = mui_textedit_control_new(w,
cf, MUI_CONTROL_TEXTBOX_FRAME);
c->uid = MUI_STD_FILE_PART_SAVE_NAME;
mui_textedit_set_text(c,
"Fulling Mill Online Return Center.pdf");
mui_textedit_set_selection(c, 0, 255);
}
cf = C2_RECT_WH(margin, 45, c2_rect_width(&wpos)-185, listbox_height);
std->listbox = c = mui_listbox_new(w, cf,
MUI_STD_FILE_PART_LISTBOX);
cf = C2_RECT_WH(15, 0, 700-185, 34);
cf = C2_RECT_WH(margin, 0, c2_rect_width(&wpos)-185, 34);
c2_rect_top_of(&cf, std->listbox->frame.t, 6);
std->popup = c = mui_popupmenu_new(w, cf,
"Popup", MUI_STD_FILE_PART_POPUP);
"Popup", MUI_STD_FILE_PART_POPUP,
MUI_TEXT_ALIGN_CENTER);
cf.r = c2_rect_width(&w->content) - margin;
cf.l = cf.r - 34;
std->recent = c = mui_popupmenu_new(w, cf,
MUI_GLYPH_POPMARK, MUI_STD_FILE_PART_RECENT,
MUI_TEXT_ALIGN_RIGHT);
// mui_control_set_state(c, MUI_CONTROL_STATE_DISABLED);
// printf("Popup: %p\n", c);
c = NULL;
TAILQ_FOREACH(c, &w->controls, self) {
if (mui_control_get_uid(c) == 0)
continue;
mui_control_set_action(c, _mui_stdfile_control_action, std);
if (mui_control_get_uid(c))
mui_control_set_action(c, _mui_stdfile_control_action, std);
}
int dopop = 1; // populate to start_path by default
if (!(flags & MUI_STDF_FLAG_NOPREF) && ui->pref_directory) {
uint32_t hash = std->re_pattern ? mui_hash(std->re_pattern) : 0;
asprintf(&std->pref_file, "%s/std_path_%04x", ui->pref_directory, hash);
printf("%s pref file: %s\n", __func__, std->pref_file);
/* read last used pathname */
FILE * f = fopen(std->pref_file, "r");
if (f) {
char * path = NULL;
size_t len = 0;
getline(&path, &len, f);
fclose(f);
if (path) {
char *nl = strrchr(path, '\n');
if (nl)
*nl = 0;
if (_mui_stdfile_populate(std, path) == 0) {
printf("%s last path[%s]: %s\n", __func__,
std->re_pattern, path);
dopop = 0;
}
free(path);
}
}
dopop = _mui_stdfile_load_pref(std);
}
if (dopop)
_mui_stdfile_populate(std, start_path);
@ -452,6 +626,20 @@ mui_stdfile_get(
return w;
}
mui_window_t *
mui_stdfile_get(
struct mui_t * ui,
c2_pt_t where,
const char * prompt,
const char * pattern,
const char * start_path,
uint16_t flags )
{
mui_window_t *w = mui_stdfile_make_window(ui, where,
prompt, pattern, start_path, NULL, flags);
return w;
}
char *
mui_stdfile_get_path(
mui_window_t * w )

View File

@ -13,7 +13,7 @@ mui_time_t
mui_get_time()
{
struct timespec tim;
clock_gettime(CLOCK_MONOTONIC_RAW, &tim);
clock_gettime(CLOCK_MONOTONIC, &tim);
uint64_t time = ((uint64_t)tim.tv_sec) * (1000000 / MUI_TIME_RES) +
tim.tv_nsec / (1000 * MUI_TIME_RES);
return time;

View File

@ -103,6 +103,41 @@ mui_titled_window_draw(
cg_fill(cg);
}
static bool
mui_wdef_titlewindow(
struct mui_window_t * win,
uint8_t what,
void * param)
{
switch (what) {
case MUI_WDEF_DRAW:
mui_titled_window_draw(win->ui, win, param);
break;
case MUI_WDEF_SELECT:
// mui_window_inval(win, NULL);
if (win->control_focus.control) {
int activate = 1;
if (win->control_focus.control->cdef)
win->control_focus.control->cdef(
win->control_focus.control,
MUI_CDEF_FOCUS, &activate);
}
break;
case MUI_WDEF_DESELECT:
if (win->control_focus.control) {
int activate = 0;
if (win->control_focus.control->cdef)
win->control_focus.control->cdef(
win->control_focus.control,
MUI_CDEF_FOCUS, &activate);
}
break;
case MUI_WDEF_DISPOSE:
break;
}
return false;
}
mui_window_t *
mui_window_create(
struct mui_t *ui,
@ -118,10 +153,10 @@ mui_window_create(
w->ui = ui;
w->frame = frame;
w->title = title ? strdup(title) : NULL;
w->wdef = wdef;
w->wdef = wdef ? wdef : mui_wdef_titlewindow;
w->flags.layer = layer;
mui_refqueue_init(&w->refs);
TAILQ_INIT(&w->controls);
TAILQ_INIT(&w->zombies);
STAILQ_INIT(&w->actions);
pixman_region32_init(&w->inval);
TAILQ_INSERT_HEAD(&ui->windows, w, self);
@ -133,9 +168,6 @@ mui_window_create(
return w;
}
void
_mui_control_free(
mui_control_t * c );
void
_mui_window_free(
mui_window_t *win)
@ -147,45 +179,57 @@ _mui_window_free(
while ((c = TAILQ_FIRST(&win->controls))) {
mui_control_dispose(c);
}
while ((c = TAILQ_FIRST(&win->zombies))) {
TAILQ_REMOVE(&win->zombies, c, self);
_mui_control_free(c);
}
if (win->title)
free(win->title);
free(win);
}
void
mui_window_dispose_actions(
mui_window_t * win)
{
if (!win)
return;
mui_action_t *a;
while ((a = STAILQ_FIRST(&win->actions)) != NULL) {
STAILQ_REMOVE_HEAD(&win->actions, self);
free(a);
}
}
void
mui_window_dispose(
mui_window_t *win)
{
if (!win)
return;
if (win->flags.zombie) {
printf("%s: DOUBLE delete %s\n", __func__, win->title);
if (!win->flags.disposed) {
win->flags.disposed = true;
bool was_front = mui_window_isfront(win);
mui_window_action(win, MUI_WINDOW_ACTION_CLOSE, NULL);
mui_window_inval(win, NULL); // just to mark the UI dirty
if (win->wdef)
win->wdef(win, MUI_WDEF_DISPOSE, NULL);
win->flags.hidden = true;
struct mui_t *ui = win->ui;
TAILQ_REMOVE(&ui->windows, win, self);
mui_window_dispose_actions(win);
if (was_front) {
mui_window_t * front = mui_window_front(ui);
if (front) {
mui_window_inval(front, NULL);
if (front->wdef)
front->wdef(front, MUI_WDEF_SELECT, NULL);
}
}
}
if (mui_refqueue_dispose(&win->refs) != 0) {
// we have some references, we'll have to wait
// printf("%s Warning: window %s still has references\n",
// __func__, win->title);
return;
}
bool was_front = mui_window_isfront(win);
mui_window_action(win, MUI_WINDOW_ACTION_CLOSE, NULL);
mui_window_inval(win, NULL); // just to mark the UI dirty
if (win->wdef)
win->wdef(win, MUI_WDEF_DISPOSE, NULL);
struct mui_t *ui = win->ui;
TAILQ_REMOVE(&ui->windows, win, self);
if (ui->event_capture == win)
ui->event_capture = NULL;
if (ui->action_active) {
// printf("%s %s is now zombie\n", __func__, win->title);
win->flags.zombie = true;
TAILQ_INSERT_TAIL(&ui->zombies, win, self);
} else
_mui_window_free(win);
if (was_front) {
mui_window_t * front = mui_window_front(ui);
if (front)
mui_window_inval(front, NULL);
}
_mui_window_free(win);
}
void
@ -193,11 +237,13 @@ mui_window_draw(
mui_window_t *win,
mui_drawable_t *dr)
{
if (!win)
return;
if (win->flags.hidden)
return;
mui_drawable_clip_push(dr, &win->frame);
if (win->wdef)
win->wdef(win, MUI_WDEF_DRAW, dr);
else
mui_titled_window_draw(win->ui, win, dr);
struct cg_ctx_t * cg = mui_drawable_get_cg(dr);
cg_save(cg);
// cg_translate(cg, content.l, content.t);
@ -210,11 +256,17 @@ mui_window_draw(
mui_drawable_clip_pop(dr);
}
/*
* Keys are passed first to the control that is in focus (if any), then
* to all the others in sequence until someone handles it (or not).
*/
bool
mui_window_handle_keyboard(
mui_window_t *win,
mui_event_t *event)
{
if (win->flags.hidden)
return false;
if (!mui_window_isfront(win))
return false;
if (win->wdef && win->wdef(win, MUI_WDEF_EVENT, event)) {
@ -222,12 +274,23 @@ mui_window_handle_keyboard(
return true;
}
// printf("%s %s checkint controls\n", __func__, win->title);
mui_control_t * c, *safe;
TAILQ_FOREACH_SAFE(c, &win->controls, self, safe) {
/*
* Start with the control in focus, if there's any
*/
mui_control_t * first = win->control_focus.control ?
win->control_focus.control :
TAILQ_FIRST(&win->controls);
mui_control_t * c = first;
while (c) {
if (mui_control_event(c, event)) {
// printf("%s control %s handled it\n", __func__, c->title);
// printf("%s control %s handled it\n", __func__, c->title);
return true;
}
c = TAILQ_NEXT(c, self);
if (!c)
c = TAILQ_FIRST(&win->controls);
if (c == first)
break;
}
return false;
}
@ -237,6 +300,8 @@ mui_window_handle_mouse(
mui_window_t *win,
mui_event_t *event)
{
if (win->flags.hidden)
return false;
if (win->wdef && win->wdef(win, MUI_WDEF_EVENT, event))
return true;
switch (event->type) {
@ -261,7 +326,8 @@ mui_window_handle_mouse(
c = NULL;
if (!c) {
/* find where we clicked in the window */
win->ui->event_capture = win;
mui_window_ref(&win->ui->event_capture, win,
FCC('E', 'V', 'C', 'P'));
win->click_loc = event->mouse.where;
c2_pt_offset(&win->click_loc, -win->frame.l, -win->frame.t);
win->flags.hit_part = MUI_WINDOW_PART_FRAME;
@ -274,7 +340,8 @@ mui_window_handle_mouse(
if (c) {
if (c->cdef && c->cdef(c, MUI_CDEF_EVENT, event)) {
// c->state = MUI_CONTROL_STATE_CLICKED;
win->control_clicked = c;
mui_control_ref(&win->control_clicked, c,
FCC('E', 'V', 'C', 'C'));
}
}
return true;
@ -302,22 +369,22 @@ mui_window_handle_mouse(
// mui_window_inval(win, NULL);
return true;
}
if (win->control_clicked) {
mui_control_t * c = win->control_clicked;
if (win->control_clicked.control) {
mui_control_t * c = win->control_clicked.control;
if (c->cdef && c->cdef(c, MUI_CDEF_EVENT, event)) {
return true;
} else
win->control_clicked = NULL;
mui_control_deref(&win->control_clicked);
}
return win->flags.hit_part != MUI_WINDOW_PART_NONE;
break;
case MUI_EVENT_BUTTONUP: {
int part = win->flags.hit_part;
win->flags.hit_part = MUI_WINDOW_PART_NONE;
win->ui->event_capture = NULL;
if (win->control_clicked) {
mui_control_t * c = win->control_clicked;
win->control_clicked = NULL;
mui_window_deref(&win->ui->event_capture);
if (win->control_clicked.control) {
mui_control_t * c = win->control_clicked.control;
mui_control_deref(&win->control_clicked);
if (c->cdef && c->cdef(c, MUI_CDEF_EVENT, event))
return true;
}
@ -326,6 +393,8 @@ mui_window_handle_mouse(
case MUI_EVENT_MOUSEENTER:
case MUI_EVENT_MOUSELEAVE:
break;
default:
break;
}
// printf("MOUSE %s button %d\n", __func__, event->mouse.button);
// printf("MOUSE %s %s\n", __func__, c->title);
@ -337,7 +406,7 @@ mui_window_inval(
mui_window_t *win,
c2_rect_t * r)
{
if (!win)
if (!win || win->flags.hidden)
return;
c2_rect_t frame = win->frame;
c2_rect_t forward = {};
@ -425,9 +494,14 @@ mui_window_select(
last = w;
}
TAILQ_INSERT_TAIL(&win->ui->windows, win, self);
if (win->wdef)
win->wdef(win, MUI_WDEF_SELECT, NULL);
done:
if (last) // we are deselecting this one, so redraw it
if (last) {// we are deselecting this one, so redraw it
mui_window_inval(last, NULL);
if (last->wdef)
win->wdef(last, MUI_WDEF_DESELECT, NULL);
}
#if 0
printf("%s %s res:%d stack is now:\n", __func__, win->title, res);
TAILQ_FOREACH(w, &win->ui->windows, self) {
@ -437,6 +511,53 @@ done:
return res;
}
void
mui_window_lock(
mui_window_t *win)
{
if (!win)
return;
if (!win->lock.window) {
mui_window_ref(&win->lock, win, FCC('l', 'o', 'c', 'k'));
win->lock.ref.count = 10; // prevent it from being deleted
} else {
win->lock.ref.count += 10;
}
// printf("%s: window %s locked %2d\n",
// __func__, win->title, win->lock.ref.count);
}
mui_window_t*
mui_window_unlock(
mui_window_t *win)
{
if (!win)
return NULL;
if (win->lock.window) {
if (win->lock.ref.trace)
printf("%s: window %s was locked\n",
__func__, win->title);
if (win->lock.ref.count > 10) {
win->lock.ref.count -= 10;
} else { // window was disposed of
int delete = win->lock.ref.count < 10;
// we are the last one, remove the lock
if (win->lock.ref.trace)
printf("%s: window %s unlocked delete %d\n",
__func__, win->title, delete);
mui_window_deref(&win->lock);
if (delete) {
mui_window_dispose(win);
win = NULL;
}
}
} else {
// if (win->lock.ref.trace)
printf("%s: window %s was not locked\n", __func__, win->title);
}
return win;
}
void
mui_window_action(
mui_window_t * win,
@ -445,14 +566,14 @@ mui_window_action(
{
if (!win)
return;
win->ui->action_active++;
mui_action_t *a;
STAILQ_FOREACH(a, &win->actions, self) {
mui_window_lock(win);
mui_action_t *a, *safe;
STAILQ_FOREACH_SAFE(a, &win->actions, self, safe) {
if (!a->window_cb)
continue;
a->window_cb(win, a->cb_param, what, param);
}
win->ui->action_active--;
mui_window_unlock(win);
}
void

19
libmui/tests/Makefile Normal file
View File

@ -0,0 +1,19 @@
# Makefile
#
# Copyright (C) 2024 Michel Pollet <buserror@gmail.com>
#
# SPDX-License-Identifier: MIT
# Check to see if we are a submodule of the MII emulator
MII_PLUG := ${wildcard ../../ui_gl/*.c}
ifneq ($(MII_PLUG),)
PLUGS += mii_ui
endif
PLUGS += mui_widgets_demo
all :
for plug in $(PLUGS); do \
$(MAKE) -C $$plug; \
done

View File

@ -0,0 +1,39 @@
# Makefile
#
# Copyright (C) 2024 Michel Pollet <buserror@gmail.com>
#
# SPDX-License-Identifier: MIT
TARGET := mii_ui_tests
LIBMUI := ../../
MII := ../../../
all : $(TARGET)
include $(LIBMUI)/Makefile.common
vpath %.c $(MII)/ui_gl
CPPFLAGS += -I$(MII)/ui_gl
.PHONY : $(TARGET)
$(TARGET) : $(LIB)/$(TARGET).so
MII_UI_OBJ := $(OBJ)/mii_mui_slots.o \
$(OBJ)/mii_mui_loadbin.o \
$(OBJ)/mii_mui_1mb.o \
$(OBJ)/mii_mui_2dsk.o \
$(OBJ)/mii_mui_about.o \
$(OBJ)/mii_mui_ssc.o \
$(OBJ)/mii_mui_utils.o
$(LIB)/$(TARGET).so : $(OBJ)/$(TARGET).o $(MII_UI_OBJ)
$(LIB)/$(TARGET).so : LDLIBS += $(LIB)/libmui.a
$(OBJ)/mii_mui_about.o : CPPFLAGS+=-DMII_ICON64_DEFINE
clean:
rm -rf $(LIB)/$(TARGET).so $(MII_UI_OBJ)
-include $(OBJ)/*.d

View File

@ -0,0 +1,4 @@
### MII Emulator test plugin
This is compiled only when libmui is part of the MII build.

View File

@ -13,7 +13,7 @@
#include <unistd.h>
#include "mui.h"
#include "mui_plugin.h"
#include "mui_shell_plugin.h"
#include "c2_geometry.h"
typedef struct cg_ui_t {
@ -24,9 +24,8 @@ typedef struct cg_ui_t {
#include "mii_mui_menus.h"
#include "mii_mui_settings.h"
#ifndef UI_VERSION
#define UI_VERSION "0.0.0"
#ifndef MUI_VERSION
#define MUI_VERSION "0.0.0"
#endif
static void
@ -39,8 +38,8 @@ _test_show_about(
return;
}
w = mui_alert(g->ui, C2_PT(0,0),
"About MII",
"Version " UI_VERSION "\n"
"About MUI",
"Version " MUI_VERSION "\n"
"Build " __DATE__ " " __TIME__,
MUI_ALERT_INFO);
mui_window_set_id(w, FCC('a','b','o','t'));
@ -49,8 +48,6 @@ _test_show_about(
static mii_machine_config_t g_machine_conf = {};
static mii_loadbin_conf_t g_loadbin_conf = {};
/* this is heavily endian dependend, as is the FCC macro */
#define FCC_INDEX(_fcc) (isdigit(_fcc>>24) ? ((_fcc >> 24) - '0') : 0)
int
_test_menubar_action(
@ -105,10 +102,12 @@ _test_menubar_action(
(char*)&item->uid, item->title);
switch (item->uid) {
case FCC('a','b','o','t'): {
_test_show_about(g);
// _test_show_about(g);
mii_mui_about(g->ui);
} break;
case FCC('q','u','i','t'): {
printf("%s Quit\n", __func__);
g->ui->quit_request = 1;
} break;
case FCC('s','l','o','t'): {
mii_mui_configure_slots(g->ui, &g_machine_conf);
@ -158,9 +157,7 @@ _init(
mui_window_t * mbar = mui_menubar_new(ui);
mui_window_set_action(mbar, _test_menubar_action, g);
mui_menubar_add_simple(mbar, MUI_GLYPH_APPLE,
FCC('a','p','p','l'),
m_apple_menu);
mui_menubar_add_menu(mbar, FCC('a','p','p','l'), m_color_apple_menu, 2);
mui_menubar_add_simple(mbar, "File",
FCC('f','i','l','e'),
m_file_menu);
@ -170,12 +167,13 @@ _init(
mui_menubar_add_simple(mbar, "CPU",
FCC('c','p','u','m'),
m_cpu_menu);
// mii_mui_configure_slots(g->ui, &g_machine_conf);
// mii_mui_load_binary(g->ui, &g_loadbin_conf);
mii_mui_load_binary(g->ui, &g_loadbin_conf);
// mii_mui_load_1mbrom(g->ui, &g_machine_conf.slot[0].conf.rom1mb);
// mii_mui_load_2dsk(g->ui, &g_machine_conf.slot[0].conf.disk2, MII_2DSK_DISKII);
mii_mui_about(g->ui);
// mii_mui_about(g->ui);
// mii_mui_configure_ssc(g->ui, &g_machine_conf.slot[0].conf.ssc);
#if 0
mui_alert(ui, C2_PT(0,0),
"Testing one Two",
@ -183,7 +181,7 @@ _init(
"This operation cannot be cancelled.",
MUI_ALERT_WARN);
#endif
#if 1
#if 0
mui_stdfile_get(ui,
C2_PT(0, 0),
"Select image for SmartPort card",
@ -199,6 +197,7 @@ _dispose(
void *_ui)
{
cg_ui_t *ui = _ui;
printf("%s\n", __func__);
mui_dispose(ui->ui);
free(ui);
}
@ -210,7 +209,6 @@ _draw(
mui_drawable_t *dr,
uint16_t all)
{
// cg_ui_t *g = param;
mui_draw(ui, dr, all);
return 1;
}
@ -221,16 +219,14 @@ _event(
void *param,
mui_event_t *event)
{
// cg_ui_t *g = param;
// printf("%s %d\n", __func__, event->type);
mui_handle_event(ui, event);
return 0;
}
mui_plug_t mui_plug = {
.init = _init,
.dispose = _dispose,
.draw = _draw,
.event = _event,
.name = "MII UI Test",
.init = _init,
.dispose = _dispose,
.draw = _draw,
.event = _event,
};

View File

@ -1,33 +0,0 @@
/*
* mui_plugin.h
*
* Copyright (C) 2023 Michel Pollet <buserror@gmail.com>
*
* SPDX-License-Identifier: MIT
*/
#pragma once
#include <stdint.h>
#include <stdbool.h>
struct mui_t;
struct mui_drawable_t;
typedef struct mui_plug_t {
void * (*init)(
struct mui_t * ui,
struct mui_plug_t * plug,
struct mui_drawable_t * dr );
void (*dispose)(
void * plug );
int (*draw)(
struct mui_t *ui,
void *param,
struct mui_drawable_t * dr,
uint16_t all );
int (*event)(
struct mui_t *ui,
void *param,
struct mui_event_t * event );
} mui_plug_t;

View File

@ -0,0 +1,24 @@
# Makefile
#
# Copyright (C) 2024 Michel Pollet <buserror@gmail.com>
#
# SPDX-License-Identifier: MIT
TARGET := mui_widgets_demo
LIBMUI := ../../
all : $(TARGET)
include $(LIBMUI)/Makefile.common
.PHONY : $(TARGET)
$(TARGET) : $(LIB)/$(TARGET).so
$(LIB)/$(TARGET).so : $(OBJ)/$(TARGET).o
$(LIB)/$(TARGET).so : LDLIBS += $(LIB)/libmui.a
clean:
rm -rf $(LIB)/$(TARGET).so
-include $(OBJ)/*.d

View File

@ -0,0 +1,110 @@
// Autogenerated with:
// libmui/utils/png2raw.c -n mui_color_apple -o ui_gl/mui_color_apple.h docs/Apple_logo_rainbow_version2_28x28.png
// Image with a W:28px, H:28px and 4 channels
// Converted to ARGB8888 and premultiplied alpha
#pragma once
#define MUI_COLOR_APPLE_SIZE 786
extern const uint32_t mui_color_apple[MUI_COLOR_APPLE_SIZE];
#ifdef MUI_COLOR_APPLE_DEFINE
const uint32_t mui_color_apple[MUI_COLOR_APPLE_SIZE] = {
28, 28, // width, height
0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,
0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,
0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,
0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,
0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,
0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,
0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,
0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,
0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,
0x481b3413,0x99396f29,0x270e1c0a,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,
0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,
0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,
0x00000000,0x00000000,0x13070d05,0xb3438230,0xff60ba45,0xff60ba45,0x240d1a09,0x00000000,
0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,
0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,
0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x02000100,0xbe478a33,0xff60ba45,
0xff60ba45,0xe155a43d,0x03010200,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,
0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,
0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,
0x00000000,0x5d234319,0xff60ba45,0xff60ba45,0xff60ba45,0x6425491b,0x00000000,0x00000000,
0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,
0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,
0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0xac417d2f,0xff60ba45,0xff60ba45,
0x9d3b722a,0x02010100,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,
0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,
0x00000000,0x00000000,0x00000000,0x01000000,0x260d1b09,0x3614270e,0x260d1b09,0x02000100,
0x00000000,0xbe478a33,0xbe478a33,0x56203e17,0x03010200,0x270e1c09,0x38162911,0x280e1c09,
0x02000100,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,
0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x57203f17,0xda529f3b,
0xff60ba45,0xff60ba45,0xff60ba45,0xe054a33d,0x8d356626,0x4d1d3815,0x4b1a3612,0x90366927,
0xe556a73e,0xff60ba45,0xff60ba45,0xff60ba45,0xdf54a23c,0x6225471a,0x00000000,0x00000000,
0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,
0x00000000,0x86326124,0xff60ba45,0xff60ba45,0xff60ba45,0xff60ba45,0xff60ba45,0xff60ba45,
0xff60ba45,0xff60ba45,0xff60ba45,0xff60ba45,0xff60ba45,0xff60ba45,0xff60ba45,0xff60ba45,
0xff60ba45,0xff60ba45,0x99396f29,0x01000000,0x00000000,0x00000000,0x00000000,0x00000000,
0x00000000,0x00000000,0x00000000,0x00000000,0x6c524e16,0xffc3b834,0xffc3b834,0xffc3b834,
0xffc3b834,0xffc3b834,0xffc3b834,0xffc3b834,0xffc3b834,0xffc3b834,0xffc3b834,0xffc3b834,
0xffc3b834,0xffc3b834,0xffc3b834,0xffc3b834,0xffc3b834,0xffc3b834,0x933b6d2d,0x08030502,
0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x100f0b03,
0xf0edac24,0xfffcb726,0xfffcb726,0xfffcb726,0xfffcb726,0xfffcb726,0xfffcb726,0xfffcb726,
0xfffcb726,0xfffcb726,0xfffcb726,0xfffcb726,0xfffcb726,0xfffcb726,0xfffcb726,0xfffcb726,
0xfffcb726,0x8d8b6515,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,
0x00000000,0x00000000,0x00000000,0x59573f0d,0xfffcb726,0xfffcb726,0xfffcb726,0xfffcb726,
0xfffcb726,0xfffcb726,0xfffcb726,0xfffcb726,0xfffcb726,0xfffcb726,0xfffcb726,0xfffcb726,
0xfffcb726,0xfffcb726,0xfffcb726,0xfffcb726,0xe8e5a623,0x0b0a0803,0x00000000,0x00000000,
0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x89855713,
0xfff9a323,0xfff9a323,0xfff9a323,0xfff9a323,0xfff9a323,0xfff9a323,0xfff9a323,0xfff9a323,
0xfff9a323,0xfff9a323,0xfff9a323,0xfff9a323,0xfff9a323,0xfff9a323,0xfff9a323,0xfff9a323,
0xa09c6616,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,
0x00000000,0x00000000,0x00000000,0xa79f5214,0xfff4811e,0xfff4811e,0xfff4811e,0xfff4811e,
0xfff4811e,0xfff4811e,0xfff4811e,0xfff4811e,0xfff4811e,0xfff4811e,0xfff4811e,0xfff4811e,
0xfff4811e,0xfff4811e,0xfff4811e,0xfff4811e,0x76703b0e,0x00000000,0x00000000,0x00000000,
0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0xa49c5313,
0xfff4811e,0xfff4811e,0xfff4811e,0xfff4811e,0xfff4811e,0xfff4811e,0xfff4811e,0xfff4811e,
0xfff4811e,0xfff4811e,0xfff4811e,0xfff4811e,0xfff4811e,0xfff4811e,0xfff4811e,0xfff4811e,
0x9d964f13,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,
0x00000000,0x00000000,0x00000000,0x7f773b12,0xfff07724,0xfff07724,0xfff07724,0xfff07724,
0xfff07724,0xfff07724,0xfff07724,0xfff07724,0xfff07724,0xfff07724,0xfff07724,0xfff07724,
0xfff07724,0xfff07724,0xfff07724,0xfff07724,0xe3d56a20,0x05040101,0x00000000,0x00000000,
0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x4e441112,
0xffdf393d,0xffdf393d,0xffdf393d,0xffdf393d,0xffdf393d,0xffdf393d,0xffdf393d,0xffdf393d,
0xffdf393d,0xffdf393d,0xffdf393d,0xffdf393d,0xffdf393d,0xffdf393d,0xffdf393d,0xffdf393d,
0xffdf393d,0x79691b1d,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,
0x00000000,0x00000000,0x00000000,0x13100404,0xf8d9383c,0xffdf393d,0xffdf393d,0xffdf393d,
0xffdf393d,0xffdf393d,0xffdf393d,0xffdf393d,0xffdf393d,0xffdf393d,0xffdf393d,0xffdf393d,
0xffdf393d,0xffdf393d,0xffdf393d,0xffdf393d,0xffdf393d,0xfddd393d,0x74651a1c,0x01000000,
0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,
0xb49e3335,0xffe1484b,0xffe1484b,0xffe1484b,0xffe1484b,0xffe1484b,0xffe1484b,0xffe1484b,
0xffe1484b,0xffe1484b,0xffe1484b,0xffe1484b,0xffe1484b,0xffe1484b,0xffe1484b,0xffe1484b,
0xffe1484b,0xffe1484b,0xffe1484b,0xab983d3f,0x00000000,0x00000000,0x00000000,0x00000000,
0x00000000,0x00000000,0x00000000,0x00000000,0x3e240e24,0xff953c96,0xff953c96,0xff953c96,
0xff953c96,0xff953c96,0xff953c96,0xff953c96,0xff953c96,0xff953c96,0xff953c96,0xff953c96,
0xff953c96,0xff953c96,0xff953c96,0xff953c96,0xff953c96,0xff953c96,0xff953c96,0x5c351536,
0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,
0x00000000,0xb86b2b6c,0xff953c96,0xff953c96,0xff953c96,0xff953c96,0xff953c96,0xff953c96,
0xff953c96,0xff953c96,0xff953c96,0xff953c96,0xff953c96,0xff953c96,0xff953c96,0xff953c96,
0xff953c96,0xff953c96,0xc8752f75,0x04020102,0x00000000,0x00000000,0x00000000,0x00000000,
0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x26160a16,0xf4a265a2,0xff5ca4d9,
0xff5ca4d9,0xff5ca4d9,0xff5ca4d9,0xff5ca4d9,0xff5ca4d9,0xff5ca4d9,0xff5ca4d9,0xff5ca4d9,
0xff5ca4d9,0xff5ca4d9,0xff5ca4d9,0xff5ca4d9,0xff5ca4d9,0xf7a466a4,0x2c1a0b1a,0x00000000,
0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,
0x00000000,0x00000000,0x630b3a52,0xff009cdb,0xff009cdb,0xff009cdb,0xff009cdb,0xff009cdb,
0xff009cdb,0xff009cdb,0xff009cdb,0xff009cdb,0xff009cdb,0xff009cdb,0xff009cdb,0xff009cdb,
0xff009cdb,0x670c3c56,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,
0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x780e4664,
0xfb0099d7,0xff009cdb,0xff009cdb,0xff009cdb,0xd90085ba,0xaa13638e,0xa913638d,0xca007bad,
0xfe009bda,0xff009cdb,0xff009cdb,0xfd009bd9,0x7f0e4a6a,0x01000000,0x00000000,0x00000000,
0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,
0x00000000,0x00000000,0x00000000,0x00000000,0x30001c29,0x8a005476,0x8a005476,0x3c072332,
0x00000000,0x00000000,0x00000000,0x00000000,0x30081e29,0x87005274,0x8c005578,0x3500202d,
0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,
0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,
0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,
0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,
0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,
0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,
0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,
0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,0x00000000,
};
#endif /* MUI_COLOR_APPLE_DEFINE */

View File

@ -0,0 +1,600 @@
/*
* ui_tests.c
*
* Copyright (C) 2023 Michel Pollet <buserror@gmail.com>
*
* SPDX-License-Identifier: MIT
*/
#define _GNU_SOURCE // for asprintf
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <unistd.h>
#include "mui.h"
#include "mui_shell_plugin.h"
#include "c2_geometry.h"
typedef struct cg_ui_t {
mui_t *ui;
} cg_ui_t;
static void
_test_show_about(
cg_ui_t *g)
{
mui_window_t *w = mui_window_get_by_id(g->ui, FCC('a','b','o','t'));
if (w) {
mui_window_select(w);
return;
}
w = mui_alert(g->ui, C2_PT(0,0),
"About MUI",
"Version " MUI_VERSION "\n"
"Build " __DATE__ " " __TIME__,
MUI_ALERT_INFO);
mui_window_set_id(w, FCC('a','b','o','t'));
}
static void
_test_textedit_demo(
mui_t *mui)
{
mui_window_t *w = mui_window_get_by_id(mui, FCC('t','e','s','t'));
if (w) {
mui_window_select(w);
return;
}
c2_pt_t where = {};
c2_rect_t wpos = C2_RECT_WH(where.x, where.y, 510, 270);
if (where.x == 0 && where.y == 0)
c2_rect_offset(&wpos,
(mui->screen_size.x / 2) - (c2_rect_width(&wpos) / 2),
(mui->screen_size.y * 0.45) - (c2_rect_height(&wpos) / 2));
w = mui_window_create(mui, wpos, NULL, MUI_WINDOW_LAYER_NORMAL,
"This is VERY Work in Progress", 0);
mui_window_set_id(w, FCC('t','e','s','t'));
mui_control_t * c = NULL;
c2_rect_t cf;
cf = C2_RECT_WH(10, 10, 480, 170);
c = mui_textedit_control_new(w, cf,
MUI_CONTROL_TEXTBOX_FRAME | MUI_CONTROL_TEXTEDIT_VERTICAL);
mui_textedit_set_text(c,
"The quick brown fox Jumps over the Lazy dog.\n"
"Lorem Ipsum is simply dummy text of the printing "
"and typesetting industry. Lorem Ipsum has been the "
"industry's standard dummy text ever since the 1500s.\n"
#if 1
"Now let's step back and look at what's happening. "
"Writing to the disk is a load and shift process, a "
"little like HIRES pattern outputs but much slower.\n"
"Also, the MPU takes a very active role in the loading "
"and shifting of disk write data. There are two 8-state "
"loops in the WRITE sequence. After initializing the "
"WRITE sequence, data is stored in the data register "
"at a critical point in the A7' loop. As (quickly "
"thereafter as the 6502 can do it, the sequencer is "
"configured to shift left at the critical point "
"instead of loading."
#endif
);
c2_rect_bottom_of(&cf, cf.b, 10);
cf.b = cf.t + 35;
c = mui_textedit_control_new(w, cf, MUI_CONTROL_TEXTBOX_FRAME);
mui_textedit_set_text(c,
"Fulling Mill Online Return Center.pdf");
}
static void
_test_static_text_and_boxes(
struct mui_t *ui)
{
mui_window_t *w = mui_window_get_by_id(ui, FCC('s','t','x','t'));
if (w) {
mui_window_select(w);
return;
}
c2_pt_t where = {};
c2_rect_t wpos = C2_RECT_WH(where.x, where.y, 566, 320);
if (where.x == 0 && where.y == 0)
c2_rect_offset(&wpos,
(ui->screen_size.x / 2) - (c2_rect_width(&wpos) / 2),
(ui->screen_size.y * 0.45) - (c2_rect_height(&wpos) / 2));
w = mui_window_create(ui, wpos, NULL, MUI_WINDOW_LAYER_NORMAL,
"Static Text and Boxes", 0);
mui_window_set_id(w, FCC('s','t','x','t'));
float base_size = mui_font_find(ui, "main")->size;
float margin = base_size * 0.7;
mui_control_t * c = NULL;
c2_rect_t cf, cb;
cf = C2_RECT_WH(margin, margin/2, 250, 3 + base_size * 4);
c = mui_groupbox_new(w, cf, "Justification: ", 0);
cb = cf;
cb.t += base_size;
cb.b = cb.t + base_size;
cb.l += margin / 3;
cb.r = cf.r - (margin / 3);
uint16_t debug_frames = 0;//MUI_CONTROL_TEXTBOX_FRAME;
c = mui_textbox_new(w, cb, "Left Aligned", NULL,
MUI_TEXT_ALIGN_LEFT | debug_frames);
cb = c2_rect_bottom_of(&cb, cb.b, 0);
c = mui_textbox_new(w, cb, "Centered & Grayed", NULL,
MUI_TEXT_ALIGN_CENTER | debug_frames);
mui_control_set_state(c, MUI_CONTROL_STATE_DISABLED);
cb = c2_rect_bottom_of(&cb, cb.b, 0);
c = mui_textbox_new(w, cb, "Right Aligned", NULL,
MUI_TEXT_ALIGN_RIGHT | debug_frames);
c2_rect_t bottom_box = cf;
bottom_box = c2_rect_bottom_of(&bottom_box, bottom_box.b, margin*1.5);
c2_rect_t sr = cf;
cf = c2_rect_right_of(&cf, cf.r, margin);
// plonk in a separator to demo them
sr.r = cf.r;
sr.t = sr.b - 2;
c2_rect_inset(&sr, 80, 0);
sr = c2_rect_bottom_of(&sr, sr.b, margin);
mui_separator_new(w, sr);
c = mui_groupbox_new(w, cf, "Style: ", MUI_TEXT_ALIGN_RIGHT);
cb = cf;
cb.t += base_size;
cb.b = cb.t + base_size;
cb.l += margin / 3;
cb.r = cf.r - (margin / 3);
c = mui_textbox_new(w, cb, "Synthetic Bold", NULL,
MUI_TEXT_ALIGN_CENTER | MUI_TEXT_STYLE_BOLD);
cb = c2_rect_bottom_of(&cb, cb.b, 0);
c = mui_textbox_new(w, cb, "With Narrow Spacing", NULL,
MUI_TEXT_ALIGN_CENTER | MUI_TEXT_STYLE_NARROW);
cb = c2_rect_bottom_of(&cb, cb.b, 0);
c = mui_textbox_new(w, cb, "El Cheapo Underline", NULL,
MUI_TEXT_ALIGN_CENTER | MUI_TEXT_STYLE_ULINE);
cb = c2_rect_bottom_of(&cb, cb.b, 0);
cf = bottom_box;
c = mui_groupbox_new(w, cf, "Spacing: ", 0);
cb = cf;
cb.t += base_size;
cb.b = cb.t + base_size*3;
cb.l += margin / 3;
cb.r = cb.l + (c2_rect_width(&cf) / 2) - (margin / 3);
c = mui_textbox_new(w, cb, "Normal\nLine\nSpacing", NULL,
MUI_TEXT_ALIGN_CENTER | 0 | debug_frames);
cb = c2_rect_right_of(&cb, cb.r, margin/3);
c = mui_textbox_new(w, cb, "Compact\nLine\nSpacing", NULL,
MUI_TEXT_ALIGN_CENTER | MUI_TEXT_ALIGN_COMPACT | debug_frames);
cf = c2_rect_right_of(&cf, cf.r, margin);
c = mui_groupbox_new(w, cf, "Justification: ", 0);
cb = cf;
cb.t += base_size;
cb.b = cb.t + base_size * 3;
cb.l += margin / 3;
cb.r = cf.r - (margin / 3);
c = mui_textbox_new(w, cb,
"This quick brown fox is both Narrow, "
"and Fully Justified these days.", NULL,
MUI_TEXT_STYLE_NARROW | MUI_TEXT_ALIGN_FULL | debug_frames);
}
/*
* This demos most of the controls, buttons, radios, checkboxes, listbox
* and a few other things.
*/
static void
_test_demo_all_controls(
struct mui_t *ui)
{
mui_window_t *w = mui_window_get_by_id(ui, FCC('d','e','m','o'));
if (w) {
mui_window_select(w);
return;
}
c2_pt_t where = {};
c2_rect_t wpos = C2_RECT_WH(where.x, where.y, 620, 380);
if (where.x == 0 && where.y == 0)
c2_rect_offset(&wpos,
(ui->screen_size.x / 2) - (c2_rect_width(&wpos) / 2),
(ui->screen_size.y * 0.45) - (c2_rect_height(&wpos) / 2));
w = mui_window_create(ui, wpos, NULL, MUI_WINDOW_LAYER_NORMAL,
"Control Demo", 0);
mui_window_set_id(w, FCC('d','e','m','o'));
float base_size = mui_font_find(ui, "main")->size;
float margin = base_size * 0.7;
mui_control_t * c = NULL;
c2_rect_t cf;
cf = C2_RECT_WH(0, 0, base_size * 5, base_size * 1.4);
c2_rect_left_of(&cf, c2_rect_width(&w->content), margin);
c2_rect_top_of(&cf, c2_rect_height(&w->content), margin);
c = mui_button_new(w,
cf, MUI_BUTTON_STYLE_DEFAULT,
"Default", 0);
c->key_equ = MUI_KEY_EQU(0, 13); // return
cf = c2_rect_left_of(&cf, cf.l, margin);
c = mui_button_new(w,
cf, MUI_BUTTON_STYLE_NORMAL,
"Normal", 0);
c->key_equ = MUI_KEY_EQU(0, 32); // space
cf = c2_rect_left_of(&cf, cf.l, margin);
c = mui_button_new(w,
cf, MUI_BUTTON_STYLE_NORMAL,
"Disabled", 0);
mui_control_set_state(c, MUI_CONTROL_STATE_DISABLED);
/* Radio can have a mask, if so, they each UID that matches the
* mask will switch like radio buttons ought to, without having to
* keep track of which are on or off */
uint32_t radio_uid = FCC('r','a','d','0');
cf = C2_RECT_WH(margin, margin, base_size * 7, base_size * 1.4);
c = mui_button_new(w, cf, MUI_BUTTON_STYLE_RADIO,
"Radio Button", FCC_INDEXED(radio_uid, 0));
c->uid_mask = FCC_MASK;
mui_control_set_value(c, 1);
cf = c2_rect_bottom_of(&cf, cf.b, 0);
c = mui_button_new(w, cf, MUI_BUTTON_STYLE_RADIO,
"Other Radio", FCC_INDEXED(radio_uid, 1));
mui_control_set_state(c, MUI_CONTROL_STATE_DISABLED);
c->uid_mask = FCC_MASK;
cf = c2_rect_bottom_of(&cf, cf.b, 0);
c = mui_button_new(w, cf, MUI_BUTTON_STYLE_RADIO,
"Third Choice", FCC_INDEXED(radio_uid, 2));
c->uid_mask = FCC_MASK;
c2_rect_t first_column = cf;
cf = c2_rect_right_of(&cf, cf.r, margin);
cf.r = cf.l + base_size * 6;
c2_rect_offset(&cf, 0, -cf.t + margin);
c = mui_button_new(w,
cf, MUI_BUTTON_STYLE_CHECKBOX,
"Checkbox", 0);
cf = c2_rect_bottom_of(&cf, cf.b, margin/2);
c = mui_button_new(w,
cf, MUI_BUTTON_STYLE_CHECKBOX,
"Another Checkbox", 0);
mui_control_set_value(c, 1);
c2_rect_t second_column = cf;
cf = first_column;
cf = c2_rect_bottom_of(&cf, cf.b, margin);
c2_rect_t lr = cf;
lr.r = lr.l + base_size * 3;
c = mui_textbox_new(w, lr, "Popup:", NULL, MUI_TEXT_ALIGN_LEFT);
cf = c2_rect_right_of(&cf, lr.r, 0);
//c2_rect_offset(&cf, 0, -cf.t + margin);
cf.b = cf.t + 34;
c = mui_popupmenu_new(w, cf, "Popup", 0, MUI_TEXT_ALIGN_LEFT);
mui_menu_items_t *items = mui_popupmenu_get_items(c);
mui_menu_items_add(items, (mui_menu_item_t){.title="1200", .uid=1200 });
mui_menu_items_add(items, (mui_menu_item_t){.title="2400", .uid=2400 });
mui_menu_items_add(items, (mui_menu_item_t){.title="4800", .uid=4800 });
mui_menu_items_add(items, (mui_menu_item_t){.title="9600", .uid=9600 });
mui_menu_items_add(items, (mui_menu_item_t){.title="19200", .uid=19200 });
// popup needs to be NULL terminated, AND prepared()
mui_menu_items_add(items, (mui_menu_item_t){.title=NULL });
mui_popupmenu_prepare(c);
lr = c2_rect_bottom_of(&lr, lr.b, margin/4);
lr.r = lr.l + base_size * 3;
c = mui_textbox_new(w, lr, "Icons:", NULL, MUI_TEXT_ALIGN_LEFT);
cf = c2_rect_bottom_of(&cf, cf.b, margin/4);
//c2_rect_offset(&cf, 0, -cf.t + margin);
cf.b = cf.t + 34;
c = mui_popupmenu_new(w, cf, "Popup", 0, MUI_TEXT_ALIGN_LEFT);
items = mui_popupmenu_get_items(c);
mui_menu_items_add(items,
(mui_menu_item_t){.title="Icon", .uid=1200, .icon=MUI_ICON_HARDDISK });
mui_menu_items_add(items,
(mui_menu_item_t){.title="Others", .uid=2400, .icon=MUI_ICON_FOLDER });
// popup needs to be NULL terminated, AND prepared()
mui_menu_items_add(items, (mui_menu_item_t){.title=NULL });
mui_popupmenu_prepare(c);
lr = c2_rect_bottom_of(&lr, lr.b, margin/4);
lr.r = lr.l + base_size * 4.2;
c = mui_textbox_new(w, lr, "Scrollbar:", NULL, MUI_TEXT_ALIGN_LEFT);
c2_rect_right_of(&cf, lr.r, margin/4);
cf = c2_rect_bottom_of(&cf, cf.b, margin/2);
cf.b = cf.t + base_size;
cf.r = cf.l + 200;
c = mui_scrollbar_new(w, cf, 0, 5, 20);
mui_scrollbar_set_max(c, 255);
// mui_scrollbar_set_page(c, 10);
cf = second_column;
cf.b = cf.t + base_size;
cf = c2_rect_right_of(&cf, cf.r, margin);
c2_rect_offset(&cf, 0, -cf.t + margin);
c = mui_textbox_new(w, cf, "Listbox:", NULL, 0);
c2_rect_bottom_of(&cf, cf.b, 0);
cf.b = cf.t + 6 * base_size;
c = mui_listbox_new(w, cf, 0);
mui_listbox_elems_t * elems = mui_listbox_get_elems(c);
for (int i = 0; i < 25; i++) {
mui_listbox_elem_t e = {
.icon = MUI_ICON_FILE,
};
asprintf((char**)&e.elem, "Item %d", i);
mui_listbox_elems_add(elems, e);
}
mui_listbox_prepare(c);
}
#define MUI_COLOR_APPLE_DEFINE
#include "mui_color_apple.h"
mui_menu_item_t m_color_apple_menu[] = {
{ .color_icon = mui_color_apple, .is_menutitle = 1, },
{ .title = "About MUI…",
.uid = FCC('a','b','o','t') },
// { .title = "-", },
{ },
};
mui_menu_item_t m_file_menu[] = {
{ .title = "Quit",
.uid = FCC('q','u','i','t'),
.key_equ = MUI_KEY_EQU(MUI_MODIFIER_SUPER, 'Q'),
.kcombo = MUI_GLYPH_COMMAND "Q" },
{ },
};
mui_menu_item_t m_sub_menu1[] = {
{ .title = "Hierarchical Menu",
.uid = FCC('s','h','m','b'),
.key_equ = MUI_KEY_EQU(MUI_MODIFIER_RCTRL, MUI_KEY_F1),
.kcombo = MUI_GLYPH_CONTROL MUI_GLYPH_F1 },
{ .title = "Disabled item",
.disabled = 1,
.key_equ = MUI_KEY_EQU(MUI_MODIFIER_RCTRL, MUI_KEY_F11),
.kcombo = MUI_GLYPH_CONTROL MUI_GLYPH_F11 },
{ .title = "-", },
{ .title = "Tick One",
.mark = MUI_GLYPH_TICK,
.uid = FCC('v','d','c','0') },
{ .title = "Tick Two",
.uid = FCC('v','d','c','1') },
{ },
};
mui_menu_item_t m_sub_menu2[] = {
{ .title = "Other Sub Menu",
.uid = FCC('s','h','m','b'),
.key_equ = MUI_KEY_EQU(MUI_MODIFIER_RCTRL, MUI_KEY_F2),
.kcombo = MUI_GLYPH_CONTROL MUI_GLYPH_F2 },
{ .title = "Other Marked Item",
.uid = FCC('s','h','m','b'),
.mark = "!", },
{ },
};
mui_menu_item_t m_menu_demo[] = {
{ .title = MUI_GLYPH_OAPPLE "-Control-Reset",
.uid = FCC('c','r','e','s'),
.key_equ = MUI_KEY_EQU(MUI_MODIFIER_RCTRL|MUI_MODIFIER_RSHIFT, MUI_KEY_F12),
.kcombo = MUI_GLYPH_CONTROL MUI_GLYPH_SHIFT MUI_GLYPH_F12 },
{ .title = "Control-Reset",
.uid = FCC('r','e','s','t'),
.key_equ = MUI_KEY_EQU(MUI_MODIFIER_RCTRL, MUI_KEY_F12),
.kcombo = MUI_GLYPH_CONTROL MUI_GLYPH_F12 },
{ .title = "-", },
{ .title = "Sub Menu",
.kcombo = MUI_GLYPH_SUBMENU,
.submenu = m_sub_menu1 },
{ .title = "Other Sub Menu",
.kcombo = MUI_GLYPH_SUBMENU,
.submenu = m_sub_menu2 },
{ },
};
mui_menu_item_t m_windows_menu[] = {
{ .title = "Basic Alert…",
.uid = FCC('a','l','e','r'), },
{ .title = "Standard 'get' file…",
.uid = FCC('s','t','d','g'), },
{ .title = "Static Text…",
.uid = FCC('s','t','x','t'), },
{ .title = "Text Edit (WIP!)…",
.uid = FCC('t','x','t','e'), },
{ .title = "Demo All Controls…",
.uid = FCC('d','e','m','o'), },
{ },
};
/*
* This is a 'window' action function that is called when the Alert window
* is interacted with. It typically you will get an ACTION_SELECT with
* ok or cancel.
*/
static int
_test_alert_action_cb(
mui_window_t *win,
void * cb_param,
uint32_t what,
void * param)
{
// cg_ui_t *g = cb_param;
printf("%s %4.4s\n", __func__, (char*)&what);
switch (what) {
case MUI_CONTROL_ACTION_SELECT: {
mui_control_t * c = param;
uint32_t uid = mui_control_get_uid(c);
printf("%s Button %4.4s\n", __func__, (char*)&uid);
} break;
}
return 0;
}
static int
_test_stdfile_action_cb(
mui_window_t *win,
void * cb_param,
uint32_t what,
void * param)
{
// cg_ui_t *g = cb_param;
printf("%s %4.4s\n", __func__, (char*)&what);
switch (what) {
case MUI_CONTROL_ACTION_SELECT: {
mui_control_t * c = param;
uint32_t uid = mui_control_get_uid(c);
printf("%s Button %4.4s\n", __func__, (char*)&uid);
} break;
case MUI_STDF_ACTION_CANCEL: {
printf("%s cancel was clicked\n", __func__);
mui_window_dispose(win);
} break;
}
return 0;
}
int
_test_menubar_action(
mui_window_t *win,
void * cb_param,
uint32_t what,
void * param)
{
cg_ui_t *g = cb_param;
// printf("%s %4.4s\n", __func__, (char*)&what);
switch (what) {
case MUI_MENUBAR_ACTION_PREPARE: {
// mui_menu_item_t * items = param;
} break;
case MUI_MENUBAR_ACTION_SELECT: {
mui_menu_item_t * item = param;
printf("%s Selected %4.4s '%s'\n", __func__,
(char*)&item->uid, item->title);
switch (item->uid) {
case FCC('a','b','o','t'): {
_test_show_about(g);
} break;
case FCC('q','u','i','t'): {
printf("%s Quit\n", __func__);
g->ui->quit_request = 1;
} break;
case FCC('a','l','e','r'): {
mui_window_t * w = mui_alert(g->ui, C2_PT(0,0),
"Testing one Two",
"Do you really want the printer to catch fire?\n"
"This operation cannot be cancelled.",
MUI_ALERT_WARN);
mui_window_set_action(w, _test_alert_action_cb, g);
} break;
case FCC('s','t','d','g'): {
mui_window_t *w = mui_stdfile_get(g->ui,
C2_PT(0, 0),
"Select image for SmartPort card",
"hdv,po,2mg",
getenv("HOME"), 0);
mui_window_set_action(w, _test_stdfile_action_cb, g);
} break;
case FCC('t','x','t','e'):
_test_textedit_demo(g->ui);
break;
case FCC('s','t','x','t'):
_test_static_text_and_boxes(g->ui);
break;
case FCC('d','e','m','o'):
_test_demo_all_controls(g->ui);
break;
default:
printf("%s menu item %4.4s %s IGNORED\n",
__func__, (char*)&item->uid, item->title);
break;
}
} break;
default:
printf("%s %4.4s IGNORED?\n", __func__, (char*)&what);
break;
}
return 0;
}
static void *
_init(
struct mui_t * ui,
struct mui_plug_t * plug,
mui_drawable_t * pix)
{
mui_init(ui);
ui->screen_size = pix->pix.size;
asprintf(&ui->pref_directory, "%s/.local/share/mui", getenv("HOME"));
cg_ui_t *g = calloc(1, sizeof(*g));
g->ui = ui;
printf("%s\n", __func__);
mui_window_t * mbar = mui_menubar_new(ui);
mui_window_set_action(mbar, _test_menubar_action, g);
mui_menubar_add_menu(mbar, FCC('a','p','p','l'), m_color_apple_menu, 2);
mui_menubar_add_simple(mbar, "File",
FCC('f','i','l','e'),
m_file_menu);
mui_menubar_add_simple(mbar, "Menus",
FCC('m','e','n','u'),
m_menu_demo);
mui_menubar_add_simple(mbar, "Windows",
FCC('w','i','n','d'),
m_windows_menu);
// _test_textedit_demo(ui);
// _test_static_text_and_boxes(ui);
_test_demo_all_controls(ui);
return g;
}
static void
_dispose(
void *_ui)
{
cg_ui_t *ui = _ui;
printf("%s\n", __func__);
mui_dispose(ui->ui);
free(ui);
}
static int
_draw(
struct mui_t *ui,
void *param,
mui_drawable_t *dr,
uint16_t all)
{
mui_draw(ui, dr, all);
return 1;
}
static int
_event(
struct mui_t *ui,
void *param,
mui_event_t *event)
{
mui_handle_event(ui, event);
return 0;
}
mui_plug_t mui_plug = {
.name = "MUI Widgets Demo",
.init = _init,
.dispose = _dispose,
.draw = _draw,
.event = _event,
};

108
libmui/utils/png2raw.c Normal file
View File

@ -0,0 +1,108 @@
#!/usr/bin/tcc -run
/*
* This tool is made to load a PNG, and convert it to a C array with
* ARGB8888 format, premultiplied alpha.
* It uses stb_image.h to load the PNG, which isn't included in the
* repository. The tool isn't essential to build, but is useful if you
* want icons in your menus.
* The output is a C array, with the width and height of the image
* followed by the pixels in ARGB8888 format.
* The output is written to stdout, unless -o is used.
*/
#include <ctype.h>
#define STB_IMAGE_IMPLEMENTATION
#define STBI_NO_SIMD
#define STBI_NO_HDR
#include "/opt/projects/stb/stb_image.h"
static void usage(const char *argv0)
{
fprintf(stderr,
"Usage: %s -n <array_name> "
"[-o <output_filename>] "
"[-t <int type>] "
"<PNG filename>\n",
argv0);
}
int main(int argc, char **argv)
{
const char *array_name = NULL;
const char *fname = "docs/Apple_logo_rainbow_version2_28x28.png";
const char *int_type = "uint32_t";
FILE *out = stdout;
for (int i = 1; i < argc; i++) {
if (!strcmp(argv[i], "-n") && i + 1 < argc) {
array_name = argv[i + 1];
i++;
} else if (!strcmp(argv[i], "-o") && i + 1 < argc) {
out = fopen(argv[i + 1], "w");
if (!out) {
fprintf(stderr, "Error opening %s\n", argv[i + 1]);
usage(argv[0]);
return 1;
}
i++;
} else if (!strcmp(argv[i], "-t") && i + 1 < argc) {
int_type = argv[i + 1];
i++;
} else if (!strcmp(argv[i], "-h")) {
usage(argv[0]);
return 0;
} else {
fname = argv[i];
}
}
if (!array_name) {
fprintf(stderr, "Missing array name\n");
usage(argv[0]);
return 1;
}
char define_name[256];
strcpy(define_name, array_name);
for (int i = 0; define_name[i]; i++)
define_name[i] = toupper(define_name[i]);
int width, height, channels;
unsigned char *data = stbi_load(fname, &width, &height, &channels, 0);
if (!data) {
fprintf(stderr, "Error loading image\n");
return 1;
}
fprintf(out, "// Autogenerated with:\n//\t");
for (int i = 0; i < argc; i++)
fprintf(out, " %s", argv[i]);
fprintf(out, "\n");
fprintf(out, "// Image with a W:%dpx, H:%dpx and %d channels\n"
"// Converted to ARGB8888 and premultiplied alpha\n"
"#pragma once\n",
width, height, channels);
fprintf(out, "#define %s_SIZE %d\n", define_name, 2 + (width * height));
fprintf(out, "extern const %s %s[%s_SIZE];\n", int_type, array_name, define_name);
fprintf(out, "#ifdef %s_DEFINE\n", define_name);
fprintf(out, "const %s %s[%s_SIZE] = {\n", int_type, array_name, define_name);
fprintf(out, "%d, %d, // width, height\n", width, height);
uint32_t *pixels = (uint32_t *)data;
for (int y = 0; y < height; y++) {
for (int x = 0; x < width; x++) {
uint32_t pixel = pixels[y * width + x];
// flip R and B channels
pixel = (pixel & 0xff00ff00) |
((pixel & 0xff) << 16) | ((pixel >> 16) & 0xff);
// also premultiply channels with alpha
uint32_t a = pixel >> 24;
pixel = (pixel & ~(0xff << 0)) |
((((((pixel >> 0) & 0xff) * a) >> 8) & 0xff) << 0);
pixel = (pixel & ~(0xff << 8)) |
((((((pixel >> 8) & 0xff) * a) >> 8) & 0xff) << 8);
pixel = (pixel & ~(0xff << 16)) |
((((((pixel >> 16) & 0xff) * a) >> 8) & 0xff) << 16);
fprintf(out, "0x%08x,", pixel);
if (((y * height) + x) % 8 == 7)
fprintf(out, "\n");
}
}
fprintf(out, "};\n");
fprintf(out, "#endif /* %s_DEFINE */\n", define_name);
return 0;
}

View File

@ -20,13 +20,38 @@
enum {
// bits used to address the LSS ROM using lss_mode
WRITE_BIT = 0,
LOAD_BIT = 1,
QA_BIT = 2,
RP_BIT = 3,
Q7_WRITE_BIT = 3,
Q6_LOAD_BIT = 2,
QA_BIT = 1,
RP_BIT = 0,
};
enum {
SIG_DR, // data register
SIG_WR, // write register
SIG_DRIVE, // selected drive
SIG_MOTOR, // motor on/off
SIG_READ_DR,
SIG_LSS_CLK,
SIG_LSS_SEQ,
SIG_LSS_CMD,
SIG_LSS_WRITE,
SIG_LSS_LOAD,
SIG_LSS_QA,
SIG_LSS_RP, // read pulse
SIG_LSS_WB, // write bit
SIG_LSS_RANDOM,
SIG_WRITE_CYCLE,
SIG_COUNT
};
const char *sig_names[] = {
"D2_DR", "D2_WR", "DRIVE", "MOTOR", "READ_DR",
"LSS_CLK", "LSS_SEQ", "LSS_CMD", "LSS_WRITE", "LSS_LOAD",
"LSS_QA", "LSS_RP", "LSS_WB", "LSS_RANDOM", "WRITE_CYCLE",
};
typedef struct mii_card_disk2_t {
mii_t * mii;
mii_dd_t drive[2];
mii_floppy_t floppy[2];
uint8_t selected;
@ -42,12 +67,19 @@ typedef struct mii_card_disk2_t {
uint8_t lss_prev_state; // for write bit
uint8_t lss_skip;
uint8_t data_register;
} mii_card_disk2_t;
uint64_t debug_last_write, debug_last_duration;
mii_vcd_t *vcd;
mii_signal_t *sig;
} mii_card_disk2_t;
static void
_mii_disk2_lss_tick(
mii_card_disk2_t *c );
static void
_mii_disk2_vcd_debug(
mii_card_disk2_t *c,
int on);
// debug, used for mish, only supports one card tho (yet)
static mii_card_disk2_t *_mish_d2 = NULL;
@ -65,7 +97,8 @@ _mii_floppy_motor_off_cb(
// printf("%s drive %d off\n", __func__, c->selected);
if (c->drive[c->selected].file && f->seed_dirty != f->seed_saved)
mii_floppy_update_tracks(f, c->drive[c->selected].file);
f->motor = 0;
f->motor = 0;
mii_raise_signal(c->sig + SIG_MOTOR, 0);
return 0;
}
@ -113,6 +146,13 @@ _mii_disk2_switch_track(
// if (track_id != 0xff)
// printf("NEW TRACK D%d: %d\n", c->selected, track_id);
uint8_t track_id_new = f->track_id[qtrack];
if (track_id != track_id_new && track_id != MII_FLOPPY_NOISE_TRACK) {
if (track_id == 0 && c->vcd)
_mii_disk2_vcd_debug(c, 0);
if (f->seed_dirty != f->seed_saved) {
// mii_floppy_resync_track(f, track_id, 0);
}
}
if (track_id_new >= MII_FLOPPY_TRACK_COUNT)
track_id_new = MII_FLOPPY_NOISE_TRACK;
/* adapt the bit position from one track to the others, from WOZ specs */
@ -126,6 +166,7 @@ _mii_disk2_switch_track(
return f->qtrack;
}
static int
_mii_disk2_init(
mii_t * mii,
@ -133,13 +174,16 @@ _mii_disk2_init(
{
mii_card_disk2_t *c = calloc(1, sizeof(*c));
slot->drv_priv = c;
c->mii = mii;
printf("%s loading in slot %d\n", __func__, slot->id + 1);
uint16_t addr = 0xc100 + (slot->id * 0x100);
mii_bank_write(
&mii->bank[MII_BANK_CARD_ROM],
addr, mii_rom_disk2, 256);
c->sig = mii_alloc_signal(&mii->sig_pool, 0, SIG_COUNT, sig_names);
for (int i = 0; i < SIG_COUNT; i++)
c->sig[i].flags |= SIG_FLAG_FILTERED;
for (int i = 0; i < 2; i++) {
mii_dd_t *dd = &c->drive[i];
dd->slot_id = slot->id + 1;
@ -149,19 +193,32 @@ _mii_disk2_init(
dd->slot_id, dd->drive);
mii_floppy_init(&c->floppy[i]);
c->floppy[i].id = i;
dd->floppy = &c->floppy[i];
}
mii_dd_register_drives(&mii->dd, c->drive, 2);
c->timer_off = mii_timer_register(mii,
_mii_floppy_motor_off_cb, c, 0,
"Disk ][ motor off");
c->timer_lss = mii_timer_register(mii,
_mii_floppy_lss_cb, c, 0,
"Disk ][ LSS");
char *n;
asprintf(&n, "Disk ][ S:%d motor off", slot->id + 1);
c->timer_off = mii_timer_register(mii, _mii_floppy_motor_off_cb, c, 0, n);
asprintf(&n, "Disk ][ S:%d LSS", slot->id + 1);
c->timer_lss = mii_timer_register(mii, _mii_floppy_lss_cb, c, 0, n);
_mish_d2 = c;
return 0;
}
static void
_mii_disk2_reset(
mii_t * mii,
struct mii_slot_t *slot )
{
mii_card_disk2_t *c = slot->drv_priv;
// printf("%s\n", __func__);
c->selected = 1;
_mii_floppy_motor_off_cb(mii, c);
c->selected = 0;
_mii_floppy_motor_off_cb(mii, c);
mii_raise_signal(c->sig + SIG_DRIVE, 0);
}
static uint8_t
_mii_disk2_access(
mii_t * mii, struct mii_slot_t *slot,
@ -172,18 +229,29 @@ _mii_disk2_access(
uint8_t ret = 0;
if (write) {
// printf("WRITE PC:%04x %4.4x: %2.2x\n", mii->cpu.PC, addr, byte);
// if (!(byte &0x80))
// printf("WRITE PC:%04x %4.4x: %2.2x\n", mii->cpu.PC, addr, byte);
c->write_register = byte;
mii_raise_signal(c->sig + SIG_WR, byte);
if (c->vcd) {
uint8_t delta = c->vcd->cycle - c->debug_last_write;
mii_raise_signal_float(
c->sig + SIG_WRITE_CYCLE, c->debug_last_duration != delta, 0);
c->debug_last_duration = delta;
c->debug_last_write = c->vcd->cycle;
}
}
int psw = addr & 0x0F;
int p = psw >> 1, on = psw & 1;
int read = 0;
switch (psw) {
case 0x00 ... 0x07: {
static const int8_t delta[4][4] = {
{0, 1, 2, -1}, {-1, 0, 1, 2}, {-2, -1, 0, 1}, {1, -2, -1, 0},
};
if (on) {
if ((f->stepper + 3) % 4 == p)
_mii_disk2_switch_track(mii, c, -2);
else if ((f->stepper + 1) % 4 == p)
_mii_disk2_switch_track(mii, c, 2);
_mii_disk2_switch_track(mii, c, delta[f->stepper][p] * 2);
f->stepper = p;
}
} break;
@ -194,35 +262,38 @@ _mii_disk2_access(
mii_timer_set(mii, c->timer_off, 0);
mii_timer_set(mii, c->timer_lss, 1);
f->motor = 1;
mii_raise_signal(c->sig + SIG_MOTOR, 1);
} else {
if (!mii_timer_get(mii, c->timer_off)) {
mii_timer_set(mii, c->timer_off, 1000000); // one second
}
int32_t timer = mii_timer_get(mii, c->timer_off);
mii_timer_set(mii, c->timer_off,
timer + (1000000 * mii->speed)); // one second
}
} break;
case 0x0A:
case 0x0B: {
if (on != c->selected) {
c->selected = on;
printf("SELECTED DRIVE: %d\n", c->selected);
// printf("SELECTED DRIVE: %d\n", c->selected);
c->floppy[on].motor = f->motor;
f->motor = 0;
mii_raise_signal(c->sig + SIG_MOTOR, 0);
}
} break;
case 0x0C:
case 0x0D:
c->lss_mode = (c->lss_mode & ~(1 << LOAD_BIT)) | (!!on << LOAD_BIT);
if (!(c->lss_mode & (1 << WRITE_BIT)) && f->heat) {
c->lss_mode = (c->lss_mode & ~(1 << Q6_LOAD_BIT)) | (!!on << Q6_LOAD_BIT);
if (!(c->lss_mode & (1 << Q7_WRITE_BIT)) && f->heat) {
uint8_t track_id = f->track_id[f->qtrack];
uint32_t byte_index = f->bit_position >> 3;
unsigned int dstb = byte_index / MII_FLOPPY_HM_HIT_SIZE;
f->heat->read.map[track_id][dstb] = 255;
f->heat->read.seed++;
read++;
}
break;
case 0x0E:
case 0x0F:
c->lss_mode = (c->lss_mode & ~(1 << WRITE_BIT)) | (!!on << WRITE_BIT);
c->lss_mode = (c->lss_mode & ~(1 << Q7_WRITE_BIT)) | (!!on << Q7_WRITE_BIT);
break;
}
/*
@ -230,8 +301,10 @@ _mii_disk2_access(
* the write protect bit to be loaded if needed, it *has* to run before
* we return this value, so we marked a skip and run it here.
*/
_mii_disk2_lss_tick(c);
c->lss_skip++;
// _mii_disk2_lss_tick(c);
// c->lss_skip++;
if (read)
mii_raise_signal(c->sig + SIG_READ_DR, c->data_register);
ret = on ? byte : c->data_register;
return ret;
@ -304,11 +377,51 @@ static mii_slot_drv_t _driver = {
.name = "disk2",
.desc = "Apple Disk ][",
.init = _mii_disk2_init,
.reset = _mii_disk2_reset,
.access = _mii_disk2_access,
.command = _mii_disk2_command,
};
MI_DRIVER_REGISTER(_driver);
static void
_mii_disk2_vcd_debug(
mii_card_disk2_t *c,
int on)
{
if (c->vcd) {
mii_vcd_close(c->vcd);
c->vcd = NULL;
printf("VCD OFF\n");
} else {
c->vcd = calloc(1, sizeof(*c->vcd));
// 2Mhz clock
mii_vcd_init(c->mii, "/tmp/disk2.vcd", c->vcd, 500);
printf("VCD ON\n");
mii_vcd_add_signal(c->vcd, c->sig + SIG_DR, 8, "DR");
mii_vcd_add_signal(c->vcd, c->sig + SIG_WR, 8, "WR");
mii_vcd_add_signal(c->vcd, c->sig + SIG_DRIVE, 1, "DRIVE");
mii_vcd_add_signal(c->vcd, c->sig + SIG_MOTOR, 1, "MOTOR");
mii_vcd_add_signal(c->vcd, c->sig + SIG_READ_DR, 8, "READ");
mii_vcd_add_signal(c->vcd, c->sig + SIG_LSS_CLK, 1, "LSS_CLK");
mii_vcd_add_signal(c->vcd, c->sig + SIG_LSS_SEQ, 4, "LSS_SEQ");
mii_vcd_add_signal(c->vcd, c->sig + SIG_LSS_CMD, 4, "LSS_CMD");
mii_vcd_add_signal(c->vcd, c->sig + SIG_LSS_WRITE, 1, "LSS_W/R");
mii_vcd_add_signal(c->vcd, c->sig + SIG_LSS_LOAD, 1, "LSS_L/S");
mii_vcd_add_signal(c->vcd, c->sig + SIG_LSS_QA, 1, "LSS_QA");
mii_vcd_add_signal(c->vcd, c->sig + SIG_LSS_RP, 1, "LSS_RP");
mii_vcd_add_signal(c->vcd, c->sig + SIG_LSS_WB, 1, "LSS_WB");
mii_vcd_add_signal(c->vcd, c->sig + SIG_LSS_RANDOM, 1, "LSS_RANDOM");
mii_vcd_add_signal(c->vcd, c->sig + SIG_WRITE_CYCLE, 1, "WRITE_CYCLE");
mii_vcd_start(c->vcd);
// put the LSS state in the VCD to start with
mii_raise_signal(c->sig + SIG_LSS_QA,
!!(c->lss_mode & (1 << QA_BIT)));
mii_raise_signal(c->sig + SIG_LSS_WRITE,
!!(c->lss_mode & (1 << Q7_WRITE_BIT)));
mii_raise_signal(c->sig + SIG_LSS_LOAD,
!!(c->lss_mode & (1 << Q6_LOAD_BIT)));
}
}
/* Sather Infamous Table, pretty much verbatim
WRITE
@ -353,8 +466,8 @@ E- FD-SL1 FD-SL1 F8-NOP F8-NOP 0A-SR 0A-SR 0A-SR 0A-SR
F- DD-SL1 4D-SL1 E0-CLR E0-CLR 0A-SR 0A-SR 0A-SR 0A-SR
*/
enum {
WRITE = (1 << WRITE_BIT),
LOAD = (1 << LOAD_BIT),
WRITE = (1 << Q7_WRITE_BIT),
LOAD = (1 << Q6_LOAD_BIT),
QA1 = (1 << QA_BIT),
RP1 = (1 << RP_BIT),
/* This keeps the transposed table more readable, as it looks like the book */
@ -380,6 +493,35 @@ static const uint8_t lss_rom16s[16][16] = {
[READ|LOAD|QA1|RP0]={ 0x0A,0x0A,0x0A,0x0A,0x0A,0x0A,0x0A,0x0A,0x0A,0x0A,0x0A,0x0A,0x0A,0x0A,0x0A,0x0A },
};
static const uint8_t SEQUENCER_ROM_16[256] __attribute__((unused)) = {
// See Understanding the Apple IIe, Figure 9.11 The DOS 3.3 Logic State Sequencer
// Note that the column order here is NOT the same as in Figure 9.11 for Q7 H (Write).
//
// Q7 L (Read) Q7 H (Write)
// Q6 L (Shift) Q6 H (Load) Q6 L (Shift) Q6 H (Load)
// QA L QA H QA L QA H QA L QA H QA L QA H
// 1 0 1 0 1 0 1 0 1 0 1 0 1 0 1 0
0x18, 0x18, 0x18, 0x18, 0x0A, 0x0A, 0x0A, 0x0A, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, 0x18, // 0
0x2D, 0x2D, 0x38, 0x38, 0x0A, 0x0A, 0x0A, 0x0A, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, 0x28, // 1
0xD8, 0x38, 0x08, 0x28, 0x0A, 0x0A, 0x0A, 0x0A, 0x39, 0x39, 0x39, 0x39, 0x3B, 0x3B, 0x3B, 0x3B, // 2
0xD8, 0x48, 0x48, 0x48, 0x0A, 0x0A, 0x0A, 0x0A, 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, 0x48, // 3
0xD8, 0x58, 0xD8, 0x58, 0x0A, 0x0A, 0x0A, 0x0A, 0x58, 0x58, 0x58, 0x58, 0x58, 0x58, 0x58, 0x58, // 4
0xD8, 0x68, 0xD8, 0x68, 0x0A, 0x0A, 0x0A, 0x0A, 0x68, 0x68, 0x68, 0x68, 0x68, 0x68, 0x68, 0x68, // 5
0xD8, 0x78, 0xD8, 0x78, 0x0A, 0x0A, 0x0A, 0x0A, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, 0x78, // 6
0xD8, 0x88, 0xD8, 0x88, 0x0A, 0x0A, 0x0A, 0x0A, 0x08, 0x08, 0x88, 0x88, 0x08, 0x08, 0x88, 0x88, // 7
0xD8, 0x98, 0xD8, 0x98, 0x0A, 0x0A, 0x0A, 0x0A, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, 0x98, // 8
0xD8, 0x29, 0xD8, 0xA8, 0x0A, 0x0A, 0x0A, 0x0A, 0xA8, 0xA8, 0xA8, 0xA8, 0xA8, 0xA8, 0xA8, 0xA8, // 9
0xCD, 0xBD, 0xD8, 0xB8, 0x0A, 0x0A, 0x0A, 0x0A, 0xB9, 0xB9, 0xB9, 0xB9, 0xBB, 0xBB, 0xBB, 0xBB, // A
0xD9, 0x59, 0xD8, 0xC8, 0x0A, 0x0A, 0x0A, 0x0A, 0xC8, 0xC8, 0xC8, 0xC8, 0xC8, 0xC8, 0xC8, 0xC8, // B
0xD9, 0xD9, 0xD8, 0xA0, 0x0A, 0x0A, 0x0A, 0x0A, 0xD8, 0xD8, 0xD8, 0xD8, 0xD8, 0xD8, 0xD8, 0xD8, // C
0xD8, 0x08, 0xE8, 0xE8, 0x0A, 0x0A, 0x0A, 0x0A, 0xE8, 0xE8, 0xE8, 0xE8, 0xE8, 0xE8, 0xE8, 0xE8, // D
0xFD, 0xFD, 0xF8, 0xF8, 0x0A, 0x0A, 0x0A, 0x0A, 0xF8, 0xF8, 0xF8, 0xF8, 0xF8, 0xF8, 0xF8, 0xF8, // E
0xDD, 0x4D, 0xE0, 0xE0, 0x0A, 0x0A, 0x0A, 0x0A, 0x88, 0x88, 0x08, 0x08, 0x88, 0x88, 0x08, 0x08 // F
// 0 1 1 3 4 5 6 7 8 9 A B C D E F
};
FILE *debug = NULL;
static void
_mii_disk2_lss_tick(
mii_card_disk2_t *c )
@ -390,65 +532,69 @@ _mii_disk2_lss_tick(
}
mii_floppy_t *f = &c->floppy[c->selected];
c->lss_mode = (c->lss_mode & ~(1 << QA_BIT)) |
(!!(c->data_register & 0x80) << QA_BIT);
if (c->vcd)
c->vcd->cycle += 1;
c->clock += 4; // 4 is 0.5us.. we run at 2MHz
uint8_t track_id = f->track_id[f->qtrack];
uint8_t * track = f->track_data[track_id];
uint32_t byte_index = f->bit_position >> 3;
uint8_t bit_index = 7 - (f->bit_position & 7);
uint8_t rp = 0;
mii_raise_signal(c->sig + SIG_LSS_CLK, c->clock >= f->bit_timing);
if (c->clock >= f->bit_timing) {
c->clock -= f->bit_timing;
uint8_t track_id = f->track_id[f->qtrack];
uint8_t * track = f->track_data[track_id];
uint32_t byte_index = f->bit_position >> 3;
uint8_t bit_index = 7 - (f->bit_position & 7);
if (!(c->lss_mode & (1 << WRITE_BIT))) {
uint8_t bit = track[byte_index];
bit = (bit >> bit_index) & 1;
c->head = (c->head << 1) | bit;
// see WOZ spec for how we do this here
if ((c->head & 0xf) == 0) {
bit = f->track_data[MII_FLOPPY_NOISE_TRACK][byte_index];
bit = (bit >> bit_index) & 1;
uint8_t bit = track[byte_index];
bit = (bit >> bit_index) & 1;
c->head = (c->head << 1) | bit;
// see WOZ spec for how we do this here
if ((c->head & 0xf) == 0) {
bit = f->track_data[MII_FLOPPY_NOISE_TRACK][byte_index];
rp = (bit >> bit_index) & 1;
// printf("RANDOM TRACK %2d %2d %2d : %d\n",
// track_id, byte_index, bit_index, bit);
} else {
bit = (c->head >> 1) & 1;
}
c->lss_mode = (c->lss_mode & ~(1 << RP_BIT)) | (bit << RP_BIT);
mii_raise_signal_float(c->sig + SIG_LSS_RANDOM, rp, 0);
} else {
rp = (c->head >> 1) & 1;
mii_raise_signal_float(c->sig + SIG_LSS_RANDOM, rp, 1);
}
if ((c->lss_mode & (1 << WRITE_BIT)) && track_id != 0xff) {
uint8_t msb = c->data_register >> 7;
#if 0
printf("WRITE %2d %4d %d : %d LSS State %x mode %x\n",
track_id, byte_index, bit_index, msb,
c->lss_state, c->lss_mode);
#endif
if (!f->tracks[track_id].dirty) {
// printf("DIRTY TRACK %2d \n", track_id);
f->tracks[track_id].dirty = 1;
f->seed_dirty++;
}
f->track_data[track_id][byte_index] &= ~(1 << bit_index);
f->track_data[track_id][byte_index] |= (msb << bit_index);
}
f->bit_position = (f->bit_position + 1) % f->tracks[track_id].bit_count;
}
c->lss_mode = (c->lss_mode & ~(1 << RP_BIT)) | (rp << RP_BIT);
c->lss_mode = (c->lss_mode & ~(1 << QA_BIT)) |
(!!(c->data_register & 0x80) << QA_BIT);
mii_raise_signal(c->sig + SIG_LSS_RP, rp);
mii_raise_signal(c->sig + SIG_LSS_QA,
!!(c->lss_mode & (1 << QA_BIT)));
mii_raise_signal(c->sig + SIG_LSS_WRITE,
!!(c->lss_mode & (1 << Q7_WRITE_BIT)));
mii_raise_signal(c->sig + SIG_LSS_LOAD,
!!(c->lss_mode & (1 << Q6_LOAD_BIT)));
const uint8_t *rom = lss_rom16s[c->lss_mode];
uint8_t cmd = rom[c->lss_state];
uint8_t next = cmd >> 4;
uint8_t action = cmd & 0xF;
mii_raise_signal(c->sig + SIG_LSS_SEQ, c->lss_state);
mii_raise_signal(c->sig + SIG_LSS_CMD, action);
if (action & 0b1000) { // Table 9.3 in Sather's book
switch (action & 0b0011) {
case 1: // SL0/1
c->data_register <<= 1;
c->data_register |= !!(action & 0b0100);
mii_raise_signal(c->sig + SIG_DR, c->data_register);
break;
case 2: // SR
c->data_register = (c->data_register >> 1) |
(!!f->write_protected << 7);
mii_raise_signal(c->sig + SIG_DR, c->data_register);
break;
case 3: {// LD
uint8_t track_id = f->track_id[f->qtrack];
c->data_register = c->write_register;
mii_raise_signal(c->sig + SIG_DR, c->data_register);
f->seed_dirty++;
if (f->heat) {
uint32_t byte_index = f->bit_position >> 3;
@ -460,12 +606,47 @@ _mii_disk2_lss_tick(
}
} else { // CLR
c->data_register = 0;
mii_raise_signal(c->sig + SIG_DR, c->data_register);
}
if ((c->lss_mode & (1 << Q7_WRITE_BIT)) &&
track_id < MII_FLOPPY_TRACK_COUNT) {
// on state 0 and 8 we write a bit...
if ((c->lss_state & 0b0111) == 0) {
uint8_t bit = c->data_register >> 7;
// uint8_t bit = !!(c->lss_state & 0x8);
mii_raise_signal_float(c->sig + SIG_LSS_WB, 1, 0);
if (!f->tracks[track_id].dirty) {
// printf("DIRTY TRACK %2d \n", track_id);
f->tracks[track_id].dirty = 1;
/*
* This little trick allows to have all the track neatly aligned
* on bit zero when formatting a floppy or doing a copy, this helps
* debug quite a bit.
*/
if (f->tracks[track_id].virgin) {
f->tracks[track_id].virgin = 0;
f->bit_position = 0;
// if (track_id == 0)
// _mii_disk2_vcd_debug(c, 1);
}
f->seed_dirty++;
}
f->track_data[track_id][byte_index] &= ~(1 << bit_index);
f->track_data[track_id][byte_index] |= (bit << bit_index);
} else {
mii_raise_signal_float(c->sig + SIG_LSS_WB, 0, 0);
}
}
c->lss_state = next;
// read pulse only last one cycle..
c->lss_mode &= ~(1 << RP_BIT);
if (c->clock >= f->bit_timing) {
c->clock -= f->bit_timing;
f->bit_position = (f->bit_position + 1) % f->tracks[track_id].bit_count;
}
}
#include <fcntl.h>
#include <unistd.h>
static void
_mii_mish_d2(
@ -510,18 +691,35 @@ _mii_mish_d2(
}
// dump a track, specify track number and number of bytes
if (!strcmp(argv[1], "track")) {
mii_card_disk2_t *c = _mish_d2;
mii_floppy_t *f = &c->floppy[sel];
if (argv[2]) {
int track = atoi(argv[2]);
if (track < 0 || track >= MII_FLOPPY_TRACK_COUNT) {
printf("Invalid track %d\n", track);
return;
}
int count = 256;
if (argv[3])
if (argv[3]) {
if (!strcmp(argv[3], "save")) {
// save one binary file in tmp with just that track
uint8_t *data = f->track_data[track];
char *filename;
asprintf(&filename, "/tmp/track_%02d.bin", track);
int fd = open(filename, O_CREAT | O_WRONLY, 0666);
write(fd, data, MII_FLOPPY_DEFAULT_TRACK_SIZE);
close(fd);
printf("Saved track %d to %s\n", track, filename);
free(filename);
return;
}
count = atoi(argv[3]);
mii_card_disk2_t *c = _mish_d2;
mii_floppy_t *f = &c->floppy[sel];
}
uint8_t *data = f->track_data[track];
for (int i = 0; i < count; i += 8) {
uint8_t *line = data + i;
#if 0
#if 1
for (int bi = 0; bi < 8; bi++) {
uint8_t b = line[bi];
for (int bbi = 0; bbi < 8; bbi++) {
@ -536,7 +734,7 @@ _mii_mish_d2(
printf("\n");
}
} else {
printf("track <track 0-36> [count]\n");
printf("track <track 0-34> [count]\n");
}
return;
}
@ -546,6 +744,52 @@ _mii_mish_d2(
f->seed_dirty = f->seed_saved = rand();
return;
}
if (!strcmp(argv[1], "resync")) {
mii_card_disk2_t *c = _mish_d2;
mii_floppy_t *f = &c->floppy[sel];
printf("Resyncing tracks\n");
for (int i = 0; i < MII_FLOPPY_TRACK_COUNT; i++)
mii_floppy_resync_track(f, i, 0);
return;
}
if (!strcmp(argv[1], "map")) {
mii_card_disk2_t *c = _mish_d2;
mii_floppy_t *f = &c->floppy[sel];
printf("Disk map:\n");
for (int i = 0; i < MII_FLOPPY_TRACK_COUNT; i++) {
mii_floppy_track_map_t map = {};
int r = mii_floppy_map_track(f, i, &map, 0);
printf("Track %2d: %7s\n",
i, r == 0 ? "OK" : "Invalid");
if (r != 0) {
printf("Track %d has %5d bits\n", i, f->tracks[i].bit_count);
for (int si = 0; si < 16; si++)
printf("[%2d hs:%4d h:%5d ds:%3d d:%5d]%s",
si, map.sector[si].hsync,
map.sector[si].header,
map.sector[si].dsync,
map.sector[si].data,
si & 1 ? "\n" : " ");
}
}
return;
}
if (!strcmp(argv[1], "trace")) {
if (debug == NULL)
debug = fopen("/tmp/disk2.log", "w");
else {
fclose(debug);
debug = NULL;
}
printf("Debug trace %s\n", debug ? "ON" : "OFF");
return;
}
if (!strcmp(argv[1], "vcd")) {
mii_card_disk2_t *c = _mish_d2;
_mii_disk2_vcd_debug(c, !c->vcd);
return;
}
}
#include "mish.h"
@ -553,6 +797,16 @@ _mii_mish_d2(
MISH_CMD_NAMES(d2, "d2");
MISH_CMD_HELP(d2,
"d2: disk ][ internals",
" <default>: dump status"
" <default>: dump status",
" list: list drives",
" sel [0-1]: select drive",
" wp [0-1]: write protect",
" track <track 0-34> [count]: dump track",
" track <track 0-34> save: save in /tmp/trackXX.bin",
" dirty: mark track as dirty",
" resync: resync all tracks",
" map: show track map",
" trace: toggle debug trace",
" vcd: toggle VCD debug"
);
MII_MISH(d2, _mii_mish_d2);

View File

@ -115,7 +115,7 @@ _mii_ee_command(
res = 0;
}
break;
case MII_SLOT_DRIVE_LOAD:
case MII_SLOT_DRIVE_LOAD: {
const char *filename = param;
mii_dd_file_t *file = NULL;
if (filename && *filename) {
@ -126,7 +126,7 @@ _mii_ee_command(
mii_dd_drive_load(&c->drive[0], file);
c->file = file ? file->map : (uint8_t*)mii_1mb_rom_data;
res = 0;
break;
} break;
}
return res;
}

171
src/drivers/mii_mb.c Normal file
View File

@ -0,0 +1,171 @@
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "mii.h"
#include "mockingboard.h"
typedef struct mii_mb_t {
mii_t * mii;
uint8_t init; // init sequence bits
bool init_done;
uint8_t timer;
struct mb_t *mb;
} mii_mb_t;
static uint64_t
_mii_mb_timer(
mii_t * mii,
void * param )
{
mii_mb_t * mb = param;
mb_clock_t clock = {
.ref_step = 1,
.ts = mii->cpu.total_cycle,
};
// delta is ALWAYS negative or zero here
int32_t delta = mii_timer_get(mii, mb->timer);
uint64_t ret = -delta + 1;
if (mb_io_sync(mb->mb, &clock)) {
// printf("MB Sync IRQ\n");
mii->cpu_state.irq = 1;
}
return ret;
}
static void
mii_mb_start(
mii_mb_t *mb)
{
// mb_io_reset(mb->mb, &clock);
printf("MB Start\n");
mb->init = 0;
mb->init_done = true;
mb->timer = mii_timer_register(mb->mii, _mii_mb_timer, mb, 1, __func__);
}
/*
* this is a protothread, so remember no locals that will survive a yield()
*/
static bool
_mii_mb_romspace_access(
struct mii_bank_t *bank,
void *param,
uint16_t addr,
uint8_t * byte,
bool write)
{
if (!bank) { // TODO: dispose
printf("%s: no bank\n", __func__);
return false;
}
mii_mb_t * mb = param;
// addr &= 0xff;
// if (mb->init_done)
switch (addr & 0xff) {
case MB_CARD_MOCKINGBOARD_ORB1 ... MB_CARD_MOCKINGBOARD_ORB1+0xf:
case MB_CARD_MOCKINGBOARD_ORB2 ... MB_CARD_MOCKINGBOARD_ORB2+0xf:
if (write) {
/*
* Once code has writen to the first registers to initialize them,
* the mockingboard is considered initialized.
* At that point we take over the 'reading' of these addresees,
* so the CARD rom (the mouse) can't read them anymore.
* It *still* should be OK as these address do not overlap the
* mouse ROM.
*/
if ((addr & 0x7f) == 2 && *byte == 0xff) {
if (!mb->init_done)
mii_mb_start(mb);
}
// printf("%s: %s addr %04x byte %02x write %d\n", __func__, bank->name, addr, *byte, write);
mb_io_write(mb->mb, *byte, addr & 0xff);
} else if (mb->init_done) {
mb_io_read(mb->mb, byte, addr & 0xff);
}
// printf("%s: %s addr %04x byte %02x write %d\n", __func__,
// bank->name, addr, *byte, write);
return mb->init_done;
break;
default:
if (!write)
*byte = mii_video_get_vapor(mb->mii);
break;
}
return false;
}
static int
_mii_mb_probe(
mii_t *mii,
uint32_t flags)
{
printf("%s %s\n", __func__, flags & MII_INIT_MOCKINGBOARD ?
"enabled" : "disabled");
// if (!(flags & MII_INIT_MOCKINGBOARD))
// return 0;
return 1;
}
static int
_mii_mb_init(
mii_t * mii,
struct mii_slot_t *slot )
{
printf("%s\n", __func__);
mii_mb_t * mb = calloc(1, sizeof(*mb));
slot->drv_priv = mb;
mb->mii = mii;
mb->mb = mb_alloc();
mb_clock_t clock = {
.ref_step = 1,
.ts = mii->cpu.total_cycle,
};
mb_io_reset(mb->mb, &clock);
uint16_t addr = 0xc100 + (slot->id * 0x100);
mii_mb_start(mb);
mii_bank_install_access_cb(&mii->bank[MII_BANK_CARD_ROM],
_mii_mb_romspace_access, mb, addr >> 8, 0);
return 0;
}
static void
_mii_mb_reset(
mii_t * mii,
struct mii_slot_t *slot )
{
mii_mb_t *mb = slot->drv_priv;
printf("%s\n", __func__);
mb_clock_t clock = {
.ref_step = 1,
.ts = mii->cpu.total_cycle,
};
mb_io_reset(mb->mb, &clock);
}
static uint8_t
_mii_mb_iospace_access(
mii_t * mii, struct mii_slot_t *slot,
uint16_t addr, uint8_t byte, bool write)
{
// mii_mb_t *mb = slot->drv_priv;
if (!write)
byte = mii_video_get_vapor(mii);
return byte;
}
static mii_slot_drv_t _driver = {
.name = "mockingboard",
.desc = "Mockingboard",
// .enable_flag = MII_INIT_MOCKINGBOARD,
.init = _mii_mb_init,
.reset = _mii_mb_reset,
.access = _mii_mb_iospace_access,
// .probe = _mii_mb_probe,
};
MI_DRIVER_REGISTER(_driver);

View File

@ -7,8 +7,6 @@
*
*/
#include "mii.h"
// https://github.com/ivanizag/izapple2/blob/master/cardMouse.go
// https://hackaday.io/project/19925-aiie-an-embedded-apple-e-emulator/log/188017-entry-23-here-mousie-mousie-mousie
// https://github.com/ct6502/apple2ts/blob/main/src/emulator/mouse.ts
@ -114,6 +112,7 @@ _mii_mouse_vbl_handler(
return 1000000 / 60;
mii_bank_t * main = &mii->bank[MII_BANK_MAIN];
// mii_bank_t * sw = &mii->bank[MII_BANK_SW];
uint8_t status = mii_bank_peek(main, MOUSE_STATUS + c->slot_offset);
uint8_t old = status;
@ -169,7 +168,7 @@ _mii_mouse_init(
// Set 8 entrypoints to sofstwitches 2 to 1f
for (int i = 0; i < 14; i++) {
uint8_t base = 0x60 + 0x05 * i;
uint8_t base = 0x30 + 0x05 * i;
data[0x12+i] = base;
data[base+0] = 0x8D; // STA $C0x2
data[base+1] = 0x82 + i + ((slot->id + 1) << 4);
@ -211,12 +210,12 @@ _mii_mouse_access(
if (write) {
byte &= 0xf;
mii_bank_poke(main, MOUSE_MODE + c->slot_offset, byte);
printf("%s: mouse mode %02x\n", __func__, byte);
mii->mouse.enabled = byte & mouseEnabled;
printf("Mouse %s\n", mii->mouse.enabled ? "enabled" : "disabled");
printf(" Interupt: %s\n", byte & mouseIntMoveEnabled ? "enabled" : "disabled");
printf(" Button: %s\n", byte & mouseIntButtonEnabled ? "enabled" : "disabled");
printf(" VBlank: %s\n", byte & mouseIntVBlankEnabled ? "enabled" : "disabled");
printf("%s: mode %02x: %s Move:%d Button:%d VBL:%d\n", __func__,
byte, mii->mouse.enabled ? "ON " : "OFF",
byte & mouseIntMoveEnabled ? 1 : 0,
byte & mouseIntButtonEnabled ? 1 : 0,
byte & mouseIntVBlankEnabled ? 1 : 0);
c->mode = byte;
}
} break;
@ -255,9 +254,9 @@ _mii_mouse_access(
mii->mouse.max_y = mii_bank_peek(main, CLAMP_MAX_LO) |
(mii_bank_peek(main, CLAMP_MAX_HI) << 8);
}
printf("Mouse clamp to %d,%d - %d,%d\n",
mii->mouse.min_x, mii->mouse.min_y,
mii->mouse.max_x, mii->mouse.max_y);
// printf("Mouse clamp to %d,%d - %d,%d\n",
// mii->mouse.min_x, mii->mouse.min_y,
// mii->mouse.max_x, mii->mouse.max_y);
break;
case 8: // home mouse
mii->mouse.x = mii->mouse.min_x;

View File

@ -169,7 +169,7 @@ _mii_nsc_probe(
/* ... A2Desktop requires the NSC to be on the main rom, the source
* claims it probe the slots, but in fact, it doesnt */
mii_bank_install_access_cb(&mii->bank[MII_BANK_ROM],
_mii_nsc_access, nsc, 0xc3, 0);
_mii_nsc_access, nsc, 0xc8, 0);
return 1;
}

View File

@ -36,7 +36,6 @@ extern const unsigned char mii_smartport_rom_data[];
#define MII_SM_DRIVE_COUNT 2
typedef struct mii_card_sm_t {
// struct mii_card_sm_t *next;
mii_dd_t drive[MII_SM_DRIVE_COUNT];
struct mii_slot_t *slot;
} mii_card_sm_t;
@ -79,6 +78,9 @@ _mii_hd_callback(
mii_bank_t * bank = &mii->bank[mii->mem[buffer >> 8].write];
mii->cpu.P.C = mii_dd_read(
&c->drive[unit], bank, buffer, blk, 1) != 0;
// if Prodos is reading a block that happens to be video memory,
// make sure the video driver knows about it
mii_video_OOB_write_check(mii, buffer, 512);
} break;
case 2: {// write block
if (!c->drive[unit].file) {
@ -107,7 +109,7 @@ _mii_sm_callback(
mii_t *mii,
uint8_t trap)
{
printf("%s\n", __func__);
// printf("%s\n", __func__);
int sid = ((mii->cpu.PC >> 8) & 0xf) - 1;
mii_card_sm_t *c = mii->slot[sid].drv_priv;
@ -122,8 +124,8 @@ _mii_sm_callback(
uint8_t spUnit = mii_read_one(mii, spParams + 1);
uint16_t spBuffer = mii_read_word(mii, spParams + 2);
printf("%s cmd %02x params %04x pcount %d unit %02x buffer %04x\n", __func__,
spCommand, spParams, spPCount, spUnit, spBuffer);
// printf("%s cmd %02x params %04x pcount %d unit %02x buffer %04x\n", __func__,
// spCommand, spParams, spPCount, spUnit, spBuffer);
switch (spCommand) {
case 0: { // get status
if (spPCount != 3) {
@ -131,77 +133,110 @@ _mii_sm_callback(
break;
}
uint8_t status = mii_read_one(mii, spParams + 4);
printf("%s: unit %d status %02x \n", __func__, spUnit, status);
// printf("%s: unit %d status %02x \n", __func__, spUnit, status);
uint8_t st = 0x80 | 0x40 | 0x20;
uint32_t bsize = 0;
if (spUnit) spUnit--;
if (spUnit < MII_SM_DRIVE_COUNT && c->drive[spUnit].file) {
st |= 0x10;
bsize = (c->drive[spUnit].file->size + 511) / 512;
}
if (status == 0) {
mii->cpu.P.C = 0;
mii->cpu.A = 0;
/* Apple IIc reference says this ought to be a status byte,
* but practice and A2Desktop says it ought to be a drive
* count, so here goes... */
// mii_write_one(mii, spBuffer++, st);
mii_write_one(mii, spBuffer++, MII_SM_DRIVE_COUNT);
mii_write_one(mii, spBuffer++, bsize);
mii_write_one(mii, spBuffer++, bsize >> 8);
mii_write_one(mii, spBuffer++, bsize >> 16);
} else if (status == 3 && spUnit < MII_SM_DRIVE_COUNT) {
if (spUnit == 0) {
mii_write_one(mii, spBuffer++, MII_SM_DRIVE_COUNT);
mii_write_one(mii, spBuffer++, 0x00);
mii_write_one(mii, spBuffer++, 0x01);
mii_write_one(mii, spBuffer++, 0x13);
} else if (spUnit <= MII_SM_DRIVE_COUNT) {
if (c->drive[spUnit-1].file) {
st |= 0x10;
bsize = (c->drive[spUnit-1].file->size + 511) / 512;
}
mii_write_one(mii, spBuffer++, st);
mii_write_one(mii, spBuffer++, bsize);
mii_write_one(mii, spBuffer++, bsize >> 8);
mii_write_one(mii, spBuffer++, bsize >> 16);
} else {
mii->cpu.P.C = 1;
mii->cpu.A = 0x21; // bad status
}
} else if (status == 3) {
mii->cpu.P.C = 0;
mii_write_one(mii, spBuffer++, st);
mii_write_one(mii, spBuffer++, bsize);
mii_write_one(mii, spBuffer++, bsize >> 8);
mii_write_one(mii, spBuffer++, bsize >> 16);
char dname[17] = "\x8MII HD 0 ";
dname[8] = '0' + spUnit;
for (int i = 0; i < 17; i++)
mii_write_one(mii, spBuffer++, dname[i]);
mii_write_one(mii, spBuffer++, 0x02); // Profile
mii_write_one(mii, spBuffer++, 0x00); // Profile
mii_write_one(mii, spBuffer++, 0x01); // Version
mii_write_one(mii, spBuffer++, 0x13);
mii->cpu.A = 0;
if (spUnit > 0 && spUnit <= MII_SM_DRIVE_COUNT) {
if (c->drive[spUnit-1].file) {
st |= 0x10;
bsize = (c->drive[spUnit-1].file->size + 511) / 512;
}
mii_write_one(mii, spBuffer++, st);
mii_write_one(mii, spBuffer++, bsize);
mii_write_one(mii, spBuffer++, bsize >> 8);
mii_write_one(mii, spBuffer++, bsize >> 16);
char dname[17] = "\x8MII HD 0 ";
dname[8] = '0' + spUnit-1;
for (int i = 0; i < 17; i++)
mii_write_one(mii, spBuffer++, dname[i]);
mii_write_one(mii, spBuffer++, 0x02); // Profile
mii_write_one(mii, spBuffer++, 0x00); // Profile
mii_write_one(mii, spBuffer++, 0x01); // Version
mii_write_one(mii, spBuffer++, 0x13);
} else {
mii->cpu.P.C = 1;
mii->cpu.A = 0x21; // bad status
}
} else {
printf("%s: unit %d bad status %d\n",
__func__, spUnit, status);
mii->cpu.P.C = 1;
mii->cpu.A = 0x21; // bad status
}
} break;
case 1: { // read
mii->cpu.P.C = 0;
mii->cpu.A = 0;
if (spPCount != 3) {
printf("%s: unit %d bad pcount %d\n",
__func__, spUnit, spPCount);
mii->cpu.P.C = 1;
break;
}
if (spUnit >= MII_SM_DRIVE_COUNT) {
printf("%s: unit %d out of range\n",
__func__, spUnit);
if (spUnit == 0 || spUnit >= MII_SM_DRIVE_COUNT) {
printf("%s: unit %d out of range\n", __func__, spUnit);
mii->cpu.P.C = 1;
mii->cpu.A = 0x28;
break;
}
uint32_t blk = mii_read_word(mii, spParams + 3) |
(mii_read_one(mii, spParams + 4) << 16) |
(mii_read_one(mii, spParams + 5) << 24);
printf("%s read block %x\n", __func__, blk);
spUnit--;
uint32_t blk = mii_read_one(mii, spParams + 4) |
(mii_read_one(mii, spParams + 5) << 8) |
(mii_read_one(mii, spParams + 6) << 16);
// printf("%s read block 0x%6x\n", __func__, blk);
if (!c->drive[spUnit].file) {
mii->cpu.P.C = 1;
mii->cpu.A = 0x2f;
break;
}
if (blk >= c->drive[spUnit].file->size / 512) {
printf("%s: block %d out of range\n",
__func__, blk);
mii->cpu.P.C = 1;
mii->cpu.A = 0x2d;
break;
}
mii_bank_t * bank = &mii->bank[mii->mem[spBuffer >> 8].write];
mii->cpu.P.C = mii_dd_read(
&c->drive[spUnit], bank, spBuffer, blk, 1) != 0;
if (mii->cpu.P.C)
mii->cpu.A = 0x2d;
// if Prodos is reading a block that happens to be video memory,
// make sure the video driver knows about it
mii_video_OOB_write_check(mii, spBuffer, 512);
// mii->cpu.P.C = 0;
} break;
case 2: { // write
mii->cpu.P.C = 0;
mii->cpu.A = 0;
if (spPCount != 3) {
printf("%s: unit %d bad pcount %d\n",
__func__, spUnit, spPCount);
@ -212,25 +247,31 @@ _mii_sm_callback(
printf("%s: unit %d out of range\n",
__func__, spUnit);
mii->cpu.P.C = 1;
mii->cpu.A = 0x28;
break;
}
uint32_t blk = mii_read_word(mii, spParams + 3) |
(mii_read_one(mii, spParams + 4) << 16) |
(mii_read_one(mii, spParams + 5) << 24);
printf("%s write block %x\n", __func__, blk);
spUnit--;
uint32_t blk = mii_read_one(mii, spParams + 4) |
(mii_read_one(mii, spParams + 5) << 8) |
(mii_read_one(mii, spParams + 6) << 16);
// printf("%s write block %x\n", __func__, blk);
if (!c->drive[spUnit].file) {
mii->cpu.P.C = 1;
mii->cpu.A = 0x2f;
break;
}
if (blk >= c->drive[spUnit].file->size / 512) {
printf("%s: block %d out of range\n",
__func__, blk);
mii->cpu.P.C = 1;
mii->cpu.A = 0x2d;
break;
}
mii_bank_t * bank = &mii->bank[mii->mem[spBuffer >> 8].read];
mii->cpu.P.C = mii_dd_write(
&c->drive[spUnit], bank, spBuffer, blk, 1) != 0;
if (mii->cpu.P.C)
mii->cpu.A = 0x2d;
} break;
}
}
@ -244,7 +285,7 @@ _mii_sm_init(
c->slot = slot;
slot->drv_priv = c;
printf("%s loading in slot %d\n", __func__, slot->id + 1);
// printf("%s loading in slot %d\n", __func__, slot->id + 1);
uint16_t addr = 0xc100 + (slot->id * 0x100);
mii_bank_write(
&mii->bank[MII_BANK_CARD_ROM],
@ -304,7 +345,7 @@ _mii_sm_command(
res = 0;
}
break;
case MII_SLOT_DRIVE_LOAD ... MII_SLOT_DRIVE_LOAD + MII_SM_DRIVE_COUNT - 1:
case MII_SLOT_DRIVE_LOAD ... MII_SLOT_DRIVE_LOAD + MII_SM_DRIVE_COUNT - 1: {
int drive = cmd - MII_SLOT_DRIVE_LOAD;
const char *filename = param;
mii_dd_file_t *file = NULL;
@ -315,7 +356,7 @@ _mii_sm_command(
}
mii_dd_drive_load(&c->drive[drive], file);
res = 0;
break;
} break;
}
return res;
}

View File

@ -6,36 +6,338 @@
* SPDX-License-Identifier: MIT
*
*/
/*
THIS IS A PLACEHOLDER DO NOT USE
*/
#include "mii.h"
Theory of operation:
There is a thread that is started for any/all cards in the system. Cards
themselves communicate via one 'command' FIFO to start/stop themselves,
which add/remove them from the private list of cards kept by the thread.
#include <errno.h>
#include <stdbool.h>
#include <stdint.h>
The cards attempts not to open the device (and start the thread) until
they are actually used, so it monitors for access from the ROM area,
(that will be happening if PR#x and IN#x are used), OR when the PC
calls into the Cx00-CxFF area.
Once running, the thread will monitor the 'tty' file descriptor for each
and will deal with 2 'data' FIFOs to send and receive data from the 6502.
The SSC driver itself just monitors the FIFO state and update the status
register, and raise IRQs as needed.
*/
/*
git clone https://github.com/colinleroy/a2tools.git
cd a2tools/src/surl-server
sudo apt-get install libcurl4-gnutls-dev libgumbo-dev libpng-dev libjq-dev libsdl-image1.2-dev
make && A2_TTY=/dev/tntX ./surl-server
*/
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <pthread.h>
#include <unistd.h>
#include <signal.h>
#include <string.h>
#include "mii.h"
#include "mii_bank.h"
#include "mii_sw.h"
#include "mii_ssc.h"
#include "fifo_declare.h"
#include "bsd_queue.h"
#define INCBIN_STYLE INCBIN_STYLE_SNAKE
#define INCBIN_PREFIX mii_
#include "incbin.h"
INCBIN(ssc_rom, "roms/mii_rom_scc_3410065a.bin");
#include <termios.h>
#include <pty.h>
#include <fcntl.h>
static const mii_ssc_setconf_t _mii_ssc_default_conf = {
.baud = 9600,
.bits = 8,
.parity = 0,
.stop = 1,
.handshake = 0,
.is_device = 1,
.is_socket = 0,
.is_pty = 0,
.socket_port = 0,
.device = "/dev/tnt0",
};
// SW1-4 SW1 is MSB, switches are inverted ? (0=on, 1=off)
static const int _mii_ssc_to_baud[16] = {
[0] = B1152000, [1] = B50, [2] = B75, [3] = B110,
[4] = B134, [5] = B150, [6] = B300, [7] = B600,
[8] = B1200, [9] = B1800, [10] = B2400,
[12] = B4800, [14] = B9600, [15] = B19200,
};
static const unsigned int _mii_ssc_to_baud_rate[16] = {
[0] = 1152000, [1] = 50, [2] = 75, [3] = 110,
[4] = 134, [5] = 150, [6] = 300, [7] = 600,
[8] = 1200, [9] = 1800, [10] = 2400, [11] = -3600,
[12] = 4800, [13] = -7200, [14] = 9600, [15] = 19200,
};
enum {
SSC_6551_CONTROL_BAUD = 0, // see table
SSC_6551_CONTROL_CLOCK = 4, // always 1
SSC_6551_CONTROL_WLEN = 5, // 0=8 bits, 1=7 bits, 2=6 bits, 3=5 bits
SSC_6551_CONTROL_STOP = 7, // 0 = 1 stop bit, 1 = 2 stop bits
SSC_6551_CONTROL_RESET = 0,
SSC_6551_COMMAND_DTR = 0, // 0=Disable Receiver (DTR), 1=Enable
SSC_6551_COMMAND_IRQ_R = 1, // 0=IRQ Enabled, 1=IRQ Disabled
// 0:IRQ_TX=0 + RTS=1
// 1:IRQ_TX=1 + RTS=0
// 2:IRQ_TX=0 + RTS=0
// 3:IRQ_TX=0 + RTS=0 + BRK
SSC_6551_COMMAND_IRQ_T = 2,
SSC_6551_COMMAND_ECHO = 4, // 0=off, 1=on
SSC_6551_COMMAND_PARITY = 5, // None, Odd, Even, Mark, Space
SSC_6551_COMMAND_RESET = (1 << SSC_6551_COMMAND_IRQ_R),
SSC_6551_PARITY_ERROR = 0,
SSC_6551_FRAMING_ERROR = 1,
SSC_6551_OVERRUN = 2,
SSC_6551_RX_FULL = 3,
SSC_6551_TX_EMPTY = 4,
SSC_6551_DCD = 5,
SSC_6551_DSR = 6,
SSC_6551_IRQ = 7,
SSC_6551_STATUS_RESET = (1 << SSC_6551_TX_EMPTY),
};
enum {
SSC_SW2_STOPBITS = 1 << 7,
SSC_SW2_DATABITS = 1 << 6,
SSC_SW2_IRQEN = 1 << 0,
};
// SW2-1 is stop bits OFF = Two, ON = One (inverted)
static const unsigned int _mii_ssc_to_stop[2] = {
[0] = 0, [1] = CSTOPB,
};
// SW2-2 is data bits
static const unsigned int _mii_ssc_to_bits[4] = {
[0] = CS8, [1] = CS7, [2] = CS6, [3] = CS5,
};
static const int _mii_scc_to_bits_count[4] = {
[0] = 8, [1] = 7, [2] = 6, [3] = 5,
};
// SW2-3-4 is parity
static const unsigned int _mii_ssc_to_parity[4] = {
[0] = 0, [1] = PARODD, [2] = PARENB, [3] = PARENB|PARODD,
};
enum {
MII_SSC_STATE_INIT = 0,
MII_SSC_STATE_START,
MII_SSC_STATE_RUNNING,
MII_SSC_STATE_STOP,
MII_SSC_STATE_STOPPED,
MII_THREAD_TERMINATE,
};
struct mii_card_ssc_t;
typedef struct mii_ssc_cmd_t {
int cmd;
union {
struct mii_card_ssc_t * card;
};
} mii_ssc_cmd_t;
DECLARE_FIFO(mii_ssc_cmd_t, mii_ssc_cmd_fifo, 8);
DEFINE_FIFO(mii_ssc_cmd_t, mii_ssc_cmd_fifo);
DECLARE_FIFO(uint8_t, mii_ssc_fifo, 16);
DEFINE_FIFO(uint8_t, mii_ssc_fifo);
typedef struct mii_card_ssc_t {
// queued when first allocated, to keep a list of all cards
STAILQ_ENTRY(mii_card_ssc_t) self;
// queued when started, for the thread
STAILQ_ENTRY(mii_card_ssc_t) started;
struct mii_slot_t * slot;
struct mii_bank_t * rom;
mii_t * mii;
uint8_t slot_offset;
mii_ssc_setconf_t conf;
int state; // current state, MII_SSC_STATE_*
char tty_path[128];
int tty_fd; // <= 0 is not opened yet
char human_config[32];
// global counter of bytes sent/received. No functional use
uint32_t total_rx, total_tx;
uint8_t timer_check;
uint32_t timer_delay;
mii_ssc_fifo_t rx,tx;
// 6551 registers
uint8_t dipsw1, dipsw2, control, command, status;
} mii_card_ssc_t;
STAILQ_HEAD(, mii_card_ssc_t)
_mii_card_ssc_slots = STAILQ_HEAD_INITIALIZER(_mii_card_ssc_slots);
/*
* These bits are only meant to communicate with the thread
*/
STAILQ_HEAD(, mii_card_ssc_t)
_mii_card_ssc_started = STAILQ_HEAD_INITIALIZER(_mii_card_ssc_started);
pthread_t _mii_ssc_thread_id = 0;
mii_ssc_cmd_fifo_t _mii_ssc_cmd = {};
static int
_mii_scc_set_conf(
mii_card_ssc_t *c,
const mii_ssc_setconf_t *conf,
int re_open);
static void*
_mii_ssc_thread(
void *param)
{
printf("%s: start\n", __func__);
// ignore the signal, we use it to wake up the thread
sigaction(SIGUSR1, &(struct sigaction){
.sa_handler = SIG_IGN,
.sa_flags = SA_RESTART,
}, NULL);
do {
/*
* Get commands from the MII running thread. Add/remove cards
* from the 'running' list, and a TERMINATE to kill the thread
*/
while (!mii_ssc_cmd_fifo_isempty(&_mii_ssc_cmd)) {
mii_ssc_cmd_t cmd = mii_ssc_cmd_fifo_read(&_mii_ssc_cmd);
switch (cmd.cmd) {
case MII_SSC_STATE_START: {
mii_card_ssc_t *c = cmd.card;
printf("%s: start slot %d\n", __func__, c->slot->id);
STAILQ_INSERT_TAIL(&_mii_card_ssc_started, c, self);
c->state = MII_SSC_STATE_RUNNING;
} break;
case MII_SSC_STATE_STOP: {
mii_card_ssc_t *c = cmd.card;
printf("%s: stop slot %d\n", __func__, c->slot->id);
STAILQ_REMOVE(&_mii_card_ssc_started, c, mii_card_ssc_t, self);
c->state = MII_SSC_STATE_STOPPED;
} break;
case MII_THREAD_TERMINATE:
printf("%s: terminate\n", __func__);
return NULL;
}
}
/* here we use select. This is not optimal on linux, but it is portable
to other OSes -- perhaps I'll add an epoll() version later */
fd_set rfds, wfds;
FD_ZERO(&rfds);
FD_ZERO(&wfds);
int maxfd = 0;
mii_card_ssc_t *c = NULL, *safe;
STAILQ_FOREACH_SAFE(c, &_mii_card_ssc_started, self, safe) {
// guy might be being reconfigured, or perhaps had an error
if (c->tty_fd < 0)
continue;
if (!mii_ssc_fifo_isempty(&c->tx)) {
FD_SET(c->tty_fd, &wfds);
if (c->tty_fd > maxfd)
maxfd = c->tty_fd;
}
if (!mii_ssc_fifo_isfull(&c->rx)) {
FD_SET(c->tty_fd, &rfds);
if (c->tty_fd > maxfd)
maxfd = c->tty_fd;
}
}
struct timeval tv = { .tv_sec = 0, .tv_usec = 1000 };
int res = select(maxfd + 1, &rfds, &wfds, NULL, &tv);
if (res < 0) {
// there are OK errors, we just ignore them
if (errno == EINTR || errno == EAGAIN || errno == EWOULDBLOCK)
continue;
printf("%s ssc select: %s\n", __func__, strerror(errno));
break;
}
if (res == 0) // timeout
continue;
STAILQ_FOREACH(c, &_mii_card_ssc_started, self) {
/* Here we know the read fifo isn't full, otherwise we wouldn'ty
have asked for more data.
See what space we have in the fifo, try reading as much as that,
and push it to the FIFO */
if (FD_ISSET(c->tty_fd, &rfds)) {
uint8_t buf[mii_ssc_cmd_fifo_fifo_size];
int max = mii_ssc_fifo_get_write_size(&c->rx);
int res = read(c->tty_fd, buf, max);
if (res < 0) {
if (errno == EINTR || errno == EAGAIN || errno == EWOULDBLOCK)
break;
c->tty_fd = -1;
printf("%s ssc read: %s\n", __func__, strerror(errno));
break;
}
for (int i = 0; i < res; i++)
mii_ssc_fifo_write(&c->rx, buf[i]);
}
/* here as well, this wouldn't be set if we hadn't got stuff
to send -- see what's in the fifo, 'peek' the bytes into
an aligned buffer, try to write it all, and then *actually*
remove them (read) the one that were sent from the fifo */
if (FD_ISSET(c->tty_fd, &wfds)) {
uint8_t buf[mii_ssc_cmd_fifo_fifo_size];
int max = mii_ssc_fifo_get_read_size(&c->tx);
for (int i = 0; i < max; i++)
buf[i] = mii_ssc_fifo_read_at(&c->tx, i);
res = write(c->tty_fd, buf, max);
if (res < 0) {
if (errno == EINTR || errno == EAGAIN || errno == EWOULDBLOCK)
break;
printf("%s ssc write: %s\n", __func__, strerror(errno));
break;
}
// flush what we've just written
while (res--)
mii_ssc_fifo_read(&c->tx);
}
}
} while (1);
return NULL;
}
static void
_mii_ssc_thread_start(
mii_card_ssc_t *c)
{
if (c->state > MII_SSC_STATE_INIT && c->state < MII_SSC_STATE_STOP)
return;
if (c->tty_fd < 0) {
printf("%s TTY not open, skip\n", __func__);
return;
}
c->state = MII_SSC_STATE_START;
mii_ssc_cmd_t cmd = { .cmd = MII_SSC_STATE_START, .card = c };
mii_ssc_cmd_fifo_write(&_mii_ssc_cmd, cmd);
// start timer that'll check out card status
mii_timer_set(c->mii, c->timer_check, c->timer_delay);
// start, or kick the thread awake
if (!_mii_ssc_thread_id) {
printf("%s: starting thread\n", __func__);
pthread_create(&_mii_ssc_thread_id, NULL, _mii_ssc_thread, NULL);
} else {
pthread_kill(_mii_ssc_thread_id, SIGUSR1);
}
}
/*
* This is called when the CPU touches the CX00-CXFF ROM area, and we
* need to install the secondary part of the ROM.
* Also, if the access was from the ROM, we start the card as there's no
* other way to know when the card is started -- we don't want to create
* a thread fro SSC if the card is being ignored by the Apple IIe
*/
static bool
_mii_ssc_select(
struct mii_bank_t *bank,
@ -44,17 +346,182 @@ _mii_ssc_select(
uint8_t * byte,
bool write)
{
if (bank == NULL) // this is normal, called on dispose
return false;
mii_card_ssc_t *c = param;
printf("%s selected:%d\n", __func__, c->slot->aux_rom_selected);
if (c->slot->aux_rom_selected)
return false;
uint16_t pc = c->mii->cpu.PC;
printf("SSC%d SELECT auxrom PC:%04x\n", c->slot->id+1, pc);
/* Supports when the ROM starts prodding into the ROM */
if (c->state != MII_SSC_STATE_RUNNING) {
printf("SSC%d: start card from ROM poke? (PC $%04x)?\n",
c->slot->id+1, pc);
if ((pc & 0xff00) == (0xc100 + (c->slot->id << 8)) ||
(pc >> 12) >= 0xc) {
_mii_scc_set_conf(c, &c->conf, 1);
_mii_ssc_thread_start(c);
}
}
mii_bank_write(c->rom, 0xc800, mii_ssc_rom_data, 2048);
c->slot->aux_rom_selected = true;
SW_SETSTATE(c->mii, SLOTAUXROM, 1);
c->mii->mem_dirty = true;
return false;
}
/* Called a some sort of proportional number of cycles related to the
baudrate to check the FIFOs and update the status/raise IRQs.
this doesn't have to be exact, it just have to be often enough to
not miss any data.
*/
static uint64_t
_mii_ssc_timer_cb(
mii_t * mii,
void * param )
{
mii_card_ssc_t *c = param;
// stop timer
if (c->state != MII_SSC_STATE_RUNNING)
return 0;
// check the FIFOs -- not technically 'true' we raise an IRQ as soon as
// theres some bytes to proceed, but we'll do it here for simplicity
uint8_t rx_full = !mii_ssc_fifo_isempty(&c->rx);
// what it really mean is 'there room for more data', not 'full'
uint8_t tx_empty = !mii_ssc_fifo_isfull(&c->tx);
uint8_t old = c->status;
c->status = (c->status & ~(1 << SSC_6551_RX_FULL)) |
(rx_full << SSC_6551_RX_FULL);
c->status = (c->status & ~(1 << SSC_6551_TX_EMPTY)) |
(tx_empty << SSC_6551_TX_EMPTY);
uint8_t irq = 0;//(c->status & (1 << SSC_6551_IRQ));
uint8_t t_irqen = ((c->command >> SSC_6551_COMMAND_IRQ_T) & 3) == 1;
uint8_t r_irqen = !(c->command & (1 << SSC_6551_COMMAND_IRQ_R));
if (old != c->status)
printf("SSC%d New Status %08b RX:%2d TX:%2d t_irqen:%d r_irqen:%d\n",
c->slot->id+1, c->status,
mii_ssc_fifo_get_read_size(&c->rx), mii_ssc_fifo_get_write_size(&c->tx),
t_irqen, r_irqen);
// we set the IRQ flag even if the real IRQs are disabled.
// rising edge triggers the IRQR
if (!irq && rx_full) {
// raise the IRQ
if (r_irqen) {
irq = 1;
printf("SSC%d: IRQ RX\n", c->slot->id+1);
mii->cpu_state.irq = 1;
}
}
if (!irq && (tx_empty)) {
// raise the IRQ
if (t_irqen) {
irq = 1;
printf("SSC%d: IRQ TX\n", c->slot->id+1);
mii->cpu_state.irq = 1;
}
}
if (irq)
c->status |= 1 << SSC_6551_IRQ;
return c->timer_delay;
}
static int
_mii_scc_set_conf(
mii_card_ssc_t *c,
const mii_ssc_setconf_t *conf,
int re_open)
{
if (conf == NULL)
conf = &_mii_ssc_default_conf;
if (!re_open && strcmp(c->conf.device, conf->device) == 0 &&
c->conf.baud == conf->baud &&
c->conf.bits == conf->bits &&
c->conf.parity == conf->parity &&
c->conf.stop == conf->stop &&
c->conf.handshake == conf->handshake &&
c->conf.is_device == conf->is_device &&
c->conf.is_socket == conf->is_socket &&
c->conf.is_pty == conf->is_pty &&
c->conf.socket_port == conf->socket_port)
return 0;
if (re_open || strcmp(c->conf.device, conf->device) != 0) {
if (c->tty_fd > 0) {
int tty = c->tty_fd;
c->tty_fd = -1;
// this will wake up the thread as well
close(tty);
}
}
c->conf = *conf;
strcpy(c->tty_path, conf->device);
if (c->tty_fd < 0) {
int new_fd = -1;
if (conf->is_pty) {
int master = 0;
int res = openpty(&master, &new_fd, c->tty_path, NULL, NULL);
if (res < 0) {
printf("SSC%d openpty: %s\n", c->slot->id+1, strerror(errno));
return -1;
}
} else {
int res = open(c->tty_path, O_RDWR | O_NOCTTY | O_NONBLOCK);
if (res < 0) {
printf("SSC%d open(%s): %s\n", c->slot->id+1,
c->tty_path, strerror(errno));
return -1;
}
new_fd = res;
}
// set non-blocking mode
int flags = fcntl(new_fd, F_GETFL, 0);
fcntl(new_fd, F_SETFL, flags | O_NONBLOCK);
c->tty_fd = new_fd;
}
if (c->tty_fd < 0) {
printf("SSC%d: %s TTY not open, skip\n", c->slot->id+1, __func__);
return -1;
}
// get current terminal settings
struct termios tio;
tcgetattr(c->tty_fd, &tio);
// set raw mode
cfmakeraw(&tio);
c->human_config[0] = 0;
// set speed
for (int i = 0; i < 16; i++) {
if (_mii_ssc_to_baud_rate[i] == conf->baud) {
c->dipsw1 = 0x80 | i;
cfsetospeed(&tio, _mii_ssc_to_baud[i]);
cfsetispeed(&tio, _mii_ssc_to_baud[i]);
sprintf(c->human_config, "Baud:%d ", conf->baud);
break;
}
}
// set 8N1
tio.c_cflag = (tio.c_cflag & ~PARENB) | _mii_ssc_to_parity[conf->parity];
tio.c_cflag = (tio.c_cflag & ~CSTOPB) | _mii_ssc_to_stop[conf->stop];
tio.c_cflag = (tio.c_cflag & ~CSIZE);
tio.c_cflag |= _mii_ssc_to_bits[conf->bits];
sprintf(c->human_config + strlen(c->human_config), "%d",
_mii_scc_to_bits_count[conf->bits]);
static const char *parity = "noeb";
sprintf(c->human_config + strlen(c->human_config), "%c%c",
parity[conf->parity], conf->stop ? '2' : '1');
// Hardware Handshake
tio.c_cflag = (tio.c_cflag & ~CRTSCTS) | (conf->handshake ? CRTSCTS : 0);
// set the new settings
tcsetattr(c->tty_fd, TCSANOW, &tio);
c->dipsw1 = 0x80 | conf->baud;
c->dipsw2 = SSC_SW2_IRQEN;
c->control = 0;
mii_ssc_fifo_reset(&c->rx);
mii_ssc_fifo_reset(&c->tx);
return 0;
}
static int
_mii_ssc_init(
mii_t * mii,
@ -65,9 +532,6 @@ _mii_ssc_init(
slot->drv_priv = c;
c->mii = mii;
printf("%s: THIS IS A PLACEHOLDER DO NOT USE\n", __func__);
printf("%s loading in slot %d\n", __func__, slot->id + 1);
c->slot_offset = slot->id + 1 + 0xc0;
uint16_t addr = 0xc100 + (slot->id * 0x100);
@ -81,6 +545,28 @@ _mii_ssc_init(
mii_bank_install_access_cb(c->rom,
_mii_ssc_select, c, addr >> 8, addr >> 8);
/*
* And this is the timer that will check the status of FIFOs and update
* the status of the card, and raise IRQs if needed
*/
char name[32];
snprintf(name, sizeof(name), "SSC %d", slot->id+1);
c->timer_check = mii_timer_register(mii,
_mii_ssc_timer_cb, c, 0, strdup(name));
// fastest speed we could get to?
c->timer_delay = 11520;
c->tty_fd = -1;
STAILQ_INSERT_TAIL(&_mii_card_ssc_slots, c, self);
c->dipsw1 = 0x80 | 14; // communication mode, 9600
// in case progs read that to decide to use IRQs or not
c->dipsw2 = SSC_SW2_IRQEN;
c->state = MII_SSC_STATE_INIT;
c->status = SSC_6551_STATUS_RESET;
c->command = SSC_6551_COMMAND_RESET;
c->control = 0;
_mii_scc_set_conf(c, &c->conf, 1);
return 0;
}
@ -90,10 +576,86 @@ _mii_ssc_dispose(
struct mii_slot_t *slot )
{
mii_card_ssc_t *c = slot->drv_priv;
STAILQ_REMOVE(&_mii_card_ssc_slots, c, mii_card_ssc_t, self);
if (c->state == MII_SSC_STATE_RUNNING) {
mii_ssc_cmd_t cmd = { .cmd = MII_SSC_STATE_STOP, .card = c };
mii_ssc_cmd_fifo_write(&_mii_ssc_cmd, cmd);
pthread_kill(_mii_ssc_thread_id, SIGUSR1);
while (c->state == MII_SSC_STATE_RUNNING)
usleep(1000);
printf("SSC%d: stopped\n", c->slot->id+1);
}
if (STAILQ_FIRST(&_mii_card_ssc_slots) == NULL && _mii_ssc_thread_id) {
printf("SSC%d: stopping thread\n", c->slot->id+1);
pthread_t id = _mii_ssc_thread_id;
_mii_ssc_thread_id = 0;
mii_ssc_cmd_t cmd = { .cmd = MII_THREAD_TERMINATE };
mii_ssc_cmd_fifo_write(&_mii_ssc_cmd, cmd);
pthread_kill(id, SIGUSR1);
pthread_join(id, NULL);
printf("SSC%d: thread stopped\n", c->slot->id+1);
}
free(c);
slot->drv_priv = NULL;
}
static void
_mii_ssc_command_set(
mii_card_ssc_t *c,
uint8_t byte)
{
mii_t * mii = c->mii;
if (!(c->command & (1 << SSC_6551_COMMAND_DTR)) &&
(byte & (1 << SSC_6551_COMMAND_DTR))) {
_mii_scc_set_conf(c, &c->conf, 1);
_mii_ssc_thread_start(c);
}
if (c->tty_fd < 0) {
printf("SSC%d: %s TTY not open, skip\n", c->slot->id+1, __func__);
return;
}
/* This triggers the IRQ if it enabled when there is a IRQ flag on,
* this make it behave more like a 'level' IRQ instead of an edge IRQ
*/
if ((c->command & (1 << SSC_6551_COMMAND_IRQ_R)) &&
!(byte & (1 << SSC_6551_COMMAND_IRQ_R))) {
if (c->status & (1 << SSC_6551_IRQ))
mii->cpu_state.irq = 1;
}
int status;
if (ioctl(c->tty_fd, TIOCMGET, &status) == -1) {
printf("SSC%d: DTR/RTS: %s\n", c->slot->id+1, strerror(errno));
}
int old = status;
status = (status & ~TIOCM_DTR) |
((byte & (1 << SSC_6551_COMMAND_DTR)) ? TIOCM_DTR : 0);
switch ((byte >> SSC_6551_COMMAND_IRQ_T) & 3) {
case 0: // IRQ_TX=0 + RTS=1
status |= TIOCM_RTS;
break;
case 1: // IRQ_TX=1 + RTS=0
status &= ~TIOCM_RTS;
break;
case 2: // IRQ_TX=0 + RTS=0
status &= ~TIOCM_RTS;
break;
case 3: // IRQ_TX=0 + RTS=0 + BRK
status &= ~TIOCM_RTS;
break;
}
if (old != status) {
printf("%s%d: $%04x DTR %d RTS %d\n", __func__, c->slot->id+1,
c->mii->cpu.PC,
(status & TIOCM_DTR) ? 1 : 0,
(status & TIOCM_RTS) ? 1 : 0); // 0=on, 1=off
if (ioctl(c->tty_fd, TIOCMSET, &status) == -1) {
printf("SSC%d: DTR/RTS: %s\n", c->slot->id+1, strerror(errno));
}
}
c->command = byte;
}
static uint8_t
_mii_ssc_access(
mii_t * mii,
@ -102,17 +664,121 @@ _mii_ssc_access(
uint8_t byte,
bool write)
{
// mii_card_ssc_t *c = slot->drv_priv;
mii_card_ssc_t *c = slot->drv_priv;
uint8_t res = 0;
int psw = addr & 0x0F;
// mii_bank_t * main = &mii->bank[MII_BANK_MAIN];
switch (psw) {
case 0x1: // DIPSW1
if (!write) {
printf("%s%d: $%04x read DIPSW1 : %02x\n",
__func__, slot->id+1, mii->cpu.PC, c->dipsw1);
res = c->dipsw1;
/* this handle access by the ROM via PR#x and IN#x */
if (c->state == MII_SSC_STATE_INIT &&
(mii->cpu.PC & 0xff00) == 0xcb00)
_mii_ssc_thread_start(c);
}
break;
case 0x2: // DIPSW2
if (!write) {
printf("%s%d: $%04x read DIPSW2 : %02x\n",
__func__, slot->id+1, mii->cpu.PC, c->dipsw2);
res = c->dipsw2;
}
break;
case 0x8: { // TD/RD
if (c->state != MII_SSC_STATE_RUNNING)
break;
if (write) {
bool tx_empty = mii_ssc_fifo_isempty(&c->tx);
// printf("%s: write %02x '%c'\n", __func__,
// byte, byte <= ' ' ? '.' : byte);
c->total_tx++;
mii_ssc_fifo_write(&c->tx, byte);
if (tx_empty) // wake thread if it's sleeping
pthread_kill(_mii_ssc_thread_id, SIGUSR1);
bool isfull = mii_ssc_fifo_isfull(&c->tx);
if (isfull) {
c->status &= ~(1 << SSC_6551_TX_EMPTY);
}
} else {
if (mii_ssc_fifo_isempty(&c->rx)) {
res = 0;
} else {
c->total_rx++;
bool wasfull = mii_ssc_fifo_isfull(&c->rx);
res = mii_ssc_fifo_read(&c->rx);
bool isempty = mii_ssc_fifo_isempty(&c->rx);
if (isempty) {
c->status &= ~(1 << SSC_6551_RX_FULL);
} else {
if (wasfull) // wake thread to read more
pthread_kill(_mii_ssc_thread_id, SIGUSR1);
// send another irq?
uint8_t r_irqen =
!(c->command & (1 << SSC_6551_COMMAND_IRQ_R));
if (r_irqen) {
mii->cpu_state.irq = 1;
}
}
}
}
} break;
case 0x9: {// STATUS
if (write) {
printf("SSC%d: RESET requesdt\n",c->slot->id+1);
_mii_ssc_command_set(c, 0x10);
break;
}
res = c->status;
// if it was set before, clear it.
c->status &= ~(1 << SSC_6551_IRQ);
} break;
case 0xa: {// COMMAND
if (!write) {
res = c->command;
break;
}
_mii_ssc_command_set(c, byte);
} break;
case 0xb: { // CONTROL
if (!write) {
res = c->control;
break;
}
c->control = byte;
struct termios tio;
tcgetattr(c->tty_fd, &tio);
// Update speed
int baud = _mii_ssc_to_baud[c->control & 0x0F];
cfsetospeed(&tio, baud);
cfsetispeed(&tio, baud);
// Update stop bits bit 7: 0 = 1 stop bit, 1 = 2 stop bits
tio.c_cflag &= ~CSTOPB;
tio.c_cflag |= _mii_ssc_to_stop[(c->control >> 7) & 1];
// Update data bits bit 5-6 0=8 bits, 1=7 bits, 2=6 bits, 3=5 bits
tio.c_cflag &= ~CSIZE;
tio.c_cflag |= _mii_ssc_to_bits[(c->control >> 6) & 3];
// parity are in c->command, bits 5-7,
// 0=None, 1=Odd, 2=Even, 3=Mark, 4=Space
tio.c_cflag &= ~PARENB;
tio.c_cflag &= ~PARODD;
tio.c_cflag |= _mii_ssc_to_parity[(c->command >> 5) & 3];
tcsetattr(c->tty_fd, TCSANOW, &tio);
printf("SSC%d: set %02x baud %d stop %d bits %d parity %d\n",
c->slot->id+1, byte,
_mii_ssc_to_baud_rate[c->control & 0x0F],
(c->control >> 7) & 1 ? 2 : 1,
_mii_scc_to_bits_count[(c->control >> 5) & 3],
(c->command >> 5) & 3);
} break;
default:
printf("%s PC:%04x addr %04x %02x wr:%d\n", __func__,
mii->cpu.PC, addr, byte, write);
// printf("%s PC:%04x addr %04x %02x wr:%d\n", __func__,
// mii->cpu.PC, addr, byte, write);
break;
}
return 0;
return res;
}
static int
@ -126,9 +792,11 @@ _mii_ssc_command(
int res = -1;
switch (cmd) {
case MII_SLOT_SSC_SET_TTY: {
const char * tty = param;
printf("%s: set tty %s\n", __func__, tty);
res = 0;
const mii_ssc_setconf_t * conf = param;
mii_card_ssc_t *c = slot->drv_priv;
res = _mii_scc_set_conf(c, conf, 0);
printf("SSC%d: set tty %s: %s\n",
slot->id+1, conf->device, c->human_config);
} break;
}
return res;
@ -143,3 +811,41 @@ static mii_slot_drv_t _driver = {
.command = _mii_ssc_command,
};
MI_DRIVER_REGISTER(_driver);
#include "mish.h"
static void
_mii_mish_ssc(
void * param,
int argc,
const char * argv[])
{
if (!argv[1] || !strcmp(argv[1], "status")) {
mii_card_ssc_t *c;
printf("SSC: cards:\n");
STAILQ_FOREACH(c, &_mii_card_ssc_slots, self) {
printf("SSC %d: %s FD: %2d path:%s %s\n", c->slot->id+1,
c->state == MII_SSC_STATE_RUNNING ? "running" : "stopped",
c->tty_fd, c->tty_path, c->human_config);
// print FIFO status, fd status, registers etc
printf(" RX: %2d/%2d TX: %2d/%2d -- total rx:%6d tx:%6d\n",
mii_ssc_fifo_get_read_size(&c->rx),
mii_ssc_fifo_get_write_size(&c->rx),
mii_ssc_fifo_get_read_size(&c->tx),
mii_ssc_fifo_get_write_size(&c->tx),
c->total_rx, c->total_tx);
printf(" DIPSW1: %08b DIPSW2: %08b\n", c->dipsw1, c->dipsw2);
printf(" CONTROL: %08b COMMAND: %08b STATUS: %08b\n",
c->control, c->command, c->status);
}
return;
}
}
MISH_CMD_NAMES(_ssc, "ssc");
MISH_CMD_HELP(_ssc,
"ssc: Super Serial internals",
" <default>: dump status"
);
MII_MISH(_ssc, _mii_mish_ssc);

23
src/drivers/mii_ssc.h Normal file
View File

@ -0,0 +1,23 @@
/*
* mii_ssc.h
*
* Copyright (C) 2023 Michel Pollet <buserror@gmail.com>
*
* SPDX-License-Identifier: MIT
*/
#pragma once
/*
* This is mostly a duplicate from the UI one in mii_mui_settings.h, but it is
* part of the way we decouple the UI from the emulator, so we can test the UI
* without having to link against the emulator.
*/
// this is to be used with MII_SLOT_SSC_SET_TTY and mii_slot_command()
typedef struct mii_ssc_setconf_t {
unsigned int baud, bits : 4, parity : 4, stop : 4, handshake : 4,
is_device : 1, is_socket : 1, is_pty : 1;
unsigned socket_port;
char device[256];
} mii_ssc_setconf_t;

View File

@ -31,21 +31,21 @@ _mii_titan_access(
{
mii_t *mii = param;
bool res = false;
mii_bank_t *main = &mii->bank[MII_BANK_MAIN];
mii_bank_t *sw = &mii->bank[MII_BANK_SW];
if (write) {
printf("titan: write %02x to %04x\n", *byte, addr);
switch (*byte) {
case 5:
mii->speed = 3.58;
mii_bank_poke(main, 0xc086, *byte);
mii->speed = MII_SPEED_TITAN;
mii_bank_poke(sw, 0xc086, *byte);
break;
case 1:
mii_bank_poke(main, 0xc086, *byte);
mii->speed = 1;
mii_bank_poke(sw, 0xc086, *byte);
mii->speed = MII_SPEED_NTSC;
break;
case 0xa: // supposed to lock it too...
mii_bank_poke(main, 0xc086, *byte);
mii->speed = 1;
mii_bank_poke(sw, 0xc086, *byte);
mii->speed = MII_SPEED_NTSC;
break;
default:
printf("titan: unknown speed %02x\n", *byte);

1217
src/drivers/mockingboard.c Normal file

File diff suppressed because it is too large Load Diff

141
src/drivers/mockingboard.h Normal file
View File

@ -0,0 +1,141 @@
/*
* mockingboard.h
* This is a straigth derivative of Clemens IIgs emulator mockingboard
* emulation code. The original code is available at:
* https://github.com/samkusin/clemens_iigs
*
* The original code is also licensed under the MIT License.
* SPDX-License-Identifier: MIT
*
*/
#pragma once
#include <stdint.h>
typedef uint64_t mb_clocks_time_t;
typedef uint32_t mb_clocks_t;
/* Typically this is passed around as the current time for the machine
and is guaranteed to be shared between machine and any external cards
based on the ref_step.
For example, MMIO clocks use the Mega2 reference step
*/
typedef struct mb_clock_t {
mb_clocks_time_t ts;
mb_clocks_t ref_step;
} mb_clock_t;
struct mb_t;
/** A bit confusing and created to avoid floating point math whenever possible
* (whether this was a good choice given modern architectures... ?)
*
* Used for calculating our system clock. These values are relative to each other.
*
* The clocks per mega2 cycle (PHI0) value will always be the largest.
* Yet since most time calculations in the emulator are done with fixed point-like
* math, the aim is to keep the clocks count per cycle high enough for fixed
* math to work with uint 32-bit numbers.
*
* SO DON'T CHANGE THESE UNLESS ALL DEPENDENT DEFINE/CALCULATIONS THAT THESE
* VALUES TRICKLE DOWN REMAIN VALID. IF YOU DO, TEST *EVERYTHING* (IWM,
* DIAGNOSTICS)
*
* Based on this, care should be taken when attempting to emulate a 8mhz machine
* in the future - though most I/O is performed using PHI0 cycles.
*
* If you divide the MB_CLOCKS_PHI0_CYCLE by the MB_CLOCKS_PHI2_FAST_CYCLE
* the value will be the effective maximum clock speed in Mhz of the CPU.
*
* Ref: https://www.kansasfest.org/wp-content/uploads/2011-krue-fpi.pdf
*/
#define MB_CLOCKS_14MHZ_CYCLE 200U // 14.318 Mhz
#define MB_CLOCKS_PHI0_CYCLE (MB_CLOCKS_14MHZ_CYCLE * 14) // 1.023 Mhz with stretch
// IMPORTANT! This is rounded from 69.8ns - which when scaling up to PHI0 translates
// to 977.7 ns without rounding. Due to the stretch cycle, this
// effectively rounds up to 980ns, which is really what most system
// timings rely on So, rounding up. Bonne chance.
#define MB_14MHZ_CYCLE_NS 70U
#define MB_MEGA2_CYCLES_PER_SECOND 1023000U
/* these are here for references - the actual functions are determined
by which bits in the address register are set on io_read and io_write
*/
#define MB_CARD_MOCKINGBOARD_ORB1 0x00
#define MB_CARD_MOCKINGBOARD_ORA1 0x01
#define MB_CARD_MOCKINGBOARD_DDRB1 0x02
#define MB_CARD_MOCKINGBOARD_DDRA1 0x03
#define MB_CARD_MOCKINGBOARD_ORB2 0x80
#define MB_CARD_MOCKINGBOARD_ORA2 0x81
#define MB_CARD_MOCKINGBOARD_DDRB2 0x82
#define MB_CARD_MOCKINGBOARD_DDRA2 0x83
/** All mmio memory operations can have this option - both onboard and
* card operations
*/
#define MB_OP_IO_NO_OP 0x01
#define MB_IS_IO_NO_OP(_flags_) (((_flags_) & MB_OP_IO_NO_OP) != 0)
//#define MB_OP_IO_CARD 0x40
//#define MB_OP_IO_DEVSEL 0x80
#define MB_CARD_IRQ 0x80000000
//
/** _clock_ is of type mb_clock_t
BEWARE - these macros act on sub second time intervals (per frame deltas.)
Do not use these utilities to calculate values over long time intervals
*/
#define mb_ns_step_from_clocks(_clocks_step_) \
((uint)(MB_14MHZ_CYCLE_NS * (_clocks_step_) / MB_CLOCKS_14MHZ_CYCLE))
#define mb_clocks_step_from_ns(_ns_) \
((mb_clocks_t)((_ns_) * MB_CLOCKS_14MHZ_CYCLE) / MB_14MHZ_CYCLE_NS)
/* intentional - paranthesized expression should be done first to avoid precision
* loss*/
#define mb_secs_from_clocks(_clock_) \
((MB_14MHZ_CYCLE_NS * (uint64_t)((_clock_)->ts / MB_CLOCKS_14MHZ_CYCLE)) * \
1.0e-9)
#ifdef __cplusplus
extern "C" {
#endif
struct mb_t *
mb_alloc();
void
mb_dispose( //
struct mb_t * mb);
void
mb_io_read( //
struct mb_t *board,
uint8_t *data,
uint8_t addr);
void
mb_io_write( //
struct mb_t *board,
uint8_t data,
uint8_t addr);
uint32_t
mb_io_sync( //
struct mb_t *board,
mb_clock_t *clock);
void
mb_io_reset( //
struct mb_t *board,
mb_clock_t *clock);
uint
mb_ay3_render( //
struct mb_t * mb,
float *samples_out,
uint sample_limit,
uint samples_per_frame,
uint samples_per_second);
#ifdef __cplusplus
}
#endif

View File

@ -59,11 +59,18 @@ mii_dd_register_drives(
uint8_t count )
{
// printf("%s: registering %d drives\n", __func__, count);
mii_dd_t * last = dd->drive;
while (last && last->next)
last = last->next;
for (int i = 0; i < count; i++) {
mii_dd_t *d = &drives[i];
d->dd = dd;
d->next = dd->drive;
dd->drive = d;
d->next = NULL;
if (last)
last->next = d;
else
dd->drive = d;
last = d;
}
}
@ -194,6 +201,8 @@ mii_dd_file_load(
res->format = MII_DD_FILE_PO;
} else if (!strcasecmp(suffix, ".nib")) {
res->format = MII_DD_FILE_NIB;
} else if (!strcasecmp(suffix, ".do")) {
res->format = MII_DD_FILE_DO;
} else if (!strcasecmp(suffix, ".woz")) {
res->format = MII_DD_FILE_WOZ;
} else if (!strcasecmp(suffix, ".2mg")) {
@ -236,6 +245,10 @@ mii_dd_overlay_load(
return 0;
if (!dd->file)
return -1;
// no overlay for PO disk images (floppy)
if (!(dd->file->format == MII_DD_FILE_PO &&
dd->file->size != 143360))
return -1;
char *filename = NULL;
char *suffix = strrchr(dd->file->pathname, '.');
@ -246,7 +259,7 @@ mii_dd_overlay_load(
}
int fd = open(filename, O_RDWR, 0666);
if (fd == -1) {
fprintf(stderr, "%s: overlay %s: %s\n", __func__,
printf("%s: overlay %s: %s\n", __func__,
filename, strerror(errno));
free(filename);
return -1;
@ -258,17 +271,17 @@ mii_dd_overlay_load(
mii_dd_overlay_header_t * h = (mii_dd_overlay_header_t *)file->start;
if (h->magic != FCC('M','I','O','V')) {
fprintf(stderr, "Overlay file %s has invalid magic\n", filename);
printf("Overlay file %s has invalid magic\n", filename);
mii_dd_file_dispose(dd->dd, file);
return -1;
}
if (h->version != 1) {
fprintf(stderr, "Overlay file %s has invalid version\n", filename);
printf("Overlay file %s has invalid version\n", filename);
mii_dd_file_dispose(dd->dd, file);
return -1;
}
if (h->size != dd->file->size / 512) {
fprintf(stderr, "Overlay file %s has invalid size\n", filename);
printf("Overlay file %s has invalid size\n", filename);
mii_dd_file_dispose(dd->dd, file);
return -1;
}
@ -280,7 +293,7 @@ mii_dd_overlay_load(
MD5_Final(md5, &d5);
if (memcmp(md5, h->src_md5, 16)) {
fprintf(stderr, "Overlay file %s has mismatched HASH!\n", filename);
printf("Overlay file %s has mismatched HASH!\n", filename);
mii_dd_file_dispose(dd->dd, file);
return -1;
}
@ -302,6 +315,10 @@ mii_dd_overlay_prepare(
return 0;
if (!dd->file)
return -1;
// no overlay for PO disk images (floppy)
if (!(dd->file->format == MII_DD_FILE_PO &&
dd->file->size != 143360))
return 0;
printf("%s: %s Preparing Overlay file\n", __func__, dd->name);
uint32_t src_blocks = dd->file->size / 512;
uint32_t bitmap_size = (src_blocks + 63) / 64;
@ -317,9 +334,9 @@ mii_dd_overlay_prepare(
}
int fd = open(filename, O_RDWR | O_CREAT | O_TRUNC, 0666);
if (fd == -1) {
fprintf(stderr, "%s: Failed to create overlay file %s: %s\n", __func__,
printf("%s: Failed to create overlay file %s: %s\n", __func__,
filename, strerror(errno));
fprintf(stderr, "%s: Allocating a RAM one, lost on quit!\n", __func__);
printf("%s: Allocating a RAM one, lost on quit!\n", __func__);
dd->overlay.file = mii_dd_file_in_ram(dd->dd, filename, size, O_RDWR);
} else {
ftruncate(fd, size);

View File

@ -14,11 +14,12 @@ struct mii_dd_t;
enum {
// MII_DD_FILE_OVERLAY = 1,
MII_DD_FILE_RAM,
MII_DD_FILE_RAM = 1,
MII_DD_FILE_ROM,
MII_DD_FILE_PO,
MII_DD_FILE_2MG = 5,
MII_DD_FILE_2MG,
MII_DD_FILE_DSK,
MII_DD_FILE_DO,
MII_DD_FILE_NIB,
MII_DD_FILE_WOZ
};
@ -65,12 +66,14 @@ typedef struct mii_dd_overlay_t {
struct mii_slot_t;
struct mii_dd_system_t;
struct mii_floppy_t;
// a disk drive, with a slot, a drive number, and a file
typedef struct mii_dd_t {
struct mii_dd_t * next;
struct mii_dd_system_t *dd;
const char * name; // ie "Disk ][ D:2"
const char * name; // ie "Disk ][ D:2"
struct mii_floppy_t * floppy; // if it's a floppy drive
uint8_t slot_id : 4, drive : 4;
struct mii_slot_t * slot;
unsigned int ro : 1, wp : 1, can_eject : 1;

View File

@ -55,11 +55,15 @@ mii_floppy_init(
// important, the +1 means we initialize the random track too
for (int i = 0; i < MII_FLOPPY_TRACK_COUNT + 1; i++) {
f->tracks[i].dirty = 0;
f->tracks[i].bit_count = 6500 * 8;
f->tracks[i].virgin = 1;
// this affects the disk 'speed' -- larger number will slow down the
// apparent speed of the disk, according to disk utilities. This value
// gives 299-300 RPM, which is the correct speed for a 5.25" floppy.
f->tracks[i].bit_count = 6400 * 8;
// fill the whole array up to the end..
uint8_t *track = f->track_data[i];
if (i != MII_FLOPPY_NOISE_TRACK) {
#if 1
#if 0
memset(track, 0, MII_FLOPPY_DEFAULT_TRACK_SIZE);
#else
for (int bi = 0; bi < MII_FLOPPY_DEFAULT_TRACK_SIZE; bi++)
@ -73,32 +77,289 @@ static void
mii_track_write_bits(
mii_floppy_track_t * dst,
uint8_t * track_data,
uint8_t bits,
uint32_t bits,
uint8_t count )
{
while (count--) {
uint32_t byte_index = dst->bit_count >> 3;
uint8_t bit_index = 7 - (dst->bit_count & 7);
track_data[byte_index] &= ~(1 << bit_index);
track_data[byte_index] |= (!!(bits >> 7) << bit_index);
track_data[byte_index] |= !!(bits & (1L << (count))) << bit_index;
dst->bit_count++;
bits <<= 1;
}
}
static uint32_t
mii_track_read_bits(
mii_floppy_track_t * src,
uint8_t * track_data,
uint32_t pos,
uint8_t count )
{
uint32_t bits = 0;
while (count--) {
pos = pos % src->bit_count;
uint32_t byte_index = pos >> 3;
uint8_t bit_index = 7 - (pos & 7);
bits <<= 1;
bits |= !!(track_data[byte_index] & (1 << bit_index));
// we CAN have a wrap around here, but it's ok
pos++;
}
return bits;
}
/*
* given a track a a starting position, look for a run of 0b1111111100,
* return the number of sync bits found, and also update the position to
* the end of the sync bits.
*/
static uint32_t
mii_floppy_find_next_sync(
mii_floppy_track_t * src,
uint8_t * track_data,
uint32_t *io_pos)
{
/* First we need to sync ourselves by finding 5 * 0b1111111100 's */
uint32_t window = 0;
/* get one bit at a time until we get one sync word */
uint32_t wi = 0;
uint32_t pos = *io_pos;
// give up after 2000 bits really, it's either there, or not
// otherwise we could be 'fooled' looping over the whole track
int tries = 10000;
do {
do {
window = (window << 1) |
mii_track_read_bits(src, track_data, pos, 1);
pos++;
if ((window & 0x3ff) == 0b1111111100)
break;
} while (tries-- > 0 );
wi = 10;
if (mii_track_read_bits(src, track_data, pos, 1) == 0) {
pos++;
wi++;
}
do {
uint16_t w = mii_track_read_bits(src, track_data, pos + wi, 9);
if (w == 0b111111110)
wi += 9;
else if ((w & 0b111111110) == 0b111111110) {
wi += 8;
break;
}
if (mii_track_read_bits(src, track_data, pos + wi, 1) == 0) {
wi++;
} else
break;
if (mii_track_read_bits(src, track_data, pos + wi, 1) == 0) {
wi++;
}
} while (tries-- > 0 && wi < 2000);
/* if this is a sector header, we're in sync here! */
if (wi >= 2 * 10)
break;
else {
wi = 0;
}
} while (tries-- > 0 && wi < 2000);
// this CAN overflow the bit count, but it's ok
pos += wi;
*io_pos = pos;
return wi;
}
#define DE44(a, b) ((((a) & 0x55) << 1) | ((b) & 0x55))
/*
* this creates a sector+data map of a bitstream, and returns the positions
* of header and data blocks, as well as how many sync bits were found.
* Function return 0 if 16 headers + data were found, -1 if not.
*/
int
mii_floppy_map_track(
mii_floppy_t *f,
uint8_t track_id,
mii_floppy_track_map_t *map,
uint8_t flags )
{
mii_floppy_track_t * src = &f->tracks[track_id];
uint8_t * track_data = f->track_data[track_id];
uint16_t hmap = 0, dmap = 0;
uint32_t pos = 0;
uint32_t wi = 0;
int sect_count = 0;
int sect_current = -1; // current sector
// do 2 passes, in case a data sector appears before it's header
int pass = 0;
do {
wi = mii_floppy_find_next_sync(src, track_data, &pos);
uint32_t header = mii_track_read_bits(src, track_data, pos, 24);
if (wi == 0) {
printf("T%2d pos:%5d hmap:%04x dmap:%04x done?\n",
track_id, pos, hmap, dmap);
return -1;
}
if (header == 0xd5aaad) { // data sector, update current sector
if (sect_current == -1) {
if (flags & 1)
printf("%s: track %2d data sector before header\n",
__func__, track_id);
} else {
dmap |= 1 << sect_current;
map->sector[sect_current].dsync = wi;
map->sector[sect_current].data = pos;
}
int skippy = 3 + 342 + 1 + 3; // header, data nibble, chk, tailer
pos += skippy * 8;
goto get_new_sync;
}
if (header != 0xd5aa96) { // not a header section? maybe DOS3.2?
if (flags & 1)
printf("%s: track %2d bizare sync found %06x\n",
__func__, track_id, header);
pos += 10; // first 10 bits aren't sync anyway
goto get_new_sync;
}
if (flags & 1)
printf("Track %2d sync %d sync bits at bit %d/%d next %08x\n",
track_id, wi, pos, src->bit_count, header);
uint8_t hb[8];
for (int hi = 0; hi < 8; hi++)
hb[hi] = mii_track_read_bits(src, track_data,
pos + 24 + (hi * 8), 8);
uint32_t tailer = mii_track_read_bits(src, track_data,
pos + 24 + (8 * 8), 20);
uint8_t vol = DE44(hb[0], hb[1]);
uint8_t track = DE44(hb[2], hb[3]);
uint8_t sector = DE44(hb[4], hb[5]);
uint8_t chk = DE44(hb[6], hb[7]);
uint8_t want = vol ^ track ^ sector;
if (chk != want) {
if (flags & 1)
printf("T%2d S%2d V%2d chk:%2x/%02x tailer %06x INVALID header\n",
track, sector, vol, chk, want, tailer);
goto get_new_sync;
}
sect_current = sector;
sect_count++;
// if we already have a header for this sector (with it's matching data)
if ((hmap & (1 << sector)) && !(dmap & (1 << sector))) {
printf("T%2d S%2d DUPLICATE sector pos:%5d hmap:%04x dmap:%04x\n",
track, sector, pos, hmap, dmap);
printf("\thsync: %3d pos:%5d dsync: %3d pos:%5d\n",
map->sector[sector].hsync, map->sector[sector].header,
map->sector[sector].dsync, map->sector[sector].data);
return -1;
}
hmap |= 1 << sector;
map->sector[sector].hsync = wi;
map->sector[sector].header = pos;
map->sector[sector].data = 0;
if (flags & 1)
printf("T%2d S%2d V%2d chk:%2x/%02x pos %5d tailer %06x hm:%04x dm:%04x\n",
track, sector, vol, chk, want, pos, tailer, hmap, dmap);
if (sect_count > 16) {
// something fishy going on there, too many sector found,
// and none of them is zero? let's bail.
printf("T%2d S%2d Too many sectors\n",
track, sector);
return -1;
}
// printf("T%2d S%2d V%2d chk:%2x/%02x tailer %06x Skipping sector\n",
// track, sector, vol, chk, want, tailer);
pos += 24 + 8 * 8 + 24; // skip the whole header
if (track_id == 0 && sector == 0) {
printf("pos %5d/%5d\n", pos, src->bit_count);
for (int bi = 0; bi < 10; bi++) {
uint32_t bits = mii_track_read_bits(src, track_data,
pos + (bi * 10), 10);
printf("%010b\n", bits);
}
printf("\n");
}
get_new_sync:
if (hmap == 0xffff && dmap == 0xffff)
break;
if (pos >= src->bit_count) {
if (pass == 0) {
printf("%s: T%2d has %d sectors hmap:%04x dmap:%04x LOOPING\n",
__func__, track_id, sect_count, hmap, dmap);
pass++;
pos = pos % src->bit_count;
}
}
} while (pos < src->bit_count);
int res = hmap == 0xffff && dmap == 0xffff ? 0 : -1;
if (res != 0) {
// if (flags & 1)
printf("%s: T%2d has %d sectors hmap:%04x dmap:%04x\n",
__func__, track_id, sect_count, hmap, dmap);
}
return res;
}
/* This reposition the sector 0 to the beginning of the track,
hopefully also realign the nibbles to something readable.
See Sather 9-28 for details
*/
void
mii_floppy_resync_track(
mii_floppy_t *f,
uint8_t track_id,
uint8_t flags )
{
mii_floppy_track_map_t map = {};
if (mii_floppy_map_track(f, track_id, &map, flags) != 0) {
printf("%s: track %2d has no sync\n", __func__, track_id);
return;
}
int32_t pos = map.sector[0].header;
int32_t wi = map.sector[0].hsync;
/* We got a sector zero, we know the number of sync bits in front, and we
know it's header position, so we can reposition it at the beginning
of the track by basically reconstructing it */
pos -= wi;
if (pos <= 10) { // already at the beginning, really.
if (flags & 1)
printf("T%2d Sector 0 at pos %d\n", track_id, pos);
return;
}
mii_floppy_track_t * src = &f->tracks[track_id];
uint8_t * track_data = f->track_data[track_id];
if (flags & 1)
printf("%s: track %2d resync from bit %5d/%5d\n",
__func__, track_id, pos, src->bit_count);
mii_floppy_track_t new = {.dirty = 1, .virgin = 0, .bit_count = 0};
uint8_t *new_track = malloc(6656);
while (new.bit_count < src->bit_count) {
int cnt = src->bit_count - new.bit_count > 32 ? 32 : src->bit_count - new.bit_count;
uint32_t bits = mii_track_read_bits(src, track_data, pos, cnt);
mii_track_write_bits(&new, new_track, bits, cnt);
pos += cnt;
}
// printf("%s: Track %2d has been resynced!\n", __func__, track_id);
memcpy(track_data, new_track, MII_FLOPPY_DEFAULT_TRACK_SIZE);
free(new_track);
src->dirty = 1;
}
/*
* NIB isn't ideal to use with our bitstream, as it's lacking the sync
* bits. It was made to use in something like our previous emulator that
* was just iterating uint8_ts.
* bits.
* Anyway, We can recreate the proper bitstream by finding sectors headers,
* filling up a few 'correct' 10 bits sync uint8_ts, then plonk said sector
* as is.
*/
static uint8_t _de44(uint8_t a, uint8_t b) {
return ((a & 0x55) << 1) | (b & 0x55);
}
static void
mii_nib_rebit_track(
uint8_t *src_track,
@ -106,22 +367,27 @@ mii_nib_rebit_track(
uint8_t * dst_track)
{
dst->bit_count = 0;
dst->virgin = 0;
uint32_t window = 0;
int srci = 0;
int seccount = 0;
int state = 0; // look for address field
int tid = 0, sid;
uint16_t hmap = 0, dmap = 0;
do {
window = (window << 8) | src_track[srci++];
switch (state) {
case 0: {
if (window != 0xffd5aa96)
break;
// uint32_t pos = dst->bit_count;
for (int i = 0; i < (seccount == 0 ? 40 : 20); i++)
mii_track_write_bits(dst, dst_track, 0xff, 10);
mii_track_write_bits(dst, dst_track, 0xff << 2, 10);
uint8_t * h = src_track + srci - 4; // incs first 0xff
// int tid = _de44(h[6], h[7]);
// int sid = _de44(h[8], h[9]);
// printf("Track %2d sector %2d\n", tid, sid);
tid = DE44(h[6], h[7]);
sid = DE44(h[8], h[9]);
// printf("Track %2d sector %2d pos %5d\n", tid, sid, pos);
hmap |= 1 << sid;
memcpy(dst_track + (dst->bit_count >> 3), h, 15);
dst->bit_count += 15 * 8;
srci += 11;
@ -131,7 +397,9 @@ mii_nib_rebit_track(
if (window != 0xffd5aaad)
break;
for (int i = 0; i < 4; i++)
mii_track_write_bits(dst, dst_track, 0xff, 10);
mii_track_write_bits(dst, dst_track, 0xff << 2, 10);
// printf("\tdata at %d\n", dst->bit_count);
dmap |= 1 << sid;
uint8_t *h = src_track + srci - 4;
memcpy(dst_track + (dst->bit_count >> 3), h, 4 + 342 + 4);
dst->bit_count += (4 + 342 + 4) * 8;
@ -141,6 +409,11 @@ mii_nib_rebit_track(
} break;
}
} while (srci < 6656);
printf("%s %d sectors found hmap %04x dmap %04x - %5d bits\n",
__func__, seccount, hmap, dmap, dst->bit_count);
if (hmap != 0xffff || dmap != 0xffff)
printf("%s: track %2d incomplete? (header 0x%04x data 0x%04x)\n",
__func__, tid, ~hmap, ~dmap);
}
static int
@ -275,6 +548,7 @@ mii_floppy_load_woz(
// printf("WOZ: Track %d not used\n", i);
continue;
}
f->tracks[i].virgin = 0;
memcpy(f->track_data[i], track, le16toh(trks->track[i].byte_count_le));
f->tracks[i].bit_count = le32toh(trks->track[i].bit_count_le);
}
@ -298,7 +572,7 @@ mii_floppy_load_woz(
(char*)&trks->chunk.id_le, le32toh(trks->chunk.size_le));
#endif
/* TODO: this doesn't work yet... */
// f->bit_timing = info->optimal_bit_timing;
//f->bit_timing = info->optimal_bit_timing;
for (int i = 0; i < MII_FLOPPY_TRACK_COUNT; i++) {
if (!(used_tracks & (1L << i))) {
// printf("WOZ: Track %d not used\n", i);
@ -307,6 +581,7 @@ mii_floppy_load_woz(
uint8_t *track = file->map +
(le16toh(trks->track[i].start_block_le) << 9);
uint32_t byte_count = (le32toh(trks->track[i].bit_count_le) + 7) >> 3;
f->tracks[i].virgin = 0;
memcpy(f->track_data[i], track, byte_count);
f->tracks[i].bit_count = le32toh(trks->track[i].bit_count_le);
}
@ -336,6 +611,7 @@ static const uint8_t PO[] = {
0x0, 0x8, 0x1, 0x9, 0x2, 0xa, 0x3, 0xb,
0x4, 0xc, 0x5, 0xd, 0x6, 0xe, 0x7, 0xf
};
static const uint8_t TRANS62[] = {
0x96, 0x97, 0x9a, 0x9b, 0x9d, 0x9e, 0x9f, 0xa6,
0xa7, 0xab, 0xac, 0xad, 0xae, 0xaf, 0xb2, 0xb3,
@ -349,49 +625,43 @@ static const uint8_t TRANS62[] = {
// This function is derived from Scullin Steel Co.'s apple2js code
// https://github.com/whscullin/apple2js/blob/e280c3d/js/formats/format_utils.ts#L140
/* Further recycled for MII .DSK decoding
* We use this function to convert the sector from byte to nibble (8 bits), then
* we pass that track to the mii_nib_rebit_track() to add 10 bit headers and
* such. It could be done in one pass, but really, it's easier to reuse it as is.
*/
/* Further recycled for MII .DSK decoding, using 10 bits sync words etc. */
static void
mii_floppy_nibblize_sector(
uint8_t vol, uint8_t track, uint8_t sector,
uint8_t **nibSec, const uint8_t *data)
const uint8_t *data,
mii_floppy_track_t *dst,
uint8_t * track_data )
{
uint8_t *wr = *nibSec;
unsigned int gap;
// Gap 1/3 (40/0x28 uint8_ts)
if (sector == 0) // Gap 1
gap = 0x80;
else { // Gap 3
gap = track == 0? 0x28 : 0x26;
}
for (uint8_t i = 0; i != gap; ++i)
*wr++ = 0xFF;
// if (track == 0 )
// printf("NIB: vol %d track %d sector %d pos %5d\n",
// vol, track, sector, dst->bit_count);
gap = sector == 0 ? 100 : track == 0 ? 20 : 20;
for (uint8_t i = 0; i < gap; ++i)
mii_track_write_bits(dst, track_data, 0xFF << 2, 10);
// Address Field
const uint8_t checksum = vol ^ track ^ sector;
*wr++ = 0xD5; *wr++ = 0xAA; *wr++ = 0x96; // Address Prolog D5 AA 96
*wr++ = (vol >> 1) | 0xAA; *wr++ = vol | 0xAA;
*wr++ = (track >> 1) | 0xAA; *wr++ = track | 0xAA;
*wr++ = (sector >> 1) | 0xAA; *wr++ = sector | 0xAA;
*wr++ = (checksum >> 1) | 0xAA; *wr++ = checksum | 0xAA;
*wr++ = 0xDE; *wr++ = 0xAA; *wr++ = 0xEB; // Epilogue DE AA EB
// Gap 2 (5 uint8_ts)
for (int i = 0; i != 5; ++i)
*wr++ = 0xFF;
mii_track_write_bits(dst, track_data, 0xd5aa96, 24);
mii_track_write_bits(dst, track_data, (vol >> 1) | 0xAA, 8);
mii_track_write_bits(dst, track_data, vol | 0xAA, 8);
mii_track_write_bits(dst, track_data, (track >> 1) | 0xAA, 8);
mii_track_write_bits(dst, track_data, track | 0xAA, 8);
mii_track_write_bits(dst, track_data, (sector >> 1) | 0xAA, 8);
mii_track_write_bits(dst, track_data, sector | 0xAA, 8);
mii_track_write_bits(dst, track_data, (checksum >> 1) | 0xAA, 8);
mii_track_write_bits(dst, track_data, checksum | 0xAA, 8);
mii_track_write_bits(dst, track_data, 0xdeaaeb, 24);
// Gap 2 (5)
for (int i = 0; i < 5; ++i)
mii_track_write_bits(dst, track_data, 0xFF << 2, 10);
// Data Field
*wr++ = 0xD5; *wr++ = 0xAA; *wr++ = 0xAD; // Data Prolog D5 AA AD
uint8_t *nibbles = wr;
mii_track_write_bits(dst, track_data, 0xd5aaad, 24);
uint8_t nibbles[0x156] = {};
const unsigned ptr2 = 0;
const unsigned ptr6 = 0x56;
for (int i = 0; i != 0x156; ++i)
nibbles[i] = 0;
int i2 = 0x55;
for (int i6 = 0x101; i6 >= 0; --i6) {
uint8_t val6 = data[i6 % 0x100];
@ -406,15 +676,13 @@ mii_floppy_nibblize_sector(
uint8_t last = 0;
for (int i = 0; i != 0x156; ++i) {
const uint8_t val = nibbles[i];
nibbles[i] = TRANS62[last ^ val];
mii_track_write_bits(dst, track_data, TRANS62[last ^ val], 8);
last = val;
}
wr += 0x156; // advance write-pointer
*wr++ = TRANS62[last];
*wr++ = 0xDE; *wr++ = 0xAA; *wr++ = 0xEB; // Epilogue DE AA EB
mii_track_write_bits(dst, track_data, TRANS62[last], 8);
mii_track_write_bits(dst, track_data, 0xdeaaeb, 24);
// Gap 3
*wr++ = 0xFF;
*nibSec = wr;
mii_track_write_bits(dst, track_data, 0xFF << 2, 10);
}
static int
@ -422,31 +690,33 @@ mii_floppy_load_dsk(
mii_floppy_t *f,
mii_dd_file_t *file )
{
uint8_t *nibbleBuf = malloc(NIBBLE_TRACK_SIZE);
const char *filename = basename(file->pathname);
const char *ext = rindex(filename, '.');
ext = ext ? ext+1 : "";
const uint8_t * secmap = DO;
if (!strcasecmp(ext, "PO")) {
printf("%s Opening %s as PO.\n", __func__, filename);
printf("%s opening %s as PO.\n", __func__, filename);
secmap = PO;
} else {
printf("%s Opening %s as DO.\n", __func__, filename);
printf("%s opening %s as DO.\n", __func__, filename);
}
for (int i = 0; i < 35; ++i) {
memset(nibbleBuf, 0xff, NIBBLE_TRACK_SIZE);
uint8_t *writePtr = nibbleBuf;
for (int phys_sector = 0; phys_sector < MAX_SECTORS; ++phys_sector) {
mii_floppy_track_t *dst = &f->tracks[i];
uint8_t *track_data = f->track_data[i];
dst->bit_count = 0;
dst->virgin = 0;
for (int phys_sector = 0; phys_sector < MAX_SECTORS; phys_sector++) {
const uint8_t dos_sector = secmap[phys_sector];
uint32_t off = ((MAX_SECTORS * i + dos_sector) * DSK_SECTOR_SIZE);
uint8_t *track = file->map + off;
uint8_t *src = file->map + off;
mii_floppy_nibblize_sector(VOLUME_NUMBER, i, phys_sector,
&writePtr, track);
src, dst, track_data);
}
mii_nib_rebit_track(nibbleBuf, &f->tracks[i], f->track_data[i]);
if (i == 0)
printf("%s: track %2d has %d bits %d bytes\n",
__func__, i, dst->bit_count, dst->bit_count >> 3);
}
free(nibbleBuf);
// DSK is read only
f->write_protected |= MII_FLOPPY_WP_RO_FORMAT;
@ -498,6 +768,8 @@ mii_floppy_load(
res = mii_floppy_load_woz(f, file);
break;
case MII_DD_FILE_DSK:
case MII_DD_FILE_PO:
case MII_DD_FILE_DO:
res = mii_floppy_load_dsk(f, file);
break;
default:

View File

@ -25,7 +25,8 @@ enum {
};
typedef struct mii_floppy_track_t {
uint8_t dirty : 1; // track has been written to
uint8_t dirty : 1, // track has been written to
virgin : 1; // track is not loaded/formatted
uint32_t bit_count;
} mii_floppy_track_t;
@ -39,10 +40,10 @@ typedef struct mii_floppy_track_t {
typedef struct mii_track_heatmap_t {
// 32 bytes of track data corresponds to one byte of heatmap
uint32_t seed, tex, cleared;
// this needs to be aligned, otherwise SSE code will die horribly
uint8_t map[MII_FLOPPY_TRACK_COUNT][MII_FLOPPY_HM_TRACK_SIZE]
__attribute__((aligned(16)));
__attribute__((aligned(32)));
uint32_t seed, tex, cleared;
} mii_track_heatmap_t;
typedef struct mii_floppy_heatmap_t {
@ -66,14 +67,17 @@ typedef struct mii_floppy_t {
mii_floppy_track_t tracks[MII_FLOPPY_TRACK_COUNT + 1];
// keep all the data together, we'll use it to make a texture
// the last track is used for noise
uint8_t track_data[MII_FLOPPY_TRACK_COUNT + 1][MII_FLOPPY_DEFAULT_TRACK_SIZE];
/* This is set by the UI to trakc the head movements, no functional use */
uint8_t track_data[MII_FLOPPY_TRACK_COUNT + 1]
[MII_FLOPPY_DEFAULT_TRACK_SIZE];
/* This is set by the UI to track the head movements,
* no functional use */
mii_floppy_heatmap_t * heat; // optional heatmap
} mii_floppy_t;
/*
* Initialize a floppy structure with random data. It is not formatted, just
* ready to use for loading a disk image, or formatting as a 'virgin' disk.
* Initialize a floppy structure with random data. It is not formatted,
* just ready to use for loading a disk image, or formatting as a
* 'virgin' disk.
*/
void
mii_floppy_init(
@ -88,3 +92,27 @@ int
mii_floppy_update_tracks(
mii_floppy_t *f,
mii_dd_file_t *file );
void
mii_floppy_resync_track(
mii_floppy_t *f,
uint8_t track_id,
uint8_t flags );
typedef struct mii_floppy_track_map_t {
struct {
int32_t hsync, dsync; // number of sync bits
uint32_t header, data; // position of the header and data
} sector[16];
} mii_floppy_track_map_t;
/*
* this creates a sector+data map of a bitstream, and returns the positions
* of header and data blocks, as well as how many sync bits were found.
* Function return 0 if 16 headers + data were found, -1 if not.
*/
int
mii_floppy_map_track(
mii_floppy_t *f,
uint8_t track_id,
mii_floppy_track_map_t *map,
uint8_t flags );

477
src/mii.c
View File

@ -19,6 +19,12 @@
#include "mii_65c02.h"
#include "minipt.h"
#if MII_65C02_DIRECT_ACCESS
static mii_cpu_state_t
_mii_cpu_direct_access_cb(
struct mii_cpu_t *cpu,
mii_cpu_state_t access );
#endif
mii_slot_drv_t * mii_slot_drv_list = NULL;
@ -26,32 +32,47 @@ static const mii_bank_t _mii_banks_init[MII_BANK_COUNT] = {
[MII_BANK_MAIN] = {
.name = "MAIN",
.base = 0x0000,
.size = 0xd0, // 208 pages, 48KB
.size = 0xc0,
},
[MII_BANK_BSR] = {
.name = "BSR",
.base = 0xd000,
.size = 64,
.mem_offset = 0xd000,
.no_alloc = 1,
},
[MII_BANK_BSR_P2] = {
.name = "BSR P2",
.base = 0xd000,
.size = 16,
.mem_offset = 0xc000,
.no_alloc = 1,
},
[MII_BANK_AUX_BASE] = {
.name = "AUX_BASE",
.base = 0x0000,
.size = 0xd0, // 208 pages, 48KB
.no_alloc = 1,
},
[MII_BANK_AUX] = {
.name = "AUX",
.base = 0x0000,
.size = 0xd0, // 208 pages, 48KB
.no_alloc = 1,
},
[MII_BANK_AUX_BSR] = {
.name = "AUX BSR",
.base = 0xd000,
.size = 64,
.mem_offset = 0xd000,
.no_alloc = 1,
},
[MII_BANK_AUX_BSR_P2] = {
.name = "AUX BSR P2",
.base = 0xd000,
.size = 16,
.mem_offset = 0xc000,
.no_alloc = 1,
},
[MII_BANK_ROM] = {
.name = "ROM",
@ -62,9 +83,17 @@ static const mii_bank_t _mii_banks_init[MII_BANK_COUNT] = {
[MII_BANK_CARD_ROM] = {
.name = "CARD ROM",
.base = 0xc100,
.size = 15,
// c100-cfff = 15 pages
// 7 * 2KB for extended ROMs for cards (not addressable directly)
// Car roms are 'banked' as well, so we don't need to copy them around
.size = 15,// + (7 * 8),
.ro = 1,
},
[MII_BANK_SW] = {
.name = "SW",
.base = 0xc000,
.size = 0x1,
},
};
@ -84,7 +113,7 @@ mii_dump_trace_state(
static const char *s_flags = "CZIDBRVN";
for (int i = 0; i < 8; i++)
printf("%c", MII_GET_P_BIT(cpu, i) ? s_flags[i] : tolower(s_flags[i]));
if (s.sync) {
// if (s.sync) {
uint8_t op[16];
for (int i = 0; i < 4; i++) {
mii_mem_access(mii, mii->cpu.PC + i, op + i, false, false);
@ -100,8 +129,8 @@ mii_dump_trace_state(
printf(" ; taken");
}
printf("\n");
} else
printf("\n");
// } else
// printf("\n");
}
void
@ -147,7 +176,7 @@ mii_sw(
mii_t *mii,
uint16_t sw)
{
return mii_bank_peek(&mii->bank[MII_BANK_MAIN], sw);
return mii_bank_peek(&mii->bank[MII_BANK_SW], sw);
}
static void
@ -157,15 +186,16 @@ mii_page_table_update(
if (likely(!mii->mem_dirty))
return;
mii->mem_dirty = 0;
bool altzp = SW_GETSTATE(mii, SWALTPZ);
bool page2 = SW_GETSTATE(mii, SWPAGE2);
bool store80 = SW_GETSTATE(mii, SW80STORE);
bool hires = SW_GETSTATE(mii, SWHIRES);
bool ramrd = SW_GETSTATE(mii, SWRAMRD);
bool ramwrt = SW_GETSTATE(mii, SWRAMWRT);
bool intcxrom = SW_GETSTATE(mii, SWINTCXROM);
bool slotc3rom = SW_GETSTATE(mii, SWSLOTC3ROM);
bool slotauxrom = SW_GETSTATE(mii, SLOTAUXROM);
uint32_t sw = mii->sw_state;
bool altzp = SWW_GETSTATE(sw, SWALTPZ);
bool page2 = SWW_GETSTATE(sw, SWPAGE2);
bool store80 = SWW_GETSTATE(sw, SW80STORE);
bool hires = SWW_GETSTATE(sw, SWHIRES);
bool ramrd = SWW_GETSTATE(sw, SWRAMRD);
bool ramwrt = SWW_GETSTATE(sw, SWRAMWRT);
bool intcxrom = SWW_GETSTATE(sw, SWINTCXROM);
bool slotc3rom = SWW_GETSTATE(sw, SWSLOTC3ROM);
bool intc8rom = SWW_GETSTATE(sw, INTC8ROM);
if (unlikely(mii->trace_cpu))
printf("%04x: MEM update altzp:%d page2:%d store80:%d "
@ -173,7 +203,8 @@ mii_page_table_update(
"slotc3rom:%d\n", mii->cpu.PC,
altzp, page2, store80, hires, ramrd, ramwrt, intcxrom, slotc3rom);
// clean slate
mii_page_set(mii, MII_BANK_MAIN, MII_BANK_MAIN, 0x00, 0xc0);
mii_page_set(mii, MII_BANK_MAIN, MII_BANK_MAIN, 0x00, 0xbf);
mii_page_set(mii, MII_BANK_SW, MII_BANK_SW, 0xc0, 0xc0);
mii_page_set(mii, MII_BANK_ROM, MII_BANK_ROM, 0xc1, 0xff);
if (altzp)
mii_page_set(mii, MII_BANK_AUX, MII_BANK_AUX, 0x00, 0x01);
@ -189,16 +220,17 @@ mii_page_table_update(
page2 ? MII_BANK_AUX : MII_BANK_MAIN,
page2 ? MII_BANK_AUX : MII_BANK_MAIN, 0x20, 0x3f);
}
// c1-cf are at ROM state when we arrive here
if (!intcxrom) {
mii_page_set(mii, MII_BANK_CARD_ROM, _SAME, 0xc1, 0xc7);
if (slotauxrom)
mii_page_set(mii, MII_BANK_CARD_ROM, _SAME, 0xc8, 0xcf);
mii_page_set(mii, MII_BANK_CARD_ROM, MII_BANK_CARD_ROM, 0xc1, 0xcf);
if (!slotc3rom)
mii_page_set(mii, MII_BANK_ROM, _SAME, 0xc3, 0xc3);
if (intc8rom)
mii_page_set(mii, MII_BANK_ROM, _SAME, 0xc8, 0xcf);
}
mii_page_set(mii,
slotc3rom ? MII_BANK_CARD_ROM : MII_BANK_ROM, _SAME, 0xc3, 0xc3);
bool bsrread = SW_GETSTATE(mii, BSRREAD);
bool bsrwrite = SW_GETSTATE(mii, BSRWRITE);
bool bsrpage2 = SW_GETSTATE(mii, BSRPAGE2);
bool bsrread = SWW_GETSTATE(sw, BSRREAD);
bool bsrwrite = SWW_GETSTATE(sw, BSRWRITE);
bool bsrpage2 = SWW_GETSTATE(sw, BSRPAGE2);
mii_page_set(mii,
bsrread ?
altzp ? MII_BANK_AUX_BSR : MII_BANK_BSR :
@ -218,6 +250,32 @@ mii_page_table_update(
0xd0, 0xdf);
}
static void
mii_bank_update_ramworks(
mii_t *mii,
uint8_t bank)
{
if (bank > 127 ||
!(mii->ramworks.avail & ((unsigned __int128)1ULL << bank)))
bank = 0;
if (!mii->ramworks.bank[bank]) {
mii->ramworks.bank[bank] = malloc(0x10000);
int c = 0, a = 0;
for (int i = 0; i < 128; i++ ) {
if (mii->ramworks.bank[i])
c++;
if (mii->ramworks.avail & ((unsigned __int128)1ULL << i))
a++;
}
printf("%s: RAMWORKS alloc bank %2d (%dKB / %dKB)\n", __func__,
bank, c * 64, a * 64);
}
mii->bank[MII_BANK_AUX_BASE].mem = mii->ramworks.bank[0];
mii->bank[MII_BANK_AUX].mem = mii->ramworks.bank[bank];
mii->bank[MII_BANK_AUX_BSR].mem = mii->ramworks.bank[bank];
mii->bank[MII_BANK_AUX_BSR_P2].mem = mii->ramworks.bank[bank];
}
void
mii_set_sw_override(
mii_t *mii,
@ -238,18 +296,15 @@ mii_set_sw_override(
* selected, it will deselect it.
*/
static bool
_mii_deselect_auxrom(
struct mii_bank_t *bank,
void *param,
_mii_deselect_cXrom(
mii_t * mii,
uint16_t addr,
uint8_t * byte,
bool write)
{
if (addr != 0xcfff)
return false;
mii_t * mii = param;
// printf("%s AUXROM:%d\n", __func__, !!(mii->sw_state & M_SLOTAUXROM));
if (!(mii->sw_state & M_SLOTAUXROM))
if (!SW_GETSTATE(mii, INTC8ROM))
return false;
for (int i = 0; i < 7; i++) {
mii_slot_t * slot = &mii->slot[i];
@ -259,13 +314,14 @@ _mii_deselect_auxrom(
slot->aux_rom_selected = false;
}
}
mii->sw_state &= ~M_SLOTAUXROM;
SW_SETSTATE(mii, INTC8ROM, 0);
mii->mem_dirty = true;
mii_page_table_update(mii);
return false;
}
static bool
_mii_select_c3rom(
_mii_select_c3introm(
struct mii_bank_t *bank,
void *param,
uint16_t addr,
@ -273,12 +329,11 @@ _mii_select_c3rom(
bool write)
{
mii_t * mii = param;
printf("%s\n", __func__);
if (mii->sw_state & M_SLOTAUXROM) {
// printf("%s: C3 aux rom re-selected\n", __func__);
mii->sw_state &= ~M_SLOTAUXROM;
if (!SW_GETSTATE(mii, SWSLOTC3ROM) && !SW_GETSTATE(mii, INTC8ROM)) {
SW_SETSTATE(mii, INTC8ROM, 1);
mii->mem_dirty = true;
mii_page_table_update(mii);
}
mii->mem_dirty = true;
return false;
}
@ -293,7 +348,8 @@ mii_access_soft_switches(
return false;
bool res = false;
uint8_t on = 0;
mii_bank_t * main = &mii->bank[MII_BANK_MAIN];
mii_bank_t * sw = &mii->bank[MII_BANK_SW];
const uint16_t sw_save = mii->sw_state;
/*
* This allows driver (titan accelerator etc) to have their own
@ -301,7 +357,7 @@ mii_access_soft_switches(
*/
if (mii->soft_switches_override && mii->soft_switches_override[addr & 0xff].cb) {
res = mii->soft_switches_override[addr & 0xff].cb(
main, mii->soft_switches_override[addr & 0xff].param,
sw, mii->soft_switches_override[addr & 0xff].param,
addr, byte, write);
if (res)
return res;
@ -336,49 +392,38 @@ mii_access_soft_switches(
*/
case 0xc080 ... 0xc08f: {
res = true;
uint8_t mode = addr & 0x0f;
static const int write_modes[4] = { 0, 1, 0, 1, };
static const int read_modes[4] = { 1, 0, 0, 1, };
uint8_t rd = read_modes[mode & 3];
uint8_t wr = write_modes[mode & 3];
if (write) {
SW_SETSTATE(mii, BSRPREWRITE, 0);
const int offSwitch = addr & 0x02;
if (addr & 0x01) { // Write switch
// 0xC081, 0xC083
if (!write) {
if (SW_GETSTATE(mii, BSRPREWRITE)) {
SW_SETSTATE(mii, BSRWRITE, 1);
}
}
SW_SETSTATE(mii, BSRPREWRITE, !write);
// 0xC08B
SW_SETSTATE(mii, BSRREAD, offSwitch);
} else {
SW_SETSTATE(mii, BSRPREWRITE, mode & 1);
// 0xC080, 0xC082
SW_SETSTATE(mii, BSRWRITE, 0);
SW_SETSTATE(mii, BSRPREWRITE, 0);
// 0xC082
SW_SETSTATE(mii, BSRREAD, !offSwitch);
}
// if (SW_GETSTATE(mii, BSRPREWRITE))
// ;
SW_SETSTATE(mii, BSRWRITE, wr);
SW_SETSTATE(mii, BSRREAD, rd);
SW_SETSTATE(mii, BSRPAGE2, !(mode & 0x08)); // A3
mii->mem_dirty = 1;
// mii->trace_cpu = 1;
// mii->state = MII_STOPPED;
if (unlikely(mii->trace_cpu))
printf("%04x: BSR mode %c%04x pre:%d read:%s write:%s %s altzp:%02x\n",
mii->cpu.PC, write ? 'W' : 'R',
addr,
SW_GETSTATE(mii, BSRPREWRITE),
rd ? "BSR" : "ROM",
wr ? "BSR" : "ROM",
SW_GETSTATE(mii, BSRPAGE2) ? "page2" : "page1",
mii_sw(mii, SWALTPZ));
SW_SETSTATE(mii, BSRPAGE2, !(addr & 0x08));
mii->mem_dirty = sw_save != mii->sw_state;
} break;
case SWPAGE2OFF:
case SWPAGE2ON:
res = true;
SW_SETSTATE(mii, SWPAGE2, addr & 1);
mii_bank_poke(main, SWPAGE2, (addr & 1) << 7);
mii->mem_dirty = 1;
// ACTUAL switch is already done in mii_access_video()
res = true; // mii_access_video(mii, addr, byte, write);
mii->mem_dirty = true;
break;
case SWHIRESOFF:
case SWHIRESON:
res = true;
SW_SETSTATE(mii, SWHIRES, addr & 1);
mii_bank_poke(main, SWHIRES, (addr & 1) << 7);
mii->mem_dirty = 1;
// printf("HIRES %s\n", (addr & 1) ? "ON" : "OFF");
// ACTUAL switch is already done in mii_access_video()
res = true; // mii_access_video(mii, addr, byte, write);
mii->mem_dirty = true;
break;
case SWSPEAKER:
res = true;
@ -394,61 +439,66 @@ mii_access_soft_switches(
// IIgs register, read by prodos tho
break;
}
if (res && !mii->mem_dirty)
if (res) {
mii_page_table_update(mii);
return res;
}
if (write) {
switch (addr) {
case SW80STOREOFF:
case SW80STOREON:
res = true;
SW_SETSTATE(mii, SW80STORE, addr & 1);
mii_bank_poke(main, SW80STORE, (addr & 1) << 7);
mii->mem_dirty = 1;
mii_bank_poke(sw, SW80STORE, (addr & 1) << 7);
break;
case SWRAMRDOFF:
case SWRAMRDON:
res = true;
SW_SETSTATE(mii, SWRAMRD, addr & 1);
mii_bank_poke(main, SWRAMRD, (addr & 1) << 7);
mii->mem_dirty = 1;
mii_bank_poke(sw, SWRAMRD, (addr & 1) << 7);
break;
case SWRAMWRTOFF:
case SWRAMWRTON:
res = true;
SW_SETSTATE(mii, SWRAMWRT, addr & 1);
mii_bank_poke(main, SWRAMWRT, (addr & 1) << 7);
mii->mem_dirty = 1;
mii_bank_poke(sw, SWRAMWRT, (addr & 1) << 7);
break;
case SWALTPZOFF:
case SWALTPZON:
res = true;
SW_SETSTATE(mii, SWALTPZ, addr & 1);
mii_bank_poke(main, SWALTPZ, (addr & 1) << 7);
mii->mem_dirty = 1;
mii_bank_poke(sw, SWALTPZ, (addr & 1) << 7);
break;
case SWINTCXROMOFF:
case SWINTCXROMON:
res = true;
SW_SETSTATE(mii, SWINTCXROM, addr & 1);
mii_bank_poke(main, SWINTCXROM, (addr & 1) << 7);
mii->mem_dirty = 1;
mii_bank_poke(sw, SWINTCXROM, (addr & 1) << 7);
break;
case SWSLOTC3ROMOFF:
case SWSLOTC3ROMON:
res = true;
SW_SETSTATE(mii, SWSLOTC3ROM, addr & 1);
mii_bank_poke(main, SWSLOTC3ROM, (addr & 1) << 7);
mii->mem_dirty = 1;
mii_bank_poke(sw, SWSLOTC3ROM, (addr & 1) << 7);
break;
case SWRAMWORKS_BANK:
mii_bank_poke(sw, SWRAMWORKS_BANK, *byte);
mii_bank_update_ramworks(mii, *byte);
break;
}
mii->mem_dirty += sw_save != mii->sw_state;
} else {
switch (addr) {
case SWBSRBANK2:
*byte = SW_GETSTATE(mii, BSRPAGE2) ? 0x80 : 0;
*byte = SW_GETSTATE(mii, BSRPAGE2) << 7;
res = true;
break;
case SWBSRREADRAM:
*byte = SW_GETSTATE(mii, BSRREAD) ? 0x80 : 0;
*byte = SW_GETSTATE(mii, BSRREAD) << 7;
res = true;
break;
case SWPAGE2OFF:
case SWPAGE2ON: // already done by the video code
res = true;
break;
case SWRAMRD:
@ -458,31 +508,35 @@ mii_access_soft_switches(
case SWALTPZ:
case SWSLOTC3ROM:
res = true;
*byte = mii_bank_peek(main, addr);
break;
case 0xc020: // toggle TAPE output ?!?!
res = true;
*byte = mii_bank_peek(sw, addr);
break;
case 0xc068:
res = true;
// IIgs register, read by prodos tho
break;
case 0xc020: // toggle TAPE output ?!?!
// res = true;
// break;
default:
res = true;
// if (addr != 0xc00b)
// printf("VAPOR LOCK %04x\n", addr);
// this doesn't work. Well it does the job of returning
// something semi random, but it's not ready to be a TRUE
// vapor lock.
*byte = mii_video_get_vapor(mii);
#if 0
/*
* this is moderately important, return some random value
* as it is supposed to represent what's on the bus at the time,
* typically video being decoded etc.
*/
*byte = mii->random[mii->random_index++];
mii->random_index &= 0xff;
// *byte = mii->random[mii->random_index++];
// mii->random_index &= 0xff;
#endif
break;
}
}
if (!res) {
// printf("%s addr %04x write %d %02x\n", __func__, addr, write, *byte);
// mii->state = MII_STOPPED;
}
mii_page_table_update(mii);
return res;
}
@ -495,27 +549,27 @@ mii_access_keyboard(
bool write)
{
bool res = false;
mii_bank_t * main = &mii->bank[MII_BANK_MAIN];
mii_bank_t * sw = &mii->bank[MII_BANK_SW];
switch (addr) {
case SWKBD:
if (!write) {
res = true;
*byte = mii_bank_peek(main, SWKBD);
*byte = mii_bank_peek(sw, SWKBD);
}
break;
case SWAKD: {
res = true;
uint8_t r = mii_bank_peek(main, SWAKD);
uint8_t r = mii_bank_peek(sw, SWAKD);
if (!write)
*byte = r;
r &= 0x7f;
mii_bank_poke(main, SWAKD, r);
mii_bank_poke(main, SWKBD, r);
mii_bank_poke(sw, SWAKD, r);
mii_bank_poke(sw, SWKBD, r);
} break;
case 0xc061 ... 0xc063: // Push Button 0, 1, 2 (Apple Keys)
res = true;
if (!write)
*byte = mii_bank_peek(main, addr);
*byte = mii_bank_peek(sw, addr);
break;
}
return res;
@ -526,25 +580,53 @@ mii_keypress(
mii_t *mii,
uint8_t key)
{
mii_bank_t * main = &mii->bank[MII_BANK_MAIN];
mii_bank_t * sw = &mii->bank[MII_BANK_SW];
key |= 0x80;
mii_bank_poke(main, SWAKD, key);
mii_bank_poke(main, SWKBD, key);
mii_bank_poke(sw, SWAKD, key);
mii_bank_poke(sw, SWKBD, key);
}
/* ramworks came populated in chunks, this duplicates these rows of chips */
#define B(x) ((unsigned __int128)1ULL << (x))
static const unsigned __int128 _mii_ramworks3_config[] = {
B(0x00)|B(0x01)|B(0x02)|B(0x03),
B(0x04)|B(0x05)|B(0x06)|B(0x07),
B(0x08)|B(0x09)|B(0x0a)|B(0x0b),
B(0x0c)|B(0x0d)|B(0x0e)|B(0x0f),
// 512K Expander
B(0x10)|B(0x11)|B(0x12)|B(0x13),
B(0x14)|B(0x15)|B(0x16)|B(0x17),
// 2MB Expander A to one meg
B(0x30),B(0x31),B(0x32),B(0x33),
B(0x34),B(0x35),B(0x36),B(0x37),
// 2MB Expander B
B(0x50),B(0x51),B(0x52),B(0x53),
B(0x54),B(0x55),B(0x56),B(0x57),
B(0x70),B(0x71),B(0x72),B(0x73),
B(0x74),B(0x75),B(0x76),B(0x77),
};
#undef B
void
mii_init(
mii_t *mii )
{
memset(mii, 0, sizeof(*mii));
mii->speed = 1.023;
mii->speed = MII_SPEED_NTSC;
mii->timer.map = 0;
for (int i = 0; i < MII_BANK_COUNT; i++)
mii->bank[i] = _mii_banks_init[i];
mii->bank[MII_BANK_ROM].mem = (uint8_t*)&iie_enhanced_rom_bin[0];
for (int i = 0; i < MII_BANK_COUNT; i++)
mii_bank_init(&mii->bank[i]);
uint8_t *mem = realloc(mii->bank[MII_BANK_MAIN].mem, 0x10000);
mii->bank[MII_BANK_MAIN].mem = mem;
mii->bank[MII_BANK_BSR].mem = mem;
mii->bank[MII_BANK_BSR_P2].mem = mem;
mii->ramworks.avail = 0;
mii_bank_update_ramworks(mii, 0);
mii->cpu.trap = MII_TRAP;
// these are called once, regardless of reset
mii_dd_system_init(mii, &mii->dd);
@ -554,19 +636,18 @@ mii_init(
mii_reset(mii, true);
mii->cpu_state = mii_cpu_init(&mii->cpu);
#if MII_65C02_DIRECT_ACCESS
mii->cpu.access_param = mii;
mii->cpu.access = _mii_cpu_direct_access_cb;
#endif
for (int i = 0; i < 7; i++)
mii->slot[i].id = i;
// srandom(time(NULL));
for (int i = 0; i < 256; i++)
mii->random[i] = random();
mii_bank_install_access_cb(&mii->bank[MII_BANK_CARD_ROM],
_mii_deselect_auxrom, mii, 0xcf, 0xcf);
mii_bank_install_access_cb(&mii->bank[MII_BANK_ROM],
_mii_deselect_auxrom, mii, 0xcf, 0xcf);
mii_bank_install_access_cb(&mii->bank[MII_BANK_ROM],
_mii_select_c3rom, mii, 0xc3, 0xc3);
_mii_select_c3introm, mii, 0xc3, 0xc3);
}
void
@ -574,7 +655,13 @@ mii_prepare(
mii_t *mii,
uint32_t flags )
{
// printf("%s driver table\n", __func__);
int banks = (flags >> MII_INIT_RAMWORKS_BIT) & 0xf;
banks = 12;
if (banks > 12)
banks = 12;
for (int i = 0; i < banks; i++) // add available banks
mii->ramworks.avail |= _mii_ramworks3_config[i];
mii_slot_drv_t * drv = mii_slot_drv_list;
while (drv) {
printf("%s driver: %s\n", __func__, drv->name);
@ -595,6 +682,12 @@ mii_dispose(
}
for (int i = 0; i < MII_BANK_COUNT; i++)
mii_bank_dispose(&mii->bank[i]);
for (int i = 0; i < 128; i++ ) {
if (mii->ramworks.bank[i]) {
free(mii->ramworks.bank[i]);
mii->ramworks.bank[i] = NULL;
}
}
mii_speaker_dispose(&mii->speaker);
mii_dd_system_dispose(&mii->dd);
mii->state = MII_INIT;
@ -609,17 +702,19 @@ mii_reset(
mii->state = MII_RUNNING;
mii->cpu_state.reset = 1;
mii_bank_t * main = &mii->bank[MII_BANK_MAIN];
mii_bank_t * sw = &mii->bank[MII_BANK_SW];
mii->sw_state = M_BSRWRITE | M_BSRPAGE2;
mii_bank_poke(main, SWSLOTC3ROM, 0);
mii_bank_poke(main, SWRAMRD, 0);
mii_bank_poke(main, SWRAMWRT, 0);
mii_bank_poke(main, SWALTPZ, 0);
mii_bank_poke(main, SW80STORE, 0);
mii_bank_poke(main, SW80COL, 0);
mii_bank_poke(sw, SWSLOTC3ROM, 0);
mii_bank_poke(sw, SWRAMRD, 0);
mii_bank_poke(sw, SWRAMWRT, 0);
mii_bank_poke(sw, SWALTPZ, 0);
mii_bank_poke(sw, SW80STORE, 0);
mii_bank_poke(sw, SW80COL, 0);
mii_bank_poke(sw, SWRAMWORKS_BANK, 0);
mii->mem_dirty = 1;
if (cold) {
/* these HAS to be reset in that state somehow */
mii_bank_poke(main, SWINTCXROM, 0);
mii_bank_poke(sw, SWINTCXROM, 0);
uint8_t z[2] = {0x55,0x55};
mii_bank_write(main, 0x3f2, z, 2);
}
@ -639,9 +734,10 @@ mii_mem_access(
bool wr,
bool do_sw)
{
if (!do_sw && addr >= 0xc000 && addr <= 0xc0ff)
if (!do_sw && addr >= 0xc000 && addr <= 0xc0ff && addr != 0xcfff)
return;
uint8_t done =
_mii_deselect_cXrom(mii, addr, d, wr) ||
mii_access_keyboard(mii, addr, d, wr) ||
mii_access_video(mii, addr, d, wr) ||
mii_access_soft_switches(mii, addr, d, wr);
@ -651,11 +747,14 @@ mii_mem_access(
if (wr) {
uint8_t m = mii->mem[page].write;
mii_bank_t * b = &mii->bank[m];
if (b->ro) {
// printf("%s write to RO bank %s %04x:%02x\n",
// __func__, b->name, addr, *d);
} else
if (!b->ro)
mii_bank_write(b, addr, d, 1);
else {
// writing to ROM *is* sort of OK in certain circumstances, if
// there is a 'special' handler of the rom page. the NSC and the
// mockinboard are examples of this.
mii_bank_access(b, addr, d, 1, true);
}
} else {
uint8_t m = mii->mem[page].read;
mii_bank_t * b = &mii->bank[m];
@ -670,7 +769,9 @@ _mii_handle_trap(
// printf("%s TRAP hit PC: %04x\n", __func__, mii->cpu.PC);
mii->cpu_state.sync = 1;
mii->cpu_state.trap = 0;
#if MII_65C02_DIRECT_ACCESS == 0
mii->cpu.state = NULL;
#endif
uint8_t trap = mii_read_one(mii, mii->cpu.PC);
mii->cpu.PC += 1;
// printf("%s TRAP %02x return PC %04x\n", __func__, trap, mii->cpu.PC);
@ -766,6 +867,73 @@ mii_timer_run(
}
}
#if MII_65C02_DIRECT_ACCESS
static mii_cpu_state_t
_mii_cpu_direct_access_cb(
struct mii_cpu_t *cpu,
mii_cpu_state_t access )
{
mii_t *mii = cpu->access_param;
uint8_t cycle = mii->timer.last_cycle;
mii_timer_run(mii,
mii->cpu.cycle > cycle ? mii->cpu.cycle - cycle :
mii->cpu.cycle);
mii->timer.last_cycle = mii->cpu.cycle;
const uint16_t addr = access.addr;
int wr = access.w;
if (access.sync) {
// log PC for the running disassembler display
mii->trace.log[mii->trace.idx] = mii->cpu.PC;
mii->trace.idx = (mii->trace.idx + 1) & (MII_PC_LOG_SIZE - 1);
}
if (unlikely(mii->debug.bp_map)) {
for (int i = 0; i < (int)sizeof(mii->debug.bp_map) * 8; i++) {
if (!(mii->debug.bp_map & (1 << i)))
continue;
if (addr >= mii->debug.bp[i].addr &&
addr < mii->debug.bp[i].addr + mii->debug.bp[i].size) {
if (((mii->debug.bp[i].kind & MII_BP_R) && !wr) ||
((mii->debug.bp[i].kind & MII_BP_W) && wr)) {
if (1 || !mii->debug.bp[i].silent) {
printf("BREAKPOINT %d at %04x PC:%04x\n",
i, addr, mii->cpu.PC);
mii_dump_run_trace(mii);
mii_dump_trace_state(mii);
mii->cpu.instruction_run = 0;
mii->state = MII_STOPPED;
}
}
if (!(mii->debug.bp[i].kind & MII_BP_STICKY))
mii->debug.bp_map &= ~(1 << i);
mii->debug.bp[i].kind |= MII_BP_HIT;
}
}
}
mii_mem_access(mii, addr, &access.data, wr, true);
return access;
}
void
mii_run(
mii_t *mii)
{
/* this runs all cycles for one instruction */
if (unlikely(mii->state != MII_RUNNING || mii->trace_cpu > 1)) {
printf("tracing\n");
mii->cpu.instruction_run = 0;
} else
mii->cpu.instruction_run = 100000;
mii->cpu_state = mii_cpu_run(&mii->cpu, mii->cpu_state);
if (unlikely(mii->cpu_state.trap))
_mii_handle_trap(mii);
}
#else
void
mii_run(
mii_t *mii)
@ -819,11 +987,8 @@ mii_run(
// log PC for the running disassembler display
mii->trace.log[mii->trace.idx] = mii->cpu.PC;
mii->trace.idx = (mii->trace.idx + 1) & (MII_PC_LOG_SIZE - 1);
for (int i = 0; i < 7; i++) {
if (mii->slot[i].drv && mii->slot[i].drv->run)
mii->slot[i].drv->run(mii, &mii->slot[i]);
}
}
#endif
//! Read one byte from and addres, using the current memory mapping
uint8_t
@ -870,3 +1035,51 @@ mii_write_word(
d = w >> 8;
mii_mem_access(mii, addr + 1, &d, 1, false);
}
void
mii_cpu_step(
mii_t *mii,
uint32_t count )
{
if (mii->state != MII_STOPPED) {
printf("mii: can't step/next, not stopped\n");
return;
}
mii->trace.step_inst = count ? count : 1;
__sync_synchronize();
mii->state = MII_STEP;
}
void
mii_cpu_next(
mii_t *mii)
{
if (mii->state != MII_STOPPED) {
printf("mii: can't step/next, not stopped\n");
return;
}
// read current opcode, find how how many bytes it take,
// then put a temporary breakpoint to the next PC.
// all of that if this is not a relative branch of course, in
// which case we use a normal 'step' behaviour
uint8_t op;
mii_mem_access(mii, mii->cpu.PC, &op, false, false);
printf("NEXT opcode %04x:%02x\n", mii->cpu.PC, op);
if (op == 0x20) { // JSR here?
// set a temp breakpoint on reading 3 bytes from PC
if (mii->debug.bp_map != 0xffff) {
int i = ffsl(~mii->debug.bp_map) - 1;
mii->debug.bp[i].addr = mii->cpu.PC + 3;
mii->debug.bp[i].kind = MII_BP_R;
mii->debug.bp[i].size = 1;
mii->debug.bp[i].silent = 1;
mii->debug.bp_map |= 1 << i;
__sync_synchronize();
mii->state = MII_RUNNING;
return;
}
printf("%s no more breakpoints available\n", __func__);
} else {
mii_cpu_step(mii, 1);
}
}

View File

@ -18,6 +18,7 @@
#include "mii_speaker.h"
#include "mii_mouse.h"
#include "mii_analog.h"
#include "mii_vcd.h"
#define likely(x) __builtin_expect(!!(x), 1)
#define unlikely(x) __builtin_expect(!!(x), 0)
@ -26,13 +27,16 @@ enum {
MII_BANK_MAIN = 0, // main 48K address space
MII_BANK_BSR, // 0xd000 - 0xffff bank switched RAM 16KB
MII_BANK_BSR_P2, // 0xd000 - 0xe000 bank switched RAM aux 4KB
// this one is the fixed one, used by video
MII_BANK_AUX_BASE, // aux 48K address space (80 cols card)
// these one can 'move' in the block of ramworks ram
MII_BANK_AUX, // aux 48K address space (80 cols card)
MII_BANK_AUX_BSR, // 0xd000 - 0xffff bank switched RAM aux 16KB
MII_BANK_AUX_BSR_P2, // 0xd000 - 0xe000 bank switched RAM aux 4KB (aux bank)
MII_BANK_ROM, // 0xc000 - 0xffff 16K ROM
MII_BANK_CARD_ROM, // 0xc100 - 0xcfff Card ROM access
MII_BANK_SW, // 0xc000 - 0xc0ff Softswitches
MII_BANK_COUNT,
};
@ -84,20 +88,32 @@ typedef struct mii_trace_t {
typedef uint64_t (*mii_timer_p)(
mii_t * mii,
void * param );
#define MII_SPEED_NTSC 1.0227271429 // 14.31818 MHz / 14
#define MII_SPEED_PAL 1.0178571429 // 14.25 MHz / 14
#define MII_SPEED_TITAN 3.58
/*
* principal emulator state, for a faceless emulation
*/
typedef struct mii_t {
mii_cpu_t cpu;
mii_cpu_state_t cpu_state;
/* this is the video frame/VBL rate vs 60hz, default to MII_SPEED_NTSC */
float speed;
unsigned int state;
/*
* These are 'cycle timers' -- they count down from a set value,
* and stop at 0 (or possiblu -1 or -2, depending on the instructions)
* and stop at 0 (or possibly -1 or -2, depending on the instructions)
* and call the callback (if present).
* The callback returns the number of cycles to wait until the next
* call.
*/
struct {
uint64_t map;
uint64_t map;
#if MII_65C02_DIRECT_ACCESS
uint8_t last_cycle;
#endif
struct {
mii_timer_p cb;
void * param;
@ -105,22 +121,28 @@ typedef struct mii_t {
const char * name; // debug
} timers[64];
} timer;
/* this is the video frame/VBL rate vs 60hz, default to 1.0 */
float speed;
mii_cpu_t cpu;
mii_cpu_state_t cpu_state;
/*
* bank index for each memory page number, this is recalculated
* everytime a soft switch is triggered
* everytime a MMU soft switch is triggered
*/
struct {
uint8_t read : 4, write : 4;
union {
struct {
uint8_t write : 4, read : 4;
};
uint8_t both;
};
} mem[256];
int mem_dirty; // recalculate mem[] on next access
struct {
unsigned __int128 avail;
uint8_t * bank[128];
} ramworks;
uint32_t sw_state; // B_SW* bitfield
mii_trace_t trace;
int trace_cpu;
mii_trap_t trap;
mii_signal_pool_t sig_pool; // vcd support
/*
* Used for debugging only
*/
@ -150,8 +172,13 @@ typedef struct mii_t {
} mii_t;
enum {
MII_INIT_NSC = (1 << 0), // Install no slot clock
MII_INIT_TITAN = (1 << 1), // Install Titan 'card'
MII_INIT_NSC = (1 << 0), // Install no slot clock
MII_INIT_TITAN = (1 << 1), // Install Titan 'card'
MII_INIT_SILENT = (1 << 2), // No audio, ever
MII_INIT_MOCKINGBOARD = (1 << 3), // Install mockingboard
// number of 256KB banks added to the ramworks
MII_INIT_RAMWORKS_BIT = 4, // bit 4 in flags. Can be up to 12
MII_INIT_DEFAULT = MII_INIT_NSC,
};
@ -332,3 +359,12 @@ mii_register_trap(
#define MII_MISH_KIND MISH_FCC('m','i','i',' ')
#define MII_MISH(_name,_cmd) \
MISH_CMD_REGISTER_KIND(_name, _cmd, 0, MII_MISH_KIND)
void
mii_cpu_step(
mii_t *mii,
uint32_t count );
void
mii_cpu_next(
mii_t *mii);

View File

@ -26,10 +26,23 @@ mii_cpu_init(
.addr = 0,
.reset = 1,
};
#if MII_65C02_DIRECT_ACCESS
#else
cpu->state = NULL;
#endif
return s;
}
#if MII_65C02_DIRECT_ACCESS
#define _FETCH(_val) { \
s.addr = _val; s.w = 0; cpu->cycle++; \
s = cpu->access(cpu, s); \
}
#define _STORE(_addr, _val) { \
s.addr = _addr; s.data = _val; s.w = 1; cpu->cycle++; \
s = cpu->access(cpu, s); \
}
#else
#define _FETCH(_val) { \
s.addr = _val; s.w = 0; cpu->cycle++; \
pt_yield(cpu->state); \
@ -38,7 +51,7 @@ mii_cpu_init(
s.addr = _addr; s.data = _val; s.w = 1; cpu->cycle++; \
pt_yield(cpu->state); \
}
#endif
#define _NZC(_val) { \
uint16_t v = (_val); \
@ -60,8 +73,12 @@ mii_cpu_run(
mii_cpu_t *cpu,
mii_cpu_state_t s)
{
#if MII_65C02_DIRECT_ACCESS
mii_op_desc_t d;
#else
mii_op_desc_t d = mii_cpu_op[cpu->IR].desc;
pt_start(cpu->state);
#endif
next_instruction:
if (unlikely(s.reset)) {
s.reset = 0;
@ -71,7 +88,7 @@ next_instruction:
cpu->S = 0xFF;
MII_SET_P(cpu, 0);
}
if (unlikely(s.irq) && cpu->P.I == 0) {
if (unlikely(s.irq && cpu->P.I == 0)) {
if (!cpu->IRQ)
cpu->IRQ = 1;
}
@ -96,6 +113,7 @@ next_instruction:
// we dont' reset the cycle here, that way calling code has a way of knowing
// how many cycles were used by the previous instruction
_FETCH(cpu->PC);
cpu->total_cycle += cpu->cycle;
s.sync = 0;
cpu->cycle = 0;
cpu->PC++;
@ -103,8 +121,12 @@ next_instruction:
d = mii_cpu_op[cpu->IR].desc;
cpu->ir_log = (cpu->ir_log << 8) | cpu->IR;
s.trap = cpu->trap && (cpu->ir_log & 0xffff) == cpu->trap;
if (unlikely(s.trap))
if (unlikely(s.trap)) {
cpu->ir_log = 0;
#if MII_65C02_DIRECT_ACCESS
return s;
#endif
}
switch (d.mode) {
case IMM:
_FETCH(cpu->PC++); cpu->_D = s.data;
@ -126,16 +148,26 @@ next_instruction:
case ABS_X: { // $xxxx,X
_FETCH(cpu->PC++); cpu->_P = s.data;
_FETCH(cpu->PC++); cpu->_P |= s.data << 8;
/*
* this seems to be only used by a2audit, ever, which is bloody
* annoying, so we just fake it to pass the test
*/
if (cpu->IR == 0xfe && cpu->X == 0 && cpu->_P == 0xc083) {
// printf("Fooling a2audit\n");
_FETCH(cpu->_P); // false read
}
cpu->_P += cpu->X;
if ((cpu->_P & 0xff00) != (s.data << 8))
cpu->cycle++;
if ((cpu->_P & 0xff00) != (s.data << 8)) {
_FETCH(cpu->PC); // false read
}
} break;
case ABS_Y: { // $xxxx,Y
_FETCH(cpu->PC++); cpu->_P = s.data;
_FETCH(cpu->PC++); cpu->_P |= s.data << 8;
cpu->_P += cpu->Y;
if ((cpu->_P & 0xff00) != (s.data << 8))
cpu->cycle++;
if ((cpu->_P & 0xff00) != (s.data << 8)) {
_FETCH(cpu->PC); // false read
}
} break;
case IND_X: { // ($xx,X)
_FETCH(cpu->PC++); cpu->_D = s.data;
@ -255,7 +287,7 @@ next_instruction:
case 0x80:
{ // BRA
cpu->_P = cpu->PC + (int8_t)cpu->_P;
_FETCH(cpu->PC);// cpu->cycle++;
_FETCH(cpu->PC);
if ((cpu->_P & 0xff00) != (cpu->PC & 0xff00))
cpu->cycle++;
cpu->PC = cpu->_P;
@ -274,7 +306,7 @@ next_instruction:
{ // BRK
// Turns out BRK is a 2 byte opcode, who knew? well that guy did:
// https://www.nesdev.org/the%20'B'%20flag%20&%20BRK%20instruction.txt#:~:text=A%20note%20on%20the%20BRK,opcode%2C%20and%20not%20just%201.
_FETCH(cpu->PC++); // cpu->cycle++;
_FETCH(cpu->PC++);
s.irq = 1;
cpu->IRQ = 2; // BRK sort of IRQ interrupt
} break;
@ -598,6 +630,7 @@ next_instruction:
/* Apparently these NOPs use 3 bytes, according to the tests */
case 0x5c: case 0xdc: case 0xfc:
_FETCH(cpu->PC++);
// fall through
/* Apparently these NOPs use 2 bytes, according to the tests */
case 0x02: case 0x22: case 0x42: case 0x62: case 0x82:
case 0xC2: case 0xE2: case 0x44: case 0x54: case 0xD4:
@ -615,7 +648,15 @@ next_instruction:
if (d.w) {
_STORE(cpu->_P, cpu->_D);
}
#if MII_65C02_DIRECT_ACCESS
// we don't need to do anything here, the store already did it
if (likely(cpu->instruction_run)) {
cpu->instruction_run--;
goto next_instruction;
}
#else
goto next_instruction;
pt_end(cpu->state);
#endif
return s;
}

Some files were not shown because too many files have changed in this diff Show More