# 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()