mirror of
https://github.com/hajimehoshi/ebiten.git
synced 2024-12-25 03:08:54 +01:00
Add ebitenmobile command
This works only for iOS so far. I'll implement Java version soon. Updates #863
This commit is contained in:
parent
424474f486
commit
9ba113861c
@ -12,11 +12,6 @@
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
// +build ios
|
||||
//go:generate file2byteslice -package=main -input=gobind.go -output=gobind.src.go -var gobindsrc
|
||||
|
||||
// TODO: Embed this header file to .framework by an original command.
|
||||
|
||||
#import <UIKit/UIKit.h>
|
||||
|
||||
@interface EbitenViewController : UIViewController
|
||||
@end
|
||||
package main
|
199
cmd/ebitenmobile/gobind.go
Normal file
199
cmd/ebitenmobile/gobind.go
Normal file
@ -0,0 +1,199 @@
|
||||
// Copyright 2019 The Ebiten 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.
|
||||
|
||||
// +build ebitenmobilegobind
|
||||
|
||||
// gobind is a wrapper of the original gobind. This command adds extra files like a view controller.
|
||||
package main
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"golang.org/x/tools/go/packages"
|
||||
)
|
||||
|
||||
var (
|
||||
lang = flag.String("lang", "", "")
|
||||
outdir = flag.String("outdir", "", "")
|
||||
javaPkg = flag.String("javapkg", "", "")
|
||||
prefix = flag.String("prefix", "", "")
|
||||
bootclasspath = flag.String("bootclasspath", "", "")
|
||||
classpath = flag.String("classpath", "", "")
|
||||
tags = flag.String("tags", "", "")
|
||||
)
|
||||
|
||||
var usage = `The Gobind tool generates Java language bindings for Go.
|
||||
|
||||
For usage details, see doc.go.`
|
||||
|
||||
func main() {
|
||||
flag.Parse()
|
||||
if err := run(); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
}
|
||||
|
||||
func run() error {
|
||||
cmd := exec.Command("gobind-original", os.Args[1:]...)
|
||||
cmd.Stdout = os.Stdout
|
||||
cmd.Stderr = os.Stderr
|
||||
if err := cmd.Run(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
pkgs, err := packages.Load(nil, flag.Args()[0])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
prefixLower := *prefix + pkgs[0].Name
|
||||
prefixUpper := strings.Title(*prefix) + strings.Title(pkgs[0].Name)
|
||||
|
||||
writeFile := func(filename string, content string) error {
|
||||
if err := ioutil.WriteFile(filepath.Join(*outdir, "src", "gobind", filename), []byte(content), 0644); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
replacePrefixes := func(content string) string {
|
||||
content = strings.ReplaceAll(content, "{{.PrefixUpper}}", prefixUpper)
|
||||
content = strings.ReplaceAll(content, "{{.PrefixLower}}", prefixLower)
|
||||
return content
|
||||
}
|
||||
|
||||
// Add additional files.
|
||||
langs := strings.Split(*lang, ",")
|
||||
for _, lang := range langs {
|
||||
switch lang {
|
||||
case "objc":
|
||||
// iOS
|
||||
if err := writeFile(prefixLower+"ebitenviewcontroller_ios.m", replacePrefixes(objcM)); err != nil {
|
||||
return err
|
||||
}
|
||||
case "java":
|
||||
// Android
|
||||
// TODO: Insert a Java file and let the original gobind compile it.
|
||||
case "go":
|
||||
// Do nothing.
|
||||
default:
|
||||
panic(fmt.Sprintf("unsupported language: %s", lang))
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
const objcM = `// Code generated by ebitenmobile. DO NOT EDIT.
|
||||
|
||||
// +build ios
|
||||
|
||||
#import <stdint.h>
|
||||
#import <UIKit/UIKit.h>
|
||||
#import <GLKit/GLkit.h>
|
||||
#import "Ebitenmobileview.objc.h"
|
||||
|
||||
@interface {{.PrefixUpper}}EbitenViewController : UIViewController
|
||||
@end
|
||||
|
||||
@implementation {{.PrefixUpper}}EbitenViewController {
|
||||
GLKView* glkView_;
|
||||
}
|
||||
|
||||
- (GLKView*)glkView {
|
||||
if (!glkView_) {
|
||||
glkView_ = [[GLKView alloc] init];
|
||||
glkView_.multipleTouchEnabled = YES;
|
||||
}
|
||||
return glkView_;
|
||||
}
|
||||
|
||||
- (void)viewDidLoad {
|
||||
[super viewDidLoad];
|
||||
|
||||
self.glkView.delegate = (id<GLKViewDelegate>)(self);
|
||||
[self.view addSubview: self.glkView];
|
||||
|
||||
EAGLContext *context = [[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES2];
|
||||
[self glkView].context = context;
|
||||
|
||||
[EAGLContext setCurrentContext:context];
|
||||
|
||||
CADisplayLink *displayLink = [CADisplayLink displayLinkWithTarget:self selector:@selector(drawFrame)];
|
||||
[displayLink addToRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
|
||||
}
|
||||
|
||||
- (void)viewDidLayoutSubviews {
|
||||
[super viewDidLayoutSubviews];
|
||||
CGRect viewRect = [[self view] frame];
|
||||
|
||||
EbitenmobileviewLayout(viewRect.size.width, viewRect.size.height, (id<EbitenmobileviewViewRectSetter>)self);
|
||||
}
|
||||
|
||||
- (void)setViewRect:(long)x y:(long)y width:(long)width height:(long)height {
|
||||
CGRect glkViewRect = CGRectMake(x, y, width, height);
|
||||
[[self glkView] setFrame:glkViewRect];
|
||||
}
|
||||
|
||||
- (void)didReceiveMemoryWarning {
|
||||
[super didReceiveMemoryWarning];
|
||||
// Dispose of any resources that can be recreated.
|
||||
// TODO: Notify this to Go world?
|
||||
}
|
||||
|
||||
- (void)drawFrame{
|
||||
[[self glkView] setNeedsDisplay];
|
||||
}
|
||||
|
||||
- (void)glkView:(GLKView*)view drawInRect:(CGRect)rect {
|
||||
NSError* err = nil;
|
||||
EbitenmobileviewUpdate(&err);
|
||||
if (err != nil) {
|
||||
NSLog(@"Error: %@", err);
|
||||
}
|
||||
}
|
||||
|
||||
- (void)updateTouches:(NSSet*)touches {
|
||||
for (UITouch* touch in touches) {
|
||||
if (touch.view != [self glkView]) {
|
||||
continue;
|
||||
}
|
||||
CGPoint location = [touch locationInView:touch.view];
|
||||
EbitenmobileviewUpdateTouchesOnIOS(touch.phase, (uintptr_t)touch, location.x, location.y);
|
||||
}
|
||||
}
|
||||
|
||||
- (void)touchesBegan:(NSSet*)touches withEvent:(UIEvent*)event {
|
||||
[self updateTouches:touches];
|
||||
}
|
||||
|
||||
- (void)touchesMoved:(NSSet*)touches withEvent:(UIEvent*)event {
|
||||
[self updateTouches:touches];
|
||||
}
|
||||
|
||||
- (void)touchesEnded:(NSSet*)touches withEvent:(UIEvent*)event {
|
||||
[self updateTouches:touches];
|
||||
}
|
||||
|
||||
- (void)touchesCancelled:(NSSet*)touches withEvent:(UIEvent*)event {
|
||||
[self updateTouches:touches];
|
||||
}
|
||||
|
||||
@end
|
||||
`
|
6
cmd/ebitenmobile/gobind.src.go
Normal file
6
cmd/ebitenmobile/gobind.src.go
Normal file
File diff suppressed because one or more lines are too long
110
cmd/ebitenmobile/gomobile.go
Normal file
110
cmd/ebitenmobile/gomobile.go
Normal file
@ -0,0 +1,110 @@
|
||||
// Copyright 2019 The Ebiten 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 main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
)
|
||||
|
||||
const gomobileHash = "597adff16ade9d88626f8caea514bb189b8c74ee"
|
||||
|
||||
func runGo(args ...string) error {
|
||||
env := []string{
|
||||
"GO111MODULE=on",
|
||||
}
|
||||
|
||||
if buildX || buildN {
|
||||
for _, e := range env {
|
||||
fmt.Printf("%s ", e)
|
||||
}
|
||||
fmt.Print("go")
|
||||
for _, arg := range args {
|
||||
fmt.Printf(" %s", arg)
|
||||
}
|
||||
fmt.Println()
|
||||
}
|
||||
|
||||
if buildN {
|
||||
return nil
|
||||
}
|
||||
|
||||
cmd := exec.Command("go", args...)
|
||||
cmd.Env = append(os.Environ(), env...)
|
||||
if out, err := cmd.CombinedOutput(); err != nil {
|
||||
return fmt.Errorf("go %v failed: %v\n%v", args, string(out), err)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func prepareGomobileCommands() error {
|
||||
tmp, err := ioutil.TempDir("", "ebitenmobile-")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
newpath := filepath.Join(tmp, "bin")
|
||||
if path := os.Getenv("PATH"); path != "" {
|
||||
newpath += string(filepath.ListSeparator) + path
|
||||
}
|
||||
os.Setenv("PATH", newpath)
|
||||
|
||||
pwd, err := os.Getwd()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// cd
|
||||
if buildX {
|
||||
fmt.Printf("cd %s\n", tmp)
|
||||
}
|
||||
if err := os.Chdir(tmp); err != nil {
|
||||
return err
|
||||
}
|
||||
defer func() {
|
||||
os.Chdir(pwd)
|
||||
}()
|
||||
|
||||
if err := runGo("mod", "init", "ebitenmobiletemporary"); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := runGo("get", "golang.org/x/mobile@"+gomobileHash); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := runGo("build", "-o", filepath.Join("bin", "gomobile"), "golang.org/x/mobile/cmd/gomobile"); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := runGo("build", "-o", filepath.Join("bin", "gobind-original"), "golang.org/x/mobile/cmd/gobind"); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := os.Mkdir("src", 0755); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := ioutil.WriteFile(filepath.Join("src", "gobind.go"), gobindsrc, 0644); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if err := runGo("build", "-o", filepath.Join("bin", "gobind"), "-tags", "ebitenmobilegobind", filepath.Join("src", "gobind.go")); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// TODO: Create a gobind wrapper
|
||||
|
||||
return nil
|
||||
}
|
222
cmd/ebitenmobile/main.go
Normal file
222
cmd/ebitenmobile/main.go
Normal file
@ -0,0 +1,222 @@
|
||||
// Copyright 2019 The Ebiten 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 main
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"text/template"
|
||||
|
||||
"golang.org/x/tools/go/packages"
|
||||
)
|
||||
|
||||
const (
|
||||
ebitenmobileCommand = "ebitenmobile"
|
||||
)
|
||||
|
||||
func init() {
|
||||
flag.Usage = func() {
|
||||
// This message is copied from `gomobile bind -h`
|
||||
fmt.Fprintf(os.Stderr, "%s bind [-target android|ios] [-bootclasspath <path>] [-classpath <path>] [-o output] [build flags] [package]", ebitenmobileCommand)
|
||||
os.Exit(2)
|
||||
}
|
||||
flag.Parse()
|
||||
}
|
||||
|
||||
func goEnv(name string) string {
|
||||
if val := os.Getenv(name); val != "" {
|
||||
return val
|
||||
}
|
||||
val, err := exec.Command("go", "env", name).Output()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
return strings.TrimSpace(string(val))
|
||||
}
|
||||
|
||||
const (
|
||||
// Copied from gomobile.
|
||||
minAndroidAPI = 15
|
||||
)
|
||||
|
||||
var (
|
||||
buildA bool // -a
|
||||
buildI bool // -i
|
||||
buildN bool // -n
|
||||
buildV bool // -v
|
||||
buildX bool // -x
|
||||
buildO string // -o
|
||||
buildGcflags string // -gcflags
|
||||
buildLdflags string // -ldflags
|
||||
buildTarget string // -target
|
||||
buildWork bool // -work
|
||||
buildBundleID string // -bundleid
|
||||
buildIOSVersion string // -iosversion
|
||||
buildAndroidAPI int // -androidapi
|
||||
buildTags string // -tags
|
||||
|
||||
bindPrefix string // -prefix
|
||||
bindJavaPkg string // -javapkg
|
||||
bindClasspath string // -classpath
|
||||
bindBootClasspath string // -bootclasspath
|
||||
)
|
||||
|
||||
func main() {
|
||||
args := flag.Args()
|
||||
if len(args) < 1 {
|
||||
flag.Usage()
|
||||
}
|
||||
|
||||
var flagset flag.FlagSet
|
||||
flagset.StringVar(&buildO, "o", "", "")
|
||||
flagset.StringVar(&buildGcflags, "gcflags", "", "")
|
||||
flagset.StringVar(&buildLdflags, "ldflags", "", "")
|
||||
flagset.StringVar(&buildTarget, "target", "android", "")
|
||||
flagset.StringVar(&buildBundleID, "bundleid", "", "")
|
||||
flagset.StringVar(&buildIOSVersion, "iosversion", "7.0", "")
|
||||
flagset.StringVar(&buildTags, "tags", "", "")
|
||||
flagset.IntVar(&buildAndroidAPI, "androidapi", minAndroidAPI, "")
|
||||
flagset.BoolVar(&buildA, "a", false, "")
|
||||
flagset.BoolVar(&buildI, "i", false, "")
|
||||
flagset.BoolVar(&buildN, "n", false, "")
|
||||
flagset.BoolVar(&buildV, "v", false, "")
|
||||
flagset.BoolVar(&buildX, "x", false, "")
|
||||
flagset.BoolVar(&buildWork, "work", false, "")
|
||||
flagset.StringVar(&bindJavaPkg, "javapkg", "", "")
|
||||
flagset.StringVar(&bindPrefix, "prefix", "", "")
|
||||
flagset.StringVar(&bindClasspath, "classpath", "", "")
|
||||
flagset.StringVar(&bindBootClasspath, "bootclasspath", "", "")
|
||||
|
||||
flagset.Parse(args[1:])
|
||||
|
||||
if err := prepareGomobileCommands(); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
switch args[0] {
|
||||
case "bind":
|
||||
if err := doBind(args, &flagset); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
default:
|
||||
flag.Usage()
|
||||
}
|
||||
}
|
||||
|
||||
func doBind(args []string, flagset *flag.FlagSet) error {
|
||||
pkgs, err := packages.Load(nil, flagset.Args()[0])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
prefixLower := bindPrefix + pkgs[0].Name
|
||||
prefixUpper := strings.Title(bindPrefix) + strings.Title(pkgs[0].Name)
|
||||
|
||||
args = append(args, "github.com/hajimehoshi/ebiten/mobile/ebitenmobileview")
|
||||
|
||||
if buildO == "" {
|
||||
fmt.Fprintln(os.Stderr, "-o must be specified.")
|
||||
os.Exit(2)
|
||||
return nil
|
||||
}
|
||||
|
||||
if buildN {
|
||||
fmt.Print("gomobile")
|
||||
for _, arg := range args {
|
||||
fmt.Print(" ", arg)
|
||||
}
|
||||
fmt.Println()
|
||||
return nil
|
||||
}
|
||||
|
||||
cmd := exec.Command("gomobile", args...)
|
||||
cmd.Env = append(cmd.Env, os.Environ()...)
|
||||
cmd.Env = append(cmd.Env, "GO111MODULE=off")
|
||||
cmd.Stdout = os.Stdout
|
||||
cmd.Stderr = os.Stderr
|
||||
if err := cmd.Run(); err != nil {
|
||||
os.Exit(err.(*exec.ExitError).ExitCode())
|
||||
return nil
|
||||
}
|
||||
|
||||
replacePrefixes := func(content string) string {
|
||||
content = strings.ReplaceAll(content, "{{.PrefixUpper}}", prefixUpper)
|
||||
content = strings.ReplaceAll(content, "{{.PrefixLower}}", prefixLower)
|
||||
return content
|
||||
}
|
||||
|
||||
switch buildTarget {
|
||||
case "android":
|
||||
// Do nothing.
|
||||
case "ios":
|
||||
dir := filepath.Join(buildO, "Versions", "A")
|
||||
|
||||
if err := ioutil.WriteFile(filepath.Join(dir, "Headers", prefixUpper+"EbitenViewController.h"), []byte(replacePrefixes(objcH)), 0644); err != nil {
|
||||
return err
|
||||
}
|
||||
// TODO: Remove 'Ebitenmobileview.objc.h' here. Now it is hard since there is a header file importing
|
||||
// that header file.
|
||||
|
||||
fs, err := ioutil.ReadDir(filepath.Join(dir, "Headers"))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
var headerFiles []string
|
||||
for _, f := range fs {
|
||||
if strings.HasSuffix(f.Name(), ".h") {
|
||||
headerFiles = append(headerFiles, f.Name())
|
||||
}
|
||||
}
|
||||
|
||||
w, err := os.OpenFile(filepath.Join(dir, "Modules", "module.modulemap"), os.O_WRONLY, 0644)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer w.Close()
|
||||
var mmVals = struct {
|
||||
Module string
|
||||
Headers []string
|
||||
}{
|
||||
Module: prefixUpper,
|
||||
Headers: headerFiles,
|
||||
}
|
||||
if err := iosModuleMapTmpl.Execute(w, mmVals); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// TODO: Remove Ebitenmobileview.objc.h?
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
const objcH = `// Code generated by ebitenmobile. DO NOT EDIT.
|
||||
|
||||
#import <UIKit/UIKit.h>
|
||||
|
||||
@interface {{.PrefixUpper}}EbitenViewController : UIViewController
|
||||
@end
|
||||
`
|
||||
|
||||
var iosModuleMapTmpl = template.Must(template.New("iosmmap").Parse(`framework module "{{.Module}}" {
|
||||
{{range .Headers}} header "{{.}}"
|
||||
{{end}}
|
||||
export *
|
||||
}`))
|
1
go.mod
1
go.mod
@ -16,5 +16,6 @@ require (
|
||||
golang.org/x/image v0.0.0-20190703141733-d6a02ce849c9
|
||||
golang.org/x/mobile v0.0.0-20190711165009-e47acb2ca7f9
|
||||
golang.org/x/sys v0.0.0-20190712062909-fae7ac547cb7
|
||||
golang.org/x/tools v0.0.0-20190816200558-6889da9d5479
|
||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 // indirect
|
||||
)
|
||||
|
7
go.sum
7
go.sum
@ -24,6 +24,7 @@ github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
|
||||
github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
|
||||
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
||||
github.com/pkg/browser v0.0.0-20180916011732-0a3d74bf9ce4/go.mod h1:4OwLy04Bl9Ef3GJJCoec+30X3LQs/0/m4HFRt/2LUSA=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8 h1:idBdZTd9UioThJp8KpM/rTSinK/ChZFBE43/WtIy8zg=
|
||||
golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||
golang.org/x/image v0.0.0-20190227222117-0694c2d4d067 h1:KYGJGHOQy8oSi1fDlSpcZF0+juKwk/hEMv5SiwHogR0=
|
||||
@ -34,11 +35,17 @@ golang.org/x/mobile v0.0.0-20190415191353-3e0bab5405d6 h1:vyLBGJPIl9ZYbcQFM2USFm
|
||||
golang.org/x/mobile v0.0.0-20190415191353-3e0bab5405d6/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o=
|
||||
golang.org/x/mobile v0.0.0-20190711165009-e47acb2ca7f9 h1:m1yPOfTCyrqD6atBVpXfysvXc1bhSMdIxBu0JQAM7mQ=
|
||||
golang.org/x/mobile v0.0.0-20190711165009-e47acb2ca7f9/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o=
|
||||
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
|
||||
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
|
||||
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
|
||||
golang.org/x/sys v0.0.0-20190312061237-fead79001313/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190429190828-d89cdac9e872 h1:cGjJzUd8RgBw428LXP65YXni0aiGNA4Bl+ls8SmLOm8=
|
||||
golang.org/x/sys v0.0.0-20190429190828-d89cdac9e872/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/sys v0.0.0-20190712062909-fae7ac547cb7 h1:LepdCS8Gf/MVejFIt8lsiexZATdoGVyp5bcyS+rYoUI=
|
||||
golang.org/x/sys v0.0.0-20190712062909-fae7ac547cb7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
|
||||
golang.org/x/tools v0.0.0-20190816200558-6889da9d5479 h1:lfN2PY/jymfnxkNHlbBF5DwPsUvhqUnrdgfK01iH2s0=
|
||||
golang.org/x/tools v0.0.0-20190816200558-6889da9d5479/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
|
||||
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
|
||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY=
|
||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
||||
|
@ -1,113 +0,0 @@
|
||||
// Copyright 2019 The Ebiten 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 ebitenmobileview
|
||||
|
||||
// #cgo ios LDFLAGS: -framework UIKit -framework GLKit -framework QuartzCore -framework OpenGLES
|
||||
//
|
||||
// #include <stdint.h>
|
||||
import "C"
|
||||
|
||||
import (
|
||||
"math"
|
||||
"runtime"
|
||||
"sync"
|
||||
|
||||
"github.com/hajimehoshi/ebiten"
|
||||
)
|
||||
|
||||
var theState state
|
||||
|
||||
// game is not exported since gomobile complains.
|
||||
// TODO: Report this error.
|
||||
type game interface {
|
||||
Update(*ebiten.Image) error
|
||||
Layout(viewWidth, viewHeight int) (screenWidth, screenHeight int)
|
||||
}
|
||||
|
||||
type state struct {
|
||||
game game
|
||||
|
||||
running bool
|
||||
|
||||
// m is a mutex required for each function.
|
||||
// For example, on Android, Update can be called from a different thread:
|
||||
// https://developer.android.com/reference/android/opengl/GLSurfaceView.Renderer
|
||||
m sync.Mutex
|
||||
}
|
||||
|
||||
func SetGame(game game) {
|
||||
theState.m.Lock()
|
||||
defer theState.m.Unlock()
|
||||
|
||||
theState.game = game
|
||||
}
|
||||
|
||||
//export ebitenLayout
|
||||
func ebitenLayout(viewWidth, viewHeight C.int, x, y, width, height *C.int) {
|
||||
theState.m.Lock()
|
||||
defer theState.m.Unlock()
|
||||
|
||||
if theState.game == nil {
|
||||
panic("ebitenmobileview: SetGame must be called before ebitenLayout")
|
||||
}
|
||||
|
||||
w, h := theState.game.Layout(int(viewWidth), int(viewHeight))
|
||||
scaleX := float64(viewWidth) / float64(w)
|
||||
scaleY := float64(viewHeight) / float64(h)
|
||||
scale := math.Min(scaleX, scaleY)
|
||||
|
||||
*width = C.int(math.Ceil(float64(w) * scale))
|
||||
*height = C.int(math.Ceil(float64(h) * scale))
|
||||
*x = (viewWidth - *width) / 2
|
||||
*y = (viewHeight - *height) / 2
|
||||
|
||||
if !theState.running {
|
||||
start(theState.game.Update, w, h, scale)
|
||||
theState.running = true
|
||||
}
|
||||
// TODO: call SetScreenSize
|
||||
}
|
||||
|
||||
//export ebitenUpdate
|
||||
func ebitenUpdate() *C.char {
|
||||
runtime.LockOSThread()
|
||||
defer runtime.UnlockOSThread()
|
||||
|
||||
theState.m.Lock()
|
||||
defer theState.m.Unlock()
|
||||
|
||||
if err := update(); err != nil {
|
||||
// TODO: When to free cstr?
|
||||
cstr := C.CString(err.Error())
|
||||
return cstr
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
//export ebitenUpdateTouchesOnIOS
|
||||
func ebitenUpdateTouchesOnIOS(phase C.int, ptr C.uintptr_t, x, y C.int) {
|
||||
theState.m.Lock()
|
||||
defer theState.m.Unlock()
|
||||
|
||||
updateTouchesOnIOSImpl(int(phase), int64(ptr), int(x), int(y))
|
||||
}
|
||||
|
||||
//export ebitenUpdateTouchesOnAndroid
|
||||
func ebitenUpdateTouchesOnAndroid(action C.int, id C.int, x, y C.int) {
|
||||
theState.m.Lock()
|
||||
defer theState.m.Unlock()
|
||||
|
||||
updateTouchesOnAndroid(int(action), int(id), int(x), int(y))
|
||||
}
|
@ -1,105 +0,0 @@
|
||||
// Copyright 2019 The Ebiten 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.
|
||||
|
||||
// +build ios
|
||||
|
||||
#import <UIKit/UIKit.h>
|
||||
#import <GLKit/GLkit.h>
|
||||
|
||||
#import "ebitenviewcontroller_ios.h"
|
||||
|
||||
#include "_cgo_export.h"
|
||||
|
||||
@implementation EbitenViewController {
|
||||
GLKView* glkView_;
|
||||
}
|
||||
|
||||
- (GLKView*)glkView {
|
||||
if (!glkView_) {
|
||||
glkView_ = [[GLKView alloc] init];
|
||||
glkView_.multipleTouchEnabled = YES;
|
||||
}
|
||||
return glkView_;
|
||||
}
|
||||
|
||||
- (void)viewDidLoad {
|
||||
[super viewDidLoad];
|
||||
|
||||
self.glkView.delegate = (id<GLKViewDelegate>)(self);
|
||||
[self.view addSubview: self.glkView];
|
||||
|
||||
EAGLContext *context = [[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES2];
|
||||
[self glkView].context = context;
|
||||
|
||||
[EAGLContext setCurrentContext:context];
|
||||
|
||||
CADisplayLink *displayLink = [CADisplayLink displayLinkWithTarget:self selector:@selector(drawFrame)];
|
||||
[displayLink addToRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
|
||||
}
|
||||
|
||||
- (void)viewDidLayoutSubviews {
|
||||
[super viewDidLayoutSubviews];
|
||||
CGRect viewRect = [[self view] frame];
|
||||
|
||||
int x, y, width, height;
|
||||
ebitenLayout(viewRect.size.width, viewRect.size.height, &x, &y, &width, &height);
|
||||
|
||||
CGRect glkViewRect = CGRectMake(x, y, width, height);
|
||||
[[self glkView] setFrame:glkViewRect];
|
||||
}
|
||||
|
||||
- (void)didReceiveMemoryWarning {
|
||||
[super didReceiveMemoryWarning];
|
||||
// Dispose of any resources that can be recreated.
|
||||
// TODO: Notify this to Go world?
|
||||
}
|
||||
|
||||
- (void)drawFrame{
|
||||
[[self glkView] setNeedsDisplay];
|
||||
}
|
||||
|
||||
- (void)glkView:(GLKView*)view drawInRect:(CGRect)rect {
|
||||
const char* err = ebitenUpdate();
|
||||
if (err != nil) {
|
||||
NSLog(@"Error: %s", err);
|
||||
}
|
||||
}
|
||||
|
||||
- (void)updateTouches:(NSSet*)touches {
|
||||
for (UITouch* touch in touches) {
|
||||
if (touch.view != [self glkView]) {
|
||||
continue;
|
||||
}
|
||||
CGPoint location = [touch locationInView:touch.view];
|
||||
ebitenUpdateTouchesOnIOS(touch.phase, (uintptr_t)touch, location.x, location.y);
|
||||
}
|
||||
}
|
||||
|
||||
- (void)touchesBegan:(NSSet*)touches withEvent:(UIEvent*)event {
|
||||
[self updateTouches:touches];
|
||||
}
|
||||
|
||||
- (void)touchesMoved:(NSSet*)touches withEvent:(UIEvent*)event {
|
||||
[self updateTouches:touches];
|
||||
}
|
||||
|
||||
- (void)touchesEnded:(NSSet*)touches withEvent:(UIEvent*)event {
|
||||
[self updateTouches:touches];
|
||||
}
|
||||
|
||||
- (void)touchesCancelled:(NSSet*)touches withEvent:(UIEvent*)event {
|
||||
[self updateTouches:touches];
|
||||
}
|
||||
|
||||
@end
|
55
mobile/ebitenmobileview/game.go
Normal file
55
mobile/ebitenmobileview/game.go
Normal file
@ -0,0 +1,55 @@
|
||||
// Copyright 2019 The Ebiten 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 ebitenmobileview
|
||||
|
||||
// #cgo ios LDFLAGS: -framework UIKit -framework GLKit -framework QuartzCore -framework OpenGLES
|
||||
//
|
||||
// #include <stdint.h>
|
||||
import "C"
|
||||
|
||||
import (
|
||||
"math"
|
||||
"runtime"
|
||||
"sync"
|
||||
|
||||
"github.com/hajimehoshi/ebiten"
|
||||
)
|
||||
|
||||
var theState state
|
||||
|
||||
// game is not exported since gomobile complains.
|
||||
// TODO: Report this error.
|
||||
type game interface {
|
||||
Update(*ebiten.Image) error
|
||||
Layout(viewWidth, viewHeight int) (screenWidth, screenHeight int)
|
||||
}
|
||||
|
||||
type state struct {
|
||||
game game
|
||||
|
||||
running bool
|
||||
|
||||
// m is a mutex required for each function.
|
||||
// For example, on Android, Update can be called from a different thread:
|
||||
// https://developer.android.com/reference/android/opengl/GLSurfaceView.Renderer
|
||||
m sync.Mutex
|
||||
}
|
||||
|
||||
func SetGame(game game) {
|
||||
theState.m.Lock()
|
||||
defer theState.m.Unlock()
|
||||
|
||||
theState.game = game
|
||||
}
|
Loading…
Reference in New Issue
Block a user