<commit_before>package pixelgl

import (
	"fmt"
	"image/color"

	"github.com/faiface/glhf"
	"github.com/faiface/mainthread"
	"github.com/faiface/pixel"
	"github.com/go-gl/mathgl/mgl32"
	"github.com/pkg/errors"
)

// Canvas is an off-screen rectangular BasicTarget and Picture at the same time, that you can draw
// onto.
//
// It supports TrianglesPosition, TrianglesColor, TrianglesPicture and PictureColor.
type Canvas struct {
	gf     *GLFrame
	shader *glShader

	cmp    pixel.ComposeMethod
	mat    mgl32.Mat3
	col    mgl32.Vec4
	smooth bool

	sprite *pixel.Sprite
}

var _ pixel.ComposeTarget = (*Canvas)(nil)

// NewCanvas creates a new empty, fully transparent Canvas with given bounds.
func NewCanvas(bounds pixel.Rect) *Canvas {
	c := &Canvas{
		gf:  NewGLFrame(bounds),
		mat: mgl32.Ident3(),
		col: mgl32.Vec4{1, 1, 1, 1},
	}

	baseShader(c)
	c.SetBounds(bounds)
	c.shader.update()
	return c
}

// SetUniform will update the named uniform with the value of any supported underlying
// attribute variable. If the uniform already exists, including defaults, they will be reassigned
// to the new value. The value can be a pointer.
func (c *Canvas) SetUniform(name string, value interface{}) {
	c.shader.setUniform(name, value)
}

// SetFragmentShader allows you to set a new fragment shader on the underlying
// framebuffer. Argument "src" is the GLSL source, not a filename.
func (c *Canvas) SetFragmentShader(src string) {
	c.shader.fs = src
	c.shader.update()
}

// MakeTriangles creates a specialized copy of the supplied Triangles that draws onto this Canvas.
//
// TrianglesPosition, TrianglesColor and TrianglesPicture are supported.
func (c *Canvas) MakeTriangles(t pixel.Triangles) pixel.TargetTriangles {
	return &canvasTriangles{
		GLTriangles: NewGLTriangles(c.shader.s, t),
		dst:         c,
	}
}

// MakePicture create a specialized copy of the supplied Picture that draws onto this Canvas.
//
// PictureColor is supported.
func (c *Canvas) MakePicture(p pixel.Picture) pixel.TargetPicture {
	if cp, ok := p.(*canvasPicture); ok {
		return &canvasPicture{
			GLPicture: cp.GLPicture,
			dst:       c,
		}
	}
	if gp, ok := p.(GLPicture); ok {
		return &canvasPicture{
			GLPicture: gp,
			dst:       c,
		}
	}
	return &canvasPicture{
		GLPicture: NewGLPicture(p),
		dst:       c,
	}
}

// SetMatrix sets a Matrix that every point will be projected by.
func (c *Canvas) SetMatrix(m pixel.Matrix) {
	// pixel.Matrix is 3x2 with an implicit 0, 0, 1 row after it. So
	// [0] [2] [4]    [0] [3] [6]
	// [1] [3] [5] => [1] [4] [7]
	//  0   0   1      0   0   1
	// since all matrix ops are affine, the last row never changes, and we don't need to copy it
	for i, j := range [...]int{0, 1, 3, 4, 6, 7} {
		c.mat[j] = float32(m[i])
	}
}

// SetColorMask sets a color that every color in triangles or a picture will be multiplied by.
func (c *Canvas) SetColorMask(col color.Color) {
	rgba := pixel.Alpha(1)
	if col != nil {
		rgba = pixel.ToRGBA(col)
	}
	c.col = mgl32.Vec4{
		float32(rgba.R),
		float32(rgba.G),
		float32(rgba.B),
		float32(rgba.A),
	}
}

// SetComposeMethod sets a Porter-Duff composition method to be used in the following draws onto
// this Canvas.
func (c *Canvas) SetComposeMethod(cmp pixel.ComposeMethod) {
	c.cmp = cmp
}

// SetBounds resizes the Canvas to the new bounds. Old content will be preserved.
func (c *Canvas) SetBounds(bounds pixel.Rect) {
	c.gf.SetBounds(bounds)
	if c.sprite == nil {
		c.sprite = pixel.NewSprite(nil, pixel.Rect{})
	}
	c.sprite.Set(c, c.Bounds())
	//c.sprite.SetMatrix(pixel.IM.Moved(c.Bounds().Center()))
}

// Bounds returns the rectangular bounds of the Canvas.
func (c *Canvas) Bounds() pixel.Rect {
	return c.gf.Bounds()
}

// SetSmooth sets whether stretched Pictures drawn onto this Canvas should be drawn smooth or
// pixely.
func (c *Canvas) SetSmooth(smooth bool) {
	c.smooth = smooth
}

// Smooth returns whether stretched Pictures drawn onto this Canvas are set to be drawn smooth or
// pixely.
func (c *Canvas) Smooth() bool {
	return c.smooth
}

// must be manually called inside mainthread
func (c *Canvas) setGlhfBounds() {
	_, _, bw, bh := intBounds(c.gf.Bounds())
	glhf.Bounds(0, 0, bw, bh)
}

// must be manually called inside mainthread
func setBlendFunc(cmp pixel.ComposeMethod) {
	switch cmp {
	case pixel.ComposeOver:
		glhf.BlendFunc(glhf.One, glhf.OneMinusSrcAlpha)
	case pixel.ComposeIn:
		glhf.BlendFunc(glhf.DstAlpha, glhf.Zero)
	case pixel.ComposeOut:
		glhf.BlendFunc(glhf.OneMinusDstAlpha, glhf.Zero)
	case pixel.ComposeAtop:
		glhf.BlendFunc(glhf.DstAlpha, glhf.OneMinusSrcAlpha)
	case pixel.ComposeRover:
		glhf.BlendFunc(glhf.OneMinusDstAlpha, glhf.One)
	case pixel.ComposeRin:
		glhf.BlendFunc(glhf.Zero, glhf.SrcAlpha)
	case pixel.ComposeRout:
		glhf.BlendFunc(glhf.Zero, glhf.OneMinusSrcAlpha)
	case pixel.ComposeRatop:
		glhf.BlendFunc(glhf.OneMinusDstAlpha, glhf.SrcAlpha)
	case pixel.ComposeXor:
		glhf.BlendFunc(glhf.OneMinusDstAlpha, glhf.OneMinusSrcAlpha)
	case pixel.ComposePlus:
		glhf.BlendFunc(glhf.One, glhf.One)
	case pixel.ComposeCopy:
		glhf.BlendFunc(glhf.One, glhf.Zero)
	default:
		panic(errors.New("Canvas: invalid compose method"))
	}
}

// Clear fills the whole Canvas with a single color.
func (c *Canvas) Clear(color color.Color) {
	c.gf.Dirty()

	rgba := pixel.ToRGBA(color)

	// color masking
	rgba = rgba.Mul(pixel.RGBA{
		R: float64(c.col[0]),
		G: float64(c.col[1]),
		B: float64(c.col[2]),
		A: float64(c.col[3]),
	})

	mainthread.CallNonBlock(func() {
		c.setGlhfBounds()
		c.gf.Frame().Begin()
		glhf.Clear(
			float32(rgba.R),
			float32(rgba.G),
			float32(rgba.B),
			float32(rgba.A),
		)
		c.gf.Frame().End()
	})
}

// Color returns the color of the pixel over the given position inside the Canvas.
func (c *Canvas) Color(at pixel.Vec) pixel.RGBA {
	return c.gf.Color(at)
}

// Texture returns the underlying OpenGL Texture of this Canvas.
//
// Implements GLPicture interface.
func (c *Canvas) Texture() *glhf.Texture {
	return c.gf.Texture()
}

// Frame returns the underlying OpenGL Frame of this Canvas.
func (c *Canvas) Frame() *glhf.Frame {
	return c.gf.frame
}

// SetPixels replaces the content of the Canvas with the provided pixels. The provided slice must be
// an alpha-premultiplied RGBA sequence of correct length (4 * width * height).
func (c *Canvas) SetPixels(pixels []uint8) {
	c.gf.Dirty()

	mainthread.Call(func() {
		tex := c.Texture()
		tex.Begin()
		tex.SetPixels(0, 0, tex.Width(), tex.Height(), pixels)
		tex.End()
	})
}

// Pixels returns an alpha-premultiplied RGBA sequence of the content of the Canvas.
func (c *Canvas) Pixels() []uint8 {
	var pixels []uint8

	mainthread.Call(func() {
		tex := c.Texture()
		tex.Begin()
		pixels = tex.Pixels(0, 0, tex.Width(), tex.Height())
		tex.End()
	})

	return pixels
}

// Draw draws the content of the Canvas onto another Target, transformed by the given Matrix, just
// like if it was a Sprite containing the whole Canvas.
func (c *Canvas) Draw(t pixel.Target, matrix pixel.Matrix) {
	c.sprite.Draw(t, matrix)
}

// DrawColorMask draws the content of the Canvas onto another Target, transformed by the given
// Matrix and multiplied by the given mask, just like if it was a Sprite containing the whole Canvas.
//
// If the color mask is nil, a fully opaque white mask will be used causing no effect.
func (c *Canvas) DrawColorMask(t pixel.Target, matrix pixel.Matrix, mask color.Color) {
	c.sprite.DrawColorMask(t, matrix, mask)
}

type canvasTriangles struct {
	*GLTriangles
	dst *Canvas
}

func (ct *canvasTriangles) draw(tex *glhf.Texture, bounds pixel.Rect) {
	ct.dst.gf.Dirty()

	// save the current state vars to avoid race condition
	cmp := ct.dst.cmp
	smt := ct.dst.smooth
	mat := ct.dst.mat
	col := ct.dst.col

	mainthread.CallNonBlock(func() {
		ct.dst.setGlhfBounds()
		setBlendFunc(cmp)

		frame := ct.dst.gf.Frame()
		shader := ct.dst.shader.s

		frame.Begin()
		shader.Begin()

		ct.dst.shader.uniformDefaults.transform = mat
		ct.dst.shader.uniformDefaults.colormask = col
		dstBounds := ct.dst.Bounds()
		ct.dst.shader.uniformDefaults.bounds = mgl32.Vec4{
			float32(dstBounds.Min.X),
			float32(dstBounds.Min.Y),
			float32(dstBounds.W()),
			float32(dstBounds.H()),
		}

		bx, by, bw, bh := intBounds(bounds)
		ct.dst.shader.uniformDefaults.texbounds = mgl32.Vec4{
			float32(bx),
			float32(by),
			float32(bw),
			float32(bh),
		}

		for loc, u := range ct.dst.shader.uniforms {
			ct.dst.shader.s.SetUniformAttr(loc, u.Value())
		}

		if tex == nil {
			ct.vs.Begin()
			ct.vs.Draw()
			ct.vs.End()
		} else {
			tex.Begin()

			if tex.Smooth() != smt {
				tex.SetSmooth(smt)
			}

			ct.vs.Begin()
			if clip, has := ct.ClipRect(); has {
				gl.Scissor(int32(clip.Min.X), int32(clip.Min.Y), int32(clip.W()), int32(clip.H()))
			}
			ct.vs.Draw()
			ct.vs.End()

			tex.End()
		}

		shader.End()
		frame.End()
	})
}

func (ct *canvasTriangles) Draw() {
	ct.draw(nil, pixel.Rect{})
}

type canvasPicture struct {
	GLPicture
	dst *Canvas
}

func (cp *canvasPicture) Draw(t pixel.TargetTriangles) {
	ct := t.(*canvasTriangles)
	if cp.dst != ct.dst {
		panic(fmt.Errorf("(%T).Draw: TargetTriangles generated by different Canvas", cp))
	}
	ct.draw(cp.GLPicture.Texture(), cp.GLPicture.Bounds())
}

const (
	canvasPosition int = iota
	canvasColor
	canvasTexCoords
	canvasIntensity
)

var defaultCanvasVertexFormat = glhf.AttrFormat{
	canvasPosition:  {Name: "aPosition", Type: glhf.Vec2},
	canvasColor:     {Name: "aColor", Type: glhf.Vec4},
	canvasTexCoords: {Name: "aTexCoords", Type: glhf.Vec2},
	canvasIntensity: {Name: "aIntensity", Type: glhf.Float},
}
<commit_msg>Moved triangle clipping to the proper location<commit_after>package pixelgl

import (
	"fmt"
	"image/color"

	"github.com/faiface/glhf"
	"github.com/faiface/mainthread"
	"github.com/faiface/pixel"
	"github.com/go-gl/mathgl/mgl32"
	"github.com/pkg/errors"
)

// Canvas is an off-screen rectangular BasicTarget and Picture at the same time, that you can draw
// onto.
//
// It supports TrianglesPosition, TrianglesColor, TrianglesPicture and PictureColor.
type Canvas struct {
	gf     *GLFrame
	shader *glShader

	cmp    pixel.ComposeMethod
	mat    mgl32.Mat3
	col    mgl32.Vec4
	smooth bool

	sprite *pixel.Sprite
}

var _ pixel.ComposeTarget = (*Canvas)(nil)

