<commit_before>package main

import (
	"database/sql"
	"encoding/json"
	"io/ioutil"
	"log"
	"net/http"

	_ "github.com/go-sql-driver/mysql"

	"github.com/domestic-apps/domestic-api/chores"
	"github.com/domestic-apps/domestic-api/tasks"

	"gopkg.in/robfig/cron.v2"
)

type secrets struct {
	Uname string `json:"username"`
	Pwd   string `json:"password"`
}

func main() {
	// get mysql username and password from configuration
	file, err := ioutil.ReadFile("./secrets.json")
	if err != nil {
		log.Fatal("File error: %v\n", err)
	}

	var s secrets
	json.Unmarshal(file, &s)
	timezone := "America/Los_Angeles"

	// Set up Database
	db, err := sql.Open("mysql",
		s.Uname+":"+s.Pwd+"@tcp(localhost:3306)/domestic?parseTime=true")
	if err != nil {
		log.Fatal(err)
	}
	if err = db.Ping(); err != nil {
		log.Fatal(err)
	}
	defer db.Close()

	// Prepare statements in application handlers.
	choresHandler := chores.InitializeHandler(db)
	tasksHandler := tasks.InitializeHandler(db, timezone)
	http.HandleFunc("/chores/", choresHandler.Handle)
	http.HandleFunc("/tasks/", tasksHandler.Handle)
	c := cron.New()
	c.AddFunc("TZ="+timezone+" 0 0 7,19 * * *", tasksHandler.Cron) // 7am and 7pm every day
	c.Start()
	log.Fatal(http.ListenAndServe(":8080", nil))
}
<commit_msg>serve ssl<commit_after>package main

import (
	"database/sql"
	"encoding/json"
	"io/ioutil"
	"log"
	"net/http"

	_ "github.com/go-sql-driver/mysql"

	"github.com/domestic-apps/domestic-api/chores"
	"github.com/domestic-apps/domestic-api/tasks"

	"gopkg.in/robfig/cron.v2"
)

type secrets struct {
	Uname string `json:"username"`
	Pwd   string `json:"password"`
	Cert  string `json:"cert"`
	Key   string `json:"key"`
}

func main() {
	// get mysql username and password from configuration
	file, err := ioutil.ReadFile("./secrets.json")
	if err != nil {
		log.Fatal("File error: %v\n", err)
	}

	var s secrets
	json.Unmarshal(file, &s)
	timezone := "America/Los_Angeles"

	// Set up Database
	db, err := sql.Open("mysql",
		s.Uname+":"+s.Pwd+"@tcp(localhost:3306)/domestic?parseTime=true")
	if err != nil {
		log.Fatal(err)
	}
	if err = db.Ping(); err != nil {
		log.Fatal(err)
	}
	defer db.Close()

	// Prepare statements in application handlers.
	choresHandler := chores.InitializeHandler(db)
	tasksHandler := tasks.InitializeHandler(db, timezone)
	http.HandleFunc("/chores/", choresHandler.Handle)
	http.HandleFunc("/tasks/", tasksHandler.Handle)
	c := cron.New()
	c.AddFunc("TZ="+timezone+" 0 0 7,19 * * *", tasksHandler.Cron) // 7am and 7pm every day
	c.Start()
	log.Fatal(http.ListenAndServeTLS(":443", s.Cert, s.Key, nil))
}
<|endoftext|>


<commit_before>package main

// github.com/cheggaaa/pb

import (
	"fmt"
	"os"
	"path/filepath"
	"strconv"
	"time"

	"github.com/cavaliercoder/grab"
	"github.com/fatih/color"
	"github.com/gosuri/uilive"
	rss "github.com/jteeuwen/go-pkg-rss"
)

func getRss(podcast *Podcast) (*rss.Feed, error) {

	feed := rss.New(1, true, nil, nil)
	if err := feed.Fetch(podcast.Url, nil); err != nil {
		return nil, err
	}
	return feed, nil
}

func getRssName(url string) (string, error) {
	feed := rss.New(1, true, nil, nil)
	if err := feed.Fetch(url, nil); err != nil {
		return "", err
	}
	return feed.Channels[0].Title, nil
}

func syncPodcasts(startDate time.Time, nameOrID string, count int, chekMode bool) error {
	allReqs := [][]*grab.Request{}
	podcasts := []*Podcast{}

	if nameOrID == "" {
		podcasts = cfg.GetAllPodcasts()
	} else {
		p, err := cfg.GetPodcastByNameOrID(nameOrID)
		if err != nil {
			return err
		}
		podcasts = append(podcasts, p)
	}

	for n, podcast := range podcasts {

		var podcastList []*DownloadItem

		filter := MakeFilter(podcast)
		filter.Count = count
		filter.StartDate = startDate

		// download rss
		feed, err := getRss(podcast)
		if err != nil {
			printPodcastInfo(podcast, podcastList, n+1, err)
			continue
		}

		if len(feed.Channels) == 0 {
			log.Warnf(fmt.Sprintf("No channels in %s", podcast.Name))
			continue
		}

		// filter
		podcastList, err = filter.FilterItems(feed.Channels[0])
		if err != nil {
			printPodcastInfo(podcast, podcastList, n+1, err)
			continue
		}

		if chekMode {
			printPodcastInfo(podcast, podcastList, n+1, err)
			continue
		}

		// check for emptiness
		if len(podcastList) == 0 {
			log.Printf("%s : %s, %d files", color.CyanString("EMPTY"), podcast.Name, len(podcastList))
			continue
		}

		// create download requests
		allReqs = append(allReqs, createRequests(podcast, podcastList))

	}

	if !chekMode {
		startDownload(allReqs)

		for _, podcast := range podcasts {
			// FIXME: put right date according to rss or Item PubDate
			podcast.LastSynced = time.Now()
			if err := cfg.UpdatePodcast(podcast); err != nil {
				return err
			}
		}
	}

	return nil
}

func printPodcastInfo(podcast *Podcast, podcastList []*DownloadItem, index int, err error) {

	status := ""
	num := color.MagentaString("[" + strconv.Itoa(index) + "] ")
	if err != nil {
		status = color.RedString("FAIL")
	} else {
		color.GreenString("OK")
	}

	log.Printf("%s %s", num, podcast.Name)
	log.Printf("\t* Url             : %s %s", podcast.Url, status)
	if err != nil {
		log.Warnf("Error: %s", err)
	} else {
		log.Printf("\t* Awaiting files  : %d", len(podcastList))
		for k, podcast := range podcastList {
			log.Printf("\t\t* [%d] : %s", k, podcast.ItemTitle)
		}
	}
}

func createRequests(podcast *Podcast, podcastList []*DownloadItem) []*grab.Request {
	reqs := []*grab.Request{}
	for _, entry := range podcastList {
		// create dir for each entry, path is set in filter
		// according to rules in configuration
		entryDownloadPath := filepath.Join(podcast.DownloadPath, entry.Dir)
		if !fileExists(entryDownloadPath) {
			if err := os.MkdirAll(entryDownloadPath, 0777); err != nil {
				log.Fatal(err)
				continue
			}
		}

		req, _ := grab.NewRequest(entry.Url)
		req.Filename = filepath.Join(entryDownloadPath, entry.Filename)
		req.Size = uint64(entry.Size)
		req.RemoveOnError = true
		reqs = append(reqs, req)
	}
	return reqs
}

func startDownload(downloadReqs [][]*grab.Request) {
	requestCount := len(downloadReqs)
	statusQueue := make(chan *downloadStatus, requestCount)
	doneQueue := make(chan bool, requestCount)

	client := grab.NewClient()

	go func() {
		// wait while all requests will be in queue
		for i := 0; i < requestCount; i++ {
			<-doneQueue
		}
		// close channels
		close(statusQueue)
		close(doneQueue)
	}()

	totalFiles := 0
	for _, podcastReq := range downloadReqs {
		totalFiles += len(podcastReq)

		go func(requests []*grab.Request) {
			curPosition := 0
			podcastTotal := len(requests)
			for _, req := range requests {

				// increas position, used for printing
				curPosition++

				// start downloading
				resp := <-client.DoAsync(req)

				// send results to monitoring channel
				statusQueue <- &downloadStatus{
					Total:    podcastTotal,
					Current:  curPosition,
					Response: resp,
				}

				// ensure files downloaded one by one, so wait complition
				for !resp.IsComplete() {
					time.Sleep(500 * time.Microsecond)
				}
			}

		}(podcastReq)
	}
	checkDownloadProgress(statusQueue, totalFiles)
	log.Infof("%d files downloaded.\n", totalFiles)
}

type downloadStatus struct {
	Total    int // total requests count
	Current  int // current position
	Response *grab.Response
}

func checkDownloadProgress(respch <-chan *downloadStatus, reqCount int) {
	timer := time.NewTicker(200 * time.Millisecond)
	ui := uilive.New()

	completed := 0
	responses := make([]*downloadStatus, 0)

	ui.Start()
	for completed < reqCount {
		select {
		case resp := <-respch:
			if resp != nil {
				responses = append(responses, resp)
			}

		case <-timer.C:
			// print completed requests
			for i, resp := range responses {
				if resp != nil && resp.Response.IsComplete() {

					if resp.Response.Error != nil {
						showProgressError(ui, resp)
					} else {
						showProgressDone(ui, resp)
					}

					responses[i] = nil
					completed++
				}
			}

			// print in progress requests
			for _, resp := range responses {
				if resp != nil {
					showProgressProc(ui, resp)
				}
			}
		}
	}

	timer.Stop()
	ui.Stop()
}

func bytesToMb(bytesCount uint64) float64 {
	return float64(bytesCount / 1024 / 1024)
}

func showProgressError(ui *uilive.Writer, status *downloadStatus) {
	fmt.Fprintf(ui.Bypass(), "Error downloading %s: %v\n",
		status.Response.Request.URL(),
		status.Response.Error)
}

func showProgressDone(ui *uilive.Writer, status *downloadStatus) {
	fmt.Fprintf(ui.Bypass(),
		"Finished %s [%d/%d] %0.2f / %0.2f Mb (%d%%)\n",
		status.Response.Filename,
		status.Current, status.Total,
		bytesToMb(status.Response.BytesTransferred()),
		bytesToMb(status.Response.Size),
		int(100*status.Response.Progress()))
}

func showProgressProc(ui *uilive.Writer, status *downloadStatus) {
	fmt.Fprintf(ui, "Downloading %s [%d/%d] %0.2f / %0.2f Mb (%d%%)\n",
		status.Response.Filename,
		status.Current, status.Total,
		bytesToMb(status.Response.BytesTransferred()),
		bytesToMb(status.Response.Size),
		int(100*status.Response.Progress()))

}
<commit_msg>convert bytes to mb in progress print<commit_after>package main

// github.com/cheggaaa/pb

import (
	"fmt"
	"os"
	"path/filepath"
	"strconv"
	"time"

	"github.com/cavaliercoder/grab"
	"github.com/fatih/color"
	"github.com/gosuri/uilive"
	rss "github.com/jteeuwen/go-pkg-rss"
)

func getRss(podcast *Podcast) (*rss.Feed, error) {

	feed := rss.New(1, true, nil, nil)
	if err := feed.Fetch(podcast.Url, nil); err != nil {
		return nil, err
	}
	return feed, nil
}

func getRssName(url string) (string, error) {
	feed := rss.New(1, true, nil, nil)
	if err := feed.Fetch(url, nil); err != nil {
		return "", err
	}
	return feed.Channels[0].Title, nil
}

func syncPodcasts(startDate time.Time, nameOrID string, count int, chekMode bool) error {
	allReqs := [][]*grab.Request{}
	podcasts := []*Podcast{}

	if nameOrID == "" {
		podcasts = cfg.GetAllPodcasts()
	} else {
		p, err := cfg.GetPodcastByNameOrID(nameOrID)
		if err != nil {
			return err
		}
		podcasts = append(podcasts, p)
	}

	for n, podcast := range podcasts {

		var podcastList []*DownloadItem

		filter := MakeFilter(podcast)
		filter.Count = count
		filter.StartDate = startDate

		// download rss
		feed, err := getRss(podcast)
		if err != nil {
			printPodcastInfo(podcast, podcastList, n+1, err)
			continue
		}

		if len(feed.Channels) == 0 {
			log.Warnf(fmt.Sprintf("No channels in %s", podcast.Name))
			continue
		}

		// filter
		podcastList, err = filter.FilterItems(feed.Channels[0])
		if err != nil {
			printPodcastInfo(podcast, podcastList, n+1, err)
			continue
		}

		if chekMode {
			printPodcastInfo(podcast, podcastList, n+1, err)
			continue
		}

		// check for emptiness
		if len(podcastList) == 0 {
			log.Printf("%s : %s, %d files", color.CyanString("EMPTY"), podcast.Name, len(podcastList))
			continue
		}

		// create download requests
		allReqs = append(allReqs, createRequests(podcast, podcastList))

	}

	if !chekMode {
		startDownload(allReqs)

		for _, podcast := range podcasts {
			// FIXME: put right date according to rss or Item PubDate
			podcast.LastSynced = time.Now()
			if err := cfg.UpdatePodcast(podcast); err != nil {
				return err
			}
		}
	}

	return nil
}

