diff --git a/.agents/skills/prog8-coder/SKILL.md b/.agents/skills/prog8-coder/SKILL.md index 9322b1e33..8b837fcf1 100644 --- a/.agents/skills/prog8-coder/SKILL.md +++ b/.agents/skills/prog8-coder/SKILL.md @@ -94,8 +94,11 @@ registers survive calls. Long operations may clobber `R12-R15`. ## Syntax Pitfalls - Hex uses `$FF`, binary uses `%1010`, and casts use `expression as type`. -- There is no automatic type widening: `byte * byte` remains a byte. Cast - explicitly when a wider result is needed. +- There is no automatic type widening in expression results: `byte * byte` + remains a byte. Cast explicitly when a wider result is needed. Assignments, + call arguments, and mixed-type expressions do widen implicitly + (`ubyte`/`byte` to `word`/`uword` to `long`); only same-width signedness + reinterpretation (`uword` to `word` and back) needs an explicit `as` cast. - `&` is an untyped address and `&&` is a typed pointer. Complex pointer field assignments may require `ptr^^.field`. - `and`, `or`, `xor`, and `not` are logical operators. Use `&`, `|`, `^`, and diff --git a/compiler/res/prog8lib/amiga1200/diskfont.p8 b/compiler/res/prog8lib/amiga1200/diskfont.p8 new file mode 100644 index 000000000..76288129f --- /dev/null +++ b/compiler/res/prog8lib/amiga1200/diskfont.p8 @@ -0,0 +1,5 @@ +%import shared_amiga_diskfont + +diskfont { + %option no_symbol_prefixing, ignore_unused +} diff --git a/compiler/res/prog8lib/amiga1200/syslib.p8 b/compiler/res/prog8lib/amiga1200/syslib.p8 index cf3bcbb1e..289632bda 100644 --- a/compiler/res/prog8lib/amiga1200/syslib.p8 +++ b/compiler/res/prog8lib/amiga1200/syslib.p8 @@ -41,6 +41,7 @@ sys { pointer @shared IFFParseBase ; loaded from disk on demand pointer @shared AsyncIOBase ; loaded from disk on demand pointer @shared LowLevelBase ; kickstart 2.0+, loaded from disk on demand + pointer @shared DiskfontBase ; loaded from disk on demand pointer @shared TimerBase ; opened on demand pointer @shared AHIBase ; ahi.device base, opened on demand by ahi.open(); for the low-level ahi.* LVO calls diff --git a/compiler/res/prog8lib/amiga500/diskfont.p8 b/compiler/res/prog8lib/amiga500/diskfont.p8 new file mode 100644 index 000000000..76288129f --- /dev/null +++ b/compiler/res/prog8lib/amiga500/diskfont.p8 @@ -0,0 +1,5 @@ +%import shared_amiga_diskfont + +diskfont { + %option no_symbol_prefixing, ignore_unused +} diff --git a/compiler/res/prog8lib/amiga500/syslib.p8 b/compiler/res/prog8lib/amiga500/syslib.p8 index 91c6d2ed3..5f6508d01 100644 --- a/compiler/res/prog8lib/amiga500/syslib.p8 +++ b/compiler/res/prog8lib/amiga500/syslib.p8 @@ -41,6 +41,7 @@ sys { pointer @shared IFFParseBase ; kickstart 2.0+, loaded from disk on demand pointer @shared AsyncIOBase ; loaded from disk on demand pointer @shared LowLevelBase ; kickstart 2.0+, loaded from disk on demand + pointer @shared DiskfontBase ; loaded from disk on demand pointer @shared TimerBase ; opened on demand ^^ubyte @shared arguments ; CLI argument string (null-terminated), or NULL if Workbench launch diff --git a/compiler/res/prog8lib/shared_amiga_diskfont.p8 b/compiler/res/prog8lib/shared_amiga_diskfont.p8 new file mode 100644 index 000000000..0bd0da498 --- /dev/null +++ b/compiler/res/prog8lib/shared_amiga_diskfont.p8 @@ -0,0 +1,65 @@ +;; Hand-maintained diskfont.library interface, trimmed to pre-3.0 content (up to Kickstart 2.0). +;; (Outline engine, diskfont control and tagged-font matching need 3.0+ and were removed.) +;; Library base: _DiskfontBase in prog8: sys.DiskfontBase +;; Bank: 13 + +%import exec +%import graphics + +diskfont { + %option merge, no_symbol_prefixing + + sub openlib() -> bool { + ; not opened at startup: diskfont.library lives on disk (not in ROM), + ; so it must be opened on demand and may legitimately fail + sys.DiskfontBase = exec.OpenLibrary("diskfont.library", 0) + return sys.DiskfontBase!=0 + } + + sub closelib() { + if sys.DiskfontBase!=0 { + exec.CloseLibrary(sys.DiskfontBase) + sys.DiskfontBase = 0 + } + } + + private graphics.TextAttr[1] openfont_attr ; scratch for openfont() + + sub openfont(str name, uword size, ubyte style) -> ^^graphics.TextFont { + ; simplified OpenDiskFont with no flags (style e.g. graphics.FS_NORMAL). + ; Name is like "helvetica.font". Returns 0 when the font cannot be found. + ^^graphics.TextAttr fa = &openfont_attr[0] + fa.Name = name + fa.YSize = size + fa.Style = style + fa.Flags = 0 + return OpenDiskFont(fa) + } + + extsub @bank 13 -30 = OpenDiskFont(^^graphics.TextAttr textAttr @A0) -> ^^graphics.TextFont @D0 + extsub @bank 13 -36 = AvailFonts(pointer buffer @A0, long bufBytes @D0, long flags @D1) -> long @D0 + extsub @bank 13 -42 = NewFontContents(pointer fontsLock @A0, str fontName @A1) -> pointer @D0 + extsub @bank 13 -48 = DisposeFontContents(pointer fontContentsHeader @A1) + extsub @bank 13 -54 = NewScaledDiskFont(^^graphics.TextFont sourceFont @A0, ^^graphics.TextAttr destTextAttr @A1) -> pointer @D0 + + ; ---- constants ---- + const uword MAXFONTPATH = $0100 + const uword FCH_ID = $0f00 + const uword TFCH_ID = $0f02 + const uword DFH_ID = $0f80 + const ubyte MAXFONTNAME = $0020 + const ubyte AFB_MEMORY = 0 + const ubyte AFF_MEMORY = $0001 + const ubyte AFB_DISK = 1 + const ubyte AFF_DISK = $0002 + const ubyte AFB_SCALED = 2 + const ubyte AFF_SCALED = $0004 + const ubyte AFB_BITMAP = 3 + const ubyte AFF_BITMAP = $0008 + const ubyte AFB_OTAG = 4 + const ubyte AFF_OTAG = $0010 + const ubyte AFB_CHARSET = 5 + const ubyte AFF_CHARSET = $0020 + const ubyte AFB_TYPE = 6 + const ubyte AFF_TYPE = $0040 +} diff --git a/compiler/res/prog8lib/shared_amiga_gfx.p8 b/compiler/res/prog8lib/shared_amiga_gfx.p8 index 9dcd0ce72..055b5dc93 100644 --- a/compiler/res/prog8lib/shared_amiga_gfx.p8 +++ b/compiler/res/prog8lib/shared_amiga_gfx.p8 @@ -3,11 +3,12 @@ ; Hides NewScreen/NewWindow boilerplate behind a gfx_lores-style API: ; per-call pen, client-relative word coordinates, explicit open/close. ; Kickstart 1.3 compatible: uses only OpenScreen/OpenWindow (no taglists, -; no AllocBitMap, no DisplayID). All rasterization is done by +; no AllocBitMap, no DisplayID), except fillto() which needs SetOutlinePen (3.0+). +; All rasterization is done by ; graphics.library; filled ellipses and flood fills use AreaEllipse/Flood ; with a chip-ram TmpRas scratch buffer allocated when the window opens. ; No double buffering (use two screens/windows or raw calls for that). -; Read rastport for direct graphics.library calls, window to pump IDCMP. +; Use rastport for direct graphics.library calls, window to pump IDCMP. %import intuition %import graphics @@ -33,13 +34,13 @@ amiga_gfx { private graphics.TmpRas[1] tmpras_store private graphics.AreaInfo[1] area_store private graphics.TextAttr[1] font_store - private ubyte[20] area_vectors ; 5 bytes per vector, AreaEllipse needs 2 + private ubyte[20] area_vectors ; 5 bytes per vector, room for 4 private pointer tmpras_buf private long tmpras_size sub fullscreen(uword w, uword h, ubyte ndepth, uword viewmodes, str title) -> bool { ; fullscreen custom screen + backdrop borderless window. - ; viewmodes: 0 = lores, graphics.HIRES for 640px, + graphics.INTERLACE for halfbrite-height. + ; viewmodes: 0 = lores, graphics.HIRES for 640px, + graphics.INTERLACE for interlaced (doubled height). if w == 0 or h == 0 return false if rastport != 0 @@ -48,13 +49,13 @@ amiga_gfx { if not untitled untitled = title[0] == 0 ; NOTE: struct literals must be constant, so allocate zeroed and patch fields. - ^^intuition.NewScreen ns = [0, 0, 0, 0, 0, 0 as ubyte, 0 as ubyte, 0, 0, 0, 0, 0, 0] + ^^intuition.NewScreen ns = [0, 0, 0, 0, 0, ; edges, size, depth + 0, 1, 0, intuition.CUSTOMSCREEN, ; pens, modes, type + 0, 0, 0, 0] ; font, title, gadgets, bitmap ns.Width = w as word ns.Height = h as word - ns.Depth = ndepth as word - ns.BlockPen = 1 + ns.Depth = ndepth ns.ViewModes = viewmodes - ns.Type = intuition.CUSTOMSCREEN if not untitled ns.DefaultTitle = title ; explicit Topaz 8 screen font (ROM-resident, no diskfont needed) @@ -72,25 +73,27 @@ amiga_gfx { ; null and "" both count as titleless. word bar = 0 if not untitled { - bar = screen.WBorTop as word - if h as word - bar <= 0 { + bar = screen.WBorTop + if h - bar <= 0 { void intuition.CloseScreen(screen) screen = 0 return false } } - const long idcmp = intuition.IDCMP_MOUSEBUTTONS | intuition.IDCMP_RAWKEY | intuition.IDCMP_VANILLAKEY - const long flags = intuition.WFLG_BACKDROP | intuition.WFLG_BORDERLESS | intuition.WFLG_ACTIVATE | intuition.WFLG_RMBTRAP | intuition.WFLG_NOCAREREFRESH - ^^intuition.NewWindow nw = [0, 0, 0, 0, 0 as ubyte, 0 as ubyte, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] + ^^intuition.NewWindow nw = [0, 0, 0, 0, ; edges, size + 0, 1, ; pens + ; RAWKEY only, never add VANILLAKEY here: with both flags set Intuition + ; diverts mappable keys to the vanilla channel and starves the raw one, + ; so cursor keys (and on Kickstart 1.3, all keys) stop arriving as rawkey. + ; Map raw codes yourself instead; ESC is $45, cursor keys are $4c-$4f. + intuition.IDCMP_MOUSEBUTTONS | intuition.IDCMP_RAWKEY, + intuition.WFLG_BACKDROP | intuition.WFLG_BORDERLESS | intuition.WFLG_ACTIVATE | intuition.WFLG_RMBTRAP | intuition.WFLG_NOCAREREFRESH, + 0, 0, 0, 0, ; no gadgets, default checkmark, title, screen + 0, 0, 0, 0, 0, intuition.CUSTOMSCREEN] ; bitmap, limits, type nw.TopEdge = bar nw.Width = w as word - nw.Height = h as word - bar - nw.BlockPen = 1 - nw.IDCMPFlags = idcmp - nw.Flags = flags - nw.Title = 0 ; backdrop window itself stays titleless and undecorated + nw.Height = h - bar nw.Screen = screen - nw.Type = intuition.CUSTOMSCREEN window = intuition.OpenWindow(nw) if window == 0 { void intuition.CloseScreen(screen) @@ -98,9 +101,10 @@ amiga_gfx { return false } depth = ndepth + intuition.ActivateWindow(window) finish_open(true, bar) width = w - height = (h as word - bar) as uword + height = (h - bar) as uword graphics.SetDrMd(rastport, graphics.RP_JAM1) if untitled { ; ShowTitle(FALSE) renders the bar behind all windows; do it with @@ -111,31 +115,31 @@ amiga_gfx { return true } - sub windowed(uword w, uword h, str title) -> bool { - ; fixed-size window on the Workbench screen. Works everywhere, + sub windowed(uword x, uword y, uword w, uword h, str title) -> bool { + ; fixed-size window on the Workbench screen at position (x,y). Works everywhere, ; but depth/palette are the Workbench's: use palette() sparingly. if w == 0 or h == 0 return false if rastport != 0 close() screen = 0 - const long idcmp = intuition.IDCMP_CLOSEWINDOW | intuition.IDCMP_REFRESHWINDOW | intuition.IDCMP_VANILLAKEY | intuition.IDCMP_MOUSEBUTTONS - const long flags = intuition.WFLG_CLOSEGADGET | intuition.WFLG_DRAGBAR | intuition.WFLG_DEPTHGADGET | intuition.WFLG_ACTIVATE - ^^intuition.NewWindow nw = [0, 0, 0, 0, 0 as ubyte, 0 as ubyte, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] - nw.LeftEdge = 10 - nw.TopEdge = 20 + ^^intuition.NewWindow nw = [0, 0, 0, 0, ; edges, size + 255, 255, ; pens + ; RAWKEY only, see fullscreen() for why. + intuition.IDCMP_CLOSEWINDOW | intuition.IDCMP_REFRESHWINDOW | intuition.IDCMP_MOUSEBUTTONS | intuition.IDCMP_RAWKEY, + intuition.WFLG_CLOSEGADGET | intuition.WFLG_DRAGBAR | intuition.WFLG_DEPTHGADGET | intuition.WFLG_ACTIVATE, + 0, 0, 0, 0, ; no gadgets, default checkmark, title, screen + 0, 0, 0, 0, 0, intuition.WBENCHSCREEN] ; bitmap, limits, type + nw.LeftEdge = x as word + nw.TopEdge = y as word nw.Width = w as word nw.Height = h as word - nw.DetailPen = 255 - nw.BlockPen = 255 - nw.IDCMPFlags = idcmp - nw.Flags = flags nw.Title = title - nw.Type = intuition.WBENCHSCREEN window = intuition.OpenWindow(nw) if window == 0 return false depth = 0 + intuition.ActivateWindow(window) finish_open(false, 0) graphics.SetDrMd(rastport, graphics.RP_JAM1) return true @@ -168,7 +172,7 @@ amiga_gfx { if width == 0 or height == 0 return graphics.SetAPen(rastport, pen) - graphics.RectFill(rastport, origin_x, origin_y, (origin_x + width) as word - 1, (origin_y + height) as word - 1) + graphics.RectFill(rastport, origin_x, origin_y, (origin_x + width) - 1, (origin_y + height) - 1) } sub plot(word x, word y, ubyte pen) { @@ -231,7 +235,7 @@ amiga_gfx { if radius < 0 return graphics.SetAPen(rastport, pen) - void do_area_ellipse(cx, cy, aspect_rx(radius), radius) + do_area_ellipse(cx, cy, aspect_rx(radius), radius) } sub filled_oval(word cx, word cy, word rx, word ry, ubyte pen) { @@ -239,7 +243,7 @@ amiga_gfx { if rx <= 0 or ry <= 0 return graphics.SetAPen(rastport, pen) - void do_area_ellipse(cx, cy, aspect_rx(rx), ry) + do_area_ellipse(cx, cy, aspect_rx(rx), ry) } sub text(word x, word y, str s, ubyte pen) { @@ -265,16 +269,15 @@ amiga_gfx { ; tweak a single colormap pen (4-bit nibbles) pointer vp = viewport() if vp != 0 - graphics.SetRGB4(vp, index as word, red, green, blue) + graphics.SetRGB4(vp, index, red, green, blue) } - sub scroll(word dx, word dy) { - ; shift the client area; the exposed strip is cleared - word xa = origin_x - word ya = origin_y - word x2 = (origin_x + width) as word - 1 - word y2 = (origin_y + height) as word - 1 - graphics.ScrollRaster(rastport, dx, dy, xa, ya, x2, y2) + sub scrollrect(word dx, word dy, word x, word y, word w, word h) { + ; scroll a client-relative rectangle + ; the rectangle should lie inside the client area. + if w <= 0 or h <= 0 + return + graphics.ScrollRaster(rastport, dx, dy, origin_x+x, origin_y+y, origin_x+x+w-1, origin_y+y+h-1) } inline sub readpixel(word x, word y) -> long { @@ -294,15 +297,28 @@ amiga_gfx { return msg.MouseX - origin_x, msg.MouseY - origin_y } + sub imsg_wait() -> ^^intuition.IntuiMessage { + ; block until the next IDCMP message arrives on our window + void exec.WaitPort(window.UserPort) + return exec.GetMsg(window.UserPort) as ^^intuition.IntuiMessage + } + + sub imsg_reply(^^intuition.IntuiMessage msg) -> ^^intuition.IntuiMessage { + ; reply the message and fetch the next one (0 when drained) + exec.ReplyMsg(msg as ^^exec.Message) + return exec.GetMsg(window.UserPort) as ^^intuition.IntuiMessage + } + + sub fill(word x, word y, ubyte pen) { ; bucket fill: recolor the connected region holding (x,y) - void do_flood(x, y, pen, 1) + do_flood(x, y, pen, 1) } sub fillto(word x, word y, ubyte pen, ubyte outline) { ; boundary fill: fill until the outline-colored border - void graphics.SetOutlinePen(rastport, outline as long) - void do_flood(x, y, pen, 0) + void graphics.SetOutlinePen(rastport, outline) + do_flood(x, y, pen, 0) } private sub aspect_rx(word rx) -> word { @@ -324,40 +340,37 @@ amiga_gfx { return intuition.GetScreenViewPort(s) } - private sub do_flood(word x, word y, ubyte pen, long mode) -> bool { + private sub do_flood(word x, word y, ubyte pen, long mode) { if rastport == 0 or tmpras_buf == 0 - return false + return graphics.SetAPen(rastport, pen) ^^graphics.TmpRas saved = rastport.TmpRas ^^graphics.TmpRas tp = &tmpras_store[0] void graphics.InitTmpRas(tp, tmpras_buf, tmpras_size) rastport.TmpRas = tp - bool ok = graphics.Flood(rastport, mode, origin_x+x, origin_y+y) + void graphics.Flood(rastport, mode, origin_x+x, origin_y+y) rastport.TmpRas = saved - return ok } private sub finish_open(bool custom, word bar) { rastport = window.RPort - origin_x = window.BorderLeft as word - origin_y = window.BorderTop as word - word right = window.BorderRight as word - word bottom = window.BorderBottom as word - width = (window.Width - origin_x - right) as uword - height = (window.Height - origin_y - bottom) as uword + origin_x = window.BorderLeft + origin_y = window.BorderTop + width = (window.Width - origin_x - window.BorderRight) as uword + height = (window.Height - origin_y - window.BorderBottom) as uword ; pixel shape from the screen's ViewPort.Modes (big-endian word at offset 32) aspect = 1 pointer vp = viewport() if vp != 0 { - uword modes = (@(vp+32) as uword) << 8 | (@(vp+33) as uword) - if (modes & (graphics.SUPERHIRES as uword)) != 0 { + uword modes = (@(vp+32) as uword) << 8 | @(vp+33) + if (modes & graphics.SUPERHIRES) != 0 { aspect = 4 - if (modes & (graphics.INTERLACE as uword)) != 0 + if (modes & graphics.INTERLACE) != 0 aspect = 2 } else { - if (modes & graphics.V_HIRES) != 0 and (modes & (graphics.INTERLACE as uword)) == 0 + if (modes & graphics.V_HIRES) != 0 and (modes & graphics.INTERLACE) == 0 aspect = 2 - if (modes & (graphics.INTERLACE as uword)) != 0 and (modes & graphics.V_HIRES) == 0 + if (modes & graphics.INTERLACE) != 0 and (modes & graphics.V_HIRES) == 0 aspect = 0 } } @@ -370,14 +383,14 @@ amiga_gfx { ; one-plane Flood scratch sized to the rastport's bitmap ; (Flood requires the TmpRas to cover the whole bitmap, not just the client area) if rastport.BitMap != 0 { - tmpras_size = rastport.BitMap.BytesPerRow as long * rastport.BitMap.Rows as long + tmpras_size = rastport.BitMap.BytesPerRow * rastport.BitMap.Rows tmpras_buf = exec.AllocMem(tmpras_size, exec.MEMF_CHIP) } } - private sub do_area_ellipse(word cx, word cy, word rx, word ry) -> bool { + private sub do_area_ellipse(word cx, word cy, word rx, word ry) { if rastport == 0 or tmpras_buf == 0 - return false + return ^^graphics.TmpRas saved_tr = rastport.TmpRas ^^graphics.AreaInfo saved_ai = rastport.AreaInfo ^^graphics.TmpRas tp = &tmpras_store[0] @@ -386,11 +399,9 @@ amiga_gfx { rastport.TmpRas = tp graphics.InitArea(ai, area_vectors, 4) rastport.AreaInfo = ai - long err = graphics.AreaEllipse(rastport, origin_x+cx, origin_y+cy, rx, ry) - if err == 0 - err = graphics.AreaEnd(rastport) + if graphics.AreaEllipse(rastport, origin_x+cx, origin_y+cy, rx, ry) == 0 + void graphics.AreaEnd(rastport) rastport.TmpRas = saved_tr rastport.AreaInfo = saved_ai - return err == 0 } } diff --git a/compiler/res/prog8lib/shared_amiga_graphics.p8 b/compiler/res/prog8lib/shared_amiga_graphics.p8 index 15507a8a9..7d73957ec 100644 --- a/compiler/res/prog8lib/shared_amiga_graphics.p8 +++ b/compiler/res/prog8lib/shared_amiga_graphics.p8 @@ -16,6 +16,7 @@ graphics { const uword LORES = 0 const ubyte LACE = $0004 const ubyte SUPERHIRES = $20 + extsub @bank 3 -30 = BltBitMap(^^BitMap srcBitMap @A0, word xSrc @D0, word ySrc @D1, ^^BitMap destBitMap @A1, word xDest @D2, word yDest @D3, word xSize @D4, word ySize @D5, ubyte minterm @D6, ubyte mask @D7, pointer tempA @A2) -> long @D0 extsub @bank 3 -36 = BltTemplate(long source @A0, word xSrc @D0, word srcMod @D1, ^^RastPort destRP @A1, word xDest @D2, word yDest @D3, word xSize @D4, word ySize @D5) extsub @bank 3 -42 = ClearEOL(^^RastPort rp @A1) diff --git a/examples/amiga/gfxdemo.p8 b/examples/amiga/gfxdemo.p8 index 5c26e65d5..6fab95962 100644 --- a/examples/amiga/gfxdemo.p8 +++ b/examples/amiga/gfxdemo.p8 @@ -5,7 +5,6 @@ %import amiga_gfx %import graphics %import intuition -%import exec main { uword[8] palette = [$0112, $0f00, $00f0, $000f, $0ff0, $0f0f, $00ff, $0fff] @@ -31,24 +30,23 @@ main { amiga_gfx.oval(240, 200, 50, 20, 6) amiga_gfx.filled_oval(40, 60, 30, 12, 5) amiga_gfx.plot(160, 128, 7) - amiga_gfx.text(160, 30, "amiga_gfx demo", 7) - word tw = amiga_gfx.textwidth("click to draw stuff, ESC=quit") - amiga_gfx.text(160 - tw/2, 60, "click to draw stuff, ESC=quit", 7) + amiga_gfx.text(160, 30, "amiga_gfx demo", 5) + word tw = amiga_gfx.textwidth("click to draw, arrows scroll") + amiga_gfx.text(160 - tw/2, 60, "click to draw, arrows scroll", 6) + tw = amiga_gfx.textwidth("ESC=quit") + amiga_gfx.text(160 - tw/2, 70, "ESC=quit", 7) amiga_gfx.setcolor(7, $f, $f, $f) ; direct library escape hatch via the exposed RastPort: ^^graphics.RastPort rp = amiga_gfx.rastport graphics.SetAPen(rp, 3) graphics.Move(rp, 10, 240) graphics.Draw(rp, 310, 240) - - ; TODO make amiga_gfx.scroll() work } sub interact() { bool done = false while not done { - void exec.WaitPort(amiga_gfx.window.UserPort) - ^^intuition.IntuiMessage msg = exec.GetMsg(amiga_gfx.window.UserPort) as ^^intuition.IntuiMessage + ^^intuition.IntuiMessage msg = amiga_gfx.imsg_wait() while msg != 0 { when msg.Class { intuition.IDCMP_CLOSEWINDOW -> done = true @@ -59,10 +57,32 @@ main { if msg.Code == intuition.MENUDOWN amiga_gfx.fillrect(mx-10, my-6, 20, 12, 2) } - intuition.IDCMP_VANILLAKEY -> if msg.Code == 27 { done = true } ; ESC pressed? + intuition.IDCMP_RAWKEY -> { + ; keyboard arrives as rawkey only: cursor keys scroll (key repeat + ; scrolls smoothly), ESC quits. Key-up events (bit 7) are ignored. + ; raw keycodes are positional (same on all national keymaps): + ; $00 ` $01-$0A 1..0 $0B - $0C = $0D \ $0F numpad-0 + ; $10-$19 Q..P $1A [ $1B ] $1D-$1F numpad-1..3 + ; $20-$28 A..L $29 ; $2A ' $2D-$2F numpad-4..6 + ; $31-$37 Z..M $38 , $39 . $3A / $3C numpad-. $3D-$3F numpad-7..9 + ; $40 space $41 backspace $42 tab $43 numpad-enter $44 return + ; $45 ESC $46 del $4A numpad-- $4C-$4F cursor up/down/right/left + ; $50-$59 F1-F10 $5A-$5E numpad ( ) / * + $5F help + ; $60/$61 shift l/r $62 caps $63 ctrl $64/$65 alt l/r $66/$67 amiga l/r + ; source: https://wiki.amigaos.net/wiki/Keymap_Library + if (msg.Code & $80) == 0 { + when (msg.Code & $7f) { + ; note: ScrollRaster moves bits towards (0,0), so the signs are flipped + $45 -> done = true ; ESC + $4c -> amiga_gfx.scrollrect(0, 8, 80, 88, 160, 120) ; up + $4d -> amiga_gfx.scrollrect(0, -8, 80, 88, 160, 120) ; down + $4e -> amiga_gfx.scrollrect(-8, 0, 80, 88, 160, 120) ; right + $4f -> amiga_gfx.scrollrect(8, 0, 80, 88, 160, 120) ; left + } + } + } } - exec.ReplyMsg(msg as ^^exec.Message) - msg = exec.GetMsg(amiga_gfx.window.UserPort) as ^^intuition.IntuiMessage + msg = amiga_gfx.imsg_reply(msg) } } } diff --git a/scripts/amigalibs2prog8.py b/scripts/amigalibs2prog8.py index bb2bc2043..3ebd7889e 100755 --- a/scripts/amigalibs2prog8.py +++ b/scripts/amigalibs2prog8.py @@ -1049,6 +1049,9 @@ LIBRARY_BANK_MAP = { # 0. CONFIG - Amiga libraries to generate # --------------------------------------------------------------------------- +# NOTE: diskfont is intentionally NOT auto-generated: shared_amiga_diskfont.p8 is +# hand-maintained, trimmed to pre-2.0 content (the SFD contains post-2.0 +# outline-engine, diskfont-control and tagged-font APIs). AMIGA_LIBS = ["exec", "dos", "graphics", "intuition", "utility", "iffparse", "arexx", "icon", "lowlevel"] # ---------------------------------------------------------------------------