<commit_before>// Copyright (c) 2010 Go Fightclub Authors

package alsa

import (
  "fmt"
  "unsafe"
  "afp"
  "os"
)

// #include <alsa/asoundlib.h>
import "C"

/////
// Alsa Source
// Listens to a microphone
type AlsaSource struct {
    ctx *afp.Context
    header afp.StreamHeader
    capture *C.snd_pcm_t
    params *C.snd_pcm_hw_params_t
}

func NewAlsaSource() afp.Filter {
    return &AlsaSource{}
}

func (self *AlsaSource) GetType() int {
    return afp.PIPE_SOURCE
}

func (self *AlsaSource) Init(ctx *afp.Context, args []string) os.Error {
    self.ctx = ctx
    return nil
}

func (self *AlsaSource) Start() {

    self.header = afp.StreamHeader {
        Version: 1,
        Channels: 1,
        SampleSize: 32,
        SampleRate: 44100,
        FrameSize: 4096,
    }

    self.ctx.HeaderSink <- self.header

    retval := self.prepare()
    if ( retval != nil) {
        panic(retval)
    }

    for {
		cbuf := make([]float32, int32(self.header.Channels) * self.header.FrameSize)
		buff := make([][]float32, self.header.FrameSize)
        length := len(cbuf)

        //first off, grab some data from alsa
        read := C.snd_pcm_readi(self.capture, unsafe.Pointer(&cbuf[0]), C.snd_pcm_uframes_t(length))
        if read < C.snd_pcm_sframes_t(length) {
            errno := C.snd_pcm_recover(self.capture, C.int(read), 0)
            if errno < 0 {
                panic(fmt.Sprint( "While reading from ALSA device, failed to recover from error: ", errno))
            }
        }

        // snd_pcm_readi gives us a one dimensional array of interleaved data
        // but what we want is a two dimensional array of samples
        chans := int(self.header.Channels)
		for slice, i := 0, 0; i < length; slice, i = slice + 1, i + chans {
			buff[slice] = make([]float32, chans)
			buff[slice] = cbuf[i : i + chans]
        }

        //send it on down the line
        self.ctx.Sink <- buff
    }

}

func (self *AlsaSource) Stop() os.Error {
    C.snd_pcm_close(self.capture)
    close(self.ctx.Sink)
    return nil
}

/////
// Alsa Sink
// Outputs to speakers via ALSA
type AlsaSink struct {
    ctx *afp.Context
    header afp.StreamHeader
    playback *C.snd_pcm_t
    params *C.snd_pcm_hw_params_t

}

func NewAlsaSink() afp.Filter {
    return &AlsaSink{}
}

func (self *AlsaSink) GetType() int {
    return afp.PIPE_SINK
}

func (self *AlsaSink) Init(ctx *afp.Context, args []string) os.Error {
    self.ctx = ctx
    return nil
}

func (self *AlsaSink) Start() {
    self.header = <-self.ctx.HeaderSource

    retval := self.prepare()
    if (retval != nil) {
        panic(retval)
    }

    for buffer := range self.ctx.Source { //reading a [][]float32
        cbuf := make([]float32, int32(self.header.Channels) * self.header.FrameSize)
        length := len(buffer)
        chans := int(self.header.Channels)

        //interleave the channels
        for i := 0; i < length; i += chans {
            for j := 0; j < chans; j++ {
                cbuf[i+j] = buffer[i / chans][j]
            }
        }

        //write some data to alsa
        written := C.snd_pcm_writei(self.playback, unsafe.Pointer(&cbuf[0]), C.snd_pcm_uframes_t(length))

        if int(written) < length {
            //not all the data was written
            panic(fmt.Sprintf("Could not write all data to ALSA device, wrote: ", written))
        }
    }

    return
}

func (self *AlsaSink) Stop() os.Error {
    C.snd_pcm_close(self.playback)
    return nil
}

// Ugly bastardized C code follows
func (self *AlsaSink) prepare() os.Error {

    if errno := C.snd_pcm_open(&self.playback, C.CString("default"), C.SND_PCM_STREAM_PLAYBACK, 0); errno < 0 {
        return os.NewError( fmt.Sprintf("Could not open device. Error %d", errno) )
    }

    if errno := C.snd_pcm_hw_params_malloc(&self.params); errno < 0 {
        return os.NewError( fmt.Sprintf("Could not allocate hardware parameter structure. Error %d", errno) )
    }

    if errno := C.snd_pcm_hw_params_any(self.playback, self.params); errno < 0 {
        return os.NewError( fmt.Sprintf("Could not initialize hardware parameter structure. Error %d", errno) )
    }

    if errno := C.snd_pcm_hw_params_set_access(self.playback, self.params, C.SND_PCM_ACCESS_RW_INTERLEAVED); errno < 0 {
        return os.NewError( fmt.Sprintf("Could not set access type. Error %d", errno) )
    }

    if errno := C.snd_pcm_hw_params_set_format(self.playback, self.params, C.SND_PCM_FORMAT_FLOAT); errno < 0 {
        return os.NewError( fmt.Sprintf("Could not set sample format. Error %d", errno) )
    }

    if errno := C.snd_pcm_hw_params_set_rate(self.playback, self.params, C.uint(self.header.SampleRate), 0); errno < 0 {
        return os.NewError( fmt.Sprintf("Could not set sample rate. Error %d", errno) )
    }

    if errno := C.snd_pcm_hw_params_set_channels(self.playback, self.params, C.uint(self.header.Channels)); errno < 0 {
        return os.NewError( fmt.Sprintf("Could not set channel count. Error %d", errno) )
    }

    if errno := C.snd_pcm_hw_params(self.playback, self.params); errno < 0 {
        return os.NewError( fmt.Sprintf("Could not set hardware parameters. Error %d", errno) )
    }

    C.snd_pcm_hw_params_free(self.params)

    if errno := C.snd_pcm_prepare(self.playback); errno < 0 {
        return os.NewError( fmt.Sprintf("Could not prepare audio device for use. Error %d", errno) )
    }

    return nil
}

//this one is slightly different
//note the change in scope
func (self *AlsaSource) prepare() os.Error {

    if errno := C.snd_pcm_open(&self.capture, C.CString("default"), C.SND_PCM_STREAM_CAPTURE, 0); errno < 0 {
        return os.NewError( fmt.Sprintf("Could not open device. Error %d", errno) )
    }

    if errno := C.snd_pcm_hw_params_malloc(&self.params); errno < 0 {
        return os.NewError( fmt.Sprintf("Could not allocate hardware parameters. Error %d", errno) )
    }

    if errno := C.snd_pcm_hw_params_any(self.capture, self.params); errno < 0 {
        return os.NewError( fmt.Sprintf("Could not initialize hardware parameter structure. Error %d", errno) )
    }

    if errno := C.snd_pcm_hw_params_set_access(self.capture, self.params, C.SND_PCM_ACCESS_RW_INTERLEAVED); errno < 0 {
        return os.NewError( fmt.Sprintf("Could not set access. Error %d", errno) )
    }

    if errno := C.snd_pcm_hw_params_set_format(self.capture, self.params, C.SND_PCM_FORMAT_FLOAT); errno < 0 {
        return os.NewError( fmt.Sprintf("Could not set sample format. Error %d", errno) )
    }

    if errno := C.snd_pcm_hw_params_set_rate(self.capture, self.params, C.uint(self.header.SampleRate), 0); errno < 0 {
        return os.NewError( fmt.Sprintf("Could not set sample rate. Error %d", errno) )
    }

    if errno := C.snd_pcm_hw_params_set_channels(self.capture, self.params, 1); errno < 0 {
        return os.NewError( fmt.Sprintf("Could not set channel count. Error %d", errno) )
    }

    if errno := C.snd_pcm_hw_params(self.capture, self.params); errno < 0 {
        return os.NewError( fmt.Sprintf("Could not set parameters. Error %d", errno) )
    }

    C.snd_pcm_hw_params_free(self.params)

    if errno := C.snd_pcm_prepare(self.capture); errno < 0 {
        return os.NewError( fmt.Sprintf("Could not prepare audio interface for use. Error %d", errno) )
    }

    return nil
}
<commit_msg>fix interleaving code<commit_after>// Copyright (c) 2010 Go Fightclub Authors

package alsa

import (
  "fmt"
  "unsafe"
  "afp"
  "os"
)

// #include <alsa/asoundlib.h>
import "C"

/////
// Alsa Source
// Listens to a microphone
type AlsaSource struct {
    ctx *afp.Context
    header afp.StreamHeader
    capture *C.snd_pcm_t
    params *C.snd_pcm_hw_params_t
}

func NewAlsaSource() afp.Filter {
    return &AlsaSource{}
}

func (self *AlsaSource) GetType() int {
    return afp.PIPE_SOURCE
}

func (self *AlsaSource) Init(ctx *afp.Context, args []string) os.Error {
    self.ctx = ctx
    return nil
}

func (self *AlsaSource) Start() {

    self.header = afp.StreamHeader {
        Version: 1,
        Channels: 1,
        SampleSize: 32,
        SampleRate: 44100,
        FrameSize: 4096,
    }

    self.ctx.HeaderSink <- self.header

    retval := self.prepare()
    if ( retval != nil) {
        panic(retval)
    }

    for {
		cbuf := make([]float32, int32(self.header.Channels) * self.header.FrameSize)
		buff := make([][]float32, self.header.FrameSize)
        length := len(cbuf)

        //first off, grab some data from alsa
        read := C.snd_pcm_readi(self.capture, unsafe.Pointer(&cbuf[0]), C.snd_pcm_uframes_t(length))
        if read < C.snd_pcm_sframes_t(length) {
            errno := C.snd_pcm_recover(self.capture, C.int(read), 0)
            if errno < 0 {
                panic(fmt.Sprint( "While reading from ALSA device, failed to recover from error: ", errno))
            }
        }

        // snd_pcm_readi gives us a one dimensional array of interleaved data
        // but what we want is a two dimensional array of samples
        chans := int(self.header.Channels)
		for slice, i := 0, 0; i < length; slice, i = slice + 1, i + chans {
			buff[slice] = make([]float32, chans)
			buff[slice] = cbuf[i : i + chans]
        }

        //send it on down the line
        self.ctx.Sink <- buff
    }

}

func (self *AlsaSource) Stop() os.Error {
    C.snd_pcm_close(self.capture)
    close(self.ctx.Sink)
    return nil
}

/////
// Alsa Sink
// Outputs to speakers via ALSA
type AlsaSink struct {
    ctx *afp.Context
    header afp.StreamHeader
    playback *C.snd_pcm_t
    params *C.snd_pcm_hw_params_t

}

func NewAlsaSink() afp.Filter {
    return &AlsaSink{}
}

func (self *AlsaSink) GetType() int {
    return afp.PIPE_SINK
}

func (self *AlsaSink) Init(ctx *afp.Context, args []string) os.Error {
    self.ctx = ctx
    return nil
}

func (self *AlsaSink) Start() {
    self.header = <-self.ctx.HeaderSource

    retval := self.prepare()
    if (retval != nil) {
        panic(retval)
    }
    cbuf := make([]float32, int32(self.header.Channels) * self.header.FrameSize)

    for buffer := range self.ctx.Source { //reading a [][]float32
        length := int(self.header.FrameSize)
        chans := int(self.header.Channels)

	streamOffset := 0
        //interleave the channels
        for i := 0; i < length; i ++ {
            for j := 0; j < chans; j++ {
                cbuf[streamOffset] = buffer[i][j]
		streamOffset++
            }
        }

        //write some data to alsa
        written := C.snd_pcm_writei(self.playback, unsafe.Pointer(&cbuf[0]), C.snd_pcm_uframes_t(length))

        if int(written) < length {
            //not all the data was written
            panic(fmt.Sprintf("Could not write all data to ALSA device, wrote: ", written))
        }
    }

    return
}

func (self *AlsaSink) Stop() os.Error {
    C.snd_pcm_close(self.playback)
    return nil
}

// Ugly bastardized C code follows
func (self *AlsaSink) prepare() os.Error {

    if errno := C.snd_pcm_open(&self.playback, C.CString("default"), C.SND_PCM_STREAM_PLAYBACK, 0); errno < 0 {
        return os.NewError( fmt.Sprintf("Could not open device. Error %d", errno) )
    }

    if errno := C.snd_pcm_hw_params_malloc(&self.params); errno < 0 {
        return os.NewError( fmt.Sprintf("Could not allocate hardware parameter structure. Error %d", errno) )
    }

    if errno := C.snd_pcm_hw_params_any(self.playback, self.params); errno < 0 {
        return os.NewError( fmt.Sprintf("Could not initialize hardware parameter structure. Error %d", errno) )
    }

    if errno := C.snd_pcm_hw_params_set_access(self.playback, self.params, C.SND_PCM_ACCESS_RW_INTERLEAVED); errno < 0 {
        return os.NewError( fmt.Sprintf("Could not set access type. Error %d", errno) )
    }

    if errno := C.snd_pcm_hw_params_set_format(self.playback, self.params, C.SND_PCM_FORMAT_FLOAT); errno < 0 {
        return os.NewError( fmt.Sprintf("Could not set sample format. Error %d", errno) )
    }

    if errno := C.snd_pcm_hw_params_set_rate(self.playback, self.params, C.uint(self.header.SampleRate), 0); errno < 0 {
        return os.NewError( fmt.Sprintf("Could not set sample rate. Error %d", errno) )
    }

    if errno := C.snd_pcm_hw_params_set_channels(self.playback, self.params, C.uint(self.header.Channels)); errno < 0 {
        return os.NewError( fmt.Sprintf("Could not set channel count. Error %d", errno) )
    }

    if errno := C.snd_pcm_hw_params(self.playback, self.params); errno < 0 {
        return os.NewError( fmt.Sprintf("Could not set hardware parameters. Error %d", errno) )
    }

    C.snd_pcm_hw_params_free(self.params)

    if errno := C.snd_pcm_prepare(self.playback); errno < 0 {
        return os.NewError( fmt.Sprintf("Could not prepare audio device for use. Error %d", errno) )
    }

    return nil
}