// NewCanvas creates a new empty, fully transparent Canvas with given bounds.
func NewCanvas(bounds pixel.Rect) *Canvas {
	c := &Canvas{
		gf:  NewGLFrame(bounds),
		mat: mgl32.Ident3(),
		col: mgl32.Vec4{1, 1, 1, 1},
	}

	baseShader(c)
	c.SetBounds(bounds)
	c.shader.update()
	return c
}

// SetUniform will update the named uniform with the value of any supported underlying
// attribute variable. If the uniform already exists, including defaults, they will be reassigned
// to the new value. The value can be a pointer.
func (c *Canvas) SetUniform(name string, value interface{}) {
	c.shader.setUniform(name, value)
}

// SetFragmentShader allows you to set a new fragment shader on the underlying
// framebuffer. Argument "src" is the GLSL source, not a filename.
func (c *Canvas) SetFragmentShader(src string) {
	c.shader.fs = src
	c.shader.update()
}

// MakeTriangles creates a specialized copy of the supplied Triangles that draws onto this Canvas.
//
// TrianglesPosition, TrianglesColor and TrianglesPicture are supported.
func (c *Canvas) MakeTriangles(t pixel.Triangles) pixel.TargetTriangles {
	return &canvasTriangles{
		GLTriangles: NewGLTriangles(c.shader.s, t),
		dst:         c,
	}
}

// MakePicture create a specialized copy of the supplied Picture that draws onto this Canvas.
//
// PictureColor is supported.
func (c *Canvas) MakePicture(p pixel.Picture) pixel.TargetPicture {
	if cp, ok := p.(*canvasPicture); ok {
		return &canvasPicture{
			GLPicture: cp.GLPicture,
			dst:       c,
		}
	}
	if gp, ok := p.(GLPicture); ok {
		return &canvasPicture{
			GLPicture: gp,
			dst:       c,
		}
	}
	return &canvasPicture{
		GLPicture: NewGLPicture(p),
		dst:       c,
	}
}

// SetMatrix sets a Matrix that every point will be projected by.
func (c *Canvas) SetMatrix(m pixel.Matrix) {
	// pixel.Matrix is 3x2 with an implicit 0, 0, 1 row after it. So
	// [0] [2] [4]    [0] [3] [6]
	// [1] [3] [5] => [1] [4] [7]
	//  0   0   1      0   0   1
	// since all matrix ops are affine, the last row never changes, and we don't need to copy it
	for i, j := range [...]int{0, 1, 3, 4, 6, 7} {
		c.mat[j] = float32(m[i])
	}
}

// SetColorMask sets a color that every color in triangles or a picture will be multiplied by.
func (c *Canvas) SetColorMask(col color.Color) {
	rgba := pixel.Alpha(1)
	if col != nil {
		rgba = pixel.ToRGBA(col)
	}
	c.col = mgl32.Vec4{
		float32(rgba.R),
		float32(rgba.G),
		float32(rgba.B),
		float32(rgba.A),
	}
}

// SetComposeMethod sets a Porter-Duff composition method to be used in the following draws onto
// this Canvas.
func (c *Canvas) SetComposeMethod(cmp pixel.ComposeMethod) {
	c.cmp = cmp
}

// SetBounds resizes the Canvas to the new bounds. Old content will be preserved.
func (c *Canvas) SetBounds(bounds pixel.Rect) {
	c.gf.SetBounds(bounds)
	if c.sprite == nil {
		c.sprite = pixel.NewSprite(nil, pixel.Rect{})
	}
	c.sprite.Set(c, c.Bounds())
	//c.sprite.SetMatrix(pixel.IM.Moved(c.Bounds().Center()))
}

// Bounds returns the rectangular bounds of the Canvas.
func (c *Canvas) Bounds() pixel.Rect {
	return c.gf.Bounds()
}

// SetSmooth sets whether stretched Pictures drawn onto this Canvas should be drawn smooth or
// pixely.
func (c *Canvas) SetSmooth(smooth bool) {
	c.smooth = smooth
}

// Smooth returns whether stretched Pictures drawn onto this Canvas are set to be drawn smooth or
// pixely.
func (c *Canvas) Smooth() bool {
	return c.smooth
}

// must be manually called inside mainthread
func (c *Canvas) setGlhfBounds() {
	_, _, bw, bh := intBounds(c.gf.Bounds())
	glhf.Bounds(0, 0, bw, bh)
}

// must be manually called inside mainthread
func setBlendFunc(cmp pixel.ComposeMethod) {
	switch cmp {
	case pixel.ComposeOver:
		glhf.BlendFunc(glhf.One, glhf.OneMinusSrcAlpha)
	case pixel.ComposeIn:
		glhf.BlendFunc(glhf.DstAlpha, glhf.Zero)
	case pixel.ComposeOut:
		glhf.BlendFunc(glhf.OneMinusDstAlpha, glhf.Zero)
	case pixel.ComposeAtop:
		glhf.BlendFunc(glhf.DstAlpha, glhf.OneMinusSrcAlpha)
	case pixel.ComposeRover:
		glhf.BlendFunc(glhf.OneMinusDstAlpha, glhf.One)
	case pixel.ComposeRin:
		glhf.BlendFunc(glhf.Zero, glhf.SrcAlpha)
	case pixel.ComposeRout:
		glhf.BlendFunc(glhf.Zero, glhf.OneMinusSrcAlpha)
	case pixel.ComposeRatop:
		glhf.BlendFunc(glhf.OneMinusDstAlpha, glhf.SrcAlpha)
	case pixel.ComposeXor:
		glhf.BlendFunc(glhf.OneMinusDstAlpha, glhf.OneMinusSrcAlpha)
	case pixel.ComposePlus:
		glhf.BlendFunc(glhf.One, glhf.One)
	case pixel.ComposeCopy:
		glhf.BlendFunc(glhf.One, glhf.Zero)
	default:
		panic(errors.New("Canvas: invalid compose method"))
	}
}

// Clear fills the whole Canvas with a single color.
func (c *Canvas) Clear(color color.Color) {
	c.gf.Dirty()

	rgba := pixel.ToRGBA(color)

	// color masking
	rgba = rgba.Mul(pixel.RGBA{
		R: float64(c.col[0]),
		G: float64(c.col[1]),
		B: float64(c.col[2]),
		A: float64(c.col[3]),
	})

	mainthread.CallNonBlock(func() {
		c.setGlhfBounds()
		c.gf.Frame().Begin()
		glhf.Clear(
			float32(rgba.R),
			float32(rgba.G),
			float32(rgba.B),
			float32(rgba.A),
		)
		c.gf.Frame().End()
	})
}

// Color returns the color of the pixel over the given position inside the Canvas.
func (c *Canvas) Color(at pixel.Vec) pixel.RGBA {
	return c.gf.Color(at)
}

// Texture returns the underlying OpenGL Texture of this Canvas.
//
// Implements GLPicture interface.
func (c *Canvas) Texture() *glhf.Texture {
	return c.gf.Texture()
}

// Frame returns the underlying OpenGL Frame of this Canvas.
func (c *Canvas) Frame() *glhf.Frame {
	return c.gf.frame
}

// SetPixels replaces the content of the Canvas with the provided pixels. The provided slice must be
// an alpha-premultiplied RGBA sequence of correct length (4 * width * height).
func (c *Canvas) SetPixels(pixels []uint8) {
	c.gf.Dirty()

	mainthread.Call(func() {
		tex := c.Texture()
		tex.Begin()
		tex.SetPixels(0, 0, tex.Width(), tex.Height(), pixels)
		tex.End()
	})
}

// Pixels returns an alpha-premultiplied RGBA sequence of the content of the Canvas.
func (c *Canvas) Pixels() []uint8 {
	var pixels []uint8

	mainthread.Call(func() {
		tex := c.Texture()
		tex.Begin()
		pixels = tex.Pixels(0, 0, tex.Width(), tex.Height())
		tex.End()
	})

	return pixels
}

// Draw draws the content of the Canvas onto another Target, transformed by the given Matrix, just
// like if it was a Sprite containing the whole Canvas.
func (c *Canvas) Draw(t pixel.Target, matrix pixel.Matrix) {
	c.sprite.Draw(t, matrix)
}

// DrawColorMask draws the content of the Canvas onto another Target, transformed by the given
// Matrix and multiplied by the given mask, just like if it was a Sprite containing the whole Canvas.
//
// If the color mask is nil, a fully opaque white mask will be used causing no effect.
func (c *Canvas) DrawColorMask(t pixel.Target, matrix pixel.Matrix, mask color.Color) {
	c.sprite.DrawColorMask(t, matrix, mask)
}

type canvasTriangles struct {
	*GLTriangles
	dst *Canvas
}

func (ct *canvasTriangles) draw(tex *glhf.Texture, bounds pixel.Rect) {
	ct.dst.gf.Dirty()

	// save the current state vars to avoid race condition
	cmp := ct.dst.cmp
	smt := ct.dst.smooth
	mat := ct.dst.mat
	col := ct.dst.col

	mainthread.CallNonBlock(func() {
		ct.dst.setGlhfBounds()
		setBlendFunc(cmp)

		frame := ct.dst.gf.Frame()
		shader := ct.dst.shader.s

		frame.Begin()
		shader.Begin()

		ct.dst.shader.uniformDefaults.transform = mat
		ct.dst.shader.uniformDefaults.colormask = col
		dstBounds := ct.dst.Bounds()
		ct.dst.shader.uniformDefaults.bounds = mgl32.Vec4{
			float32(dstBounds.Min.X),
			float32(dstBounds.Min.Y),
			float32(dstBounds.W()),
			float32(dstBounds.H()),
		}

		bx, by, bw, bh := intBounds(bounds)
		ct.dst.shader.uniformDefaults.texbounds = mgl32.Vec4{
			float32(bx),
			float32(by),
			float32(bw),
			float32(bh),
		}

		for loc, u := range ct.dst.shader.uniforms {
			ct.dst.shader.s.SetUniformAttr(loc, u.Value())
		}

		if clip, has := ct.ClipRect(); has {
			gl.Scissor(int32(clip.Min.X), int32(clip.Min.Y), int32(clip.W()), int32(clip.H()))
		}

		if tex == nil {
			ct.vs.Begin()
			ct.vs.Draw()
			ct.vs.End()
		} else {
			tex.Begin()

			if tex.Smooth() != smt {
				tex.SetSmooth(smt)
			}

			ct.vs.Begin()
			ct.vs.Draw()
			ct.vs.End()

			tex.End()
		}

		shader.End()
		frame.End()
	})
}

func (ct *canvasTriangles) Draw() {
	ct.draw(nil, pixel.Rect{})
}

type canvasPicture struct {
	GLPicture
	dst *Canvas
}

func (cp *canvasPicture) Draw(t pixel.TargetTriangles) {
	ct := t.(*canvasTriangles)
	if cp.dst != ct.dst {
		panic(fmt.Errorf("(%T).Draw: TargetTriangles generated by different Canvas", cp))
	}
	ct.draw(cp.GLPicture.Texture(), cp.GLPicture.Bounds())
}

const (
	canvasPosition int = iota
	canvasColor
	canvasTexCoords
	canvasIntensity
)

var defaultCanvasVertexFormat = glhf.AttrFormat{
	canvasPosition:  {Name: "aPosition", Type: glhf.Vec2},
	canvasColor:     {Name: "aColor", Type: glhf.Vec4},
	canvasTexCoords: {Name: "aTexCoords", Type: glhf.Vec2},
	canvasIntensity: {Name: "aIntensity", Type: glhf.Float},
}
<|endoftext|>


<commit_before>package pixelgl

import (
	"image"
	"image/color"
	"runtime"

	"github.com/faiface/glhf"
	"github.com/faiface/mainthread"
	"github.com/faiface/pixel"
	"github.com/go-gl/glfw/v3.2/glfw"
	"github.com/pkg/errors"
)

// WindowConfig is a structure for specifying all possible properties of a Window. Properties are
// chosen in such a way, that you usually only need to set a few of them - defaults (zeros) should
// usually be sensible.
//
// Note that you always need to set the Bounds of a Window.
type WindowConfig struct {
	// Title at the top of the Window.
	Title string

	// Icon specifies the icon images available to be used by the window. This is usually
	// displayed in the top bar of the window or in the task bar of the desktop environment.
	//
	// If passed one image, it will use that image, if passed an array of images those of or
	// closest to the sizes desired by the system are selected. The desired image sizes varies
	// depending on platform and system settings. The selected images will be rescaled as
	// needed. Good sizes include 16x16, 32x32 and 48x48.
	//
	// Note: Setting this value doesn't have an effect on OSX. You'll need to set the icon when
	// bundling your application for release.
	Icon []pixel.Picture

	// Bounds specify the bounds of the Window in pixels.
	Bounds pixel.Rect

	// If set to nil, the Window will be windowed. Otherwise it will be fullscreen on the
	// specified Monitor.
	Monitor *Monitor

	// Whether the Window is resizable.
	Resizable bool

	// Undecorated Window ommits the borders and decorations (close button, etc.).
	Undecorated bool

	// NoIconify specifies whether fullscreen windows should not automatically
	// iconify (and restore the previous video mode) on focus loss.
	NoIconify bool

	// AlwaysOnTop specifies whether the windowed mode window will be floating
	// above other regular windows, also called topmost or always-on-top.
	// This is intended primarily for debugging purposes and cannot be used to
	// implement proper full screen windows.
	AlwaysOnTop bool

	// VSync (vertical synchronization) synchronizes Window's framerate with the framerate of
	// the monitor.
	VSync bool
}

