Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Ebitengine doesn't work on Android Termux #3057

Closed
1 of 11 tasks
vsm0 opened this issue Aug 4, 2024 · 37 comments
Closed
1 of 11 tasks

Ebitengine doesn't work on Android Termux #3057

vsm0 opened this issue Aug 4, 2024 · 37 comments

Comments

@vsm0
Copy link

vsm0 commented Aug 4, 2024

Ebitengine Version

2.7.8

Operating System

  • Windows
  • macOS
  • Linux
  • FreeBSD
  • OpenBSD
  • Android
  • iOS
  • Nintendo Switch
  • PlayStation 5
  • Xbox
  • Web Browsers

Go Version (go version)

1.22.5

What steps will reproduce the problem?

Create main.go with the following contents (basic window example):

package main

import (
	"github.com/hajimehoshi/ebiten/v2"
	"github.com/hajimehoshi/ebiten/v2/ebitenutil"
)

const (
	WIDTH = 480
	HEIGHT = 360
)

type Game struct {}

func (g *Game) Layout(widith, height int) (int, int) {
	return WIDTH, HEIGHT
}

func (g *Game) Update() error {
	return nil
}

func (g *Game) Draw(img *ebiten.Image) {
	ebitenutil.DebugPrint(img, "Hello World")
}

func main() {
	ebiten.SetWindowSize(WIDTH * 2, HEIGHT * 2)
	ebiten.SetWindowTitle("Untitled")

	if err := ebiten.RunGame(&Game{}); err != nil {
		panic(err)
	}
}

create go.mod and run program:

go mod init example
go mod tidy
go run .

What is the expected result?

A bare window with some text should appear.

What happens instead?

The following error appears during compilation:

android.c:171:52: error: incompatible pointer to integer conversion passing 'ANativeWindow *' (aka 'struct ANativeWindow *') to parameter of type 'EGLNativeWindowType' (aka 'unsigned long') [-Wint-conversion]
../../../../usr/include/EGL/egl.h:136:109: note: passing argument to parameter 'win' here

Anything else you feel useful to add?

The compilation environment is Termux, which is non-standard Android.

After some investigation, the clash between parameter value and signature is possibly due an incorrect/incomplete appraisal of the compilation environment, within the header eglplatform.h, as noted by the error.

A standard Android target (as defined in eglplatform.h) has the following conditional definition:

#elif defined(__ANDROID__) || defined(ANDROID)
#include <android/native_window.h>
...
typedef struct ANativeWindow* EGLNativeWindowType;

However, Termux modifies this slightly:

#elif (defined(__ANDROID__) || defined(ANDROID)) && !defined(__TERMUX__)
...                                                                 
typedef struct ANativeWindow* EGLNativeWindowType;

Since this definition doesn't match, I suspect it falls through to the next defined variable 'unix' (which I confirmed by preprocessing gomobile/app/android.c using gcc -E -dM android.c):

#elif defined(__unix__)
...
typedef khronos_uintptr_t EGLNativeWindowType;

It's my first time digging so deep into library internals, but unless I'm way off mark, it's possible that gomobile uses the standard eglplatform.h header, which mistakes the Termux environment for a standard Android one.

All in all, this is an issue for a specific use case, and I'm not experienced enough to know how hard or simple a fix this would be. Hopefully it isn't too niche, as it would be nice for the Termux target to be supported. It is a near complete UNIX environment that can already handle most desktop programs/tools I've thrown at it.

@hajimehoshi
Copy link
Owner

I guess this is an issue in golang.org/x/mobile. If you can reproduce this with gomobile (not ebitenmobile), please report this to github.com/golang/go.

@vsm0
Copy link
Author

vsm0 commented Aug 4, 2024

Thx for the reply.
I'm not sure how to do that right now, so I'll do some searching later.

@hajimehoshi
Copy link
Owner

I'd try to compile an example project in golang.org/x/mobile like golang.org/x/mobile/example/basic

@vsm0
Copy link
Author

vsm0 commented Aug 4, 2024

Yes the same error was produced. I'll report it there now.
Thx for the help

@hajimehoshi
Copy link
Owner