//this one is slightly different
//note the change in scope
func (self *AlsaSource) prepare() os.Error {

    if errno := C.snd_pcm_open(&self.capture, C.CString("default"), C.SND_PCM_STREAM_CAPTURE, 0); errno < 0 {
        return os.NewError( fmt.Sprintf("Could not open device. Error %d", errno) )
    }

    if errno := C.snd_pcm_hw_params_malloc(&self.params); errno < 0 {
        return os.NewError( fmt.Sprintf("Could not allocate hardware parameters. Error %d", errno) )
    }

    if errno := C.snd_pcm_hw_params_any(self.capture, self.params); errno < 0 {
        return os.NewError( fmt.Sprintf("Could not initialize hardware parameter structure. Error %d", errno) )
    }

    if errno := C.snd_pcm_hw_params_set_access(self.capture, self.params, C.SND_PCM_ACCESS_RW_INTERLEAVED); errno < 0 {
        return os.NewError( fmt.Sprintf("Could not set access. Error %d", errno) )
    }

    if errno := C.snd_pcm_hw_params_set_format(self.capture, self.params, C.SND_PCM_FORMAT_FLOAT); errno < 0 {
        return os.NewError( fmt.Sprintf("Could not set sample format. Error %d", errno) )
    }

    if errno := C.snd_pcm_hw_params_set_rate(self.capture, self.params, C.uint(self.header.SampleRate), 0); errno < 0 {
        return os.NewError( fmt.Sprintf("Could not set sample rate. Error %d", errno) )
    }

    if errno := C.snd_pcm_hw_params_set_channels(self.capture, self.params, 1); errno < 0 {
        return os.NewError( fmt.Sprintf("Could not set channel count. Error %d", errno) )
    }

    if errno := C.snd_pcm_hw_params(self.capture, self.params); errno < 0 {
        return os.NewError( fmt.Sprintf("Could not set parameters. Error %d", errno) )
    }

    C.snd_pcm_hw_params_free(self.params)

    if errno := C.snd_pcm_prepare(self.capture); errno < 0 {
        return os.NewError( fmt.Sprintf("Could not prepare audio interface for use. Error %d", errno) )
    }

    return nil
}
<|endoftext|>


<commit_before><commit_msg>github: the githubarchive.org hour is {0..23} not {00..23} :/<commit_after><|endoftext|>


<commit_before>package sdl

import (
	"fmt"
	"os"
	"sync"

	"github.com/forestgiant/eff"
)

const (
	defaultWindowTitle = "Effulgent"
	defaultWidth       = 480
	defaultHeight      = 320
	defaultFrameRate   = 60
)

var startTime uint32
var delta uint32
var currentFPS uint32

// KeyEnumHandler function that is called when a key board event occurs
type KeyEnumHandler func(key Keycode)

// Canvas creates window and renderer and calls all drawable methods
type Canvas struct {
	eff.Shape

	window              *Window
	windowTitle         string
	clickables          []eff.Clickable
	fullscreen          bool
	keyUpHandlers       []eff.KeyHandler
	keyDownHandlers     []eff.KeyHandler
	keyDownEnumHandlers []KeyEnumHandler
	keyUpEnumHandlers   []KeyEnumHandler
	mouseDownHandlers   []eff.MouseButtonHandler
	mouseUpHandlers     []eff.MouseButtonHandler
	mouseWheelHandlers  []eff.MouseWheelHandler
	mouseMoveHandlers   []eff.MouseMoveHandler
	frameRate           int
	useVsync            bool
	sdlGraphics         *Graphics
	fonts               []*Font
}

// NewCanvas creates a new SDL canvas instance
func NewCanvas(title string, width int, height int, clearColor eff.Color, frameRate int, useVsync bool) *Canvas {
	c := Canvas{}
	c.windowTitle = title
	c.SetRect(eff.Rect{
		X: 0,
		Y: 0,
		W: width,
		H: height,
	})
	c.SetBackgroundColor(clearColor)
	c.frameRate = frameRate
	c.useVsync = useVsync
	return &c
}

// AddClickable adds a struct that implements the eff.Clickable interface
func (c *Canvas) AddClickable(clickable eff.Clickable) {
	c.clickables = append(c.clickables, clickable)
}

// RemoveClickable removes a struct that implements the eff.Clickable interface
func (c *Canvas) RemoveClickable(clickable eff.Clickable) {
	index := -1
	for i, d := range c.clickables {
		if d == clickable {
			index = i
			break
		}
	}
	if index == -1 {
		return
	}

	c.clickables = append(c.clickables[:index], c.clickables[index+1:]...)
}

// AddKeyUpHandler adds key up event handler to the canvas
func (c *Canvas) AddKeyUpHandler(handler eff.KeyHandler) {
	c.keyUpHandlers = append(c.keyUpHandlers, handler)
}

// AddKeyDownHandler adds key down event handler to the canvas
func (c *Canvas) AddKeyDownHandler(handler eff.KeyHandler) {
	c.keyDownHandlers = append(c.keyDownHandlers, handler)
}

// AddKeyUpEnumHandler adds key up event handler to the canvas
func (c *Canvas) AddKeyUpEnumHandler(handler KeyEnumHandler) {
	c.keyUpEnumHandlers = append(c.keyUpEnumHandlers, handler)
}

// AddKeyDownEnumHandler adds key down event handler to the canvas
func (c *Canvas) AddKeyDownEnumHandler(handler KeyEnumHandler) {
	c.keyDownEnumHandlers = append(c.keyDownEnumHandlers, handler)
}

// AddMouseDownHandler adds mouse down event handler to canvas
func (c *Canvas) AddMouseDownHandler(handler eff.MouseButtonHandler) {
	c.mouseDownHandlers = append(c.mouseDownHandlers, handler)
}

// AddMouseUpHandler adds mouse up event handler to canvas
func (c *Canvas) AddMouseUpHandler(handler eff.MouseButtonHandler) {
	c.mouseUpHandlers = append(c.mouseUpHandlers, handler)
}

// AddMouseMoveHandler adds mouse move event handler to canvas
func (c *Canvas) AddMouseMoveHandler(handler eff.MouseMoveHandler) {
	c.mouseMoveHandlers = append(c.mouseMoveHandlers, handler)
}

// AddMouseWheelHandler adds mouse wheel event handler to canvas
func (c *Canvas) AddMouseWheelHandler(handler eff.MouseWheelHandler) {
	c.mouseWheelHandlers = append(c.mouseWheelHandlers, handler)
}

// Run creates an infinite loop that renders all drawables, init is only call once and draw and update are called once per frame
func (c *Canvas) Run(setup func()) {
	lastFPSPrintTime := getTicks()
	init := func(wg *sync.WaitGroup) int {

		if c.Rect().W == 0 || c.Rect().H == 0 {
			c.SetRect(eff.Rect{
				X: 0,
				Y: 0,
				W: defaultWidth,
				H: defaultHeight,
			})
		}

		if len(c.windowTitle) == 0 {
			c.windowTitle = defaultWindowTitle
		}

		if c.frameRate == 0 {
			c.frameRate = defaultFrameRate
		}

		var err error
		mainThread <- func() {
			c.window, err = createWindow(
				c.windowTitle,
				windowPosUndefined,
				windowPosUndefined,
				c.Rect().W,
				c.Rect().H,
				windowOpenGl|windowAllowHighDPI|windowMouseCapture,
			)
			drawableW, _ := c.window.getDrawableSize()
			scale := float64(drawableW) / float64(c.Rect().W)

			if err != nil {
				fmt.Fprintf(os.Stderr, "Failed to create window: %s\n", err)
				// return 1
				return
			}

			if c.fullscreen {
				c.window.setFullscreen(windowFullscreen)
			} else {
				c.window.setFullscreen(0)
			}

			renderFlags := rendererAccelerated | rendererPresentVsync
			if !c.useVsync {
				renderFlags = rendererAccelerated
			}

			renderer, err := createRenderer(
				c.window,
				-1,
				uint32(renderFlags),
			)
			if err != nil {
				fmt.Fprintln(os.Stderr, "Failed to create renderer: ", err)
				// return 2
				return
			}

			c.sdlGraphics = NewGraphics(renderer, scale)

			c.sdlGraphics.renderer.clear()

			c.SetGraphics(c.sdlGraphics)

			wg.Done()
		}

		startTime = getTicks()
		return 0
	}

	run := func() {
		running := true

		for running {
			mainThread <- func() {
				for event := pollEvent(); event != nil; event = pollEvent() {
					switch t := event.(type) {
					case *quitEvent:
						running = false
					case *keyUpEvent:
						switch t.Keysym.Sym {
						case KeyQ:
							running = false
						case KeyF:
							c.fullscreen = !c.fullscreen
							if c.fullscreen {
								c.window.setFullscreen(windowFullscreen)
							} else {
								c.window.setFullscreen(0)
							}
						}

						for _, handler := range c.keyUpHandlers {
							go handler(getKeyName(t.Keysym.Sym))
						}
						for _, handler := range c.keyUpEnumHandlers {
							go handler(t.Keysym.Sym)
						}
					case *keyDownEvent:
						for _, handler := range c.keyDownHandlers {
							go handler(getKeyName(t.Keysym.Sym))
						}
						for _, handler := range c.keyDownEnumHandlers {
							go handler(t.Keysym.Sym)
						}
					case *mouseDownEvent:
						leftState := t.Button == mouseLeft
						middleState := t.Button == mouseMiddle
						rightState := t.Button == mouseRight

						mousePoint := eff.Point{
							X: int(t.X),
							Y: int(t.Y),
						}

						for _, handler := range c.mouseDownHandlers {
							go handler(leftState, middleState, rightState)
						}

						for _, clickable := range c.clickables {
							if clickable == nil || !clickable.IsVisible() {
								continue
							}

							hb := clickable.Hitbox()
							if hb.Inside(mousePoint) {
								go clickable.MouseDown(leftState, middleState, rightState)
							}
						}

						err := captureMouse(true)
						if err != nil {
							fmt.Println(err)
						}

					case *mouseUpEvent:
						leftState := t.Button == mouseLeft
						middleState := t.Button == mouseMiddle
						rightState := t.Button == mouseRight

						mousePoint := eff.Point{
							X: int(t.X),
							Y: int(t.Y),
						}

						for _, handler := range c.mouseUpHandlers {
							go handler(leftState, middleState, rightState)
						}

						for _, clickable := range c.clickables {
							if clickable == nil || !clickable.IsVisible() {
								continue
							}

							hb := clickable.Hitbox()
							if hb.Inside(mousePoint) {
								go clickable.MouseUp(leftState, middleState, rightState)
							}
						}

						err := captureMouse(false)
						if err != nil {
							fmt.Println(err)
						}
					case *mouseMotionEvent:
						mousePoint := eff.Point{
							X: int(t.X),
							Y: int(t.Y),
						}

						for _, handler := range c.mouseMoveHandlers {
							go handler(mousePoint.X, mousePoint.Y)
						}

						for _, clickable := range c.clickables {
							if clickable == nil || !clickable.IsVisible() {
								continue
							}

							hb := clickable.Hitbox()
							if hb.Inside(mousePoint) {
								if !clickable.IsMouseOver() {
									go clickable.MouseOver()
								}
							} else {
								if clickable.IsMouseOver() {
									go clickable.MouseOut()
								}
							}
						}

					case *mouseWheelEvent:
						for _, handler := range c.mouseWheelHandlers {
							go handler(int(t.X), int(t.Y))
						}
					}
				}
				bgColor := c.BackgroundColor()
				c.sdlGraphics.renderer.setDrawColor(
					uint8(bgColor.R),
					uint8(bgColor.G),
					uint8(bgColor.B),
					uint8(bgColor.A),
				)

				drawableW, _ := c.window.getDrawableSize()
				scale := float64(drawableW) / float64(c.Rect().W)
				if scale != c.sdlGraphics.scale {
					c.sdlGraphics.scale = scale
					c.SetShouldDraw(true)
					c.SetTextureInvalid(true)
					c.RedrawChildren()
					c.InvalidateChildTextures()
					for _, font := range c.fonts {
						font.refresh(c.sdlGraphics.scale)
					}
				}
				c.sdlGraphics.renderer.setTarget(nil)
				c.sdlGraphics.renderer.clear()
			}

			c.Draw(c)

			c.HandleUpdate()

			mainThread <- func() {

				printFPS := func() {
					delta = getTicks() - startTime
					if delta != 0 {
						currentFPS = 1000 / delta
					}
					if getTicks()-lastFPSPrintTime >= 1000 {
						fmt.Println(currentFPS, "fps")
						lastFPSPrintTime = getTicks()
					}
				}

				enforceFPS := func() {
					timeBetweenFrames := getTicks() - startTime
					targetTimeBetweenFrames := 1000 / uint32(c.frameRate)

					if timeBetweenFrames < targetTimeBetweenFrames {
						delay(targetTimeBetweenFrames - timeBetweenFrames)
					}

				}

				c.window.updateSurface()
				c.sdlGraphics.renderer.present()

				enforceFPS()
				printFPS()

				startTime = getTicks()
			}
		}
	}

	lockMain(func() {
		var wg sync.WaitGroup
		wg.Add(1)
		initOK := init(&wg)
		wg.Wait()
		if initOK != 0 {
			os.Exit(initOK)
		}

		setup()
		run()
		mainThread <- func() {
			// Clean up goes here
			c.sdlGraphics.renderer.destroy()
			c.window.destroy()

			//Quit SDL
			quit()
			close(mainDone) // stop mainThread
		}
	})
}

// Fullscreen get the full screen state of the window
func (c *Canvas) Fullscreen() bool {
	return c.fullscreen
}

// SetFullscreen set the fullscreen state of the window
func (c *Canvas) SetFullscreen(fullscreen bool) {
	c.fullscreen = fullscreen
}

// OpenFont creates a eff.Font object, used for rendering text
func (c *Canvas) OpenFont(path string, size int) (eff.Font, error) {

	f, err := openFont(path, size, c.sdlGraphics.scale)
	if err != nil {
		return nil, err
	}
	c.fonts = append(c.fonts, f)
	return f, nil
}