// Window is a window handler. Use this type to manipulate a window (input, drawing, etc.).
type Window struct {
	window *glfw.Window

	bounds             pixel.Rect
	canvas             *Canvas
	vsync              bool
	cursorVisible      bool
	cursorInsideWindow bool

	// need to save these to correctly restore a fullscreen window
	restore struct {
		xpos, ypos, width, height int
	}

	prevInp, currInp, tempInp struct {
		mouse   pixel.Vec
		buttons [KeyLast + 1]bool
		repeat  [KeyLast + 1]bool
		scroll  pixel.Vec
		typed   string
	}

	prevJoy, currJoy, tempJoy joystickState
}

var currWin *Window

// NewWindow creates a new Window with it's properties specified in the provided config.
//
// If Window creation fails, an error is returned (e.g. due to unavailable graphics device).
func NewWindow(cfg WindowConfig) (*Window, error) {
	bool2int := map[bool]int{
		true:  glfw.True,
		false: glfw.False,
	}

	w := &Window{bounds: cfg.Bounds, cursorVisible: true}

	err := mainthread.CallErr(func() error {
		var err error

		glfw.WindowHint(glfw.ContextVersionMajor, 3)
		glfw.WindowHint(glfw.ContextVersionMinor, 3)
		glfw.WindowHint(glfw.OpenGLProfile, glfw.OpenGLCoreProfile)
		glfw.WindowHint(glfw.OpenGLForwardCompatible, glfw.True)

		glfw.WindowHint(glfw.Resizable, bool2int[cfg.Resizable])
		glfw.WindowHint(glfw.Decorated, bool2int[!cfg.Undecorated])
		glfw.WindowHint(glfw.Floating, bool2int[cfg.AlwaysOnTop])
		glfw.WindowHint(glfw.AutoIconify, bool2int[!cfg.NoIconify])

		var share *glfw.Window
		if currWin != nil {
			share = currWin.window
		}
		_, _, width, height := intBounds(cfg.Bounds)
		w.window, err = glfw.CreateWindow(
			width,
			height,
			cfg.Title,
			nil,
			share,
		)
		if err != nil {
			return err
		}

		// enter the OpenGL context
		w.begin()
		glhf.Init()
		w.end()

		return nil
	})
	if err != nil {
		return nil, errors.Wrap(err, "creating window failed")
	}

	if len(cfg.Icon) > 0 {
		imgs := make([]image.Image, len(cfg.Icon))
		for i, icon := range cfg.Icon {
			pic := pixel.PictureDataFromPicture(icon)
			imgs[i] = pic.Image()
		}
		mainthread.Call(func() {
			w.window.SetIcon(imgs)
		})
	}

	w.SetVSync(cfg.VSync)

	w.initInput()
	w.SetMonitor(cfg.Monitor)

	w.canvas = NewCanvas(cfg.Bounds)
	w.Update()

	runtime.SetFinalizer(w, (*Window).Destroy)

	return w, nil
}

// Destroy destroys the Window. The Window can't be used any further.
func (w *Window) Destroy() {
	mainthread.Call(func() {
		w.window.Destroy()
	})
}

// Update swaps buffers and polls events. Call this method at the end of each frame.
func (w *Window) Update() {
	mainthread.Call(func() {
		_, _, oldW, oldH := intBounds(w.bounds)
		newW, newH := w.window.GetSize()
		w.bounds = w.bounds.ResizedMin(w.bounds.Size().Add(pixel.V(
			float64(newW-oldW),
			float64(newH-oldH),
		)))
	})

	w.canvas.SetBounds(w.bounds)

	mainthread.Call(func() {
		w.begin()

		framebufferWidth, framebufferHeight := w.window.GetFramebufferSize()
		glhf.Bounds(0, 0, framebufferWidth, framebufferHeight)

		glhf.Clear(0, 0, 0, 0)
		w.canvas.gf.Frame().Begin()
		w.canvas.gf.Frame().Blit(
			nil,
			0, 0, w.canvas.Texture().Width(), w.canvas.Texture().Height(),
			0, 0, framebufferWidth, framebufferHeight,
		)
		w.canvas.gf.Frame().End()

		if w.vsync {
			glfw.SwapInterval(1)
		} else {
			glfw.SwapInterval(0)
		}
		w.window.SwapBuffers()
		w.end()
	})

	w.UpdateInput()
}

// SetClosed sets the closed flag of the Window.
//
// This is useful when overriding the user's attempt to close the Window, or just to close the
// Window from within the program.
func (w *Window) SetClosed(closed bool) {
	mainthread.Call(func() {
		w.window.SetShouldClose(closed)
	})
}

// Closed returns the closed flag of the Window, which reports whether the Window should be closed.
//
// The closed flag is automatically set when a user attempts to close the Window.
func (w *Window) Closed() bool {
	var closed bool
	mainthread.Call(func() {
		closed = w.window.ShouldClose()
	})
	return closed
}

// SetTitle changes the title of the Window.
func (w *Window) SetTitle(title string) {
	mainthread.Call(func() {
		w.window.SetTitle(title)
	})
}

// SetBounds sets the bounds of the Window in pixels. Bounds can be fractional, but the actual size
// of the window will be rounded to integers.
func (w *Window) SetBounds(bounds pixel.Rect) {
	w.bounds = bounds
	mainthread.Call(func() {
		_, _, width, height := intBounds(bounds)
		w.window.SetSize(width, height)
	})
}

// SetPos sets the position, in screen coordinates, of the upper-left corner
// of the client area of the window. Position can be fractional, but the actual position
// of the window will be rounded to integers.
//
// If it is a full screen window, this function does nothing.
func (w *Window) SetPos(pos pixel.Vec) {
	mainthread.Call(func() {
		left, top := int(pos.X), int(pos.Y)
		w.window.SetPos(left, top)
	})
}

// GetPos gets the position, in screen coordinates, of the upper-left corner
// of the client area of the window. The position is rounded to integers.
func (w *Window) GetPos() pixel.Vec {
	var v pixel.Vec
	mainthread.Call(func() {
		x, y := w.window.GetPos()
		v = pixel.V(float64(x), float64(y))
	})
	return v
}

// Bounds returns the current bounds of the Window.
func (w *Window) Bounds() pixel.Rect {
	return w.bounds
}

func (w *Window) setFullscreen(monitor *Monitor) {
	mainthread.Call(func() {
		w.restore.xpos, w.restore.ypos = w.window.GetPos()
		w.restore.width, w.restore.height = w.window.GetSize()

		mode := monitor.monitor.GetVideoMode()

		w.window.SetMonitor(
			monitor.monitor,
			0,
			0,
			mode.Width,
			mode.Height,
			mode.RefreshRate,
		)
	})
}

func (w *Window) setWindowed() {
	mainthread.Call(func() {
		w.window.SetMonitor(
			nil,
			w.restore.xpos,
			w.restore.ypos,
			w.restore.width,
			w.restore.height,
			0,
		)
	})
}

// SetMonitor sets the Window fullscreen on the given Monitor. If the Monitor is nil, the Window
// will be restored to windowed state instead.
//
// The Window will be automatically set to the Monitor's resolution. If you want a different
// resolution, you will need to set it manually with SetBounds method.
func (w *Window) SetMonitor(monitor *Monitor) {
	if w.Monitor() != monitor {
		if monitor != nil {
			w.setFullscreen(monitor)
		} else {
			w.setWindowed()
		}
	}
}

// Monitor returns a monitor the Window is fullscreen on. If the Window is not fullscreen, this
// function returns nil.
func (w *Window) Monitor() *Monitor {
	var monitor *glfw.Monitor
	mainthread.Call(func() {
		monitor = w.window.GetMonitor()
	})
	if monitor == nil {
		return nil
	}
	return &Monitor{
		monitor: monitor,
	}
}

// Focused returns true if the Window has input focus.
func (w *Window) Focused() bool {
	var focused bool
	mainthread.Call(func() {
		focused = w.window.GetAttrib(glfw.Focused) == glfw.True
	})
	return focused
}

// SetVSync sets whether the Window's Update should synchronize with the monitor refresh rate.
func (w *Window) SetVSync(vsync bool) {
	w.vsync = vsync
}

// VSync returns whether the Window is set to synchronize with the monitor refresh rate.
func (w *Window) VSync() bool {
	return w.vsync
}

// SetCursorVisible sets the visibility of the mouse cursor inside the Window client area.
func (w *Window) SetCursorVisible(visible bool) {
	w.cursorVisible = visible
	mainthread.Call(func() {
		if visible {
			w.window.SetInputMode(glfw.CursorMode, glfw.CursorNormal)
		} else {
			w.window.SetInputMode(glfw.CursorMode, glfw.CursorHidden)
		}
	})
}

// CursorVisible returns the visibility status of the mouse cursor.
func (w *Window) CursorVisible() bool {
	return w.cursorVisible
}

// Note: must be called inside the main thread.
func (w *Window) begin() {
	if currWin != w {
		w.window.MakeContextCurrent()
		currWin = w
	}
}

// Note: must be called inside the main thread.
func (w *Window) end() {
	// nothing, really
}

// MakeTriangles generates a specialized copy of the supplied Triangles that will draw onto this
// Window.
//
// Window supports TrianglesPosition, TrianglesColor and TrianglesPicture.
func (w *Window) MakeTriangles(t pixel.Triangles) pixel.TargetTriangles {
	return w.canvas.MakeTriangles(t)
}

// MakePicture generates a specialized copy of the supplied Picture that will draw onto this Window.
//
// Window supports PictureColor.
func (w *Window) MakePicture(p pixel.Picture) pixel.TargetPicture {
	return w.canvas.MakePicture(p)
}

// SetMatrix sets a Matrix that every point will be projected by.
func (w *Window) SetMatrix(m pixel.Matrix) {
	w.canvas.SetMatrix(m)
}

// SetColorMask sets a global color mask for the Window.
func (w *Window) SetColorMask(c color.Color) {
	w.canvas.SetColorMask(c)
}

// SetComposeMethod sets a Porter-Duff composition method to be used in the following draws onto
// this Window.
func (w *Window) SetComposeMethod(cmp pixel.ComposeMethod) {
	w.canvas.SetComposeMethod(cmp)
}

// SetSmooth sets whether the stretched Pictures drawn onto this Window should be drawn smooth or
// pixely.
func (w *Window) SetSmooth(smooth bool) {
	w.canvas.SetSmooth(smooth)
}

// Smooth returns whether the stretched Pictures drawn onto this Window are set to be drawn smooth
// or pixely.
func (w *Window) Smooth() bool {
	return w.canvas.Smooth()
}

// Clear clears the Window with a single color.
func (w *Window) Clear(c color.Color) {
	w.canvas.Clear(c)
}

// Color returns the color of the pixel over the given position inside the Window.
func (w *Window) Color(at pixel.Vec) pixel.RGBA {
	return w.canvas.Color(at)
}

// Canvas returns the window's underlying Canvas
func (w *Window) Canvas() *Canvas {
	return w.canvas
}
<commit_msg>Expose pixelgl.Window.SwapBuffers<commit_after>package pixelgl

import (
	"image"
	"image/color"
	"runtime"

	"github.com/faiface/glhf"
	"github.com/faiface/mainthread"
	"github.com/faiface/pixel"
	"github.com/go-gl/glfw/v3.2/glfw"
	"github.com/pkg/errors"
)

// WindowConfig is a structure for specifying all possible properties of a Window. Properties are
// chosen in such a way, that you usually only need to set a few of them - defaults (zeros) should
// usually be sensible.
//
// Note that you always need to set the Bounds of a Window.
type WindowConfig struct {
	// Title at the top of the Window.
	Title string

	// Icon specifies the icon images available to be used by the window. This is usually
	// displayed in the top bar of the window or in the task bar of the desktop environment.
	//
	// If passed one image, it will use that image, if passed an array of images those of or
	// closest to the sizes desired by the system are selected. The desired image sizes varies
	// depending on platform and system settings. The selected images will be rescaled as
	// needed. Good sizes include 16x16, 32x32 and 48x48.
	//
	// Note: Setting this value doesn't have an effect on OSX. You'll need to set the icon when
	// bundling your application for release.
	Icon []pixel.Picture

	// Bounds specify the bounds of the Window in pixels.
	Bounds pixel.Rect

	// If set to nil, the Window will be windowed. Otherwise it will be fullscreen on the
	// specified Monitor.
	Monitor *Monitor

	// Whether the Window is resizable.
	Resizable bool

	// Undecorated Window ommits the borders and decorations (close button, etc.).
	Undecorated bool

	// NoIconify specifies whether fullscreen windows should not automatically
	// iconify (and restore the previous video mode) on focus loss.
	NoIconify bool

	// AlwaysOnTop specifies whether the windowed mode window will be floating
	// above other regular windows, also called topmost or always-on-top.
	// This is intended primarily for debugging purposes and cannot be used to
	// implement proper full screen windows.
	AlwaysOnTop bool

	// VSync (vertical synchronization) synchronizes Window's framerate with the framerate of
	// the monitor.
	VSync bool
}