func printPodcastInfo(podcast *Podcast, podcastList []*DownloadItem, index int, err error) {

	status := ""
	num := color.MagentaString("[" + strconv.Itoa(index) + "] ")
	if err != nil {
		status = color.RedString("FAIL")
	} else {
		color.GreenString("OK")
	}

	log.Printf("%s %s", num, podcast.Name)
	log.Printf("\t* Url             : %s %s", podcast.Url, status)
	if err != nil {
		log.Warnf("Error: %s", err)
	} else {
		log.Printf("\t* Awaiting files  : %d", len(podcastList))
		for k, podcast := range podcastList {
			log.Printf("\t\t* [%d] : %s", k, podcast.ItemTitle)
		}
	}
}

func createRequests(podcast *Podcast, podcastList []*DownloadItem) []*grab.Request {
	reqs := []*grab.Request{}
	for _, entry := range podcastList {
		// create dir for each entry, path is set in filter
		// according to rules in configuration
		entryDownloadPath := filepath.Join(podcast.DownloadPath, entry.Dir)
		if !fileExists(entryDownloadPath) {
			if err := os.MkdirAll(entryDownloadPath, 0777); err != nil {
				log.Fatal(err)
				continue
			}
		}

		req, _ := grab.NewRequest(entry.Url)
		req.Filename = filepath.Join(entryDownloadPath, entry.Filename)
		req.Size = uint64(entry.Size)
		req.RemoveOnError = true
		reqs = append(reqs, req)
	}
	return reqs
}

func startDownload(downloadReqs [][]*grab.Request) {
	requestCount := len(downloadReqs)
	statusQueue := make(chan *downloadStatus, requestCount)
	doneQueue := make(chan bool, requestCount)

	client := grab.NewClient()

	go func() {
		// wait while all requests will be in queue
		for i := 0; i < requestCount; i++ {
			<-doneQueue
		}
		// close channels
		close(statusQueue)
		close(doneQueue)
	}()

	totalFiles := 0
	for _, podcastReq := range downloadReqs {
		totalFiles += len(podcastReq)

		go func(requests []*grab.Request) {
			curPosition := 0
			podcastTotal := len(requests)
			for _, req := range requests {

				// increas position, used for printing
				curPosition++

				// start downloading
				resp := <-client.DoAsync(req)

				// send results to monitoring channel
				statusQueue <- &downloadStatus{
					Total:    podcastTotal,
					Current:  curPosition,
					Response: resp,
				}

				// ensure files downloaded one by one, so wait complition
				for !resp.IsComplete() {
					time.Sleep(500 * time.Microsecond)
				}
			}

		}(podcastReq)
	}
	checkDownloadProgress(statusQueue, totalFiles)
	log.Infof("%d files downloaded.\n", totalFiles)
}

type downloadStatus struct {
	Total    int // total requests count
	Current  int // current position
	Response *grab.Response
}

func checkDownloadProgress(respch <-chan *downloadStatus, reqCount int) {
	timer := time.NewTicker(200 * time.Millisecond)
	ui := uilive.New()

	completed := 0
	responses := make([]*downloadStatus, 0)

	ui.Start()
	for completed < reqCount {
		select {
		case resp := <-respch:
			if resp != nil {
				responses = append(responses, resp)
			}

		case <-timer.C:
			// print completed requests
			for i, resp := range responses {
				if resp != nil && resp.Response.IsComplete() {

					if resp.Response.Error != nil {
						showProgressError(ui, resp)
					} else {
						showProgressDone(ui, resp)
					}

					responses[i] = nil
					completed++
				}
			}

			// print in progress requests
			for _, resp := range responses {
				if resp != nil {
					showProgressProc(ui, resp)
				}
			}
		}
	}

	timer.Stop()
	ui.Stop()
}

func bytesToMb(bytesCount uint64) float64 {
	return float64(bytesCount) / float64(1024*1024)
}

func showProgressError(ui *uilive.Writer, status *downloadStatus) {
	fmt.Fprintf(ui.Bypass(), "Error downloading %s: %v\n",
		status.Response.Request.URL(),
		status.Response.Error)
}

func showProgressDone(ui *uilive.Writer, status *downloadStatus) {
	fmt.Fprintf(ui.Bypass(),
		"Finished %s [%d/%d] %0.2f / %0.2f Mb (%d%%)\n",
		status.Response.Filename,
		status.Current, status.Total,
		bytesToMb(status.Response.BytesTransferred()),
		bytesToMb(status.Response.Size),
		int(100*status.Response.Progress()))
}

func showProgressProc(ui *uilive.Writer, status *downloadStatus) {
	fmt.Fprintf(ui, "Downloading %s [%d/%d] %0.2f / %0.2f Mb (%d%%)\n",
		status.Response.Filename,
		status.Current, status.Total,
		bytesToMb(status.Response.BytesTransferred()),
		bytesToMb(status.Response.Size),
		int(100*status.Response.Progress()))

}
<|endoftext|>


<commit_before>// Copyright 2016 The OPA Authors.  All rights reserved.
// Use of this source code is governed by an Apache2
// license that can be found in the LICENSE file.

package storage

import (
	"fmt"
	"net/url"
	"strconv"
	"strings"

	"github.com/open-policy-agent/opa/ast"
)

// Path refers to a document in storage.
type Path []string

// ParsePath returns a new path for the given str.
func ParsePath(str string) (path Path, ok bool) {
	if len(str) == 0 {
		return nil, false
	}
	if str[0] != '/' {
		return nil, false
	}
	if len(str) == 1 {
		return Path{}, true
	}
	parts := strings.Split(str[1:], "/")
	return parts, true
}

// ParsePathEscaped returns a new path for the given escaped str.
func ParsePathEscaped(str string) (path Path, ok bool) {
	path, ok = ParsePath(str)
	if !ok {
		return
	}
	for i := range path {
		segment, err := url.PathUnescape(path[i])
		if err == nil {
			path[i] = segment
		}
	}
	return
}

// NewPathForRef returns a new path for the given ref.
func NewPathForRef(ref ast.Ref) (path Path, err error) {

	if len(ref) == 0 {
		return nil, fmt.Errorf("empty reference (indicates error in caller)")
	}

	if len(ref) == 1 {
		return Path{}, nil
	}

	for _, term := range ref[1:] {
		switch v := term.Value.(type) {
		case ast.String:
			path = append(path, string(v))
		case ast.Number:
			path = append(path, v.String())
		case ast.Boolean, ast.Null:
			return nil, &Error{
				Code:    NotFoundErr,
				Message: fmt.Sprintf("%v: does not exist", ref),
			}
		case ast.Array, ast.Object, ast.Set:
			return nil, fmt.Errorf("composites cannot be base document keys: %v", ref)
		default:
			return nil, fmt.Errorf("unresolved reference (indicates error in caller): %v", ref)
		}
	}

	return path, nil
}

// Compare performs lexigraphical comparison on p and other and returns -1 if p
// is less than other, 0 if p is equal to other, or 1 if p is greater than
// other.
func (p Path) Compare(other Path) (cmp int) {
	min := len(p)
	if len(other) < min {
		min = len(other)
	}
	for i := 0; i < min; i++ {
		if cmp := strings.Compare(p[i], other[i]); cmp != 0 {
			return cmp
		}
	}
	if len(p) < len(other) {
		return -1
	}
	if len(p) == len(other) {
		return 0
	}
	return 1
}

// Equal returns true if p is the same as other.
func (p Path) Equal(other Path) bool {
	return p.Compare(other) == 0
}

// HasPrefix returns true if p starts with other.
func (p Path) HasPrefix(other Path) bool {
	if len(other) > len(p) {
		return false
	}
	for i := range other {
		if p[i] != other[i] {
			return false
		}
	}
	return true
}

// Ref returns a ref that represents p rooted at head.
func (p Path) Ref(head *ast.Term) (ref ast.Ref) {
	ref = make(ast.Ref, len(p)+1)
	ref[0] = head
	for i := range p {
		idx, err := strconv.ParseInt(p[i], 10, 64)
		if err == nil {
			ref[i+1] = ast.IntNumberTerm(int(idx))
		} else {
			ref[i+1] = ast.StringTerm(p[i])
		}
	}
	return ref
}

func (p Path) String() string {
	buf := make([]string, len(p))
	for i := range buf {
		buf[i] = url.PathEscape(p[i])
	}
	return "/" + strings.Join(buf, "/")
}

// MustParsePath returns a new Path for s. If s cannot be parsed, this function
// will panic. This is mostly for test purposes.
func MustParsePath(s string) Path {
	path, ok := ParsePath(s)
	if !ok {
		panic(s)
	}
	return path
}
<commit_msg>storage: Size the Path correctly in construction.<commit_after>// Copyright 2016 The OPA Authors.  All rights reserved.
// Use of this source code is governed by an Apache2
// license that can be found in the LICENSE file.

package storage

import (
	"fmt"
	"net/url"
	"strconv"
	"strings"

	"github.com/open-policy-agent/opa/ast"
)

// Path refers to a document in storage.
type Path []string

// ParsePath returns a new path for the given str.
func ParsePath(str string) (path Path, ok bool) {
	if len(str) == 0 {
		return nil, false
	}
	if str[0] != '/' {
		return nil, false
	}
	if len(str) == 1 {
		return Path{}, true
	}
	parts := strings.Split(str[1:], "/")
	return parts, true
}

// ParsePathEscaped returns a new path for the given escaped str.
func ParsePathEscaped(str string) (path Path, ok bool) {
	path, ok = ParsePath(str)
	if !ok {
		return
	}
	for i := range path {
		segment, err := url.PathUnescape(path[i])
		if err == nil {
			path[i] = segment
		}
	}
	return
}

// NewPathForRef returns a new path for the given ref.
func NewPathForRef(ref ast.Ref) (path Path, err error) {

	if len(ref) == 0 {
		return nil, fmt.Errorf("empty reference (indicates error in caller)")
	}

	if len(ref) == 1 {
		return Path{}, nil
	}

	path = make(Path, 0, len(ref)-1)

	for _, term := range ref[1:] {
		switch v := term.Value.(type) {
		case ast.String:
			path = append(path, string(v))
		case ast.Number:
			path = append(path, v.String())
		case ast.Boolean, ast.Null:
			return nil, &Error{
				Code:    NotFoundErr,
				Message: fmt.Sprintf("%v: does not exist", ref),
			}
		case ast.Array, ast.Object, ast.Set:
			return nil, fmt.Errorf("composites cannot be base document keys: %v", ref)
		default:
			return nil, fmt.Errorf("unresolved reference (indicates error in caller): %v", ref)
		}
	}

	return path, nil
}

// Compare performs lexigraphical comparison on p and other and returns -1 if p
// is less than other, 0 if p is equal to other, or 1 if p is greater than
// other.
func (p Path) Compare(other Path) (cmp int) {
	min := len(p)
	if len(other) < min {
		min = len(other)
	}
	for i := 0; i < min; i++ {
		if cmp := strings.Compare(p[i], other[i]); cmp != 0 {
			return cmp
		}
	}
	if len(p) < len(other) {
		return -1
	}
	if len(p) == len(other) {
		return 0
	}
	return 1
}

// Equal returns true if p is the same as other.
func (p Path) Equal(other Path) bool {
	return p.Compare(other) == 0
}

// HasPrefix returns true if p starts with other.
func (p Path) HasPrefix(other Path) bool {
	if len(other) > len(p) {
		return false
	}
	for i := range other {
		if p[i] != other[i] {
			return false
		}
	}
	return true
}

// Ref returns a ref that represents p rooted at head.
func (p Path) Ref(head *ast.Term) (ref ast.Ref) {
	ref = make(ast.Ref, len(p)+1)
	ref[0] = head
	for i := range p {
		idx, err := strconv.ParseInt(p[i], 10, 64)
		if err == nil {
			ref[i+1] = ast.IntNumberTerm(int(idx))
		} else {
			ref[i+1] = ast.StringTerm(p[i])
		}
	}
	return ref
}

func (p Path) String() string {
	buf := make([]string, len(p))
	for i := range buf {
		buf[i] = url.PathEscape(p[i])
	}
	return "/" + strings.Join(buf, "/")
}

