mirror of
https://github.com/ivanizag/izapple2.git
synced 2026-10-25 03:02:24 +00:00
A file dropped on the window went to drive 1 or drive 2 depending on the half it landed on. There was no way to know beforehand which drive was which or what each one had inserted, and no sign afterwards of where the file went. The machines with two Disk II cards have four drives and only the first two could be reached. F8 now shows a screen with one area per removable media drive, each with the name of its drive and the image it has inserted, or EMPTY, and the same screen appears for a moment after a drop with the drive that got the file marked. It is a text screen of 80 columns rendered the same way the help is, so SnapshotMessageGenerator takes the number of columns now. The drives register with a label and tell the media they have, so the frontends ask the machine with GetRemovableMediaDrives instead of assuming two. The name of the media is written by the emulation goroutine and read by the frontends, hence the atomic access. The new frontend/shared package holds what the frontends have in common and the emulator library has no place for: the composition of the screen and the mapping from a position to a drive. a2sdl reads the position of the pointer on the desktop when the file is dropped. The mouse motion events do not arrive while another application drags a file, so the last known position was stale and the file could land on the wrong drive. a2sdl3 shows the areas while the file is being dragged over the window, which is something only SDL3 reports. a2ebiten had no drag and drop at all, although the help it shows said otherwise. Ebitengine hides the paths of the files dropped, but it opens the real files and the handle tells the path back, so the images are loaded like on every other frontend, compressed files included.
195 lines
4.5 KiB
Go
195 lines
4.5 KiB
Go
package screen
|
|
|
|
import (
|
|
"image"
|
|
"image/color"
|
|
"strings"
|
|
)
|
|
|
|
// SnapshotParts the currently visible screen
|
|
func SnapshotParts(vs VideoSource, screenMode int) *image.RGBA {
|
|
videoMode := vs.GetCurrentVideoMode()
|
|
isSecondPage := (videoMode & VideoSecondPage) != 0
|
|
videoBase := videoMode & VideoBaseMask
|
|
mixMode := videoMode & VideoMixTextMask
|
|
modifiers := videoMode & VideoModifiersMask
|
|
|
|
snapScreen := snapshotByMode(vs, videoMode, screenMode)
|
|
snapPage1 := snapshotByMode(vs, videoMode&^VideoSecondPage, screenMode)
|
|
snapPage2 := snapshotByMode(vs, videoMode|VideoSecondPage, screenMode)
|
|
var snapAux *image.RGBA
|
|
|
|
/*
|
|
if videoBase == videoRGBMix {
|
|
_, mask := snapshotDoubleHiResModeMono(a, isSecondPage, true /*isRGBMixMode*/ /*, color.White)
|
|
snapAux = filterMask(mask)
|
|
}*/
|
|
|
|
if videoBase == VideoText40RGB {
|
|
snapAux = snapshotText40RGBColors(vs, isSecondPage)
|
|
} else {
|
|
switch mixMode {
|
|
case VideoMixText80:
|
|
snapAux = snapshotByMode(vs, VideoText80|modifiers, screenMode)
|
|
case VideoMixText40RGB:
|
|
snapAux = snapshotByMode(vs, VideoText40RGB|modifiers, screenMode)
|
|
default:
|
|
snapAux = snapshotByMode(vs, VideoText40|modifiers, screenMode)
|
|
}
|
|
}
|
|
|
|
return mixFourSnapshots([]*image.RGBA{snapScreen, snapAux, snapPage1, snapPage2})
|
|
}
|
|
|
|
// VideoModeName returns the name of the current video mode
|
|
func VideoModeName(vs VideoSource) string {
|
|
videoMode := vs.GetCurrentVideoMode()
|
|
videoBase := videoMode & VideoBaseMask
|
|
mixMode := videoMode & VideoMixTextMask
|
|
|
|
var name string
|
|
|
|
switch videoBase {
|
|
case VideoText40:
|
|
name = "TEXT40COL"
|
|
case VideoText80:
|
|
name = "TEXT80COL"
|
|
case VideoText40RGB:
|
|
name = "TEXT40COLRGB"
|
|
case VideoGR:
|
|
name = "GR"
|
|
case VideoDGR:
|
|
name = "DGR"
|
|
case VideoHGR:
|
|
name = "HGR"
|
|
case VideoDHGR:
|
|
name = "DHGR"
|
|
case VideoMono560:
|
|
name = "Mono560"
|
|
case VideoRGBMix:
|
|
name = "RGBMIX"
|
|
case VideoRGB160:
|
|
name = "RGB160"
|
|
case VideoSHR:
|
|
name = "SHR"
|
|
case VideoVidex:
|
|
name = "VIDEX"
|
|
default:
|
|
name = "Unknown video mode"
|
|
}
|
|
|
|
if (videoMode & VideoSecondPage) != 0 {
|
|
name += "-PAGE2"
|
|
}
|
|
|
|
if (videoMode & VideoAltText) != 0 {
|
|
name += "-ALT"
|
|
}
|
|
|
|
if (videoMode & VideoFourColors) != 0 {
|
|
name += "-4COLORS"
|
|
}
|
|
|
|
switch mixMode {
|
|
case VideoMixText40:
|
|
name += "-MIX40"
|
|
case VideoMixText80:
|
|
name += "-MIX80"
|
|
case VideoMixText40RGB:
|
|
name += "-MIX40RGB"
|
|
}
|
|
|
|
return name
|
|
}
|
|
|
|
func mixFourSnapshots(snaps []*image.RGBA) *image.RGBA {
|
|
width := snaps[0].Rect.Dx()
|
|
height := snaps[0].Rect.Dy()
|
|
size := image.Rect(0, 0, width*2, height*2)
|
|
out := image.NewRGBA(size)
|
|
|
|
for i := 1; i < 4; i++ {
|
|
if snaps[i].Bounds().Dx() < width {
|
|
snaps[i] = doubleWidthFilter(snaps[i])
|
|
}
|
|
}
|
|
|
|
for y := range height {
|
|
for x := range width {
|
|
out.Set(x, y, snaps[0].At(x, y))
|
|
out.Set(x+width, y, snaps[1].At(x, y))
|
|
out.Set(x, y+height, snaps[2].At(x, y))
|
|
out.Set(x+width, y+height, snaps[3].At(x, y))
|
|
}
|
|
}
|
|
|
|
return out
|
|
}
|
|
|
|
func doubleWidthFilter(in *image.RGBA) *image.RGBA {
|
|
b := in.Bounds()
|
|
size := image.Rect(0, 0, 2*b.Dx(), b.Dy())
|
|
out := image.NewRGBA(size)
|
|
for y := b.Min.Y; y < b.Max.Y; y++ {
|
|
for x := b.Min.X; x < b.Max.X; x++ {
|
|
c := in.At(x, y)
|
|
out.Set(2*x, y, c)
|
|
out.Set(2*x+1, y, c)
|
|
}
|
|
}
|
|
return out
|
|
}
|
|
|
|
// SnapshotCharacterGenerator shows the current character set
|
|
func SnapshotCharacterGenerator(vs VideoSource, isAltText bool) *image.RGBA {
|
|
text := make([]uint8, textLines*text40Columns)
|
|
for l := range textLines {
|
|
for c := range text40Columns {
|
|
text[text40Columns*l+c] = 0x20 + 0x80 // Space
|
|
}
|
|
}
|
|
|
|
for l := range 8 {
|
|
for c := range 32 {
|
|
text[text40Columns*(2*l+4)+c+4] = uint8(l*32 + c)
|
|
}
|
|
}
|
|
|
|
snap := renderText(vs, text, isAltText, nil, color.White)
|
|
snap = linesSeparatedFilter(snap)
|
|
return snap
|
|
}
|
|
|
|
// SnapshotMessageGenerator shows a message on the screen, on 40 or 80 columns.
|
|
// The character generator of the machine is used, but not its text memory, so
|
|
// it works whatever the machine is displaying and whether or not it has 80
|
|
// columns of its own.
|
|
func SnapshotMessageGenerator(vs VideoSource, message string, is80Columns bool) *image.RGBA {
|
|
if !vs.SupportsLowercase() {
|
|
message = strings.ToUpper(message)
|
|
}
|
|
|
|
columns := text40Columns
|
|
if is80Columns {
|
|
columns = 2 * text40Columns
|
|
}
|
|
|
|
lines := strings.Split(message, "\n")
|
|
text := make([]uint8, textLines*columns)
|
|
for i := range text {
|
|
text[i] = 0x20 + 0x80 // Space
|
|
}
|
|
|
|
for l, line := range lines {
|
|
for c, char := range line {
|
|
if c < columns && l < textLines {
|
|
text[columns*l+c] = uint8(char) + 0x80
|
|
}
|
|
}
|
|
}
|
|
|
|
snap := renderText(vs, text, false, nil, color.White)
|
|
snap = linesSeparatedFilter(snap)
|
|
return snap
|
|
}
|