// Window is a window handler. Use this type to manipulate a window (input, drawing, etc.).
type Window struct {
	window *glfw.Window

	bounds             pixel.Rect
	canvas             *Canvas
	vsync              bool
	cursorVisible      bool
	cursorInsideWindow bool

	// need to save these to correctly restore a fullscreen window
	restore struct {
		xpos, ypos, width, height int
	}

	prevInp, currInp, tempInp struct {
		mouse   pixel.Vec
		buttons [KeyLast + 1]bool
		repeat  [KeyLast + 1]bool
		scroll  pixel.Vec
		typed   string
	}

	prevJoy, currJoy, tempJoy joystickState
}

var currWin *Window

// NewWindow creates a new Window with it's properties specified in the provided config.
//
// If Window creation fails, an error is returned (e.g. due to unavailable graphics device).
func NewWindow(cfg WindowConfig) (*Window, error) {
	bool2int := map[bool]int{
		true:  glfw.True,
		false: glfw.False,
	}

	w := &Window{bounds: cfg.Bounds, cursorVisible: true}

	err := mainthread.CallErr(func() error {
		var err error

		glfw.WindowHint(glfw.ContextVersionMajor, 3)
		glfw.WindowHint(glfw.ContextVersionMinor, 3)
		glfw.WindowHint(glfw.OpenGLProfile, glfw.OpenGLCoreProfile)
		glfw.WindowHint(glfw.OpenGLForwardCompatible, glfw.True)

		glfw.WindowHint(glfw.Resizable, bool2int[cfg.Resizable])
		glfw.WindowHint(glfw.Decorated, bool2int[!cfg.Undecorated])
		glfw.WindowHint(glfw.Floating, bool2int[cfg.AlwaysOnTop])
		glfw.WindowHint(glfw.AutoIconify, bool2int[!cfg.NoIconify])

		var share *glfw.Window
		if currWin != nil {
			share = currWin.window
		}
		_, _, width, height := intBounds(cfg.Bounds)
		w.window, err = glfw.CreateWindow(
			width,
			height,
			cfg.Title,
			nil,
			share,
		)
		if err != nil {
			return err
		}

		// enter the OpenGL context
		w.begin()
		glhf.Init()
		w.end()

		return nil
	})
	if err != nil {
		return nil, errors.Wrap(err, "creating window failed")
	}

	if len(cfg.Icon) > 0 {
		imgs := make([]image.Image, len(cfg.Icon))
		for i, icon := range cfg.Icon {
			pic := pixel.PictureDataFromPicture(icon)
			imgs[i] = pic.Image()
		}
		mainthread.Call(func() {
			w.window.SetIcon(imgs)
		})
	}

	w.SetVSync(cfg.VSync)

	w.initInput()
	w.SetMonitor(cfg.Monitor)

	w.canvas = NewCanvas(cfg.Bounds)
	w.Update()

	runtime.SetFinalizer(w, (*Window).Destroy)

	return w, nil
}

// Destroy destroys the Window. The Window can't be used any further.
func (w *Window) Destroy() {
	mainthread.Call(func() {
		w.window.Destroy()
	})
}

// Update swaps buffers and polls events. Call this method at the end of each frame.
func (w *Window) Update() {
	w.SwapBuffers()
	w.UpdateInput()
}

// SwapBuffers swaps buffers. Call this to swap buffers without polling window events.
// Note that Update invokes SwapBuffers.
func (w *Window) SwapBuffers() {
	mainthread.Call(func() {
		_, _, oldW, oldH := intBounds(w.bounds)
		newW, newH := w.window.GetSize()
		w.bounds = w.bounds.ResizedMin(w.bounds.Size().Add(pixel.V(
			float64(newW-oldW),
			float64(newH-oldH),
		)))
	})

	w.canvas.SetBounds(w.bounds)

	mainthread.Call(func() {
		w.begin()

		framebufferWidth, framebufferHeight := w.window.GetFramebufferSize()
		glhf.Bounds(0, 0, framebufferWidth, framebufferHeight)

		glhf.Clear(0, 0, 0, 0)
		w.canvas.gf.Frame().Begin()
		w.canvas.gf.Frame().Blit(
			nil,
			0, 0, w.canvas.Texture().Width(), w.canvas.Texture().Height(),
			0, 0, framebufferWidth, framebufferHeight,
		)
		w.canvas.gf.Frame().End()

		if w.vsync {
			glfw.SwapInterval(1)
		} else {
			glfw.SwapInterval(0)
		}
		w.window.SwapBuffers()
		w.end()
	})
}

// SetClosed sets the closed flag of the Window.
//
// This is useful when overriding the user's attempt to close the Window, or just to close the
// Window from within the program.
func (w *Window) SetClosed(closed bool) {
	mainthread.Call(func() {
		w.window.SetShouldClose(closed)
	})
}

// Closed returns the closed flag of the Window, which reports whether the Window should be closed.
//
// The closed flag is automatically set when a user attempts to close the Window.
func (w *Window) Closed() bool {
	var closed bool
	mainthread.Call(func() {
		closed = w.window.ShouldClose()
	})
	return closed
}

// SetTitle changes the title of the Window.
func (w *Window) SetTitle(title string) {
	mainthread.Call(func() {
		w.window.SetTitle(title)
	})
}

// SetBounds sets the bounds of the Window in pixels. Bounds can be fractional, but the actual size
// of the window will be rounded to integers.
func (w *Window) SetBounds(bounds pixel.Rect) {
	w.bounds = bounds
	mainthread.Call(func() {
		_, _, width, height := intBounds(bounds)
		w.window.SetSize(width, height)
	})
}

// SetPos sets the position, in screen coordinates, of the upper-left corner
// of the client area of the window. Position can be fractional, but the actual position
// of the window will be rounded to integers.
//
// If it is a full screen window, this function does nothing.
func (w *Window) SetPos(pos pixel.Vec) {
	mainthread.Call(func() {
		left, top := int(pos.X), int(pos.Y)
		w.window.SetPos(left, top)
	})
}

// GetPos gets the position, in screen coordinates, of the upper-left corner
// of the client area of the window. The position is rounded to integers.
func (w *Window) GetPos() pixel.Vec {
	var v pixel.Vec
	mainthread.Call(func() {
		x, y := w.window.GetPos()
		v = pixel.V(float64(x), float64(y))
	})
	return v
}

// Bounds returns the current bounds of the Window.
func (w *Window) Bounds() pixel.Rect {
	return w.bounds
}

func (w *Window) setFullscreen(monitor *Monitor) {
	mainthread.Call(func() {
		w.restore.xpos, w.restore.ypos = w.window.GetPos()
		w.restore.width, w.restore.height = w.window.GetSize()

		mode := monitor.monitor.GetVideoMode()

		w.window.SetMonitor(
			monitor.monitor,
			0,
			0,
			mode.Width,
			mode.Height,
			mode.RefreshRate,
		)
	})
}

func (w *Window) setWindowed() {
	mainthread.Call(func() {
		w.window.SetMonitor(
			nil,
			w.restore.xpos,
			w.restore.ypos,
			w.restore.width,
			w.restore.height,
			0,
		)
	})
}

// SetMonitor sets the Window fullscreen on the given Monitor. If the Monitor is nil, the Window
// will be restored to windowed state instead.
//
// The Window will be automatically set to the Monitor's resolution. If you want a different
// resolution, you will need to set it manually with SetBounds method.
func (w *Window) SetMonitor(monitor *Monitor) {
	if w.Monitor() != monitor {
		if monitor != nil {
			w.setFullscreen(monitor)
		} else {
			w.setWindowed()
		}
	}
}

// Monitor returns a monitor the Window is fullscreen on. If the Window is not fullscreen, this
// function returns nil.
func (w *Window) Monitor() *Monitor {
	var monitor *glfw.Monitor
	mainthread.Call(func() {
		monitor = w.window.GetMonitor()
	})
	if monitor == nil {
		return nil
	}
	return &Monitor{
		monitor: monitor,
	}
}

// Focused returns true if the Window has input focus.
func (w *Window) Focused() bool {
	var focused bool
	mainthread.Call(func() {
		focused = w.window.GetAttrib(glfw.Focused) == glfw.True
	})
	return focused
}

// SetVSync sets whether the Window's Update should synchronize with the monitor refresh rate.
func (w *Window) SetVSync(vsync bool) {
	w.vsync = vsync
}

// VSync returns whether the Window is set to synchronize with the monitor refresh rate.
func (w *Window) VSync() bool {
	return w.vsync
}

// SetCursorVisible sets the visibility of the mouse cursor inside the Window client area.
func (w *Window) SetCursorVisible(visible bool) {
	w.cursorVisible = visible
	mainthread.Call(func() {
		if visible {
			w.window.SetInputMode(glfw.CursorMode, glfw.CursorNormal)
		} else {
			w.window.SetInputMode(glfw.CursorMode, glfw.CursorHidden)
		}
	})
}

// CursorVisible returns the visibility status of the mouse cursor.
func (w *Window) CursorVisible() bool {
	return w.cursorVisible
}

// Note: must be called inside the main thread.
func (w *Window) begin() {
	if currWin != w {
		w.window.MakeContextCurrent()
		currWin = w
	}
}

// Note: must be called inside the main thread.
func (w *Window) end() {
	// nothing, really
}

// MakeTriangles generates a specialized copy of the supplied Triangles that will draw onto this
// Window.
//
// Window supports TrianglesPosition, TrianglesColor and TrianglesPicture.
func (w *Window) MakeTriangles(t pixel.Triangles) pixel.TargetTriangles {
	return w.canvas.MakeTriangles(t)
}

// MakePicture generates a specialized copy of the supplied Picture that will draw onto this Window.
//
// Window supports PictureColor.
func (w *Window) MakePicture(p pixel.Picture) pixel.TargetPicture {
	return w.canvas.MakePicture(p)
}

// SetMatrix sets a Matrix that every point will be projected by.
func (w *Window) SetMatrix(m pixel.Matrix) {
	w.canvas.SetMatrix(m)
}

// SetColorMask sets a global color mask for the Window.
func (w *Window) SetColorMask(c color.Color) {
	w.canvas.SetColorMask(c)
}

// SetComposeMethod sets a Porter-Duff composition method to be used in the following draws onto
// this Window.
func (w *Window) SetComposeMethod(cmp pixel.ComposeMethod) {
	w.canvas.SetComposeMethod(cmp)
}

// SetSmooth sets whether the stretched Pictures drawn onto this Window should be drawn smooth or
// pixely.
func (w *Window) SetSmooth(smooth bool) {
	w.canvas.SetSmooth(smooth)
}

// Smooth returns whether the stretched Pictures drawn onto this Window are set to be drawn smooth
// or pixely.
func (w *Window) Smooth() bool {
	return w.canvas.Smooth()
}

// Clear clears the Window with a single color.
func (w *Window) Clear(c color.Color) {
	w.canvas.Clear(c)
}

// Color returns the color of the pixel over the given position inside the Window.
func (w *Window) Color(at pixel.Vec) pixel.RGBA {
	return w.canvas.Color(at)
}

// Canvas returns the window's underlying Canvas
func (w *Window) Canvas() *Canvas {
	return w.canvas
}
<|endoftext|>


<commit_before>package store

import (
	"gob"
	"strconv"
	"strings"
)

var root = node{v:"", ds:make(map[string]node), cas:Dir}

var errPath = "/store/error"

// This structure should be kept immutable.
type node struct {
	v string
	cas string
	ds map[string]node
}

func (n node) readdir() string {
	names := make([]string, len(n.ds))
	i := 0
	for name, _ := range n.ds {
		names[i] = name + "\n"
		i++
	}
	return strings.Join(names, "")
}

func (n node) get(parts []string) (string, string) {
	switch len(parts) {
	case 0:
		if len(n.ds) > 0 {
			return n.readdir(), n.cas
		} else {
			return n.v, n.cas
		}
	default:
		if m, ok := n.ds[parts[0]]; ok {
			return m.get(parts[1:])
		}
		return "", Missing
	}
	panic("can't happen")
}

func (n node) getp(path string) (string, string) {
	if err := checkPath(path); err != nil {
		return "", Missing
	}

	return n.get(split(path))
}

func copyMap(a map[string]node) map[string]node {
	b := make(map[string]node)
	for k,v := range a {
		b[k] = v
	}
	return b
}

// Return value is replacement node
func (n node) set(parts []string, v, cas string, keep bool) (node, bool) {
	if len(parts) == 0 {
		return node{v, cas, n.ds}, keep
	}

	n.ds = copyMap(n.ds)
	p, ok := n.ds[parts[0]].set(parts[1:], v, cas, keep)
	n.ds[parts[0]] = p, ok
	n.cas = Dir
	return n, len(n.ds) > 0
}

func (n node) setp(k, v, cas string, keep bool) node {
	if err := checkPath(k); err != nil {
		return n
	}

	n, _ = n.set(split(k), v, cas, keep)
	return n
}

