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