mirror of
https://github.com/hajimehoshi/ebiten.git
synced 2024-12-25 11:18: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
|
// See the License for the specific language governing permissions and
|
||||||
// limitations under the License.
|
// 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.
|
package main
|
||||||
|
|
||||||
#import <UIKit/UIKit.h>
|
|
||||||
|
|
||||||
@interface EbitenViewController : UIViewController
|
|
||||||
@end
|
|
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/image v0.0.0-20190703141733-d6a02ce849c9
|
||||||
golang.org/x/mobile v0.0.0-20190711165009-e47acb2ca7f9
|
golang.org/x/mobile v0.0.0-20190711165009-e47acb2ca7f9
|
||||||
golang.org/x/sys v0.0.0-20190712062909-fae7ac547cb7
|
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
|
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 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
|
||||||
github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
|
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=
|
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 h1:idBdZTd9UioThJp8KpM/rTSinK/ChZFBE43/WtIy8zg=
|
||||||
golang.org/x/exp v0.0.0-20190306152737-a1d7652674e8/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
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=
|
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-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 h1:m1yPOfTCyrqD6atBVpXfysvXc1bhSMdIxBu0JQAM7mQ=
|
||||||
golang.org/x/mobile v0.0.0-20190711165009-e47acb2ca7f9/go.mod h1:E/iHnbuqvinMTCcRqshq8CkpyQDoeVncDDYHnLhea+o=
|
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-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 h1:cGjJzUd8RgBw428LXP65YXni0aiGNA4Bl+ls8SmLOm8=
|
||||||
golang.org/x/sys v0.0.0-20190429190828-d89cdac9e872/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
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 h1:LepdCS8Gf/MVejFIt8lsiexZATdoGVyp5bcyS+rYoUI=
|
||||||
golang.org/x/sys v0.0.0-20190712062909-fae7ac547cb7/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
|
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/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 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY=
|
||||||
gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
|
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