func (n node) apply(seqn uint64, mut string) (rep node, ev Event) {
	ev.Seqn, ev.Cas, ev.Mut = seqn, strconv.Uitoa64(seqn), mut
	if seqn == 1 {
		d := gob.NewDecoder(strings.NewReader(mut))
		if d.Decode(&ev.Seqn) == nil {
			ev.Cas = ""
			ev.Err = d.Decode(&rep)
			if ev.Err != nil {
				ev.Seqn = seqn + 1
				rep = n
			}
			return
		}
	}

	cas, keep := "", false
	ev.Path, ev.Body, cas, keep, ev.Err = decode(mut)
	if ev.Err != nil {
		ev.Path, ev.Body, cas, keep = errPath, ev.Err.String(), Clobber, true
	}

	_, curCas := n.getp(ev.Path)
	if cas != curCas && cas != Clobber {
		ev.Err = CasMismatchError
		ev.Path, ev.Body, cas, keep = errPath, ev.Err.String(), Clobber, true
	}

	if !keep {
		ev.Cas = Missing
	}

	rep = n.setp(ev.Path, ev.Body, ev.Cas, keep)
	return
}
<commit_msg>only check old cas if necessary<commit_after>package store

import (
	"gob"
	"strconv"
	"strings"
)

var root = node{v:"", ds:make(map[string]node), cas:Dir}

var errPath = "/store/error"

// This structure should be kept immutable.
type node struct {
	v string
	cas string
	ds map[string]node
}

func (n node) readdir() string {
	names := make([]string, len(n.ds))
	i := 0
	for name, _ := range n.ds {
		names[i] = name + "\n"
		i++
	}
	return strings.Join(names, "")
}

func (n node) get(parts []string) (string, string) {
	switch len(parts) {
	case 0:
		if len(n.ds) > 0 {
			return n.readdir(), n.cas
		} else {
			return n.v, n.cas
		}
	default:
		if m, ok := n.ds[parts[0]]; ok {
			return m.get(parts[1:])
		}
		return "", Missing
	}
	panic("can't happen")
}

func (n node) getp(path string) (string, string) {
	if err := checkPath(path); err != nil {
		return "", Missing
	}

	return n.get(split(path))
}

func copyMap(a map[string]node) map[string]node {
	b := make(map[string]node)
	for k,v := range a {
		b[k] = v
	}
	return b
}

// Return value is replacement node
func (n node) set(parts []string, v, cas string, keep bool) (node, bool) {
	if len(parts) == 0 {
		return node{v, cas, n.ds}, keep
	}

	n.ds = copyMap(n.ds)
	p, ok := n.ds[parts[0]].set(parts[1:], v, cas, keep)
	n.ds[parts[0]] = p, ok
	n.cas = Dir
	return n, len(n.ds) > 0
}

func (n node) setp(k, v, cas string, keep bool) node {
	if err := checkPath(k); err != nil {
		return n
	}

	n, _ = n.set(split(k), v, cas, keep)
	return n
}

func (n node) apply(seqn uint64, mut string) (rep node, ev Event) {
	ev.Seqn, ev.Cas, ev.Mut = seqn, strconv.Uitoa64(seqn), mut
	if seqn == 1 {
		d := gob.NewDecoder(strings.NewReader(mut))
		if d.Decode(&ev.Seqn) == nil {
			ev.Cas = ""
			ev.Err = d.Decode(&rep)
			if ev.Err != nil {
				ev.Seqn = seqn + 1
				rep = n
			}
			return
		}
	}

	cas, keep := "", false
	ev.Path, ev.Body, cas, keep, ev.Err = decode(mut)
	if ev.Err != nil {
		ev.Path, ev.Body, cas, keep = errPath, ev.Err.String(), Clobber, true
	}

	if cas != Clobber {
		_, curCas := n.getp(ev.Path)
		if cas != curCas {
			ev.Err = CasMismatchError
			ev.Path, ev.Body, cas, keep = errPath, ev.Err.String(), Clobber, true
		}
	}

	if !keep {
		ev.Cas = Missing
	}

	rep = n.setp(ev.Path, ev.Body, ev.Cas, keep)
	return
}
<|endoftext|>


