well_of_text/src/well_of_text.nim

214 lines
5.7 KiB
Nim
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# SPDX-FileCopyrightText: ☭ Emery Hemingway
# SPDX-License-Identifier: Unlicense
import std/[asyncdispatch, options, os, tables]
import preserves, syndicate, syndicate/[capabilities]
import bumpy, pixie
import sdl2
import ./wells
const typefacePath =
"/nix/store/ay5vhxszmibk0nrhx1vid4nhgvgdniq8-corefonts-1/share/fonts/truetype/Trebuchet_MS.ttf"
proc unixSocketPath: Unix =
result.path = getEnv("SYNDICATE_SOCK")
if result.path == "":
result.path = getEnv("XDG_RUNTIME_DIR", "/run/user/1000") / "dataspace"
proc envStep: Assertion =
var s = getEnv("SYNDICATE_STEP")
if s != "": parsePreserves(s, Cap)
else: capabilities.mint().toPreserve(Cap)
type
SdlError = object of CatchableError
template check(res: cint) =
if res != 0:
let msg = $sdl2.getError()
raise newException(SdlError, msg)
template check(res: SDL_Return) =
if res == SdlError:
let msg = $sdl2.getError()
raise newException(SdlError, msg)
func toVec2(p: Point): Vec2 = vec2(float p.x, float p.y)
const
amask = uint32 0xff000000
rmask = uint32 0x000000ff
gmask = uint32 0x0000ff00
bmask = uint32 0x00ff0000
type
App = ref object
screen: Image
window: WindowPtr
renderer: RendererPtr
texture: TexturePtr
rect: bumpy.Rect
viewPoint: Vec2
well: Well
zoomFactor: float
func rect(img: Image): bumpy.Rect =
result.w = float img.width
result.h = float img.height
proc newApp(length: cint): App =
## Create a new square plane of `length` pixels.
result = App(
topFrame: newFrame(length shl 1, length shl 1),
zoomFactor: 1.0,
)
app.well = newWell(length, length)
discard createWindowAndRenderer(
length, length,
SDL_WINDOW_RESIZABLE,
result.window, result.renderer)
var info: RendererInfo
check getRendererInfo(result.renderer, addr info)
echo "SDL Renderer: ", info.name
func toSdl(rect: bumpy.Rect): sdl2.Rect =
(result.x, result.y, result.w, result.h) =
(cint rect.x, cint rect.y, cint rect.w, cint rect.h)
proc viewPort(app: App; wh: Vec2): bumpy.Rect =
result.wh = wh / app.zoomFactor
result.xy = app.viewPoint - (result.wh * 0.5)
proc redraw(app: App) =
assert app.zoomFactor != 0.0
var
(w, h) = app.window.getSize
sdlViewPort = rect(-float(w shr 1), -float(h shr 1), float w, float h)
viewPort = app.viewPort(sdlViewPort.wh)
app.renderer.setDrawColor(0x80, 0x80, 0x80)
app.renderer.clear()
let wellRect = well.Rect
if viewPort.overlaps wellRect:
for (pane, rect) in well.intersectingPanes(viewPort)
let
texture = pane.texture(rect.wh *­app.zoomFactor)
overlap = viewPort and rect
src = rect(overlap.xy - app.rect.xy, overlap.wh)
var dst: bumpy.Rect
dst.x = (overlap.x - viewPort.x) * (sdlViewPort.w / viewPort.w)
dst.y = (overlap.y - viewPort.y) * (sdlViewPort.h / viewPort.h)
dst.wh =
if app.zoomFactor == 1.0:
overlap.wh # correct
elif app.zoomFactor > 1.0:
sdlViewPort.wh - dst.xy # correct?
else:
overlap.wh * app.zoomFactor
var (sdlSrc, sdlDst) = (src.toSdl, dst.toSdl)
app.renderer.copy(texture, addr sdlSrc, addr sdlDst)
app.renderer.present()
proc resize(app: App) =
## Resize to new dimensions of the SDL window.
redraw(app)
proc zoom(app: App; change: float) =
app.zoomFactor = app.zoomFactor * (1.0 + ((1 / 8) * change))
app.redraw()
proc pan(app: App; xy: Vec2) =
app.viewPoint.xy = app.viewPoint.xy + (xy / app.zoomFactor)
app.redraw()
proc recenter(app: App) =
reset app.viewPoint
app.zoomFactor = 1.0
app.redraw()
proc setImage(app: App; image: Image) =
app.rect = image.rect
echo "create surface of ", image.width, "x", cint image.height, " pixels"
var
dataPtr = image.data[0].addr
surface = createRGBSurfaceFrom(
dataPtr,
cint image.width, cint image.height,
cint 32, cint 4*image.width,
rmask, gmask, bmask, amask)
app.texture = createTextureFromSurface(app.renderer, surface)
proc main() =
discard sdl2.init(INIT_TIMER or INIT_VIDEO or INIT_EVENTS)
let app = newApp(512)
let
typeface = readTypeface(typefacePath)
fontTODO = newFont(typeface, 48)
app.redraw()
#[
let
unix = unixSocketPath()
step = envStep()
let actor = bootDataspace("chat") do (root: Cap; turn: var Turn):
connect(turn, unix, step) do (turn: var Turn; ds: Cap):
echo "connected to syndicate over UNIX-socket"
asyncCheck actor.future
]#
const
sdlTimeout = 500
asyncPollTimeout = 500
let streamTODO = newFileStream(stdin)
var
evt = sdl2.defaultEvent
mousePanning: bool
lineTODO: string
while true:
if readLine(streamTODO, lineTODO):
app.well.append(line, fontTODO)
app.redraw()
# asyncdispatch.poll(0)
if waitEventTimeout(evt, sdlTimeout):
case evt.kind
of MouseWheel:
app.zoom(evt.wheel.y.float)
of WindowEvent:
if evt.window.event == WindowEvent_Resized:
app.resize()
of MouseMotion:
if mousePanning:
var xy = vec2(evt.motion.xrel.float, evt.motion.yrel.float)
app.pan(xy * 2.0)
of MouseButtonDown:
case evt.button.button
of BUTTON_MIDDLE:
mousePanning = true
else: discard
of MouseButtonUp:
case evt.button.button
of BUTTON_MIDDLE:
mousePanning = false
else: discard
of KeyUp:
try:
let code = evt.key.keysym.scancode.Scancode
echo "code: ", code
if code in {SDL_SCANCODE_SPACE, SDL_SCANCODE_ESCAPE}:
app.recenter()
except:
# invalid event.key.keysym.sym sometimes arrive
discard
of KeyDown: discard
of QuitEvent: quit(0)
else:
echo evt.kind
main()