// MustParsePath returns a new Path for s. If s cannot be parsed, this function
// will panic. This is mostly for test purposes.
func MustParsePath(s string) Path {
	path, ok := ParsePath(s)
	if !ok {
		panic(s)
	}
	return path
}
<|endoftext|>


<commit_before>package storage

import (
	"bytes"
	"crypto/hmac"
	"crypto/sha256"
	"encoding/base64"
	"encoding/xml"
	"fmt"
	"io"
	"io/ioutil"
	"net/http"
	"net/url"
	"reflect"
	"strconv"
	"strings"
	"time"
)

var (
	fixedTime = time.Date(2050, time.December, 20, 21, 55, 0, 0, time.FixedZone("GMT", -6))
)

func (c Client) computeHmac256(message string) string {
	h := hmac.New(sha256.New, c.accountKey)
	h.Write([]byte(message))
	return base64.StdEncoding.EncodeToString(h.Sum(nil))
}

func currentTimeRfc1123Formatted() string {
	return timeRfc1123Formatted(time.Now().UTC())
}

func timeRfc1123Formatted(t time.Time) string {
	return t.Format(http.TimeFormat)
}

func mergeParams(v1, v2 url.Values) url.Values {
	out := url.Values{}
	for k, v := range v1 {
		out[k] = v
	}
	for k, v := range v2 {
		vals, ok := out[k]
		if ok {
			vals = append(vals, v...)
			out[k] = vals
		} else {
			out[k] = v
		}
	}
	return out
}

func prepareBlockListRequest(blocks []Block) string {
	s := `<?xml version="1.0" encoding="utf-8"?><BlockList>`
	for _, v := range blocks {
		s += fmt.Sprintf("<%s>%s</%s>", v.Status, v.ID, v.Status)
	}
	s += `</BlockList>`
	return s
}

func xmlUnmarshal(body io.Reader, v interface{}) error {
	data, err := ioutil.ReadAll(body)
	if err != nil {
		return err
	}
	return xml.Unmarshal(data, v)
}

func xmlMarshal(v interface{}) (io.Reader, int, error) {
	b, err := xml.Marshal(v)
	if err != nil {
		return nil, 0, err
	}
	return bytes.NewReader(b), len(b), nil
}

func headersFromStruct(v interface{}) map[string]string {
	headers := make(map[string]string)
	value := reflect.ValueOf(v)
	for i := 0; i < value.NumField(); i++ {
		key := value.Type().Field(i).Tag.Get("header")
		if key != "" {
			reflectedValue := reflect.Indirect(value.Field(i))
			var val string
			if reflectedValue.IsValid() {
				switch reflectedValue.Type() {
				case reflect.TypeOf(fixedTime):
					val = timeRfc1123Formatted(reflectedValue.Interface().(time.Time))
				case reflect.TypeOf(uint64(0)), reflect.TypeOf(uint(0)):
					val = strconv.FormatUint(reflectedValue.Uint(), 10)
				case reflect.TypeOf(int(0)):
					val = strconv.FormatInt(reflectedValue.Int(), 10)
				default:
					val = reflectedValue.String()
				}
			}
			if val != "" {
				headers[key] = val
			}
		}
	}
	return headers
}

// merges extraHeaders into headers and returns headers
func mergeHeaders(headers, extraHeaders map[string]string) map[string]string {
	for k, v := range extraHeaders {
		headers[k] = v
	}
	return headers
}

func addToHeaders(h map[string]string, key, value string) map[string]string {
	if value != "" {
		h[key] = value
	}
	return h
}

func addTimeToHeaders(h map[string]string, key string, value *time.Time) map[string]string {
	if value != nil {
		h = addToHeaders(h, key, timeRfc1123Formatted(*value))
	}
	return h
}

func addTimeout(params url.Values, timeout uint) url.Values {
	if timeout > 0 {
		params.Add("timeout", fmt.Sprintf("%v", timeout))
	}
	return params
}

func addSnapshot(params url.Values, snapshot *time.Time) url.Values {
	if snapshot != nil {
		params.Add("snapshot", timeRfc1123Formatted(*snapshot))
	}
	return params
}

func getTimeFromHeaders(h http.Header, key string) (*time.Time, error) {
	var out time.Time
	var err error
	outStr := h.Get(key)
	if outStr != "" {
		out, err = time.Parse(time.RFC1123, outStr)
		if err != nil {
			return nil, err
		}
	}
	return &out, nil
}

// TimeRFC1123 is an alias for time.Time needed for custom Unmarshalling
type TimeRFC1123 time.Time

// UnmarshalXML is a custom unmarshaller that overrides the default time unmarshal which uses a different time layout.
func (t *TimeRFC1123) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error {
	var value string
	d.DecodeElement(&value, &start)
	parse, err := time.Parse(time.RFC1123, value)
	if err != nil {
		return err
	}
	*t = TimeRFC1123(parse)
	return nil
}