// OpenImage creates and eff.Image object, used for drawing an image
func (c *Canvas) OpenImage(path string) (eff.Image, error) {

	wg := &sync.WaitGroup{}
	wg.Add(1)

	var t *texture
	var err error
	var w int
	var h int
	mainThread <- func() {
		s, err := loadImg(path)
		if err != nil {
			wg.Done()
			return
		}
		w = int(s.w)
		h = int(s.h)
		t, err = c.sdlGraphics.renderer.createTextureFromSurface(s)
		freeSurface(s)
		wg.Done()
	}
	wg.Wait()
	if err != nil {
		return nil, err
	}

	image := &Image{
		path:    path,
		texture: t,
		w:       w,
		h:       h,
	}

	return image, nil
}
<commit_msg>Updated fullscreen mode to refresh the UI and to use Desktop fullscreen<commit_after>package sdl

import (
	"fmt"
	"os"
	"sync"

	"github.com/forestgiant/eff"
)

const (
	defaultWindowTitle = "Effulgent"
	defaultWidth       = 480
	defaultHeight      = 320
	defaultFrameRate   = 60
)

var startTime uint32
var delta uint32
var currentFPS uint32

// KeyEnumHandler function that is called when a key board event occurs
type KeyEnumHandler func(key Keycode)

// Canvas creates window and renderer and calls all drawable methods
type Canvas struct {
	eff.Shape

	window              *Window
	windowTitle         string
	clickables          []eff.Clickable
	fullscreen          bool
	keyUpHandlers       []eff.KeyHandler
	keyDownHandlers     []eff.KeyHandler
	keyDownEnumHandlers []KeyEnumHandler
	keyUpEnumHandlers   []KeyEnumHandler
	mouseDownHandlers   []eff.MouseButtonHandler
	mouseUpHandlers     []eff.MouseButtonHandler
	mouseWheelHandlers  []eff.MouseWheelHandler
	mouseMoveHandlers   []eff.MouseMoveHandler
	frameRate           int
	useVsync            bool
	sdlGraphics         *Graphics
	fonts               []*Font
}

// NewCanvas creates a new SDL canvas instance
func NewCanvas(title string, width int, height int, clearColor eff.Color, frameRate int, useVsync bool) *Canvas {
	c := Canvas{}
	c.windowTitle = title
	c.SetRect(eff.Rect{
		X: 0,
		Y: 0,
		W: width,
		H: height,
	})
	c.SetBackgroundColor(clearColor)
	c.frameRate = frameRate
	c.useVsync = useVsync
	return &c
}

// AddClickable adds a struct that implements the eff.Clickable interface
func (c *Canvas) AddClickable(clickable eff.Clickable) {
	c.clickables = append(c.clickables, clickable)
}

// RemoveClickable removes a struct that implements the eff.Clickable interface
func (c *Canvas) RemoveClickable(clickable eff.Clickable) {
	index := -1
	for i, d := range c.clickables {
		if d == clickable {
			index = i
			break
		}
	}
	if index == -1 {
		return
	}

	c.clickables = append(c.clickables[:index], c.clickables[index+1:]...)
}

// AddKeyUpHandler adds key up event handler to the canvas
func (c *Canvas) AddKeyUpHandler(handler eff.KeyHandler) {
	c.keyUpHandlers = append(c.keyUpHandlers, handler)
}

// AddKeyDownHandler adds key down event handler to the canvas
func (c *Canvas) AddKeyDownHandler(handler eff.KeyHandler) {
	c.keyDownHandlers = append(c.keyDownHandlers, handler)
}

// AddKeyUpEnumHandler adds key up event handler to the canvas
func (c *Canvas) AddKeyUpEnumHandler(handler KeyEnumHandler) {
	c.keyUpEnumHandlers = append(c.keyUpEnumHandlers, handler)
}

// AddKeyDownEnumHandler adds key down event handler to the canvas
func (c *Canvas) AddKeyDownEnumHandler(handler KeyEnumHandler) {
	c.keyDownEnumHandlers = append(c.keyDownEnumHandlers, handler)
}

// AddMouseDownHandler adds mouse down event handler to canvas
func (c *Canvas) AddMouseDownHandler(handler eff.MouseButtonHandler) {
	c.mouseDownHandlers = append(c.mouseDownHandlers, handler)
}

// AddMouseUpHandler adds mouse up event handler to canvas
func (c *Canvas) AddMouseUpHandler(handler eff.MouseButtonHandler) {
	c.mouseUpHandlers = append(c.mouseUpHandlers, handler)
}

// AddMouseMoveHandler adds mouse move event handler to canvas
func (c *Canvas) AddMouseMoveHandler(handler eff.MouseMoveHandler) {
	c.mouseMoveHandlers = append(c.mouseMoveHandlers, handler)
}

// AddMouseWheelHandler adds mouse wheel event handler to canvas
func (c *Canvas) AddMouseWheelHandler(handler eff.MouseWheelHandler) {
	c.mouseWheelHandlers = append(c.mouseWheelHandlers, handler)
}

// Run creates an infinite loop that renders all drawables, init is only call once and draw and update are called once per frame
func (c *Canvas) Run(setup func()) {
	lastFPSPrintTime := getTicks()
	init := func(wg *sync.WaitGroup) int {

		if c.Rect().W == 0 || c.Rect().H == 0 {
			c.SetRect(eff.Rect{
				X: 0,
				Y: 0,
				W: defaultWidth,
				H: defaultHeight,
			})
		}

		if len(c.windowTitle) == 0 {
			c.windowTitle = defaultWindowTitle
		}

		if c.frameRate == 0 {
			c.frameRate = defaultFrameRate
		}

		var err error
		mainThread <- func() {
			c.window, err = createWindow(
				c.windowTitle,
				windowPosUndefined,
				windowPosUndefined,
				c.Rect().W,
				c.Rect().H,
				windowOpenGl|windowAllowHighDPI|windowMouseCapture,
			)
			drawableW, _ := c.window.getDrawableSize()
			scale := float64(drawableW) / float64(c.Rect().W)

			if err != nil {
				fmt.Fprintf(os.Stderr, "Failed to create window: %s\n", err)
				// return 1
				return
			}

			if c.fullscreen {
				c.window.setFullscreen(windowFullscreen)
			} else {
				c.window.setFullscreen(0)
			}

			renderFlags := rendererAccelerated | rendererPresentVsync
			if !c.useVsync {
				renderFlags = rendererAccelerated
			}

			renderer, err := createRenderer(
				c.window,
				-1,
				uint32(renderFlags),
			)
			if err != nil {
				fmt.Fprintln(os.Stderr, "Failed to create renderer: ", err)
				// return 2
				return
			}

			c.sdlGraphics = NewGraphics(renderer, scale)

			c.sdlGraphics.renderer.clear()

			c.SetGraphics(c.sdlGraphics)

			wg.Done()
		}

		startTime = getTicks()
		return 0
	}

	run := func() {
		refreshUI := func() {
			drawableW, _ := c.window.getDrawableSize()
			scale := float64(drawableW) / float64(c.Rect().W)
			c.sdlGraphics.scale = scale
			c.SetShouldDraw(true)
			c.SetTextureInvalid(true)
			c.RedrawChildren()
			c.InvalidateChildTextures()
			for _, font := range c.fonts {
				font.refresh(c.sdlGraphics.scale)
			}
		}

		running := true

		for running {
			mainThread <- func() {
				for event := pollEvent(); event != nil; event = pollEvent() {
					switch t := event.(type) {
					case *quitEvent:
						running = false
					case *keyUpEvent:
						switch t.Keysym.Sym {
						case KeyQ:
							running = false
						case KeyF:
							c.fullscreen = !c.fullscreen
							if c.fullscreen {
								c.window.setFullscreen(windowFullscreenDesktop)
							} else {
								c.window.setFullscreen(0)
							}

							refreshUI()
						}

						for _, handler := range c.keyUpHandlers {
							go handler(getKeyName(t.Keysym.Sym))
						}
						for _, handler := range c.keyUpEnumHandlers {
							go handler(t.Keysym.Sym)
						}
					case *keyDownEvent:
						for _, handler := range c.keyDownHandlers {
							go handler(getKeyName(t.Keysym.Sym))
						}
						for _, handler := range c.keyDownEnumHandlers {
							go handler(t.Keysym.Sym)
						}
					case *mouseDownEvent:
						leftState := t.Button == mouseLeft
						middleState := t.Button == mouseMiddle
						rightState := t.Button == mouseRight

						mousePoint := eff.Point{
							X: int(t.X),
							Y: int(t.Y),
						}

						for _, handler := range c.mouseDownHandlers {
							go handler(leftState, middleState, rightState)
						}

						for _, clickable := range c.clickables {
							if clickable == nil || !clickable.IsVisible() {
								continue
							}

							hb := clickable.Hitbox()
							if hb.Inside(mousePoint) {
								go clickable.MouseDown(leftState, middleState, rightState)
							}
						}

						err := captureMouse(true)
						if err != nil {
							fmt.Println(err)
						}

					case *mouseUpEvent:
						leftState := t.Button == mouseLeft
						middleState := t.Button == mouseMiddle
						rightState := t.Button == mouseRight

						mousePoint := eff.Point{
							X: int(t.X),
							Y: int(t.Y),
						}

						for _, handler := range c.mouseUpHandlers {
							go handler(leftState, middleState, rightState)
						}

						for _, clickable := range c.clickables {
							if clickable == nil || !clickable.IsVisible() {
								continue
							}

							hb := clickable.Hitbox()
							if hb.Inside(mousePoint) {
								go clickable.MouseUp(leftState, middleState, rightState)
							}
						}

						err := captureMouse(false)
						if err != nil {
							fmt.Println(err)
						}
					case *mouseMotionEvent:
						mousePoint := eff.Point{
							X: int(t.X),
							Y: int(t.Y),
						}

						for _, handler := range c.mouseMoveHandlers {
							go handler(mousePoint.X, mousePoint.Y)
						}

						for _, clickable := range c.clickables {
							if clickable == nil || !clickable.IsVisible() {
								continue
							}

							hb := clickable.Hitbox()
							if hb.Inside(mousePoint) {
								if !clickable.IsMouseOver() {
									go clickable.MouseOver()
								}
							} else {
								if clickable.IsMouseOver() {
									go clickable.MouseOut()
								}
							}
						}

					case *mouseWheelEvent:
						for _, handler := range c.mouseWheelHandlers {
							go handler(int(t.X), int(t.Y))
						}
					}
				}
				bgColor := c.BackgroundColor()
				c.sdlGraphics.renderer.setDrawColor(
					uint8(bgColor.R),
					uint8(bgColor.G),
					uint8(bgColor.B),
					uint8(bgColor.A),
				)

				drawableW, _ := c.window.getDrawableSize()
				scale := float64(drawableW) / float64(c.Rect().W)
				if scale != c.sdlGraphics.scale {
					refreshUI()
				}
				c.sdlGraphics.renderer.setTarget(nil)
				c.sdlGraphics.renderer.clear()
			}

			c.Draw(c)

			c.HandleUpdate()

			mainThread <- func() {

				printFPS := func() {
					delta = getTicks() - startTime
					if delta != 0 {
						currentFPS = 1000 / delta
					}
					if getTicks()-lastFPSPrintTime >= 1000 {
						fmt.Println(currentFPS, "fps")
						lastFPSPrintTime = getTicks()
					}
				}

				enforceFPS := func() {
					timeBetweenFrames := getTicks() - startTime
					targetTimeBetweenFrames := 1000 / uint32(c.frameRate)

					if timeBetweenFrames < targetTimeBetweenFrames {
						delay(targetTimeBetweenFrames - timeBetweenFrames)
					}

				}

				c.window.updateSurface()
				c.sdlGraphics.renderer.present()

				enforceFPS()
				printFPS()

				startTime = getTicks()
			}
		}
	}

	lockMain(func() {
		var wg sync.WaitGroup
		wg.Add(1)
		initOK := init(&wg)
		wg.Wait()
		if initOK != 0 {
			os.Exit(initOK)
		}

		setup()
		run()
		mainThread <- func() {
			// Clean up goes here
			c.sdlGraphics.renderer.destroy()
			c.window.destroy()

			//Quit SDL
			quit()
			close(mainDone) // stop mainThread
		}
	})
}

// Fullscreen get the full screen state of the window
func (c *Canvas) Fullscreen() bool {
	return c.fullscreen
}

// SetFullscreen set the fullscreen state of the window
func (c *Canvas) SetFullscreen(fullscreen bool) {
	c.fullscreen = fullscreen
}

// OpenFont creates a eff.Font object, used for rendering text
func (c *Canvas) OpenFont(path string, size int) (eff.Font, error) {

	f, err := openFont(path, size, c.sdlGraphics.scale)
	if err != nil {
		return nil, err
	}
	c.fonts = append(c.fonts, f)
	return f, nil
}

// OpenImage creates and eff.Image object, used for drawing an image
func (c *Canvas) OpenImage(path string) (eff.Image, error) {

	wg := &sync.WaitGroup{}
	wg.Add(1)

	var t *texture
	var err error
	var w int
	var h int
	mainThread <- func() {
		s, err := loadImg(path)
		if err != nil {
			wg.Done()
			return
		}
		w = int(s.w)
		h = int(s.h)
		t, err = c.sdlGraphics.renderer.createTextureFromSurface(s)
		freeSurface(s)
		wg.Done()
	}
	wg.Wait()
	if err != nil {
		return nil, err
	}

	image := &Image{
		path:    path,
		texture: t,
		w:       w,
		h:       h,
	}

	return image, nil
}
<|endoftext|>


<commit_before>package main

import (
	"fmt"
	"log"
	"net"
	"net/http"
	_ "net/http/pprof" // register in DefaultServerMux
	"os"
	"time"

	"crypto/tls"

	grpc_middleware "github.com/grpc-ecosystem/go-grpc-middleware"
	grpc_logrus "github.com/grpc-ecosystem/go-grpc-middleware/logging/logrus"
	grpc_prometheus "github.com/grpc-ecosystem/go-grpc-prometheus"
	"github.com/improbable-eng/grpc-web/go/grpcweb"
	"github.com/mwitkow/go-conntrack"
	"github.com/mwitkow/grpc-proxy/proxy"
	"github.com/prometheus/client_golang/prometheus/promhttp"
	"github.com/sirupsen/logrus"
	"github.com/spf13/pflag"
	"golang.org/x/net/context"
	_ "golang.org/x/net/trace" // register in DefaultServerMux
	"google.golang.org/grpc"
	"google.golang.org/grpc/grpclog"
	"google.golang.org/grpc/metadata"
)

