Skip to content

Commit 3c78eb6

Browse files
committed
Merge branch 'Fix-Vibration'
# Conflicts: # app/src/main/cpp/extras/evshim.c # app/src/main/java/app/gamenative/ui/component/dialog/ControllerTab.kt # app/src/main/java/app/gamenative/ui/screen/xserver/XServerScreen.kt
2 parents c918f04 + 78644aa commit 3c78eb6

18 files changed

Lines changed: 997 additions & 391 deletions

File tree

README.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,6 @@ GameNative allows you to play games you own on Steam, Epic and GOG directly on A
77

88
[Playing Stray on Poco F6](https://github.com/user-attachments/assets/1870fd14-7de9-4054-ba92-d3a5c73686b5)
99

10-
This is a fork of [Pluvia](https://github.com/oxters168/Pluvia), a Steam client for Android.
11-
1210
## How to Use
1311

1412
(Note that GameNative is still in its early stages, and all games may not work, or may require tweaking to get working well)
@@ -42,6 +40,8 @@ Join our [Discord server](https://discord.gg/2hKv4VfZfE) for support and updates
4240
## License
4341
[GPL 3.0](https://github.com/utkarshdalal/GameNative/blob/master/LICENSE)
4442

43+
See [THIRD_PARTY_NOTICES](THIRD_PARTY_NOTICES) for attributions, copyleft source offers, and notices about third-party and proprietary components bundled with the application.
44+
4545
## Analytics & Privacy
4646

4747
GameNative uses [PostHog](https://posthog.com) for anonymous analytics. No personal information is ever collected — no names, emails, IPs, or device identifiers.

THIRD_PARTY_NOTICES

Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,104 @@ This file contains notices for third-party assets and libraries used in this pro
44

55
---
66

7+
## Scope and Completeness
8+
9+
This list is **not exhaustive**. GameNative builds on the broader open-source
10+
Wine / Proton / DXVK / VKD3D / Mesa / box64 / FEX / PulseAudio ecosystem, and
11+
additional smaller components may be present in bundled archives under
12+
`app/src/main/assets/` and prebuilt native libraries under
13+
`app/src/main/jniLibs/`. Where a component is distributed under a copyleft
14+
license (LGPL, GPL, MPL), the terms of that license apply regardless of whether
15+
the component is named in this file. The sections below highlight the items
16+
that most users will want to know about, together with a blanket offer for the
17+
corresponding source of any copyleft component we distribute in binary form.
18+
19+
---
20+
21+
## Availability of Source for Copyleft Components
22+
23+
For any component distributed in binary form with this application that is
24+
covered by a copyleft license (LGPL-2.1, LGPL-3.0, GPL-2.0, GPL-3.0, or
25+
MPL-2.0), the corresponding source code is available from the upstream project
26+
at the revision used. Where practical, the upstream name and revision are
27+
recorded alongside the binary — for example in `VERSION` / `COMMIT` metadata
28+
inside the bundled archive, in the filename of the archive itself, or in
29+
`app/src/main/cpp/` for components built from source in this repository.
30+
31+
If you cannot obtain the source from upstream, contact
32+
Utkarsh Dalal on our
33+
[Discord server](https://discord.gg/2hKv4VfZfE)) and we will supply it, in
34+
accordance with LGPL-2.1 §6, LGPL-3.0 §4, GPL-2.0 §3, and GPL-3.0 §6. This
35+
offer is valid for at least three years from the date on which you received
36+
the binary.
37+
38+
---
39+
40+
## libredirect.so (Proprietary)
41+
42+
**Component:** `libredirect.so`
43+
**Location:** `app/src/main/assets/redirect.tzst`
44+
**License:** Proprietary — all rights reserved by the GameNative maintainers.
45+
46+
`libredirect.so` is a small native shim developed by the GameNative
47+
maintainers. It is loaded **only into Wine / Proton subprocesses** at runtime
48+
via `LD_PRELOAD`; it is not linked with, and does not run inside the address
49+
space of, the Kotlin/Java Android application. Its responsibilities are:
50+
51+
1. **Identifier compatibility.** Some guest-side components embed the
52+
historical Android package identifier `com.winlator`. `libredirect.so`
53+
rewrites those references to the current application identifier at runtime
54+
so that games which hardcode or capture the identifier during launch
55+
continue to boot correctly under GameNative.
56+
2. **Android 15 (API 35) compatibility.** Recent Android releases tightened
57+
restrictions on how executables stored under an app's private data
58+
directory may be invoked. `libredirect.so` adapts affected process
59+
launches so that the emulation stack continues to function on Android 15
60+
and later.
61+
62+
`libredirect.so` was written from scratch by the GameNative maintainers and
63+
is not derived from any code in this repository. It is kept closed-source
64+
for a specific reason. AI-assisted and commercial forks routinely take
65+
open-source Android projects, rename them, and redistribute them through
66+
commercial channels as their own products. Because `libredirect.so` is
67+
specific to this application — not a generic tool that a fork could use
68+
unchanged — publishing its source would reduce rebranding GameNative into
69+
a working fork to a near-trivial operation. Keeping it closed raises the
70+
cost of that path substantially. Every other component in this repository
71+
— the application UI, store integrations, container management, device
72+
configuration, and all code the community actively contributes to —
73+
remains fully open under GPL-3.0 and may be forked, modified, and
74+
redistributed under those terms.
75+
76+
### Relationship to the GPL-3.0 application
77+
78+
GameNative's Kotlin/Java application is distributed under GPL-3.0 and remains
79+
so. `libredirect.so` runs inside separate subprocesses (Wine and Proton
80+
instances) and communicates with those processes through standard
81+
operating-system interfaces only. It is not compiled or linked into the
82+
Android application, does not share the Android application's address space,
83+
and does not expose a library interface to it. Under the FSF's and SFLC's
84+
longstanding interpretation of the GPL's "mere aggregation" language
85+
(GPL-3.0 §5, GPL-2.0 §2), this makes `libredirect.so` a separate program
86+
aggregated with GameNative on the same distribution medium rather than a
87+
derivative work of the application.
88+
89+
### Separability
90+
91+
The GameNative application can be rebuilt without `libredirect.so` by
92+
deleting `app/src/main/assets/redirect.tzst` before building the APK. The
93+
resulting GPL-3.0 application builds, installs, and runs as a standalone
94+
program; it does not require `libredirect.so` to be compiled or linked.
95+
Practical runtime compatibility will be reduced: on Android 15 and later,
96+
most Windows games launched through the bundled emulation stack will fail
97+
to start, and on earlier Android versions some games that embed the
98+
historical `com.winlator` identifier will fail to receive input. This
99+
reflects GPL's guarantee that users can modify and rebuild the application
100+
itself; it does not imply that `libredirect.so` is part of the application
101+
in the sense of GPL-3.0 §0 ("based on").
102+
103+
---
104+
7105
## Kenney Input Prompts
8106

9107
**Asset Pack:** Input Prompts

app/src/main/cpp/extras/evshim.c

Lines changed: 11 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -7,14 +7,14 @@
77
#include <dlfcn.h>
88
#include <fcntl.h>
99
#include <errno.h>
10+
#include <stddef.h>
1011
#include <string.h>
1112
#include <stdlib.h>
1213
#include <stdio.h>
1314
#include <pthread.h>
1415
#include <unistd.h>
1516
#include <SDL2/SDL.h>
1617
#include <stdarg.h>
17-
#include <stdatomic.h>
1818
#include <sys/inotify.h>
1919
#include <poll.h>
2020
#include <time.h>
@@ -32,10 +32,12 @@ static int rumble_fd[MAX_GAMEPADS] = {-1};
3232
static void *handle = NULL;
3333
static pthread_mutex_t shm_mutex = PTHREAD_MUTEX_INITIALIZER;
3434

35-
/* Set to 1 on the player index being refreshed by the SDL keepalive so that
36-
* the resulting re-entrant OnRumble() call does not overwrite shared memory
37-
* (which may have been zeroed by a stop command that raced the keepalive). */
38-
static atomic_int g_keepalive_active[MAX_GAMEPADS];
35+
/* Set to 1 on the keepalive thread before calling SDL_JoystickRumble so the
36+
* resulting synchronous (same-thread) re-entrant OnRumble() call does not
37+
* overwrite shared memory (which may have been zeroed by a stop command that
38+
* raced the keepalive). Thread-local so legitimate OnRumble calls from other
39+
* threads are never suppressed. */
40+
static __thread int t_keepalive_active = 0;
3941

4042
struct gamepad_io {
4143
int16_t lx, ly, rx, ry, lt, rt;
@@ -89,7 +91,7 @@ static int OnRumble(void *userdata,
8991
* We must NOT update last_rumble or pwrite in that case: the game may
9092
* have already sent OnRumble(0,0) to stop vibration, and re-writing
9193
* the old non-zero values would restart rumble on the Android side. */
92-
if (atomic_load(&g_keepalive_active[idx])) {
94+
if (t_keepalive_active) {
9395
LOGD("Rumble P%d low=%u high=%u [keepalive noop]\n", idx,
9496
low_frequency_rumble, high_frequency_rumble);
9597
return 0;
@@ -179,7 +181,7 @@ static void *vjoy_updater(void *arg)
179181
ssize_t n = pread(fd, &cur, sizeof cur, 0);
180182
pthread_mutex_unlock(&shm_mutex);
181183

182-
if (n == sizeof cur && memcmp(&cur, &last_state, sizeof cur) != 0) {
184+
if (n == sizeof cur && memcmp(&cur, &last_state, offsetof(struct gamepad_io, low_freq_rumble)) != 0) {
183185

184186
p_SDL_JoystickSetVirtualAxis (js, 0, cur.lx);
185187
p_SDL_JoystickSetVirtualAxis (js, 1, cur.ly);
@@ -220,9 +222,9 @@ static void *vjoy_updater(void *arg)
220222
/* Flag this player's slot before calling SDL so that the
221223
* synchronous OnRumble() re-entry is recognised as a
222224
* keepalive and does not overwrite shared memory. */
223-
atomic_store(&g_keepalive_active[idx], 1);
225+
t_keepalive_active = 1;
224226
p_SDL_JoystickRumble(js, kl, kh, RUMBLE_KEEPALIVE_DUR_MS);
225-
atomic_store(&g_keepalive_active[idx], 0);
227+
t_keepalive_active = 0;
226228
LOGD("Rumble keepalive P%d low=%u high=%u\n", idx, kl, kh);
227229
}
228230
}
@@ -241,10 +243,6 @@ static void *vjoy_updater(void *arg)
241243
p_SDL_Delay(5);
242244
}
243245
}
244-
245-
/* Unreachable in normal operation; cleanup for early-return paths above. */
246-
if (ino_fd >= 0) close(ino_fd);
247-
return NULL;
248246
}
249247

250248
__attribute__((constructor))

app/src/main/java/app/gamenative/service/SteamService.kt

Lines changed: 6 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -173,6 +173,7 @@ import app.gamenative.statsgen.Achievement
173173
import app.gamenative.statsgen.StatType
174174
import app.gamenative.statsgen.StatsAchievementsGenerator
175175
import app.gamenative.statsgen.VdfParser
176+
import app.gamenative.utils.DownloadSpeedConfig
176177
import java.nio.ByteBuffer
177178
import java.nio.ByteOrder
178179

@@ -1657,34 +1658,11 @@ class SteamService : Service(), IChallengeUrlChanged {
16571658
return@launch
16581659
}
16591660

1660-
// Some notes here:
1661-
// Write should always be 1 in mobile device, as normally it does not use a SSD for storage
1662-
// And to have maximum throughput, set downloadRatio = decompressRatio = 1.0 x CPU Cores
1663-
var downloadRatio = 0.0
1664-
var decompressRatio = 0.0
1665-
1666-
when (PrefManager.downloadSpeed) {
1667-
8 -> {
1668-
downloadRatio = 0.6
1669-
decompressRatio = 0.2
1670-
}
1671-
16 -> {
1672-
downloadRatio = 1.2
1673-
decompressRatio = 0.4
1674-
}
1675-
24 -> {
1676-
downloadRatio = 1.5
1677-
decompressRatio = 0.5
1678-
}
1679-
32 -> {
1680-
downloadRatio = 2.4
1681-
decompressRatio = 0.8
1682-
}
1683-
}
1684-
1685-
val cpuCores = Runtime.getRuntime().availableProcessors()
1686-
val maxDownloads = (cpuCores * downloadRatio).toInt().coerceAtLeast(1)
1687-
val maxDecompress = (cpuCores * decompressRatio).toInt().coerceAtLeast(1)
1661+
// Moved to DownloadSpeedConfig
1662+
val speedConfig = DownloadSpeedConfig()
1663+
val cpuCores = speedConfig.cpuCores
1664+
val maxDownloads = speedConfig.maxDownloads
1665+
val maxDecompress = speedConfig.maxDecompress
16881666

16891667
Timber.i("CPU Cores: $cpuCores")
16901668
Timber.i("maxDownloads: $maxDownloads")

0 commit comments

Comments
 (0)