<commit_before>/*
Copyright 2017 The OpenEBS Authors

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package task

import (
	"fmt"
	"strings"

	"github.com/golang/glog"
	"github.com/pkg/errors"

	"github.com/openebs/maya/pkg/apis/openebs.io/v1alpha1"
	m_k8s_client "github.com/openebs/maya/pkg/client/k8s"
	mach_apis_meta_v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)

// TaskSpecFetcher is the contract to fetch task
// specification that includes the task's meta specification
type TaskSpecFetcher interface {
	Fetch(taskName string) (runtask *v1alpha1.RunTask, err error)
}

// K8sTaskSpecFetcher deals with fetching a task specifications
// from K8s cluster
//
// NOTE:
//  A task is a K8s ConfigMap
type K8sTaskSpecFetcher struct {
	// k8sClient to make K8s API calls
	//
	// NOTE:
	//  This is also helpful for mocking during UT
	k8sClient *m_k8s_client.K8sClient
}

// FetchSpec returns specifications of a provided task in yaml
// string format
//
// NOTE:
//  This is an implementation of TaskSpecFetcher interface
func (f *K8sTaskSpecFetcher) Fetch(taskName string) (runtask *v1alpha1.RunTask, err error) {
	rtGetter := defaultRunTaskGetter(getRunTaskSpec{
		taskName:  taskName,
		k8sClient: f.k8sClient,
	})

	return rtGetter.Get()
}

// NewK8sTaskSpecFetcher returns a new instance of K8sTaskSpecFetcher based on the
// provided namespace.
//
// NOTE:
//  SearchNamespace refers to the K8s namespace where a task
// is expected to be found
func NewK8sTaskSpecFetcher(searchNamespace string) (*K8sTaskSpecFetcher, error) {
	kc, err := m_k8s_client.NewK8sClient(searchNamespace)
	if err != nil {
		return nil, err
	}

	return &K8sTaskSpecFetcher{
		k8sClient: kc,
	}, nil
}

// runTaskGetter abstracts fetching of runtask instance
type runTaskGetter interface {
	Get() (runtask *v1alpha1.RunTask, err error)
}

// getRunTaskSpec composes common properties required to get a run task
// instance
type getRunTaskSpec struct {
	taskName  string
	k8sClient *m_k8s_client.K8sClient
}

// runTaskGetterFn abstracts fetching of runtask instance based on the provided
// runtask specifications
type runTaskGetterFn func(getSpec getRunTaskSpec) (runtask *v1alpha1.RunTask, err error)

// getRunTaskFromConfigMap fetches runtask instance from a config map instance
func getRunTaskFromConfigMap(g getRunTaskSpec) (runtask *v1alpha1.RunTask, err error) {
	if len(strings.TrimSpace(g.taskName)) == 0 {
		err = fmt.Errorf("missing run task name: failed to get runtask from config map")
		return
	}

	if g.k8sClient == nil {
		err = fmt.Errorf("nil kubernetes client found: failed to get runtask '%s' from config map", g.taskName)
		return
	}

	cm, err := g.k8sClient.GetConfigMap(g.taskName, mach_apis_meta_v1.GetOptions{})
	if err != nil {
		err = errors.Wrap(err, fmt.Sprintf("failed to get run task '%s' from config map", g.taskName))
		return
	}

	runtask = &v1alpha1.RunTask{}
	runtask.Name = g.taskName
	runtask.Spec.Meta = cm.Data["meta"]
	runtask.Spec.Task = cm.Data["task"]
	runtask.Spec.PostRun = cm.Data["post"]

	return
}

// getRunTaskFromCustomResource fetches runtask instance from its custom
// resource instance
func getRunTaskFromCustomResource(g getRunTaskSpec) (runtask *v1alpha1.RunTask, err error) {
	if len(strings.TrimSpace(g.taskName)) == 0 {
		err = fmt.Errorf("missing run task name: failed to get runtask from custom resource")
		return
	}

	if g.k8sClient == nil {
		err = fmt.Errorf("nil kubernetes client: failed to get runtask '%s' from custom resource", g.taskName)
		return
	}

	runtask, err = g.k8sClient.GetOEV1alpha1RunTask(g.taskName, mach_apis_meta_v1.GetOptions{})
	if err != nil {
		err = errors.Wrap(err, fmt.Sprintf("failed to get runtask '%s' from custom resource", g.taskName))
	}

	return
}

// getRunTask enables fetching a run task instance based on various run task
// getter strategies
//
// NOTE:
//  This is an implementation of runTaskGetter
type getRunTask struct {
	// getRunTaskSpec is the specifications required to fetch runtask instance
	getRunTaskSpec
	// currentStrategy is the latest strategy to fetch runtask instance
	currentStrategy runTaskGetterFn
	// oldStrategies are the older strategies to fetch runtask instance
	oldStrategies []runTaskGetterFn
}

// Get returns an instance of runtask
func (g *getRunTask) Get() (runtask *v1alpha1.RunTask, err error) {
	var allStrategies []runTaskGetterFn
	if g.currentStrategy != nil {
		allStrategies = append(allStrategies, g.currentStrategy)
	}

	if len(g.oldStrategies) != 0 {
		allStrategies = append(allStrategies, g.oldStrategies...)
	}

	if len(allStrategies) == 0 {
		err = fmt.Errorf("no strategies to get runtask: failed to get runtask '%s'", g.taskName)
		return
	}

	for _, s := range allStrategies {
		runtask, err = s(g.getRunTaskSpec)
		if err == nil {
			return
		}

		err = errors.Wrap(err, fmt.Sprintf("failed to get runtask '%s'", g.taskName))
		glog.Warningf("%s", err)
	}

	// at this point, we have a real error we can not recover from
	err = fmt.Errorf("exhausted all strategies to get runtask: failed to get runtask '%s'", g.taskName)
	return
}

func defaultRunTaskGetter(getSpec getRunTaskSpec) *getRunTask {
	// current strategy
	current := getRunTaskFromCustomResource
	// older strategies
	old := []runTaskGetterFn{getRunTaskFromConfigMap}

	return &getRunTask{
		getRunTaskSpec:  getSpec,
		currentStrategy: current,
		oldStrategies:   old,
	}
}
<commit_msg>Fixed the golint error in maya/pkg/task/fetch.go (#665)<commit_after>/*
Copyright 2017 The OpenEBS Authors

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package task

import (
	"fmt"
	"strings"

	"github.com/golang/glog"
	"github.com/pkg/errors"

	"github.com/openebs/maya/pkg/apis/openebs.io/v1alpha1"
	m_k8s_client "github.com/openebs/maya/pkg/client/k8s"
	mach_apis_meta_v1 "k8s.io/apimachinery/pkg/apis/meta/v1"
)

// TaskSpecFetcher is the contract to fetch task
// specification that includes the task's meta specification
type TaskSpecFetcher interface {
	Fetch(taskName string) (runtask *v1alpha1.RunTask, err error)
}

// K8sTaskSpecFetcher deals with fetching a task specifications
// from K8s cluster
//
// NOTE:
//  A task is a K8s ConfigMap
type K8sTaskSpecFetcher struct {
	// k8sClient to make K8s API calls
	//
	// NOTE:
	//  This is also helpful for mocking during UT
	k8sClient *m_k8s_client.K8sClient
}

// Fetch returns specifications of a provided task in yaml
// string format
//
// NOTE:
//  This is an implementation of TaskSpecFetcher interface
func (f *K8sTaskSpecFetcher) Fetch(taskName string) (runtask *v1alpha1.RunTask, err error) {
	rtGetter := defaultRunTaskGetter(getRunTaskSpec{
		taskName:  taskName,
		k8sClient: f.k8sClient,
	})

	return rtGetter.Get()
}

// NewK8sTaskSpecFetcher returns a new instance of K8sTaskSpecFetcher based on the
// provided namespace.
//
// NOTE:
//  SearchNamespace refers to the K8s namespace where a task
// is expected to be found
func NewK8sTaskSpecFetcher(searchNamespace string) (*K8sTaskSpecFetcher, error) {
	kc, err := m_k8s_client.NewK8sClient(searchNamespace)
	if err != nil {
		return nil, err
	}

	return &K8sTaskSpecFetcher{
		k8sClient: kc,
	}, nil
}

// runTaskGetter abstracts fetching of runtask instance
type runTaskGetter interface {
	Get() (runtask *v1alpha1.RunTask, err error)
}

// getRunTaskSpec composes common properties required to get a run task
// instance
type getRunTaskSpec struct {
	taskName  string
	k8sClient *m_k8s_client.K8sClient
}

// runTaskGetterFn abstracts fetching of runtask instance based on the provided
// runtask specifications
type runTaskGetterFn func(getSpec getRunTaskSpec) (runtask *v1alpha1.RunTask, err error)

// getRunTaskFromConfigMap fetches runtask instance from a config map instance
func getRunTaskFromConfigMap(g getRunTaskSpec) (runtask *v1alpha1.RunTask, err error) {
	if len(strings.TrimSpace(g.taskName)) == 0 {
		err = fmt.Errorf("missing run task name: failed to get runtask from config map")
		return
	}

	if g.k8sClient == nil {
		err = fmt.Errorf("nil kubernetes client found: failed to get runtask '%s' from config map", g.taskName)
		return
	}

	cm, err := g.k8sClient.GetConfigMap(g.taskName, mach_apis_meta_v1.GetOptions{})
	if err != nil {
		err = errors.Wrap(err, fmt.Sprintf("failed to get run task '%s' from config map", g.taskName))
		return
	}

	runtask = &v1alpha1.RunTask{}
	runtask.Name = g.taskName
	runtask.Spec.Meta = cm.Data["meta"]
	runtask.Spec.Task = cm.Data["task"]
	runtask.Spec.PostRun = cm.Data["post"]

	return
}

// getRunTaskFromCustomResource fetches runtask instance from its custom
// resource instance
func getRunTaskFromCustomResource(g getRunTaskSpec) (runtask *v1alpha1.RunTask, err error) {
	if len(strings.TrimSpace(g.taskName)) == 0 {
		err = fmt.Errorf("missing run task name: failed to get runtask from custom resource")
		return
	}

	if g.k8sClient == nil {
		err = fmt.Errorf("nil kubernetes client: failed to get runtask '%s' from custom resource", g.taskName)
		return
	}

	runtask, err = g.k8sClient.GetOEV1alpha1RunTask(g.taskName, mach_apis_meta_v1.GetOptions{})
	if err != nil {
		err = errors.Wrap(err, fmt.Sprintf("failed to get runtask '%s' from custom resource", g.taskName))
	}

	return
}

// getRunTask enables fetching a run task instance based on various run task
// getter strategies
//
// NOTE:
//  This is an implementation of runTaskGetter
type getRunTask struct {
	// getRunTaskSpec is the specifications required to fetch runtask instance
	getRunTaskSpec
	// currentStrategy is the latest strategy to fetch runtask instance
	currentStrategy runTaskGetterFn
	// oldStrategies are the older strategies to fetch runtask instance
	oldStrategies []runTaskGetterFn
}

// Get returns an instance of runtask
func (g *getRunTask) Get() (runtask *v1alpha1.RunTask, err error) {
	var allStrategies []runTaskGetterFn
	if g.currentStrategy != nil {
		allStrategies = append(allStrategies, g.currentStrategy)
	}

	if len(g.oldStrategies) != 0 {
		allStrategies = append(allStrategies, g.oldStrategies...)
	}

	if len(allStrategies) == 0 {
		err = fmt.Errorf("no strategies to get runtask: failed to get runtask '%s'", g.taskName)
		return
	}

	for _, s := range allStrategies {
		runtask, err = s(g.getRunTaskSpec)
		if err == nil {
			return
		}

		err = errors.Wrap(err, fmt.Sprintf("failed to get runtask '%s'", g.taskName))
		glog.Warningf("%s", err)
	}

	// at this point, we have a real error we can not recover from
	err = fmt.Errorf("exhausted all strategies to get runtask: failed to get runtask '%s'", g.taskName)
	return
}

func defaultRunTaskGetter(getSpec getRunTaskSpec) *getRunTask {
	// current strategy
	current := getRunTaskFromCustomResource
	// older strategies
	old := []runTaskGetterFn{getRunTaskFromConfigMap}

	return &getRunTask{
		getRunTaskSpec:  getSpec,
		currentStrategy: current,
		oldStrategies:   old,
	}
}
<|endoftext|>


<commit_before>/*
Copyright 2016 The Kubernetes Authors.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package util

import (
	"bytes"
	"encoding/json"
	"errors"
	"fmt"
	"io/ioutil"
	"net/http"
	"os"
	"os/exec"
	"strings"

	"github.com/gorilla/mux"
)

// SendRequest will serialize 'object' and send it using the given method to
// the given URL, through the provided client
func SendRequest(c *http.Client, method string, url string, object interface{}) (*http.Response, error) {
	data, err := json.Marshal(object)
	if err != nil {
		return nil, fmt.Errorf("Failed to marshal request: %s", err.Error())
	}

	req, err := http.NewRequest(method, url, bytes.NewReader(data))
	if err != nil {
		return nil, fmt.Errorf("Failed to create request object: %s", err.Error())
	}

	req.Header.Set("Content-Type", "application/json")

	resp, err := c.Do(req)
	if err != nil {
		return nil, fmt.Errorf("Failed to send request: %s", err.Error())
	}

	return resp, nil
}

// WriteResponse will serialize 'object' to the HTTP ResponseWriter
// using the 'code' as the HTTP status code
func WriteResponse(w http.ResponseWriter, code int, object interface{}) {
	data, err := json.Marshal(object)
	if err != nil {
		w.WriteHeader(http.StatusInternalServerError)
		return
	}

	w.WriteHeader(code)
	w.Header().Set("Content-Type", "application/json")
	fmt.Fprintf(w, string(data))
}

// BodyToObject will convert the incoming HTTP request into the
// passed in 'object'
func BodyToObject(r *http.Request, object interface{}) error {
	body, err := ioutil.ReadAll(r.Body)
	if err != nil {
		return err
	}

	err = json.Unmarshal(body, object)
	if err != nil {
		return err
	}

	return nil
}

// ResponseBodyToObject will reading the HTTP response into the
// passed in 'object'
func ResponseBodyToObject(r *http.Response, object interface{}) error {
	body, err := ioutil.ReadAll(r.Body)
	if err != nil {
		return err
	}

	err = json.Unmarshal(body, object)
	if err != nil {
		return err
	}

	return nil
}

// ExtractVarFromRequest will return the values of the passed in
// MUX variable name (varName). The variables are the substitution
// strings from the URL.
func ExtractVarFromRequest(r *http.Request, varName string) string {
	return mux.Vars(r)[varName]
}

// ExecCmd executes a command and returns the stdout + error, if any
func ExecCmd(cmd string) (string, error) {
	fmt.Println("command: " + cmd)

	parts := strings.Fields(cmd)
	head := parts[0]
	parts = parts[1:]

	out, err := exec.Command(head, parts...).CombinedOutput()
	if err != nil {
		fmt.Printf("Command failed with: %s\n", err)
		fmt.Printf("Output: %s\n", out)
		return "", err
	}
	return string(out), nil
}

// Fetch will do an HTTP GET to the passed in URL, returning
// HTTP Body of the response or any error
func Fetch(u string) (string, error) {
	fmt.Printf("Fetching: %s\n", u)
	resp, err := http.Get(u)
	if err != nil {
		return "", err
	}
	defer resp.Body.Close()
	body, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return "", err
	}
	return string(body), nil
}

// FetchObject will do an HTTP GET to the passed in URL, returning
// the response parsed as a JSON object, or any error
func FetchObject(u string, object interface{}) error {
	r, err := http.Get(u)
	if err != nil {
		return err
	}
	defer r.Body.Close()
	body, err := ioutil.ReadAll(r.Body)
	if err != nil {
		return err
	}

	err = json.Unmarshal(body, object)
	if err != nil {
		return err
	}
	return nil
}

// FetchChartToFile fetches the given chart to an opened file. File will be closed in any case.
func FetchChartToFile(chart string, f *os.File) error {
	defer f.Close()
	if !strings.HasPrefix(chart, "gs://") {
		return errors.New("chart name format gs://... is requried")
	}
	u := strings.Replace(chart, "gs://", "https://storage.googleapis.com/", 1)
	bytes, err := Fetch(u)
	if err != nil {
		return err
	}
	_, err = f.Write([]byte(bytes))
	return err
}
<commit_msg>Remove unused method from pkg/util/util.go (#524)<commit_after>/*
Copyright 2016 The Kubernetes Authors.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

package util

import (
	"bytes"
	"encoding/json"
	"errors"
	"fmt"
	"io/ioutil"
	"net/http"
	"os"
	"os/exec"
	"strings"
)

// SendRequest will serialize 'object' and send it using the given method to
// the given URL, through the provided client
func SendRequest(c *http.Client, method string, url string, object interface{}) (*http.Response, error) {
	data, err := json.Marshal(object)
	if err != nil {
		return nil, fmt.Errorf("Failed to marshal request: %s", err.Error())
	}

	req, err := http.NewRequest(method, url, bytes.NewReader(data))
	if err != nil {
		return nil, fmt.Errorf("Failed to create request object: %s", err.Error())
	}

	req.Header.Set("Content-Type", "application/json")

	resp, err := c.Do(req)
	if err != nil {
		return nil, fmt.Errorf("Failed to send request: %s", err.Error())
	}

	return resp, nil
}

// WriteResponse will serialize 'object' to the HTTP ResponseWriter
// using the 'code' as the HTTP status code
func WriteResponse(w http.ResponseWriter, code int, object interface{}) {
	data, err := json.Marshal(object)
	if err != nil {
		w.WriteHeader(http.StatusInternalServerError)
		return
	}

	w.WriteHeader(code)
	w.Header().Set("Content-Type", "application/json")
	fmt.Fprintf(w, string(data))
}

// BodyToObject will convert the incoming HTTP request into the
// passed in 'object'
func BodyToObject(r *http.Request, object interface{}) error {
	body, err := ioutil.ReadAll(r.Body)
	if err != nil {
		return err
	}

	err = json.Unmarshal(body, object)
	if err != nil {
		return err
	}

	return nil
}

// ResponseBodyToObject will reading the HTTP response into the
// passed in 'object'
func ResponseBodyToObject(r *http.Response, object interface{}) error {
	body, err := ioutil.ReadAll(r.Body)
	if err != nil {
		return err
	}

	err = json.Unmarshal(body, object)
	if err != nil {
		return err
	}

	return nil
}

// ExecCmd executes a command and returns the stdout + error, if any
func ExecCmd(cmd string) (string, error) {
	fmt.Println("command: " + cmd)

	parts := strings.Fields(cmd)
	head := parts[0]
	parts = parts[1:]

	out, err := exec.Command(head, parts...).CombinedOutput()
	if err != nil {
		fmt.Printf("Command failed with: %s\n", err)
		fmt.Printf("Output: %s\n", out)
		return "", err
	}
	return string(out), nil
}

// Fetch will do an HTTP GET to the passed in URL, returning
// HTTP Body of the response or any error
func Fetch(u string) (string, error) {
	fmt.Printf("Fetching: %s\n", u)
	resp, err := http.Get(u)
	if err != nil {
		return "", err
	}
	defer resp.Body.Close()
	body, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return "", err
	}
	return string(body), nil
}

// FetchObject will do an HTTP GET to the passed in URL, returning
// the response parsed as a JSON object, or any error
func FetchObject(u string, object interface{}) error {
	r, err := http.Get(u)
	if err != nil {
		return err
	}
	defer r.Body.Close()
	body, err := ioutil.ReadAll(r.Body)
	if err != nil {
		return err
	}

	err = json.Unmarshal(body, object)
	if err != nil {
		return err
	}
	return nil
}

// FetchChartToFile fetches the given chart to an opened file. File will be closed in any case.
func FetchChartToFile(chart string, f *os.File) error {
	defer f.Close()
	if !strings.HasPrefix(chart, "gs://") {
		return errors.New("chart name format gs://... is requried")
	}
	u := strings.Replace(chart, "gs://", "https://storage.googleapis.com/", 1)
	bytes, err := Fetch(u)
	if err != nil {
		return err
	}
	_, err = f.Write([]byte(bytes))
	return err
}
<|endoftext|>


<commit_before>package api

import (
	"gopkg.in/mgo.v2"
)

var (
	s        *mgo.Session
	err      error
	host     string = "127.0.0.1"
	database string = "sparck-poll"
)

func init() {
	s, err = mgo.Dial(host)
	if err != nil {
		panic(err)
	}
	s.SetMode(mgo.Monotonic, true)

	c := s.DB(database).C("poll")
	index := mgo.Index{
		Key: []string{"$text:name"},
	}

	err := c.EnsureIndex(index)
	if err != nil {
		panic(err)
	}
}
<commit_msg>index search<commit_after>package api

import (
	"gopkg.in/mgo.v2"
)

var (
	s        *mgo.Session
	err      error
	host     string = "127.0.0.1"
	database string = "sparck-poll"
)

func init() {
	s, err = mgo.Dial(host)
	if err != nil {
		panic(err)
	}
	s.SetMode(mgo.Monotonic, true)

	c := s.DB(database).C("poll")

	// @travis mongodb not have text search enabled
	/*
		index := mgo.Index{
			Key: []string{"$text:name"},
		}

		err := c.EnsureIndex(index)
		if err != nil {
			panic(err)
		}
	*/
}
<|endoftext|>


<commit_before>package command

import (
	"context"
	"fmt"
	"net/http"
	"time"

	"github.com/chrislusf/seaweedfs/weed/pb"
	"github.com/chrislusf/seaweedfs/weed/pb/filer_pb"
	"github.com/chrislusf/seaweedfs/weed/security"

	"github.com/gorilla/mux"

	"github.com/chrislusf/seaweedfs/weed/glog"
	"github.com/chrislusf/seaweedfs/weed/s3api"
	stats_collect "github.com/chrislusf/seaweedfs/weed/stats"
	"github.com/chrislusf/seaweedfs/weed/util"
)

var (
	s3StandaloneOptions S3Options
)

type S3Options struct {
	filer           *string
	port            *int
	config          *string
	domainName      *string
	tlsPrivateKey   *string
	tlsCertificate  *string
	metricsHttpPort *int
}

func init() {
	cmdS3.Run = runS3 // break init cycle
	s3StandaloneOptions.filer = cmdS3.Flag.String("filer", "localhost:8888", "filer server address")
	s3StandaloneOptions.port = cmdS3.Flag.Int("port", 8333, "s3 server http listen port")
	s3StandaloneOptions.domainName = cmdS3.Flag.String("domainName", "", "suffix of the host name, {bucket}.{domainName}")
	s3StandaloneOptions.config = cmdS3.Flag.String("config", "", "path to the config file")
	s3StandaloneOptions.tlsPrivateKey = cmdS3.Flag.String("key.file", "", "path to the TLS private key file")
	s3StandaloneOptions.tlsCertificate = cmdS3.Flag.String("cert.file", "", "path to the TLS certificate file")
	s3StandaloneOptions.metricsHttpPort = cmdS3.Flag.Int("metricsPort", 0, "Prometheus metrics listen port")
}