Apparently gomobile doesn't work well with Termux golang/go#47727

@vsm0
Copy link
Author

vsm0 commented Aug 4, 2024

I see. I'm not sure what full native means, but besides this gomobile issue, other programs compile fine, even those with cgo. Tbh I've never even seen those errors while using Go on Termux. I've compiled other packages just fine, like raylib-go.
AFAIK I already get the latest stable Go compiler.

Since you mention the issue in the link above, should I still report my issue personally?

@hajimehoshi
Copy link
Owner

Since you mention the issue in the link above, should I still report my issue personally?

I guess so as your error (EGL) seems new. It'd be nice to add a link to 47727 as a reference.

@vsm0
Copy link
Author

vsm0 commented Aug 4, 2024

Alright, will do thx

@hajimehoshi
Copy link
Owner

golang/go#68727

Didn't you test gomobile...?

@hajimehoshi
Copy link
Owner

Oh, I thought you tried ebitenmobile, but what you did was just go-run. Then this is an issue in ebitengine, not in gomobile.

@vsm0
Copy link
Author

vsm0 commented Aug 4, 2024

Yeah, it's not packaging for Android. It's pretty much standard Go compilation on the command line.

As a side note, I installed Go in Ubuntu from within Termux and it built successfully, but didn't run due to x11 issues.

@hajimehoshi
Copy link
Owner

Yes the same error was produced

Just in case, what did you reproduce at that time?

@vsm0
Copy link
Author

vsm0 commented Aug 4, 2024

The error (pointer conversion) only occured with the base Termux setup. It didn't compile. And it errored both in gomobile and upstream mobile. So I think you're right and it is an upstream bug.

@hajimehoshi
Copy link
Owner

The error (pointer conversion) only occured with the base Termux setup. It didn't compile. And it errored both in gomobile and upstream mobile.

What command did you run actually?

@vsm0
Copy link
Author

vsm0 commented Aug 4, 2024

I first created the main.go file
Then:

go mod init example
go mod tidy
go run .

Nothing special really.

@hajimehoshi
Copy link
Owner

So this is not a test for gomobile, right?

@vsm0
Copy link
Author

vsm0 commented Aug 4, 2024

It [the ebiten module] pulled in gomobile as a dependency, and the issue was in the android.c within gomobile/app/.
Again it's not specific to gomobile, since the issue was reproducible upstream.

@hajimehoshi
Copy link
Owner

I'm confused. Is this issue reproducible with gomobile without ebitengine? What upstream did you mean?

@vsm0
Copy link
Author

vsm0 commented Aug 4, 2024

Maybe I'm confused. How exactly is gomobile related to golang.org/x/mobile?

At first, the error came from gomobile as far as I could tell.

Then, as per your suggestion, I tried compiling an example from the mobile source. It was the basic example, that draws something (a green triangle on red bg) - independent of ebiten. The error was thus reproduced.

@hajimehoshi
Copy link
Owner

Maybe I'm confused. How exactly is gomobile related to golang.org/x/mobile?

gomobile is a tool in golang.org/x/mobile/cmd. Apparently you could reproduce the issue with the plain go-run, then you don't need to use it.

Then, as per your suggestion, I tried compiling an example from the mobile source. It was the base example, that draws something - independent of ebiten. The error was thus reproduced.

Then, in golang/go#68727, you could report the issue with a script to compile the gomobile example without your Ebitengine code, right?

@hajimehoshi
Copy link
Owner

I'm gonna sleep soon.

My current guess is that GOOS is android in Termux so there are some confusions in various libraries. Even if we could fix the egl issue, I'm not sure I could run an Ebitengine game since Termux doesn't seem to have X11.

@vsm0
Copy link
Author

vsm0 commented Aug 4, 2024

Then, in golang/go#68727, you could report the issue with a script to compile the gomobile example without your Ebitengine code, right?

You mean change the issue content to be x/mobile specific? Yeah, ig. It would be the same script though, because all I did was go into the example directory and type go run .

@vsm0
Copy link
Author

vsm0 commented Aug 4, 2024