// returns a map of custom metadata values from the specified HTTP header
func getMetadataFromHeaders(header http.Header) map[string]string {
	metadata := make(map[string]string)
	for k, v := range header {
		// Can't trust CanonicalHeaderKey() to munge case
		// reliably. "_" is allowed in identifiers:
		// https://msdn.microsoft.com/en-us/library/azure/dd179414.aspx
		// https://msdn.microsoft.com/library/aa664670(VS.71).aspx
		// http://tools.ietf.org/html/rfc7230#section-3.2
		// ...but "_" is considered invalid by
		// CanonicalMIMEHeaderKey in
		// https://golang.org/src/net/textproto/reader.go?s=14615:14659#L542
		// so k can be "X-Ms-Meta-Lol" or "x-ms-meta-lol_rofl".
		k = strings.ToLower(k)
		if len(v) == 0 || !strings.HasPrefix(k, strings.ToLower(userDefinedMetadataHeaderPrefix)) {
			continue
		}
		// metadata["lol"] = content of the last X-Ms-Meta-Lol header
		k = k[len(userDefinedMetadataHeaderPrefix):]
		metadata[k] = v[len(v)-1]
	}

	if len(metadata) == 0 {
		return nil
	}

	return metadata
}
<commit_msg>Fix Snapshot parameter format (#684)<commit_after>package storage

import (
	"bytes"
	"crypto/hmac"
	"crypto/sha256"
	"encoding/base64"
	"encoding/xml"
	"fmt"
	"io"
	"io/ioutil"
	"net/http"
	"net/url"
	"reflect"
	"strconv"
	"strings"
	"time"
)

var (
	fixedTime = time.Date(2050, time.December, 20, 21, 55, 0, 0, time.FixedZone("GMT", -6))
)

func (c Client) computeHmac256(message string) string {
	h := hmac.New(sha256.New, c.accountKey)
	h.Write([]byte(message))
	return base64.StdEncoding.EncodeToString(h.Sum(nil))
}

func currentTimeRfc1123Formatted() string {
	return timeRfc1123Formatted(time.Now().UTC())
}

func timeRfc1123Formatted(t time.Time) string {
	return t.Format(http.TimeFormat)
}

func mergeParams(v1, v2 url.Values) url.Values {
	out := url.Values{}
	for k, v := range v1 {
		out[k] = v
	}
	for k, v := range v2 {
		vals, ok := out[k]
		if ok {
			vals = append(vals, v...)
			out[k] = vals
		} else {
			out[k] = v
		}
	}
	return out
}

func prepareBlockListRequest(blocks []Block) string {
	s := `<?xml version="1.0" encoding="utf-8"?><BlockList>`
	for _, v := range blocks {
		s += fmt.Sprintf("<%s>%s</%s>", v.Status, v.ID, v.Status)
	}
	s += `</BlockList>`
	return s
}

func xmlUnmarshal(body io.Reader, v interface{}) error {
	data, err := ioutil.ReadAll(body)
	if err != nil {
		return err
	}
	return xml.Unmarshal(data, v)
}

func xmlMarshal(v interface{}) (io.Reader, int, error) {
	b, err := xml.Marshal(v)
	if err != nil {
		return nil, 0, err
	}
	return bytes.NewReader(b), len(b), nil
}

func headersFromStruct(v interface{}) map[string]string {
	headers := make(map[string]string)
	value := reflect.ValueOf(v)
	for i := 0; i < value.NumField(); i++ {
		key := value.Type().Field(i).Tag.Get("header")
		if key != "" {
			reflectedValue := reflect.Indirect(value.Field(i))
			var val string
			if reflectedValue.IsValid() {
				switch reflectedValue.Type() {
				case reflect.TypeOf(fixedTime):
					val = timeRfc1123Formatted(reflectedValue.Interface().(time.Time))
				case reflect.TypeOf(uint64(0)), reflect.TypeOf(uint(0)):
					val = strconv.FormatUint(reflectedValue.Uint(), 10)
				case reflect.TypeOf(int(0)):
					val = strconv.FormatInt(reflectedValue.Int(), 10)
				default:
					val = reflectedValue.String()
				}
			}
			if val != "" {
				headers[key] = val
			}
		}
	}
	return headers
}

// merges extraHeaders into headers and returns headers
func mergeHeaders(headers, extraHeaders map[string]string) map[string]string {
	for k, v := range extraHeaders {
		headers[k] = v
	}
	return headers
}

func addToHeaders(h map[string]string, key, value string) map[string]string {
	if value != "" {
		h[key] = value
	}
	return h
}

func addTimeToHeaders(h map[string]string, key string, value *time.Time) map[string]string {
	if value != nil {
		h = addToHeaders(h, key, timeRfc1123Formatted(*value))
	}
	return h
}

func addTimeout(params url.Values, timeout uint) url.Values {
	if timeout > 0 {
		params.Add("timeout", fmt.Sprintf("%v", timeout))
	}
	return params
}

func addSnapshot(params url.Values, snapshot *time.Time) url.Values {
	if snapshot != nil {
		params.Add("snapshot", snapshot.Format("2006-01-02T15:04:05.0000000Z"))
	}
	return params
}

func getTimeFromHeaders(h http.Header, key string) (*time.Time, error) {
	var out time.Time
	var err error
	outStr := h.Get(key)
	if outStr != "" {
		out, err = time.Parse(time.RFC1123, outStr)
		if err != nil {
			return nil, err
		}
	}
	return &out, nil
}

// TimeRFC1123 is an alias for time.Time needed for custom Unmarshalling
type TimeRFC1123 time.Time

// UnmarshalXML is a custom unmarshaller that overrides the default time unmarshal which uses a different time layout.
func (t *TimeRFC1123) UnmarshalXML(d *xml.Decoder, start xml.StartElement) error {
	var value string
	d.DecodeElement(&value, &start)
	parse, err := time.Parse(time.RFC1123, value)
	if err != nil {
		return err
	}
	*t = TimeRFC1123(parse)
	return nil
}

// returns a map of custom metadata values from the specified HTTP header
func getMetadataFromHeaders(header http.Header) map[string]string {
	metadata := make(map[string]string)
	for k, v := range header {
		// Can't trust CanonicalHeaderKey() to munge case
		// reliably. "_" is allowed in identifiers:
		// https://msdn.microsoft.com/en-us/library/azure/dd179414.aspx
		// https://msdn.microsoft.com/library/aa664670(VS.71).aspx
		// http://tools.ietf.org/html/rfc7230#section-3.2
		// ...but "_" is considered invalid by
		// CanonicalMIMEHeaderKey in
		// https://golang.org/src/net/textproto/reader.go?s=14615:14659#L542
		// so k can be "X-Ms-Meta-Lol" or "x-ms-meta-lol_rofl".
		k = strings.ToLower(k)
		if len(v) == 0 || !strings.HasPrefix(k, strings.ToLower(userDefinedMetadataHeaderPrefix)) {
			continue
		}
		// metadata["lol"] = content of the last X-Ms-Meta-Lol header
		k = k[len(userDefinedMetadataHeaderPrefix):]
		metadata[k] = v[len(v)-1]
	}

	if len(metadata) == 0 {
		return nil
	}

	return metadata
}
<|endoftext|>


<commit_before>// Copyright 2022 The Oto Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package oto

import (
	"errors"
	"fmt"
	"reflect"
	"runtime"
	"sync"
	"time"
	"unsafe"

	"golang.org/x/sys/windows"

	"github.com/hajimehoshi/oto/v2/internal/mux"
)

type comThread struct {
	funcCh chan func()
}

func newCOMThread() (*comThread, error) {
	funcCh := make(chan func())
	errCh := make(chan error)
	go func() {
		runtime.LockOSThread()
		defer runtime.UnlockOSThread()

		if err := _CoInitializeEx(nil, _COINIT_MULTITHREADED); err != nil {
			errCh <- err
		}
		defer _CoUninitialize()

		close(errCh)

		for f := range funcCh {
			f()
		}
	}()

	if err := <-errCh; err != nil {
		return nil, err
	}

	return &comThread{
		funcCh: funcCh,
	}, nil
}

func (c *comThread) Run(f func()) {
	ch := make(chan struct{})
	c.funcCh <- func() {
		f()
		close(ch)
	}
	<-ch
}

type wasapiContext struct {
	sampleRate   int
	channelCount int
	mux          *mux.Mux

	comThread *comThread
	err       atomicError
	suspended bool

	sampleReadyEvent windows.Handle
	client           *_IAudioClient2
	mixFormat        *_WAVEFORMATEXTENSIBLE
	bufferFrames     uint32
	renderClient     *_IAudioRenderClient
	currentDeviceID  string
	enumerator       *_IMMDeviceEnumerator

	buf []float32

	m sync.Mutex
}

var errDeviceSwitched = errors.New("oto: device switched")

func newWASAPIContext(sampleRate, channelCount int, mux *mux.Mux) (context *wasapiContext, ferr error) {
	t, err := newCOMThread()
	if err != nil {
		return nil, err
	}

	c := &wasapiContext{
		sampleRate:   sampleRate,
		channelCount: channelCount,
		mux:          mux,
		comThread:    t,
	}

	ev, err := windows.CreateEventEx(nil, nil, 0, windows.EVENT_ALL_ACCESS)
	if err != nil {
		return nil, err
	}
	defer func() {
		if ferr != nil {
			windows.CloseHandle(ev)
		}
	}()
	c.sampleReadyEvent = ev

	if err := c.start(); err != nil {
		return nil, err
	}

	return c, nil
}

func (c *wasapiContext) isDeviceSwitched() (bool, error) {
	// If the audio is suspended, do nothing.
	if c.isSuspended() {
		return false, nil
	}

	var switched bool
	var cerr error
	c.comThread.Run(func() {
		device, err := c.enumerator.GetDefaultAudioEndPoint(eRender, eConsole)
		if err != nil {
			cerr = err
			return
		}
		defer device.Release()

		id, err := device.GetId()
		if err != nil {
			cerr = err
			return
		}

		if c.currentDeviceID == id {
			return
		}
		switched = true
	})

	return switched, cerr
}

func (c *wasapiContext) start() error {
	var cerr error
	c.comThread.Run(func() {
		if err := c.startOnCOMThread(); err != nil {
			cerr = err
			return
		}
	})
	if cerr != nil {
		return cerr
	}

	go func() {
		if err := c.loop(); err != nil {
			if !errors.Is(err, _AUDCLNT_E_DEVICE_INVALIDATED) && errors.Is(err, _AUDCLNT_E_RESOURCES_INVALIDATED) && !errors.Is(err, errDeviceSwitched) {
				c.err.TryStore(err)
				return
			}

			// If the context is suspended, do not resume now.
			if c.isSuspended() {
				return
			}

			// Probably the driver is missing temporarily e.g. plugging out the headset.
			// Recreate the device.
			if err := c.start(); err != nil {
				c.err.TryStore(err)
				return
			}
			return
		}
	}()

	return nil
}

func (c *wasapiContext) startOnCOMThread() (ferr error) {
	if c.enumerator == nil {
		e, err := _CoCreateInstance(&uuidMMDeviceEnumerator, nil, uint32(_CLSCTX_ALL), &uuidIMMDeviceEnumerator)
		if err != nil {
			return err
		}
		c.enumerator = (*_IMMDeviceEnumerator)(e)
		defer func() {
			if ferr != nil {
				c.enumerator.Release()
				c.enumerator = nil
			}
		}()
	}

	device, err := c.enumerator.GetDefaultAudioEndPoint(eRender, eConsole)
	if err != nil {
		return err
	}
	defer device.Release()

	id, err := device.GetId()
	if err != nil {
		return err
	}
	c.currentDeviceID = id

	if c.client != nil {
		c.client.Release()
		c.client = nil
	}

	client, err := device.Activate(&uuidIAudioClient2, uint32(_CLSCTX_ALL), nil)
	if err != nil {
		return err
	}
	c.client = (*_IAudioClient2)(client)

	if err := c.client.SetClientProperties(&_AudioClientProperties{
		cbSize:     uint32(unsafe.Sizeof(_AudioClientProperties{})),
		bIsOffload: 0,                    // false
		eCategory:  _AudioCategory_Other, // In the example, AudioCategory_ForegroundOnlyMedia was used, but this value is deprecated.
	}); err != nil {
		return err
	}

	// Check the format is supported by WASAPI.
	// Stereo with 48000 [Hz] is likely supported, but mono and/or other sample rates are unlikely supported.
	// Fallback to WinMM in this case anyway.
	const bitsPerSample = 32
	nBlockAlign := c.channelCount * bitsPerSample / 8
	var channelMask uint32
	switch c.channelCount {
	case 1:
		channelMask = _SPEAKER_FRONT_CENTER
	case 2:
		channelMask = _SPEAKER_FRONT_LEFT | _SPEAKER_FRONT_RIGHT
	}
	f := &_WAVEFORMATEXTENSIBLE{
		wFormatTag:      _WAVE_FORMAT_EXTENSIBLE,
		nChannels:       uint16(c.channelCount),
		nSamplesPerSec:  uint32(c.sampleRate),
		nAvgBytesPerSec: uint32(c.sampleRate * nBlockAlign),
		nBlockAlign:     uint16(nBlockAlign),
		wBitsPerSample:  bitsPerSample,
		cbSize:          0x16,
		Samples:         bitsPerSample,
		dwChannelMask:   channelMask,
		SubFormat:       _KSDATAFORMAT_SUBTYPE_IEEE_FLOAT,
	}
	closest, err := c.client.IsFormatSupported(_AUDCLNT_SHAREMODE_SHARED, f)
	if err != nil {
		return err
	}
	if closest != nil {
		return fmt.Errorf("oto: the specified format is not supported (there is the closest format instead)")
	}
	c.mixFormat = f

	if err := c.client.Initialize(_AUDCLNT_SHAREMODE_SHARED,
		_AUDCLNT_STREAMFLAGS_EVENTCALLBACK|_AUDCLNT_STREAMFLAGS_NOPERSIST,
		0, 0, c.mixFormat, nil); err != nil {
		return err
	}

	frames, err := c.client.GetBufferSize()
	if err != nil {
		return err
	}
	c.bufferFrames = frames

	if c.renderClient != nil {
		c.renderClient.Release()
		c.renderClient = nil
	}

	renderClient, err := c.client.GetService(&uuidIAudioRenderClient)
	if err != nil {
		return err
	}
	c.renderClient = (*_IAudioRenderClient)(renderClient)

	if err := c.client.SetEventHandle(c.sampleReadyEvent); err != nil {
		return err
	}

	// TODO: Should some errors be allowed? See WASAPIManager.cpp in the official example SimpleWASAPIPlaySound.

	if err := c.client.Start(); err != nil {
		return err
	}

	return nil
}

func (c *wasapiContext) loop() error {
	runtime.LockOSThread()
	defer runtime.UnlockOSThread()

	if err := _CoInitializeEx(nil, _COINIT_MULTITHREADED); err != nil {
		c.client.Stop()
		return err
	}
	defer _CoUninitialize()

	if err := c.loopOnRenderThread(); err != nil {
		c.client.Stop()
		return err
	}

	return nil
}

func (c *wasapiContext) loopOnRenderThread() error {
	last := time.Now()
	for {
		evt, err := windows.WaitForSingleObject(c.sampleReadyEvent, windows.INFINITE)
		if err != nil {
			return err
		}
		if evt != windows.WAIT_OBJECT_0 {
			return fmt.Errorf("oto: WaitForSingleObject failed: returned value: %d", evt)
		}

		if err := c.writeOnRenderThread(); err != nil {
			return err
		}

		// Checking the current default audio device might be an expensive operation.
		// Check this repeatedly but with some time interval.
		if now := time.Now(); now.Sub(last) >= 500*time.Millisecond {
			switched, err := c.isDeviceSwitched()
			if err != nil {
				return err
			}
			if switched {
				return errDeviceSwitched
			}
			last = now
		}
	}
}

func (c *wasapiContext) writeOnRenderThread() error {
	c.m.Lock()
	defer c.m.Unlock()

	paddingFrames, err := c.client.GetCurrentPadding()
	if err != nil {
		return err
	}

	frames := c.bufferFrames - paddingFrames
	if frames <= 0 {
		return nil
	}

	// Get the destination buffer.
	dstBuf, err := c.renderClient.GetBuffer(frames)
	if err != nil {
		return err
	}

	// Calculate the buffer size.
	buflen := int(frames) * c.channelCount
	if cap(c.buf) < buflen {
		c.buf = make([]float32, buflen)
	} else {
		c.buf = c.buf[:buflen]
	}

	// Read the buffer from the players.
	c.mux.ReadFloat32s(c.buf)

	// Copy the read buf to the destination buffer.
	var dst []float32
	h := (*reflect.SliceHeader)(unsafe.Pointer(&dst))
	h.Data = uintptr(unsafe.Pointer(dstBuf))
	h.Len = buflen
	h.Cap = buflen
	copy(dst, c.buf)

	// Release the buffer.
	if err := c.renderClient.ReleaseBuffer(frames, 0); err != nil {
		return err
	}

	c.buf = c.buf[:0]
	return nil
}

func (c *wasapiContext) Suspend() error {
	var cerr error
	c.comThread.Run(func() {
		c.m.Lock()
		defer c.m.Unlock()
		c.suspended = true

		if err := c.client.Stop(); err != nil {
			cerr = err
			return
		}
	})
	return cerr
}

func (c *wasapiContext) Resume() error {
	var cerr error
	c.comThread.Run(func() {
		c.m.Lock()
		defer c.m.Unlock()
		c.suspended = false

		if err := c.client.Start(); err != nil {
			if !errors.Is(err, _AUDCLNT_E_DEVICE_INVALIDATED) && errors.Is(err, _AUDCLNT_E_RESOURCES_INVALIDATED) {
				cerr = err
				return
			}

			go func() {
				// Probably the driver is missing temporarily e.g. plugging out the headset.
				// Recreate the device.
				if err := c.start(); err != nil {
					c.err.TryStore(err)
					return
				}
			}()
			return
		}
	})
	return cerr
}

func (c *wasapiContext) isSuspended() bool {
	c.m.Lock()
	defer c.m.Unlock()
	return c.suspended
}

func (c *wasapiContext) Err() error {
	return c.err.Load()
}
<commit_msg>windows: bug fix: wrong error check<commit_after>// Copyright 2022 The Oto Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package oto

import (
	"errors"
	"fmt"
	"reflect"
	"runtime"
	"sync"
	"time"
	"unsafe"

	"golang.org/x/sys/windows"

	"github.com/hajimehoshi/oto/v2/internal/mux"
)

type comThread struct {
	funcCh chan func()
}

func newCOMThread() (*comThread, error) {
	funcCh := make(chan func())
	errCh := make(chan error)
	go func() {
		runtime.LockOSThread()
		defer runtime.UnlockOSThread()

		if err := _CoInitializeEx(nil, _COINIT_MULTITHREADED); err != nil {
			errCh <- err
		}
		defer _CoUninitialize()

		close(errCh)

		for f := range funcCh {
			f()
		}
	}()

	if err := <-errCh; err != nil {
		return nil, err
	}

	return &comThread{
		funcCh: funcCh,
	}, nil
}

func (c *comThread) Run(f func()) {
	ch := make(chan struct{})
	c.funcCh <- func() {
		f()
		close(ch)
	}
	<-ch
}

type wasapiContext struct {
	sampleRate   int
	channelCount int
	mux          *mux.Mux

	comThread *comThread
	err       atomicError
	suspended bool

	sampleReadyEvent windows.Handle
	client           *_IAudioClient2
	mixFormat        *_WAVEFORMATEXTENSIBLE
	bufferFrames     uint32
	renderClient     *_IAudioRenderClient
	currentDeviceID  string
	enumerator       *_IMMDeviceEnumerator

	buf []float32

	m sync.Mutex
}

var errDeviceSwitched = errors.New("oto: device switched")

func newWASAPIContext(sampleRate, channelCount int, mux *mux.Mux) (context *wasapiContext, ferr error) {
	t, err := newCOMThread()
	if err != nil {
		return nil, err
	}

	c := &wasapiContext{
		sampleRate:   sampleRate,
		channelCount: channelCount,
		mux:          mux,
		comThread:    t,
	}

	ev, err := windows.CreateEventEx(nil, nil, 0, windows.EVENT_ALL_ACCESS)
	if err != nil {
		return nil, err
	}
	defer func() {
		if ferr != nil {
			windows.CloseHandle(ev)
		}
	}()
	c.sampleReadyEvent = ev

	if err := c.start(); err != nil {
		return nil, err
	}

	return c, nil
}

func (c *wasapiContext) isDeviceSwitched() (bool, error) {
	// If the audio is suspended, do nothing.
	if c.isSuspended() {
		return false, nil
	}

	var switched bool
	var cerr error
	c.comThread.Run(func() {
		device, err := c.enumerator.GetDefaultAudioEndPoint(eRender, eConsole)
		if err != nil {
			cerr = err
			return
		}
		defer device.Release()

		id, err := device.GetId()
		if err != nil {
			cerr = err
			return
		}

		if c.currentDeviceID == id {
			return
		}
		switched = true
	})

	return switched, cerr
}

func (c *wasapiContext) start() error {
	var cerr error
	c.comThread.Run(func() {
		if err := c.startOnCOMThread(); err != nil {
			cerr = err
			return
		}
	})
	if cerr != nil {
		return cerr
	}

	go func() {
		if err := c.loop(); err != nil {
			if !errors.Is(err, _AUDCLNT_E_DEVICE_INVALIDATED) && !errors.Is(err, _AUDCLNT_E_RESOURCES_INVALIDATED) && !errors.Is(err, errDeviceSwitched) {
				c.err.TryStore(err)
				return
			}

			// If the context is suspended, do not resume now.
			if c.isSuspended() {
				return
			}

			// Probably the driver is missing temporarily e.g. plugging out the headset.
			// Recreate the device.
			if err := c.start(); err != nil {
				c.err.TryStore(err)
				return
			}
			return
		}
	}()

	return nil
}

func (c *wasapiContext) startOnCOMThread() (ferr error) {
	if c.enumerator == nil {
		e, err := _CoCreateInstance(&uuidMMDeviceEnumerator, nil, uint32(_CLSCTX_ALL), &uuidIMMDeviceEnumerator)
		if err != nil {
			return err
		}
		c.enumerator = (*_IMMDeviceEnumerator)(e)
		defer func() {
			if ferr != nil {
				c.enumerator.Release()
				c.enumerator = nil
			}
		}()
	}

	device, err := c.enumerator.GetDefaultAudioEndPoint(eRender, eConsole)
	if err != nil {
		return err
	}
	defer device.Release()

	id, err := device.GetId()
	if err != nil {
		return err
	}
	c.currentDeviceID = id

	if c.client != nil {
		c.client.Release()
		c.client = nil
	}

	client, err := device.Activate(&uuidIAudioClient2, uint32(_CLSCTX_ALL), nil)
	if err != nil {
		return err
	}
	c.client = (*_IAudioClient2)(client)

	if err := c.client.SetClientProperties(&_AudioClientProperties{
		cbSize:     uint32(unsafe.Sizeof(_AudioClientProperties{})),
		bIsOffload: 0,                    // false
		eCategory:  _AudioCategory_Other, // In the example, AudioCategory_ForegroundOnlyMedia was used, but this value is deprecated.
	}); err != nil {
		return err
	}

	// Check the format is supported by WASAPI.
	// Stereo with 48000 [Hz] is likely supported, but mono and/or other sample rates are unlikely supported.
	// Fallback to WinMM in this case anyway.
	const bitsPerSample = 32
	nBlockAlign := c.channelCount * bitsPerSample / 8
	var channelMask uint32
	switch c.channelCount {
	case 1:
		channelMask = _SPEAKER_FRONT_CENTER
	case 2:
		channelMask = _SPEAKER_FRONT_LEFT | _SPEAKER_FRONT_RIGHT
	}
	f := &_WAVEFORMATEXTENSIBLE{
		wFormatTag:      _WAVE_FORMAT_EXTENSIBLE,
		nChannels:       uint16(c.channelCount),
		nSamplesPerSec:  uint32(c.sampleRate),
		nAvgBytesPerSec: uint32(c.sampleRate * nBlockAlign),
		nBlockAlign:     uint16(nBlockAlign),
		wBitsPerSample:  bitsPerSample,
		cbSize:          0x16,
		Samples:         bitsPerSample,
		dwChannelMask:   channelMask,
		SubFormat:       _KSDATAFORMAT_SUBTYPE_IEEE_FLOAT,
	}
	closest, err := c.client.IsFormatSupported(_AUDCLNT_SHAREMODE_SHARED, f)
	if err != nil {
		return err
	}
	if closest != nil {
		return fmt.Errorf("oto: the specified format is not supported (there is the closest format instead)")
	}
	c.mixFormat = f

	if err := c.client.Initialize(_AUDCLNT_SHAREMODE_SHARED,
		_AUDCLNT_STREAMFLAGS_EVENTCALLBACK|_AUDCLNT_STREAMFLAGS_NOPERSIST,
		0, 0, c.mixFormat, nil); err != nil {
		return err
	}

	frames, err := c.client.GetBufferSize()
	if err != nil {
		return err
	}
	c.bufferFrames = frames

	if c.renderClient != nil {
		c.renderClient.Release()
		c.renderClient = nil
	}

	renderClient, err := c.client.GetService(&uuidIAudioRenderClient)
	if err != nil {
		return err
	}
	c.renderClient = (*_IAudioRenderClient)(renderClient)

	if err := c.client.SetEventHandle(c.sampleReadyEvent); err != nil {
		return err
	}

	// TODO: Should some errors be allowed? See WASAPIManager.cpp in the official example SimpleWASAPIPlaySound.

	if err := c.client.Start(); err != nil {
		return err
	}

	return nil
}

func (c *wasapiContext) loop() error {
	runtime.LockOSThread()
	defer runtime.UnlockOSThread()

	if err := _CoInitializeEx(nil, _COINIT_MULTITHREADED); err != nil {
		c.client.Stop()
		return err
	}
	defer _CoUninitialize()

	if err := c.loopOnRenderThread(); err != nil {
		c.client.Stop()
		return err
	}

	return nil
}

func (c *wasapiContext) loopOnRenderThread() error {
	last := time.Now()
	for {
		evt, err := windows.WaitForSingleObject(c.sampleReadyEvent, windows.INFINITE)
		if err != nil {
			return err
		}
		if evt != windows.WAIT_OBJECT_0 {
			return fmt.Errorf("oto: WaitForSingleObject failed: returned value: %d", evt)
		}

		if err := c.writeOnRenderThread(); err != nil {
			return err
		}

		// Checking the current default audio device might be an expensive operation.
		// Check this repeatedly but with some time interval.
		if now := time.Now(); now.Sub(last) >= 500*time.Millisecond {
			switched, err := c.isDeviceSwitched()
			if err != nil {
				return err
			}
			if switched {
				return errDeviceSwitched
			}
			last = now
		}
	}
}

func (c *wasapiContext) writeOnRenderThread() error {
	c.m.Lock()
	defer c.m.Unlock()

	paddingFrames, err := c.client.GetCurrentPadding()
	if err != nil {
		return err
	}

	frames := c.bufferFrames - paddingFrames
	if frames <= 0 {
		return nil
	}

	// Get the destination buffer.
	dstBuf, err := c.renderClient.GetBuffer(frames)
	if err != nil {
		return err
	}

	// Calculate the buffer size.
	buflen := int(frames) * c.channelCount
	if cap(c.buf) < buflen {
		c.buf = make([]float32, buflen)
	} else {
		c.buf = c.buf[:buflen]
	}

	// Read the buffer from the players.
	c.mux.ReadFloat32s(c.buf)

	// Copy the read buf to the destination buffer.
	var dst []float32
	h := (*reflect.SliceHeader)(unsafe.Pointer(&dst))
	h.Data = uintptr(unsafe.Pointer(dstBuf))
	h.Len = buflen
	h.Cap = buflen
	copy(dst, c.buf)

	// Release the buffer.
	if err := c.renderClient.ReleaseBuffer(frames, 0); err != nil {
		return err
	}

	c.buf = c.buf[:0]
	return nil
}

func (c *wasapiContext) Suspend() error {
	var cerr error
	c.comThread.Run(func() {
		c.m.Lock()
		defer c.m.Unlock()
		c.suspended = true

		if err := c.client.Stop(); err != nil {
			cerr = err
			return
		}
	})
	return cerr
}

func (c *wasapiContext) Resume() error {
	var cerr error
	c.comThread.Run(func() {
		c.m.Lock()
		defer c.m.Unlock()
		c.suspended = false

		if err := c.client.Start(); err != nil {
			if !errors.Is(err, _AUDCLNT_E_DEVICE_INVALIDATED) && !errors.Is(err, _AUDCLNT_E_RESOURCES_INVALIDATED) {
				cerr = err
				return
			}

			go func() {
				// Probably the driver is missing temporarily e.g. plugging out the headset.
				// Recreate the device.
				if err := c.start(); err != nil {
					c.err.TryStore(err)
					return
				}
			}()
			return
		}
	})
	return cerr
}

func (c *wasapiContext) isSuspended() bool {
	c.m.Lock()
	defer c.m.Unlock()
	return c.suspended
}

func (c *wasapiContext) Err() error {
	return c.err.Load()
}
<|endoftext|>


<commit_before>// Copyright 2015 CoreOS, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package activation

import (
	"net"
)

// Listeners returns a slice containing a net.Listener for each matching socket type
// passed to this process.
//
// The order of the file descriptors is preserved in the returned slice.
// Nil values are used to fill any gaps. For example if systemd were to return file descriptors
// corresponding with "udp, tcp, tcp", then the slice would contain {nil, net.Listener, net.Listener}
func Listeners(unsetEnv bool) ([]net.Listener, error) {
	files := Files(unsetEnv)
	listeners := make([]net.Listener, len(files))

	for i, f := range files {
		if pc, err := net.FileListener(f); err == nil {
			listeners[i] = pc
		}
	}
	return listeners, nil
}
<commit_msg>Added possibility to use TLS with systemd socket activation<commit_after>// Copyright 2015 CoreOS, Inc.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package activation

import (
	"crypto/tls"
	"net"
)

// Listeners returns a slice containing a net.Listener for each matching socket type
// passed to this process.
//
// The order of the file descriptors is preserved in the returned slice.
// Nil values are used to fill any gaps. For example if systemd were to return file descriptors
// corresponding with "udp, tcp, tcp", then the slice would contain {nil, net.Listener, net.Listener}
func Listeners(unsetEnv bool) ([]net.Listener, error) {
	files := Files(unsetEnv)
	listeners := make([]net.Listener, len(files))

	for i, f := range files {
		if pc, err := net.FileListener(f); err == nil {
			listeners[i] = pc
		}
	}
	return listeners, nil
}

// TLSListeners returns a slice containing a net.listener for each matching TCP socket type
// passed to this process.
// It uses default Listeners func and forces TCP sockets handlers to use TLS based on tlsConfig.
func TLSListeners(unsetEnv bool, tlsConfig *tls.Config) ([]net.Listener, error) {
	listeners, err := Listeners(unsetEnv)

	if listeners == nil || err != nil {
		return nil, err
	}

	if tlsConfig != nil && err == nil {
		tlsConfig.NextProtos = []string{"http/1.1"}

		for i, l := range listeners {
			// Activate TLS only for TCP sockets
			if l.Addr().Network() == "tcp" {
				listeners[i] = tls.NewListener(l, tlsConfig)
			}
		}
	}

	return listeners, err
}
<|endoftext|>


<commit_before>package hyperv

import (
	"fmt"
	"net"
	"os"
	"time"

	"github.com/docker/machine/libmachine/drivers"
	"github.com/docker/machine/libmachine/log"
	"github.com/docker/machine/libmachine/mcnflag"
	"github.com/docker/machine/libmachine/mcnutils"
	"github.com/docker/machine/libmachine/ssh"
	"github.com/docker/machine/libmachine/state"
)

type Driver struct {
	*drivers.BaseDriver
	Boot2DockerURL string
	VSwitch        string
	DiskSize       int
	MemSize        int
	CPU            int
	MacAddr        string
	VLanID         int
}

const (
	defaultDiskSize = 20000
	defaultMemory   = 1024
	defaultCPU      = 1
	defaultVLanID   = 0
)

// NewDriver creates a new Hyper-v driver with default settings.
func NewDriver(hostName, storePath string) *Driver {
	return &Driver{
		DiskSize: defaultDiskSize,
		MemSize:  defaultMemory,
		CPU:      defaultCPU,
		BaseDriver: &drivers.BaseDriver{
			MachineName: hostName,
			StorePath:   storePath,
		},
	}
}

// GetCreateFlags registers the flags this driver adds to
// "docker hosts create"
func (d *Driver) GetCreateFlags() []mcnflag.Flag {
	return []mcnflag.Flag{
		mcnflag.StringFlag{
			Name:   "hyperv-boot2docker-url",
			Usage:  "URL of the boot2docker ISO. Defaults to the latest available version.",
			EnvVar: "HYPERV_BOOT2DOCKER_URL",
		},
		mcnflag.StringFlag{
			Name:   "hyperv-virtual-switch",
			Usage:  "Virtual switch name. Defaults to first found.",
			EnvVar: "HYPERV_VIRTUAL_SWITCH",
		},
		mcnflag.IntFlag{
			Name:   "hyperv-disk-size",
			Usage:  "Maximum size of dynamically expanding disk in MB.",
			Value:  defaultDiskSize,
			EnvVar: "HYPERV_DISK_SIZE",
		},
		mcnflag.IntFlag{
			Name:   "hyperv-memory",
			Usage:  "Memory size for host in MB.",
			Value:  defaultMemory,
			EnvVar: "HYPERV_MEMORY",
		},
		mcnflag.IntFlag{
			Name:   "hyperv-cpu-count",
			Usage:  "number of CPUs for the machine",
			Value:  defaultCPU,
			EnvVar: "HYPERV_CPU_COUNT",
		},
		mcnflag.StringFlag{
			Name:   "hyperv-static-macaddress",
			Usage:  "Hyper-V network adapter's static MAC address.",
			EnvVar: "HYPERV_STATIC_MACADDRESS",
		},
		mcnflag.IntFlag{
			Name:   "hyperv-vlan-id",
			Usage:  "Hyper-V network adapter's VLAN ID if any",
			Value:  defaultVLanID,
			EnvVar: "HYPERV_VLAN_ID",
		},
	}
}

func (d *Driver) SetConfigFromFlags(flags drivers.DriverOptions) error {
	d.Boot2DockerURL = flags.String("hyperv-boot2docker-url")
	d.VSwitch = flags.String("hyperv-virtual-switch")
	d.DiskSize = flags.Int("hyperv-disk-size")
	d.MemSize = flags.Int("hyperv-memory")
	d.CPU = flags.Int("hyperv-cpu-count")
	d.MacAddr = flags.String("hyperv-static-macaddress")
	d.VLanID = flags.Int("hyperv-vlan-id")
	d.SSHUser = "docker"
	d.SetSwarmConfigFromFlags(flags)

	return nil
}

func (d *Driver) GetSSHHostname() (string, error) {
	return d.GetIP()
}

// DriverName returns the name of the driver
func (d *Driver) DriverName() string {
	return "hyperv"
}

func (d *Driver) GetURL() (string, error) {
	ip, err := d.GetIP()
	if err != nil {
		return "", err
	}

	if ip == "" {
		return "", nil
	}

	return fmt.Sprintf("tcp://%s", net.JoinHostPort(ip, "2376")), nil
}

func (d *Driver) GetState() (state.State, error) {
	stdout, err := cmdOut("(", "hyper-v\\Get-VM", d.MachineName, ").state")
	if err != nil {
		return state.None, fmt.Errorf("Failed to find the VM status")
	}

	resp := parseLines(stdout)
	if len(resp) < 1 {
		return state.None, nil
	}

	switch resp[0] {
	case "Running":
		return state.Running, nil
	case "Off":
		return state.Stopped, nil
	default:
		return state.None, nil
	}
}

// PreCreateCheck checks that the machine creation process can be started safely.
func (d *Driver) PreCreateCheck() error {
	// Check that powershell was found
	if powershell == "" {
		return ErrPowerShellNotFound
	}

	// Check that hyperv is installed
	if err := hypervAvailable(); err != nil {
		return err
	}

	// Check that the user is an Administrator
	isAdmin, err := isAdministrator()
	if err != nil {
		return err
	}
	if !isAdmin {
		return ErrNotAdministrator
	}

	// Check that there is a virtual switch already configured
	if _, err := d.chooseVirtualSwitch(); err != nil {
		return err
	}

	// Downloading boot2docker to cache should be done here to make sure
	// that a download failure will not leave a machine half created.
	b2dutils := mcnutils.NewB2dUtils(d.StorePath)
	err = b2dutils.UpdateISOCache(d.Boot2DockerURL)
	return err
}

func (d *Driver) Create() error {
	b2dutils := mcnutils.NewB2dUtils(d.StorePath)
	if err := b2dutils.CopyIsoToMachineDir(d.Boot2DockerURL, d.MachineName); err != nil {
		return err
	}

	log.Infof("Creating SSH key...")
	if err := ssh.GenerateSSHKey(d.GetSSHKeyPath()); err != nil {
		return err
	}

	log.Infof("Creating VM...")
	virtualSwitch, err := d.chooseVirtualSwitch()
	if err != nil {
		return err
	}

	log.Infof("Using switch %q", virtualSwitch)

	diskImage, err := d.generateDiskImage()
	if err != nil {
		return err
	}

	if err := cmd("hyper-v\\New-VM",
		d.MachineName,
		"-Path", fmt.Sprintf("'%s'", d.ResolveStorePath(".")),
		"-SwitchName", quote(virtualSwitch),
		"-MemoryStartupBytes", toMb(d.MemSize)); err != nil {
		return err
	}

	if d.CPU > 1 {
		if err := cmd("hyper-v\\Set-VMProcessor",
			d.MachineName,
			"-Count", fmt.Sprintf("%d", d.CPU)); err != nil {
			return err
		}
	}

	if d.MacAddr != "" {
		if err := cmd("hyper-v\\Set-VMNetworkAdapter",
			"-VMName", d.MachineName,
			"-StaticMacAddress", fmt.Sprintf("\"%s\"", d.MacAddr)); err != nil {
			return err
		}
	}

	if d.VLanID > 0 {
		if err := cmd("hyper-v\\Set-VMNetworkAdapterVlan",
			"-VMName", d.MachineName,
			"-Access",
			"-VlanId", fmt.Sprintf("%d", d.VLanID)); err != nil {
			return err
		}
	}

	if err := cmd("hyper-v\\Set-VMDvdDrive",
		"-VMName", d.MachineName,
		"-Path", quote(d.ResolveStorePath("boot2docker.iso"))); err != nil {
		return err
	}

	if err := cmd("hyper-v\\Add-VMHardDiskDrive",
		"-VMName", d.MachineName,
		"-Path", quote(diskImage)); err != nil {
		return err
	}

	log.Infof("Starting VM...")
	return d.Start()
}

func (d *Driver) chooseVirtualSwitch() (string, error) {
	if d.VSwitch == "" {
		// Default to the first external switche and in the process avoid DockerNAT
		stdout, err := cmdOut("(hyper-v\\Get-VMSwitch -SwitchType External).Name")
		if err != nil {
			return "", err
		}

		switches := parseLines(stdout)

		if len(switches) < 1 {
			return "", fmt.Errorf("no External vswitch found. A valid vswitch must be available for this command to run. Check https://docs.docker.com/machine/drivers/hyper-v/")
		}

		return switches[0], nil
	}

	stdout, err := cmdOut("(hyper-v\\Get-VMSwitch).Name")
	if err != nil {
		return "", err
	}

	switches := parseLines(stdout)

	found := false
	for _, name := range switches {
		if name == d.VSwitch {
			found = true
			break
		}
	}

	if !found {
		return "", fmt.Errorf("vswitch %q not found", d.VSwitch)
	}

	return d.VSwitch, nil
}

// waitForIP waits until the host has a valid IP
func (d *Driver) waitForIP() (string, error) {
	log.Infof("Waiting for host to start...")

	for {
		ip, _ := d.GetIP()
		if ip != "" {
			return ip, nil
		}

		time.Sleep(1 * time.Second)
	}
}

// waitStopped waits until the host is stopped
func (d *Driver) waitStopped() error {
	log.Infof("Waiting for host to stop...")

	for {
		s, err := d.GetState()
		if err != nil {
			return err
		}

		if s != state.Running {
			return nil
		}

		time.Sleep(1 * time.Second)
	}
}

// Start starts an host
func (d *Driver) Start() error {
	if err := cmd("hyper-v\\Start-VM", d.MachineName); err != nil {
		return err
	}

	ip, err := d.waitForIP()
	if err != nil {
		return err
	}

	d.IPAddress = ip

	return nil
}

// Stop stops an host
func (d *Driver) Stop() error {
	if err := cmd("hyper-v\\Stop-VM", d.MachineName); err != nil {
		return err
	}

	if err := d.waitStopped(); err != nil {
		return err
	}

	d.IPAddress = ""

	return nil
}

// Remove removes an host
func (d *Driver) Remove() error {
	s, err := d.GetState()
	if err != nil {
		return err
	}

	if s == state.Running {
		if err := d.Kill(); err != nil {
			return err
		}
	}

	return cmd("hyper-v\\Remove-VM", d.MachineName, "-Force")
}

// Restart stops and starts an host
func (d *Driver) Restart() error {
	err := d.Stop()
	if err != nil {
		return err
	}

	return d.Start()
}

// Kill force stops an host
func (d *Driver) Kill() error {
	if err := cmd("hyper-v\\Stop-VM", d.MachineName, "-TurnOff"); err != nil {
		return err
	}

	if err := d.waitStopped(); err != nil {
		return err
	}

	d.IPAddress = ""

	return nil
}

func (d *Driver) GetIP() (string, error) {
	s, err := d.GetState()
	if err != nil {
		return "", err
	}
	if s != state.Running {
		return "", drivers.ErrHostIsNotRunning
	}

	stdout, err := cmdOut("((", "hyper-v\\Get-VM", d.MachineName, ").networkadapters[0]).ipaddresses[0]")
	if err != nil {
		return "", err
	}

	resp := parseLines(stdout)
	if len(resp) < 1 {
		return "", fmt.Errorf("IP not found")
	}

	return resp[0], nil
}

func (d *Driver) publicSSHKeyPath() string {
	return d.GetSSHKeyPath() + ".pub"
}

// generateDiskImage creates a small fixed vhd, put the tar in, convert to dynamic, then resize
func (d *Driver) generateDiskImage() (string, error) {
	diskImage := d.ResolveStorePath("disk.vhd")
	fixed := d.ResolveStorePath("fixed.vhd")

	// Resizing vhds requires administrator privileges
	// incase the user is only a hyper-v admin then create the disk at the target size to avoid resizing.
	isWindowsAdmin, err := isWindowsAdministrator()
	if err != nil {
		return "", err
	}
	fixedDiskSize := "10MB"
	if !isWindowsAdmin {
		fixedDiskSize = toMb(d.DiskSize)
	}

	log.Infof("Creating VHD")
	if err := cmd("hyper-v\\New-VHD", "-Path", quote(fixed), "-SizeBytes", fixedDiskSize, "-Fixed"); err != nil {
		return "", err
	}

	tarBuf, err := mcnutils.MakeDiskImage(d.publicSSHKeyPath())
	if err != nil {
		return "", err
	}

	file, err := os.OpenFile(fixed, os.O_WRONLY, 0644)
	if err != nil {
		return "", err
	}
	defer file.Close()

	file.Seek(0, os.SEEK_SET)
	_, err = file.Write(tarBuf.Bytes())
	if err != nil {
		return "", err
	}
	file.Close()

	if err := cmd("hyper-v\\Convert-VHD", "-Path", quote(fixed), "-DestinationPath", quote(diskImage), "-VHDType", "Dynamic", "-DeleteSource"); err != nil {
		return "", err
	}

	if isWindowsAdmin {
		if err := cmd("hyper-v\\Resize-VHD", "-Path", quote(diskImage), "-SizeBytes", toMb(d.DiskSize)); err != nil {
			return "", err
		}
	}

	return diskImage, nil
}
<commit_msg>Use the correct case for Hyper-V module name.<commit_after>package hyperv

import (
	"fmt"
	"net"
	"os"
	"time"

	"github.com/docker/machine/libmachine/drivers"
	"github.com/docker/machine/libmachine/log"
	"github.com/docker/machine/libmachine/mcnflag"
	"github.com/docker/machine/libmachine/mcnutils"
	"github.com/docker/machine/libmachine/ssh"
	"github.com/docker/machine/libmachine/state"
)

type Driver struct {
	*drivers.BaseDriver
	Boot2DockerURL string
	VSwitch        string
	DiskSize       int
	MemSize        int
	CPU            int
	MacAddr        string
	VLanID         int
}

const (
	defaultDiskSize = 20000
	defaultMemory   = 1024
	defaultCPU      = 1
	defaultVLanID   = 0
)

// NewDriver creates a new Hyper-v driver with default settings.
func NewDriver(hostName, storePath string) *Driver {
	return &Driver{
		DiskSize: defaultDiskSize,
		MemSize:  defaultMemory,
		CPU:      defaultCPU,
		BaseDriver: &drivers.BaseDriver{
			MachineName: hostName,
			StorePath:   storePath,
		},
	}
}

// GetCreateFlags registers the flags this driver adds to
// "docker hosts create"
func (d *Driver) GetCreateFlags() []mcnflag.Flag {
	return []mcnflag.Flag{
		mcnflag.StringFlag{
			Name:   "hyperv-boot2docker-url",
			Usage:  "URL of the boot2docker ISO. Defaults to the latest available version.",
			EnvVar: "HYPERV_BOOT2DOCKER_URL",
		},
		mcnflag.StringFlag{
			Name:   "hyperv-virtual-switch",
			Usage:  "Virtual switch name. Defaults to first found.",
			EnvVar: "HYPERV_VIRTUAL_SWITCH",
		},
		mcnflag.IntFlag{
			Name:   "hyperv-disk-size",
			Usage:  "Maximum size of dynamically expanding disk in MB.",
			Value:  defaultDiskSize,
			EnvVar: "HYPERV_DISK_SIZE",
		},
		mcnflag.IntFlag{
			Name:   "hyperv-memory",
			Usage:  "Memory size for host in MB.",
			Value:  defaultMemory,
			EnvVar: "HYPERV_MEMORY",
		},
		mcnflag.IntFlag{
			Name:   "hyperv-cpu-count",
			Usage:  "number of CPUs for the machine",
			Value:  defaultCPU,
			EnvVar: "HYPERV_CPU_COUNT",
		},
		mcnflag.StringFlag{
			Name:   "hyperv-static-macaddress",
			Usage:  "Hyper-V network adapter's static MAC address.",
			EnvVar: "HYPERV_STATIC_MACADDRESS",
		},
		mcnflag.IntFlag{
			Name:   "hyperv-vlan-id",
			Usage:  "Hyper-V network adapter's VLAN ID if any",
			Value:  defaultVLanID,
			EnvVar: "HYPERV_VLAN_ID",
		},
	}
}

func (d *Driver) SetConfigFromFlags(flags drivers.DriverOptions) error {
	d.Boot2DockerURL = flags.String("hyperv-boot2docker-url")
	d.VSwitch = flags.String("hyperv-virtual-switch")
	d.DiskSize = flags.Int("hyperv-disk-size")
	d.MemSize = flags.Int("hyperv-memory")
	d.CPU = flags.Int("hyperv-cpu-count")
	d.MacAddr = flags.String("hyperv-static-macaddress")
	d.VLanID = flags.Int("hyperv-vlan-id")
	d.SSHUser = "docker"
	d.SetSwarmConfigFromFlags(flags)

	return nil
}

func (d *Driver) GetSSHHostname() (string, error) {
	return d.GetIP()
}

// DriverName returns the name of the driver
func (d *Driver) DriverName() string {
	return "hyperv"
}

func (d *Driver) GetURL() (string, error) {
	ip, err := d.GetIP()
	if err != nil {
		return "", err
	}

	if ip == "" {
		return "", nil
	}

	return fmt.Sprintf("tcp://%s", net.JoinHostPort(ip, "2376")), nil
}

func (d *Driver) GetState() (state.State, error) {
	stdout, err := cmdOut("(", "Hyper-V\\Get-VM", d.MachineName, ").state")
	if err != nil {
		return state.None, fmt.Errorf("Failed to find the VM status")
	}

	resp := parseLines(stdout)
	if len(resp) < 1 {
		return state.None, nil
	}

	switch resp[0] {
	case "Running":
		return state.Running, nil
	case "Off":
		return state.Stopped, nil
	default:
		return state.None, nil
	}
}

// PreCreateCheck checks that the machine creation process can be started safely.
func (d *Driver) PreCreateCheck() error {
	// Check that powershell was found
	if powershell == "" {
		return ErrPowerShellNotFound
	}

	// Check that hyperv is installed
	if err := hypervAvailable(); err != nil {
		return err
	}

	// Check that the user is an Administrator
	isAdmin, err := isAdministrator()
	if err != nil {
		return err
	}
	if !isAdmin {
		return ErrNotAdministrator
	}

	// Check that there is a virtual switch already configured
	if _, err := d.chooseVirtualSwitch(); err != nil {
		return err
	}

	// Downloading boot2docker to cache should be done here to make sure
	// that a download failure will not leave a machine half created.
	b2dutils := mcnutils.NewB2dUtils(d.StorePath)
	err = b2dutils.UpdateISOCache(d.Boot2DockerURL)
	return err
}

func (d *Driver) Create() error {
	b2dutils := mcnutils.NewB2dUtils(d.StorePath)
	if err := b2dutils.CopyIsoToMachineDir(d.Boot2DockerURL, d.MachineName); err != nil {
		return err
	}

	log.Infof("Creating SSH key...")
	if err := ssh.GenerateSSHKey(d.GetSSHKeyPath()); err != nil {
		return err
	}

	log.Infof("Creating VM...")
	virtualSwitch, err := d.chooseVirtualSwitch()
	if err != nil {
		return err
	}

	log.Infof("Using switch %q", virtualSwitch)

	diskImage, err := d.generateDiskImage()
	if err != nil {
		return err
	}

	if err := cmd("Hyper-V\\New-VM",
		d.MachineName,
		"-Path", fmt.Sprintf("'%s'", d.ResolveStorePath(".")),
		"-SwitchName", quote(virtualSwitch),
		"-MemoryStartupBytes", toMb(d.MemSize)); err != nil {
		return err
	}

	if d.CPU > 1 {
		if err := cmd("Hyper-V\\Set-VMProcessor",
			d.MachineName,
			"-Count", fmt.Sprintf("%d", d.CPU)); err != nil {
			return err
		}
	}

	if d.MacAddr != "" {
		if err := cmd("Hyper-V\\Set-VMNetworkAdapter",
			"-VMName", d.MachineName,
			"-StaticMacAddress", fmt.Sprintf("\"%s\"", d.MacAddr)); err != nil {
			return err
		}
	}

	if d.VLanID > 0 {
		if err := cmd("Hyper-V\\Set-VMNetworkAdapterVlan",
			"-VMName", d.MachineName,
			"-Access",
			"-VlanId", fmt.Sprintf("%d", d.VLanID)); err != nil {
			return err
		}
	}

	if err := cmd("Hyper-V\\Set-VMDvdDrive",
		"-VMName", d.MachineName,
		"-Path", quote(d.ResolveStorePath("boot2docker.iso"))); err != nil {
		return err
	}

	if err := cmd("Hyper-V\\Add-VMHardDiskDrive",
		"-VMName", d.MachineName,
		"-Path", quote(diskImage)); err != nil {
		return err
	}

	log.Infof("Starting VM...")
	return d.Start()
}

func (d *Driver) chooseVirtualSwitch() (string, error) {
	if d.VSwitch == "" {
		// Default to the first external switche and in the process avoid DockerNAT
		stdout, err := cmdOut("(Hyper-V\\Get-VMSwitch -SwitchType External).Name")
		if err != nil {
			return "", err
		}

		switches := parseLines(stdout)

		if len(switches) < 1 {
			return "", fmt.Errorf("no External vswitch found. A valid vswitch must be available for this command to run. Check https://docs.docker.com/machine/drivers/hyper-v/")
		}

		return switches[0], nil
	}

	stdout, err := cmdOut("(Hyper-V\\Get-VMSwitch).Name")
	if err != nil {
		return "", err
	}

	switches := parseLines(stdout)

	found := false
	for _, name := range switches {
		if name == d.VSwitch {
			found = true
			break
		}
	}

	if !found {
		return "", fmt.Errorf("vswitch %q not found", d.VSwitch)
	}

	return d.VSwitch, nil
}

// waitForIP waits until the host has a valid IP
func (d *Driver) waitForIP() (string, error) {
	log.Infof("Waiting for host to start...")

	for {
		ip, _ := d.GetIP()
		if ip != "" {
			return ip, nil
		}

		time.Sleep(1 * time.Second)
	}
}

// waitStopped waits until the host is stopped
func (d *Driver) waitStopped() error {
	log.Infof("Waiting for host to stop...")

	for {
		s, err := d.GetState()
		if err != nil {
			return err
		}

		if s != state.Running {
			return nil
		}

		time.Sleep(1 * time.Second)
	}
}

// Start starts an host
func (d *Driver) Start() error {
	if err := cmd("Hyper-V\\Start-VM", d.MachineName); err != nil {
		return err
	}

	ip, err := d.waitForIP()
	if err != nil {
		return err
	}

	d.IPAddress = ip

	return nil
}

// Stop stops an host
func (d *Driver) Stop() error {
	if err := cmd("Hyper-V\\Stop-VM", d.MachineName); err != nil {
		return err
	}

	if err := d.waitStopped(); err != nil {
		return err
	}

	d.IPAddress = ""

	return nil
}

// Remove removes an host
func (d *Driver) Remove() error {
	s, err := d.GetState()
	if err != nil {
		return err
	}

	if s == state.Running {
		if err := d.Kill(); err != nil {
			return err
		}
	}

	return cmd("Hyper-V\\Remove-VM", d.MachineName, "-Force")
}

// Restart stops and starts an host
func (d *Driver) Restart() error {
	err := d.Stop()
	if err != nil {
		return err
	}

	return d.Start()
}

// Kill force stops an host
func (d *Driver) Kill() error {
	if err := cmd("Hyper-V\\Stop-VM", d.MachineName, "-TurnOff"); err != nil {
		return err
	}

	if err := d.waitStopped(); err != nil {
		return err
	}

	d.IPAddress = ""

	return nil
}

func (d *Driver) GetIP() (string, error) {
	s, err := d.GetState()
	if err != nil {
		return "", err
	}
	if s != state.Running {
		return "", drivers.ErrHostIsNotRunning
	}

	stdout, err := cmdOut("((", "Hyper-V\\Get-VM", d.MachineName, ").networkadapters[0]).ipaddresses[0]")
	if err != nil {
		return "", err
	}

	resp := parseLines(stdout)
	if len(resp) < 1 {
		return "", fmt.Errorf("IP not found")
	}

	return resp[0], nil
}

func (d *Driver) publicSSHKeyPath() string {
	return d.GetSSHKeyPath() + ".pub"
}

// generateDiskImage creates a small fixed vhd, put the tar in, convert to dynamic, then resize
func (d *Driver) generateDiskImage() (string, error) {
	diskImage := d.ResolveStorePath("disk.vhd")
	fixed := d.ResolveStorePath("fixed.vhd")

	// Resizing vhds requires administrator privileges
	// incase the user is only a hyper-v admin then create the disk at the target size to avoid resizing.
	isWindowsAdmin, err := isWindowsAdministrator()
	if err != nil {
		return "", err
	}
	fixedDiskSize := "10MB"
	if !isWindowsAdmin {
		fixedDiskSize = toMb(d.DiskSize)
	}

	log.Infof("Creating VHD")
	if err := cmd("Hyper-V\\New-VHD", "-Path", quote(fixed), "-SizeBytes", fixedDiskSize, "-Fixed"); err != nil {
		return "", err
	}

	tarBuf, err := mcnutils.MakeDiskImage(d.publicSSHKeyPath())
	if err != nil {
		return "", err
	}

	file, err := os.OpenFile(fixed, os.O_WRONLY, 0644)
	if err != nil {
		return "", err
	}
	defer file.Close()

	file.Seek(0, os.SEEK_SET)
	_, err = file.Write(tarBuf.Bytes())
	if err != nil {
		return "", err
	}
	file.Close()

	if err := cmd("Hyper-V\\Convert-VHD", "-Path", quote(fixed), "-DestinationPath", quote(diskImage), "-VHDType", "Dynamic", "-DeleteSource"); err != nil {
		return "", err
	}

	if isWindowsAdmin {
		if err := cmd("Hyper-V\\Resize-VHD", "-Path", quote(diskImage), "-SizeBytes", toMb(d.DiskSize)); err != nil {
			return "", err
		}
	}

	return diskImage, nil
}
<|endoftext|>


<commit_before>package gorm_test

import (
	"database/sql"
	"database/sql/driver"
	"errors"

	"reflect"
	"time"
)

type User struct {
	Id                int64
	Age               int64
	UserNum           Num
	Name              string        `sql:"size:255"`
	Birthday          time.Time     // Time
	CreatedAt         time.Time     // CreatedAt: Time of record is created, will be insert automatically
	UpdatedAt         time.Time     // UpdatedAt: Time of record is updated, will be updated automatically
	Emails            []Email       // Embedded structs
	BillingAddress    Address       // Embedded struct
	BillingAddressId  sql.NullInt64 // Embedded struct's foreign key
	ShippingAddress   Address       // Embedded struct
	ShippingAddressId int64         // Embedded struct's foreign key
	CreditCard        CreditCard
	Latitude          float64
	Languages         []Language `gorm:"many2many:user_languages;"`
	CompanyId         int64
	Company           Company
	Role
	PasswordHash      []byte
	IgnoreMe          int64                 `sql:"-"`
	IgnoreStringSlice []string              `sql:"-"`
	Ignored           struct{ Name string } `sql:"-"`
	IgnoredPointer    *User                 `sql:"-"`
}

type CreditCard struct {
	ID        int8
	Number    string
	UserId    sql.NullInt64
	CreatedAt time.Time
	UpdatedAt time.Time
	DeletedAt time.Time
}

type Email struct {
	Id        int16
	UserId    int
	Email     string `sql:"type:varchar(100);"`
	CreatedAt time.Time
	UpdatedAt time.Time
}

type Address struct {
	ID        int
	Address1  string
	Address2  string
	Post      string
	CreatedAt time.Time
	UpdatedAt time.Time
	DeletedAt time.Time
}

type Language struct {
	Id   int
	Name string
}

type Product struct {
	Id                    int64
	Code                  string
	Price                 int64
	CreatedAt             time.Time
	UpdatedAt             time.Time
	AfterFindCallTimes    int64
	BeforeCreateCallTimes int64
	AfterCreateCallTimes  int64
	BeforeUpdateCallTimes int64
	AfterUpdateCallTimes  int64
	BeforeSaveCallTimes   int64
	AfterSaveCallTimes    int64
	BeforeDeleteCallTimes int64
	AfterDeleteCallTimes  int64
}

type Company struct {
	Id    int64
	Name  string
	Owner *User `sql:"-"`
}

type Role struct {
	Name string
}

func (role *Role) Scan(value interface{}) error {
	if b, ok := value.([]uint8); ok {
		role.Name = string(b)
	} else {
		role.Name = value.(string)
	}
	return nil
}

func (role Role) Value() (driver.Value, error) {
	return role.Name, nil
}

func (role Role) IsAdmin() bool {
	return role.Name == "admin"
}

type Num int64

func (i *Num) Scan(src interface{}) error {
	switch s := src.(type) {
	case []byte:
	case int64:
		*i = Num(s)
	default:
		return errors.New("Cannot scan NamedInt from " + reflect.ValueOf(src).String())
	}
	return nil
}

type Animal struct {
	Counter    uint64    `gorm:"primary_key:yes"`
	Name       string    `sql:"DEFAULT:'galeone'"`
	From       string    //test reserved sql keyword as field name
	Age        time.Time `sql:"DEFAULT:current_timestamp"`
	unexported string    // unexported value
	CreatedAt  time.Time
	UpdatedAt  time.Time
}

type JoinTable struct {
	From uint64
	To   uint64
	Time time.Time `sql:"default: null"`
}

type Post struct {
	Id             int64
	CategoryId     sql.NullInt64
	MainCategoryId int64
	Title          string
	Body           string
	Comments       []*Comment
	Category       Category
	MainCategory   Category
}

type Category struct {
	Id   int64
	Name string
}

type Comment struct {
	Id      int64
	PostId  int64
	Content string
	Post    Post
}

// Scanner
type NullValue struct {
	Id      int64
	Name    sql.NullString `sql:"not null"`
	Age     sql.NullInt64
	Male    sql.NullBool
	Height  sql.NullFloat64
	AddedAt NullTime
}

type NullTime struct {
	Time  time.Time
	Valid bool
}

func (nt *NullTime) Scan(value interface{}) error {
	if value == nil {
		nt.Valid = false
		return nil
	}
	nt.Time, nt.Valid = value.(time.Time), true
	return nil
}

func (nt NullTime) Value() (driver.Value, error) {
	if !nt.Valid {
		return nil, nil
	}
	return nt.Time, nil
}
<commit_msg>Test ID as foreign key<commit_after>package gorm_test

import (
	"database/sql"
	"database/sql/driver"
	"errors"

	"reflect"
	"time"
)

type User struct {
	Id                int64
	Age               int64
	UserNum           Num
	Name              string        `sql:"size:255"`
	Birthday          time.Time     // Time
	CreatedAt         time.Time     // CreatedAt: Time of record is created, will be insert automatically
	UpdatedAt         time.Time     // UpdatedAt: Time of record is updated, will be updated automatically
	Emails            []Email       // Embedded structs
	BillingAddress    Address       // Embedded struct
	BillingAddressID  sql.NullInt64 // Embedded struct's foreign key
	ShippingAddress   Address       // Embedded struct
	ShippingAddressId int64         // Embedded struct's foreign key
	CreditCard        CreditCard
	Latitude          float64
	Languages         []Language `gorm:"many2many:user_languages;"`
	CompanyID         int64
	Company           Company
	Role
	PasswordHash      []byte
	IgnoreMe          int64                 `sql:"-"`
	IgnoreStringSlice []string              `sql:"-"`
	Ignored           struct{ Name string } `sql:"-"`
	IgnoredPointer    *User                 `sql:"-"`
}

type CreditCard struct {
	ID        int8
	Number    string
	UserId    sql.NullInt64
	CreatedAt time.Time
	UpdatedAt time.Time
	DeletedAt time.Time
}

type Email struct {
	Id        int16
	UserId    int
	Email     string `sql:"type:varchar(100);"`
	CreatedAt time.Time
	UpdatedAt time.Time
}

type Address struct {
	ID        int
	Address1  string
	Address2  string
	Post      string
	CreatedAt time.Time
	UpdatedAt time.Time
	DeletedAt time.Time
}

type Language struct {
	Id   int
	Name string
}

type Product struct {
	Id                    int64
	Code                  string
	Price                 int64
	CreatedAt             time.Time
	UpdatedAt             time.Time
	AfterFindCallTimes    int64
	BeforeCreateCallTimes int64
	AfterCreateCallTimes  int64
	BeforeUpdateCallTimes int64
	AfterUpdateCallTimes  int64
	BeforeSaveCallTimes   int64
	AfterSaveCallTimes    int64
	BeforeDeleteCallTimes int64
	AfterDeleteCallTimes  int64
}

type Company struct {
	Id    int64
	Name  string
	Owner *User `sql:"-"`
}

type Role struct {
	Name string
}

func (role *Role) Scan(value interface{}) error {
	if b, ok := value.([]uint8); ok {
		role.Name = string(b)
	} else {
		role.Name = value.(string)
	}
	return nil
}

func (role Role) Value() (driver.Value, error) {
	return role.Name, nil
}

func (role Role) IsAdmin() bool {
	return role.Name == "admin"
}

type Num int64

func (i *Num) Scan(src interface{}) error {
	switch s := src.(type) {
	case []byte:
	case int64:
		*i = Num(s)
	default:
		return errors.New("Cannot scan NamedInt from " + reflect.ValueOf(src).String())
	}
	return nil
}

type Animal struct {
	Counter    uint64    `gorm:"primary_key:yes"`
	Name       string    `sql:"DEFAULT:'galeone'"`
	From       string    //test reserved sql keyword as field name
	Age        time.Time `sql:"DEFAULT:current_timestamp"`
	unexported string    // unexported value
	CreatedAt  time.Time
	UpdatedAt  time.Time
}

type JoinTable struct {
	From uint64
	To   uint64
	Time time.Time `sql:"default: null"`
}

type Post struct {
	Id             int64
	CategoryId     sql.NullInt64
	MainCategoryId int64
	Title          string
	Body           string
	Comments       []*Comment
	Category       Category
	MainCategory   Category
}

type Category struct {
	Id   int64
	Name string
}

type Comment struct {
	Id      int64
	PostId  int64
	Content string
	Post    Post
}

// Scanner
type NullValue struct {
	Id      int64
	Name    sql.NullString `sql:"not null"`
	Age     sql.NullInt64
	Male    sql.NullBool
	Height  sql.NullFloat64
	AddedAt NullTime
}

type NullTime struct {
	Time  time.Time
	Valid bool
}

func (nt *NullTime) Scan(value interface{}) error {
	if value == nil {
		nt.Valid = false
		return nil
	}
	nt.Time, nt.Valid = value.(time.Time), true
	return nil
}

func (nt NullTime) Value() (driver.Value, error) {
	if !nt.Valid {
		return nil, nil
	}
	return nt.Time, nil
}
<|endoftext|>


<commit_before>// Copyright 2014 Hajime Hoshi
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package ebiten

import (
	"github.com/hajimehoshi/ebiten/internal/graphics"
	"github.com/hajimehoshi/ebiten/internal/graphics/internal/opengl"
	"github.com/hajimehoshi/ebiten/internal/ui"
)

func useGLContext(f func(*opengl.Context)) {
	ui.ExecOnUIThread(func() {
		f(glContext)
	})
}

func newGraphicsContext(screenWidth, screenHeight, screenScale int) (*graphicsContext, error) {
	c := &graphicsContext{}
	if err := c.setSize(screenWidth, screenHeight, screenScale); err != nil {
		return nil, err
	}
	return c, nil
}

type graphicsContext struct {
	screen      *Image
	defaultR    *Image
	screenScale int
}

func (c *graphicsContext) preUpdate() error {
	return c.screen.Clear()
}

func (c *graphicsContext) postUpdate() error {
	if err := c.defaultR.Clear(); err != nil {
		return err
	}

	scale := float64(c.screenScale)
	options := &DrawImageOptions{}
	options.GeoM.Scale(scale, scale)
	if err := c.defaultR.DrawImage(c.screen, options); err != nil {
		return err
	}
	return nil
}

func (c *graphicsContext) setSize(screenWidth, screenHeight, screenScale int) error {
	if c.defaultR != nil {
		c.defaultR.dispose()
	}
	if c.screen != nil {
		c.screen.dispose()
	}

	var err error
	useGLContext(func(g *opengl.Context) {
		f, err := graphics.NewZeroFramebuffer(g, screenWidth*screenScale, screenHeight*screenScale)
		if err != nil {
			return
		}

		texture, err := graphics.NewTexture(g, screenWidth, screenHeight, g.Nearest)
		if err != nil {
			return
		}
		screenF, err := graphics.NewFramebufferFromTexture(g, texture)
		if err != nil {
			return
		}
		screen := &Image{framebuffer: screenF, texture: texture}
		c.defaultR = &Image{framebuffer: f, texture: nil}
		c.screen = screen
		c.screenScale = screenScale
	})
	return err
}
<commit_msg>graphics: Rename defaultR -> defaultRenderTarget<commit_after>// Copyright 2014 Hajime Hoshi
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package ebiten

import (
	"github.com/hajimehoshi/ebiten/internal/graphics"
	"github.com/hajimehoshi/ebiten/internal/graphics/internal/opengl"
	"github.com/hajimehoshi/ebiten/internal/ui"
)

func useGLContext(f func(*opengl.Context)) {
	ui.ExecOnUIThread(func() {
		f(glContext)
	})
}

func newGraphicsContext(screenWidth, screenHeight, screenScale int) (*graphicsContext, error) {
	c := &graphicsContext{}
	if err := c.setSize(screenWidth, screenHeight, screenScale); err != nil {
		return nil, err
	}
	return c, nil
}

type graphicsContext struct {
	screen              *Image
	defaultRenderTarget *Image
	screenScale         int
}

func (c *graphicsContext) preUpdate() error {
	return c.screen.Clear()
}

func (c *graphicsContext) postUpdate() error {
	if err := c.defaultRenderTarget.Clear(); err != nil {
		return err
	}

	scale := float64(c.screenScale)
	options := &DrawImageOptions{}
	options.GeoM.Scale(scale, scale)
	if err := c.defaultRenderTarget.DrawImage(c.screen, options); err != nil {
		return err
	}
	return nil
}

func (c *graphicsContext) setSize(screenWidth, screenHeight, screenScale int) error {
	if c.defaultRenderTarget != nil {
		c.defaultRenderTarget.dispose()
	}
	if c.screen != nil {
		c.screen.dispose()
	}

	var err error
	useGLContext(func(g *opengl.Context) {
		f, err := graphics.NewZeroFramebuffer(g, screenWidth*screenScale, screenHeight*screenScale)
		if err != nil {
			return
		}

		texture, err := graphics.NewTexture(g, screenWidth, screenHeight, g.Nearest)
		if err != nil {
			return
		}
		screenF, err := graphics.NewFramebufferFromTexture(g, texture)
		if err != nil {
			return
		}
		screen := &Image{framebuffer: screenF, texture: texture}
		c.defaultRenderTarget = &Image{framebuffer: f, texture: nil}
		c.screen = screen
		c.screenScale = screenScale
	})
	return err
}
<|endoftext|>