var cmdS3 = &Command{
	UsageLine: "s3 [-port=8333] [-filer=<ip:port>] [-config=</path/to/config.json>]",
	Short:     "start a s3 API compatible server that is backed by a filer",
	Long: `start a s3 API compatible server that is backed by a filer.

	By default, you can use any access key and secret key to access the S3 APIs.
	To enable credential based access, create a config.json file similar to this:

{
  "identities": [
    {
      "name": "some_name",
      "credentials": [
        {
          "accessKey": "some_access_key1",
          "secretKey": "some_secret_key1"
        }
      ],
      "actions": [
        "Admin",
        "Read",
        "Write"
      ]
    },
    {
      "name": "some_read_only_user",
      "credentials": [
        {
          "accessKey": "some_access_key2",
          "secretKey": "some_secret_key2"
        }
      ],
      "actions": [
        "Read"
      ]
    },
    {
      "name": "some_normal_user",
      "credentials": [
        {
          "accessKey": "some_access_key3",
          "secretKey": "some_secret_key3"
        }
      ],
      "actions": [
        "Read",
        "Write"
      ]
    },
    {
      "name": "user_limited_to_bucket1",
      "credentials": [
        {
          "accessKey": "some_access_key4",
          "secretKey": "some_secret_key4"
        }
      ],
      "actions": [
        "Read:bucket1",
        "Write:bucket1"
      ]
    }
  ]
}

`,
}

func runS3(cmd *Command, args []string) bool {

	util.LoadConfiguration("security", false)

	go stats_collect.StartMetricsServer(*s3StandaloneOptions.metricsHttpPort)

	return s3StandaloneOptions.startS3Server()

}

func (s3opt *S3Options) startS3Server() bool {

	filerGrpcAddress, err := pb.ParseFilerGrpcAddress(*s3opt.filer)
	if err != nil {
		glog.Fatal(err)
		return false
	}

	filerBucketsPath := "/buckets"

	grpcDialOption := security.LoadClientTLS(util.GetViper(), "grpc.client")

	// metrics read from the filer
	var metricsAddress string
	var metricsIntervalSec int

	for {
		err = pb.WithGrpcFilerClient(filerGrpcAddress, grpcDialOption, func(client filer_pb.SeaweedFilerClient) error {
			resp, err := client.GetFilerConfiguration(context.Background(), &filer_pb.GetFilerConfigurationRequest{})
			if err != nil {
				return fmt.Errorf("get filer %s configuration: %v", filerGrpcAddress, err)
			}
			filerBucketsPath = resp.DirBuckets
			metricsAddress, metricsIntervalSec = resp.MetricsAddress, int(resp.MetricsIntervalSec)
			glog.V(0).Infof("S3 read filer buckets dir: %s", filerBucketsPath)
			return nil
		})
		if err != nil {
			glog.V(0).Infof("wait to connect to filer %s grpc address %s", *s3opt.filer, filerGrpcAddress)
			time.Sleep(time.Second)
		} else {
			glog.V(0).Infof("connected to filer %s grpc address %s", *s3opt.filer, filerGrpcAddress)
			break
		}
	}

	go stats_collect.LoopPushingMetric("s3", stats_collect.SourceName(uint32(*s3opt.port)), metricsAddress, metricsIntervalSec)

	router := mux.NewRouter().SkipClean(true)

	_, s3ApiServer_err := s3api.NewS3ApiServer(router, &s3api.S3ApiServerOption{
		Filer:            *s3opt.filer,
		Port:             *s3opt.port,
		FilerGrpcAddress: filerGrpcAddress,
		Config:           *s3opt.config,
		DomainName:       *s3opt.domainName,
		BucketsPath:      filerBucketsPath,
		GrpcDialOption:   grpcDialOption,
	})
	if s3ApiServer_err != nil {
		glog.Fatalf("S3 API Server startup error: %v", s3ApiServer_err)
	}

	httpS := &http.Server{Handler: router}

	listenAddress := fmt.Sprintf(":%d", *s3opt.port)
	s3ApiListener, err := util.NewListener(listenAddress, time.Duration(10)*time.Second)
	if err != nil {
		glog.Fatalf("S3 API Server listener on %s error: %v", listenAddress, err)
	}

	if *s3opt.tlsPrivateKey != "" {
		glog.V(0).Infof("Start Seaweed S3 API Server %s at https port %d", util.Version(), *s3opt.port)
		if err = httpS.ServeTLS(s3ApiListener, *s3opt.tlsCertificate, *s3opt.tlsPrivateKey); err != nil {
			glog.Fatalf("S3 API Server Fail to serve: %v", err)
		}
	} else {
		glog.V(0).Infof("Start Seaweed S3 API Server %s at http port %d", util.Version(), *s3opt.port)
		if err = httpS.Serve(s3ApiListener); err != nil {
			glog.Fatalf("S3 API Server Fail to serve: %v", err)
		}
	}

	return true

}
<commit_msg>s3: adjust config examples<commit_after>package command

import (
	"context"
	"fmt"
	"net/http"
	"time"

	"github.com/chrislusf/seaweedfs/weed/pb"
	"github.com/chrislusf/seaweedfs/weed/pb/filer_pb"
	"github.com/chrislusf/seaweedfs/weed/security"

	"github.com/gorilla/mux"

	"github.com/chrislusf/seaweedfs/weed/glog"
	"github.com/chrislusf/seaweedfs/weed/s3api"
	stats_collect "github.com/chrislusf/seaweedfs/weed/stats"
	"github.com/chrislusf/seaweedfs/weed/util"
)

var (
	s3StandaloneOptions S3Options
)

type S3Options struct {
	filer           *string
	port            *int
	config          *string
	domainName      *string
	tlsPrivateKey   *string
	tlsCertificate  *string
	metricsHttpPort *int
}

func init() {
	cmdS3.Run = runS3 // break init cycle
	s3StandaloneOptions.filer = cmdS3.Flag.String("filer", "localhost:8888", "filer server address")
	s3StandaloneOptions.port = cmdS3.Flag.Int("port", 8333, "s3 server http listen port")
	s3StandaloneOptions.domainName = cmdS3.Flag.String("domainName", "", "suffix of the host name, {bucket}.{domainName}")
	s3StandaloneOptions.config = cmdS3.Flag.String("config", "", "path to the config file")
	s3StandaloneOptions.tlsPrivateKey = cmdS3.Flag.String("key.file", "", "path to the TLS private key file")
	s3StandaloneOptions.tlsCertificate = cmdS3.Flag.String("cert.file", "", "path to the TLS certificate file")
	s3StandaloneOptions.metricsHttpPort = cmdS3.Flag.Int("metricsPort", 0, "Prometheus metrics listen port")
}

var cmdS3 = &Command{
	UsageLine: "s3 [-port=8333] [-filer=<ip:port>] [-config=</path/to/config.json>]",
	Short:     "start a s3 API compatible server that is backed by a filer",
	Long: `start a s3 API compatible server that is backed by a filer.

	By default, you can use any access key and secret key to access the S3 APIs.
	To enable credential based access, create a config.json file similar to this:

{
  "identities": [
    {
      "name": "anonymous",
      "actions": [
        "Read"
      ]
    },
    {
      "name": "some_admin_user",
      "credentials": [
        {
          "accessKey": "some_access_key1",
          "secretKey": "some_secret_key1"
        }
      ],
      "actions": [
        "Admin",
        "Read",
        "List",
        "Tagging",
        "Write"
      ]
    },
    {
      "name": "some_read_only_user",
      "credentials": [
        {
          "accessKey": "some_access_key2",
          "secretKey": "some_secret_key2"
        }
      ],
      "actions": [
        "Read"
      ]
    },
    {
      "name": "some_normal_user",
      "credentials": [
        {
          "accessKey": "some_access_key3",
          "secretKey": "some_secret_key3"
        }
      ],
      "actions": [
        "Read",
        "List",
        "Tagging",
        "Write"
      ]
    },
    {
      "name": "user_limited_to_bucket1",
      "credentials": [
        {
          "accessKey": "some_access_key4",
          "secretKey": "some_secret_key4"
        }
      ],
      "actions": [
        "Read:bucket1",
        "List:bucket1",
        "Tagging:bucket1",
        "Write:bucket1"
      ]
    }
  ]
}

`,
}

func runS3(cmd *Command, args []string) bool {

	util.LoadConfiguration("security", false)

	go stats_collect.StartMetricsServer(*s3StandaloneOptions.metricsHttpPort)

	return s3StandaloneOptions.startS3Server()

}

func (s3opt *S3Options) startS3Server() bool {

	filerGrpcAddress, err := pb.ParseFilerGrpcAddress(*s3opt.filer)
	if err != nil {
		glog.Fatal(err)
		return false
	}

	filerBucketsPath := "/buckets"

	grpcDialOption := security.LoadClientTLS(util.GetViper(), "grpc.client")

	// metrics read from the filer
	var metricsAddress string
	var metricsIntervalSec int

	for {
		err = pb.WithGrpcFilerClient(filerGrpcAddress, grpcDialOption, func(client filer_pb.SeaweedFilerClient) error {
			resp, err := client.GetFilerConfiguration(context.Background(), &filer_pb.GetFilerConfigurationRequest{})
			if err != nil {
				return fmt.Errorf("get filer %s configuration: %v", filerGrpcAddress, err)
			}
			filerBucketsPath = resp.DirBuckets
			metricsAddress, metricsIntervalSec = resp.MetricsAddress, int(resp.MetricsIntervalSec)
			glog.V(0).Infof("S3 read filer buckets dir: %s", filerBucketsPath)
			return nil
		})
		if err != nil {
			glog.V(0).Infof("wait to connect to filer %s grpc address %s", *s3opt.filer, filerGrpcAddress)
			time.Sleep(time.Second)
		} else {
			glog.V(0).Infof("connected to filer %s grpc address %s", *s3opt.filer, filerGrpcAddress)
			break
		}
	}

	go stats_collect.LoopPushingMetric("s3", stats_collect.SourceName(uint32(*s3opt.port)), metricsAddress, metricsIntervalSec)

	router := mux.NewRouter().SkipClean(true)

	_, s3ApiServer_err := s3api.NewS3ApiServer(router, &s3api.S3ApiServerOption{
		Filer:            *s3opt.filer,
		Port:             *s3opt.port,
		FilerGrpcAddress: filerGrpcAddress,
		Config:           *s3opt.config,
		DomainName:       *s3opt.domainName,
		BucketsPath:      filerBucketsPath,
		GrpcDialOption:   grpcDialOption,
	})
	if s3ApiServer_err != nil {
		glog.Fatalf("S3 API Server startup error: %v", s3ApiServer_err)
	}

	httpS := &http.Server{Handler: router}

	listenAddress := fmt.Sprintf(":%d", *s3opt.port)
	s3ApiListener, err := util.NewListener(listenAddress, time.Duration(10)*time.Second)
	if err != nil {
		glog.Fatalf("S3 API Server listener on %s error: %v", listenAddress, err)
	}

	if *s3opt.tlsPrivateKey != "" {
		glog.V(0).Infof("Start Seaweed S3 API Server %s at https port %d", util.Version(), *s3opt.port)
		if err = httpS.ServeTLS(s3ApiListener, *s3opt.tlsCertificate, *s3opt.tlsPrivateKey); err != nil {
			glog.Fatalf("S3 API Server Fail to serve: %v", err)
		}
	} else {
		glog.V(0).Infof("Start Seaweed S3 API Server %s at http port %d", util.Version(), *s3opt.port)
		if err = httpS.Serve(s3ApiListener); err != nil {
			glog.Fatalf("S3 API Server Fail to serve: %v", err)
		}
	}

	return true

}
<|endoftext|>


<commit_before>// Package fspath contains routines for fspath manipulation
package fspath

import (
	"errors"
	"path"
	"path/filepath"
	"regexp"
	"strings"

	"github.com/rclone/rclone/fs/driveletter"
)

const (
	configNameRe = `[\w_ -]+`
	remoteNameRe = `^(:?` + configNameRe + `):`
)

var (
	errInvalidCharacters = errors.New("config name contains invalid characters - may only contain 0-9, A-Z ,a-z ,_ , - and space ")
	errCantBeEmpty       = errors.New("can't use empty string as a path")
	errCantStartWithDash = errors.New("config name starts with -")

	// urlMatcher is a pattern to match an rclone URL
	// note that this matches invalid remoteNames
	urlMatcher = regexp.MustCompile(`^(:?[^\\/:]*):(.*)$`)

	// configNameMatcher is a pattern to match an rclone config name
	configNameMatcher = regexp.MustCompile(`^` + configNameRe + `$`)

	// remoteNameMatcher is a pattern to match an rclone remote name
	remoteNameMatcher = regexp.MustCompile(remoteNameRe + `$`)
)

