214 lines
5.7 KiB
Nim
214 lines
5.7 KiB
Nim
# 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()
|