207 lines
5.4 KiB
Nim
207 lines
5.4 KiB
Nim
# SPDX-FileCopyrightText: ☭ Emery Hemingway
|
|
# SPDX-License-Identifier: Unlicense
|
|
|
|
import std/[asyncdispatch, options, os, streams, 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(
|
|
well: newWell(length, length),
|
|
zoomFactor: 1.0,
|
|
)
|
|
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 = app.well.rect
|
|
if viewPort.overlaps wellRect:
|
|
for (index, rect) in app.well.intersectingPanes(viewPort):
|
|
let texture = app.well.texture(index, app.renderer, rect.wh * app.zoomFactor)
|
|
if texture.isNil: break
|
|
let
|
|
overlap = viewPort and rect
|
|
src = rect(overlap.xy - 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)
|
|
else:
|
|
echo "no overlap of ", viewPort, " and ", wellRect
|
|
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 main() =
|
|
|
|
discard sdl2.init(INIT_TIMER or INIT_VIDEO or INIT_EVENTS)
|
|
let app = newApp(512)
|
|
|
|
let
|
|
typeface = readTypeface(typefacePath)
|
|
font = newFont(typeface)
|
|
# TODO
|
|
|
|
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 stream = newFileStream(stdin)
|
|
# TODO
|
|
|
|
var
|
|
evt = sdl2.defaultEvent
|
|
mousePanning: bool
|
|
line: string
|
|
while true:
|
|
if readLine(stream, line):
|
|
# TODO
|
|
app.well.append(line, font)
|
|
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()
|