// CheckConfigName returns an error if configName is invalid
func CheckConfigName(configName string) error {
	if !configNameMatcher.MatchString(configName) {
		return errInvalidCharacters
	}
	// Reject configName, if it starts with -, complicates usage. (#4261)
	if strings.HasPrefix(configName, "-") {
		return errCantStartWithDash
	}
	return nil
}

// CheckRemoteName returns an error if remoteName is invalid
func CheckRemoteName(remoteName string) error {
	if !remoteNameMatcher.MatchString(remoteName) {
		return errInvalidCharacters
	}
	return nil
}

// Parse deconstructs a remote path into configName and fsPath
//
// If the path is a local path then configName will be returned as "".
//
// So "remote:path/to/dir" will return "remote", "path/to/dir"
// and "/path/to/local" will return ("", "/path/to/local")
//
// Note that this will turn \ into / in the fsPath on Windows
//
// An error may be returned if the remote name has invalid characters
// in it or if the path is empty.
func Parse(path string) (configName, fsPath string, err error) {
	if path == "" {
		return "", "", errCantBeEmpty
	}
	parts := urlMatcher.FindStringSubmatch(path)
	configName, fsPath = "", path
	if parts != nil && !driveletter.IsDriveLetter(parts[1]) {
		configName, fsPath = parts[1], parts[2]
		err = CheckRemoteName(configName + ":")
		if err != nil {
			return configName, fsPath, errInvalidCharacters
		}
	}
	// change native directory separators to / if there are any
	fsPath = filepath.ToSlash(fsPath)
	return configName, fsPath, nil
}

// Split splits a remote into a parent and a leaf
//
// if it returns leaf as an empty string then remote is a directory
//
// if it returns parent as an empty string then that means the current directory
//
// The returned values have the property that parent + leaf == remote
// (except under Windows where \ will be translated into /)
func Split(remote string) (parent string, leaf string, err error) {
	remoteName, remotePath, err := Parse(remote)
	if err != nil {
		return "", "", err
	}
	if remoteName != "" {
		remoteName += ":"
	}
	// Construct new remote name without last segment
	parent, leaf = path.Split(remotePath)
	return remoteName + parent, leaf, nil
}

// JoinRootPath joins any number of path elements into a single path, adding a
// separating slash if necessary. The result is Cleaned; in particular,
// all empty strings are ignored.
// If the first non empty element has a leading "//" this is preserved.
func JoinRootPath(elem ...string) string {
	for i, e := range elem {
		if e != "" {
			if strings.HasPrefix(e, "//") {
				return "/" + path.Clean(strings.Join(elem[i:], "/"))
			}
			return path.Clean(strings.Join(elem[i:], "/"))
		}
	}
	return ""
}
<commit_msg>fs: fix formatting of errInvalidCharacters error message<commit_after>// Package fspath contains routines for fspath manipulation
package fspath

import (
	"errors"
	"path"
	"path/filepath"
	"regexp"
	"strings"

	"github.com/rclone/rclone/fs/driveletter"
)

const (
	configNameRe = `[\w_ -]+`
	remoteNameRe = `^(:?` + configNameRe + `):`
)

var (
	errInvalidCharacters = errors.New("config name contains invalid characters - may only contain 0-9, A-Z ,a-z ,_ , - and space")
	errCantBeEmpty       = errors.New("can't use empty string as a path")
	errCantStartWithDash = errors.New("config name starts with -")

	// urlMatcher is a pattern to match an rclone URL
	// note that this matches invalid remoteNames
	urlMatcher = regexp.MustCompile(`^(:?[^\\/:]*):(.*)$`)

	// configNameMatcher is a pattern to match an rclone config name
	configNameMatcher = regexp.MustCompile(`^` + configNameRe + `$`)

	// remoteNameMatcher is a pattern to match an rclone remote name
	remoteNameMatcher = regexp.MustCompile(remoteNameRe + `$`)
)

// CheckConfigName returns an error if configName is invalid
func CheckConfigName(configName string) error {
	if !configNameMatcher.MatchString(configName) {
		return errInvalidCharacters
	}
	// Reject configName, if it starts with -, complicates usage. (#4261)
	if strings.HasPrefix(configName, "-") {
		return errCantStartWithDash
	}
	return nil
}

// CheckRemoteName returns an error if remoteName is invalid
func CheckRemoteName(remoteName string) error {
	if !remoteNameMatcher.MatchString(remoteName) {
		return errInvalidCharacters
	}
	return nil
}

// Parse deconstructs a remote path into configName and fsPath
//
// If the path is a local path then configName will be returned as "".
//
// So "remote:path/to/dir" will return "remote", "path/to/dir"
// and "/path/to/local" will return ("", "/path/to/local")
//
// Note that this will turn \ into / in the fsPath on Windows
//
// An error may be returned if the remote name has invalid characters
// in it or if the path is empty.
func Parse(path string) (configName, fsPath string, err error) {
	if path == "" {
		return "", "", errCantBeEmpty
	}
	parts := urlMatcher.FindStringSubmatch(path)
	configName, fsPath = "", path
	if parts != nil && !driveletter.IsDriveLetter(parts[1]) {
		configName, fsPath = parts[1], parts[2]
		err = CheckRemoteName(configName + ":")
		if err != nil {
			return configName, fsPath, errInvalidCharacters
		}
	}
	// change native directory separators to / if there are any
	fsPath = filepath.ToSlash(fsPath)
	return configName, fsPath, nil
}

// Split splits a remote into a parent and a leaf
//
// if it returns leaf as an empty string then remote is a directory
//
// if it returns parent as an empty string then that means the current directory
//
// The returned values have the property that parent + leaf == remote
// (except under Windows where \ will be translated into /)
func Split(remote string) (parent string, leaf string, err error) {
	remoteName, remotePath, err := Parse(remote)
	if err != nil {
		return "", "", err
	}
	if remoteName != "" {
		remoteName += ":"
	}
	// Construct new remote name without last segment
	parent, leaf = path.Split(remotePath)
	return remoteName + parent, leaf, nil
}

// JoinRootPath joins any number of path elements into a single path, adding a
// separating slash if necessary. The result is Cleaned; in particular,
// all empty strings are ignored.
// If the first non empty element has a leading "//" this is preserved.
func JoinRootPath(elem ...string) string {
	for i, e := range elem {
		if e != "" {
			if strings.HasPrefix(e, "//") {
				return "/" + path.Clean(strings.Join(elem[i:], "/"))
			}
			return path.Clean(strings.Join(elem[i:], "/"))
		}
	}
	return ""
}
<|endoftext|>


<commit_before>//
// Arpapp
//

package main

import (
	"flag"
	"fmt"
	"log"
	"net/http"
	"os"
	"os/exec"
	"regexp"
	"sort"
	"strconv"
	"time"
)

type ArpEntry struct {
	online bool
	stamp  time.Time
}

const (
	ARPREGEX  = ".*? \\((.*?)\\) "
	LOGSIZE   = 16
	HTMLSTART = `<title>arpapp</title>

arpapp

`
	HTMLEND = `</pre></body></html>`
)

var (
	host     string
	port     int
	interval time.Duration
	arplog   map[string]ArpEntry
	arpregex *regexp.Regexp
)

func die(msg string, code int) {
	log.Fatalln(msg)
	os.Exit(code)
}

func setup() {
	var err error
	var intervalstr string

	flag.Usage = func() {
		fmt.Fprintf(os.Stderr, "Usage: %s [options] PORT\n", os.Args[0])
		flag.PrintDefaults()
	}

	flag.StringVar(&host, "h", "127.0.0.1", "HTTP server bind HOST")
	flag.StringVar(&intervalstr, "i", "10m", "Scan INTERVAL")
	flag.Parse()

	if len(flag.Args()) < 1 {
		die("You have to specify a port!", 2)
	}

	interval, err = time.ParseDuration(intervalstr)
	if err != nil {
		die("Couldn't parse interval!", 2)
	}

	port, err = strconv.Atoi(flag.Args()[0])
	if err != nil || port <= 0 {
		die("Port has to be a positive integer!", 2)
	}

	arpregex = regexp.MustCompile(ARPREGEX)
	arplog = make(map[string]ArpEntry, LOGSIZE)
}

func scan() {
	data, err := exec.Command("arp", "-a").Output()
	if err != nil {
		log.Println("ERROR: running 'arp -a'")
		return
	}

	matches := arpregex.FindAllStringSubmatch(string(data), -1)
	seen := make(map[string]bool, len(matches))
	for i := 0; i < len(matches); i++ {
		ip := matches[i][1]
		if exec.Command("ping", "-c1", "-W1", ip).Run() == nil {
			seen[ip] = true
		} else {
			continue
		}

		if _, present := arplog[ip]; present {
			if !arplog[ip].online {
				arplog[ip] = ArpEntry{online: true, stamp: time.Now()}
			}
		} else {
			arplog[ip] = ArpEntry{online: true, stamp: time.Now()}
		}
	}
	for ip, _ := range arplog {
		if _, present := seen[ip]; !present {
			arplog[ip] = ArpEntry{online: false, stamp: time.Now()}
		}
	}
}

func render(out http.ResponseWriter, req *http.Request) {
	keys := make([]string, len(arplog))
	i := 0
	for ip, _ := range arplog {
		keys[i] = ip
		i++
	}
	sort.Strings(keys)

	fmt.Fprintf(out, HTMLSTART)
	for _, ip := range keys {
		entry := arplog[ip]
		duration := time.Since(entry.stamp)

		fmt.Fprintf(out, "%-15s since %s (%s)\n", ip, entry.stamp.Format("2006-01-02 15:04:10 GMT"), duration.String())
	}
	fmt.Fprintf(out, "\ninterval: %s", interval)
	fmt.Fprintf(out, HTMLEND)
}

func main() {
	setup()

	http.HandleFunc("/", render)
	go http.ListenAndServe(fmt.Sprintf("%s:%d", host, port), nil)

	for {
		scan()
		time.Sleep(interval)
	}
}
<commit_msg>Fixed time-stamp printing<commit_after>//
// Arpapp
//

package main

import (
	"flag"
	"fmt"
	"log"
	"net/http"
	"os"
	"os/exec"
	"regexp"
	"sort"
	"strconv"
	"time"
)

type ArpEntry struct {
	online bool
	stamp  time.Time
}

const (
	ARPREGEX  = ".*? \\((.*?)\\) "
	LOGSIZE   = 16
	HTMLSTART = `<title>arpapp</title>

arpapp

`
	HTMLEND = `</pre></body></html>`
)

var (
	host     string
	port     int
	interval time.Duration
	arplog   map[string]ArpEntry
	arpregex *regexp.Regexp
)

func die(msg string, code int) {
	log.Fatalln(msg)
	os.Exit(code)
}

func setup() {
	var err error
	var intervalstr string

	flag.Usage = func() {
		fmt.Fprintf(os.Stderr, "Usage: %s [options] PORT\n", os.Args[0])
		flag.PrintDefaults()
	}

	flag.StringVar(&host, "h", "127.0.0.1", "HTTP server bind HOST")
	flag.StringVar(&intervalstr, "i", "10m", "Scan INTERVAL")
	flag.Parse()

	if len(flag.Args()) < 1 {
		die("You have to specify a port!", 2)
	}

	interval, err = time.ParseDuration(intervalstr)
	if err != nil {
		die("Couldn't parse interval!", 2)
	}

	port, err = strconv.Atoi(flag.Args()[0])
	if err != nil || port <= 0 {
		die("Port has to be a positive integer!", 2)
	}

	arpregex = regexp.MustCompile(ARPREGEX)
	arplog = make(map[string]ArpEntry, LOGSIZE)
}

func scan() {
	data, err := exec.Command("arp", "-a").Output()
	if err != nil {
		log.Println("ERROR: running 'arp -a'")
		return
	}

	matches := arpregex.FindAllStringSubmatch(string(data), -1)
	seen := make(map[string]bool, len(matches))
	for i := 0; i < len(matches); i++ {
		ip := matches[i][1]
		if exec.Command("ping", "-c1", "-W1", ip).Run() == nil {
			seen[ip] = true
		} else {
			continue
		}

		if _, present := arplog[ip]; present {
			if !arplog[ip].online {
				arplog[ip] = ArpEntry{online: true, stamp: time.Now()}
			}
		} else {
			arplog[ip] = ArpEntry{online: true, stamp: time.Now()}
		}
	}
	for ip, _ := range arplog {
		if _, present := seen[ip]; !present {
			arplog[ip] = ArpEntry{online: false, stamp: time.Now()}
		}
	}
}

func render(out http.ResponseWriter, req *http.Request) {
	keys := make([]string, len(arplog))
	i := 0
	for ip, _ := range arplog {
		keys[i] = ip
		i++
	}
	sort.Strings(keys)

	fmt.Fprintf(out, HTMLSTART)
	for _, ip := range keys {
		entry := arplog[ip]
		duration := time.Since(entry.stamp)

		fmt.Fprintf(out, "%-15s since %s (%s)\n", ip, entry.stamp.Format("2006-01-02 15:04:05 MST"), duration.String())
	}
	fmt.Fprintf(out, "\ninterval: %s", interval)
	fmt.Fprintf(out, HTMLEND)
}

func main() {
	setup()

	http.HandleFunc("/", render)
	go http.ListenAndServe(fmt.Sprintf("%s:%d", host, port), nil)

	for {
		scan()
		time.Sleep(interval)
	}
}
<|endoftext|>