var (
	flagBindAddr    = pflag.String("server_bind_address", "0.0.0.0", "address to bind the server to")
	flagHttpPort    = pflag.Int("server_http_debug_port", 8080, "TCP port to listen on for HTTP1.1 debug calls.")
	flagHttpTlsPort = pflag.Int("server_http_tls_port", 8443, "TCP port to listen on for HTTPS (gRPC, gRPC-Web).")

	flagAllowAllOrigins = pflag.Bool("allow_all_origins", false, "allow requests from any origin.")
	flagAllowedOrigins  = pflag.StringSlice("allowed_origins", nil, "comma-separated list of origin URLs which are allowed to make cross-origin requests.")
	flagAllowedHeaders  = pflag.StringSlice("allowed_headers", []string{}, "comma-separated list of headers which are allowed to propagate to the gRPC backend.")

	runHttpServer = pflag.Bool("run_http_server", true, "whether to run HTTP server")
	runTlsServer  = pflag.Bool("run_tls_server", true, "whether to run TLS server")

	useWebsockets         = pflag.Bool("use_websockets", false, "whether to use beta websocket transport layer")
	websocketPingInterval = pflag.Duration("websocket_ping_interval", 0, "whether to use websocket keepalive pinging. Only used when using websockets. Configured interval must be >= 1s.")

	flagHttpMaxWriteTimeout = pflag.Duration("server_http_max_write_timeout", 10*time.Second, "HTTP server config, max write duration.")
	flagHttpMaxReadTimeout  = pflag.Duration("server_http_max_read_timeout", 10*time.Second, "HTTP server config, max read duration.")
)

func main() {
	pflag.Parse()
	for _, flag := range pflag.Args() {
		if flag == "true" || flag == "false" {
			logrus.Fatal("Boolean flags should be set using --flag=false, --flag=true or --flag (which is short for --flag=true). You cannot use --flag true or --flag false.")
		}
		logrus.Fatal("Unknown argument: " + flag)
	}

	logrus.SetOutput(os.Stdout)
	logEntry := logrus.NewEntry(logrus.StandardLogger())

	if *flagAllowAllOrigins && len(*flagAllowedOrigins) != 0 {
		logrus.Fatal("Ambiguous --allow_all_origins and --allow_origins configuration. Either set --allow_all_origins=true OR specify one or more origins to whitelist with --allow_origins, not both.")
	}

	grpcServer := buildGrpcProxyServer(logEntry)
	errChan := make(chan error)

	allowedOrigins := makeAllowedOrigins(*flagAllowedOrigins)

	options := []grpcweb.Option{
		grpcweb.WithCorsForRegisteredEndpointsOnly(false),
		grpcweb.WithOriginFunc(makeHttpOriginFunc(allowedOrigins)),
	}

	if *useWebsockets {
		logrus.Println("using websockets")
		options = append(
			options,
			grpcweb.WithWebsockets(true),
			grpcweb.WithWebsocketOriginFunc(makeWebsocketOriginFunc(allowedOrigins)),
		)
		if *websocketPingInterval >= time.Second {
			logrus.Infof("websocket keepalive pinging enabled, the timeout interval is %s", websocketPingInterval.String())
		}
		options = append(
			options,
			grpcweb.WithWebsocketPingInterval(*websocketPingInterval),
		)
	}

	if len(*flagAllowedHeaders) > 0 {
		options = append(
			options,
			grpcweb.WithAllowedRequestHeaders(*flagAllowedHeaders),
		)
	}

	wrappedGrpc := grpcweb.WrapServer(grpcServer, options...)

	if !*runHttpServer && !*runTlsServer {
		logrus.Fatalf("Both run_http_server and run_tls_server are set to false. At least one must be enabled for grpcweb proxy to function correctly.")
	}

	if *runHttpServer {
		// Debug server.
		debugServer := buildServer(wrappedGrpc)
		http.Handle("/metrics", promhttp.Handler())
		debugListener := buildListenerOrFail("http", *flagHttpPort)
		serveServer(debugServer, debugListener, "http", errChan)
	}

	if *runTlsServer {
		// Debug server.
		servingServer := buildServer(wrappedGrpc)
		servingListener := buildListenerOrFail("http", *flagHttpTlsPort)
		servingListener = tls.NewListener(servingListener, buildServerTlsOrFail())
		serveServer(servingServer, servingListener, "http_tls", errChan)
	}

	<-errChan
	// TODO(mwitkow): Add graceful shutdown.
}

func buildServer(wrappedGrpc *grpcweb.WrappedGrpcServer) *http.Server {
	return &http.Server{
		WriteTimeout: *flagHttpMaxWriteTimeout,
		ReadTimeout:  *flagHttpMaxReadTimeout,
		Handler: http.HandlerFunc(func(resp http.ResponseWriter, req *http.Request) {
			wrappedGrpc.ServeHTTP(resp, req)
		}),
	}
}

func serveServer(server *http.Server, listener net.Listener, name string, errChan chan error) {
	go func() {
		logrus.Infof("listening for %s on: %v", name, listener.Addr().String())
		if err := server.Serve(listener); err != nil {
			errChan <- fmt.Errorf("%s server error: %v", name, err)
		}
	}()
}

func buildGrpcProxyServer(logger *logrus.Entry) *grpc.Server {
	// gRPC-wide changes.
	grpc.EnableTracing = true
	grpc_logrus.ReplaceGrpcLogger(logger)

	// gRPC proxy logic.
	backendConn := dialBackendOrFail()
	director := func(ctx context.Context, fullMethodName string) (context.Context, *grpc.ClientConn, error) {
		md, _ := metadata.FromIncomingContext(ctx)
		outCtx, _ := context.WithCancel(ctx)
		mdCopy := md.Copy()
		delete(mdCopy, "user-agent")
		outCtx = metadata.NewOutgoingContext(outCtx, mdCopy)
		return outCtx, backendConn, nil
	}
	// Server with logging and monitoring enabled.
	return grpc.NewServer(
		grpc.CustomCodec(proxy.Codec()), // needed for proxy to function.
		grpc.UnknownServiceHandler(proxy.TransparentHandler(director)),
		grpc_middleware.WithUnaryServerChain(
			grpc_logrus.UnaryServerInterceptor(logger),
			grpc_prometheus.UnaryServerInterceptor,
		),
		grpc_middleware.WithStreamServerChain(
			grpc_logrus.StreamServerInterceptor(logger),
			grpc_prometheus.StreamServerInterceptor,
		),
	)
}

func buildListenerOrFail(name string, port int) net.Listener {
	addr := fmt.Sprintf("%s:%d", *flagBindAddr, port)
	listener, err := net.Listen("tcp", addr)
	if err != nil {
		log.Fatalf("failed listening for '%v' on %v: %v", name, port, err)
	}
	return conntrack.NewListener(listener,
		conntrack.TrackWithName(name),
		conntrack.TrackWithTcpKeepAlive(20*time.Second),
		conntrack.TrackWithTracing(),
	)
}

func makeHttpOriginFunc(allowedOrigins *allowedOrigins) func(origin string) bool {
	if *flagAllowAllOrigins {
		return func(origin string) bool {
			return true
		}
	}
	return allowedOrigins.IsAllowed
}

func makeWebsocketOriginFunc(allowedOrigins *allowedOrigins) func(req *http.Request) bool {
	if *flagAllowAllOrigins {
		return func(req *http.Request) bool {
			return true
		}
	} else {
		return func(req *http.Request) bool {
			origin, err := grpcweb.WebsocketRequestOrigin(req)
			if err != nil {
				grpclog.Warning(err)
				return false
			}
			return allowedOrigins.IsAllowed(origin)
		}
	}
}

func makeAllowedOrigins(origins []string) *allowedOrigins {
	o := map[string]struct{}{}
	for _, allowedOrigin := range origins {
		o[allowedOrigin] = struct{}{}
	}
	return &allowedOrigins{
		origins: o,
	}
}

type allowedOrigins struct {
	origins map[string]struct{}
}