My current guess is that GOOS is android in Termux so there are some confusions in various libraries. Even if we could fix the egl issue, I'm not sure I could run an Ebitengine game since Termux doesn't seem to have X11.

It does have x11. The x11 problem was in the Ubuntu setup I added for a different test.

I can run x11 programs just fine. A gui, Java Swing, etc.

The fix could be nothing more than adding a definition condition for Termux to follow the Termux patches, but anyway it's not that important (certainly not for such a long discussion lol)

I'm gonna sleep soon

Same, it's like past 3:00 and I have class tomorrow :')

@hajimehoshi
Copy link
Owner

hajimehoshi commented Aug 4, 2024

The fix should not be so simple for Ebitengine since we would have to change Ebitengine to use Linux version even though GOOS=android.

Perhaps GOOS=linux CGO_ENABLED=1 go run . for your Ebitengine code might work. Please try tomorrow.

You mean change the issue content to be x/mobile specific?

Nah, now I understand the situation more deeply, and Ebitengine should not use gomobile-related code for Termux. So this is not a gomobile issue as far as I understand. Sorry for going back and forth. I don't know whether gomobile examples should work for Termux or not.

Good night!

@hajimehoshi hajimehoshi changed the title EGLNativeWindowType definition mismatch on Android Termux Ebitengine doesn't work on Android Termux Aug 5, 2024
@hajimehoshi hajimehoshi added this to the v2.9.0 milestone Aug 5, 2024
@vsm0
Copy link
Author

vsm0 commented Aug 5, 2024

I revisited my Termux-Ubuntu setup, since it compiled successfully, and I have x11 configured right.

The aforementioned main.go builds, but has a runtime error:

panic: gamepad: Stat failed: permission denied

goroutine 1 [running, locked to thread]:
main.main()
        /home/.../ebiten/main.go:32 +0x68

I was wondering if you have encountered this issue before, or if it would be possible for the time being to forego setting up gamepad for now, just to get something running

@hajimehoshi
Copy link
Owner

I have never seen this error. We might be able to skip gamepad settings in this case, but we need more investigation.

@vsm0
Copy link
Author

vsm0 commented Aug 6, 2024

Just an update of my experiments.
I can't seem to recall whether or not raylib-go actually worked [within standard Termux] before, but the latest I've tried fails to build with familiar errors:

./platforms/rcore_android.c:899:81: error: incompatible pointer to integer conversion passing 'ANativeWindow *' (aka 'struct ANativeWindow *') to parameter of type 'EGLNativeWindowType' (aka 'unsigned long') [-Wint-conversion]
../../../../usr/include/EGL/egl.h:136:109: note: passing argument to parameter 'win' here
In file included from rcore.c:508:
./platforms/rcore_android.c:963:97: error: incompatible pointer to integer conversion passing 'ANativeWindow *' (aka 'struct ANativeWindow *') to parameter of type 'EGLNativeWindowType' (aka 'unsigned long') [-Wint-conversion]
../../../../usr/include/EGL/egl.h:136:109: note: passing argument to parameter 'win' here

Pixel2 (by gen2brain) together with its opengl backend builds and runs fine, surprisingly enough. I wonder if their [gl] codebase is somehow more platform friendly or something. It'd be nice to maybe merge their implementation as a future goal, though I can't begin to imagine how much time/effort doing this would be, nor the legal details

EDIT: raylib-go does build and run in Termux, but within the Ubuntu environment. There is one runtime error:

error: XDG_RUNTIME_DIR is invalid or not set in the environment

But it runs regardless. However, Ebiten still error due to the gamepad

@hajimehoshi
Copy link
Owner

hajimehoshi commented Aug 7, 2024

@vsm0

Is it possible to patch this on your local Ebitengine and see whether it works?

