## This example show how to have real time pixie using the X11 API.

import math, pixie, std/os
import
  x11/xlib,
  x11/xutil,
  x11/x

let
  w: int32 = 256
  h: int32 = 256

var
  screen = newImage(w, h)
  ctx = newContext(screen)

var
  display: PDisplay
  window: Window
  deleteMessage: Atom
  graphicsContext: GC
  frameCount = 0

proc render() =
  ## Called every frame by main while loop
  
  # draw shiny sphere on gradient background
  let linerGradient = newPaint(pkGradientLinear)
  linerGradient.gradientHandlePositions.add(vec2(0, 0))
  linerGradient.gradientHandlePositions.add(vec2(0, 256))
  linerGradient.gradientStops.add(
    ColorStop(color: pixie.color(0, 0, 0, 1), position: 0))
  linerGradient.gradientStops.add(
    ColorStop(color: pixie.color(1, 1, 1, 1), position: 1))
  ctx.fillStyle = linerGradient
  ctx.fillRect(0, 0, 256, 256)
  let radialGradient = newPaint(pkGradientRadial)
  radialGradient.gradientHandlePositions.add(vec2(128, 128))
  radialGradient.gradientHandlePositions.add(vec2(256, 128))
  radialGradient.gradientHandlePositions.add(vec2(128, 256))
  radialGradient.gradientStops.add(
    ColorStop(color: pixie.color(1, 1, 1, 1), position: 0))
  radialGradient.gradientStops.add(
    ColorStop(color: pixie.color(0, 0, 0, 1), position: 1))
  ctx.fillStyle = radialGradient
  ctx.fillCircle(circle(
    vec2(128.0, 128.0 + sin(float32(frameCount)/10.0) * 20),
    76.8
  ))
  inc frameCount
  var frameBuffer = addr ctx.image.data[0]
  let image = XCreateImage(display, DefaultVisualOfScreen(
      DefaultScreenOfDisplay(display)), 24, ZPixmap, 0, cast[cstring](
      frameBuffer), w.cuint, h.cuint, 8, w*4)
  discard XPutImage(display, window, graphicsContext, image, 0, 0, 0, 0, w.cuint, h.cuint)

const
  borderWidth = 0
  eventMask = ButtonPressMask or KeyPressMask or ExposureMask

proc init() =
  display = XOpenDisplay(nil)
  if display == nil:
    quit "Failed to open display"

  let
    screen = XDefaultScreen(display)
    rootWindow = XRootWindow(display, screen)
    foregroundColor = XBlackPixel(display, screen)
    backgroundColor = XWhitePixel(display, screen)

  window = XCreateSimpleWindow(display, rootWindow, -1, -1, w.cuint, h.cuint,
      borderWidth, foregroundColor, backgroundColor)


  discard XSetStandardProperties(display, window, "X11 Example", "window", 0,
      nil, 0, nil)

  discard XSelectInput(display, window, eventMask)
  discard XMapWindow(display, window)

  deleteMessage = XInternAtom(display, "WM_DELETE_WINDOW", false.XBool)
  discard XSetWMProtocols(display, window, deleteMessage.addr, 1)

  graphicsContext = XDefaultGC(display, screen)


proc mainLoop() =
  ## Process events until the quit event is received
  var event: XEvent
  var exposed: bool = false
  while true:
    if exposed: render()
    if XPending(display) > 0:
      discard XNextEvent(display, event.addr)
      case event.theType
      of Expose:
        render()
        exposed = true
      of ClientMessage:
        if cast[Atom](event.xclient.data.l[0]) == deleteMessage:
          break
      of KeyPress:
        let key = XLookupKeysym(cast[PXKeyEvent](event.addr), 0)
        if key != 0:
          echo "Key ", key, " pressed"
      of ButtonPressMask:
        echo "Mouse button ", event.xbutton.button, " pressed at ",
            event.xbutton.x, ",", event.xbutton.y
      else:
        discard
    sleep 14

proc main() =
  init()
  mainLoop()
  discard XDestroyWindow(display, window)
  discard XCloseDisplay(display)

main()