func (a *allowedOrigins) IsAllowed(origin string) bool {
	_, ok := a.origins[origin]
	return ok
}
<commit_msg>Remove connection header from grpcwebproxy (#588)<commit_after>package main

import (
	"fmt"
	"log"
	"net"
	"net/http"
	_ "net/http/pprof" // register in DefaultServerMux
	"os"
	"time"

	"crypto/tls"

	grpc_middleware "github.com/grpc-ecosystem/go-grpc-middleware"
	grpc_logrus "github.com/grpc-ecosystem/go-grpc-middleware/logging/logrus"
	grpc_prometheus "github.com/grpc-ecosystem/go-grpc-prometheus"
	"github.com/improbable-eng/grpc-web/go/grpcweb"
	"github.com/mwitkow/go-conntrack"
	"github.com/mwitkow/grpc-proxy/proxy"
	"github.com/prometheus/client_golang/prometheus/promhttp"
	"github.com/sirupsen/logrus"
	"github.com/spf13/pflag"
	"golang.org/x/net/context"
	_ "golang.org/x/net/trace" // register in DefaultServerMux
	"google.golang.org/grpc"
	"google.golang.org/grpc/grpclog"
	"google.golang.org/grpc/metadata"
)

var (
	flagBindAddr    = pflag.String("server_bind_address", "0.0.0.0", "address to bind the server to")
	flagHttpPort    = pflag.Int("server_http_debug_port", 8080, "TCP port to listen on for HTTP1.1 debug calls.")
	flagHttpTlsPort = pflag.Int("server_http_tls_port", 8443, "TCP port to listen on for HTTPS (gRPC, gRPC-Web).")

	flagAllowAllOrigins = pflag.Bool("allow_all_origins", false, "allow requests from any origin.")
	flagAllowedOrigins  = pflag.StringSlice("allowed_origins", nil, "comma-separated list of origin URLs which are allowed to make cross-origin requests.")
	flagAllowedHeaders  = pflag.StringSlice("allowed_headers", []string{}, "comma-separated list of headers which are allowed to propagate to the gRPC backend.")

	runHttpServer = pflag.Bool("run_http_server", true, "whether to run HTTP server")
	runTlsServer  = pflag.Bool("run_tls_server", true, "whether to run TLS server")

	useWebsockets         = pflag.Bool("use_websockets", false, "whether to use beta websocket transport layer")
	websocketPingInterval = pflag.Duration("websocket_ping_interval", 0, "whether to use websocket keepalive pinging. Only used when using websockets. Configured interval must be >= 1s.")

	flagHttpMaxWriteTimeout = pflag.Duration("server_http_max_write_timeout", 10*time.Second, "HTTP server config, max write duration.")
	flagHttpMaxReadTimeout  = pflag.Duration("server_http_max_read_timeout", 10*time.Second, "HTTP server config, max read duration.")
)

func main() {
	pflag.Parse()
	for _, flag := range pflag.Args() {
		if flag == "true" || flag == "false" {
			logrus.Fatal("Boolean flags should be set using --flag=false, --flag=true or --flag (which is short for --flag=true). You cannot use --flag true or --flag false.")
		}
		logrus.Fatal("Unknown argument: " + flag)
	}

	logrus.SetOutput(os.Stdout)
	logEntry := logrus.NewEntry(logrus.StandardLogger())

	if *flagAllowAllOrigins && len(*flagAllowedOrigins) != 0 {
		logrus.Fatal("Ambiguous --allow_all_origins and --allow_origins configuration. Either set --allow_all_origins=true OR specify one or more origins to whitelist with --allow_origins, not both.")
	}

	grpcServer := buildGrpcProxyServer(logEntry)
	errChan := make(chan error)

	allowedOrigins := makeAllowedOrigins(*flagAllowedOrigins)

	options := []grpcweb.Option{
		grpcweb.WithCorsForRegisteredEndpointsOnly(false),
		grpcweb.WithOriginFunc(makeHttpOriginFunc(allowedOrigins)),
	}

	if *useWebsockets {
		logrus.Println("using websockets")
		options = append(
			options,
			grpcweb.WithWebsockets(true),
			grpcweb.WithWebsocketOriginFunc(makeWebsocketOriginFunc(allowedOrigins)),
		)
		if *websocketPingInterval >= time.Second {
			logrus.Infof("websocket keepalive pinging enabled, the timeout interval is %s", websocketPingInterval.String())
		}
		options = append(
			options,
			grpcweb.WithWebsocketPingInterval(*websocketPingInterval),
		)
	}

	if len(*flagAllowedHeaders) > 0 {
		options = append(
			options,
			grpcweb.WithAllowedRequestHeaders(*flagAllowedHeaders),
		)
	}

	wrappedGrpc := grpcweb.WrapServer(grpcServer, options...)

	if !*runHttpServer && !*runTlsServer {
		logrus.Fatalf("Both run_http_server and run_tls_server are set to false. At least one must be enabled for grpcweb proxy to function correctly.")
	}

	if *runHttpServer {
		// Debug server.
		debugServer := buildServer(wrappedGrpc)
		http.Handle("/metrics", promhttp.Handler())
		debugListener := buildListenerOrFail("http", *flagHttpPort)
		serveServer(debugServer, debugListener, "http", errChan)
	}

	if *runTlsServer {
		// Debug server.
		servingServer := buildServer(wrappedGrpc)
		servingListener := buildListenerOrFail("http", *flagHttpTlsPort)
		servingListener = tls.NewListener(servingListener, buildServerTlsOrFail())
		serveServer(servingServer, servingListener, "http_tls", errChan)
	}

	<-errChan
	// TODO(mwitkow): Add graceful shutdown.
}

func buildServer(wrappedGrpc *grpcweb.WrappedGrpcServer) *http.Server {
	return &http.Server{
		WriteTimeout: *flagHttpMaxWriteTimeout,
		ReadTimeout:  *flagHttpMaxReadTimeout,
		Handler: http.HandlerFunc(func(resp http.ResponseWriter, req *http.Request) {
			wrappedGrpc.ServeHTTP(resp, req)
		}),
	}
}

func serveServer(server *http.Server, listener net.Listener, name string, errChan chan error) {
	go func() {
		logrus.Infof("listening for %s on: %v", name, listener.Addr().String())
		if err := server.Serve(listener); err != nil {
			errChan <- fmt.Errorf("%s server error: %v", name, err)
		}
	}()
}

func buildGrpcProxyServer(logger *logrus.Entry) *grpc.Server {
	// gRPC-wide changes.
	grpc.EnableTracing = true
	grpc_logrus.ReplaceGrpcLogger(logger)

	// gRPC proxy logic.
	backendConn := dialBackendOrFail()
	director := func(ctx context.Context, fullMethodName string) (context.Context, *grpc.ClientConn, error) {
		md, _ := metadata.FromIncomingContext(ctx)
		outCtx, _ := context.WithCancel(ctx)
		mdCopy := md.Copy()
		delete(mdCopy, "user-agent")
		// If this header is present in the request from the web client,
		// the actual connection to the backend will not be established.
		// https://github.com/improbable-eng/grpc-web/issues/568
		delete(mdCopy, "connection")
		outCtx = metadata.NewOutgoingContext(outCtx, mdCopy)
		return outCtx, backendConn, nil
	}
	// Server with logging and monitoring enabled.
	return grpc.NewServer(
		grpc.CustomCodec(proxy.Codec()), // needed for proxy to function.
		grpc.UnknownServiceHandler(proxy.TransparentHandler(director)),
		grpc_middleware.WithUnaryServerChain(
			grpc_logrus.UnaryServerInterceptor(logger),
			grpc_prometheus.UnaryServerInterceptor,
		),
		grpc_middleware.WithStreamServerChain(
			grpc_logrus.StreamServerInterceptor(logger),
			grpc_prometheus.StreamServerInterceptor,
		),
	)
}

func buildListenerOrFail(name string, port int) net.Listener {
	addr := fmt.Sprintf("%s:%d", *flagBindAddr, port)
	listener, err := net.Listen("tcp", addr)
	if err != nil {
		log.Fatalf("failed listening for '%v' on %v: %v", name, port, err)
	}
	return conntrack.NewListener(listener,
		conntrack.TrackWithName(name),
		conntrack.TrackWithTcpKeepAlive(20*time.Second),
		conntrack.TrackWithTracing(),
	)
}

func makeHttpOriginFunc(allowedOrigins *allowedOrigins) func(origin string) bool {
	if *flagAllowAllOrigins {
		return func(origin string) bool {
			return true
		}
	}
	return allowedOrigins.IsAllowed
}

func makeWebsocketOriginFunc(allowedOrigins *allowedOrigins) func(req *http.Request) bool {
	if *flagAllowAllOrigins {
		return func(req *http.Request) bool {
			return true
		}
	} else {
		return func(req *http.Request) bool {
			origin, err := grpcweb.WebsocketRequestOrigin(req)
			if err != nil {
				grpclog.Warning(err)
				return false
			}
			return allowedOrigins.IsAllowed(origin)
		}
	}
}

func makeAllowedOrigins(origins []string) *allowedOrigins {
	o := map[string]struct{}{}
	for _, allowedOrigin := range origins {
		o[allowedOrigin] = struct{}{}
	}
	return &allowedOrigins{
		origins: o,
	}
}

type allowedOrigins struct {
	origins map[string]struct{}
}

func (a *allowedOrigins) IsAllowed(origin string) bool {
	_, ok := a.origins[origin]
	return ok
}
<|endoftext|>


<commit_before>package main

import (
	"flag"
	"fmt"

	"github.com/keybase/kbfs/libkbfs"
	"golang.org/x/net/context"
)

func mdResetOne(
	ctx context.Context, config libkbfs.Config, tlfPath string,
	dryRun bool) error {
	handle, err := parseTLFPath(ctx, config.KBPKI(), tlfPath)
	if err != nil {
		return err
	}

	// This function is loosely adapted from
	// folderBranchOps.initMDLocked.

	username, uid, err := config.KBPKI().GetCurrentUserInfo(ctx)
	if err != nil {
		return err
	}

	// Make sure we're a writer before doing anything else.
	if !handle.IsWriter(uid) {
		return libkbfs.NewWriteAccessError(
			handle, username, handle.GetCanonicalPath())
	}

	fmt.Printf("Looking for unmerged branch...\n")

	_, unmergedIRMD, err := config.MDOps().GetForHandle(
		ctx, handle, libkbfs.Unmerged)
	if err != nil {
		return err
	}
	if unmergedIRMD != (libkbfs.ImmutableRootMetadata{}) {
		return fmt.Errorf(
			"%s has unmerged data; try unstaging it first",
			tlfPath)
	}

	fmt.Printf("Getting latest metadata...\n")

	_, irmd, err := config.MDOps().GetForHandle(
		ctx, handle, libkbfs.Merged)
	if err != nil {
		return err
	}

	if irmd == (libkbfs.ImmutableRootMetadata{}) {
		fmt.Printf("No TLF found for %q\n", tlfPath)
		return nil
	}

	rootPtr := irmd.Data().Dir.BlockPointer
	if rootPtr.Ref().IsValid() {
		var dirBlock libkbfs.DirBlock
		err = config.BlockOps().Get(ctx, irmd, rootPtr, &dirBlock)
		if err == nil {
			fmt.Printf("Got no error when getting root block %s; aborting\n", rootPtr)
			return nil
		}
		fmt.Printf("Got error %s when getting root block %s, so revision %d is broken. Making successor...\n",
			err, rootPtr, irmd.Revision())
	} else {
		// This happens in the wild, but only for folders used
		// for journal-related testing early on.
		fmt.Printf("Root block pointer is invalid, so revision %d is broken. Making successor...\n",
			irmd.Revision())
	}

	rmdNext, err := irmd.MakeSuccessor(config.Codec(), irmd.MdID(), true)
	if err != nil {
		return err
	}

	// TODO: Add an option to scan for and use the last known good
	// root block.
	_, info, readyBlockData, err :=
		libkbfs.ResetRootBlock(ctx, config, uid, rmdNext)
	if err != nil {
		return err
	}

	fmt.Printf(
		"Will put an empty root block for tlfID=%s with blockInfo=%s and bufLen=%d\n",
		rmdNext.TlfID(), info, readyBlockData.GetEncodedSize())
	fmt.Print("Will put MD:\n")
	err = mdDumpOneReadOnly(ctx, config, rmdNext.ReadOnly())
	if err != nil {
		return err
	}

	if dryRun {
		fmt.Print("Dry-run set; not doing anything\n")
		return nil
	}

	fmt.Printf("Putting block %s...\n", info)

	err = libkbfs.PutBlockCheckQuota(
		ctx, config.BlockServer(), config.Reporter(),
		rmdNext.TlfID(), info.BlockPointer, readyBlockData,
		handle.GetCanonicalName())
	if err != nil {
		return err
	}

	// Assume there's no need to unembed the block changes.

	fmt.Printf("Putting revision %d...\n", rmdNext.Revision())

	mdID, err := config.MDOps().Put(ctx, rmdNext)
	if err != nil {
		return err
	}

	fmt.Printf("New MD has id %s\n", mdID)

	return nil
}

const mdResetUsageStr = `Usage:
  kbfstool md reset /keybase/[public|private]/user1,assertion2

`

func mdReset(ctx context.Context, config libkbfs.Config, args []string) (exitStatus int) {
	flags := flag.NewFlagSet("kbfs md reset", flag.ContinueOnError)
	dryRun := flags.Bool("d", false, "Dry run: don't actually do anything.")
	err := flags.Parse(args)
	if err != nil {
		printError("md reset", err)
		return 1
	}

	inputs := flags.Args()
	if len(inputs) != 1 {
		fmt.Print(mdResetUsageStr)
		return 1
	}

	err = mdResetOne(ctx, config, inputs[0], *dryRun)
	if err != nil {
		printError("md reset", err)
		return 1
	}

	fmt.Print("\n")

	return 0
}
<commit_msg>Add confirmation<commit_after>package main

import (
	"bufio"
	"flag"
	"fmt"
	"os"
	"strings"

	"github.com/keybase/kbfs/libkbfs"
	"golang.org/x/net/context"
)

func mdResetOne(
	ctx context.Context, config libkbfs.Config, tlfPath string,
	dryRun bool) error {
	handle, err := parseTLFPath(ctx, config.KBPKI(), tlfPath)
	if err != nil {
		return err
	}

	// This function is loosely adapted from
	// folderBranchOps.initMDLocked.

	username, uid, err := config.KBPKI().GetCurrentUserInfo(ctx)
	if err != nil {
		return err
	}

	// Make sure we're a writer before doing anything else.
	if !handle.IsWriter(uid) {
		return libkbfs.NewWriteAccessError(
			handle, username, handle.GetCanonicalPath())
	}

	fmt.Printf("Looking for unmerged branch...\n")

	_, unmergedIRMD, err := config.MDOps().GetForHandle(
		ctx, handle, libkbfs.Unmerged)
	if err != nil {
		return err
	}
	if unmergedIRMD != (libkbfs.ImmutableRootMetadata{}) {
		return fmt.Errorf(
			"%s has unmerged data; try unstaging it first",
			tlfPath)
	}

	fmt.Printf("Getting latest metadata...\n")

	_, irmd, err := config.MDOps().GetForHandle(
		ctx, handle, libkbfs.Merged)
	if err != nil {
		return err
	}

	if irmd == (libkbfs.ImmutableRootMetadata{}) {
		fmt.Printf("No TLF found for %q\n", tlfPath)
		return nil
	}

	rootPtr := irmd.Data().Dir.BlockPointer
	if rootPtr.Ref().IsValid() {
		var dirBlock libkbfs.DirBlock
		err = config.BlockOps().Get(ctx, irmd, rootPtr, &dirBlock)
		if err == nil {
			fmt.Printf("Got no error when getting root block %s; aborting\n", rootPtr)
			return nil
		}
		fmt.Printf("Got error %s when getting root block %s, so revision %d is broken. Making successor...\n",
			err, rootPtr, irmd.Revision())
	} else {
		// This happens in the wild, but only for folders used
		// for journal-related testing early on.
		fmt.Printf("Root block pointer is invalid, so revision %d is broken. Making successor...\n",
			irmd.Revision())
	}

	rmdNext, err := irmd.MakeSuccessor(config.Codec(), irmd.MdID(), true)
	if err != nil {
		return err
	}

	// TODO: Add an option to scan for and use the last known good
	// root block.
	_, info, readyBlockData, err :=
		libkbfs.ResetRootBlock(ctx, config, uid, rmdNext)
	if err != nil {
		return err
	}

	fmt.Printf(
		"Will put an empty root block for tlfID=%s with blockInfo=%s and bufLen=%d\n",
		rmdNext.TlfID(), info, readyBlockData.GetEncodedSize())
	fmt.Print("Will put MD:\n")
	err = mdDumpOneReadOnly(ctx, config, rmdNext.ReadOnly())
	if err != nil {
		return err
	}

	if dryRun {
		fmt.Print("Dry-run set; not doing anything\n")
		return nil
	}

	fmt.Print("Are you sure you want to continue? [y/N]: ")
	response, err := bufio.NewReader(os.Stdin).ReadString('\n')
	if err != nil {
		return err
	}
	response = strings.ToLower(strings.TrimSpace(response))
	if response != "y" {
		fmt.Printf("Didn't confirm; not doing anything\n")
		return nil
	}

	fmt.Printf("Putting block %s...\n", info)

	err = libkbfs.PutBlockCheckQuota(
		ctx, config.BlockServer(), config.Reporter(),
		rmdNext.TlfID(), info.BlockPointer, readyBlockData,
		handle.GetCanonicalName())
	if err != nil {
		return err
	}

	// Assume there's no need to unembed the block changes.

	fmt.Printf("Putting revision %d...\n", rmdNext.Revision())

	mdID, err := config.MDOps().Put(ctx, rmdNext)
	if err != nil {
		return err
	}

	fmt.Printf("New MD has id %s\n", mdID)

	return nil
}

const mdResetUsageStr = `Usage:
  kbfstool md reset /keybase/[public|private]/user1,assertion2

`

func mdReset(ctx context.Context, config libkbfs.Config, args []string) (exitStatus int) {
	flags := flag.NewFlagSet("kbfs md reset", flag.ContinueOnError)
	dryRun := flags.Bool("d", false, "Dry run: don't actually do anything.")
	err := flags.Parse(args)
	if err != nil {
		printError("md reset", err)
		return 1
	}

	inputs := flags.Args()
	if len(inputs) != 1 {
		fmt.Print(mdResetUsageStr)
		return 1
	}

	err = mdResetOne(ctx, config, inputs[0], *dryRun)
	if err != nil {
		printError("md reset", err)
		return 1
	}

	fmt.Print("\n")

	return 0
}
<|endoftext|>


<commit_before>package kite

import (
	"errors"
	"flag"
	"koding/newKite/kd/util"
	"os"
	"path/filepath"
)

type Uninstall struct{}

func NewUninstall() *Uninstall {
	return &Uninstall{}
}

func (*Uninstall) Definition() string {
	return "Uninstall a kite"
}

func (*Uninstall) Exec() error {
	flag.Parse()
	if flag.NArg() != 1 {
		return errors.New("You should give a kite name")
	}
	name := flag.Arg(0)

	path := filepath.Join(util.GetKdPath(), "kites", name+".kite")
	return os.RemoveAll(path)
}
<commit_msg>improve uninstall command<commit_after>package kite

import (
	"errors"
	"flag"
	"fmt"
	"koding/newKite/kd/util"
	"os"
	"path/filepath"
)

type Uninstall struct{}

func NewUninstall() *Uninstall {
	return &Uninstall{}
}

func (*Uninstall) Definition() string {
	return "Uninstall a kite"
}

func (*Uninstall) Exec() error {
	flag.Parse()
	if flag.NArg() != 1 {
		return errors.New("You should give a full kite name. Exampe: fs-1.0.0")
	}
	fullName := flag.Arg(0)

	if _, _, err := splitVersion(fullName, false); err != nil {
		return err
	}

	installed, err := isInstalled(fullName)
	if err != nil {
		return err
	}
	if !installed {
		return fmt.Errorf("%s is not installed", fullName)
	}

	return os.RemoveAll(getBundlePath(fullName))
}

// getBundlePath returns the bundle path of a given kite.
// Example: "adsf-1.2.3" -> "~/.kd/kites/asdf-1.2.3.kite"
func getBundlePath(fullKiteName string) string {
	return filepath.Join(util.GetKdPath(), "kites", fullKiteName+".kite")
}

// isInstalled returns true if the kite is installed.
func isInstalled(fullKiteName string) (bool, error) {
	return exists(getBundlePath(fullKiteName))
}

// exists returns whether the given file or directory exists or not.
func exists(path string) (bool, error) {
	_, err := os.Stat(path)
	if err == nil {
		return true, nil
	}
	if os.IsNotExist(err) {
		return false, nil
	}
	return false, err
}
<|endoftext|>


<commit_before>// Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved.

package core

import (
	"github.com/hashicorp/terraform/helper/schema"

	"github.com/oracle/terraform-provider-baremetal/client"
	"github.com/oracle/terraform-provider-baremetal/crud"
)

func SubnetResource() *schema.Resource {
	return &schema.Resource{
		Create: createSubnet,
		Read:   readSubnet,
		Update: updateSubnet,
		Delete: deleteSubnet,
		Schema: map[string]*schema.Schema{
			"availability_domain": {
				Type:     schema.TypeString,
				Required: true,
				ForceNew: true,
			},
			"cidr_block": {
				Type:     schema.TypeString,
				Required: true,
				ForceNew: true,
			},
			"compartment_id": {
				Type:     schema.TypeString,
				Required: true,
				ForceNew: true,
			},
			"route_table_id": {
				Type:     schema.TypeString,
				Required: true,
				ForceNew: true,
			},
			"vcn_id": {
				Type:     schema.TypeString,
				Required: true,
				ForceNew: true,
			},
			"security_list_ids": {
				Type:     schema.TypeSet,
				Required: true,
				ForceNew: true,
				Set:      schema.HashString,
				Elem: &schema.Schema{
					Type: schema.TypeString,
				},
			},
			"display_name": {
				Type:     schema.TypeString,
				Optional: true,
				Computed: true,
			},
<<<<<<< HEAD
			"dns_label": {
				Type:     schema.TypeString,
				Optional: true,
				Computed: true,
			},
			"dhcp_options_id": {
				Type: schema.TypeString,
=======
			"dhcp_options_id": {
				Type:     schema.TypeString,
				Optional: true,
			},
			"dns_label": {
				Type:     schema.TypeString,
>>>>>>> 61edbd6db31bd1392a23f52271f2f8057dc74e8f
				Optional: true,
			},
			"id": {
				Type:     schema.TypeString,
				Computed: true,
			},
			"state": {
				Type:     schema.TypeString,
				Computed: true,
			},
			"time_created": {
				Type:     schema.TypeString,
				Computed: true,
			},
			"virtual_router_ip": {
				Type:     schema.TypeString,
				Computed: true,
			},
			"virtual_router_mac": {
				Type:     schema.TypeString,
				Computed: true,
			},
		},
	}
}

func createSubnet(d *schema.ResourceData, m interface{}) (e error) {
	sync := &SubnetResourceCrud{}
	sync.D = d
	sync.Client = m.(client.BareMetalClient)
	return crud.CreateResource(d, sync)
}

func readSubnet(d *schema.ResourceData, m interface{}) (e error) {
	sync := &SubnetResourceCrud{}
	sync.D = d
	sync.Client = m.(client.BareMetalClient)
	return crud.ReadResource(sync)
}

func updateSubnet(d *schema.ResourceData, m interface{}) (e error) {
	client := m.(client.BareMetalClient)
	sync := &SubnetResourceCrud{}
	sync.D = d
	sync.Client = client
	return crud.UpdateResource(sync.D, sync)
}

func deleteSubnet(d *schema.ResourceData, m interface{}) (e error) {
	sync := &SubnetResourceCrud{}
	sync.D = d
	sync.Client = m.(client.BareMetalClient)
	return crud.DeleteResource(sync)
}
<commit_msg>Fix another bad merge<commit_after>// Copyright (c) 2017, Oracle and/or its affiliates. All rights reserved.

package core

import (
	"github.com/hashicorp/terraform/helper/schema"

	"github.com/oracle/terraform-provider-baremetal/client"
	"github.com/oracle/terraform-provider-baremetal/crud"
)

func SubnetResource() *schema.Resource {
	return &schema.Resource{
		Create: createSubnet,
		Read:   readSubnet,
		Update: updateSubnet,
		Delete: deleteSubnet,
		Schema: map[string]*schema.Schema{
			"availability_domain": {
				Type:     schema.TypeString,
				Required: true,
				ForceNew: true,
			},
			"cidr_block": {
				Type:     schema.TypeString,
				Required: true,
				ForceNew: true,
			},
			"compartment_id": {
				Type:     schema.TypeString,
				Required: true,
				ForceNew: true,
			},
			"route_table_id": {
				Type:     schema.TypeString,
				Required: true,
				ForceNew: true,
			},
			"vcn_id": {
				Type:     schema.TypeString,
				Required: true,
				ForceNew: true,
			},
			"security_list_ids": {
				Type:     schema.TypeSet,
				Required: true,
				ForceNew: true,
				Set:      schema.HashString,
				Elem: &schema.Schema{
					Type: schema.TypeString,
				},
			},
			"display_name": {
				Type:     schema.TypeString,
				Optional: true,
				Computed: true,
			},

			"dhcp_options_id": {
				Type:     schema.TypeString,
				Optional: true,
			},
			"dns_label": {
				Type:     schema.TypeString,
				Optional: true,
			},
			"id": {
				Type:     schema.TypeString,
				Computed: true,
			},
			"state": {
				Type:     schema.TypeString,
				Computed: true,
			},
			"time_created": {
				Type:     schema.TypeString,
				Computed: true,
			},
			"virtual_router_ip": {
				Type:     schema.TypeString,
				Computed: true,
			},
			"virtual_router_mac": {
				Type:     schema.TypeString,
				Computed: true,
			},
		},
	}
}

func createSubnet(d *schema.ResourceData, m interface{}) (e error) {
	sync := &SubnetResourceCrud{}
	sync.D = d
	sync.Client = m.(client.BareMetalClient)
	return crud.CreateResource(d, sync)
}

func readSubnet(d *schema.ResourceData, m interface{}) (e error) {
	sync := &SubnetResourceCrud{}
	sync.D = d
	sync.Client = m.(client.BareMetalClient)
	return crud.ReadResource(sync)
}

func updateSubnet(d *schema.ResourceData, m interface{}) (e error) {
	client := m.(client.BareMetalClient)
	sync := &SubnetResourceCrud{}
	sync.D = d
	sync.Client = client
	return crud.UpdateResource(sync.D, sync)
}

func deleteSubnet(d *schema.ResourceData, m interface{}) (e error) {
	sync := &SubnetResourceCrud{}
	sync.D = d
	sync.Client = m.(client.BareMetalClient)
	return crud.DeleteResource(sync)
}
<|endoftext|>


<commit_before>// Package appsettings provides simple key/value store functionality designed
// to be used for easily storing and recalling application runtime settings
package appsettings

import (
	"encoding/json"
	"errors"
	"io/ioutil"
	"os"
	"strconv"
	"sync"
)

// DataTree is the host of key and branches of values
type DataTree interface {
	GetString(key string) (string, error)
	SetString(key string, val string)
	GetInt(key string) (int, error)
	SetInt(key string, val int)
	GetInt64(key string) (int64, error)
	SetInt64(key string, val int64)
	Delete(key string)
	DeleteTree(key string)
	GetTree(key string) DataTree

	GetTrees() map[string]*tree
	GetLeaves() map[string]string
}

type tree struct {
	Branches map[string]*tree
	Leaves   map[string]string

	sync.Mutex
}

// AppSettings is the root most DataTree
type AppSettings struct {
	filename string

	*tree
}

// NewAppSettings gets a new AppSettings struct
func NewAppSettings(dbFilename string) (*AppSettings, error) {
	a := &AppSettings{
		filename: dbFilename,
		tree: &tree{
			Branches: make(map[string]*tree),
			Leaves:   make(map[string]string),
		},
	}

	if _, err := os.Stat(dbFilename); os.IsNotExist(err) {
		d1, _ := json.Marshal(a)
		err := ioutil.WriteFile(dbFilename, d1, 0644)
		if err != nil {
			return nil, err
		}
	} else {
		d1, err := ioutil.ReadFile(dbFilename)
		if err != nil {
			return nil, err
		}

		err = json.Unmarshal(d1, a)
		if err != nil {
			return nil, err
		}
	}

	return a, nil
}

// ErrUndefinedKey is returned when the key requested from get is undefined.
var ErrUndefinedKey = errors.New("undefined key")

// GetString gets the give keys leaf as a string.
//
// Returns an ErrUndefinedKey if the key is not defined
func (a *tree) GetString(key string) (string, error) {
	a.Lock()
	defer a.Unlock()

	if _, ok := a.Leaves[key]; !ok {
		return "", ErrUndefinedKey
	}

	return a.Leaves[key], nil
}

// SetString sets the give key leaf as a string.
func (a *tree) SetString(key string, val string) {
	a.Lock()
	defer a.Unlock()

	a.Leaves[key] = val
}

// GetInt gets the give keys leaf as an int.
//
// Returns an ErrUndefinedKey if the key is not defined.
//
// Will return an error if ParseInt of the value of the leaf fails.
func (a *tree) GetInt(key string) (int, error) {
	str, err := a.GetString(key)
	if err != nil {
		return 0, err
	}

	i, err := strconv.Atoi(str)
	if err != nil {
		return 0, err
	}
	return i, nil
}

// SetInt sets the give key leaf as a int.
func (a *tree) SetInt(key string, val int) {
	a.Lock()
	defer a.Unlock()

	a.Leaves[key] = strconv.Itoa(val)
}

// GetInt64 gets the give keys leaf as an int64.
//
// Returns an ErrUndefinedKey if the key is not defined.
//
// Will return an error if ParseInt(...,10, 64) of the value of the leaf fails.
func (a *tree) GetInt64(key string) (int64, error) {
	str, err := a.GetString(key)
	if err != nil {
		return 0, err
	}

	i, err := strconv.ParseInt(str, 10, 64)
	if err != nil {
		return 0, err
	}
	return i, nil
}

// SetInt64 sets the give key leaf as a int64.
func (a *tree) SetInt64(key string, val int64) {
	a.Lock()
	defer a.Unlock()

	a.Leaves[key] = strconv.FormatInt(val, 10)
}

// Delete removes the given leaf from the branch.
func (a *tree) Delete(key string) {
	a.Lock()
	defer a.Unlock()

	delete(a.Leaves, key)
}

// Delete removes the given tree from the branch.
func (a *tree) DeleteTree(key string) {
	a.Lock()
	defer a.Unlock()

	delete(a.Branches, key)
}

func (a *tree) GetLeaves() map[string]string {
	return a.Leaves
}

func (a *tree) GetTrees() map[string]*tree {
	return a.Branches
}

// GetTree fetches a tree for app setting storage
func (a *tree) GetTree(key string) DataTree {
	a.Lock()
	defer a.Unlock()

	if _, ok := a.Branches[key]; !ok {
		a.Branches[key] = &tree{
			Branches: make(map[string]*tree),
			Leaves:   make(map[string]string),
		}
	}

	return a.Branches[key]
}

// HasTree checks if the given key is defined as a tree
func (a *tree) HasTree(key string) bool {
	a.Lock()
	defer a.Unlock()

	_, ok := a.Branches[key]
	return ok
}

// HasTree checks if the given key is defined as a leaf value
func (a *tree) HasLeaf(key string) bool {
	a.Lock()
	defer a.Unlock()

	_, ok := a.Leaves[key]
	return ok
}

// Persist causes the current state of the app settings to be persisted.
func (a *AppSettings) Persist() error {
	a.Lock()
	defer a.Unlock()

	d1, err := json.Marshal(a)
	if err != nil {
		return err
	}

	err = ioutil.WriteFile(a.filename, d1, 0644)
	if err != nil {
		return err
	}

	return nil
}
<commit_msg>Adds new members to interface<commit_after>// Package appsettings provides simple key/value store functionality designed
// to be used for easily storing and recalling application runtime settings
package appsettings

import (
	"encoding/json"
	"errors"
	"io/ioutil"
	"os"
	"strconv"
	"sync"
)

// DataTree is the host of key and branches of values
type DataTree interface {
	GetString(key string) (string, error)
	SetString(key string, val string)
	GetInt(key string) (int, error)
	SetInt(key string, val int)
	GetInt64(key string) (int64, error)
	SetInt64(key string, val int64)
	Delete(key string)
	DeleteTree(key string)
	GetTree(key string) DataTree

	HasTree(key string) bool
	HasLeaf(key string) bool

	GetTrees() map[string]*tree
	GetLeaves() map[string]string
}

type tree struct {
	Branches map[string]*tree
	Leaves   map[string]string

	sync.Mutex
}

// AppSettings is the root most DataTree
type AppSettings struct {
	filename string

	*tree
}

// NewAppSettings gets a new AppSettings struct
func NewAppSettings(dbFilename string) (*AppSettings, error) {
	a := &AppSettings{
		filename: dbFilename,
		tree: &tree{
			Branches: make(map[string]*tree),
			Leaves:   make(map[string]string),
		},
	}

	if _, err := os.Stat(dbFilename); os.IsNotExist(err) {
		d1, _ := json.Marshal(a)
		err := ioutil.WriteFile(dbFilename, d1, 0644)
		if err != nil {
			return nil, err
		}
	} else {
		d1, err := ioutil.ReadFile(dbFilename)
		if err != nil {
			return nil, err
		}

		err = json.Unmarshal(d1, a)
		if err != nil {
			return nil, err
		}
	}

	return a, nil
}

// ErrUndefinedKey is returned when the key requested from get is undefined.
var ErrUndefinedKey = errors.New("undefined key")

// GetString gets the give keys leaf as a string.
//
// Returns an ErrUndefinedKey if the key is not defined
func (a *tree) GetString(key string) (string, error) {
	a.Lock()
	defer a.Unlock()

	if _, ok := a.Leaves[key]; !ok {
		return "", ErrUndefinedKey
	}

	return a.Leaves[key], nil
}

// SetString sets the give key leaf as a string.
func (a *tree) SetString(key string, val string) {
	a.Lock()
	defer a.Unlock()

	a.Leaves[key] = val
}

// GetInt gets the give keys leaf as an int.
//
// Returns an ErrUndefinedKey if the key is not defined.
//
// Will return an error if ParseInt of the value of the leaf fails.
func (a *tree) GetInt(key string) (int, error) {
	str, err := a.GetString(key)
	if err != nil {
		return 0, err
	}

	i, err := strconv.Atoi(str)
	if err != nil {
		return 0, err
	}
	return i, nil
}

// SetInt sets the give key leaf as a int.
func (a *tree) SetInt(key string, val int) {
	a.Lock()
	defer a.Unlock()

	a.Leaves[key] = strconv.Itoa(val)
}

// GetInt64 gets the give keys leaf as an int64.
//
// Returns an ErrUndefinedKey if the key is not defined.
//
// Will return an error if ParseInt(...,10, 64) of the value of the leaf fails.
func (a *tree) GetInt64(key string) (int64, error) {
	str, err := a.GetString(key)
	if err != nil {
		return 0, err
	}

	i, err := strconv.ParseInt(str, 10, 64)
	if err != nil {
		return 0, err
	}
	return i, nil
}

// SetInt64 sets the give key leaf as a int64.
func (a *tree) SetInt64(key string, val int64) {
	a.Lock()
	defer a.Unlock()

	a.Leaves[key] = strconv.FormatInt(val, 10)
}

// Delete removes the given leaf from the branch.
func (a *tree) Delete(key string) {
	a.Lock()
	defer a.Unlock()

	delete(a.Leaves, key)
}

// Delete removes the given tree from the branch.
func (a *tree) DeleteTree(key string) {
	a.Lock()
	defer a.Unlock()

	delete(a.Branches, key)
}

func (a *tree) GetLeaves() map[string]string {
	return a.Leaves
}

func (a *tree) GetTrees() map[string]*tree {
	return a.Branches
}

// GetTree fetches a tree for app setting storage
func (a *tree) GetTree(key string) DataTree {
	a.Lock()
	defer a.Unlock()

	if _, ok := a.Branches[key]; !ok {
		a.Branches[key] = &tree{
			Branches: make(map[string]*tree),
			Leaves:   make(map[string]string),
		}
	}

	return a.Branches[key]
}

// HasTree checks if the given key is defined as a tree
func (a *tree) HasTree(key string) bool {
	a.Lock()
	defer a.Unlock()

	_, ok := a.Branches[key]
	return ok
}

// HasTree checks if the given key is defined as a leaf value
func (a *tree) HasLeaf(key string) bool {
	a.Lock()
	defer a.Unlock()

	_, ok := a.Leaves[key]
	return ok
}

// Persist causes the current state of the app settings to be persisted.
func (a *AppSettings) Persist() error {
	a.Lock()
	defer a.Unlock()

	d1, err := json.Marshal(a)
	if err != nil {
		return err
	}

	err = ioutil.WriteFile(a.filename, d1, 0644)
	if err != nil {
		return err
	}

	return nil
}
<|endoftext|>


<commit_before>package main

import (
	"bytes"
	"encoding/json"
	log "github.com/Sirupsen/logrus"
	"github.com/facchinm/go-serial"
	"io"
	"strconv"
	"time"
)

type SerialConfig struct {
	Name string
	Baud int

	// Size     int // 0 get translated to 8
	// Parity   SomeNewTypeToGetCorrectDefaultOf_None
	// StopBits SomeNewTypeToGetCorrectDefaultOf_1

	// RTSFlowControl bool
	// DTRFlowControl bool
	// XONFlowControl bool

	// CRLFTranslate bool
	// TimeoutStuff int
	RtsOn bool
	DtrOn bool
}

type serport struct {
	// The serial port connection.
	portConf *SerialConfig
	portIo   io.ReadWriteCloser

	done chan bool // signals the end of this request

	// Keep track of whether we're being actively closed
	// just so we don't show scary error messages
	isClosing bool

	// counter incremented on queue, decremented on write
	itemsInBuffer int

	// buffered channel containing up to 25600 outbound messages.
	sendBuffered chan Cmd

	// unbuffered channel of outbound messages that bypass internal serial port buffer
	sendNoBuf chan Cmd

	// Do we have an extra channel/thread to watch our buffer?
	BufferType string
	//bufferwatcher *BufferflowDummypause
	bufferwatcher Bufferflow

	// Keep track of whether this is the primary serial port, i.e. cnc controller
	// or if its secondary, i.e. a backup port or arduino or something tertiary
	IsPrimary   bool
	IsSecondary bool
}

type Cmd struct {
	data                       string
	id                         string
	skippedBuffer              bool
	willHandleCompleteResponse bool
}

type CmdComplete struct {
	Cmd     string
	Id      string
	P       string
	BufSize int    `json:"-"`
	D       string `json:"-"`
}

type qwReport struct {
	Cmd  string
	QCnt int
	Id   string
	D    string `json:"-"`
	Buf  string `json:"-"`
	P    string
}

type SpPortMessage struct {
	// P string // the port, i.e. com22
	D string // the data, i.e. G0 X0 Y0
}

func (p *serport) reader() {

	//var buf bytes.Buffer
	ch := make([]byte, 1024)
	timeCheckOpen := time.Now()

	for {

		n, err := p.portIo.Read(ch)

		//if we detect that port is closing, break out o this for{} loop.
		if p.isClosing {
			strmsg := "Shutting down reader on " + p.portConf.Name
			log.Println(strmsg)
			h.broadcastSys <- []byte(strmsg)
			break
		}

		// read can return legitimate bytes as well as an error
		// so process the bytes if n > 0
		if n > 0 {
			//log.Print("Read " + strconv.Itoa(n) + " bytes ch: " + string(ch))
			data := string(ch[:n])
			//log.Print("The data i will convert to json is:")
			//log.Print(data)

			// give the data to our bufferflow so it can do it's work
			// to read/translate the data to see if it wants to block
			// writes to the serialport. each bufferflow type will decide
			// this on its own based on its logic, i.e. tinyg vs grbl vs others
			//p.b.bufferwatcher..OnIncomingData(data)
			p.bufferwatcher.OnIncomingData(data)

			// see if the OnIncomingData handled the broadcast back
			// to the user. this option was added in case the OnIncomingData wanted
			// to do something fancier or implementation specific, i.e. TinyG Buffer
			// actually sends back data on a perline basis rather than our method
			// where we just send the moment we get it. the reason for this is that
			// the browser was sometimes getting back packets out of order which
			// of course would screw things up when parsing

			if p.bufferwatcher.IsBufferGloballySendingBackIncomingData() == false {
				//m := SpPortMessage{"Alice", "Hello"}
				m := SpPortMessage{data}
				//log.Print("The m obj struct is:")
				//log.Print(m)

				//b, err := json.MarshalIndent(m, "", "\t")
				b, err := json.Marshal(m)
				if err != nil {
					log.Println(err)
					h.broadcastSys <- []byte("Error creating json on " + p.portConf.Name + " " +
						err.Error() + " The data we were trying to convert is: " + string(ch[:n]))
					break
				}
				//log.Print("Printing out json byte data...")
				//log.Print(string(b))
				h.broadcastSys <- b
				//h.broadcastSys <- []byte("{ \"p\" : \"" + p.portConf.Name + "\", \"d\": \"" + string(ch[:n]) + "\" }\n")
			}
		}

		// double check that we got characters in the buffer
		// before deciding if an EOF is legitimately a reason
		// to close the port because we're seeing that on some
		// os's like Linux/Ubuntu you get an EOF when you open
		// the port. Perhaps the EOF was buffered from a previous
		// close and the OS doesn't clear out that buffer on a new
		// connect. This means we'll only catch EOF's when there are
		// other characters with it, but that seems to work ok
		if n <= 0 {
			if err == io.EOF || err == io.ErrUnexpectedEOF {
				// hit end of file
				log.Println("Hit end of file on serial port")
				h.broadcastSys <- []byte("{\"Cmd\":\"OpenFail\",\"Desc\":\"Got EOF (End of File) on port which usually means another app other than Serial Port JSON Server is locking your port. " + err.Error() + "\",\"Port\":\"" + p.portConf.Name + "\",\"Baud\":" + strconv.Itoa(p.portConf.Baud) + "}")

			}

			if err != nil {
				log.Println(err)
				h.broadcastSys <- []byte("Error reading on " + p.portConf.Name + " " +
					err.Error() + " Closing port.")
				h.broadcastSys <- []byte("{\"Cmd\":\"OpenFail\",\"Desc\":\"Got error reading on port. " + err.Error() + "\",\"Port\":\"" + p.portConf.Name + "\",\"Baud\":" + strconv.Itoa(p.portConf.Baud) + "}")
				break
			}

			// Keep track of time difference between two consecutive read with n == 0 and err == nil
			// we get here if the port has been disconnected while open (cpu usage will jump to 100%)
			// let's close the port only if the events are extremely fast (<1ms)
			if err == nil {
				diff := time.Since(timeCheckOpen)
				if diff.Nanoseconds() < 1000000 {
					p.isClosing = true
				}
				timeCheckOpen = time.Now()
			}
		}
	}
	p.portIo.Close()
}

// this method runs as its own thread because it's instantiated
// as a "go" method. so if it blocks inside, it is ok
func (p *serport) writerBuffered() {

	// this method can panic if user closes serial port and something is
	// in BlockUntilReady() and then a send occurs on p.sendNoBuf

	defer func() {
		if e := recover(); e != nil {
			// e is the interface{} typed-value we passed to panic()
			log.Println("Got panic: ", e) // Prints "Whoops: boom!"
		}
	}()

	// this for loop blocks on p.sendBuffered until that channel
	// sees something come in
	for data := range p.sendBuffered {

		//log.Printf("Got p.sendBuffered. data:%v, id:%v\n", string(data.data), string(data.id))

		// we want to block here if we are being asked
		// to pause.
		goodToGo, willHandleCompleteResponse := p.bufferwatcher.BlockUntilReady(string(data.data), data.id)

		if goodToGo == false {
			log.Println("We got back from BlockUntilReady() but apparently we must cancel this cmd")
			// since we won't get a buffer decrement in p.sendNoBuf, we must do it here
			p.itemsInBuffer--
		} else {
			// send to the non-buffered serial port writer
			//log.Println("About to send to p.sendNoBuf channel")
			data.willHandleCompleteResponse = willHandleCompleteResponse
			p.sendNoBuf <- data
		}
	}
	msgstr := "writerBuffered just got closed. make sure you make a new one. port:" + p.portConf.Name
	log.Println(msgstr)
	h.broadcastSys <- []byte(msgstr)
}

// this method runs as its own thread because it's instantiated
// as a "go" method. so if it blocks inside, it is ok
func (p *serport) writerNoBuf() {
	// this for loop blocks on p.send until that channel
	// sees something come in
	for data := range p.sendNoBuf {

		//log.Printf("Got p.sendNoBuf. data:%v, id:%v\n", string(data.data), string(data.id))

		// if we get here, we were able to write successfully
		// to the serial port because it blocks until it can write

		// decrement counter
		p.itemsInBuffer--
		log.Printf("itemsInBuffer:%v\n", p.itemsInBuffer)
		//h.broadcastSys <- []byte("{\"Cmd\":\"Write\",\"QCnt\":" + strconv.Itoa(p.itemsInBuffer) + ",\"Byte\":" + strconv.Itoa(n2) + ",\"Port\":\"" + p.portConf.Name + "\"}")

		// For reducing load on websocket, stop transmitting write data
		buf := "Buf"
		if data.skippedBuffer {
			buf = "NoBuf"
		}
		qwr := qwReport{
			Cmd:  "Write",
			QCnt: p.itemsInBuffer,
			Id:   string(data.id),
			D:    string(data.data),
			Buf:  buf,
			P:    p.portConf.Name,
		}
		qwrJson, _ := json.Marshal(qwr)
		h.broadcastSys <- qwrJson

		// FINALLY, OF ALL THE CODE IN THIS PROJECT
		// WE TRULY/FINALLY GET TO WRITE TO THE SERIAL PORT!
		n2, err := p.portIo.Write([]byte(data.data))

		// see if we need to send back the completeResponse
		if data.willHandleCompleteResponse == false {
			// we need to send back complete response
			// Send fake cmd:"Complete" back
			//strCmd := data.data
			m := CmdComplete{"CompleteFake", data.id, p.portConf.Name, -1, data.data}
			msgJson, err := json.Marshal(m)
			if err == nil {
				h.broadcastSys <- msgJson
			}

		}

		log.Print("Just wrote ", n2, " bytes to serial: ", string(data.data))
		//log.Print(n2)
		//log.Print(" bytes to serial: ")
		//log.Print(data)
		if err != nil {
			errstr := "Error writing to " + p.portConf.Name + " " + err.Error() + " Closing port."
			log.Print(errstr)
			h.broadcastSys <- []byte(errstr)
			break
		}
	}
	msgstr := "Shutting down writer on " + p.portConf.Name
	log.Println(msgstr)
	h.broadcastSys <- []byte(msgstr)
	p.portIo.Close()
}

func spHandlerOpen(portname string, baud int, buftype string, isSecondary bool) {

	log.Print("Inside spHandler")

	var out bytes.Buffer

	out.WriteString("Opening serial port ")
	out.WriteString(portname)
	out.WriteString(" at ")
	out.WriteString(strconv.Itoa(baud))
	out.WriteString(" baud")
	log.Print(out.String())

	//h.broadcast <- []byte("Opened a serial port ")
	//h.broadcastSys <- out.Bytes()

	isPrimary := true
	if isSecondary {
		isPrimary = false
	}

	conf := &SerialConfig{Name: portname, Baud: baud, RtsOn: true}

	mode := &serial.Mode{
		BaudRate: baud,
		Vmin:     0,
		Vtimeout: 10,
	}

	sp, err := serial.OpenPort(portname, mode)
	log.Print("Just tried to open port")
	if err != nil {
		//log.Fatal(err)
		log.Print("Error opening port " + err.Error())
		//h.broadcastSys <- []byte("Error opening port. " + err.Error())
		h.broadcastSys <- []byte("{\"Cmd\":\"OpenFail\",\"Desc\":\"Error opening port. " + err.Error() + "\",\"Port\":\"" + conf.Name + "\",\"Baud\":" + strconv.Itoa(conf.Baud) + "}")

		return
	}
	log.Print("Opened port successfully")
	//p := &serport{send: make(chan []byte, 256), portConf: conf, portIo: sp}
	// we can go up to 256,000 lines of gcode in the buffer
	p := &serport{sendBuffered: make(chan Cmd, 256000), sendNoBuf: make(chan Cmd), portConf: conf, portIo: sp, BufferType: buftype, IsPrimary: isPrimary, IsSecondary: isSecondary}

	bw := &BufferflowDefault{}
	bw.Init()
	bw.Port = portname
	p.bufferwatcher = bw

	sh.register <- p
	defer func() { sh.unregister <- p }()
	// this is internally buffered thread to not send to serial port if blocked
	go p.writerBuffered()
	// this is thread to send to serial port regardless of block
	go p.writerNoBuf()
	p.reader()
	//go p.reader()
	//p.done = make(chan bool)
	//<-p.done
}

func spHandlerClose(p *serport) {
	p.isClosing = true
	//close the port
	//elicit response from hardware to close out p.reader()
	_, _ = p.portIo.Write([]byte("?"))

	p.bufferwatcher.Close()
	p.portIo.Close()
	// unregister myself
	// we already have a deferred unregister in place from when
	// we opened. the only thing holding up that thread is the p.reader()
	// so if we close the reader we should get an exit
	h.broadcastSys <- []byte("Closing serial port " + p.portConf.Name)
}
<commit_msg>modify serial close() routine<commit_after>package main

import (
	"bytes"
	"encoding/json"
	log "github.com/Sirupsen/logrus"
	"github.com/facchinm/go-serial"
	"io"
	"strconv"
	"time"
)

type SerialConfig struct {
	Name string
	Baud int

	// Size     int // 0 get translated to 8
	// Parity   SomeNewTypeToGetCorrectDefaultOf_None
	// StopBits SomeNewTypeToGetCorrectDefaultOf_1

	// RTSFlowControl bool
	// DTRFlowControl bool
	// XONFlowControl bool

	// CRLFTranslate bool
	// TimeoutStuff int
	RtsOn bool
	DtrOn bool
}

type serport struct {
	// The serial port connection.
	portConf *SerialConfig
	portIo   io.ReadWriteCloser

	done chan bool // signals the end of this request

	// Keep track of whether we're being actively closed
	// just so we don't show scary error messages
	isClosing bool

	// counter incremented on queue, decremented on write
	itemsInBuffer int

	// buffered channel containing up to 25600 outbound messages.
	sendBuffered chan Cmd

	// unbuffered channel of outbound messages that bypass internal serial port buffer
	sendNoBuf chan Cmd

	// Do we have an extra channel/thread to watch our buffer?
	BufferType string
	//bufferwatcher *BufferflowDummypause
	bufferwatcher Bufferflow

	// Keep track of whether this is the primary serial port, i.e. cnc controller
	// or if its secondary, i.e. a backup port or arduino or something tertiary
	IsPrimary   bool
	IsSecondary bool
}

type Cmd struct {
	data                       string
	id                         string
	skippedBuffer              bool
	willHandleCompleteResponse bool
}

type CmdComplete struct {
	Cmd     string
	Id      string
	P       string
	BufSize int    `json:"-"`
	D       string `json:"-"`
}

type qwReport struct {
	Cmd  string
	QCnt int
	Id   string
	D    string `json:"-"`
	Buf  string `json:"-"`
	P    string
}

type SpPortMessage struct {
	// P string // the port, i.e. com22
	D string // the data, i.e. G0 X0 Y0
}

func (p *serport) reader() {

	//var buf bytes.Buffer
	ch := make([]byte, 1024)
	timeCheckOpen := time.Now()

	for {

		n, err := p.portIo.Read(ch)

		//if we detect that port is closing, break out o this for{} loop.
		if p.isClosing {
			strmsg := "Shutting down reader on " + p.portConf.Name
			log.Println(strmsg)
			h.broadcastSys <- []byte(strmsg)
			break
		}

		// read can return legitimate bytes as well as an error
		// so process the bytes if n > 0
		if n > 0 {
			//log.Print("Read " + strconv.Itoa(n) + " bytes ch: " + string(ch))
			data := string(ch[:n])
			//log.Print("The data i will convert to json is:")
			//log.Print(data)

			// give the data to our bufferflow so it can do it's work
			// to read/translate the data to see if it wants to block
			// writes to the serialport. each bufferflow type will decide
			// this on its own based on its logic, i.e. tinyg vs grbl vs others
			//p.b.bufferwatcher..OnIncomingData(data)
			p.bufferwatcher.OnIncomingData(data)

			// see if the OnIncomingData handled the broadcast back
			// to the user. this option was added in case the OnIncomingData wanted
			// to do something fancier or implementation specific, i.e. TinyG Buffer
			// actually sends back data on a perline basis rather than our method
			// where we just send the moment we get it. the reason for this is that
			// the browser was sometimes getting back packets out of order which
			// of course would screw things up when parsing

			if p.bufferwatcher.IsBufferGloballySendingBackIncomingData() == false {
				//m := SpPortMessage{"Alice", "Hello"}
				m := SpPortMessage{data}
				//log.Print("The m obj struct is:")
				//log.Print(m)

				//b, err := json.MarshalIndent(m, "", "\t")
				b, err := json.Marshal(m)
				if err != nil {
					log.Println(err)
					h.broadcastSys <- []byte("Error creating json on " + p.portConf.Name + " " +
						err.Error() + " The data we were trying to convert is: " + string(ch[:n]))
					break
				}
				//log.Print("Printing out json byte data...")
				//log.Print(string(b))
				h.broadcastSys <- b
				//h.broadcastSys <- []byte("{ \"p\" : \"" + p.portConf.Name + "\", \"d\": \"" + string(ch[:n]) + "\" }\n")
			}
		}

		// double check that we got characters in the buffer
		// before deciding if an EOF is legitimately a reason
		// to close the port because we're seeing that on some
		// os's like Linux/Ubuntu you get an EOF when you open
		// the port. Perhaps the EOF was buffered from a previous
		// close and the OS doesn't clear out that buffer on a new
		// connect. This means we'll only catch EOF's when there are
		// other characters with it, but that seems to work ok
		if n <= 0 {
			if err == io.EOF || err == io.ErrUnexpectedEOF {
				// hit end of file
				log.Println("Hit end of file on serial port")
				h.broadcastSys <- []byte("{\"Cmd\":\"OpenFail\",\"Desc\":\"Got EOF (End of File) on port which usually means another app other than Serial Port JSON Server is locking your port. " + err.Error() + "\",\"Port\":\"" + p.portConf.Name + "\",\"Baud\":" + strconv.Itoa(p.portConf.Baud) + "}")

			}

			if err != nil {
				log.Println(err)
				h.broadcastSys <- []byte("Error reading on " + p.portConf.Name + " " +
					err.Error() + " Closing port.")
				h.broadcastSys <- []byte("{\"Cmd\":\"OpenFail\",\"Desc\":\"Got error reading on port. " + err.Error() + "\",\"Port\":\"" + p.portConf.Name + "\",\"Baud\":" + strconv.Itoa(p.portConf.Baud) + "}")
				break
			}

			// Keep track of time difference between two consecutive read with n == 0 and err == nil
			// we get here if the port has been disconnected while open (cpu usage will jump to 100%)
			// let's close the port only if the events are extremely fast (<1ms)
			if err == nil {
				diff := time.Since(timeCheckOpen)
				if diff.Nanoseconds() < 1000000 {
					p.isClosing = true
				}
				timeCheckOpen = time.Now()
			}
		}
	}
	spCloseReal(p)
}

// this method runs as its own thread because it's instantiated
// as a "go" method. so if it blocks inside, it is ok
func (p *serport) writerBuffered() {

	// this method can panic if user closes serial port and something is
	// in BlockUntilReady() and then a send occurs on p.sendNoBuf

	defer func() {
		if e := recover(); e != nil {
			// e is the interface{} typed-value we passed to panic()
			log.Println("Got panic: ", e) // Prints "Whoops: boom!"
		}
	}()

	// this for loop blocks on p.sendBuffered until that channel
	// sees something come in
	for data := range p.sendBuffered {

		//log.Printf("Got p.sendBuffered. data:%v, id:%v\n", string(data.data), string(data.id))

		// we want to block here if we are being asked
		// to pause.
		goodToGo, willHandleCompleteResponse := p.bufferwatcher.BlockUntilReady(string(data.data), data.id)

		if goodToGo == false {
			log.Println("We got back from BlockUntilReady() but apparently we must cancel this cmd")
			// since we won't get a buffer decrement in p.sendNoBuf, we must do it here
			p.itemsInBuffer--
		} else {
			// send to the non-buffered serial port writer
			//log.Println("About to send to p.sendNoBuf channel")
			data.willHandleCompleteResponse = willHandleCompleteResponse
			p.sendNoBuf <- data
		}
	}
	msgstr := "writerBuffered just got closed. make sure you make a new one. port:" + p.portConf.Name
	log.Println(msgstr)
	h.broadcastSys <- []byte(msgstr)
}

// this method runs as its own thread because it's instantiated
// as a "go" method. so if it blocks inside, it is ok
func (p *serport) writerNoBuf() {
	// this for loop blocks on p.send until that channel
	// sees something come in
	for data := range p.sendNoBuf {

		//log.Printf("Got p.sendNoBuf. data:%v, id:%v\n", string(data.data), string(data.id))

		// if we get here, we were able to write successfully
		// to the serial port because it blocks until it can write

		// decrement counter
		p.itemsInBuffer--
		log.Printf("itemsInBuffer:%v\n", p.itemsInBuffer)
		//h.broadcastSys <- []byte("{\"Cmd\":\"Write\",\"QCnt\":" + strconv.Itoa(p.itemsInBuffer) + ",\"Byte\":" + strconv.Itoa(n2) + ",\"Port\":\"" + p.portConf.Name + "\"}")

		// For reducing load on websocket, stop transmitting write data
		buf := "Buf"
		if data.skippedBuffer {
			buf = "NoBuf"
		}
		qwr := qwReport{
			Cmd:  "Write",
			QCnt: p.itemsInBuffer,
			Id:   string(data.id),
			D:    string(data.data),
			Buf:  buf,
			P:    p.portConf.Name,
		}
		qwrJson, _ := json.Marshal(qwr)
		h.broadcastSys <- qwrJson

		// FINALLY, OF ALL THE CODE IN THIS PROJECT
		// WE TRULY/FINALLY GET TO WRITE TO THE SERIAL PORT!
		n2, err := p.portIo.Write([]byte(data.data))

		// see if we need to send back the completeResponse
		if data.willHandleCompleteResponse == false {
			// we need to send back complete response
			// Send fake cmd:"Complete" back
			//strCmd := data.data
			m := CmdComplete{"CompleteFake", data.id, p.portConf.Name, -1, data.data}
			msgJson, err := json.Marshal(m)
			if err == nil {
				h.broadcastSys <- msgJson
			}

		}

		log.Print("Just wrote ", n2, " bytes to serial: ", string(data.data))
		//log.Print(n2)
		//log.Print(" bytes to serial: ")
		//log.Print(data)
		if err != nil {
			errstr := "Error writing to " + p.portConf.Name + " " + err.Error() + " Closing port."
			log.Print(errstr)
			h.broadcastSys <- []byte(errstr)
			break
		}
	}
	msgstr := "Shutting down writer on " + p.portConf.Name
	log.Println(msgstr)
	h.broadcastSys <- []byte(msgstr)
	p.portIo.Close()
}

func spHandlerOpen(portname string, baud int, buftype string, isSecondary bool) {

	log.Print("Inside spHandler")

	var out bytes.Buffer

	out.WriteString("Opening serial port ")
	out.WriteString(portname)
	out.WriteString(" at ")
	out.WriteString(strconv.Itoa(baud))
	out.WriteString(" baud")
	log.Print(out.String())

	//h.broadcast <- []byte("Opened a serial port ")
	//h.broadcastSys <- out.Bytes()

	isPrimary := true
	if isSecondary {
		isPrimary = false
	}

	conf := &SerialConfig{Name: portname, Baud: baud, RtsOn: true}

	mode := &serial.Mode{
		BaudRate: baud,
		Vmin:     0,
		Vtimeout: 1,
	}

	sp, err := serial.OpenPort(portname, mode)
	log.Print("Just tried to open port")
	if err != nil {
		//log.Fatal(err)
		log.Print("Error opening port " + err.Error())
		//h.broadcastSys <- []byte("Error opening port. " + err.Error())
		h.broadcastSys <- []byte("{\"Cmd\":\"OpenFail\",\"Desc\":\"Error opening port. " + err.Error() + "\",\"Port\":\"" + conf.Name + "\",\"Baud\":" + strconv.Itoa(conf.Baud) + "}")

		return
	}
	log.Print("Opened port successfully")
	//p := &serport{send: make(chan []byte, 256), portConf: conf, portIo: sp}
	// we can go up to 256,000 lines of gcode in the buffer
	p := &serport{sendBuffered: make(chan Cmd, 256000), sendNoBuf: make(chan Cmd), portConf: conf, portIo: sp, BufferType: buftype, IsPrimary: isPrimary, IsSecondary: isSecondary}

	bw := &BufferflowDefault{}
	bw.Init()
	bw.Port = portname
	p.bufferwatcher = bw

	sh.register <- p
	defer func() { sh.unregister <- p }()
	// this is internally buffered thread to not send to serial port if blocked
	go p.writerBuffered()
	// this is thread to send to serial port regardless of block
	go p.writerNoBuf()
	p.reader()
	//go p.reader()
	//p.done = make(chan bool)
	//<-p.done
}

func spHandlerClose(p *serport) {
	p.isClosing = true
	h.broadcastSys <- []byte("Closing serial port " + p.portConf.Name)
}

func spCloseReal(p *serport) {
	p.bufferwatcher.Close()
	p.portIo.Close()
}
<|endoftext|>