diff --git a/internal/gamepad/gamepad_linux.go b/internal/gamepad/gamepad_linux.go
index d97d43970..17fd00a13 100644
--- a/internal/gamepad/gamepad_linux.go
+++ b/internal/gamepad/gamepad_linux.go
@@ -54,6 +54,9 @@ func (g *nativeGamepadsImpl) init(gamepads *gamepads) error {
                if err == unix.ENOENT {
                        return nil
                }
+               if err == unix.EPERM {
+                       return nil
+               }
                return fmt.Errorf("gamepad: Stat failed: %w", err)
        }
        if stat.Mode&unix.S_IFDIR == 0 {

@vsm0
Copy link
Author

vsm0 commented Aug 7, 2024

It didn't work. This is how I applied the patch:

mkdir workspace
cd workspace
mkdir src
touch src/main.go

I wrote the aforementioned main.go contents into src/main.go

cd src
go mod init src
go mod tidy
cd ..
gh repo clone hajimehoshi/ebiten
go work init ./src
go work use ./src
go work use ./ebiten
go work use ./ebiten/ebitenutil

Then I applied the patch to the clone.

go run -x ./src

The same error occurs.
I know for a fact that my workspace and patch are set up correctly since I added a print statement to the erroring code, just to make sure it was running the local modified repo, not the original:

BECAUSE I AM HERE
panic: gamepad: Stat failed: permission denied                        
goroutine 1 [running, locked to thread]:
main.main()
        /data/data/com.termux/files/home/prog/go/workspace/src/main.go:32 +0x68                                                             exit status 2

@hajimehoshi
Copy link
Owner

BECAUSE I AM HERE

Where did you add this?

@vsm0
Copy link
Author

vsm0 commented Aug 7, 2024

BECAUSE I AM HERE

Where did you add this?

At the top of the patched error checking, before all the if statements.

It [the message] wasn't really to check what the value of the error was, but to acertain the build was pulling from the local repo version not the go cache or github.

func (g *nativeGamepadsImpl) init(gamepads *gamepads) error {
        // Check the existence of the directory `dirName`.
        var stat unix.Stat_t
        if err := unix.Stat(dirName, &stat); err != nil {
                fmt.Println("BECAUSE I AM HERE")
                if err == unix.ENOENT {
                        return nil
                }
                if err == unix.EPERM {
                        return nil
                }
                return fmt.Errorf("gamepad: Stat failed: %w", err)
  }

@hajimehoshi
Copy link
Owner

OK. Apparently, the error "permission denied" is EACCES, not EPERM What about this?

diff --git a/internal/gamepad/gamepad_linux.go b/internal/gamepad/gamepad_linux.go
index d97d43970..17fd00a13 100644
--- a/internal/gamepad/gamepad_linux.go
+++ b/internal/gamepad/gamepad_linux.go
@@ -54,6 +54,9 @@ func (g *nativeGamepadsImpl) init(gamepads *gamepads) error {
                if err == unix.ENOENT {
                        return nil
                }
+               if err == unix.EACCES {
+                       return nil
+               }
                return fmt.Errorf("gamepad: Stat failed: %w", err)
        }
        if stat.Mode&unix.S_IFDIR == 0 {

@vsm0
Copy link
Author

vsm0 commented Aug 7, 2024

Screenshot_20240807-182658.png

:)

Although this is within proot environment. Maybe next goal is baseline Termux /s

@hajimehoshi
Copy link
Owner

As /dev/input is not accessible in your environment, is it possible to use inputs like keyboards?

@vsm0
Copy link
Author

vsm0 commented Aug 7, 2024

Yes, keyboard input does work, even for ebiten.

The Android soft keyboard doesn't provide a sustained press (for checks like ebiten.IsKeyPressed) so the behavior is like IsJustPressed.

The best option is an external keyboard, although back when I was developing using other frameworks I just installed a game keyboard that simulated the expected sustained press.

EDIT: Gboard caused the following log message upon keypress, although this is probably how most Android keyboards work:

(II) Added unknown keysym 0xe0 to keycode 92

But it's a non-critical issue I think, since it works either way.

@hajimehoshi
Copy link
Owner

Thanks. Apparently the keyboard inputs don't have anything to do with /dev/input.

I've update Ebitengine. If there is no other problem, can we close this issue as solved?

@vsm0
Copy link
Author

vsm0 commented Aug 8, 2024

As per the title now, yeah I guess. Again, thanks for the help.

Termux development isn't the best right now, but it's nice that I get to test Ebiten and other frameworks without a computer at hand.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests

2 participants