mirror of
https://github.com/Droid-Deck/DroidDeck.git
synced 2026-11-08 08:04:47 -10:00
Merge main into playstore and harden SDK compatibility
Bring in the ten upstream commits through 55362d68 without rewriting contributor history.
Guard the bottom-screen accessibility watcher on API 33, remove a duplicate HOME query, translate 22 new strings across 32 locales and clean the extracted candidate when runtime installation is cancelled before replacement. Add cancellation coverage while retaining rollback protection.
Validation: 3,293 JVM tests, 1,623 Python tests (eight skips), sanitized GPU harness, release APK and app bundle builds, full lint with zero errors, runtime assets and native dependency/export checks.
Physical verification of this exact candidate, performance parity and native 16 KiB audio support remain pending.
This commit is contained in:
commit
8a8bb2053c
91 files changed
+3025
-604
No files matched your search
@@ -18,6 +18,10 @@
|
||||
<intent>
|
||||
<action android:name="android.content.pm.action.CONFIRM_PERMISSIONS" />
|
||||
</intent>
|
||||
<intent>
|
||||
<action android:name="android.intent.action.MAIN" />
|
||||
<category android:name="android.intent.category.SECONDARY_HOME" />
|
||||
</intent>
|
||||
</queries>
|
||||
|
||||
<uses-permission android:name="android.permission.REORDER_TASKS" />
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
// Whole-GPU busy share without root on Adreno (KGSL) devices whose kgsl sysfs is blocked for apps: a thread
|
||||
// samples the memstore ringbuffer timestamps (kgsl_busy.h) once a millisecond while running.
|
||||
// samples the memstore timestamps (kgsl_busy.h) once a millisecond while running. Ringbuffer entries are used where
|
||||
// the kernel writes them; on hardware-scheduled kernels, which leave them at 0, the contexts' own entries are used.
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <pthread.h>
|
||||
@@ -15,13 +16,21 @@
|
||||
#include "kgsl_busy.h"
|
||||
|
||||
#define SAMPLE_NS 1000000L
|
||||
// Context mode: how many samples between scans of the context entries.
|
||||
#define SCAN_EVERY 200
|
||||
|
||||
enum { MODE_RINGBUFFER = 1, MODE_CONTEXTS = 2 };
|
||||
|
||||
struct reader {
|
||||
int fd;
|
||||
void *map;
|
||||
size_t bytes;
|
||||
int first;
|
||||
int mode;
|
||||
uint32_t last[KGSL_BUSY_SLOTS];
|
||||
uint32_t *seen, *ctx_last;
|
||||
int live[KGSL_BUSY_MAX_LIVE];
|
||||
int live_count;
|
||||
pthread_mutex_t lock;
|
||||
pthread_t thread;
|
||||
int thread_on;
|
||||
@@ -32,9 +41,18 @@ struct reader {
|
||||
static void *sample_loop(void *arg) {
|
||||
struct reader *r = arg;
|
||||
const struct timespec gap = {0, SAMPLE_NS};
|
||||
int until_scan = 0;
|
||||
while (atomic_load(&r->run)) {
|
||||
int moved = 0;
|
||||
int busy = kgsl_busy_sample(r->map, r->first, r->last, &moved);
|
||||
int moved = 0, busy;
|
||||
if (r->mode == MODE_CONTEXTS) {
|
||||
if (until_scan-- <= 0) {
|
||||
r->live_count = kgsl_busy_scan_contexts(r->map, r->first, r->seen, r->live, KGSL_BUSY_MAX_LIVE);
|
||||
until_scan = SCAN_EVERY;
|
||||
}
|
||||
busy = kgsl_busy_sample_contexts(r->map, r->live, r->live_count, r->ctx_last, &moved);
|
||||
} else {
|
||||
busy = kgsl_busy_sample(r->map, r->first, r->last, &moved);
|
||||
}
|
||||
if (busy) atomic_fetch_add(&r->busy, 1);
|
||||
if (moved) atomic_fetch_add(&r->moves, 1);
|
||||
atomic_fetch_add(&r->samples, 1);
|
||||
@@ -62,6 +80,8 @@ static void reader_close(struct reader *r) {
|
||||
reader_run(r, 0);
|
||||
if (r->map && r->map != MAP_FAILED) munmap(r->map, r->bytes);
|
||||
if (r->fd >= 0) close(r->fd);
|
||||
free(r->seen);
|
||||
free(r->ctx_last);
|
||||
pthread_mutex_destroy(&r->lock);
|
||||
free(r);
|
||||
}
|
||||
@@ -85,7 +105,16 @@ static int reader_open(struct reader **out) {
|
||||
int moved = 0, started = 0;
|
||||
kgsl_busy_sample(r->map, r->first, r->last, &moved);
|
||||
for (int i = 0; i < KGSL_BUSY_SLOTS; i++) started |= r->last[i] != 0;
|
||||
if (!started) { reader_close(r); return -ENODATA; }
|
||||
if (started) {
|
||||
r->mode = MODE_RINGBUFFER;
|
||||
} else {
|
||||
r->seen = calloc((size_t)r->first, sizeof(uint32_t));
|
||||
r->ctx_last = calloc((size_t)r->first, sizeof(uint32_t));
|
||||
if (!r->seen || !r->ctx_last) { reader_close(r); return -ENOMEM; }
|
||||
r->live_count = kgsl_busy_scan_contexts(r->map, r->first, r->seen, r->live, KGSL_BUSY_MAX_LIVE);
|
||||
kgsl_busy_sample_contexts(r->map, r->live, r->live_count, r->ctx_last, &moved);
|
||||
r->mode = MODE_CONTEXTS;
|
||||
}
|
||||
*out = r;
|
||||
return 0;
|
||||
}
|
||||
@@ -103,6 +132,10 @@ JNIEXPORT jlong JNICALL Java_com_droiddeck_launcher_session_KgslBusy_nativeOpen(
|
||||
return e ? 0 : (jlong)(intptr_t)r;
|
||||
}
|
||||
|
||||
JNIEXPORT jint JNICALL Java_com_droiddeck_launcher_session_KgslBusy_nativeMode(JNIEnv *env, jclass cls, jlong handle) {
|
||||
return handle ? ((struct reader *)(intptr_t)handle)->mode : 0;
|
||||
}
|
||||
|
||||
JNIEXPORT void JNICALL Java_com_droiddeck_launcher_session_KgslBusy_nativeRun(JNIEnv *env, jclass cls, jlong handle, jboolean on) {
|
||||
if (handle) reader_run((struct reader *)(intptr_t)handle, on ? 1 : 0);
|
||||
}
|
||||
|
||||
@@ -34,4 +34,44 @@ static inline int kgsl_busy_sample(const volatile uint32_t *memstore, int first,
|
||||
return busy;
|
||||
}
|
||||
|
||||
// Hardware-scheduled kernels (every gen8 GPU and some gen7 ones: .gpudev = *_hwsched_gpudev in adreno-gpulist.h)
|
||||
// leave the ringbuffer entries at 0. The GMU firmware writes each context's own entry instead
|
||||
// (gen8_hwsched_hfi.c send_context_pointers). Context ids run from 1 below the ringbuffer entries, lowest free first,
|
||||
// and kgsl zeroes an entry when its context is created or destroyed.
|
||||
#define KGSL_BUSY_MAX_LIVE 64
|
||||
|
||||
// Collects the contexts to sample: every entry below first with a non-zero start stamp (kgsl zeroes an entry when its
|
||||
// context is created or destroyed). Entries whose start stamp changed since the previous scan (seen[]) or that are
|
||||
// busy now come first, so the max_live cap only drops idle ones. Returns how many ids went into live[].
|
||||
static inline int kgsl_busy_scan_contexts(const volatile uint32_t *memstore, int first, uint32_t *seen, int *live, int max_live) {
|
||||
int n = 0, idle = 0;
|
||||
int idle_ids[KGSL_BUSY_MAX_LIVE];
|
||||
for (int id = 1; id < first; id++) {
|
||||
const volatile uint32_t *entry = memstore + (size_t)id * KGSL_BUSY_ENTRY_WORDS;
|
||||
uint32_t sop = entry[KGSL_BUSY_SOP], eop = entry[KGSL_BUSY_EOP];
|
||||
int changed = sop != seen[id];
|
||||
seen[id] = sop;
|
||||
if ((changed || sop != eop) && n < max_live) live[n++] = id;
|
||||
else if (sop != 0 && idle < KGSL_BUSY_MAX_LIVE) idle_ids[idle++] = id;
|
||||
}
|
||||
for (int i = 0; i < idle && n < max_live; i++) live[n++] = idle_ids[i];
|
||||
return n;
|
||||
}
|
||||
|
||||
// 1 when a live context has a submission between its start and end timestamps. *moved is set when a start
|
||||
// timestamp differs from last[] (indexed by context id), which then holds the new ones.
|
||||
static inline int kgsl_busy_sample_contexts(const volatile uint32_t *memstore, const int *live, int count, uint32_t *last, int *moved) {
|
||||
int busy = 0;
|
||||
for (int i = 0; i < count; i++) {
|
||||
const volatile uint32_t *entry = memstore + (size_t)live[i] * KGSL_BUSY_ENTRY_WORDS;
|
||||
uint32_t sop = entry[KGSL_BUSY_SOP], eop = entry[KGSL_BUSY_EOP];
|
||||
if (sop != eop) busy = 1;
|
||||
if (sop != last[live[i]]) {
|
||||
*moved = 1;
|
||||
last[live[i]] = sop;
|
||||
}
|
||||
}
|
||||
return busy;
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -16,21 +16,18 @@
|
||||
#include <unistd.h>
|
||||
|
||||
#include "../adrenotools/src/hook/kgsl.h"
|
||||
#include "kgsl_layout.h"
|
||||
|
||||
#define CP_TYPE7_PKT 0x70000000u
|
||||
#define CP_NOP 0x10u
|
||||
#define CP_REG_TO_MEM 0x3eu
|
||||
#define CP_ALWAYS_ON_COUNTER 0x980u
|
||||
#define RBBM_ALWAYS_COUNT 0u
|
||||
// gen7 CP protection marks registers 0x000-0x4ff readable by command buffers (adreno-gpulist.h); a counter
|
||||
// kgsl places anywhere else is not read.
|
||||
#define READABLE_LAST 0x4ffu
|
||||
#define XO_HZ 19200000.0
|
||||
#define NOP_DWORDS 0x3fffu
|
||||
#define NOP_BLOCKS 4
|
||||
|
||||
struct sampler {
|
||||
int fd;
|
||||
const struct kgsl_layout *layout;
|
||||
unsigned context;
|
||||
unsigned counter_reg;
|
||||
struct kgsl_gpumem_alloc_id ib, out;
|
||||
@@ -60,7 +57,7 @@ static void sampler_close(struct sampler *s) {
|
||||
if (s->out.id) { struct kgsl_gpumem_free_id f = {.id = s->out.id}; ioctl(s->fd, IOCTL_KGSL_GPUMEM_FREE_ID, &f); }
|
||||
if (s->context) { struct kgsl_drawctxt_destroy d = {.drawctxt_id = s->context}; ioctl(s->fd, IOCTL_KGSL_DRAWCTXT_DESTROY, &d); }
|
||||
if (s->counter_reg) {
|
||||
struct kgsl_perfcounter_put p = {.groupid = KGSL_PERFCOUNTER_GROUP_RBBM, .countable = RBBM_ALWAYS_COUNT};
|
||||
struct kgsl_perfcounter_put p = {.groupid = KGSL_PERFCOUNTER_GROUP_RBBM, .countable = s->layout->cycles};
|
||||
ioctl(s->fd, IOCTL_KGSL_PERFCOUNTER_PUT, &p);
|
||||
}
|
||||
close(s->fd);
|
||||
@@ -69,7 +66,7 @@ static void sampler_close(struct sampler *s) {
|
||||
}
|
||||
|
||||
static void emit_reads(struct sampler *s, size_t *n, unsigned pass) {
|
||||
const unsigned regs[2] = {CP_ALWAYS_ON_COUNTER, s->counter_reg};
|
||||
const unsigned regs[2] = {s->layout->always_on, s->counter_reg};
|
||||
for (unsigned k = 0; k < 2; k++) {
|
||||
uint64_t dst = s->out.gpuaddr + (uint64_t)(pass * 2 + k) * sizeof(uint64_t);
|
||||
s->ib_map[(*n)++] = pkt7(CP_REG_TO_MEM, 3);
|
||||
@@ -85,10 +82,16 @@ static int sampler_open(struct sampler **out) {
|
||||
if (!s) return -ENOMEM;
|
||||
s->fd = open("/dev/kgsl-3d0", O_RDWR | O_CLOEXEC);
|
||||
if (s->fd < 0) { int e = -errno; s->fd = -1; sampler_close(s); return e; }
|
||||
struct kgsl_perfcounter_get get = {.groupid = KGSL_PERFCOUNTER_GROUP_RBBM, .countable = RBBM_ALWAYS_COUNT};
|
||||
struct kgsl_devinfo info;
|
||||
memset(&info, 0, sizeof(info));
|
||||
struct kgsl_device_getproperty prop = {.type = KGSL_PROP_DEVICE_INFO, .value = &info, .sizebytes = sizeof(info)};
|
||||
if (ioctl(s->fd, IOCTL_KGSL_DEVICE_GETPROPERTY, &prop)) { int e = -errno; sampler_close(s); return e; }
|
||||
s->layout = kgsl_layout_for(info.chip_id);
|
||||
if (!s->layout) { sampler_close(s); return -ENOTSUP; }
|
||||
struct kgsl_perfcounter_get get = {.groupid = KGSL_PERFCOUNTER_GROUP_RBBM, .countable = s->layout->cycles};
|
||||
if (ioctl(s->fd, IOCTL_KGSL_PERFCOUNTER_GET, &get)) { int e = -errno; sampler_close(s); return e; }
|
||||
s->counter_reg = get.offset;
|
||||
if (!get.offset || get.offset > READABLE_LAST) { sampler_close(s); return -ERANGE; }
|
||||
if (!get.offset || get.offset > s->layout->readable_last) { sampler_close(s); return -ERANGE; }
|
||||
struct kgsl_drawctxt_create ctx = {.flags = KGSL_CONTEXT_NO_GMEM_ALLOC | KGSL_CONTEXT_PREAMBLE};
|
||||
if (ioctl(s->fd, IOCTL_KGSL_DRAWCTXT_CREATE, &ctx)) { int e = -errno; sampler_close(s); return e; }
|
||||
s->context = ctx.drawctxt_id;
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
#ifndef DROIDDECK_KGSL_LAYOUT_H
|
||||
#define DROIDDECK_KGSL_LAYOUT_H
|
||||
#include <stddef.h>
|
||||
|
||||
// Register layout per GPU generation. A counter kgsl places outside the range the kernel's CP protection lets
|
||||
// command buffers read (adreno-gpulist.h *_protected_regs) is not read.
|
||||
struct kgsl_layout {
|
||||
unsigned always_on; // CP_ALWAYS_ON_COUNTER
|
||||
unsigned cycles; // RBBM countable that counts GPU cycles
|
||||
unsigned readable_last; // last register of the readable range starting at 0
|
||||
};
|
||||
// A6xx/A7xx: CP_ALWAYS_ON_COUNTER 0x980, RBBM countable 0 = ALWAYS_COUNT, 0x000-0x4ff readable (gen7 list).
|
||||
static const struct kgsl_layout KGSL_LAYOUT_A7XX = {0x980u, 0u, 0x4ffu};
|
||||
// A8xx: CP_ALWAYS_ON_COUNTER 0x8e7 (gen8_reg.h), RBBM countable 1 = US_ALWAYS_COUNT (0 is NEVER_COUNT, Mesa
|
||||
// a8xx_perfcntrs.xml), 0x000-0x3a3 readable (gen8_0_0_protected_regs).
|
||||
static const struct kgsl_layout KGSL_LAYOUT_A8XX = {0x8e7u, 1u, 0x3a3u};
|
||||
|
||||
// The KGSL chip id's top byte: 0x44 on every A8xx (Adreno 810/829/830/840, Mesa freedreno_devices.py),
|
||||
// 0x43 or the generation number on A6xx/A7xx. Later generations are not known, so nothing is read there.
|
||||
static inline const struct kgsl_layout *kgsl_layout_for(unsigned chip_id) {
|
||||
unsigned core = chip_id >> 24;
|
||||
if (core == 0x44u) return &KGSL_LAYOUT_A8XX;
|
||||
if (core > 0x44u) return NULL;
|
||||
return &KGSL_LAYOUT_A7XX;
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -461,7 +461,7 @@ class FrontEndActivity : ComponentActivity() {
|
||||
private var fexPreset by mutableStateOf("")
|
||||
private var forceSsbs by mutableStateOf(false)
|
||||
private var steamClientArch by mutableStateOf("arm64")
|
||||
private var runSteamAtStartup by mutableStateOf(false)
|
||||
private var startupSession by mutableStateOf(SessionPrefs.STARTUP_OFF)
|
||||
private var bootMovie by mutableStateOf(com.droiddeck.launcher.session.BootMovie.DROIDDECK_30)
|
||||
private var bootMovieSound by mutableStateOf(true)
|
||||
private var bootMovieCustomName by mutableStateOf<String?>(null)
|
||||
@@ -552,7 +552,7 @@ class FrontEndActivity : ComponentActivity() {
|
||||
private var dualDisplayId by mutableStateOf(-1)
|
||||
private var dualScreenDevice by mutableStateOf(false)
|
||||
private var dualScreenMode by mutableStateOf(com.droiddeck.launcher.input.DualScreen.MODE_AUTO)
|
||||
private var dualScreenLauncher by mutableStateOf(true)
|
||||
private var dualScreenSessions by mutableStateOf(com.droiddeck.launcher.input.DualScreen.MODE_AUTO)
|
||||
private var dualScreenControls by mutableStateOf(true)
|
||||
private var launcherStarted = false
|
||||
private lateinit var displayManager: DisplayManager
|
||||
@@ -561,6 +561,7 @@ class FrontEndActivity : ComponentActivity() {
|
||||
override fun onDisplayRemoved(displayId: Int) = refreshSecondScreenDisplays()
|
||||
override fun onDisplayChanged(displayId: Int) = refreshSecondScreenDisplays()
|
||||
}
|
||||
private val bottomScreenListener: () -> Unit = { refreshSecondScreenDisplays() }
|
||||
private val secondScreenTouchListener = object : android.hardware.input.InputManager.InputDeviceListener {
|
||||
override fun onInputDeviceAdded(deviceId: Int) = refreshSecondScreenDisplays()
|
||||
override fun onInputDeviceRemoved(deviceId: Int) = refreshSecondScreenDisplays()
|
||||
@@ -1086,7 +1087,7 @@ class FrontEndActivity : ComponentActivity() {
|
||||
dualScreen = dualDisplayId >= 0 && com.droiddeck.launcher.ui.DualScreenPanel.available,
|
||||
dualScreenDevice = dualScreenDevice,
|
||||
dualScreenMode = dualScreenMode,
|
||||
dualScreenLauncher = dualScreenLauncher,
|
||||
dualScreenSessions = dualScreenSessions,
|
||||
dualScreenControls = dualScreenControls,
|
||||
packages = packageRows,
|
||||
packageCatalogLoading = catalogLoading,
|
||||
@@ -1323,8 +1324,8 @@ class FrontEndActivity : ComponentActivity() {
|
||||
SessionPrefs.setDualScreenMode(this, mode)
|
||||
refreshSecondScreenDisplays()
|
||||
},
|
||||
onDualScreenLauncher = { on ->
|
||||
SessionPrefs.setDualScreenLauncher(this, on)
|
||||
onDualScreenSessions = { mode ->
|
||||
SessionPrefs.setDualScreenSessionMode(this, mode)
|
||||
refreshSecondScreenDisplays()
|
||||
},
|
||||
onDualScreenControls = { on ->
|
||||
@@ -1532,7 +1533,7 @@ class FrontEndActivity : ComponentActivity() {
|
||||
}
|
||||
}
|
||||
|
||||
if (savedInstanceState == null) ui.post { startSteamAtStartupIfEnabled() }
|
||||
if (savedInstanceState == null) ui.post { startSteamAtStartupIfEnabled(cold = true) }
|
||||
else {
|
||||
restoreCompatLaunch(savedInstanceState)
|
||||
pendingSwitch = savedIntent(savedInstanceState, KEY_PENDING_SWITCH)
|
||||
@@ -1601,9 +1602,14 @@ class FrontEndActivity : ComponentActivity() {
|
||||
startSteamAtStartupIfEnabled()
|
||||
}
|
||||
|
||||
private fun startSteamAtStartupIfEnabled() {
|
||||
if (intent.action != Intent.ACTION_MAIN) return
|
||||
if (SessionPrefs.runSteamAtStartup(this) && !isFinishing && !isDestroyed) startSteamSession()
|
||||
private fun startSteamAtStartupIfEnabled(cold: Boolean = false) {
|
||||
if (intent.action != Intent.ACTION_MAIN && !(cold && intent.action == null)) return
|
||||
if (isFinishing || isDestroyed) return
|
||||
when (SessionPrefs.startupSession(this)) {
|
||||
SessionPrefs.STARTUP_STEAM -> startSteamSession()
|
||||
SessionPrefs.STARTUP_DESKTOP -> startSession(Intent(this, SessionActivity::class.java)
|
||||
.putExtra(SessionService.EXTRA_MODE, SessionService.MODE_DESKTOP))
|
||||
}
|
||||
}
|
||||
|
||||
private fun playSteam() {
|
||||
@@ -1792,6 +1798,7 @@ class FrontEndActivity : ComponentActivity() {
|
||||
addDataScheme("file")
|
||||
})
|
||||
displayManager.registerDisplayListener(secondScreenDisplayListener, ui)
|
||||
com.droiddeck.launcher.input.DualScreen.watchTaken(bottomScreenListener)
|
||||
getSystemService(android.hardware.input.InputManager::class.java)?.registerInputDeviceListener(secondScreenTouchListener, ui)
|
||||
launcherStarted = true
|
||||
refreshSecondScreenDisplays()
|
||||
@@ -1836,6 +1843,7 @@ class FrontEndActivity : ComponentActivity() {
|
||||
com.droiddeck.launcher.stores.StoresState.libraryListener = null
|
||||
com.droiddeck.launcher.stores.battlenet.BattleNetClient.opener = null
|
||||
displayManager.unregisterDisplayListener(secondScreenDisplayListener)
|
||||
com.droiddeck.launcher.input.DualScreen.unwatchTaken(bottomScreenListener)
|
||||
getSystemService(android.hardware.input.InputManager::class.java)?.unregisterInputDeviceListener(secondScreenTouchListener)
|
||||
launcherStarted = false
|
||||
com.droiddeck.launcher.ui.DualScreenPanel.hide(taskId)
|
||||
@@ -1874,10 +1882,10 @@ class FrontEndActivity : ComponentActivity() {
|
||||
dualScreenMode = SessionPrefs.dualScreenMode(this)
|
||||
secondScreenDisplays = SecondScreenDisplays.available(displayManager, ownDisplayId)
|
||||
dualScreenDevice = com.droiddeck.launcher.input.DualScreen.anyDisplay(displayManager, ownDisplayId) != null
|
||||
dualScreenLauncher = SessionPrefs.dualScreenLauncher(this)
|
||||
dualScreenSessions = SessionPrefs.dualScreenSessionMode(this)
|
||||
dualScreenControls = SessionPrefs.dualScreenSessionControls(this)
|
||||
val target = com.droiddeck.launcher.input.DualScreen.display(this, displayManager, ownDisplayId)
|
||||
dualDisplayId = if (dualScreenLauncher) target?.displayId ?: -1 else -1
|
||||
dualDisplayId = target?.displayId ?: -1
|
||||
syncDualPanel()
|
||||
}
|
||||
|
||||
@@ -2202,7 +2210,7 @@ class FrontEndActivity : ComponentActivity() {
|
||||
steamRepairQueued = steamRepairQueued,
|
||||
mangoapp = mangoapp,
|
||||
steamController = if (mode == SessionService.MODE_STEAM) steamController else null,
|
||||
runSteamAtStartup = mode == SessionService.MODE_STEAM && runSteamAtStartup,
|
||||
startupSession = startupSession,
|
||||
bootMovie = if (mode == SessionService.MODE_STEAM) bootMovie else null,
|
||||
bootMovieSound = bootMovieSound,
|
||||
bootMovieCustomName = bootMovieCustomName,
|
||||
@@ -2292,9 +2300,9 @@ class FrontEndActivity : ComponentActivity() {
|
||||
onReturnAfterGame = { on -> SessionPrefs.setReturnAfterGame(this, on); returnAfterGame = on },
|
||||
onShaderPrecache = { on -> SessionPrefs.setShaderPrecache(this, on); shaderPrecache = on },
|
||||
onSteamController = { id -> SessionPrefs.setSteamController(this, id); steamController = id },
|
||||
onRunSteamAtStartup = { on ->
|
||||
SessionPrefs.setRunSteamAtStartup(this, on)
|
||||
runSteamAtStartup = on
|
||||
onStartupSession = { id ->
|
||||
SessionPrefs.setStartupSession(this, id)
|
||||
startupSession = id
|
||||
},
|
||||
onBootMovie = { id -> com.droiddeck.launcher.session.BootMovie.select(this, id); loadBootMovie() },
|
||||
onBootMovieSound = { on -> SessionPrefs.setBootMovieSound(this, on); bootMovieSound = on },
|
||||
@@ -2485,7 +2493,7 @@ class FrontEndActivity : ComponentActivity() {
|
||||
returnAfterGame = SessionPrefs.returnAfterGame(this)
|
||||
loadShaderPrecache(mode)
|
||||
steamController = SessionPrefs.steamController(this)
|
||||
runSteamAtStartup = SessionPrefs.runSteamAtStartup(this)
|
||||
startupSession = SessionPrefs.startupSession(this)
|
||||
loadBootMovie()
|
||||
hdrOn = SessionPrefs.hdr(this, mode)
|
||||
upscaler = SessionPrefs.upscaler(this)
|
||||
|
||||
@@ -475,6 +475,11 @@ class SessionActivity : ComponentActivity(), SurfaceHolder.Callback {
|
||||
private var sessionActivityDisplayId = Display.DEFAULT_DISPLAY
|
||||
private lateinit var displayManager: DisplayManager
|
||||
private var secondScreenPresentation: SecondScreenPresentation? = null
|
||||
private val bottomScreenListener: () -> Unit = {
|
||||
if (secondScreenPresentation?.display?.displayId?.let(com.droiddeck.launcher.input.DualScreen::taken) == true) closeSecondScreen(reset = false)
|
||||
refreshSecondScreenDisplays()
|
||||
}
|
||||
|
||||
private val displayListener = object : DisplayManager.DisplayListener {
|
||||
override fun onDisplayAdded(displayId: Int) {
|
||||
notifyActivityDisplayStateChanged(displayId)
|
||||
@@ -2748,15 +2753,14 @@ class SessionActivity : ComponentActivity(), SurfaceHolder.Callback {
|
||||
|
||||
private fun dockDrawer(): Boolean {
|
||||
if (pipUi || !started || glassesScreen != null || !::displayManager.isInitialized) return false
|
||||
if (!SessionPrefs.dualScreenLauncher(this)) return false
|
||||
val target = com.droiddeck.launcher.input.DualScreen.display(this, displayManager, shownOnDisplayId) ?: return false
|
||||
val target = com.droiddeck.launcher.input.DualScreen.sessionDisplay(this, displayManager, shownOnDisplayId) ?: return false
|
||||
if (secondScreenDisplays.none { it.id == target.displayId }) return false
|
||||
val shown = secondScreenPresentation?.display?.displayId
|
||||
return shown == null || shown == target.displayId
|
||||
}
|
||||
|
||||
private fun openDockedMenu(): Boolean {
|
||||
val target = com.droiddeck.launcher.input.DualScreen.display(this, displayManager, shownOnDisplayId) ?: return false
|
||||
val target = com.droiddeck.launcher.input.DualScreen.sessionDisplay(this, displayManager, shownOnDisplayId) ?: return false
|
||||
return try {
|
||||
val presentation = secondScreenPresentation ?: secondScreenPresentation(target).also {
|
||||
selectedSecondScreenDisplay = target.displayId
|
||||
@@ -3041,7 +3045,7 @@ class SessionActivity : ComponentActivity(), SurfaceHolder.Callback {
|
||||
private fun openDualScreenControls() {
|
||||
if (SessionState.secondScreenAutoOpened || secondScreenMode != SecondScreenMode.NONE || pipUi) return
|
||||
if (!SessionPrefs.dualScreenSessionControls(this)) return
|
||||
val target = com.droiddeck.launcher.input.DualScreen.display(this, displayManager, shownOnDisplayId) ?: return
|
||||
val target = com.droiddeck.launcher.input.DualScreen.sessionDisplay(this, displayManager, shownOnDisplayId) ?: return
|
||||
if (secondScreenDisplays.none { it.id == target.displayId }) return
|
||||
SessionState.secondScreenAutoOpened = true
|
||||
selectedSecondScreenDisplay = target.displayId
|
||||
@@ -3089,6 +3093,12 @@ class SessionActivity : ComponentActivity(), SurfaceHolder.Callback {
|
||||
closeSecondScreen(reset = false)
|
||||
return
|
||||
}
|
||||
if (com.droiddeck.launcher.input.DualScreen.taken(target.displayId)) {
|
||||
if (secondScreenPresentation != null) closeSecondScreen(reset = false)
|
||||
secondScreenMode = mode
|
||||
SessionState.secondScreenMode = mode
|
||||
return
|
||||
}
|
||||
var presentation = secondScreenPresentation
|
||||
if (presentation?.display?.displayId != target.displayId) {
|
||||
dropDockedDrawer()
|
||||
@@ -3133,6 +3143,9 @@ class SessionActivity : ComponentActivity(), SurfaceHolder.Callback {
|
||||
onPage = { com.droiddeck.launcher.ui.SessionBottomTab.pick(SessionState.sessionId, it) },
|
||||
onClose = { selectSecondScreenMode(SecondScreenMode.NONE) },
|
||||
onMenu = if (dockDrawer()) ({ drawerOpen = true }) else null,
|
||||
strip = { pageId ->
|
||||
com.droiddeck.launcher.ui.SessionBottomStrip(pageId) { if (::hud.isInitialized) hud.liveFps else null }
|
||||
},
|
||||
)
|
||||
}
|
||||
}),
|
||||
@@ -3294,7 +3307,7 @@ class SessionActivity : ComponentActivity(), SurfaceHolder.Callback {
|
||||
private fun openBottomKeyboard() {
|
||||
if (!started) return
|
||||
if (secondScreenPresentation?.showsKeyboard != true && dockDrawer()) {
|
||||
val target = com.droiddeck.launcher.input.DualScreen.display(this, displayManager, shownOnDisplayId) ?: return
|
||||
val target = com.droiddeck.launcher.input.DualScreen.sessionDisplay(this, displayManager, shownOnDisplayId) ?: return
|
||||
if (drawerOpen) drawerOpen = false
|
||||
typingReturnMode = secondScreenMode
|
||||
selectedSecondScreenDisplay = target.displayId
|
||||
@@ -3976,6 +3989,7 @@ class SessionActivity : ComponentActivity(), SurfaceHolder.Callback {
|
||||
if (::hud.isInitialized) hud.setBackgrounded(false)
|
||||
refreshActivityDisplayId()
|
||||
displayManager.registerDisplayListener(displayListener, Handler(Looper.getMainLooper()))
|
||||
com.droiddeck.launcher.input.DualScreen.watchTaken(bottomScreenListener)
|
||||
for (name in arrayOf("peak_refresh_rate", "min_refresh_rate")) {
|
||||
contentResolver.registerContentObserver(android.provider.Settings.System.getUriFor(name), false, refreshSettingObserver)
|
||||
}
|
||||
@@ -3996,6 +4010,7 @@ class SessionActivity : ComponentActivity(), SurfaceHolder.Callback {
|
||||
if (::hud.isInitialized) hud.setBackgrounded(true)
|
||||
updateHandCamera()
|
||||
displayManager.unregisterDisplayListener(displayListener)
|
||||
com.droiddeck.launcher.input.DualScreen.unwatchTaken(bottomScreenListener)
|
||||
contentResolver.unregisterContentObserver(refreshSettingObserver)
|
||||
closeSecondScreen(reset = false)
|
||||
startedScreens = (startedScreens - 1).coerceAtLeast(0)
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
package com.droiddeck.launcher.hud
|
||||
|
||||
import android.os.SystemClock
|
||||
|
||||
internal object BottomHudPresence {
|
||||
private const val STALE_MS = 2_500L
|
||||
|
||||
@Volatile private var seenAt = 0L
|
||||
|
||||
fun beat() {
|
||||
seenAt = SystemClock.elapsedRealtime()
|
||||
}
|
||||
|
||||
val showing: Boolean get() = seenAt != 0L && SystemClock.elapsedRealtime() - seenAt < STALE_MS
|
||||
}
|
||||
@@ -193,11 +193,15 @@ class DroidDeckHudHost(context: Context) {
|
||||
metrics.snapshot()
|
||||
Thread.sleep(500)
|
||||
val mango = metrics.snapshot()
|
||||
fun shown(value: Any?, unit: String) = if (value == null) "none" else "$value$unit"
|
||||
val readers = com.droiddeck.launcher.session.GpuStatsComponent.readerStatus()
|
||||
file.writeText(metrics.also { it.setMangoReadings(null) }.buildDiagnosticsReport(context) + "\n== As the HUD shows them (MangoHud's sources) ==\n" +
|
||||
"CPU ${mango.cpuPercent}% ${mango.cpuTempC}°C, GPU ${mango.gpuPercent}% ${mango.gpuClockMhz}MHz ${mango.gpuTempC}°C, " +
|
||||
"CPU ${shown(mango.cpuPercent, "%")} ${shown(mango.cpuTempC, "°C")}, GPU ${shown(mango.gpuPercent, "%")} " +
|
||||
"${shown(mango.gpuClockMhz, "MHz")} ${shown(mango.gpuTempC, "°C")}, " +
|
||||
"RAM ${mango.ramUsedText()}/${mango.ramTotalText()}, VRAM ${mango.vramText()}, battery ${mango.battery.percent}% " +
|
||||
"${mango.battery.watts}W charging=${mango.battery.charging}\n" +
|
||||
"GPU feeds: ${com.droiddeck.launcher.session.GpuStatsComponent.sources.ifEmpty { "none (no session, or kgsl absent)" }}\n" +
|
||||
(if (readers.isNotEmpty()) "GPU readers without root: $readers\n" else "") +
|
||||
"CPU temperature zones: ${LinuxRuntime.cpuTempZones().joinToString { it.name }}\n" +
|
||||
"Frames: ${if (feed.engine.isNotEmpty() || feed.pid > 0) "gamescope (pid ${feed.pid}, ${feed.engine})" else "compositor"}, " +
|
||||
"fps ${feed.stats.fps}\n")
|
||||
|
||||
@@ -538,19 +538,19 @@ class DroidDeckHudView(context: Context) : View(context) {
|
||||
}
|
||||
|
||||
private fun buildDockTab() {
|
||||
val numPx = sp(20f)
|
||||
val numPx = sp(TAB_NUM_SP)
|
||||
val spans = tabSpans()
|
||||
val padX = sp(9f); val padY = sp(7f)
|
||||
val padX = sp(TAB_PAD_X_SP); val padY = sp(TAB_PAD_Y_SP)
|
||||
placeRun(padX, padY + capHeight(numPx), spans)
|
||||
val (w, h) = dockTabSize()
|
||||
contentW = w; contentH = h
|
||||
tabRect = RectF(0f, 0f, contentW, contentH)
|
||||
}
|
||||
|
||||
private fun tabSpans() = listOf(Span(one(fpsNow), colValue, sp(20f)), Span("fps", colDim, sp(8.5f)))
|
||||
private fun tabSpans() = listOf(Span(one(fpsNow), colValue, sp(TAB_NUM_SP)), Span("fps", colDim, sp(TAB_LABEL_SP)))
|
||||
|
||||
internal fun dockTabSize(): Pair<Float, Float> =
|
||||
Pair(runWidth(tabSpans()) + 2f * sp(9f), capHeight(sp(20f)) + 2f * sp(7f))
|
||||
Pair(runWidth(tabSpans()) + 2f * sp(TAB_PAD_X_SP), capHeight(sp(TAB_NUM_SP)) + 2f * sp(TAB_PAD_Y_SP))
|
||||
|
||||
private fun drawTab(canvas: Canvas, rect: RectF, edgeOf: DroidDeckHudDock = dock) {
|
||||
val r = sp(8f); val stroke = outlineWidth(); val h = stroke / 2f
|
||||
@@ -1292,8 +1292,8 @@ class DroidDeckHudView(context: Context) : View(context) {
|
||||
canvas.translate(-w / 2f, -h / 2f)
|
||||
canvas.saveLayerAlpha(0f, 0f, w, h, (blend * 255f).roundToInt())
|
||||
drawTab(canvas, RectF(0f, 0f, w, h), previewEdge)
|
||||
var x = sp(9f)
|
||||
val baseline = sp(7f) + capHeight(sp(20f))
|
||||
var x = sp(TAB_PAD_X_SP)
|
||||
val baseline = sp(TAB_PAD_Y_SP) + capHeight(sp(TAB_NUM_SP))
|
||||
for (span in tabSpans()) {
|
||||
drawPaint.textSize = span.sizePx
|
||||
drawPaint.color = blend(span.color)
|
||||
@@ -1483,6 +1483,10 @@ class DroidDeckHudView(context: Context) : View(context) {
|
||||
internal val isDragging: Boolean get() = lockController.isDragging() || tabDragging
|
||||
|
||||
private companion object {
|
||||
const val TAB_NUM_SP = 10f
|
||||
const val TAB_LABEL_SP = 6f
|
||||
const val TAB_PAD_X_SP = 5f
|
||||
const val TAB_PAD_Y_SP = 4f
|
||||
const val REFRESH_MS = 500L
|
||||
private const val HUE_IDLE_MS = 500L
|
||||
const val MIN_SCALE = 0.5f
|
||||
|
||||
@@ -19,6 +19,10 @@ internal class FrameStats {
|
||||
private var sessionStartNs = 0L
|
||||
private var sessionFrames = 0L
|
||||
|
||||
private val slowTimes = LongArray(SLOW_FRAMES)
|
||||
private var slowHead = 0
|
||||
private var slowSize = 0
|
||||
|
||||
@Volatile var fps = 0f
|
||||
private set
|
||||
|
||||
@@ -51,6 +55,11 @@ internal class FrameStats {
|
||||
if (lowsSize < LOWS_FRAMES) lowsSize++
|
||||
if (sessionStartNs == 0L) sessionStartNs = timeNs - frametimeNs
|
||||
sessionFrames++
|
||||
if (frametimeNs > SLOW_NS) {
|
||||
slowTimes[slowHead] = timeNs
|
||||
slowHead = (slowHead + 1) % SLOW_FRAMES
|
||||
if (slowSize < SLOW_FRAMES) slowSize++
|
||||
}
|
||||
}
|
||||
count++
|
||||
}
|
||||
@@ -68,6 +77,8 @@ internal class FrameStats {
|
||||
cachedLowsNs = 0L
|
||||
sessionStartNs = 0L
|
||||
sessionFrames = 0L
|
||||
slowHead = 0
|
||||
slowSize = 0
|
||||
fps = 0f
|
||||
count++
|
||||
}
|
||||
@@ -94,6 +105,16 @@ internal class FrameStats {
|
||||
@Synchronized
|
||||
fun sessionSeconds(nowNs: Long): Float = if (sessionStartNs == 0L) 0f else (nowNs - sessionStartNs) / 1e9f
|
||||
|
||||
@Synchronized
|
||||
fun slowFrames(nowNs: Long, windowNs: Long): Int {
|
||||
var n = 0
|
||||
for (i in 0 until slowSize) {
|
||||
if (nowNs - slowTimes[(slowHead - 1 - i + SLOW_FRAMES) % SLOW_FRAMES] > windowNs) break
|
||||
n++
|
||||
}
|
||||
return n
|
||||
}
|
||||
|
||||
class Lows(val low1Fps: Float, val low01Fps: Float, val low001Fps: Float) {
|
||||
companion object {
|
||||
val EMPTY = Lows(0f, 0f, 0f)
|
||||
@@ -128,6 +149,8 @@ internal class FrameStats {
|
||||
const val FPS_PERIOD_NS = 500_000_000L
|
||||
const val LOWS_FRAMES = 8192
|
||||
private const val PAUSE_NS = 1_000_000_000L
|
||||
const val SLOW_NS = 33_333_334L
|
||||
private const val SLOW_FRAMES = 2048
|
||||
private const val LOWS_RECOMPUTE_NS = 250_000_000L
|
||||
}
|
||||
}
|
||||
@@ -158,6 +181,8 @@ internal class GameFrameFeed(private val file: File, val stats: FrameStats = Fra
|
||||
@Volatile var refreshMilliHz = 0
|
||||
private set
|
||||
|
||||
val live: Boolean get() = fromGamescope
|
||||
|
||||
fun poll(nowNs: Long): Boolean {
|
||||
val compositorAdded = compositorFrames != seenCompositorFrames
|
||||
seenCompositorFrames = compositorFrames
|
||||
|
||||
@@ -870,6 +870,9 @@ public class HudMetrics {
|
||||
|
||||
private long[] lastCoreTotal = null;
|
||||
private long[] lastCoreIdle = null;
|
||||
private static final int PER_CORE_STAT = 1, PER_CORE_ESTIMATE = 2;
|
||||
/** Sources of the last per-core reading: PER_CORE_STAT and/or PER_CORE_ESTIMATE (clock speed); 0 when no core was read. */
|
||||
private volatile int perCoreSources = 0;
|
||||
|
||||
public int[] readPerCorePercent() {
|
||||
int cores = Runtime.getRuntime().availableProcessors();
|
||||
@@ -909,13 +912,20 @@ public class HudMetrics {
|
||||
}
|
||||
}
|
||||
} catch (Exception ignored) {}
|
||||
int sources = 0;
|
||||
for (int i = 0; i < out.length; i++) {
|
||||
if (out[i] >= 0) continue;
|
||||
if (out[i] >= 0) {
|
||||
sources |= PER_CORE_STAT;
|
||||
continue;
|
||||
}
|
||||
Long cur = readLongFromLine("/sys/devices/system/cpu/cpu" + i + "/cpufreq/scaling_cur_freq");
|
||||
Long max = readLongFromLine("/sys/devices/system/cpu/cpu" + i + "/cpufreq/cpuinfo_max_freq");
|
||||
if (cur != null && max != null && max > 0L)
|
||||
if (cur != null && max != null && max > 0L) {
|
||||
out[i] = clampPercent((int) ((Math.max(0L, Math.min(cur, max)) * 100L) / max));
|
||||
sources |= PER_CORE_ESTIMATE;
|
||||
}
|
||||
}
|
||||
perCoreSources = sources;
|
||||
return out;
|
||||
}
|
||||
|
||||
@@ -1008,10 +1018,13 @@ public class HudMetrics {
|
||||
int v = s.perCorePercent[i];
|
||||
cores.append('c').append(i).append('=').append(v < 0 ? "?" : (v + "%"));
|
||||
}
|
||||
if (s.perCorePercent.length > 0)
|
||||
metricHit(sb, "Per-core %", cores.toString(), "/proc/stat (per-cpu lines)");
|
||||
int coreSources = perCoreSources;
|
||||
if (s.perCorePercent.length > 0 && coreSources != 0)
|
||||
metricHit(sb, "Per-core %", cores.toString(), coreSources == PER_CORE_STAT ? "/proc/stat (per-cpu lines)"
|
||||
: coreSources == PER_CORE_ESTIMATE ? "scaling_cur_freq / cpuinfo_max_freq (clock estimate)"
|
||||
: "/proc/stat (per-cpu lines); scaling_cur_freq / cpuinfo_max_freq (clock estimate) for the other cores");
|
||||
else
|
||||
metricMiss(sb, "Per-core %", "/proc/stat");
|
||||
metricMiss(sb, "Per-core %", "/proc/stat", "/sys/devices/system/cpu/cpu*/cpufreq/scaling_cur_freq");
|
||||
|
||||
StringBuilder coreClk = new StringBuilder();
|
||||
for (int i = 0; i < s.perCoreClockMhz.length; i++) {
|
||||
|
||||
@@ -0,0 +1,150 @@
|
||||
package com.droiddeck.launcher.input
|
||||
|
||||
import android.accessibilityservice.AccessibilityService
|
||||
import android.accessibilityservice.AccessibilityServiceInfo
|
||||
import android.content.Intent
|
||||
import android.content.pm.PackageManager
|
||||
import android.graphics.Rect
|
||||
import android.hardware.display.DisplayManager
|
||||
import android.os.Build
|
||||
import android.os.Handler
|
||||
import android.os.HandlerThread
|
||||
import android.util.Log
|
||||
import android.view.Display
|
||||
import android.view.accessibility.AccessibilityEvent
|
||||
import android.view.accessibility.AccessibilityWindowInfo
|
||||
import android.view.inputmethod.InputMethodManager
|
||||
import java.util.concurrent.ConcurrentHashMap
|
||||
|
||||
object BottomScreenGuests {
|
||||
enum class Top { HOME, GUEST, NONE }
|
||||
|
||||
internal const val MIN_COVER = 0.5f
|
||||
|
||||
internal fun classify(pkg: String?, homes: Set<String>): Top = when (pkg) {
|
||||
null -> Top.NONE
|
||||
in homes -> Top.HOME
|
||||
else -> Top.GUEST
|
||||
}
|
||||
|
||||
internal fun holder(topPkg: String?, arrival: String?, homes: Set<String>): String? = when {
|
||||
topPkg != null -> topPkg.takeIf { classify(it, homes) == Top.GUEST }
|
||||
arrival != null -> arrival.takeIf { classify(it, homes) == Top.GUEST }
|
||||
else -> null
|
||||
}
|
||||
|
||||
internal fun ignorable(type: Int): Boolean = type == AccessibilityWindowInfo.TYPE_INPUT_METHOD ||
|
||||
type == AccessibilityWindowInfo.TYPE_ACCESSIBILITY_OVERLAY ||
|
||||
type == AccessibilityWindowInfo.TYPE_SPLIT_SCREEN_DIVIDER ||
|
||||
type == AccessibilityWindowInfo.TYPE_MAGNIFICATION_OVERLAY
|
||||
|
||||
fun supported(model: String = Build.MODEL, sdk: Int = Build.VERSION.SDK_INT): Boolean =
|
||||
sdk >= 33 && DualScreen.yieldsBottom(model)
|
||||
}
|
||||
|
||||
@androidx.annotation.RequiresApi(33)
|
||||
class BottomScreenWatch(private val service: AccessibilityService) {
|
||||
private class Arrival(val pkg: String)
|
||||
|
||||
private val thread = HandlerThread("bottom-screen").apply { start() }
|
||||
private val handler = Handler(thread.looper)
|
||||
private val own = service.packageName
|
||||
private val arrivals = ConcurrentHashMap<Int, Arrival>()
|
||||
private var homes: Set<String> = emptySet()
|
||||
private var keyboards: Set<String> = emptySet()
|
||||
private var holders: Map<Int, String?> = emptyMap()
|
||||
private val check = Runnable { evaluate() }
|
||||
|
||||
fun start() {
|
||||
current = this
|
||||
service.serviceInfo = service.serviceInfo?.apply {
|
||||
eventTypes = eventTypes or AccessibilityEvent.TYPE_WINDOWS_CHANGED or AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED
|
||||
flags = flags or AccessibilityServiceInfo.FLAG_RETRIEVE_INTERACTIVE_WINDOWS
|
||||
}
|
||||
handler.post {
|
||||
homes = homePackages()
|
||||
keyboards = keyboardPackages()
|
||||
evaluate()
|
||||
}
|
||||
}
|
||||
|
||||
fun onEvent(event: AccessibilityEvent) {
|
||||
when (event.eventType) {
|
||||
AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED -> {
|
||||
val pkg = event.packageName?.toString() ?: return
|
||||
if (event.displayId == Display.DEFAULT_DISPLAY || pkg == own || pkg == SYSTEM_UI) return
|
||||
arrivals[event.displayId] = Arrival(pkg)
|
||||
}
|
||||
AccessibilityEvent.TYPE_WINDOWS_CHANGED -> Unit
|
||||
else -> return
|
||||
}
|
||||
handler.removeCallbacks(check)
|
||||
handler.postDelayed(check, 150)
|
||||
}
|
||||
|
||||
fun stop() {
|
||||
handler.removeCallbacksAndMessages(null)
|
||||
handler.post {
|
||||
if (current === this) {
|
||||
current = null
|
||||
DualScreen.setTaken(emptySet())
|
||||
}
|
||||
}
|
||||
thread.quitSafely()
|
||||
}
|
||||
|
||||
private fun evaluate() {
|
||||
val seen = HashMap(arrivals)
|
||||
val all = runCatching { service.windowsOnAllDisplays }.getOrNull() ?: return
|
||||
val displays = service.getSystemService(DisplayManager::class.java) ?: return
|
||||
val taken = mutableMapOf<Int, String?>()
|
||||
for (i in 0 until all.size()) {
|
||||
val id = all.keyAt(i)
|
||||
if (id == Display.DEFAULT_DISPLAY) continue
|
||||
val display = displays.getDisplay(id)?.takeIf(DualScreen::builtIn) ?: continue
|
||||
val windows = all.valueAt(i)
|
||||
val topPkg = windows.asSequence()
|
||||
.filter { !BottomScreenGuests.ignorable(it.type) && cover(it, display) >= BottomScreenGuests.MIN_COVER }
|
||||
.sortedByDescending { it.layer }
|
||||
.firstNotNullOfOrNull { packageOf(it)?.takeUnless { pkg -> pkg == own } }
|
||||
val arrival = seen[id]
|
||||
if (topPkg != null && arrival != null) arrivals.remove(id, arrival)
|
||||
BottomScreenGuests.holder(topPkg, arrival?.pkg?.takeUnless { it in keyboards }, homes)?.let { taken[id] = it }
|
||||
}
|
||||
if (current !== this) return
|
||||
if (taken != holders) Log.i(TAG, "bottom screen holders: $taken")
|
||||
holders = taken
|
||||
DualScreen.setTaken(taken.keys)
|
||||
}
|
||||
|
||||
private fun cover(window: AccessibilityWindowInfo, display: Display): Float {
|
||||
val bounds = Rect().also(window::getBoundsInScreen)
|
||||
val area = display.mode.physicalWidth.toFloat() * display.mode.physicalHeight
|
||||
return if (area <= 0f) 0f else bounds.width().toFloat() * bounds.height() / area
|
||||
}
|
||||
|
||||
private fun packageOf(window: AccessibilityWindowInfo): String? =
|
||||
runCatching { window.getRoot(0)?.packageName?.toString() }.getOrNull()
|
||||
|
||||
private fun homePackages(): Set<String> {
|
||||
val pm = service.packageManager
|
||||
return listOf(Intent.CATEGORY_HOME, Intent.CATEGORY_SECONDARY_HOME).flatMap { category ->
|
||||
runCatching {
|
||||
pm.queryIntentActivities(Intent(Intent.ACTION_MAIN).addCategory(category), PackageManager.MATCH_ALL)
|
||||
.filter { it.priority >= 0 }
|
||||
.mapNotNull { it.activityInfo?.packageName }
|
||||
}.getOrDefault(emptyList())
|
||||
}.toSet()
|
||||
}
|
||||
|
||||
private fun keyboardPackages(): Set<String> =
|
||||
runCatching {
|
||||
service.getSystemService(InputMethodManager::class.java)?.inputMethodList.orEmpty().map { it.packageName }.toSet()
|
||||
}.getOrDefault(emptySet())
|
||||
|
||||
private companion object {
|
||||
@Volatile var current: BottomScreenWatch? = null
|
||||
const val TAG = "BottomScreen"
|
||||
const val SYSTEM_UI = "com.android.systemui"
|
||||
}
|
||||
}
|
||||
@@ -18,6 +18,23 @@ object DualScreen {
|
||||
private const val TYPE_INTERNAL = 1
|
||||
private val knownModels = setOf("retroid pocket duo", "ayn thor")
|
||||
private val touchedDisplays = mutableSetOf<Int>()
|
||||
private val takenListeners = java.util.concurrent.CopyOnWriteArraySet<() -> Unit>()
|
||||
@Volatile private var takenDisplays: Set<Int> = emptySet()
|
||||
private val main by lazy { android.os.Handler(android.os.Looper.getMainLooper()) }
|
||||
|
||||
fun yieldsBottom(model: String): Boolean = model.trim().lowercase().let { m -> knownModels.any(m::startsWith) }
|
||||
|
||||
fun taken(displayId: Int): Boolean = displayId in takenDisplays
|
||||
|
||||
fun setTaken(ids: Set<Int>) {
|
||||
if (ids == takenDisplays) return
|
||||
takenDisplays = ids
|
||||
main.post { takenListeners.forEach { it() } }
|
||||
}
|
||||
|
||||
fun watchTaken(listener: () -> Unit) { takenListeners += listener }
|
||||
|
||||
fun unwatchTaken(listener: () -> Unit) { takenListeners -= listener }
|
||||
|
||||
fun builtInDisplay(displayManager: DisplayManager, primaryDisplayId: Int): Display? {
|
||||
val primary = displayManager.getDisplay(primaryDisplayId)
|
||||
@@ -61,20 +78,25 @@ object DualScreen {
|
||||
(x.max - x.min + 1f).roundToInt() to (y.max - y.min + 1f).roundToInt()
|
||||
}
|
||||
|
||||
fun display(context: Context, displayManager: DisplayManager, primaryDisplayId: Int): Display? =
|
||||
when (SessionPrefs.dualScreenMode(context)) {
|
||||
fun sessionDisplay(context: Context, displayManager: DisplayManager, primaryDisplayId: Int): Display? =
|
||||
display(context, displayManager, primaryDisplayId, SessionPrefs.dualScreenSessionMode(context))
|
||||
|
||||
fun display(context: Context, displayManager: DisplayManager, primaryDisplayId: Int, mode: String = SessionPrefs.dualScreenMode(context)): Display? =
|
||||
when (mode) {
|
||||
MODE_OFF -> null
|
||||
MODE_ON -> internalDisplay(displayManager, primaryDisplayId) ?: anyDisplay(displayManager, primaryDisplayId)
|
||||
else -> builtInDisplay(displayManager, primaryDisplayId)
|
||||
}
|
||||
}?.takeUnless { taken(it.displayId) }
|
||||
|
||||
fun anyDisplay(displayManager: DisplayManager, primaryDisplayId: Int): Display? =
|
||||
displayManager.getDisplays(DisplayManager.DISPLAY_CATEGORY_PRESENTATION).firstOrNull {
|
||||
it.isValid && it.displayId != primaryDisplayId && (it.flags and Display.FLAG_PRESENTATION) != 0
|
||||
}
|
||||
|
||||
fun builtIn(display: Display): Boolean = isBuiltIn(displayType(display), Build.MODEL)
|
||||
|
||||
internal fun isBuiltIn(type: Int?, model: String): Boolean =
|
||||
if (type != null) type == TYPE_INTERNAL else model.trim().lowercase() in knownModels
|
||||
if (type != null) type == TYPE_INTERNAL else yieldsBottom(model)
|
||||
|
||||
private fun displayType(display: Display): Int? =
|
||||
runCatching { Display::class.java.getMethod("getType").invoke(display) as Int }.getOrNull()
|
||||
|
||||
@@ -46,12 +46,20 @@ object HomeButtonShortcuts {
|
||||
}
|
||||
|
||||
class HomeButtonService : AccessibilityService(), InputManager.InputDeviceListener {
|
||||
private var bottomScreen: BottomScreenWatch? = null
|
||||
|
||||
override fun onServiceConnected() {
|
||||
getSystemService(InputManager::class.java).registerInputDeviceListener(this, Handler(Looper.getMainLooper()))
|
||||
if (android.os.Build.VERSION.SDK_INT >= 33 && bottomScreen == null && BottomScreenGuests.supported()) bottomScreen = BottomScreenWatch(this).also { it.start() }
|
||||
}
|
||||
|
||||
private fun stopBottomScreen() {
|
||||
if (android.os.Build.VERSION.SDK_INT >= 33) bottomScreen?.stop()
|
||||
bottomScreen = null
|
||||
}
|
||||
|
||||
override fun onKeyEvent(event: KeyEvent) = HomeButtonShortcuts.onKeyEvent(this, event)
|
||||
override fun onAccessibilityEvent(event: AccessibilityEvent?) {}
|
||||
override fun onAccessibilityEvent(event: AccessibilityEvent?) { if (android.os.Build.VERSION.SDK_INT >= 33) event?.let { bottomScreen?.onEvent(it) } }
|
||||
override fun onInterrupt() { HomeButtonShortcuts.release() }
|
||||
override fun onInputDeviceAdded(deviceId: Int) {}
|
||||
override fun onInputDeviceChanged(deviceId: Int) {}
|
||||
@@ -59,12 +67,14 @@ class HomeButtonService : AccessibilityService(), InputManager.InputDeviceListen
|
||||
|
||||
override fun onUnbind(intent: Intent?): Boolean {
|
||||
HomeButtonShortcuts.release()
|
||||
stopBottomScreen()
|
||||
com.droiddeck.launcher.core.SteamIntegration.syncShortcutsListing(this)
|
||||
return super.onUnbind(intent)
|
||||
}
|
||||
|
||||
override fun onDestroy() {
|
||||
HomeButtonShortcuts.release()
|
||||
stopBottomScreen()
|
||||
getSystemService(InputManager::class.java).unregisterInputDeviceListener(this)
|
||||
super.onDestroy()
|
||||
}
|
||||
|
||||
@@ -318,7 +318,12 @@ public final class LinuxRuntimeInstaller {
|
||||
RuntimeFileTree.delete(staging, null);
|
||||
return false;
|
||||
}
|
||||
requireNotInterrupted();
|
||||
try {
|
||||
requireNotInterrupted();
|
||||
} catch (InterruptedIOException e) {
|
||||
discard(staging, null);
|
||||
throw e;
|
||||
}
|
||||
FileUtils.writeString(new File(staging, VERSION_FILE), release.version);
|
||||
|
||||
if (!discard(old, null)) {
|
||||
|
||||
@@ -21,7 +21,8 @@ class GpuMemComponent(private val file: File, private val root: File) : SessionP
|
||||
thread = Thread({
|
||||
while (running) {
|
||||
try { Thread.sleep(PERIOD_MS) } catch (e: InterruptedException) { break }
|
||||
if (running && (OverlayShown.check(root) || com.droiddeck.launcher.hud.DroidDeckHudHost.showing)) sample()?.let { write(it) }
|
||||
if (running && (OverlayShown.check(root) || com.droiddeck.launcher.hud.DroidDeckHudHost.showing ||
|
||||
com.droiddeck.launcher.hud.BottomHudPresence.showing)) sample()?.let { write(it) }
|
||||
}
|
||||
}, "gpu-mem").apply { isDaemon = true; start() }
|
||||
}
|
||||
|
||||
@@ -31,6 +31,8 @@ class GpuStatsComponent(val dir: File, private val root: File) : SessionPart() {
|
||||
|
||||
fun prepare(): Boolean {
|
||||
sources = ""
|
||||
KgslBusy.status = "not tried"
|
||||
KgslClock.status = "not tried"
|
||||
if (!File(KGSL).exists()) return false
|
||||
val unreadable = MANGOHUD_FILES.any { File(KGSL, it).let { f -> f.exists() && !f.canRead() } }
|
||||
// Where SELinux hides the kgsl nodes completely (exists() is false too), the layer and cycle-counter feeds stand in.
|
||||
@@ -96,6 +98,7 @@ class GpuStatsComponent(val dir: File, private val root: File) : SessionPart() {
|
||||
if (load.failed) {
|
||||
memstore?.close()
|
||||
memstore = null
|
||||
KgslBusy.status += ", but the stamps never moved"
|
||||
Log.i(TAG, "hud: kgsl memstore timestamps never moved; GPU load from Vulkan timing")
|
||||
} else if (load.working) {
|
||||
Log.i(TAG, "hud: GPU load from the kgsl memstore (all GPU work)")
|
||||
@@ -114,7 +117,9 @@ class GpuStatsComponent(val dir: File, private val root: File) : SessionPart() {
|
||||
}
|
||||
|
||||
private fun memstoreSource(): String = when {
|
||||
memstoreLoad?.working == true -> "kgsl memstore ringbuffer timestamps (all GPU work)"
|
||||
memstoreLoad?.working == true ->
|
||||
if (memstore?.mode == KgslBusy.MODE_CONTEXTS) "kgsl memstore per-context timestamps (all GPU work)"
|
||||
else "kgsl memstore ringbuffer timestamps (all GPU work)"
|
||||
memstoreLoad?.failed == true -> LAYER_SOURCE
|
||||
else -> "kgsl memstore once its timestamps move; until then $LAYER_SOURCE"
|
||||
}
|
||||
@@ -133,7 +138,8 @@ class GpuStatsComponent(val dir: File, private val root: File) : SessionPart() {
|
||||
sources = ""
|
||||
}
|
||||
|
||||
private fun hudShown(): Boolean = OverlayShown.check(root) || com.droiddeck.launcher.hud.DroidDeckHudHost.showing
|
||||
private fun hudShown(): Boolean = OverlayShown.check(root) || com.droiddeck.launcher.hud.DroidDeckHudHost.showing ||
|
||||
com.droiddeck.launcher.hud.BottomHudPresence.showing
|
||||
|
||||
// Android hides the load (kgsl sysfs blocked): the kgsl memstore gives the whole GPU's busy share. The in-game
|
||||
// Vulkan layer and the compositor time their own GPU work until the memstore timestamps are seen moving, and
|
||||
@@ -338,6 +344,12 @@ class GpuStatsComponent(val dir: File, private val root: File) : SessionPart() {
|
||||
|
||||
/** Where the running session's GPU feeds come from, for the HUD diagnostics export. */
|
||||
@Volatile var sources = ""
|
||||
|
||||
/** Why the no-root GPU readers work or not, for the HUD diagnostics export; empty where they were not needed. */
|
||||
fun readerStatus(): String =
|
||||
if (KgslBusy.status == "not tried" && KgslClock.status == "not tried") ""
|
||||
else "kgsl memstore ${KgslBusy.status}; GPU cycle counter ${KgslClock.status}"
|
||||
|
||||
private const val STALE_MS = 30_000L
|
||||
|
||||
fun guestLoadDir(root: File) = File(root, GUEST_LOAD_DIR.removePrefix("/"))
|
||||
|
||||
@@ -108,5 +108,9 @@ class HwmonComponent(val dir: File, private val root: File) : SessionPart() {
|
||||
|
||||
private fun readNumber(path: String): Long? =
|
||||
runCatching { File(path).readText().trim().toLong() }.getOrNull()?.takeIf { it >= 0 }
|
||||
|
||||
fun fanSource(): String? = FAN_SOURCES().firstOrNull { readNumber(it) != null }
|
||||
|
||||
fun readRpm(path: String): Long? = readNumber(path)
|
||||
}
|
||||
}
|
||||
@@ -10,6 +10,7 @@ object KgslBusy {
|
||||
private const val TAG = "SessionService"
|
||||
|
||||
@JvmStatic private external fun nativeOpen(error: IntArray): Long
|
||||
@JvmStatic private external fun nativeMode(handle: Long): Int
|
||||
@JvmStatic private external fun nativeRun(handle: Long, on: Boolean)
|
||||
@JvmStatic private external fun nativeRead(handle: Long, out: LongArray): Boolean
|
||||
@JvmStatic private external fun nativeClose(handle: Long)
|
||||
@@ -24,7 +25,10 @@ object KgslBusy {
|
||||
if (handle != 0L) nativeRun(handle, on)
|
||||
}
|
||||
|
||||
/** Running totals: busy samples, samples, samples in which a ringbuffer started new work. */
|
||||
/** Which memstore entries are watched: [MODE_RINGBUFFER] or [MODE_CONTEXTS] (hardware-scheduled kernels). */
|
||||
val mode: Int = if (handle != 0L) runCatching { nativeMode(handle) }.getOrDefault(0) else 0
|
||||
|
||||
/** Running totals: busy samples, samples, samples in which a ringbuffer or context started new work. */
|
||||
@Synchronized fun counts(): LongArray? {
|
||||
if (handle == 0L) return null
|
||||
val out = LongArray(3)
|
||||
@@ -37,14 +41,27 @@ object KgslBusy {
|
||||
}
|
||||
}
|
||||
|
||||
const val MODE_RINGBUFFER = 1
|
||||
const val MODE_CONTEXTS = 2
|
||||
|
||||
/** What the last [open] found, for the HUD diagnostics export. */
|
||||
@Volatile var status = "not tried"
|
||||
|
||||
fun open(): Reader? {
|
||||
if (!loaded) return null
|
||||
if (!loaded) {
|
||||
status = "gpuclock library did not load"
|
||||
return null
|
||||
}
|
||||
val error = IntArray(1)
|
||||
val handle = runCatching { nativeOpen(error) }.getOrDefault(0L)
|
||||
if (handle == 0L) {
|
||||
status = "open failed: ${KgslErrors.describe(error[0])}"
|
||||
Log.i(TAG, "hud: no GPU load from the kgsl memstore (open error ${error[0]})")
|
||||
return null
|
||||
}
|
||||
return Reader(handle)
|
||||
val reader = Reader(handle)
|
||||
status = if (reader.mode == MODE_CONTEXTS) "per-context stamps (hardware-scheduled kernel)" else "ringbuffer stamps"
|
||||
Log.i(TAG, "hud: kgsl memstore reader uses $status")
|
||||
return reader
|
||||
}
|
||||
}
|
||||
@@ -20,7 +20,10 @@ object KgslClock {
|
||||
|
||||
class Sampler internal constructor(private var handle: Long) : AutoCloseable {
|
||||
/** MHz, or null when this sample was refused or failed its checks. */
|
||||
@Synchronized fun mhz(): Int? = if (handle == 0L) null else nativeSample(handle).takeIf { it > 0 }
|
||||
@Synchronized fun mhz(): Int? = sample().takeIf { it > 0 }
|
||||
|
||||
/** MHz, or a negative native code: -errno from kgsl or [RANGE_CHECK_FAILED]. */
|
||||
@Synchronized fun sample(): Int = if (handle == 0L) CLOSED else nativeSample(handle)
|
||||
|
||||
@Synchronized override fun close() {
|
||||
if (handle != 0L) nativeClose(handle)
|
||||
@@ -28,22 +31,41 @@ object KgslClock {
|
||||
}
|
||||
}
|
||||
|
||||
/** A sampler that already produced one good reading, or null (the reason goes to the log). */
|
||||
/** -EBADF, what the native side also returns for a closed handle. */
|
||||
private const val CLOSED = -9
|
||||
|
||||
/** The native sample code for a reading that failed its range checks (kgsl_clock.c sampler_mhz). */
|
||||
const val RANGE_CHECK_FAILED = -1000
|
||||
|
||||
/** What the last [open] found, for the HUD diagnostics export. */
|
||||
@Volatile var status = "not tried"
|
||||
|
||||
/** A sampler that already produced one good reading, or null (the reason goes to the log and [status]). */
|
||||
fun open(): Sampler? {
|
||||
if (!loaded) return null
|
||||
if (!loaded) {
|
||||
status = "gpuclock library did not load"
|
||||
return null
|
||||
}
|
||||
val error = IntArray(1)
|
||||
val handle = runCatching { nativeOpen(error) }.getOrDefault(0L)
|
||||
if (handle == 0L) {
|
||||
status = "open failed: ${KgslErrors.describe(error[0])}"
|
||||
Log.i(TAG, "hud: no GPU clock from the cycle counter (open error ${error[0]})")
|
||||
return null
|
||||
}
|
||||
val sampler = Sampler(handle)
|
||||
val first = (0 until 3).firstNotNullOfOrNull { sampler.mhz() }
|
||||
var code = 0
|
||||
val first = (0 until 3).firstNotNullOfOrNull {
|
||||
code = sampler.sample()
|
||||
code.takeIf { it > 0 }
|
||||
}
|
||||
if (first == null) {
|
||||
Log.i(TAG, "hud: GPU cycle counter gave no usable reading")
|
||||
status = "no usable reading: ${KgslErrors.describe(code)}"
|
||||
Log.i(TAG, "hud: GPU cycle counter gave no usable reading ($code)")
|
||||
sampler.close()
|
||||
return null
|
||||
}
|
||||
status = "working ($first MHz at start)"
|
||||
Log.i(TAG, "hud: GPU clock from the cycle counter ($first MHz now)")
|
||||
return sampler
|
||||
}
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
package com.droiddeck.launcher.session
|
||||
|
||||
/** Readable text for the negative errno codes the gpuclock library returns. */
|
||||
internal object KgslErrors {
|
||||
fun describe(code: Int): String {
|
||||
if (code == 0) return "no error code"
|
||||
if (code == KgslClock.RANGE_CHECK_FAILED) return "sample out of range"
|
||||
val name = runCatching { android.system.OsConstants.errnoName(-code) }.getOrNull()
|
||||
return if (name != null) "$name ($code)" else "error $code"
|
||||
}
|
||||
}
|
||||
@@ -86,12 +86,26 @@ object SessionPrefs {
|
||||
prefs(context).edit().putBoolean("animations", on).apply()
|
||||
}
|
||||
|
||||
fun dualScreenMode(context: Context): String =
|
||||
prefs(context).getString("dualScreenMode", null)?.takeIf { it in com.droiddeck.launcher.input.DualScreen.modes }
|
||||
?: com.droiddeck.launcher.input.DualScreen.MODE_AUTO
|
||||
private fun savedDualScreenMode(context: Context, key: String): String? =
|
||||
prefs(context).getString(key, null)?.takeIf { it in com.droiddeck.launcher.input.DualScreen.modes }
|
||||
|
||||
private fun legacyDualScreenMode(context: Context): String =
|
||||
if (!prefs(context).getBoolean("dualScreenLauncher", true)) com.droiddeck.launcher.input.DualScreen.MODE_OFF
|
||||
else savedDualScreenMode(context, "dualScreenMode") ?: com.droiddeck.launcher.input.DualScreen.MODE_AUTO
|
||||
|
||||
fun dualScreenMode(context: Context): String = legacyDualScreenMode(context)
|
||||
|
||||
fun setDualScreenMode(context: Context, mode: String) {
|
||||
prefs(context).edit().putString("dualScreenMode", mode).apply()
|
||||
val sessions = dualScreenSessionMode(context)
|
||||
prefs(context).edit().remove("dualScreenLauncher").putString("dualScreenMode", mode).putString("dualScreenSessionMode", sessions).apply()
|
||||
}
|
||||
|
||||
fun dualScreenSessionMode(context: Context): String =
|
||||
savedDualScreenMode(context, "dualScreenSessionMode") ?: legacyDualScreenMode(context)
|
||||
|
||||
fun setDualScreenSessionMode(context: Context, mode: String) {
|
||||
val launcher = dualScreenMode(context)
|
||||
prefs(context).edit().remove("dualScreenLauncher").putString("dualScreenMode", launcher).putString("dualScreenSessionMode", mode).apply()
|
||||
}
|
||||
|
||||
fun secondScreenPanel(context: Context): String? = prefs(context).getString("secondScreenPanel", null)
|
||||
@@ -113,6 +127,31 @@ object SessionPrefs {
|
||||
prefs(context).edit().putString("statsStrip", id).apply()
|
||||
}
|
||||
|
||||
fun bottomStripMetrics(context: Context): Set<StripMetric> = StripMetric.parse(prefs(context).getString("bottomStripMetrics", null))
|
||||
|
||||
fun setBottomStripMetric(context: Context, metric: StripMetric, on: Boolean) {
|
||||
val now = bottomStripMetrics(context).let { if (on) it + metric else it - metric }
|
||||
prefs(context).edit().putString("bottomStripMetrics", StripMetric.encode(now)).apply()
|
||||
}
|
||||
|
||||
fun bottomHud(context: Context): Boolean = prefs(context).getBoolean("bottomHud", true)
|
||||
|
||||
fun setBottomHud(context: Context, on: Boolean) {
|
||||
prefs(context).edit().putBoolean("bottomHud", on).apply()
|
||||
}
|
||||
|
||||
fun bottomStripOnTabs(context: Context): Boolean = prefs(context).getBoolean("bottomStripTabs", true)
|
||||
|
||||
fun setBottomStripOnTabs(context: Context, on: Boolean) {
|
||||
prefs(context).edit().putBoolean("bottomStripTabs", on).apply()
|
||||
}
|
||||
|
||||
fun statsHud(context: Context): String? = prefs(context).getString("statsHud", null)
|
||||
|
||||
fun setStatsHud(context: Context, id: String) {
|
||||
prefs(context).edit().putString("statsHud", id).apply()
|
||||
}
|
||||
|
||||
fun statsSpeedUnit(context: Context): String? = prefs(context).getString("statsSpeedUnit", null)
|
||||
|
||||
fun setStatsSpeedUnit(context: Context, id: String) {
|
||||
@@ -137,12 +176,6 @@ object SessionPrefs {
|
||||
prefs(context).edit().putStringSet("telemetryOff", keys.toSet()).apply()
|
||||
}
|
||||
|
||||
fun dualScreenLauncher(context: Context): Boolean = prefs(context).getBoolean("dualScreenLauncher", true)
|
||||
|
||||
fun setDualScreenLauncher(context: Context, on: Boolean) {
|
||||
prefs(context).edit().putBoolean("dualScreenLauncher", on).apply()
|
||||
}
|
||||
|
||||
fun dualScreenSessionControls(context: Context): Boolean = prefs(context).getBoolean("dualScreenSessionControls", true)
|
||||
|
||||
fun setDualScreenSessionControls(context: Context, on: Boolean) {
|
||||
@@ -939,10 +972,17 @@ object SessionPrefs {
|
||||
prefs(context).edit().apply { if (name == null) remove("bootMovieCustomName") else putString("bootMovieCustomName", name) }.apply()
|
||||
}
|
||||
|
||||
fun runSteamAtStartup(context: Context): Boolean = prefs(context).getBoolean("runSteamAtStartup", false)
|
||||
const val STARTUP_OFF = ""
|
||||
const val STARTUP_STEAM = "steam"
|
||||
const val STARTUP_DESKTOP = "desktop"
|
||||
|
||||
fun setRunSteamAtStartup(context: Context, on: Boolean) {
|
||||
prefs(context).edit().putBoolean("runSteamAtStartup", on).apply()
|
||||
fun startupSession(context: Context): String {
|
||||
val p = prefs(context)
|
||||
return p.getString("startupSession", null) ?: if (p.getBoolean("runSteamAtStartup", false)) STARTUP_STEAM else STARTUP_OFF
|
||||
}
|
||||
|
||||
fun setStartupSession(context: Context, id: String) {
|
||||
prefs(context).edit().putString("startupSession", id).remove("runSteamAtStartup").apply()
|
||||
}
|
||||
|
||||
fun addedGamesDirs(context: Context): List<String> {
|
||||
|
||||
@@ -11,8 +11,8 @@ object SettingsReset {
|
||||
private const val TAG = "SettingsReset"
|
||||
|
||||
internal val SESSION_KEYS = setOf(
|
||||
"orientation", "launcherFullscreen", "animations", "theme", "runSteamAtStartup", "addedGamesArt",
|
||||
"dualScreenMode", "dualScreenLauncher", "dualScreenSessionControls",
|
||||
"orientation", "launcherFullscreen", "animations", "theme", "runSteamAtStartup", "startupSession", "addedGamesArt",
|
||||
"dualScreenMode", "dualScreenSessionMode", "dualScreenLauncher", "dualScreenSessionControls",
|
||||
"gameStoresEnabled", "gameStoresSpeedTier", "gameStoresParallel", "storesOpenTab", "storesShowMature",
|
||||
"storesInstallLanguage",
|
||||
"hud", "hudDeck", "hudFps", "hudBattery", "hudGraph", "hudColor", "hudOpacity", "__KEEP_droidDeckHud__", "__KEEP_droidDeckHudPos__", "backHold", "backActionsInverted", "backDoubleTapMs", "touch", "osc", "shape", "steamController",
|
||||
@@ -26,7 +26,7 @@ object SettingsReset {
|
||||
"upscaler", "upscaleSharpness", "screenEffects", "textureAnisotropy", "textureLodBias",
|
||||
"steamDownloadsInBackground", "steam_offline", "bootMovie", "bootMovieSound", "bootMovieVolume", "bootMovieSeconds",
|
||||
"bootMovieApplyNext", "bootMovieSteamSeen",
|
||||
"questDefaultsV1", "telemetryAuto", "telemetryOff", "statsStrip", "statsDash", "statsSpeedUnit",
|
||||
"questDefaultsV1", "telemetryAuto", "telemetryOff", "statsStrip", "statsHud", "bottomHud", "bottomStripMetrics", "bottomStripTabs", "statsDash", "statsSpeedUnit",
|
||||
)
|
||||
|
||||
internal val SESSION_PREFIXES = listOf(
|
||||
|
||||
@@ -1,12 +1,38 @@
|
||||
package com.droiddeck.launcher.session
|
||||
|
||||
enum class StatsStrip(val id: String) {
|
||||
PANEL("panel"), TILES("tiles"), RINGS("rings");
|
||||
LINE("line"), PANEL("panel"), TILES("tiles"), RINGS("rings");
|
||||
|
||||
fun next(): StatsStrip = entries[(ordinal + 1) % entries.size]
|
||||
|
||||
companion object {
|
||||
fun from(id: String?): StatsStrip = entries.firstOrNull { it.id == id } ?: PANEL
|
||||
fun from(id: String?): StatsStrip = entries.firstOrNull { it.id == id } ?: LINE
|
||||
}
|
||||
}
|
||||
|
||||
enum class StripMetric(val id: String, val defaultOn: Boolean = true) {
|
||||
ENGINE("engine"), GRAPH("graph"), LOWS("lows"), LOW001("low001"), GPU("gpu"), GPU_TEMP("gputemp", false),
|
||||
CPU("cpu"), CPU_TEMP("cputemp"), VRAM("vram"), RAM("ram"), CLOCK("clock");
|
||||
|
||||
companion object {
|
||||
val defaults: Set<StripMetric> = entries.filter { it.defaultOn }.toSet()
|
||||
|
||||
fun parse(saved: String?): Set<StripMetric> =
|
||||
if (saved == null) defaults
|
||||
else saved.split(',').mapNotNull { id -> entries.firstOrNull { it.id == id.trim() } }.toSet()
|
||||
|
||||
fun encode(on: Set<StripMetric>): String = entries.filter { it in on }.joinToString(",") { it.id }
|
||||
}
|
||||
}
|
||||
|
||||
enum class StatsHud(val id: String) {
|
||||
BIG_FPS("bigfps"), FULL("full"), TILES("tiles"), RINGS("rings"), GRAPHS("graphs"), CORES("cores"),
|
||||
HEAT("heat"), GAME("game"), PACING("pacing"), NIGHT("night"), MEGA("mega");
|
||||
|
||||
fun next(): StatsHud = entries[(ordinal + 1) % entries.size]
|
||||
|
||||
companion object {
|
||||
fun from(id: String?): StatsHud = entries.firstOrNull { it.id == id } ?: BIG_FPS
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -246,7 +246,7 @@ object StoreInstallRoot {
|
||||
val sidecar = StoreGameSidecar.read(folder)
|
||||
when {
|
||||
sidecar == null -> Unfinished(store, null, folder)
|
||||
store == Store.GOG && sidecar.state == StoreGameSidecar.STATE_UPDATING -> null
|
||||
sidecar.state == StoreGameSidecar.STATE_UPDATING -> null
|
||||
!sidecar.isInstalled -> Unfinished(store, sidecar.id, folder)
|
||||
else -> null
|
||||
}
|
||||
|
||||
@@ -81,7 +81,7 @@ object StoresState {
|
||||
runCatching { com.droiddeck.launcher.stores.battlenet.BattleNetClient.syncAccount(app) }
|
||||
val found = Store.entries.mapNotNull { store -> StoreAccounts.displayName(app, store)?.let { store to it } }
|
||||
val onDisk = StoreInstallRoot.gameFolders(app).mapNotNull { folder ->
|
||||
StoreGameSidecar.read(folder)?.takeIf { it.isInstalled || it.store == Store.GOG && it.state == StoreGameSidecar.STATE_UPDATING }?.let { InstalledStoreGame(it, folder) }
|
||||
StoreGameSidecar.read(folder)?.takeIf { it.isInstalled || it.state == StoreGameSidecar.STATE_UPDATING }?.let { InstalledStoreGame(it, folder) }
|
||||
} + runCatching { com.droiddeck.launcher.stores.battlenet.BattleNetBackend.installedGames(app) }.getOrDefault(emptyList())
|
||||
val partial = StoreInstallRoot.unfinished(app)
|
||||
val version = if (StoresNative.available) StoresNative.version ?: "" else ""
|
||||
|
||||
@@ -87,6 +87,7 @@ fun BottomDashboard(
|
||||
onClose: () -> Unit,
|
||||
onMenu: (() -> Unit)?,
|
||||
gameStarts: Int = 0,
|
||||
strip: (@Composable (String?) -> Unit)? = null,
|
||||
) {
|
||||
val colors = MaterialTheme.colorScheme
|
||||
var current by remember { mutableStateOf(pickBottomPage(remembered, pages.map { it.id })) }
|
||||
@@ -110,6 +111,7 @@ fun BottomDashboard(
|
||||
}
|
||||
if (onMenu != null) BarButton(Icons.Outlined.Menu, stringResource(R.string.touch_droiddeck_menu), "bottom-menu", onMenu)
|
||||
}
|
||||
strip?.invoke(page?.id)
|
||||
Box(Modifier.weight(1f).fillMaxWidth()) {
|
||||
if (page != null) page.content()
|
||||
else Text(stringResource(R.string.bottom_dashboard_empty), fontSize = 15.sp, color = colors.onSurfaceVariant,
|
||||
|
||||
@@ -15,8 +15,10 @@ class BottomPageActions(
|
||||
val runningGame: () -> String? = { null },
|
||||
)
|
||||
|
||||
internal const val BOTTOM_PAGE_TELEMETRY = "telemetry"
|
||||
|
||||
fun sessionBottomPages(actions: BottomPageActions): List<BottomPage> = listOf(
|
||||
BottomPage("telemetry", R.string.bottom_page_telemetry, Icons.Outlined.Speed) { BottomTelemetryPage(actions.fps, actions.runningGame) },
|
||||
BottomPage(BOTTOM_PAGE_TELEMETRY, R.string.bottom_page_telemetry, Icons.Outlined.Speed) { BottomTelemetryPage(actions.fps, actions.runningGame) },
|
||||
BottomPage("downloads", R.string.bottom_page_downloads, Icons.Outlined.Download) { BottomDownloadsPage() },
|
||||
BottomPage("notifications", R.string.bottom_page_notifications, Icons.Outlined.Notifications) { BottomNotificationsPage(actions.openSettings) },
|
||||
BottomPage("screenshots", R.string.bottom_page_screenshots, Icons.Outlined.PhotoLibrary) { BottomScreenshotsPage(actions.takeScreenshot) },
|
||||
|
||||
@@ -0,0 +1,85 @@
|
||||
package com.droiddeck.launcher.ui
|
||||
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.DisposableEffect
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableIntStateOf
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.rememberUpdatedState
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import com.droiddeck.launcher.hud.DroidDeckHudConfig
|
||||
import com.droiddeck.launcher.hud.FrameStats
|
||||
import com.droiddeck.launcher.hud.GameFrameFeed
|
||||
import com.droiddeck.launcher.hud.HudFrames
|
||||
import com.droiddeck.launcher.hud.HudMetrics
|
||||
import com.droiddeck.launcher.hud.KeyValueSet
|
||||
import com.droiddeck.launcher.hud.MangoReadings
|
||||
import com.droiddeck.launcher.runtime.LinuxRuntime
|
||||
import com.droiddeck.launcher.session.SessionPrefs
|
||||
import com.droiddeck.launcher.session.StripMetric
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.delay
|
||||
import kotlinx.coroutines.isActive
|
||||
import kotlinx.coroutines.withContext
|
||||
import java.io.File
|
||||
|
||||
private const val STRIP_FRAME_MS = 100L
|
||||
private const val STRIP_METRICS_MS = 500L
|
||||
private const val STRIP_TEMP_PLAIN = 0xFFF2F5F9.toInt()
|
||||
|
||||
@Composable
|
||||
internal fun SessionBottomStrip(pageId: String?, fps: () -> Float?) {
|
||||
val context = LocalContext.current
|
||||
val frames = remember { GameFrameFeed(File(LinuxRuntime.rootDir(context), HudFrames.PATH)) }
|
||||
DisposableEffect(frames) { onDispose { frames.close() } }
|
||||
val page by rememberUpdatedState(pageId)
|
||||
var frameTick by remember { mutableIntStateOf(0) }
|
||||
var device by remember { mutableStateOf<HudMetrics.Snapshot?>(null) }
|
||||
var clock by remember { mutableStateOf("") }
|
||||
var onTabs by remember { mutableStateOf(SessionPrefs.bottomHud(context) && SessionPrefs.bottomStripOnTabs(context)) }
|
||||
var metrics by remember { mutableStateOf(SessionPrefs.bottomStripMetrics(context)) }
|
||||
var thresholds by remember { mutableStateOf<Map<HudMetrics.TempSensor, HudMetrics.Thresholds>>(emptyMap()) }
|
||||
val tempDisplay = remember { HudMetrics.TempDisplay.from(KeyValueSet(DroidDeckHudConfig.encode(DroidDeckHudConfig.load(context)))) }
|
||||
LaunchedEffect(frames) {
|
||||
while (isActive) {
|
||||
if (frames.poll(System.nanoTime()) || !frames.live) frameTick++
|
||||
delay(STRIP_FRAME_MS)
|
||||
}
|
||||
}
|
||||
LaunchedEffect(Unit) {
|
||||
val reader = HudMetrics(context).apply { setMangoReadings(MangoReadings(File(context.filesDir, "session"), LinuxRuntime.rootDir(context))) }
|
||||
thresholds = withContext(Dispatchers.IO) {
|
||||
HudMetrics.TempSensor.values().mapNotNull { sensor -> runCatching { sensor to reader.resolveThresholds(sensor, tempDisplay) }.getOrNull() }.toMap()
|
||||
}
|
||||
while (isActive) {
|
||||
onTabs = SessionPrefs.bottomHud(context) && SessionPrefs.bottomStripOnTabs(context)
|
||||
metrics = SessionPrefs.bottomStripMetrics(context)
|
||||
if (onTabs && page != BOTTOM_PAGE_TELEMETRY) {
|
||||
if (StripMetric.GPU in metrics || StripMetric.VRAM in metrics) com.droiddeck.launcher.hud.BottomHudPresence.beat()
|
||||
device = withContext(Dispatchers.IO) { runCatching { reader.snapshot() }.getOrNull() }
|
||||
clock = java.text.DateFormat.getTimeInstance(java.text.DateFormat.SHORT).format(java.util.Date())
|
||||
}
|
||||
delay(STRIP_METRICS_MS)
|
||||
}
|
||||
}
|
||||
if (!onTabs || pageId == BOTTOM_PAGE_TELEMETRY) return
|
||||
frameTick.let {
|
||||
val live = frames.live
|
||||
val stats = frames.stats
|
||||
val graph = FloatArray(FrameStats.GRAPH_FRAMES).also { if (live) stats.graph(it) }
|
||||
LineStrip(StripData(
|
||||
fps = if (live) stats.fps else fps(), device = device,
|
||||
fpsHistory = emptyList(), cpuHistory = emptyList(), gpuHistory = emptyList(), wattsHistory = emptyList(), clock = clock,
|
||||
engine = if (live && !frames.steamFocused) frames.engine else "",
|
||||
lows = if (live) stats.lows(System.nanoTime()) else null, graph = graph, metrics = metrics,
|
||||
fahrenheit = tempDisplay.fahrenheit,
|
||||
tempColor = { sensor, c ->
|
||||
thresholds[sensor]?.let { Color(HudMetrics.tempColor(c.toFloat(), it, tempDisplay, STRIP_TEMP_PLAIN)) } ?: Color(STRIP_TEMP_PLAIN)
|
||||
},
|
||||
))
|
||||
}
|
||||
}
|
||||
@@ -1,9 +1,13 @@
|
||||
package com.droiddeck.launcher.ui
|
||||
|
||||
import android.content.Context
|
||||
import android.content.res.Resources
|
||||
import android.system.Os
|
||||
import android.system.OsConstants
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.BoxWithConstraints
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.fillMaxHeight
|
||||
@@ -22,6 +26,7 @@ import androidx.compose.material3.Switch
|
||||
import androidx.compose.material3.SwitchDefaults
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.DisposableEffect
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableIntStateOf
|
||||
@@ -41,18 +46,30 @@ import androidx.compose.ui.text.style.TextOverflow
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.unit.sp
|
||||
import com.droiddeck.launcher.R
|
||||
import com.droiddeck.launcher.core.CpuCores
|
||||
import com.droiddeck.launcher.frontend.AddedGameArt
|
||||
import com.droiddeck.launcher.frontend.Library
|
||||
import com.droiddeck.launcher.frontend.SteamGameArt
|
||||
import com.droiddeck.launcher.gpu.GpuInfo
|
||||
import com.droiddeck.launcher.hud.DroidDeckHudConfig
|
||||
import com.droiddeck.launcher.hud.DroidDeckHudHost
|
||||
import com.droiddeck.launcher.hud.FrameStats
|
||||
import com.droiddeck.launcher.hud.GameFrameFeed
|
||||
import com.droiddeck.launcher.hud.HudFrames
|
||||
import com.droiddeck.launcher.hud.HudMetrics
|
||||
import com.droiddeck.launcher.hud.KeyValueSet
|
||||
import com.droiddeck.launcher.hud.MangoReadings
|
||||
import com.droiddeck.launcher.runtime.LinuxRuntime
|
||||
import com.droiddeck.launcher.session.GameStatusFiles
|
||||
import com.droiddeck.launcher.session.GameTelemetry
|
||||
import com.droiddeck.launcher.session.HwmonComponent
|
||||
import com.droiddeck.launcher.input.SteamTouchConfig
|
||||
import com.droiddeck.launcher.session.SessionPrefs
|
||||
import com.droiddeck.launcher.session.SessionService
|
||||
import com.droiddeck.launcher.session.SessionState
|
||||
import com.droiddeck.launcher.session.SpeedUnit
|
||||
import com.droiddeck.launcher.session.StatsDash
|
||||
import com.droiddeck.launcher.session.StatsHud
|
||||
import com.droiddeck.launcher.session.StatsStrip
|
||||
import com.droiddeck.launcher.session.Telemetry
|
||||
import com.droiddeck.launcher.session.TelemetryOutput
|
||||
@@ -72,13 +89,16 @@ import java.util.concurrent.atomic.AtomicReference
|
||||
|
||||
private const val DRAW_MS = 66L
|
||||
private const val TELEMETRY_STALE_MS = 3_000L
|
||||
private const val METRICS_MS = 1_000L
|
||||
private const val METRICS_MS = 500L
|
||||
private const val THUNDER_MS = 500L
|
||||
private const val THUNDER_RETRY_MS = 3_000L
|
||||
private const val ELITE_MS = 1_000L
|
||||
private const val ELITE_SEARCH_MS = 10_000L
|
||||
private const val OUTPUT_SCAN_MS = 3_000L
|
||||
private const val PERF_N = 30
|
||||
private const val PERF_N = 60
|
||||
private const val FRAME_MS = 100L
|
||||
private const val MINUTE_NS = 60_000_000_000L
|
||||
private const val TEMP_PLAIN = 0xFFF2F5F9.toInt()
|
||||
|
||||
private class PortTraffic {
|
||||
@Volatile var packets = 0L
|
||||
@@ -112,7 +132,7 @@ fun BottomTelemetryPage(fps: () -> Float?, runningGame: () -> String? = { null }
|
||||
var unknown by remember { mutableStateOf<List<UnknownTraffic>>(emptyList()) }
|
||||
var strip by remember { mutableStateOf(StatsStrip.from(SessionPrefs.statsStrip(context))) }
|
||||
var dash by remember { mutableStateOf(StatsDash.from(SessionPrefs.statsDash(context))) }
|
||||
var speedUnit by remember { mutableStateOf(SpeedUnit.from(SessionPrefs.statsSpeedUnit(context))) }
|
||||
val speedUnit = SpeedUnit.from(SessionPrefs.statsSpeedUnit(context))
|
||||
val shownUnit = speedUnit.resolve(Resources.getSystem().configuration.locales[0].country)
|
||||
val fpsHistory = remember { History(PERF_N) }
|
||||
val cpuHistory = remember { History(PERF_N) }
|
||||
@@ -124,6 +144,21 @@ fun BottomTelemetryPage(fps: () -> Float?, runningGame: () -> String? = { null }
|
||||
var perfTick by remember { mutableIntStateOf(0) }
|
||||
var traceTick by remember { mutableIntStateOf(0) }
|
||||
var clock by remember { mutableStateOf("") }
|
||||
val frames = remember { GameFrameFeed(File(LinuxRuntime.rootDir(context), HudFrames.PATH)) }
|
||||
DisposableEffect(frames) { onDispose { frames.close() } }
|
||||
val currentFps = { if (frames.live) frames.stats.fps else fps() }
|
||||
var hud by remember { mutableStateOf(StatsHud.from(SessionPrefs.statsHud(context))) }
|
||||
var hudOn by remember { mutableStateOf(SessionPrefs.bottomHud(context)) }
|
||||
var hudTick by remember { mutableIntStateOf(0) }
|
||||
var fanRpm by remember { mutableStateOf<Long?>(null) }
|
||||
var gpuName by remember { mutableStateOf("") }
|
||||
var proton by remember { mutableStateOf<String?>(null) }
|
||||
var playedSeconds by remember { mutableStateOf<Long?>(null) }
|
||||
var cover by remember { mutableStateOf<File?>(null) }
|
||||
var coreGroups by remember { mutableStateOf<List<CoreGroup>>(emptyList()) }
|
||||
var thresholds by remember { mutableStateOf<Map<HudMetrics.TempSensor, HudMetrics.Thresholds>>(emptyMap()) }
|
||||
val tempDisplay = remember { HudMetrics.TempDisplay.from(KeyValueSet(DroidDeckHudConfig.encode(DroidDeckHudConfig.load(context)))) }
|
||||
val gameCores = remember { CpuCores.parse(SessionPrefs.gameCpus(context)) }
|
||||
|
||||
LaunchedEffect(Unit) {
|
||||
for (port in GameTelemetry.PORTS) launch(Dispatchers.IO) {
|
||||
@@ -167,22 +202,49 @@ fun BottomTelemetryPage(fps: () -> Float?, runningGame: () -> String? = { null }
|
||||
speedTrace.add(r.speedKmh ?: 0f)
|
||||
traceTick++
|
||||
}
|
||||
fpsNow = fps()
|
||||
fpsNow = currentFps()
|
||||
unknown = traffic.filter { (_, t) -> t.packets > 0 && !t.readable }.map { (port, t) -> UnknownTraffic(port, t.packets, t.length) }
|
||||
delay(DRAW_MS)
|
||||
}
|
||||
}
|
||||
LaunchedEffect(frames) {
|
||||
while (isActive) {
|
||||
if (frames.poll(System.nanoTime())) hudTick++
|
||||
delay(FRAME_MS)
|
||||
}
|
||||
}
|
||||
LaunchedEffect(Unit) {
|
||||
val metrics = HudMetrics(context).apply { setMangoReadings(MangoReadings(File(context.filesDir, "session"), LinuxRuntime.rootDir(context))) }
|
||||
val fanPath = withContext(Dispatchers.IO) { runCatching { HwmonComponent.fanSource() }.getOrNull() }
|
||||
gpuName = withContext(Dispatchers.IO) { runCatching { GpuInfo.detect().displayName(context) }.getOrDefault("") }
|
||||
coreGroups = withContext(Dispatchers.IO) { coreGroups() }
|
||||
thresholds = withContext(Dispatchers.IO) {
|
||||
HudMetrics.TempSensor.values().mapNotNull { sensor -> runCatching { sensor to metrics.resolveThresholds(sensor, tempDisplay) }.getOrNull() }.toMap()
|
||||
}
|
||||
var protonPid = 0
|
||||
while (isActive) {
|
||||
device = withContext(Dispatchers.IO) { runCatching { metrics.snapshot() }.getOrNull() }
|
||||
fps()?.takeIf { it > 0f }?.let { fpsHistory.add(it) }
|
||||
fanRpm = fanPath?.let { path -> withContext(Dispatchers.IO) { HwmonComponent.readRpm(path) } }
|
||||
val pid = if (frames.live && !frames.steamFocused) frames.pid else 0
|
||||
if (pid == 0) {
|
||||
protonPid = 0
|
||||
if (!frames.live) proton = null
|
||||
} else if (pid != protonPid) {
|
||||
val name = withContext(Dispatchers.IO) { DroidDeckHudHost.protonName(pid) }
|
||||
proton = name
|
||||
if (name != null) protonPid = pid
|
||||
}
|
||||
playedSeconds = if (pid > 0 && running != null) withContext(Dispatchers.IO) { processSeconds(pid) } else null
|
||||
val sampledAt = android.os.SystemClock.elapsedRealtime()
|
||||
currentFps()?.takeIf { it > 0f }?.let { fpsHistory.add(it, sampledAt) }
|
||||
device?.let { s ->
|
||||
s.cpuPercent?.let { cpuHistory.add(it.toFloat()) }
|
||||
s.gpuPercent?.let { gpuHistory.add(it.toFloat()) }
|
||||
s.cpuPercent?.let { cpuHistory.add(it.toFloat(), sampledAt) }
|
||||
s.gpuPercent?.let { gpuHistory.add(it.toFloat(), sampledAt) }
|
||||
s.battery?.watts?.takeIf { it > 0f }?.let { wattsHistory.add(it) }
|
||||
}
|
||||
clock = java.text.DateFormat.getTimeInstance(java.text.DateFormat.SHORT).format(java.util.Date())
|
||||
hudOn = SessionPrefs.bottomHud(context)
|
||||
if (hudOn) com.droiddeck.launcher.hud.BottomHudPresence.beat()
|
||||
perfTick++
|
||||
delay(METRICS_MS)
|
||||
}
|
||||
@@ -221,6 +283,7 @@ fun BottomTelemetryPage(fps: () -> Float?, runningGame: () -> String? = { null }
|
||||
val id = running?.toLongOrNull()
|
||||
gameName = if (id == null) null
|
||||
else withContext(Dispatchers.IO) { runCatching { SteamTouchConfig.gameName(context, id.toInt()) }.getOrNull() }
|
||||
cover = if (id == null) null else withContext(Dispatchers.IO) { runCatching { gameCover(context, id.toInt()) }.getOrNull() }
|
||||
}
|
||||
LaunchedEffect(rescan) {
|
||||
while (isActive) {
|
||||
@@ -230,96 +293,157 @@ fun BottomTelemetryPage(fps: () -> Float?, runningGame: () -> String? = { null }
|
||||
}
|
||||
}
|
||||
|
||||
Column(Modifier.fillMaxSize().verticalScroll(rememberScrollState()), verticalArrangement = Arrangement.spacedBy(10.dp)) {
|
||||
perfTick.let {
|
||||
StatsStripView(strip, StripData(fpsNow, device, fpsHistory.snapshot(), cpuHistory.snapshot(), gpuHistory.snapshot(), wattsHistory.snapshot(), clock,
|
||||
if (running != null) gameName else steamClient?.let { stringResource(steamClientLabel(it)) })) {
|
||||
strip = strip.next()
|
||||
SessionPrefs.setStatsStrip(context, strip.id)
|
||||
}
|
||||
}
|
||||
race?.let { r ->
|
||||
val shown = dash.resolve(r.source)
|
||||
traceTick.let {
|
||||
RaceDashView(shown, r, TraceData(throttleTrace.snapshot(), brakeTrace.snapshot(), speedTrace.snapshot()), shownUnit) {
|
||||
dash = StatsDash.tapped(shown)
|
||||
SessionPrefs.setStatsDash(context, dash.id)
|
||||
val title = if (running != null) gameName else steamClient?.let { stringResource(steamClientLabel(it)) }
|
||||
val noData = race == null && thunder.isEmpty() && elite == null
|
||||
BoxWithConstraints(Modifier.fillMaxSize()) {
|
||||
val viewport = maxHeight
|
||||
Column(Modifier.fillMaxSize().verticalScroll(rememberScrollState()), verticalArrangement = Arrangement.spacedBy(10.dp)) {
|
||||
if (!hudOn) Unit
|
||||
else if (noData) hudTick.let {
|
||||
perfTick.let {
|
||||
val now = System.nanoTime()
|
||||
val live = frames.live
|
||||
val stats = frames.stats
|
||||
val graph = FloatArray(FrameStats.GRAPH_FRAMES).also { if (live) stats.graph(it) }
|
||||
val hz = (frames.refreshMilliHz + 499) / 1000
|
||||
val data = HudData(
|
||||
fps = fpsNow, avgFps = if (live) stats.averageFps() else null, lows = if (live) stats.lows(now) else FrameStats.Lows.EMPTY,
|
||||
graph = graph, slowPerMinute = if (live) stats.slowFrames(now, MINUTE_NS) else null,
|
||||
engine = if (live && !frames.steamFocused) frames.engine else "",
|
||||
resolution = if (frames.outputWidth > 0 && frames.outputHeight > 0) {
|
||||
"${frames.outputWidth}x${frames.outputHeight}" + (if (hz > 0) " @ ${hz}Hz" else "")
|
||||
} else "",
|
||||
refreshHz = hz, proton = proton, device = device, gpuName = gpuName, fanRpm = fanRpm,
|
||||
title = title, cover = cover, played = playedSeconds?.let(::playedText), clock = clock,
|
||||
fpsHistory = fpsHistory.snapshot(), cpuHistory = cpuHistory.snapshot(), gpuHistory = gpuHistory.snapshot(),
|
||||
historySeconds = maxOf(fpsHistory.spanSeconds(), cpuHistory.spanSeconds(), gpuHistory.spanSeconds()),
|
||||
coreGroups = coreGroups, gameCores = gameCores, fahrenheit = tempDisplay.fahrenheit,
|
||||
tempColor = { sensor, c ->
|
||||
thresholds[sensor]?.let { Color(HudMetrics.tempColor(c.toFloat(), it, tempDisplay, TEMP_PLAIN)) } ?: Color(TEMP_PLAIN)
|
||||
},
|
||||
redC = { thresholds[it]?.redC },
|
||||
)
|
||||
StatsHudView(hud, data, Modifier.fillMaxWidth().height(viewport)) {
|
||||
hud = hud.next()
|
||||
SessionPrefs.setStatsHud(context, hud.id)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (thunder.isNotEmpty()) {
|
||||
if (FlightLabels.drawable(thunder)) FlightDash(thunder)
|
||||
else TelemetryCard(stringResource(R.string.bottom_telemetry_war_thunder)) { ValueGrid(thunder.take(GameStatusFiles.WAR_THUNDER_ROWS)) }
|
||||
}
|
||||
elite?.let { e ->
|
||||
if (e.pips != null || e.fuelMain != null) ShipDash(e)
|
||||
else TelemetryCard(stringResource(R.string.bottom_telemetry_elite)) {
|
||||
ValueGrid(listOfNotNull(
|
||||
e.fuelMain?.let { stringResource(R.string.bottom_telemetry_fuel) to "%.1f t".format(it) },
|
||||
e.cargo?.let { stringResource(R.string.bottom_telemetry_cargo) to "%.0f t".format(it) },
|
||||
e.pips?.takeIf { it.size == 3 }?.let { p ->
|
||||
stringResource(R.string.bottom_telemetry_pips) to p.joinToString(" · ") { "%.1f".format(it / 2f) }
|
||||
},
|
||||
e.legal?.let { stringResource(R.string.bottom_telemetry_legal) to it },
|
||||
e.balance?.let { stringResource(R.string.bottom_telemetry_balance) to "%,d CR".format(it) },
|
||||
))
|
||||
}
|
||||
}
|
||||
if (race == null && thunder.isEmpty() && elite == null) {
|
||||
Text(stringResource(R.string.bottom_telemetry_help),
|
||||
fontSize = 14.sp, color = colors.onSurfaceVariant, modifier = Modifier.padding(horizontal = 4.dp))
|
||||
}
|
||||
if (outputs.isNotEmpty() || menuGames.isNotEmpty()) TelemetryCard(stringResource(R.string.bottom_telemetry_send_title)) {
|
||||
if (outputs.isNotEmpty()) SwitchRow(stringResource(R.string.bottom_telemetry_send_auto), stringResource(R.string.bottom_telemetry_send_auto_hint), auto, true) { want ->
|
||||
scope.launch {
|
||||
withContext(Dispatchers.IO) {
|
||||
SessionPrefs.setTelemetryAuto(context, want)
|
||||
if (want) TelemetryOutput.applyAutomatic(context, running)
|
||||
else TelemetryOutput.restoreTargets(outputs, running).forEach { TelemetryOutput.set(it, false) }
|
||||
} else hudTick.let {
|
||||
perfTick.let {
|
||||
val live = frames.live
|
||||
val graph = FloatArray(FrameStats.GRAPH_FRAMES).also { if (live) frames.stats.graph(it) }
|
||||
val data = StripData(
|
||||
fpsNow, device, fpsHistory.snapshot(), cpuHistory.snapshot(), gpuHistory.snapshot(), wattsHistory.snapshot(), clock, title,
|
||||
engine = if (live && !frames.steamFocused) frames.engine else "",
|
||||
lows = if (live) frames.stats.lows(System.nanoTime()) else null, graph = graph,
|
||||
metrics = SessionPrefs.bottomStripMetrics(context), fahrenheit = tempDisplay.fahrenheit,
|
||||
tempColor = { sensor, c ->
|
||||
thresholds[sensor]?.let { Color(HudMetrics.tempColor(c.toFloat(), it, tempDisplay, TEMP_PLAIN)) } ?: Color(TEMP_PLAIN)
|
||||
},
|
||||
)
|
||||
StatsStripView(strip, data) {
|
||||
strip = strip.next()
|
||||
SessionPrefs.setStatsStrip(context, strip.id)
|
||||
}
|
||||
auto = want
|
||||
rescan++
|
||||
}
|
||||
}
|
||||
for (game in outputs) {
|
||||
val locked = TelemetryOutput.isRunning(game.prefixId, running)
|
||||
val hint = if (locked) stringResource(R.string.bottom_telemetry_send_close, game.name) else null
|
||||
SwitchRow(game.name, hint, game.on, !locked) { want ->
|
||||
race?.let { r ->
|
||||
val shown = dash.resolve(r.source)
|
||||
traceTick.let {
|
||||
RaceDashView(shown, r, TraceData(throttleTrace.snapshot(), brakeTrace.snapshot(), speedTrace.snapshot()), shownUnit) {
|
||||
dash = StatsDash.tapped(shown)
|
||||
SessionPrefs.setStatsDash(context, dash.id)
|
||||
}
|
||||
}
|
||||
}
|
||||
if (thunder.isNotEmpty()) {
|
||||
if (FlightLabels.drawable(thunder)) FlightDash(thunder)
|
||||
else TelemetryCard(stringResource(R.string.bottom_telemetry_war_thunder)) { ValueGrid(thunder.take(GameStatusFiles.WAR_THUNDER_ROWS)) }
|
||||
}
|
||||
elite?.let { e ->
|
||||
if (e.pips != null || e.fuelMain != null) ShipDash(e)
|
||||
else TelemetryCard(stringResource(R.string.bottom_telemetry_elite)) {
|
||||
ValueGrid(listOfNotNull(
|
||||
e.fuelMain?.let { stringResource(R.string.bottom_telemetry_fuel) to "%.1f t".format(it) },
|
||||
e.cargo?.let { stringResource(R.string.bottom_telemetry_cargo) to "%.0f t".format(it) },
|
||||
e.pips?.takeIf { it.size == 3 }?.let { p ->
|
||||
stringResource(R.string.bottom_telemetry_pips) to p.joinToString(" · ") { "%.1f".format(it / 2f) }
|
||||
},
|
||||
e.legal?.let { stringResource(R.string.bottom_telemetry_legal) to it },
|
||||
e.balance?.let { stringResource(R.string.bottom_telemetry_balance) to "%,d CR".format(it) },
|
||||
))
|
||||
}
|
||||
}
|
||||
if (outputs.isNotEmpty() || menuGames.isNotEmpty()) TelemetryCard(stringResource(R.string.bottom_telemetry_send_title)) {
|
||||
if (outputs.isNotEmpty()) SwitchRow(stringResource(R.string.bottom_telemetry_send_auto), stringResource(R.string.bottom_telemetry_send_auto_hint), auto, true) { want ->
|
||||
scope.launch {
|
||||
val ok = withContext(Dispatchers.IO) {
|
||||
val key = TelemetryOutput.key(game)
|
||||
val off = SessionPrefs.telemetryOff(context)
|
||||
SessionPrefs.setTelemetryOff(context, if (want) off - key else off + key)
|
||||
TelemetryOutput.set(game, want)
|
||||
withContext(Dispatchers.IO) {
|
||||
SessionPrefs.setTelemetryAuto(context, want)
|
||||
if (want) TelemetryOutput.applyAutomatic(context, running)
|
||||
else TelemetryOutput.restoreTargets(outputs, running).forEach { TelemetryOutput.set(it, false) }
|
||||
}
|
||||
outputFailed = if (ok) null else game.name
|
||||
auto = want
|
||||
rescan++
|
||||
}
|
||||
}
|
||||
for (game in outputs) {
|
||||
val locked = TelemetryOutput.isRunning(game.prefixId, running)
|
||||
val hint = if (locked) stringResource(R.string.bottom_telemetry_send_close, game.name) else null
|
||||
SwitchRow(game.name, hint, game.on, !locked) { want ->
|
||||
scope.launch {
|
||||
val ok = withContext(Dispatchers.IO) {
|
||||
val key = TelemetryOutput.key(game)
|
||||
val off = SessionPrefs.telemetryOff(context)
|
||||
SessionPrefs.setTelemetryOff(context, if (want) off - key else off + key)
|
||||
TelemetryOutput.set(game, want)
|
||||
}
|
||||
outputFailed = if (ok) null else game.name
|
||||
rescan++
|
||||
}
|
||||
}
|
||||
}
|
||||
for (game in menuGames) {
|
||||
Text(stringResource(R.string.bottom_telemetry_send_menu, game.name, game.setting, game.port), fontSize = 13.sp, color = colors.onSurfaceVariant)
|
||||
}
|
||||
if (outputs.isNotEmpty()) Text(stringResource(R.string.bottom_telemetry_send_note), fontSize = 12.sp, color = colors.onSurfaceVariant)
|
||||
outputFailed?.let { Text(stringResource(R.string.bottom_telemetry_send_failed, it), fontSize = 12.sp, color = colors.error) }
|
||||
}
|
||||
for (game in menuGames) {
|
||||
Text(stringResource(R.string.bottom_telemetry_send_menu, game.name, game.setting, game.port), fontSize = 13.sp, color = colors.onSurfaceVariant)
|
||||
for (u in unknown) {
|
||||
Text(stringResource(R.string.bottom_telemetry_unknown_format, u.port, u.packets, u.length), fontSize = 13.sp,
|
||||
color = colors.error, modifier = Modifier.padding(horizontal = 4.dp))
|
||||
}
|
||||
if (busyPorts.isNotEmpty()) {
|
||||
Text(stringResource(R.string.bottom_telemetry_port_busy, busyPorts.sorted().joinToString()), fontSize = 13.sp,
|
||||
color = colors.error, modifier = Modifier.padding(horizontal = 4.dp))
|
||||
}
|
||||
if (outputs.isNotEmpty()) Text(stringResource(R.string.bottom_telemetry_send_note), fontSize = 12.sp, color = colors.onSurfaceVariant)
|
||||
outputFailed?.let { Text(stringResource(R.string.bottom_telemetry_send_failed, it), fontSize = 12.sp, color = colors.error) }
|
||||
}
|
||||
for (u in unknown) {
|
||||
Text(stringResource(R.string.bottom_telemetry_unknown_format, u.port, u.packets, u.length), fontSize = 13.sp,
|
||||
color = colors.error, modifier = Modifier.padding(horizontal = 4.dp))
|
||||
}
|
||||
if (busyPorts.isNotEmpty()) {
|
||||
Text(stringResource(R.string.bottom_telemetry_port_busy, busyPorts.sorted().joinToString()), fontSize = 13.sp,
|
||||
color = colors.error, modifier = Modifier.padding(horizontal = 4.dp))
|
||||
}
|
||||
ChoiceRow(stringResource(R.string.stats_speed_units),
|
||||
if (speedUnit == SpeedUnit.AUTO) stringResource(R.string.stats_speed_units_auto, shownUnit.label) else speedUnit.label) {
|
||||
speedUnit = speedUnit.next()
|
||||
SessionPrefs.setStatsSpeedUnit(context, speedUnit.id)
|
||||
}
|
||||
Text(stringResource(R.string.stats_tap_hint), fontSize = 12.sp, color = colors.onSurfaceVariant, modifier = Modifier.padding(horizontal = 4.dp))
|
||||
}
|
||||
}
|
||||
|
||||
private fun processSeconds(pid: Int): Long? = runCatching {
|
||||
val fields = File("/proc/$pid/stat").readText().substringAfterLast(')').trim().split(' ')
|
||||
val startTicks = fields[19].toLong()
|
||||
val uptime = File("/proc/uptime").readText().substringBefore(' ').toDouble()
|
||||
(uptime - startTicks.toDouble() / Os.sysconf(OsConstants._SC_CLK_TCK)).toLong()
|
||||
}.getOrNull()?.takeIf { it >= 0 }
|
||||
|
||||
private fun playedText(seconds: Long): String =
|
||||
if (seconds >= 3600) "%d:%02d:%02d".format(seconds / 3600, seconds / 60 % 60, seconds % 60) else "%d:%02d".format(seconds / 60, seconds % 60)
|
||||
|
||||
private fun gameCover(context: Context, appId: Int): File? =
|
||||
SteamGameArt.chosenFile(context, appId, AddedGameArt.Slot.COVER)
|
||||
?: Library.steamCacheImage(File(SessionPrefs.activeSteamRoot(context), "appcache/librarycache"), appId, listOf("library_capsule.jpg", "library_600x900.jpg"))
|
||||
|
||||
private fun coreGroups(): List<CoreGroup> {
|
||||
val out = mutableListOf<CoreGroup>()
|
||||
for (core in CpuCores.all) {
|
||||
val ghz = CpuCores.maxGhz(core)
|
||||
val last = out.lastOrNull()
|
||||
if (last != null && last.maxGhz == ghz) out[out.lastIndex] = CoreGroup(last.cores + core, ghz) else out += CoreGroup(listOf(core), ghz)
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
private fun httpGet(url: String): String {
|
||||
val c = URL(url).openConnection() as HttpURLConnection
|
||||
c.connectTimeout = 300
|
||||
@@ -331,19 +455,8 @@ private fun httpGet(url: String): String {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
internal fun steamClientLabel(arch: String): Int = if (arch == "x86_64") R.string.stats_steam_client_x86 else R.string.stats_steam_client_arm
|
||||
|
||||
@Composable
|
||||
private fun ChoiceRow(title: String, value: String, onClick: () -> Unit) {
|
||||
val colors = MaterialTheme.colorScheme
|
||||
Row(Modifier.fillMaxWidth().clickable(role = Role.Button, onClick = onClick).padding(horizontal = 4.dp, vertical = 6.dp),
|
||||
verticalAlignment = Alignment.CenterVertically) {
|
||||
Text(title, fontSize = 15.sp, color = colors.onBackground, modifier = Modifier.weight(1f))
|
||||
Text(value, fontSize = 15.sp, fontWeight = FontWeight.Bold, color = LocalPalette.current.primary)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun SwitchRow(title: String, hint: String?, checked: Boolean, enabled: Boolean, onChange: (Boolean) -> Unit) {
|
||||
val colors = MaterialTheme.colorScheme
|
||||
|
||||
@@ -1,10 +1,17 @@
|
||||
package com.droiddeck.launcher.ui
|
||||
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import com.droiddeck.launcher.R
|
||||
import com.droiddeck.launcher.hud.DroidDeckHudSize
|
||||
import com.droiddeck.launcher.session.SessionPrefs
|
||||
import com.droiddeck.launcher.session.StripMetric
|
||||
|
||||
@Composable
|
||||
internal fun DroidDeckHudSettings(host: MenuHost, a: DrawerActions, track: (String) -> Modifier) {
|
||||
@@ -31,6 +38,7 @@ internal fun DroidDeckHudSettings(host: MenuHost, a: DrawerActions, track: (Stri
|
||||
ToggleRow(host, "hud-snap", stringResource(R.string.hud_snap), stringResource(R.string.hud_snap_hint), on("hudSnap"),
|
||||
chipModifier = track("hud-snap")) { a.onHudSetting("hudSnap", flag(it)) }
|
||||
}
|
||||
if (a.dashboardAvailable && a.secondScreenDisplays.isNotEmpty()) BottomStripSettings(host, track)
|
||||
if (!a.hudOn) return
|
||||
|
||||
SettingsGroup(stringResource(R.string.hud_group_look)) {
|
||||
@@ -96,6 +104,47 @@ internal fun DroidDeckHudSettings(host: MenuHost, a: DrawerActions, track: (Stri
|
||||
|
||||
private fun flag(on: Boolean) = if (on) "1" else "0"
|
||||
|
||||
@Composable
|
||||
private fun BottomStripSettings(host: MenuHost, track: (String) -> Modifier) {
|
||||
val context = LocalContext.current
|
||||
var hudOn by remember { mutableStateOf(SessionPrefs.bottomHud(context)) }
|
||||
var onTabs by remember { mutableStateOf(SessionPrefs.bottomStripOnTabs(context)) }
|
||||
var shown by remember { mutableStateOf(SessionPrefs.bottomStripMetrics(context)) }
|
||||
SettingsGroup(stringResource(R.string.bottom_hud_group)) {
|
||||
ToggleRow(host, "bottom-hud", stringResource(R.string.bottom_hud_group), null, hudOn,
|
||||
chipModifier = track("bottom-hud")) { on ->
|
||||
hudOn = on
|
||||
SessionPrefs.setBottomHud(context, on)
|
||||
}
|
||||
ToggleRow(host, "bottom-strip-tabs", stringResource(R.string.bottom_strip_tabs), null, onTabs,
|
||||
enabled = hudOn, chipModifier = track("bottom-strip-tabs")) { on ->
|
||||
onTabs = on
|
||||
SessionPrefs.setBottomStripOnTabs(context, on)
|
||||
}
|
||||
for ((metric, label) in BOTTOM_STRIP_ROWS) {
|
||||
ToggleRow(host, "bottom-strip-${metric.id}", stringResource(label), null, metric in shown,
|
||||
enabled = hudOn, chipModifier = track("bottom-strip-${metric.id}")) { on ->
|
||||
shown = if (on) shown + metric else shown - metric
|
||||
SessionPrefs.setBottomStripMetric(context, metric, on)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private val BOTTOM_STRIP_ROWS = listOf(
|
||||
StripMetric.ENGINE to R.string.hud_metric_engine,
|
||||
StripMetric.GRAPH to R.string.hud_metric_graph,
|
||||
StripMetric.LOWS to R.string.bottom_strip_lows,
|
||||
StripMetric.LOW001 to R.string.hud_metric_low001,
|
||||
StripMetric.GPU to R.string.hud_metric_gpu,
|
||||
StripMetric.GPU_TEMP to R.string.hud_metric_gpu_temp,
|
||||
StripMetric.CPU to R.string.hud_metric_cpu,
|
||||
StripMetric.CPU_TEMP to R.string.bottom_strip_cpu_temp,
|
||||
StripMetric.VRAM to R.string.hud_metric_vram,
|
||||
StripMetric.RAM to R.string.hud_metric_ram,
|
||||
StripMetric.CLOCK to R.string.hud_metric_clock,
|
||||
)
|
||||
|
||||
private val EXTRA = mapOf(DroidDeckHudSize.PILL to setOf("Temp", "Power"))
|
||||
|
||||
private class HudMetric(val chip: String, val key: String, val label: Int)
|
||||
|
||||
@@ -136,7 +136,7 @@ class FrontEndState(
|
||||
val dualScreen: Boolean = false,
|
||||
val dualScreenDevice: Boolean = false,
|
||||
val dualScreenMode: String = com.droiddeck.launcher.input.DualScreen.MODE_AUTO,
|
||||
val dualScreenLauncher: Boolean = true,
|
||||
val dualScreenSessions: String = com.droiddeck.launcher.input.DualScreen.MODE_AUTO,
|
||||
val dualScreenControls: Boolean = true,
|
||||
val packages: List<PackageRow>? = null,
|
||||
val packageCatalogLoading: Boolean = false,
|
||||
@@ -244,7 +244,7 @@ class FrontEndActions(
|
||||
val onLauncherFullscreen: (Boolean) -> Unit = {},
|
||||
val onAnimationsEnabled: (Boolean) -> Unit = {},
|
||||
val onDualScreenMode: (String) -> Unit = {},
|
||||
val onDualScreenLauncher: (Boolean) -> Unit = {},
|
||||
val onDualScreenSessions: (String) -> Unit = {},
|
||||
val onDualScreenControls: (Boolean) -> Unit = {},
|
||||
val onDownloadSource: (String) -> Unit = {},
|
||||
val onDownloadMirror: (String) -> Unit = {},
|
||||
|
||||
@@ -435,21 +435,23 @@ internal fun SetupPanel(
|
||||
s.launcherFullscreen,
|
||||
) { a.onLauncherFullscreen(it) }
|
||||
if (s.dualScreenDevice) {
|
||||
ChoiceRow(
|
||||
host, "dual-screen", stringResource(R.string.setup_dual_screen), stringResource(R.string.setup_dual_screen_hint),
|
||||
listOf(
|
||||
com.droiddeck.launcher.input.DualScreen.MODE_AUTO to stringResource(R.string.setup_dual_screen_auto),
|
||||
com.droiddeck.launcher.input.DualScreen.MODE_ON to stringResource(R.string.setup_dual_screen_on),
|
||||
com.droiddeck.launcher.input.DualScreen.MODE_OFF to stringResource(R.string.setup_dual_screen_off),
|
||||
),
|
||||
s.dualScreenMode, onPick = a.onDualScreenMode,
|
||||
val dualModes = listOf(
|
||||
com.droiddeck.launcher.input.DualScreen.MODE_AUTO to stringResource(R.string.setup_dual_screen_auto),
|
||||
com.droiddeck.launcher.input.DualScreen.MODE_ON to stringResource(R.string.setup_dual_screen_on),
|
||||
com.droiddeck.launcher.input.DualScreen.MODE_OFF to stringResource(R.string.setup_dual_screen_off),
|
||||
)
|
||||
}
|
||||
if (s.dualScreenDevice && s.dualScreenMode != com.droiddeck.launcher.input.DualScreen.MODE_OFF) {
|
||||
ToggleRow(host, "dual-screen-menu", stringResource(R.string.setup_dual_screen_menu),
|
||||
stringResource(R.string.setup_dual_screen_menu_hint), s.dualScreenLauncher) { a.onDualScreenLauncher(it) }
|
||||
ToggleRow(host, "dual-screen-controls", stringResource(R.string.setup_dual_screen_controls),
|
||||
stringResource(R.string.setup_dual_screen_controls_hint), s.dualScreenControls) { a.onDualScreenControls(it) }
|
||||
ChoiceRow(
|
||||
host, "dual-screen", stringResource(R.string.setup_dual_screen_launcher), stringResource(R.string.setup_dual_screen_launcher_hint),
|
||||
dualModes, s.dualScreenMode, onPick = a.onDualScreenMode,
|
||||
)
|
||||
ChoiceRow(
|
||||
host, "dual-screen-sessions", stringResource(R.string.setup_dual_screen_sessions), stringResource(R.string.setup_dual_screen_sessions_hint),
|
||||
dualModes, s.dualScreenSessions, onPick = a.onDualScreenSessions,
|
||||
)
|
||||
if (s.dualScreenSessions != com.droiddeck.launcher.input.DualScreen.MODE_OFF) {
|
||||
ToggleRow(host, "dual-screen-controls", stringResource(R.string.setup_dual_screen_controls),
|
||||
stringResource(R.string.setup_dual_screen_controls_hint), s.dualScreenControls) { a.onDualScreenControls(it) }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -460,7 +460,7 @@ internal fun LiftHero(
|
||||
scrim { sink.value }
|
||||
if (shown || open) Box(
|
||||
Modifier.fillMaxSize()
|
||||
.graphicsLayer { alpha = sink.value }.background(Color.Black.copy(alpha = 0.42f))
|
||||
.overhang().graphicsLayer { alpha = sink.value }.background(Color.Black.copy(alpha = 0.42f))
|
||||
.focusProperties { canFocus = false }
|
||||
.clickable(interactionSource = remember { MutableInteractionSource() }, indication = null, enabled = open && !launching) { close() },
|
||||
)
|
||||
|
||||
@@ -89,7 +89,7 @@ class ModeSettings(
|
||||
val steamController: String? = null,
|
||||
val unfoldedControls: Boolean? = null,
|
||||
val unfoldedControlsWithController: Boolean = false,
|
||||
val runSteamAtStartup: Boolean = false,
|
||||
val startupSession: String = SessionPrefs.STARTUP_OFF,
|
||||
val bootMovie: String? = null,
|
||||
val bootMovieSound: Boolean = true,
|
||||
val bootMovieCustomName: String? = null,
|
||||
@@ -141,7 +141,7 @@ class ModeSettingsActions(
|
||||
val onSteamController: (String) -> Unit = {},
|
||||
val onUnfoldedControls: (Boolean) -> Unit = {},
|
||||
val onUnfoldedControlsWithController: (Boolean) -> Unit = {},
|
||||
val onRunSteamAtStartup: (Boolean) -> Unit = {},
|
||||
val onStartupSession: (String) -> Unit = {},
|
||||
val onBootMovie: (String) -> Unit = {},
|
||||
val onBootMovieSound: (Boolean) -> Unit = {},
|
||||
val onImportBootMovie: () -> Unit = {},
|
||||
@@ -355,11 +355,15 @@ fun ModeSheetRows(s: ModeSettings, a: ModeSettingsActions, tab: String, extras:
|
||||
ToggleRow(host, "pip-auto", stringResource(R.string.pip_auto), null,
|
||||
s.pipAutoEnter, onChange = a.onPipAutoEnter)
|
||||
}
|
||||
if (steam) SettingsGroup(stringResource(R.string.mode_startup)) {
|
||||
ToggleRow(
|
||||
host, "steam-startup", stringResource(R.string.mode_steam_startup),
|
||||
stringResource(R.string.mode_steam_startup_hint),
|
||||
s.runSteamAtStartup, onChange = a.onRunSteamAtStartup,
|
||||
SettingsGroup(stringResource(R.string.mode_startup)) {
|
||||
ChoiceRow(
|
||||
host, "startup-session", stringResource(R.string.mode_startup_session),
|
||||
stringResource(R.string.mode_startup_session_hint),
|
||||
listOf(
|
||||
SessionPrefs.STARTUP_OFF to stringResource(R.string.common_never),
|
||||
SessionPrefs.STARTUP_STEAM to stringResource(R.string.rail_steam),
|
||||
SessionPrefs.STARTUP_DESKTOP to stringResource(R.string.rail_desktop),
|
||||
), s.startupSession, onPick = a.onStartupSession,
|
||||
)
|
||||
}
|
||||
extras.performance()
|
||||
|
||||
@@ -29,6 +29,7 @@ import androidx.compose.animation.core.FastOutSlowInEasing
|
||||
import androidx.compose.foundation.layout.offset
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.foundation.layout.heightIn
|
||||
import androidx.compose.foundation.layout.widthIn
|
||||
import androidx.compose.material.icons.outlined.Close
|
||||
import androidx.compose.material.icons.outlined.Terminal
|
||||
import androidx.compose.material.icons.outlined.Dashboard
|
||||
@@ -475,8 +476,6 @@ internal fun SessionDrawer(open: Boolean, requestedPage: SessionDrawerPage, cont
|
||||
SteamActionPill(
|
||||
stringResource(R.string.common_close), Icons.Outlined.Close, focus.track(page, "close"),
|
||||
) { host.open = null; a.onClose() }
|
||||
Spacer(Modifier.width(4.dp))
|
||||
BottomScreenModes(a, focus.track(page, "bottom-modes")) { host.open = null }
|
||||
}
|
||||
Spacer(Modifier.weight(1f))
|
||||
SteamActionPill(
|
||||
@@ -876,69 +875,15 @@ private fun StopSessionButton(modifier: Modifier = Modifier, onClick: () -> Unit
|
||||
val fill by animateColorAsState(if (hot) colors.error.copy(alpha = 0.18f) else Color.Transparent, Motion.tw(220), label = "dangerFill")
|
||||
val shape = RoundedCornerShape(20.dp)
|
||||
val stopDescription = stringResource(R.string.stop_session)
|
||||
val stopLabel = stringResource(R.string.drawer_stop)
|
||||
var iconOnly by remember(stopLabel, androidx.compose.ui.platform.LocalConfiguration.current.screenWidthDp) { mutableStateOf(false) }
|
||||
Row(
|
||||
verticalAlignment = Alignment.CenterVertically, horizontalArrangement = Arrangement.spacedBy(6.dp),
|
||||
modifier = modifier.heightIn(min = 44.dp).semantics { contentDescription = stopDescription }.testTag("drawer-stop")
|
||||
verticalAlignment = Alignment.CenterVertically, horizontalArrangement = Arrangement.Center,
|
||||
modifier = modifier.heightIn(min = 44.dp).widthIn(min = 44.dp).semantics { contentDescription = stopDescription }.testTag("drawer-stop")
|
||||
.clip(shape).background(fill).glideBorder(hot, shape, colors.error.copy(alpha = 0.9f), colors.error.copy(alpha = 0.55f))
|
||||
.hoverable(src).clickable(role = androidx.compose.ui.semantics.Role.Button, interactionSource = src, indication = LocalIndication.current, onClick = onClick)
|
||||
.controllerConfirm(onClick = onClick)
|
||||
.padding(start = 12.dp, end = 14.dp),
|
||||
.padding(horizontal = 12.dp),
|
||||
) {
|
||||
Icon(Icons.Outlined.PowerSettingsNew, contentDescription = null, tint = colors.error, modifier = Modifier.size(18.dp))
|
||||
if (!iconOnly) Text(
|
||||
stopLabel, fontSize = 14.sp, fontWeight = FontWeight.SemiBold, color = colors.error,
|
||||
maxLines = 1, softWrap = false, onTextLayout = { if (it.hasVisualOverflow) iconOnly = true },
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun BottomScreenModes(a: DrawerActions, modifier: Modifier, onPick: () -> Unit) {
|
||||
val pal = LocalPalette.current
|
||||
val colors = MaterialTheme.colorScheme
|
||||
val modes = buildList {
|
||||
if (com.droiddeck.launcher.session.SessionState.deckPad) add(SecondScreenMode.DECK_CONTROLS to Icons.Outlined.SportsEsports)
|
||||
add(SecondScreenMode.KEYBOARD_TRACKPAD to Icons.Outlined.Keyboard)
|
||||
add(SecondScreenMode.TERMINAL to Icons.Outlined.Terminal)
|
||||
if (a.dashboardAvailable) add(SecondScreenMode.DASHBOARD to Icons.Outlined.Dashboard)
|
||||
if (a.desktopAvailable) add(SecondScreenMode.DESKTOP to Icons.Outlined.DesktopWindows)
|
||||
}
|
||||
Row(
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
horizontalArrangement = Arrangement.spacedBy(4.dp),
|
||||
modifier = modifier.focusGroup().clip(RoundedCornerShape(26.dp)).border(1.dp, pal.line, RoundedCornerShape(26.dp)).padding(4.dp),
|
||||
) {
|
||||
modes.forEach { (mode, icon) ->
|
||||
val selected = a.secondScreenMode == mode
|
||||
val source = remember { MutableInteractionSource() }
|
||||
val hot = source.collectIsFocusedAsState().value || source.collectIsHoveredAsState().value
|
||||
val shape = RoundedCornerShape(20.dp)
|
||||
val pick = { onPick(); a.onSecondScreenMode(mode) }
|
||||
val label = stringResource(mode.label)
|
||||
Box(
|
||||
contentAlignment = Alignment.Center,
|
||||
modifier = Modifier.size(width = 52.dp, height = 40.dp).semantics { contentDescription = label }
|
||||
.clip(shape)
|
||||
.background(when {
|
||||
selected -> pal.signal
|
||||
hot -> pal.signal.copy(alpha = 0.14f)
|
||||
else -> Color.Transparent
|
||||
})
|
||||
.glideBorder(hot, shape, pal.signal)
|
||||
.hoverable(source)
|
||||
.selectable(selected, interactionSource = source, indication = LocalIndication.current, role = Role.Tab, onClick = pick)
|
||||
.controllerConfirm(onClick = pick),
|
||||
) {
|
||||
Icon(icon, contentDescription = null, modifier = Modifier.size(22.dp),
|
||||
tint = when {
|
||||
selected -> colors.background
|
||||
hot -> pal.signal
|
||||
else -> colors.onBackground
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,667 @@
|
||||
package com.droiddeck.launcher.ui
|
||||
|
||||
import android.graphics.BlurMaskFilter
|
||||
import android.graphics.LinearGradient
|
||||
import android.graphics.Paint
|
||||
import android.graphics.Shader
|
||||
import androidx.compose.foundation.Canvas
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.BoxWithConstraints
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.ColumnScope
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.RowScope
|
||||
import androidx.compose.foundation.layout.Spacer
|
||||
import androidx.compose.foundation.layout.fillMaxHeight
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.height
|
||||
import androidx.compose.foundation.layout.offset
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.size
|
||||
import androidx.compose.foundation.layout.width
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.clip
|
||||
import androidx.compose.ui.draw.drawWithCache
|
||||
import androidx.compose.ui.geometry.Offset
|
||||
import androidx.compose.ui.geometry.Size
|
||||
import androidx.compose.ui.graphics.Brush
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.graphics.Path
|
||||
import androidx.compose.ui.graphics.PathEffect
|
||||
import androidx.compose.ui.graphics.Shadow
|
||||
import androidx.compose.ui.graphics.StrokeCap
|
||||
import androidx.compose.ui.graphics.StrokeJoin
|
||||
import androidx.compose.ui.graphics.drawscope.Stroke
|
||||
import androidx.compose.ui.graphics.drawscope.drawIntoCanvas
|
||||
import androidx.compose.ui.graphics.lerp
|
||||
import androidx.compose.ui.graphics.nativeCanvas
|
||||
import androidx.compose.ui.graphics.toArgb
|
||||
import androidx.compose.ui.layout.ContentScale
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.text.AnnotatedString
|
||||
import androidx.compose.ui.text.SpanStyle
|
||||
import androidx.compose.ui.text.TextStyle
|
||||
import androidx.compose.ui.text.buildAnnotatedString
|
||||
import androidx.compose.ui.text.font.FontFamily
|
||||
import androidx.compose.ui.text.font.FontWeight
|
||||
import androidx.compose.ui.text.style.TextAlign
|
||||
import androidx.compose.ui.text.style.TextOverflow
|
||||
import androidx.compose.ui.text.withStyle
|
||||
import androidx.compose.ui.unit.Dp
|
||||
import androidx.compose.ui.unit.TextUnit
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.unit.sp
|
||||
import coil.compose.AsyncImage
|
||||
import com.droiddeck.launcher.R
|
||||
import com.droiddeck.launcher.hud.DroidDeckMark
|
||||
import com.droiddeck.launcher.hud.FrameStats
|
||||
import com.droiddeck.launcher.hud.HudMetrics
|
||||
import com.droiddeck.launcher.session.StatsHud
|
||||
import java.io.File
|
||||
import kotlin.math.max
|
||||
import kotlin.math.min
|
||||
import kotlin.math.roundToInt
|
||||
|
||||
internal class CoreGroup(val cores: List<Int>, val maxGhz: Double?)
|
||||
|
||||
internal class HudData(
|
||||
val fps: Float?,
|
||||
val avgFps: Float?,
|
||||
val lows: FrameStats.Lows,
|
||||
val graph: FloatArray,
|
||||
val slowPerMinute: Int?,
|
||||
val engine: String,
|
||||
val resolution: String,
|
||||
val refreshHz: Int,
|
||||
val proton: String?,
|
||||
val device: HudMetrics.Snapshot?,
|
||||
val gpuName: String,
|
||||
val fanRpm: Long?,
|
||||
val title: String?,
|
||||
val cover: File?,
|
||||
val played: String?,
|
||||
val clock: String,
|
||||
val fpsHistory: List<Float>,
|
||||
val cpuHistory: List<Float>,
|
||||
val gpuHistory: List<Float>,
|
||||
val coreGroups: List<CoreGroup>,
|
||||
val gameCores: Set<Int>,
|
||||
val fahrenheit: Boolean,
|
||||
val tempColor: (HudMetrics.TempSensor, Int) -> Color,
|
||||
val redC: (HudMetrics.TempSensor) -> Int?,
|
||||
val historySeconds: Int = 0,
|
||||
) {
|
||||
val frametimeMs: Float? get() = fps?.takeIf { it > 0f }?.let { 1000f / it }
|
||||
val hz: Int get() = if (refreshHz > 0) refreshHz else 60
|
||||
}
|
||||
|
||||
private object HudInk {
|
||||
val value = Color(0xFFF2F5F9)
|
||||
val dim = Color(0xFF9AA4B2)
|
||||
val low = Color(0xFFE4E8EE)
|
||||
val vram = Color(0xFFC98BFF)
|
||||
val sep = Color(0xFF5D6876)
|
||||
val header = Color(0xFFEAF4FF)
|
||||
val amber = Color(0xFFFFB74D)
|
||||
val tile = Color(0x0EFFFFFF)
|
||||
}
|
||||
|
||||
private val Mono = FontFamily.Monospace
|
||||
private val HEADER = 40.dp
|
||||
private const val UNIT = 0.62f
|
||||
|
||||
@Composable
|
||||
internal fun StatsHudView(style: StatsHud, d: HudData, modifier: Modifier, onTap: () -> Unit) {
|
||||
val accent = LocalPalette.current.primary
|
||||
val night = style == StatsHud.NIGHT
|
||||
Box(modifier.clickable(onClick = onTap)) {
|
||||
HudPanel(d.clock, if (style == StatsHud.GRAPHS) stringResource(R.string.stats_hud_last_seconds, d.historySeconds) else null, night, accent) {
|
||||
when (style) {
|
||||
StatsHud.BIG_FPS -> BigFps(d, accent)
|
||||
StatsHud.FULL -> Full(d, accent)
|
||||
StatsHud.TILES -> Tiles(d, accent)
|
||||
StatsHud.RINGS -> Rings(d, accent)
|
||||
StatsHud.GRAPHS -> Graphs(d, accent)
|
||||
StatsHud.CORES -> Cores(d, accent)
|
||||
StatsHud.HEAT -> Heat(d)
|
||||
StatsHud.GAME -> GameCard(d, accent)
|
||||
StatsHud.PACING -> Pacing(d, accent)
|
||||
StatsHud.NIGHT -> Night(d, night)
|
||||
StatsHud.MEGA -> Mega(d, accent)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun HudPanel(clock: String, right: String?, dim: Boolean, accent: Color, content: @Composable ColumnScope.() -> Unit) {
|
||||
val headerInk = if (dim) Color(0xFF7D8794) else HudInk.header
|
||||
Column(
|
||||
Modifier.fillMaxSize()
|
||||
.drawWithCache {
|
||||
val w = size.width
|
||||
val h = size.height
|
||||
val stroke = 2.dp.toPx()
|
||||
val corner = 7.dp.toPx()
|
||||
val radius = min(h / 2f, 64.dp.toPx())
|
||||
val headerPx = HEADER.toPx()
|
||||
val a = accent.toArgb()
|
||||
val body = dPath(0f, 0f, w, h, radius, corner)
|
||||
val half = stroke / 2f
|
||||
val outline = dPath(half, half, w - half, h - half, radius - half, corner)
|
||||
val fill = Paint(Paint.ANTI_ALIAS_FLAG).apply { color = if (dim) argb(245, 0xFF040507.toInt()) else argb(240, 0xFF0E1014.toInt()) }
|
||||
val band = Paint(Paint.ANTI_ALIAS_FLAG).apply {
|
||||
shader = LinearGradient(0f, 0f, w, 0f, argb(if (dim) 20 else 56, a), argb(0, a), Shader.TileMode.CLAMP)
|
||||
}
|
||||
val divider = Paint(Paint.ANTI_ALIAS_FLAG).apply { color = argb(if (dim) 38 else 90, a) }
|
||||
val blur = BlurMaskFilter(4.dp.toPx(), BlurMaskFilter.Blur.NORMAL)
|
||||
val line = Paint(Paint.ANTI_ALIAS_FLAG).apply { style = Paint.Style.STROKE; strokeWidth = stroke; color = argb(if (dim) 90 else 255, a) }
|
||||
val markH = min(h, w) * 0.55f
|
||||
val markX = w - DroidDeckMark.widthFor(markH) - 16.dp.toPx()
|
||||
val markBody = android.graphics.Color.argb(if (dim) 9 else 20, 255, 255, 255)
|
||||
val markOrb = argb(if (dim) 9 else 20, a)
|
||||
onDrawBehind {
|
||||
drawIntoCanvas { canvas ->
|
||||
val c = canvas.nativeCanvas
|
||||
c.drawPath(body, fill)
|
||||
c.save()
|
||||
c.clipPath(body)
|
||||
c.drawRect(0f, 0f, w, headerPx + 1f, band)
|
||||
c.drawRect(0f, headerPx, w, headerPx + max(1f, density), divider)
|
||||
DroidDeckMark.draw(c, markX, (h - markH) / 2f, markH, markBody, markOrb)
|
||||
c.restore()
|
||||
for (pass in 0..2) {
|
||||
line.maskFilter = if (pass < 2) blur else null
|
||||
c.drawPath(outline, line)
|
||||
}
|
||||
line.maskFilter = null
|
||||
}
|
||||
}
|
||||
}
|
||||
.padding(start = 24.dp, end = 34.dp, bottom = 14.dp),
|
||||
) {
|
||||
Row(Modifier.fillMaxWidth().height(HEADER), verticalAlignment = Alignment.CenterVertically, horizontalArrangement = Arrangement.spacedBy(8.dp)) {
|
||||
DroidDeckLogo(16.dp, body = headerInk, orb = if (dim) accent.copy(alpha = 0.5f) else accent)
|
||||
Text("DroidDeck", fontSize = 14.sp, fontWeight = FontWeight.Bold, color = headerInk, maxLines = 1)
|
||||
if (clock.isNotEmpty()) {
|
||||
Text("·", fontSize = 13.sp, fontFamily = Mono, color = HudInk.sep)
|
||||
Text(clock, fontSize = 13.sp, fontFamily = Mono, fontWeight = FontWeight.Bold, color = HudInk.dim, maxLines = 1)
|
||||
}
|
||||
if (right != null) {
|
||||
Spacer(Modifier.weight(1f))
|
||||
Text(right, fontSize = 13.sp, fontFamily = Mono, fontWeight = FontWeight.Bold, color = HudInk.dim, maxLines = 1)
|
||||
}
|
||||
}
|
||||
Spacer(Modifier.height(12.dp))
|
||||
content()
|
||||
}
|
||||
}
|
||||
|
||||
private fun argb(alpha: Int, rgb: Int): Int = (alpha shl 24) or (rgb and 0xFFFFFF)
|
||||
|
||||
private fun dPath(l: Float, t: Float, r: Float, b: Float, rad: Float, corner: Float) = android.graphics.Path().apply {
|
||||
moveTo(l, t + corner)
|
||||
lineTo(l, b - corner); quadTo(l, b, l + corner, b)
|
||||
lineTo(r - rad, b)
|
||||
arcTo(r - 2f * rad, b - 2f * rad, r, b, 90f, -90f, false)
|
||||
lineTo(r, t + rad)
|
||||
arcTo(r - 2f * rad, t, r, t + 2f * rad, 0f, -90f, false)
|
||||
lineTo(l + corner, t); quadTo(l, t, l, t + corner)
|
||||
close()
|
||||
}
|
||||
|
||||
private fun f1(v: Float?): String = v?.takeIf { it > 0f }?.let { "%.1f".format(it) } ?: "–"
|
||||
|
||||
private fun gib(bytes: Long?): String = bytes?.let { "%.1f".format(it / 1073741824.0) } ?: "–"
|
||||
|
||||
private fun AnnotatedString.Builder.num(value: String, unit: String, size: TextUnit, color: Color = HudInk.value, unitScale: Float = UNIT) {
|
||||
withStyle(SpanStyle(color = color)) { append(value) }
|
||||
if (unit.isNotEmpty()) withStyle(SpanStyle(color = HudInk.dim, fontSize = size * unitScale)) { append(unit) }
|
||||
}
|
||||
|
||||
private fun AnnotatedString.Builder.gap() = append(" ")
|
||||
|
||||
private fun tempLabel(d: HudData, c: Int): String = if (d.fahrenheit) "${c * 9 / 5 + 32}°F" else "$c°C"
|
||||
|
||||
private fun AnnotatedString.Builder.temp(d: HudData, sensor: HudMetrics.TempSensor, c: Int?, size: TextUnit) {
|
||||
if (c == null) { num("–", "", size); return }
|
||||
val shown = if (d.fahrenheit) (c * 9 / 5 + 32) else c
|
||||
num(shown.toString(), if (d.fahrenheit) "°F" else "°C", size, d.tempColor(sensor, c))
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun Big(value: String, unit: String, size: TextUnit, color: Color, glow: Boolean, unitScale: Float = 0.27f, modifier: Modifier = Modifier) {
|
||||
Text(
|
||||
buildAnnotatedString { num(value, unit, size, color, unitScale) },
|
||||
modifier = modifier, fontSize = size, fontFamily = Mono, fontWeight = FontWeight.Bold, maxLines = 1, softWrap = false,
|
||||
style = if (glow) TextStyle(shadow = Shadow(color.copy(alpha = 0.85f), Offset.Zero, 18f)) else TextStyle.Default,
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun Line(text: AnnotatedString, size: TextUnit, modifier: Modifier = Modifier, align: TextAlign = TextAlign.Start) {
|
||||
Text(text, modifier = modifier, fontSize = size, fontFamily = Mono, fontWeight = FontWeight.Bold, color = HudInk.value, maxLines = 1,
|
||||
softWrap = false, overflow = TextOverflow.Clip, textAlign = align)
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun Small(text: String, size: TextUnit = 12.sp, color: Color = HudInk.dim, modifier: Modifier = Modifier) {
|
||||
Text(text, modifier = modifier, fontSize = size, fontFamily = Mono, fontWeight = FontWeight.Bold, color = color, maxLines = 1, overflow = TextOverflow.Ellipsis)
|
||||
}
|
||||
|
||||
private fun lowsText(d: HudData, size: TextUnit, accent: Color, deep: Boolean = true): AnnotatedString = buildAnnotatedString {
|
||||
fun part(label: String, v: Float) {
|
||||
withStyle(SpanStyle(color = HudInk.dim)) { append("$label ") }
|
||||
withStyle(SpanStyle(color = accent)) { append(f1(v)) }
|
||||
}
|
||||
part("1%", d.lows.low1Fps)
|
||||
withStyle(SpanStyle(color = HudInk.sep)) { append(" · ") }
|
||||
part("0.1%", d.lows.low01Fps)
|
||||
if (deep) {
|
||||
withStyle(SpanStyle(color = HudInk.sep)) { append(" · ") }
|
||||
part("0.01%", d.lows.low001Fps)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun Graph(values: List<Float>, lo: Float, hi: Float, color: Color, modifier: Modifier) {
|
||||
Canvas(modifier) {
|
||||
if (values.size < 2 || hi <= lo) return@Canvas
|
||||
val path = Path()
|
||||
values.forEachIndexed { i, v ->
|
||||
val x = i / (values.size - 1f) * size.width
|
||||
val y = size.height - ((v - lo) / (hi - lo)).coerceIn(0f, 1f) * size.height
|
||||
if (i == 0) path.moveTo(x, y) else path.lineTo(x, y)
|
||||
}
|
||||
drawPath(path, color.copy(alpha = 0.22f), style = Stroke(width = 6.dp.toPx(), cap = StrokeCap.Round, join = StrokeJoin.Round))
|
||||
drawPath(path, color, style = Stroke(width = 2.2f.dp.toPx(), cap = StrokeCap.Round, join = StrokeJoin.Round))
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun FrameGraph(d: HudData, accent: Color, height: Dp) {
|
||||
Graph(d.graph.toList(), 0f, FrameStats.GRAPH_MAX_MS, lerp(accent, Color.White, 0.3f), Modifier.fillMaxWidth().height(height))
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun Rows(rows: List<Pair<Pair<String, Color>, AnnotatedString>>, size: TextUnit, labelWidth: Dp, gap: Dp) {
|
||||
Column(verticalArrangement = Arrangement.spacedBy(gap)) {
|
||||
for ((label, value) in rows) {
|
||||
Row(verticalAlignment = Alignment.CenterVertically) {
|
||||
Text(label.first, modifier = Modifier.width(labelWidth), fontSize = size, fontFamily = Mono, fontWeight = FontWeight.Bold,
|
||||
color = label.second, maxLines = 1, softWrap = false)
|
||||
Line(value, size)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun row(label: String, color: Color, value: AnnotatedString) = (label to color) to value
|
||||
|
||||
@Composable
|
||||
private fun RowScope.Tile(key: String, keyColor: Color, value: AnnotatedString, sub: String, glow: Color? = null) {
|
||||
Column(
|
||||
Modifier.weight(1f).fillMaxHeight().clip(RoundedCornerShape(9.dp)).background(HudInk.tile).padding(horizontal = 11.dp, vertical = 6.dp),
|
||||
verticalArrangement = Arrangement.Center,
|
||||
) {
|
||||
Small(key, 12.sp, keyColor)
|
||||
Text(value, fontSize = 28.sp, fontFamily = Mono, fontWeight = FontWeight.Bold, color = HudInk.value, maxLines = 1, softWrap = false,
|
||||
style = if (glow != null) TextStyle(shadow = Shadow(glow.copy(alpha = 0.85f), Offset.Zero, 16f)) else TextStyle.Default)
|
||||
Small(sub)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun Ring(ringSize: Dp, fraction: Float?, color: Color, label: String, sub: String?, center: @Composable () -> Unit) {
|
||||
Column(horizontalAlignment = Alignment.CenterHorizontally) {
|
||||
Box(Modifier.size(ringSize), contentAlignment = Alignment.Center) {
|
||||
Canvas(Modifier.fillMaxSize()) {
|
||||
val w = size.minDimension * 0.085f
|
||||
val tl = Offset(w, w)
|
||||
val sz = Size(size.width - 2 * w, size.height - 2 * w)
|
||||
drawArc(Color.White.copy(alpha = 0.08f), 0f, 360f, false, topLeft = tl, size = sz, style = Stroke(width = w))
|
||||
if (fraction != null) {
|
||||
val sweep = (fraction.coerceIn(0f, 1f) * 360f).coerceAtLeast(1f)
|
||||
drawArc(color.copy(alpha = 0.25f), -90f, sweep, false, topLeft = tl, size = sz, style = Stroke(width = w * 1.9f, cap = StrokeCap.Round))
|
||||
drawArc(color, -90f, sweep, false, topLeft = tl, size = sz, style = Stroke(width = w, cap = StrokeCap.Round))
|
||||
}
|
||||
}
|
||||
center()
|
||||
}
|
||||
Small(label, 13.sp, color)
|
||||
if (sub != null) Small(sub, 11.5.sp)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun ColumnScope.BigFps(d: HudData, accent: Color) {
|
||||
Big(f1(d.fps), "fps", 104.sp, accent, glow = true, modifier = Modifier.offset(x = (-4).dp))
|
||||
Line(lowsText(d, 16.sp, accent), 16.sp, Modifier.padding(top = 10.dp))
|
||||
Spacer(Modifier.weight(1f))
|
||||
FrameGraph(d, accent, 72.dp)
|
||||
Small(listOfNotNull(d.engine.ifEmpty { null }, d.title, d.played).joinToString(" · "), modifier = Modifier.padding(top = 8.dp))
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun ColumnScope.Full(d: HudData, accent: Color) {
|
||||
val s = d.device
|
||||
val z = 16.sp
|
||||
val left = listOf(
|
||||
row("GPU", StatsColors.gpu, AnnotatedString(d.gpuName)),
|
||||
row("GPU", StatsColors.gpu, buildAnnotatedString { num(s?.gpuPercent?.toString() ?: "–", "%", z); gap(); temp(d, HudMetrics.TempSensor.GPU, s?.gpuTempC, z) }),
|
||||
row("", StatsColors.gpu, buildAnnotatedString { num(s?.gpuClockMhz?.toString() ?: "–", "MHz", z) }),
|
||||
row("CPU", StatsColors.cpu, buildAnnotatedString { num(s?.cpuPercent?.toString() ?: "–", "%", z); gap(); temp(d, HudMetrics.TempSensor.CPU, s?.cpuTempC, z) }),
|
||||
row("", StatsColors.cpu, buildAnnotatedString { num(s?.cpuClockMhz?.toString() ?: "–", "MHz", z) }),
|
||||
row("VRAM", HudInk.vram, buildAnnotatedString { num(gib(s?.vramUsedBytes), "GiB", z) }),
|
||||
row("RAM", StatsColors.ram, buildAnnotatedString { num(gib(s?.ramUsedBytes), "GiB", z); gap(); num(s?.ramPercent?.roundToInt()?.toString() ?: "–", "%", z) }),
|
||||
row("BAT", StatsColors.bat, buildAnnotatedString { num(s?.battery?.percent?.toString() ?: "–", "%", z); gap(); num(s?.battery?.watts?.takeIf { it > 0f }?.let { "%.1f".format(it) } ?: "–", "W", z) }),
|
||||
)
|
||||
val right = listOf(
|
||||
row(d.engine.ifEmpty { "FPS" }, accent, buildAnnotatedString { num(f1(d.fps), "FPS", z, accent) }),
|
||||
row("", accent, buildAnnotatedString { num(f1(d.frametimeMs), "ms", z) }),
|
||||
row("AVG", HudInk.low, buildAnnotatedString { num(f1(d.avgFps), "FPS", z) }),
|
||||
row("1%", HudInk.low, buildAnnotatedString { num(f1(d.lows.low1Fps), "FPS", z) }),
|
||||
row("0.1%", HudInk.low, buildAnnotatedString { num(f1(d.lows.low01Fps), "FPS", z) }),
|
||||
row("0.01%", HudInk.low, buildAnnotatedString { num(f1(d.lows.low001Fps), "FPS", z) }),
|
||||
row("TEMP", StatsColors.bat, buildAnnotatedString { temp(d, HudMetrics.TempSensor.BATTERY, s?.battery?.tempC, z) }),
|
||||
row("FAN", HudInk.dim, buildAnnotatedString { num(d.fanRpm?.toString() ?: "–", "rpm", z) }),
|
||||
)
|
||||
Row(horizontalArrangement = Arrangement.spacedBy(28.dp)) {
|
||||
Rows(left, z, 52.dp, 6.dp)
|
||||
Rows(right, z, 62.dp, 6.dp)
|
||||
}
|
||||
Spacer(Modifier.weight(1f))
|
||||
val frametime = stringResource(R.string.stats_hud_frametime)
|
||||
Line(buildAnnotatedString {
|
||||
withStyle(SpanStyle(color = accent)) { append(frametime) }
|
||||
withStyle(SpanStyle(color = HudInk.dim)) { append(" min %.1f · max %.1f ms".format(d.graph.filter { it > 0f }.minOrNull() ?: 0f, d.graph.maxOrNull() ?: 0f)) }
|
||||
}, 12.5.sp, Modifier.padding(bottom = 6.dp))
|
||||
FrameGraph(d, accent, 44.dp)
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun ColumnScope.Tiles(d: HudData, accent: Color) {
|
||||
val s = d.device
|
||||
val z = 28.sp
|
||||
val u = 0.5f
|
||||
Column(Modifier.weight(1f), verticalArrangement = Arrangement.spacedBy(8.dp)) {
|
||||
Row(Modifier.weight(1f), horizontalArrangement = Arrangement.spacedBy(8.dp)) {
|
||||
Tile("FPS", accent, buildAnnotatedString { num(f1(d.fps), "", z) }, "${f1(d.avgFps)} avg · ${f1(d.lows.low1Fps)} 1%", glow = accent)
|
||||
Tile("FRAME", HudInk.dim, buildAnnotatedString { num(f1(d.frametimeMs), "ms", z, unitScale = u) }, "max %.1f ms".format(d.graph.maxOrNull() ?: 0f))
|
||||
Tile("GPU", StatsColors.gpu, buildAnnotatedString { num(s?.gpuPercent?.toString() ?: "–", "%", z, unitScale = u) },
|
||||
listOfNotNull(s?.gpuTempC?.let { tempLabel(d, it) }, s?.gpuClockMhz?.let { "$it MHz" }).joinToString(" · "))
|
||||
}
|
||||
Row(Modifier.weight(1f), horizontalArrangement = Arrangement.spacedBy(8.dp)) {
|
||||
Tile("CPU", StatsColors.cpu, buildAnnotatedString { num(s?.cpuPercent?.toString() ?: "–", "%", z, unitScale = u) },
|
||||
listOfNotNull(s?.cpuTempC?.let { tempLabel(d, it) }, s?.cpuClockMhz?.let { "$it MHz" }).joinToString(" · "))
|
||||
Tile("RAM", StatsColors.ram, buildAnnotatedString { num(s?.ramPercent?.roundToInt()?.toString() ?: "–", "%", z, unitScale = u) },
|
||||
"${gib(s?.ramUsedBytes)} / ${gib(s?.ramTotalBytes)} GiB")
|
||||
Tile("VRAM", HudInk.vram, buildAnnotatedString { num(gib(s?.vramUsedBytes), "GiB", z, unitScale = u) }, d.gpuName)
|
||||
}
|
||||
Row(Modifier.weight(1f), horizontalArrangement = Arrangement.spacedBy(8.dp)) {
|
||||
Tile("BAT TEMP", StatsColors.bat, buildAnnotatedString { temp(d, HudMetrics.TempSensor.BATTERY, s?.battery?.tempC, z) }, stringResource(R.string.stats_hud_battery_temp))
|
||||
Tile("FAN", HudInk.dim, buildAnnotatedString { num(d.fanRpm?.toString() ?: "–", "", z) }, "rpm")
|
||||
Tile("BATTERY", StatsColors.bat, buildAnnotatedString { num(s?.battery?.percent?.toString() ?: "–", "%", z, unitScale = u) },
|
||||
listOfNotNull(s?.battery?.watts?.takeIf { it > 0f }?.let { "%.1f W".format(it) }, s?.battery?.runtimeText).joinToString(" · "))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun ColumnScope.Rings(d: HudData, accent: Color) {
|
||||
val s = d.device
|
||||
Row(Modifier.weight(1f).fillMaxWidth(), verticalAlignment = Alignment.CenterVertically, horizontalArrangement = Arrangement.spacedBy(26.dp)) {
|
||||
Ring(200.dp, d.fps?.let { it / d.hz }, accent, stringResource(R.string.stats_hud_fps_of, d.hz), "1% ${f1(d.lows.low1Fps)} · 0.1% ${f1(d.lows.low01Fps)}") {
|
||||
Big(f1(d.fps), "fps", 40.sp, HudInk.value, glow = true, unitScale = 0.4f)
|
||||
}
|
||||
Column(verticalArrangement = Arrangement.spacedBy(14.dp)) {
|
||||
Row(horizontalArrangement = Arrangement.spacedBy(22.dp)) {
|
||||
Ring(96.dp, s?.gpuPercent?.let { it / 100f }, StatsColors.gpu, "GPU", s?.gpuTempC?.let { tempLabel(d, it) }) { Big(s?.gpuPercent?.toString() ?: "–", "%", 20.sp, HudInk.value, false, 0.6f) }
|
||||
Ring(96.dp, s?.cpuPercent?.let { it / 100f }, StatsColors.cpu, "CPU", s?.cpuTempC?.let { tempLabel(d, it) }) { Big(s?.cpuPercent?.toString() ?: "–", "%", 20.sp, HudInk.value, false, 0.6f) }
|
||||
}
|
||||
Row(horizontalArrangement = Arrangement.spacedBy(22.dp)) {
|
||||
Ring(96.dp, s?.ramPercent?.let { it / 100f }, StatsColors.ram, "RAM", s?.let { "${gib(it.ramUsedBytes)} GiB" }) {
|
||||
Big(s?.ramPercent?.roundToInt()?.toString() ?: "–", "%", 20.sp, HudInk.value, false, 0.6f)
|
||||
}
|
||||
Ring(96.dp, s?.battery?.percent?.let { it / 100f }, StatsColors.bat, "BAT", s?.battery?.watts?.takeIf { it > 0f }?.let { "%.1f W".format(it) }) {
|
||||
Big(s?.battery?.percent?.toString() ?: "–", "%", 20.sp, HudInk.value, false, 0.6f)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun ColumnScope.Graphs(d: HudData, accent: Color) {
|
||||
val s = d.device
|
||||
val frameMs = d.fpsHistory.map { 1000f / max(it, 1f) }
|
||||
@Composable
|
||||
fun graphRow(label: String, color: Color, values: List<Float>, hi: Float, value: AnnotatedString) {
|
||||
Row(Modifier.fillMaxWidth().height(56.dp), verticalAlignment = Alignment.CenterVertically, horizontalArrangement = Arrangement.spacedBy(12.dp)) {
|
||||
Small(label, 14.sp, color, Modifier.width(62.dp))
|
||||
Graph(values, 0f, hi, color, Modifier.weight(1f).height(52.dp))
|
||||
Line(value, 24.sp, Modifier.width(128.dp), TextAlign.End)
|
||||
}
|
||||
}
|
||||
Column(Modifier.weight(1f), verticalArrangement = Arrangement.SpaceEvenly) {
|
||||
graphRow("FPS", accent, d.fpsHistory, max(d.hz.toFloat(), d.fpsHistory.maxOrNull() ?: 0f) * 1.1f, buildAnnotatedString { num(f1(d.fps), "fps", 24.sp, accent, 0.5f) })
|
||||
graphRow("FRAME", lerp(accent, Color.White, 0.3f), frameMs, max(40f, (frameMs.maxOrNull() ?: 0f) * 1.2f), buildAnnotatedString { num(f1(d.frametimeMs), "ms", 24.sp, unitScale = 0.5f) })
|
||||
graphRow("CPU", StatsColors.cpu, d.cpuHistory, 100f, buildAnnotatedString { num(s?.cpuPercent?.toString() ?: "–", "%", 24.sp, unitScale = 0.5f) })
|
||||
graphRow("GPU", StatsColors.gpu, d.gpuHistory, 100f, buildAnnotatedString { num(s?.gpuPercent?.toString() ?: "–", "%", 24.sp, unitScale = 0.5f) })
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun ColumnScope.Cores(d: HudData, accent: Color) {
|
||||
val s = d.device
|
||||
Row(verticalAlignment = Alignment.Bottom, horizontalArrangement = Arrangement.spacedBy(16.dp)) {
|
||||
Big(f1(d.fps), "fps", 40.sp, accent, glow = true, unitScale = 0.38f)
|
||||
Small(listOfNotNull(d.engine.ifEmpty { null }, d.frametimeMs?.let { "%.1f ms".format(it) },
|
||||
s?.cpuPercent?.let { "CPU $it%" }, s?.cpuTempC?.let { tempLabel(d, it) }).joinToString(" · "), 15.sp, modifier = Modifier.padding(bottom = 6.dp))
|
||||
}
|
||||
Spacer(Modifier.height(10.dp))
|
||||
val gameTag = stringResource(R.string.stats_hud_game_cores)
|
||||
Column(verticalArrangement = Arrangement.spacedBy(5.dp)) {
|
||||
for (group in d.coreGroups) {
|
||||
for (core in group.cores) {
|
||||
val p = s?.perCorePercent?.getOrNull(core)?.takeIf { it >= 0 }
|
||||
val mhz = s?.perCoreClockMhz?.getOrNull(core)?.takeIf { it > 0 }
|
||||
Row(Modifier.fillMaxWidth().height(20.dp), verticalAlignment = Alignment.CenterVertically, horizontalArrangement = Arrangement.spacedBy(10.dp)) {
|
||||
Small("C$core", 14.sp, StatsColors.cpu, Modifier.width(40.dp))
|
||||
Box(Modifier.weight(1f).height(14.dp).clip(RoundedCornerShape(7.dp)).background(Color.White.copy(alpha = 0.07f))) {
|
||||
if (p != null && p > 0) Box(
|
||||
Modifier.fillMaxHeight().fillMaxWidth(p / 100f).clip(RoundedCornerShape(7.dp))
|
||||
.background(Brush.horizontalGradient(listOf(Color(0xFF2B6FC9), if (p >= 80) HudInk.amber else StatsColors.cpu))),
|
||||
)
|
||||
}
|
||||
Line(buildAnnotatedString { num(p?.toString() ?: "–", "%", 14.sp); append(" "); num(mhz?.toString() ?: "–", "MHz", 14.sp) },
|
||||
14.sp, Modifier.width(118.dp), TextAlign.End)
|
||||
}
|
||||
}
|
||||
val label = group.maxGhz?.let { stringResource(R.string.stats_hud_core_group, group.cores.size, "%.2f".format(it)) }
|
||||
?: stringResource(R.string.stats_hud_core_group_unknown, group.cores.size)
|
||||
Row(Modifier.padding(start = 50.dp), verticalAlignment = Alignment.CenterVertically, horizontalArrangement = Arrangement.spacedBy(6.dp)) {
|
||||
Small(label, 11.5.sp)
|
||||
if (d.gameCores.isNotEmpty() && group.cores.any { it in d.gameCores }) {
|
||||
Text(gameTag.uppercase(), fontSize = 10.sp, fontFamily = Mono, fontWeight = FontWeight.Bold, color = Color(0xFF0A0B0D),
|
||||
modifier = Modifier.clip(RoundedCornerShape(4.dp)).background(StatsColors.cpu).padding(horizontal = 4.dp))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun ColumnScope.Heat(d: HudData) {
|
||||
val s = d.device
|
||||
@Composable
|
||||
fun bar(label: String, color: Color, sensor: HudMetrics.TempSensor, c: Int?) {
|
||||
val red = d.redC(sensor)
|
||||
Row(Modifier.fillMaxWidth().height(28.dp), verticalAlignment = Alignment.CenterVertically, horizontalArrangement = Arrangement.spacedBy(12.dp)) {
|
||||
Small(label, 15.sp, color, Modifier.width(48.dp))
|
||||
BoxWithConstraints(Modifier.weight(1f).height(20.dp).clip(RoundedCornerShape(10.dp)).background(Color.White.copy(alpha = 0.07f))) {
|
||||
if (c != null && c > 0) Box(Modifier.fillMaxHeight().fillMaxWidth((c / 100f).coerceIn(0f, 1f)).clip(RoundedCornerShape(10.dp))
|
||||
.background(Brush.horizontalGradient(listOf(color.copy(alpha = 0.55f), color))))
|
||||
if (red != null && red in 1..99) Box(Modifier.offset(x = maxWidth * (red / 100f)).width(2.dp).fillMaxHeight().background(Color(0xFFEF5350)))
|
||||
}
|
||||
Line(buildAnnotatedString { temp(d, sensor, c, 24.sp) }, 24.sp, Modifier.width(92.dp), TextAlign.End)
|
||||
}
|
||||
}
|
||||
Column(verticalArrangement = Arrangement.spacedBy(12.dp)) {
|
||||
bar("CPU", StatsColors.cpu, HudMetrics.TempSensor.CPU, s?.cpuTempC)
|
||||
bar("GPU", StatsColors.gpu, HudMetrics.TempSensor.GPU, s?.gpuTempC)
|
||||
bar("BAT", StatsColors.bat, HudMetrics.TempSensor.BATTERY, s?.battery?.tempC)
|
||||
}
|
||||
Small(stringResource(R.string.stats_hud_red_mark), 12.sp, modifier = Modifier.padding(top = 8.dp))
|
||||
Spacer(Modifier.weight(1f))
|
||||
Row(Modifier.fillMaxWidth().height(96.dp), horizontalArrangement = Arrangement.spacedBy(8.dp)) {
|
||||
Tile("FAN", HudInk.dim, buildAnnotatedString { num(d.fanRpm?.toString() ?: "–", "", 28.sp) }, "rpm")
|
||||
Tile("POWER", StatsColors.bat, buildAnnotatedString { num(s?.battery?.watts?.takeIf { it > 0f }?.let { "%.1f".format(it) } ?: "–", "W", 28.sp, unitScale = 0.5f) },
|
||||
stringResource(R.string.stats_hud_whole_device))
|
||||
Tile("BATTERY", StatsColors.bat, buildAnnotatedString { num(s?.battery?.percent?.toString() ?: "–", "%", 28.sp, unitScale = 0.5f) }, s?.battery?.runtimeText ?: "")
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun ColumnScope.GameCard(d: HudData, accent: Color) {
|
||||
Row(Modifier.weight(1f).fillMaxWidth(), horizontalArrangement = Arrangement.spacedBy(20.dp)) {
|
||||
Box(
|
||||
Modifier.width(150.dp).fillMaxHeight().clip(RoundedCornerShape(10.dp))
|
||||
.background(Brush.linearGradient(listOf(lerp(accent, Color.Black, 0.55f), Color(0xFF0B0D10)))),
|
||||
contentAlignment = Alignment.BottomStart,
|
||||
) {
|
||||
if (d.cover != null) AsyncImage(model = d.cover, contentDescription = null, contentScale = ContentScale.Crop, modifier = Modifier.fillMaxSize())
|
||||
else Text(d.title.orEmpty(), fontSize = 20.sp, fontWeight = FontWeight.Bold, color = Color.White.copy(alpha = 0.85f), maxLines = 3,
|
||||
modifier = Modifier.padding(12.dp))
|
||||
}
|
||||
Column(verticalArrangement = Arrangement.spacedBy(6.dp)) {
|
||||
Text(d.title.orEmpty(), fontSize = 26.sp, fontWeight = FontWeight.Bold, color = Color.White, maxLines = 2, overflow = TextOverflow.Ellipsis)
|
||||
d.played?.let { Small(stringResource(R.string.stats_hud_playing_for, it), 13.5.sp) }
|
||||
Line(buildAnnotatedString {
|
||||
withStyle(SpanStyle(color = accent)) { append(d.engine) }
|
||||
if (d.engine.isNotEmpty() && d.proton != null) withStyle(SpanStyle(color = HudInk.dim)) { append(" · ") }
|
||||
d.proton?.let { withStyle(SpanStyle(color = HudInk.vram)) { append(it) } }
|
||||
}, 13.5.sp)
|
||||
if (d.resolution.isNotEmpty()) Small(d.resolution, 13.5.sp)
|
||||
Big(f1(d.fps), "fps", 60.sp, accent, glow = true, unitScale = 0.3f, modifier = Modifier.padding(top = 6.dp))
|
||||
Line(lowsText(d, 14.sp, accent, deep = false), 14.sp)
|
||||
}
|
||||
}
|
||||
Spacer(Modifier.height(10.dp))
|
||||
FrameGraph(d, accent, 40.dp)
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun ColumnScope.Pacing(d: HudData, accent: Color) {
|
||||
@Composable
|
||||
fun stat(label: String, value: String, color: Color = HudInk.value) {
|
||||
Column {
|
||||
Small(label, 13.sp)
|
||||
Text(value, fontSize = 22.sp, fontFamily = Mono, fontWeight = FontWeight.Bold, color = color, maxLines = 1)
|
||||
}
|
||||
}
|
||||
Row(verticalAlignment = Alignment.Bottom, horizontalArrangement = Arrangement.spacedBy(24.dp)) {
|
||||
Big(f1(d.fps), "fps", 52.sp, accent, glow = true, unitScale = 0.3f)
|
||||
stat(stringResource(R.string.stats_hud_avg_frame), d.frametimeMs?.let { "%.1f ms".format(it) } ?: "–")
|
||||
stat(stringResource(R.string.stats_hud_low), f1(d.lows.low1Fps))
|
||||
val slow = d.slowPerMinute
|
||||
stat(stringResource(R.string.stats_hud_slow_frames), slow?.let { stringResource(R.string.stats_hud_per_minute, it) } ?: "–",
|
||||
if ((slow ?: 0) > 0) HudInk.amber else HudInk.value)
|
||||
}
|
||||
Spacer(Modifier.height(12.dp))
|
||||
val target = 1000f / d.hz
|
||||
BoxWithConstraints(Modifier.weight(1f).fillMaxWidth()) {
|
||||
val graphColor = lerp(accent, Color.White, 0.3f)
|
||||
Canvas(Modifier.fillMaxSize()) {
|
||||
val axis = Color.White.copy(alpha = 0.12f)
|
||||
drawLine(axis, Offset(0f, 0f), Offset(0f, size.height), 1.dp.toPx())
|
||||
drawLine(axis, Offset(0f, size.height), Offset(size.width, size.height), 1.dp.toPx())
|
||||
val dash = PathEffect.dashPathEffect(floatArrayOf(6.dp.toPx(), 5.dp.toPx()))
|
||||
for ((ms, color) in listOf(target to Color.White.copy(alpha = 0.25f), target * 2 to HudInk.amber.copy(alpha = 0.5f))) {
|
||||
val y = size.height * (1f - ms / FrameStats.GRAPH_MAX_MS)
|
||||
drawLine(color, Offset(0f, y), Offset(size.width, y), 1.dp.toPx(), pathEffect = dash)
|
||||
}
|
||||
}
|
||||
Graph(d.graph.toList(), 0f, FrameStats.GRAPH_MAX_MS, graphColor, Modifier.fillMaxSize())
|
||||
for ((ms, color) in listOf(target to HudInk.dim, target * 2 to HudInk.amber)) {
|
||||
Small("%.1f ms · %d fps".format(ms, (1000f / ms).roundToInt()), 11.sp, color,
|
||||
Modifier.align(Alignment.TopEnd).offset(y = maxHeight * (1f - ms / FrameStats.GRAPH_MAX_MS) - 17.dp))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun ColumnScope.Night(d: HudData, dim: Boolean) {
|
||||
val clockInk = if (dim) Color(0xFF6F7A87) else HudInk.value
|
||||
val fpsInk = if (dim) Color(0xFF4D7FA8) else LocalPalette.current.primary
|
||||
Column(Modifier.weight(1f), verticalArrangement = Arrangement.Center) {
|
||||
Text(d.clock, fontSize = 88.sp, fontWeight = FontWeight.Light, color = clockInk, maxLines = 1, softWrap = false)
|
||||
Line(buildAnnotatedString {
|
||||
num(f1(d.fps), "fps", 30.sp, fpsInk)
|
||||
d.device?.battery?.percent?.let {
|
||||
withStyle(SpanStyle(color = HudInk.sep)) { append(" · ") }
|
||||
num(it.toString(), "%", 30.sp, if (dim) Color(0xFF6B5A44) else StatsColors.bat)
|
||||
}
|
||||
}, 30.sp, Modifier.padding(top = 14.dp))
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun ColumnScope.Mega(d: HudData, accent: Color) {
|
||||
val s = d.device
|
||||
val z = 13.5.sp
|
||||
val left = mutableListOf(
|
||||
row("GPU", StatsColors.gpu, AnnotatedString(d.gpuName)),
|
||||
row("GPU", StatsColors.gpu, buildAnnotatedString {
|
||||
num(s?.gpuPercent?.toString() ?: "–", "%", z); gap(); temp(d, HudMetrics.TempSensor.GPU, s?.gpuTempC, z); gap(); num(s?.gpuClockMhz?.toString() ?: "–", "MHz", z)
|
||||
}),
|
||||
row("CPU", StatsColors.cpu, buildAnnotatedString {
|
||||
num(s?.cpuPercent?.toString() ?: "–", "%", z); gap(); temp(d, HudMetrics.TempSensor.CPU, s?.cpuTempC, z); gap(); num(s?.cpuClockMhz?.toString() ?: "–", "MHz", z)
|
||||
}),
|
||||
)
|
||||
val cores = s?.perCorePercent?.size ?: 0
|
||||
for (i in 0 until cores) {
|
||||
left += row("C$i", StatsColors.cpu, buildAnnotatedString {
|
||||
num(s?.perCorePercent?.getOrNull(i)?.takeIf { it >= 0 }?.toString() ?: "–", "%", z); gap()
|
||||
num(s?.perCoreClockMhz?.getOrNull(i)?.takeIf { it > 0 }?.toString() ?: "–", "MHz", z)
|
||||
})
|
||||
}
|
||||
val right = listOf(
|
||||
row("VRAM", HudInk.vram, buildAnnotatedString { num(gib(s?.vramUsedBytes), "GiB", z) }),
|
||||
row("RAM", StatsColors.ram, buildAnnotatedString { num("${gib(s?.ramUsedBytes)}/${gib(s?.ramTotalBytes)}", "GiB", z); gap(); num(s?.ramPercent?.roundToInt()?.toString() ?: "–", "%", z) }),
|
||||
row("SWP", StatsColors.ram, buildAnnotatedString { num("${gib(s?.swapUsedBytes)}/${gib(s?.swapTotalBytes)}", "GiB", z) }),
|
||||
row("NET", accent, buildAnnotatedString {
|
||||
withStyle(SpanStyle(color = HudInk.dim)) { append("↓") }; num(s?.netDownBps?.let { (it / 1024).toString() } ?: "–", "", z)
|
||||
append(" "); withStyle(SpanStyle(color = HudInk.dim)) { append("↑") }; num(s?.netUpBps?.let { (it / 1024).toString() } ?: "–", "KB/s", z)
|
||||
}),
|
||||
row("BAT", StatsColors.bat, buildAnnotatedString {
|
||||
num(s?.battery?.percent?.toString() ?: "–", "%", z); gap(); temp(d, HudMetrics.TempSensor.BATTERY, s?.battery?.tempC, z); gap()
|
||||
num(s?.battery?.watts?.takeIf { it > 0f }?.let { "%.1f".format(it) } ?: "–", "W", z)
|
||||
}),
|
||||
row("FAN", HudInk.dim, buildAnnotatedString { num(d.fanRpm?.toString() ?: "–", "rpm", z) }),
|
||||
row(d.engine.ifEmpty { "FPS" }, accent, buildAnnotatedString { num(f1(d.fps), "FPS", z, accent); gap(); num(f1(d.frametimeMs), "ms", z) }),
|
||||
row("AVG", HudInk.low, buildAnnotatedString { num(f1(d.avgFps), "FPS", z) }),
|
||||
row("1%", HudInk.low, buildAnnotatedString { num(f1(d.lows.low1Fps), "FPS", z) }),
|
||||
row("0.1%", HudInk.low, buildAnnotatedString { num(f1(d.lows.low01Fps), "FPS", z) }),
|
||||
row("0.01%", HudInk.low, buildAnnotatedString { num(f1(d.lows.low001Fps), "FPS", z) }),
|
||||
)
|
||||
Row(horizontalArrangement = Arrangement.spacedBy(24.dp)) {
|
||||
Rows(left, z, 44.dp, 3.dp)
|
||||
Rows(right, z, 58.dp, 3.dp)
|
||||
}
|
||||
Spacer(Modifier.weight(1f))
|
||||
Line(buildAnnotatedString {
|
||||
withStyle(SpanStyle(color = HudInk.dim)) { append("RES ") }; append(d.resolution.ifEmpty { "–" })
|
||||
d.proton?.let { withStyle(SpanStyle(color = HudInk.dim)) { append(" · ") }; withStyle(SpanStyle(color = HudInk.vram)) { append(it) } }
|
||||
d.played?.let { withStyle(SpanStyle(color = HudInk.dim)) { append(" · ") }; append(it) }
|
||||
}, 12.5.sp, Modifier.padding(bottom = 6.dp))
|
||||
FrameGraph(d, accent, 30.dp)
|
||||
}
|
||||
@@ -36,18 +36,23 @@ import androidx.compose.ui.graphics.drawscope.drawIntoCanvas
|
||||
import androidx.compose.ui.graphics.lerp
|
||||
import androidx.compose.ui.graphics.nativeCanvas
|
||||
import androidx.compose.ui.graphics.toArgb
|
||||
import androidx.compose.ui.layout.Layout
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.text.font.FontFamily
|
||||
import androidx.compose.ui.text.font.FontWeight
|
||||
import androidx.compose.ui.text.style.TextAlign
|
||||
import androidx.compose.ui.text.style.TextOverflow
|
||||
import androidx.compose.ui.unit.Constraints
|
||||
import androidx.compose.ui.unit.Dp
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.unit.TextUnit
|
||||
import androidx.compose.ui.unit.sp
|
||||
import com.droiddeck.launcher.R
|
||||
import com.droiddeck.launcher.hud.DroidDeckMark
|
||||
import com.droiddeck.launcher.hud.FrameStats
|
||||
import com.droiddeck.launcher.hud.HudMetrics
|
||||
import com.droiddeck.launcher.session.StatsStrip
|
||||
import com.droiddeck.launcher.session.StripMetric
|
||||
import kotlin.math.max
|
||||
import kotlin.math.roundToInt
|
||||
|
||||
@@ -66,11 +71,21 @@ internal object StatsColors {
|
||||
|
||||
internal class History(private val capacity: Int) {
|
||||
private val values = ArrayDeque<Float>(capacity)
|
||||
fun add(v: Float) {
|
||||
if (values.size == capacity) values.removeFirst()
|
||||
private val times = ArrayDeque<Long>(capacity)
|
||||
fun add(v: Float, atMs: Long = 0L) {
|
||||
if (values.size == capacity) {
|
||||
values.removeFirst()
|
||||
times.removeFirst()
|
||||
}
|
||||
values.addLast(v)
|
||||
times.addLast(atMs)
|
||||
}
|
||||
fun snapshot(): List<Float> = values.toList()
|
||||
fun spanSeconds(): Int {
|
||||
val first = times.firstOrNull()?.takeIf { it > 0L } ?: return 0
|
||||
val last = times.last()
|
||||
return ((last - first + 500L) / 1000L).toInt()
|
||||
}
|
||||
}
|
||||
|
||||
private val MARK_RATIO = DroidDeckMark.widthFor(1f)
|
||||
@@ -144,6 +159,12 @@ internal class StripData(
|
||||
val wattsHistory: List<Float>,
|
||||
val clock: String,
|
||||
val game: String? = null,
|
||||
val engine: String = "",
|
||||
val lows: FrameStats.Lows? = null,
|
||||
val graph: FloatArray? = null,
|
||||
val metrics: Set<StripMetric> = StripMetric.defaults,
|
||||
val fahrenheit: Boolean = false,
|
||||
val tempColor: ((HudMetrics.TempSensor, Int) -> Color)? = null,
|
||||
)
|
||||
|
||||
private fun percentTemp(percent: Int?, temp: Int?): String =
|
||||
@@ -158,6 +179,7 @@ private fun fpsText(fps: Float?): String = fps?.takeIf { it > 0f }?.let { "%.1f"
|
||||
internal fun StatsStripView(style: StatsStrip, d: StripData, onTap: () -> Unit) {
|
||||
Box(Modifier.fillMaxWidth().clickable(onClick = onTap)) {
|
||||
when (style) {
|
||||
StatsStrip.LINE -> LineStrip(d)
|
||||
StatsStrip.PANEL -> PanelStrip(d)
|
||||
StatsStrip.TILES -> TilesStrip(d)
|
||||
StatsStrip.RINGS -> RingsStrip(d)
|
||||
@@ -165,6 +187,113 @@ internal fun StatsStripView(style: StatsStrip, d: StripData, onTap: () -> Unit)
|
||||
}
|
||||
}
|
||||
|
||||
private object LineInk {
|
||||
val value = Color(0xFFF2F5F9)
|
||||
val dim = Color(0xFF9AA4B2)
|
||||
val vram = Color(0xFFC98BFF)
|
||||
}
|
||||
|
||||
private class LineItem(val priority: Int, val content: @Composable () -> Unit)
|
||||
|
||||
@Composable
|
||||
internal fun LineStrip(d: StripData, modifier: Modifier = Modifier) {
|
||||
val pal = LocalPalette.current
|
||||
val shape = RoundedCornerShape(12.dp)
|
||||
val m = d.metrics
|
||||
val s = d.device
|
||||
val items = ArrayList<LineItem>()
|
||||
items += LineItem(0) { DroidDeckLogo(16.dp) }
|
||||
if (StripMetric.ENGINE in m && d.engine.isNotEmpty()) items += LineItem(70) { LineText(d.engine, LineInk.dim, 12.sp) }
|
||||
items += LineItem(0) {
|
||||
Row(verticalAlignment = Alignment.Bottom) {
|
||||
LineText(fpsText(d.fps), LineInk.value, 17.sp)
|
||||
LineText("fps", LineInk.dim, 10.sp, Modifier.padding(start = 2.dp, bottom = 2.dp))
|
||||
}
|
||||
}
|
||||
val graph = d.graph
|
||||
if (StripMetric.GRAPH in m && graph != null) items += LineItem(60) {
|
||||
Sparkline(graph.asList(), 0f, FrameStats.GRAPH_MAX_MS, lerp(pal.primary, Color.White, 0.3f), Modifier.width(56.dp).height(16.dp))
|
||||
}
|
||||
if (StripMetric.LOWS in m) {
|
||||
items += LineItem(10) { LinePair("1%", LineInk.dim, lowText(d.lows?.low1Fps), StatsColors.cpu) }
|
||||
items += LineItem(90) { LinePair("0.1%", LineInk.dim, lowText(d.lows?.low01Fps), StatsColors.cpu) }
|
||||
}
|
||||
if (StripMetric.LOW001 in m) items += LineItem(100) { LinePair("0.01%", LineInk.dim, lowText(d.lows?.low001Fps), StatsColors.cpu) }
|
||||
if (StripMetric.GPU in m || StripMetric.GPU_TEMP in m) items += LineItem(20) {
|
||||
LineMetric("GPU", StatsColors.gpu, if (StripMetric.GPU in m) s?.gpuPercent else null,
|
||||
if (StripMetric.GPU_TEMP in m) s?.gpuTempC else null, HudMetrics.TempSensor.GPU, d)
|
||||
}
|
||||
if (StripMetric.CPU in m || StripMetric.CPU_TEMP in m) items += LineItem(30) {
|
||||
LineMetric("CPU", StatsColors.cpu, if (StripMetric.CPU in m) s?.cpuPercent else null,
|
||||
if (StripMetric.CPU_TEMP in m) s?.cpuTempC else null, HudMetrics.TempSensor.CPU, d)
|
||||
}
|
||||
if (StripMetric.VRAM in m) items += LineItem(80) { LinePair("VRAM", LineInk.vram, gibText(s?.vramUsedBytes), LineInk.vram) }
|
||||
if (StripMetric.RAM in m) items += LineItem(40) { LinePair("RAM", StatsColors.ram, s?.let { "${it.ramPercent.roundToInt()}%" } ?: "–", StatsColors.ram) }
|
||||
val clock = StripMetric.CLOCK in m && d.clock.isNotEmpty()
|
||||
if (clock) items += LineItem(50) { LineText(d.clock, LineInk.dim, 12.sp) }
|
||||
PriorityRow(
|
||||
IntArray(items.size) { items[it].priority }, 10.dp, pinLast = clock,
|
||||
modifier = modifier.fillMaxWidth().clip(shape).background(pal.surface).border(1.dp, pal.primary.copy(alpha = 0.35f), shape)
|
||||
.padding(horizontal = 10.dp, vertical = 6.dp),
|
||||
) {
|
||||
items.forEach { it.content() }
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun PriorityRow(priorities: IntArray, spacing: Dp, pinLast: Boolean, modifier: Modifier = Modifier, content: @Composable () -> Unit) {
|
||||
Layout(content, modifier) { measurables, constraints ->
|
||||
val gap = spacing.roundToPx()
|
||||
val loose = Constraints(maxHeight = constraints.maxHeight)
|
||||
val placeables = measurables.map { it.measure(loose) }
|
||||
val max = if (constraints.hasBoundedWidth) constraints.maxWidth else Int.MAX_VALUE
|
||||
val keep = fitByPriority(IntArray(placeables.size) { placeables[it].width }, priorities.copyOf(placeables.size), gap, max)
|
||||
val height = maxOf(constraints.minHeight, placeables.filterIndexed { i, _ -> keep[i] }.maxOfOrNull { it.height } ?: 0)
|
||||
val used = placeables.filterIndexed { i, _ -> keep[i] }.sumOf { it.width } + gap * maxOf(0, keep.count { it } - 1)
|
||||
val width = if (constraints.hasBoundedWidth) constraints.maxWidth else used
|
||||
layout(width, height) {
|
||||
var x = 0
|
||||
val last = placeables.lastIndex
|
||||
placeables.forEachIndexed { i, p ->
|
||||
if (!keep[i]) return@forEachIndexed
|
||||
val px = if (pinLast && i == last) width - p.width else x
|
||||
p.placeRelative(px, (height - p.height) / 2)
|
||||
x += p.width + gap
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun LineText(text: String, color: Color, size: TextUnit = 13.sp, modifier: Modifier = Modifier) {
|
||||
Text(text, fontSize = size, fontWeight = FontWeight.Bold, fontFamily = FontFamily.Monospace, color = color, maxLines = 1, softWrap = false,
|
||||
modifier = modifier)
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun LinePair(label: String, labelColor: Color, value: String, valueColor: Color) {
|
||||
Row(verticalAlignment = Alignment.CenterVertically, horizontalArrangement = Arrangement.spacedBy(4.dp)) {
|
||||
LineText(label, labelColor, 11.sp)
|
||||
LineText(value, valueColor)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun LineMetric(label: String, color: Color, percent: Int?, tempC: Int?, sensor: HudMetrics.TempSensor, d: StripData) {
|
||||
Row(verticalAlignment = Alignment.CenterVertically, horizontalArrangement = Arrangement.spacedBy(4.dp)) {
|
||||
LineText(label, color, 11.sp)
|
||||
if (percent == null && tempC == null) LineText("–", color)
|
||||
if (percent != null) LineText("$percent%", color)
|
||||
if (tempC != null) LineText(tempText(tempC, d.fahrenheit), d.tempColor?.invoke(sensor, tempC) ?: LineInk.value)
|
||||
}
|
||||
}
|
||||
|
||||
private fun lowText(fps: Float?): String = fps?.takeIf { it > 0f }?.let { "%.1f".format(it) } ?: "–"
|
||||
|
||||
private fun gibText(bytes: Long?): String = bytes?.let { "%.1fGiB".format(it / 1073741824.0) } ?: "–"
|
||||
|
||||
private fun tempText(c: Int, fahrenheit: Boolean): String = if (fahrenheit) "${(c * 9f / 5f + 32f).roundToInt()}°F" else "$c°C"
|
||||
|
||||
@Composable
|
||||
private fun PanelStrip(d: StripData) {
|
||||
val pal = LocalPalette.current
|
||||
|
||||
@@ -38,6 +38,8 @@ import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.geometry.Rect
|
||||
import androidx.compose.ui.layout.boundsInParent
|
||||
import androidx.compose.ui.layout.layout
|
||||
import androidx.compose.ui.unit.Constraints
|
||||
import androidx.compose.ui.layout.onPlaced
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
@@ -166,11 +168,17 @@ internal fun SteamHome(s: FrontEndState, a: FrontEndActions, modifier: Modifier)
|
||||
)
|
||||
}
|
||||
|
||||
internal fun Modifier.overhang(by: androidx.compose.ui.unit.Dp = 4.dp): Modifier = layout { measurable, constraints ->
|
||||
val e = by.roundToPx()
|
||||
val placeable = measurable.measure(Constraints.fixed(constraints.maxWidth + e * 2, constraints.maxHeight + e * 2))
|
||||
layout(constraints.maxWidth, constraints.maxHeight) { placeable.place(-e, -e) }
|
||||
}
|
||||
|
||||
@Composable
|
||||
internal fun WallFade() {
|
||||
val colors = MaterialTheme.colorScheme
|
||||
Box(
|
||||
Modifier.fillMaxSize().background(
|
||||
Modifier.fillMaxSize().overhang().background(
|
||||
Brush.horizontalGradient(
|
||||
0f to colors.background.copy(alpha = 0.97f),
|
||||
0.34f to colors.background.copy(alpha = 0.86f),
|
||||
@@ -179,7 +187,7 @@ internal fun WallFade() {
|
||||
),
|
||||
),
|
||||
)
|
||||
Box(Modifier.fillMaxSize().background(Brush.verticalGradient(0.55f to Color.Transparent, 1f to colors.background.copy(alpha = 0.9f))))
|
||||
Box(Modifier.fillMaxSize().overhang().background(Brush.verticalGradient(0.55f to Color.Transparent, 1f to colors.background.copy(alpha = 0.9f))))
|
||||
}
|
||||
|
||||
@Composable
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
package com.droiddeck.launcher.ui
|
||||
|
||||
internal fun fitByPriority(widths: IntArray, priorities: IntArray, spacing: Int, maxWidth: Int): BooleanArray {
|
||||
val keep = BooleanArray(widths.size)
|
||||
var used = 0
|
||||
var count = 0
|
||||
for (i in widths.indices.sortedWith(compareBy({ priorities[it] }, { it }))) {
|
||||
val add = widths[i] + if (count > 0) spacing else 0
|
||||
if (used + add > maxWidth) break
|
||||
keep[i] = true
|
||||
used += add
|
||||
count++
|
||||
}
|
||||
return keep
|
||||
}
|
||||
@@ -318,8 +318,8 @@
|
||||
<string name="mode_background_downloads_hint">تأخير التعليق في الخلفية أثناء قيام Steam بالتنزيل.</string>
|
||||
<string name="mode_suspend_note">«تلقائي» يوقف مؤقتًا في الخلفية ويستأنف عند الظهور. «يدوي» ينتظر الضغط على «استئناف». «أبدًا» يُبقيه قيد التشغيل.</string>
|
||||
<string name="mode_startup">بدء التشغيل</string>
|
||||
<string name="mode_steam_startup">تشغيل Steam مع DroidDeck</string>
|
||||
<string name="mode_steam_startup_hint">يفتح جلسة Steam عند تشغيل DroidDeck.</string>
|
||||
<string name="mode_startup_session">الفتح عند التشغيل</string>
|
||||
<string name="mode_startup_session_hint">يبدأ هذه الجلسة عند تشغيل DroidDeck.</string>
|
||||
<string name="mode_decky">Decky</string>
|
||||
<string name="mode_decky_checking">جارٍ البحث عن إصدارات…</string>
|
||||
<string name="mode_decky_ready">جاهز لتثبيت %1$s.</string>
|
||||
@@ -893,13 +893,9 @@
|
||||
<string name="setup_fullscreen">ملء الشاشة</string>
|
||||
<string name="setup_fullscreen_on">يخفي شريطي الحالة والتنقّل في Android</string>
|
||||
<string name="setup_fullscreen_off">يعرض شريطي الحالة والتنقّل في Android</string>
|
||||
<string name="setup_dual_screen">الشاشة المزدوجة</string>
|
||||
<string name="setup_dual_screen_hint">يستخدم الوضع التلقائي الشاشة الثانية في الأجهزة المحمولة التي تحتوي على شاشة مدمجة. يستخدم وضع التشغيل أي شاشة ثانية. يُبقي وضع الإيقاف كل شيء على شاشة واحدة.</string>
|
||||
<string name="setup_dual_screen_auto">تلقائي</string>
|
||||
<string name="setup_dual_screen_on">تشغيل</string>
|
||||
<string name="setup_dual_screen_off">إيقاف</string>
|
||||
<string name="setup_dual_screen_menu">قائمة الشاشة السفلية</string>
|
||||
<string name="setup_dual_screen_menu_hint">ينقل الأقسام وقائمة اللعبة إلى الشاشة الثانية ويتم الكتابة هناك، لتبقى الشاشة العلوية بعرضها الكامل. يبدّل L2 وR2 بين الأقسام.</string>
|
||||
<string name="setup_dual_screen_controls">عناصر تحكم الشاشة السفلية في الألعاب</string>
|
||||
<string name="setup_dual_screen_controls_hint">يفتح لوحة اللمس ولوحة المفاتيح على الشاشة الثانية عند بدء الجلسة.</string>
|
||||
<string name="dual_typing_title">الكتابة على الشاشة العلوية</string>
|
||||
@@ -1890,8 +1886,6 @@
|
||||
<string name="stats_trace">دواسة الوقود والمكابح، آخر 10 s</string>
|
||||
<string name="stats_speed_chart">السرعة، %1$s</string>
|
||||
<string name="stats_gear">الترس</string>
|
||||
<string name="stats_speed_units">وحدات السرعة</string>
|
||||
<string name="stats_speed_units_auto">تلقائي (%1$s)</string>
|
||||
<string name="stats_steam_client_arm">عميل Steam ARM</string>
|
||||
<string name="stats_steam_client_x86">عميل Steam x86</string>
|
||||
<string name="stats_throttle">دواسة الوقود</string>
|
||||
@@ -1909,7 +1903,6 @@
|
||||
<string name="stats_fuel_t">الوقود، t</string>
|
||||
<string name="stats_fuel_main">الرئيسي %1$s t</string>
|
||||
<string name="stats_fuel_reserve">الاحتياطي %1$s t</string>
|
||||
<string name="stats_tap_hint">انقر على الشريط العلوي أو لوحة العدادات لتغيير نمطها.</string>
|
||||
<string name="bottom_telemetry_send_title">إرسال بيانات لوحة العدادات</string>
|
||||
<string name="bottom_telemetry_send_auto">تلقائي</string>
|
||||
<string name="bottom_telemetry_send_auto_hint">يفعّلها لكل لعبة مدرجة هنا، بما في ذلك الألعاب التي يُعثر عليها لاحقًا. تبقى الألعاب التي توقفها متوقفة.</string>
|
||||
@@ -1948,7 +1941,6 @@
|
||||
<string name="bottom_telemetry_balance">الرصيد</string>
|
||||
<string name="bottom_telemetry_war_thunder">War Thunder</string>
|
||||
<string name="bottom_telemetry_elite">Elite Dangerous</string>
|
||||
<string name="bottom_telemetry_help">فعِّل إخراج البيانات في لعبة السباق (يُسمّى غالبًا القياس عن بُعد أو UDP) لرؤية لوحة عدادات حية هنا. تبقى البيانات على هذا الجهاز.</string>
|
||||
<string name="bottom_telemetry_unknown_format">يستقبل المنفذ %1$d بيانات (%2$d حزمة بحجم %3$d بايت)، لكن بتنسيق لا تستطيع هذه الصفحة قراءته بعد.</string>
|
||||
<string name="bottom_telemetry_port_busy">يستخدم برنامج آخر المنفذ %1$s بالفعل، لذا لا يمكن عرض تلك اللعبة هنا.</string>
|
||||
|
||||
@@ -3785,4 +3777,26 @@
|
||||
<string name="second_mode_desktop">سطح المكتب</string>
|
||||
<string name="bottom_desktop_mode_trackpad">لوحة اللمس</string>
|
||||
<string name="bottom_desktop_title">سطح المكتب · %1$s</string>
|
||||
<string name="setup_dual_screen_launcher">المشغّل على شاشتين</string>
|
||||
<string name="setup_dual_screen_launcher_hint">يضع تلقائي أقسام المشغّل (Steam والمكتبة والمتاجر وغيرها) على الشاشة الثانية المدمجة. يستخدم تشغيل أي شاشة ثانية. يبقي إيقاف المشغّل على شاشة واحدة.</string>
|
||||
<string name="setup_dual_screen_sessions">الجلسات على شاشتين</string>
|
||||
<string name="setup_dual_screen_sessions_hint">يضع تلقائي قائمة اللعبة على الشاشة الثانية المدمجة بدل تراكبها. يستخدم تشغيل أي شاشة ثانية. يبقي إيقاف القائمة على شاشة اللعبة؛ ويمكنك اختيار شاشة ثانية منها.</string>
|
||||
<string name="stats_hud_frametime">زمن الإطار</string>
|
||||
<string name="stats_hud_last_seconds">آخر %1$d ث</string>
|
||||
<string name="stats_hud_avg_frame">متوسط الإطار</string>
|
||||
<string name="stats_hud_low" formatted="false">أدنى 1 %</string>
|
||||
<string name="stats_hud_slow_frames">إطارات تتجاوز 33 مللي ثانية</string>
|
||||
<string name="stats_hud_per_minute">%1$d / دقيقة</string>
|
||||
<string name="stats_hud_playing_for">وقت اللعب: %1$s</string>
|
||||
<string name="stats_hud_core_group">%1$d أنوية، حتى %2$s غيغاهرتز</string>
|
||||
<string name="stats_hud_core_group_unknown">%1$d أنوية</string>
|
||||
<string name="stats_hud_fps_of">إطارات في الثانية عند %1$d هرتز</string>
|
||||
<string name="stats_hud_red_mark">تشير العلامة الحمراء إلى الموضع الذي تعرض فيه لوحة المعلومات درجة الحرارة بالأحمر.</string>
|
||||
<string name="stats_hud_whole_device">الجهاز بالكامل</string>
|
||||
<string name="bottom_hud_group">لوحة معلومات الشاشة السفلية</string>
|
||||
<string name="bottom_strip_tabs">شريط في كل تبويب</string>
|
||||
<string name="bottom_strip_lows" formatted="false">أدنى 1 % و0.1 %</string>
|
||||
<string name="bottom_strip_cpu_temp">درجة حرارة المعالج</string>
|
||||
<string name="stats_hud_battery_temp">البطارية</string>
|
||||
<string name="stats_hud_game_cores">لعبة</string>
|
||||
</resources>
|
||||
@@ -318,8 +318,8 @@
|
||||
<string name="mode_background_downloads_hint">Retrasa la suspensión en segundo plano mientras Steam esté descargando.</string>
|
||||
<string name="mode_suspend_note">Auto pausa en segundo plano y reanuda al mostrarse. Manual espera a Reanudar. Nunca sigue ejecutándose.</string>
|
||||
<string name="mode_startup">Inicio</string>
|
||||
<string name="mode_steam_startup">Iniciar Steam con DroidDeck</string>
|
||||
<string name="mode_steam_startup_hint">Abre la sesión de Steam al iniciar DroidDeck.</string>
|
||||
<string name="mode_startup_session">Abrir al iniciar</string>
|
||||
<string name="mode_startup_session_hint">Inicia esta sesión cuando se abre DroidDeck.</string>
|
||||
<string name="mode_decky">Decky</string>
|
||||
<string name="mode_decky_checking">Buscando versiones…</string>
|
||||
<string name="mode_decky_ready">Listo para instalar %1$s.</string>
|
||||
@@ -911,13 +911,9 @@
|
||||
<string name="setup_fullscreen">Pantalla completa</string>
|
||||
<string name="setup_fullscreen_on">Oculta las barras de estado y navegación de Android</string>
|
||||
<string name="setup_fullscreen_off">Muestra las barras de estado y navegación de Android</string>
|
||||
<string name="setup_dual_screen">Pantalla dual</string>
|
||||
<string name="setup_dual_screen_hint">Automático usa la segunda pantalla en consolas portátiles que la traen integrada. Activado usa cualquier segunda pantalla. Desactivado deja todo en una sola pantalla.</string>
|
||||
<string name="setup_dual_screen_auto">Automático</string>
|
||||
<string name="setup_dual_screen_on">Activado</string>
|
||||
<string name="setup_dual_screen_off">Desactivado</string>
|
||||
<string name="setup_dual_screen_menu">Menú en la pantalla inferior</string>
|
||||
<string name="setup_dual_screen_menu_hint">Mueve las secciones y el menú del juego a la segunda pantalla y escribe ahí, para que la pantalla superior conserve la vista completa. L2 y R2 cambian de sección.</string>
|
||||
<string name="setup_dual_screen_controls">Controles en la pantalla inferior en juegos</string>
|
||||
<string name="setup_dual_screen_controls_hint">Abre el trackpad y el teclado en la segunda pantalla cuando empieza una sesión.</string>
|
||||
<string name="dual_typing_title">Escribiendo en la pantalla superior</string>
|
||||
@@ -1888,8 +1884,6 @@
|
||||
<string name="stats_trace">Acelerador y freno, últimos 10 s</string>
|
||||
<string name="stats_speed_chart">Velocidad, %1$s</string>
|
||||
<string name="stats_gear">Marcha</string>
|
||||
<string name="stats_speed_units">Unidades de velocidad</string>
|
||||
<string name="stats_speed_units_auto">Automático (%1$s)</string>
|
||||
<string name="stats_steam_client_arm">Cliente de Steam ARM</string>
|
||||
<string name="stats_steam_client_x86">Cliente de Steam x86</string>
|
||||
<string name="stats_throttle">Acelerador</string>
|
||||
@@ -1907,7 +1901,6 @@
|
||||
<string name="stats_fuel_t">Combustible, t</string>
|
||||
<string name="stats_fuel_main">Principal %1$s t</string>
|
||||
<string name="stats_fuel_reserve">Reserva %1$s t</string>
|
||||
<string name="stats_tap_hint">Toca la franja superior o el tablero para cambiar su estilo.</string>
|
||||
<string name="bottom_telemetry_send_title">Enviar datos al panel</string>
|
||||
<string name="bottom_telemetry_send_auto">Automático</string>
|
||||
<string name="bottom_telemetry_send_auto_hint">Lo activa en todos los juegos de esta lista, incluidos los que se encuentren más adelante. Los juegos que desactives seguirán desactivados.</string>
|
||||
@@ -1946,7 +1939,6 @@
|
||||
<string name="bottom_telemetry_balance">Saldo</string>
|
||||
<string name="bottom_telemetry_war_thunder">War Thunder</string>
|
||||
<string name="bottom_telemetry_elite">Elite Dangerous</string>
|
||||
<string name="bottom_telemetry_help">Activa la salida de datos de un juego de carreras (a menudo llamada telemetría o UDP) para ver aquí un panel en vivo. Los datos no salen de este dispositivo.</string>
|
||||
<string name="bottom_telemetry_unknown_format">El puerto %1$d recibe datos (%2$d paquetes de %3$d bytes), pero en un formato que esta página aún no puede leer.</string>
|
||||
<string name="bottom_telemetry_port_busy">Otro programa ya usa el puerto %1$s, así que ese juego no se puede mostrar aquí.</string>
|
||||
<string name="rtinst_removing">Eliminando el runtime de Linux</string>
|
||||
@@ -3647,4 +3639,26 @@
|
||||
<string name="second_mode_desktop">Escritorio</string>
|
||||
<string name="bottom_desktop_mode_trackpad">Trackpad</string>
|
||||
<string name="bottom_desktop_title">Escritorio · %1$s</string>
|
||||
<string name="setup_dual_screen_launcher">Lanzador en dos pantallas</string>
|
||||
<string name="setup_dual_screen_launcher_hint">Automático coloca las secciones del lanzador (Steam, Biblioteca, Tiendas y otras) en la segunda pantalla integrada. Activado usa cualquier segunda pantalla. Desactivado mantiene el lanzador en una sola pantalla.</string>
|
||||
<string name="setup_dual_screen_sessions">Sesiones en dos pantallas</string>
|
||||
<string name="setup_dual_screen_sessions_hint">Automático coloca el menú del juego en la segunda pantalla integrada en lugar de superponerlo. Activado usa cualquier segunda pantalla. Desactivado mantiene el menú en la pantalla del juego; aún puedes elegir una segunda pantalla desde él.</string>
|
||||
<string name="stats_hud_frametime">Tiempo de fotograma</string>
|
||||
<string name="stats_hud_last_seconds">últimos %1$d s</string>
|
||||
<string name="stats_hud_avg_frame">fotograma medio</string>
|
||||
<string name="stats_hud_low" formatted="false">1 % más bajo</string>
|
||||
<string name="stats_hud_slow_frames">fotogramas de más de 33 ms</string>
|
||||
<string name="stats_hud_per_minute">%1$d / min</string>
|
||||
<string name="stats_hud_playing_for">Tiempo de juego: %1$s</string>
|
||||
<string name="stats_hud_core_group">%1$d núcleos, hasta %2$s GHz</string>
|
||||
<string name="stats_hud_core_group_unknown">%1$d núcleos</string>
|
||||
<string name="stats_hud_fps_of">FPS de %1$d Hz</string>
|
||||
<string name="stats_hud_red_mark">La marca roja indica dónde el HUD muestra la temperatura en rojo.</string>
|
||||
<string name="stats_hud_whole_device">todo el dispositivo</string>
|
||||
<string name="bottom_hud_group">HUD de la pantalla inferior</string>
|
||||
<string name="bottom_strip_tabs">Franja en cada pestaña</string>
|
||||
<string name="bottom_strip_lows" formatted="false">1 % y 0,1 % más bajos</string>
|
||||
<string name="bottom_strip_cpu_temp">Temperatura de CPU</string>
|
||||
<string name="stats_hud_battery_temp">Batería</string>
|
||||
<string name="stats_hud_game_cores">Juego</string>
|
||||
</resources>
|
||||
@@ -318,8 +318,8 @@
|
||||
<string name="mode_background_downloads_hint">Отлага фоновото спиране, докато Steam активно изтегля.</string>
|
||||
<string name="mode_suspend_note">Автоматично поставя на пауза във фонов режим и продължава при показване. Ръчно изчаква „Продължи“. Никога продължава да работи.</string>
|
||||
<string name="mode_startup">Стартиране</string>
|
||||
<string name="mode_steam_startup">Стартиране на Steam заедно с DroidDeck</string>
|
||||
<string name="mode_steam_startup_hint">Отваря сесията на Steam при стартиране на DroidDeck.</string>
|
||||
<string name="mode_startup_session">Отваряне при стартиране</string>
|
||||
<string name="mode_startup_session_hint">Стартира тази сесия, когато DroidDeck се стартира.</string>
|
||||
<string name="mode_decky">Decky</string>
|
||||
<string name="mode_decky_checking">Проверка за издания…</string>
|
||||
<string name="mode_decky_ready">Готово за инсталиране на %1$s.</string>
|
||||
@@ -881,13 +881,9 @@
|
||||
<string name="setup_fullscreen">Цял екран</string>
|
||||
<string name="setup_fullscreen_on">Скрива лентите за състояние и навигация на Android</string>
|
||||
<string name="setup_fullscreen_off">Показва лентите за състояние и навигация на Android</string>
|
||||
<string name="setup_dual_screen">Двоен екран</string>
|
||||
<string name="setup_dual_screen_hint">Автоматично използва втория екран на преносими устройства с вграден такъв. Вкл. използва всеки втори дисплей. Изкл. държи всичко на един екран.</string>
|
||||
<string name="setup_dual_screen_auto">Автоматично</string>
|
||||
<string name="setup_dual_screen_on">Вкл.</string>
|
||||
<string name="setup_dual_screen_off">Изкл.</string>
|
||||
<string name="setup_dual_screen_menu">Меню на долния екран</string>
|
||||
<string name="setup_dual_screen_menu_hint">Премества секциите и менюто по време на игра на втория екран и въвеждането става там, така горният екран остава с пълен изглед. L2 и R2 сменят секциите.</string>
|
||||
<string name="setup_dual_screen_controls">Контроли на долния екран в игрите</string>
|
||||
<string name="setup_dual_screen_controls_hint">Отваря тракпада и клавиатурата на втория екран при стартиране на сесия.</string>
|
||||
<string name="dual_typing_title">Въвеждане за горния екран</string>
|
||||
@@ -1802,8 +1798,6 @@
|
||||
<string name="stats_trace">Газ и спирачка, последните 10 s</string>
|
||||
<string name="stats_speed_chart">Скорост, %1$s</string>
|
||||
<string name="stats_gear">Предавка</string>
|
||||
<string name="stats_speed_units">Единици за скорост</string>
|
||||
<string name="stats_speed_units_auto">Автоматично (%1$s)</string>
|
||||
<string name="stats_steam_client_arm">Steam клиент ARM</string>
|
||||
<string name="stats_steam_client_x86">Steam клиент x86</string>
|
||||
<string name="stats_throttle">Газ</string>
|
||||
@@ -1821,7 +1815,6 @@
|
||||
<string name="stats_fuel_t">Гориво, t</string>
|
||||
<string name="stats_fuel_main">Основно %1$s t</string>
|
||||
<string name="stats_fuel_reserve">Резерв %1$s t</string>
|
||||
<string name="stats_tap_hint">Докоснете горната лента или таблото, за да смените стила му.</string>
|
||||
<string name="bottom_telemetry_send_title">Изпращане на данни за таблото</string>
|
||||
<string name="bottom_telemetry_send_auto">Автоматично</string>
|
||||
<string name="bottom_telemetry_send_auto_hint">Включва го за всяка игра от списъка тук, включително игри, открити по-късно. Игрите, които изключите, остават изключени.</string>
|
||||
@@ -1860,7 +1853,6 @@
|
||||
<string name="bottom_telemetry_balance">Баланс</string>
|
||||
<string name="bottom_telemetry_war_thunder">War Thunder</string>
|
||||
<string name="bottom_telemetry_elite">Elite Dangerous</string>
|
||||
<string name="bottom_telemetry_help">Включете извеждането на данни в състезателна игра (често наричано телеметрия или UDP), за да виждате тук табло на живо. Данните остават на това устройство.</string>
|
||||
<string name="bottom_telemetry_unknown_format">Порт %1$d получава данни (%2$d пакета по %3$d байта), но във формат, който тази страница все още не може да чете.</string>
|
||||
<string name="bottom_telemetry_port_busy">Друга програма вече използва порт %1$s, затова тази игра не може да се показва тук.</string>
|
||||
|
||||
@@ -3637,4 +3629,26 @@
|
||||
<string name="second_mode_desktop">Работен плот</string>
|
||||
<string name="bottom_desktop_mode_trackpad">Тракпад</string>
|
||||
<string name="bottom_desktop_title">Работен плот · %1$s</string>
|
||||
<string name="setup_dual_screen_launcher">Стартер на два екрана</string>
|
||||
<string name="setup_dual_screen_launcher_hint">Автоматично поставя разделите на стартера (Steam, Библиотека, Магазини и други) на вградения втори екран. Включено използва всеки втори екран. Изключено оставя стартера на един екран.</string>
|
||||
<string name="setup_dual_screen_sessions">Сесии на два екрана</string>
|
||||
<string name="setup_dual_screen_sessions_hint">Автоматично поставя менюто на играта на вградения втори екран вместо като наслагване. Включено използва всеки втори екран. Изключено оставя менюто на екрана на играта; от него все още може да изберете втори екран.</string>
|
||||
<string name="stats_hud_frametime">Време за кадър</string>
|
||||
<string name="stats_hud_last_seconds">последни %1$d сек</string>
|
||||
<string name="stats_hud_avg_frame">среден кадър</string>
|
||||
<string name="stats_hud_low" formatted="false">най-ниски 1 %</string>
|
||||
<string name="stats_hud_slow_frames">кадри над 33 мс</string>
|
||||
<string name="stats_hud_per_minute">%1$d / мин</string>
|
||||
<string name="stats_hud_playing_for">Време в игра: %1$s</string>
|
||||
<string name="stats_hud_core_group">%1$d ядра, до %2$s GHz</string>
|
||||
<string name="stats_hud_core_group_unknown">%1$d ядра</string>
|
||||
<string name="stats_hud_fps_of">FPS при %1$d Hz</string>
|
||||
<string name="stats_hud_red_mark">Червеният маркер показва къде HUD изобразява температурата в червено.</string>
|
||||
<string name="stats_hud_whole_device">цялото устройство</string>
|
||||
<string name="bottom_hud_group">HUD на долния екран</string>
|
||||
<string name="bottom_strip_tabs">Лента във всеки раздел</string>
|
||||
<string name="bottom_strip_lows" formatted="false">най-ниски 1 % и 0,1 %</string>
|
||||
<string name="bottom_strip_cpu_temp">Температура на CPU</string>
|
||||
<string name="stats_hud_battery_temp">Батерия</string>
|
||||
<string name="stats_hud_game_cores">Игра</string>
|
||||
</resources>
|
||||
@@ -318,8 +318,8 @@
|
||||
<string name="mode_background_downloads_hint">Oddálí pozastavení na pozadí, pokud Steam aktivně stahuje.</string>
|
||||
<string name="mode_suspend_note">Automaticky pozastaví na pozadí a obnoví při zobrazení. Manuálně čeká na Obnovit. Nikdy běh nezastavuje.</string>
|
||||
<string name="mode_startup">Spouštění</string>
|
||||
<string name="mode_steam_startup">Spustit Steam s DroidDeck</string>
|
||||
<string name="mode_steam_startup_hint">Otevře relaci Steamu při spuštění DroidDeck.</string>
|
||||
<string name="mode_startup_session">Otevřít při spuštění</string>
|
||||
<string name="mode_startup_session_hint">Spustí tuto relaci při spuštění DroidDeck.</string>
|
||||
<string name="mode_decky">Decky</string>
|
||||
<string name="mode_decky_checking">Hledání vydání…</string>
|
||||
<string name="mode_decky_ready">Připraveno k instalaci %1$s.</string>
|
||||
@@ -887,13 +887,9 @@
|
||||
<string name="setup_fullscreen">Celá obrazovka</string>
|
||||
<string name="setup_fullscreen_on">Skryje stavový a navigační panel Androidu</string>
|
||||
<string name="setup_fullscreen_off">Zobrazí stavový a navigační panel Androidu</string>
|
||||
<string name="setup_dual_screen">Dvě obrazovky</string>
|
||||
<string name="setup_dual_screen_hint">Automaticky používá druhou obrazovku na kapesních zařízeních, která ji mají vestavěnou. Zapnuto používá jakýkoli druhý displej. Vypnuto nechá vše na jedné obrazovce.</string>
|
||||
<string name="setup_dual_screen_auto">Automaticky</string>
|
||||
<string name="setup_dual_screen_on">Zapnuto</string>
|
||||
<string name="setup_dual_screen_off">Vypnuto</string>
|
||||
<string name="setup_dual_screen_menu">Nabídka na spodní obrazovce</string>
|
||||
<string name="setup_dual_screen_menu_hint">Přesune sekce a herní nabídku na druhou obrazovku a píše se tam, takže horní obrazovka zůstane celá volná. L2 a R2 přepínají sekce.</string>
|
||||
<string name="setup_dual_screen_controls">Ovládání na spodní obrazovce ve hrách</string>
|
||||
<string name="setup_dual_screen_controls_hint">Při spuštění relace otevře touchpad a klávesnici na druhé obrazovce.</string>
|
||||
<string name="dual_typing_title">Psaní pro horní obrazovku</string>
|
||||
@@ -1846,8 +1842,6 @@
|
||||
<string name="stats_trace">Plyn a brzda, posledních 10 s</string>
|
||||
<string name="stats_speed_chart">Rychlost, %1$s</string>
|
||||
<string name="stats_gear">Rychlostní stupeň</string>
|
||||
<string name="stats_speed_units">Jednotky rychlosti</string>
|
||||
<string name="stats_speed_units_auto">Automaticky (%1$s)</string>
|
||||
<string name="stats_steam_client_arm">Klient Steam ARM</string>
|
||||
<string name="stats_steam_client_x86">Klient Steam x86</string>
|
||||
<string name="stats_throttle">Plyn</string>
|
||||
@@ -1865,7 +1859,6 @@
|
||||
<string name="stats_fuel_t">Palivo, t</string>
|
||||
<string name="stats_fuel_main">Hlavní %1$s t</string>
|
||||
<string name="stats_fuel_reserve">Rezerva %1$s t</string>
|
||||
<string name="stats_tap_hint">Klepnutím na horní pruh nebo palubní desku změníte jejich styl.</string>
|
||||
<string name="bottom_telemetry_send_title">Odesílat data pro palubní desku</string>
|
||||
<string name="bottom_telemetry_send_auto">Automaticky</string>
|
||||
<string name="bottom_telemetry_send_auto_hint">Zapne to pro každou zde uvedenou hru, včetně her nalezených později. Hry, které vypnete, zůstanou vypnuté.</string>
|
||||
@@ -1904,7 +1897,6 @@
|
||||
<string name="bottom_telemetry_balance">Zůstatek</string>
|
||||
<string name="bottom_telemetry_war_thunder">War Thunder</string>
|
||||
<string name="bottom_telemetry_elite">Elite Dangerous</string>
|
||||
<string name="bottom_telemetry_help">Zapněte v závodní hře výstup dat (často nazývaný telemetrie nebo UDP) a uvidíte zde živou palubní desku. Data zůstávají v tomto zařízení.</string>
|
||||
<string name="bottom_telemetry_unknown_format">Port %1$d přijímá data (%2$d paketů po %3$d bajtech), ale ve formátu, který tato stránka zatím neumí přečíst.</string>
|
||||
<string name="bottom_telemetry_port_busy">Port %1$s již používá jiný program, takže se zde tato hra nemůže zobrazit.</string>
|
||||
|
||||
@@ -3711,4 +3703,26 @@
|
||||
<string name="second_mode_desktop">Plocha</string>
|
||||
<string name="bottom_desktop_mode_trackpad">Trackpad</string>
|
||||
<string name="bottom_desktop_title">Plocha · %1$s</string>
|
||||
<string name="setup_dual_screen_launcher">Spouštěč na dvou obrazovkách</string>
|
||||
<string name="setup_dual_screen_launcher_hint">Automaticky umístí části spouštěče (Steam, Knihovna, Obchody a další) na vestavěnou druhou obrazovku. Zapnuto použije libovolnou druhou obrazovku. Vypnuto ponechá spouštěč na jedné obrazovce.</string>
|
||||
<string name="setup_dual_screen_sessions">Relace na dvou obrazovkách</string>
|
||||
<string name="setup_dual_screen_sessions_hint">Automaticky umístí herní nabídku na vestavěnou druhou obrazovku místo překrytí. Zapnuto použije libovolnou druhou obrazovku. Vypnuto ponechá nabídku na herní obrazovce; stále z ní můžete vybrat druhou obrazovku.</string>
|
||||
<string name="stats_hud_frametime">Doba snímku</string>
|
||||
<string name="stats_hud_last_seconds">posledních %1$d s</string>
|
||||
<string name="stats_hud_avg_frame">průměrný snímek</string>
|
||||
<string name="stats_hud_low" formatted="false">nejnižší 1 %</string>
|
||||
<string name="stats_hud_slow_frames">snímky nad 33 ms</string>
|
||||
<string name="stats_hud_per_minute">%1$d / min</string>
|
||||
<string name="stats_hud_playing_for">Doba hraní: %1$s</string>
|
||||
<string name="stats_hud_core_group">%1$d jader, až %2$s GHz</string>
|
||||
<string name="stats_hud_core_group_unknown">%1$d jader</string>
|
||||
<string name="stats_hud_fps_of">FPS při %1$d Hz</string>
|
||||
<string name="stats_hud_red_mark">Červená značka ukazuje, kde HUD zobrazí teplotu červeně.</string>
|
||||
<string name="stats_hud_whole_device">celé zařízení</string>
|
||||
<string name="bottom_hud_group">HUD spodní obrazovky</string>
|
||||
<string name="bottom_strip_tabs">Lišta na každé kartě</string>
|
||||
<string name="bottom_strip_lows" formatted="false">nejnižší 1 % a 0,1 %</string>
|
||||
<string name="bottom_strip_cpu_temp">Teplota CPU</string>
|
||||
<string name="stats_hud_battery_temp">Baterie</string>
|
||||
<string name="stats_hud_game_cores">Hra</string>
|
||||
</resources>
|
||||
@@ -318,8 +318,8 @@
|
||||
<string name="mode_background_downloads_hint">Udskyder baggrundspause, mens Steam aktivt downloader.</string>
|
||||
<string name="mode_suspend_note">Auto sætter på pause i baggrunden og genoptager, når den vises. Manuel venter på Genoptag. Aldrig fortsætter med at køre.</string>
|
||||
<string name="mode_startup">Opstart</string>
|
||||
<string name="mode_steam_startup">Start Steam med DroidDeck</string>
|
||||
<string name="mode_steam_startup_hint">Åbner Steam-sessionen, når DroidDeck starter.</string>
|
||||
<string name="mode_startup_session">Åbn ved start</string>
|
||||
<string name="mode_startup_session_hint">Starter denne session, når DroidDeck startes.</string>
|
||||
<string name="mode_decky">Decky</string>
|
||||
<string name="mode_decky_checking">Søger efter udgivelser…</string>
|
||||
<string name="mode_decky_ready">Klar til at installere %1$s.</string>
|
||||
@@ -881,13 +881,9 @@
|
||||
<string name="setup_fullscreen">Fuldskærm</string>
|
||||
<string name="setup_fullscreen_on">Skjuler Androids status- og navigationsbjælker</string>
|
||||
<string name="setup_fullscreen_off">Viser Androids status- og navigationsbjælker</string>
|
||||
<string name="setup_dual_screen">Dobbeltskærm</string>
|
||||
<string name="setup_dual_screen_hint">Automatisk bruger den anden skærm på håndholdte enheder med en indbygget ekstra skærm. Til bruger enhver anden skærm. Fra holder alt på én skærm.</string>
|
||||
<string name="setup_dual_screen_auto">Automatisk</string>
|
||||
<string name="setup_dual_screen_on">Til</string>
|
||||
<string name="setup_dual_screen_off">Fra</string>
|
||||
<string name="setup_dual_screen_menu">Menu på nederste skærm</string>
|
||||
<string name="setup_dual_screen_menu_hint">Flytter sektionerne og menuen i spillet til den anden skærm og skriver der, så den øverste skærm beholder hele visningen. L2 og R2 skifter sektion.</string>
|
||||
<string name="setup_dual_screen_controls">Kontroller på nederste skærm i spil</string>
|
||||
<string name="setup_dual_screen_controls_hint">Åbner pegefeltet og tastaturet på den anden skærm, når en session starter.</string>
|
||||
<string name="dual_typing_title">Skriver til den øverste skærm</string>
|
||||
@@ -1802,8 +1798,6 @@
|
||||
<string name="stats_trace">Speeder og bremse, seneste 10 s</string>
|
||||
<string name="stats_speed_chart">Hastighed, %1$s</string>
|
||||
<string name="stats_gear">Gear</string>
|
||||
<string name="stats_speed_units">Hastighedsenheder</string>
|
||||
<string name="stats_speed_units_auto">Auto (%1$s)</string>
|
||||
<string name="stats_steam_client_arm">Steam-klient ARM</string>
|
||||
<string name="stats_steam_client_x86">Steam-klient x86</string>
|
||||
<string name="stats_throttle">Speeder</string>
|
||||
@@ -1821,7 +1815,6 @@
|
||||
<string name="stats_fuel_t">Brændstof, t</string>
|
||||
<string name="stats_fuel_main">Hoved %1$s t</string>
|
||||
<string name="stats_fuel_reserve">Reserve %1$s t</string>
|
||||
<string name="stats_tap_hint">Tryk på den øverste bjælke eller instrumentbrættet for at skifte stil.</string>
|
||||
<string name="bottom_telemetry_send_title">Send instrumentbrætdata</string>
|
||||
<string name="bottom_telemetry_send_auto">Auto</string>
|
||||
<string name="bottom_telemetry_send_auto_hint">Slår det til for alle spil på listen her, også spil, der findes senere. Spil, du slår fra, forbliver slået fra.</string>
|
||||
@@ -1860,7 +1853,6 @@
|
||||
<string name="bottom_telemetry_balance">Saldo</string>
|
||||
<string name="bottom_telemetry_war_thunder">War Thunder</string>
|
||||
<string name="bottom_telemetry_elite">Elite Dangerous</string>
|
||||
<string name="bottom_telemetry_help">Slå dataoutput til i et racerspil (ofte kaldet telemetri eller UDP) for at se et live-instrumentbræt her. Det forbliver på denne enhed.</string>
|
||||
<string name="bottom_telemetry_unknown_format">Port %1$d modtager data (%2$d pakker á %3$d bytes), men i et format, som denne side endnu ikke kan læse.</string>
|
||||
<string name="bottom_telemetry_port_busy">Et andet program bruger allerede port %1$s, så det spil kan ikke vises her.</string>
|
||||
|
||||
@@ -3637,4 +3629,26 @@
|
||||
<string name="second_mode_desktop">Skrivebord</string>
|
||||
<string name="bottom_desktop_mode_trackpad">Trackpad</string>
|
||||
<string name="bottom_desktop_title">Skrivebord · %1$s</string>
|
||||
<string name="setup_dual_screen_launcher">Starter på to skærme</string>
|
||||
<string name="setup_dual_screen_launcher_hint">Automatisk viser starterens sektioner (Steam, Bibliotek, Butikker og andre) på den indbyggede anden skærm. Til bruger enhver anden skærm. Fra holder starteren på én skærm.</string>
|
||||
<string name="setup_dual_screen_sessions">Sessioner på to skærme</string>
|
||||
<string name="setup_dual_screen_sessions_hint">Automatisk viser spilmenuen på den indbyggede anden skærm i stedet for som overlejring. Til bruger enhver anden skærm. Fra holder menuen på spilskærmen; du kan stadig vælge en anden skærm fra den.</string>
|
||||
<string name="stats_hud_frametime">Billedtid</string>
|
||||
<string name="stats_hud_last_seconds">seneste %1$d s</string>
|
||||
<string name="stats_hud_avg_frame">gennemsnitligt billede</string>
|
||||
<string name="stats_hud_low" formatted="false">laveste 1 %</string>
|
||||
<string name="stats_hud_slow_frames">billeder over 33 ms</string>
|
||||
<string name="stats_hud_per_minute">%1$d / min</string>
|
||||
<string name="stats_hud_playing_for">Spilletid: %1$s</string>
|
||||
<string name="stats_hud_core_group">%1$d kerner, op til %2$s GHz</string>
|
||||
<string name="stats_hud_core_group_unknown">%1$d kerner</string>
|
||||
<string name="stats_hud_fps_of">FPS ved %1$d Hz</string>
|
||||
<string name="stats_hud_red_mark">Det røde mærke viser, hvor HUD viser temperaturen med rødt.</string>
|
||||
<string name="stats_hud_whole_device">hele enheden</string>
|
||||
<string name="bottom_hud_group">HUD på nederste skærm</string>
|
||||
<string name="bottom_strip_tabs">Stribe på hver fane</string>
|
||||
<string name="bottom_strip_lows" formatted="false">laveste 1 % og 0,1 %</string>
|
||||
<string name="bottom_strip_cpu_temp">CPU-temperatur</string>
|
||||
<string name="stats_hud_battery_temp">Batteri</string>
|
||||
<string name="stats_hud_game_cores">Spil</string>
|
||||
</resources>
|
||||
@@ -318,8 +318,8 @@
|
||||
<string name="mode_background_downloads_hint">Verzögert das Anhalten im Hintergrund, solange Steam aktiv herunterlädt.</string>
|
||||
<string name="mode_suspend_note">Auto pausiert im Hintergrund und setzt fort, sobald die App wieder sichtbar ist. Manuell wartet auf „Fortsetzen“. Nie lässt alles weiterlaufen.</string>
|
||||
<string name="mode_startup">Start</string>
|
||||
<string name="mode_steam_startup">Steam mit DroidDeck starten</string>
|
||||
<string name="mode_steam_startup_hint">Öffnet die Steam-Sitzung, wenn DroidDeck startet.</string>
|
||||
<string name="mode_startup_session">Beim Start öffnen</string>
|
||||
<string name="mode_startup_session_hint">Startet diese Sitzung, wenn DroidDeck startet.</string>
|
||||
<string name="mode_decky">Decky</string>
|
||||
<string name="mode_decky_checking">Suche nach Releases…</string>
|
||||
<string name="mode_decky_ready">Bereit zur Installation von %1$s.</string>
|
||||
@@ -873,13 +873,9 @@
|
||||
<string name="setup_fullscreen">Vollbild</string>
|
||||
<string name="setup_fullscreen_on">Blendet die Status- und Navigationsleiste von Android aus</string>
|
||||
<string name="setup_fullscreen_off">Zeigt die Status- und Navigationsleiste von Android an</string>
|
||||
<string name="setup_dual_screen">Zwei Bildschirme</string>
|
||||
<string name="setup_dual_screen_hint">Automatisch nutzt den zweiten Bildschirm auf Handhelds mit eingebautem Zweitbildschirm. Ein nutzt einen beliebigen zweiten Bildschirm. Aus lässt alles auf einem Bildschirm.</string>
|
||||
<string name="setup_dual_screen_auto">Automatisch</string>
|
||||
<string name="setup_dual_screen_on">Ein</string>
|
||||
<string name="setup_dual_screen_off">Aus</string>
|
||||
<string name="setup_dual_screen_menu">Menü auf dem unteren Bildschirm</string>
|
||||
<string name="setup_dual_screen_menu_hint">Verschiebt die Bereiche und das Menü im Spiel auf den zweiten Bildschirm und tippt dort, damit der obere Bildschirm die volle Ansicht behält. L2 und R2 wechseln den Bereich.</string>
|
||||
<string name="setup_dual_screen_controls">Steuerung auf dem unteren Bildschirm in Spielen</string>
|
||||
<string name="setup_dual_screen_controls_hint">Öffnet beim Start einer Sitzung Trackpad und Tastatur auf dem zweiten Bildschirm.</string>
|
||||
<string name="dual_typing_title">Eingabe für den oberen Bildschirm</string>
|
||||
@@ -1794,8 +1790,6 @@
|
||||
<string name="stats_trace">Gas und Bremse, letzte 10 s</string>
|
||||
<string name="stats_speed_chart">Geschwindigkeit, %1$s</string>
|
||||
<string name="stats_gear">Gang</string>
|
||||
<string name="stats_speed_units">Geschwindigkeitseinheit</string>
|
||||
<string name="stats_speed_units_auto">Automatisch (%1$s)</string>
|
||||
<string name="stats_steam_client_arm">Steam-Client ARM</string>
|
||||
<string name="stats_steam_client_x86">Steam-Client x86</string>
|
||||
<string name="stats_throttle">Gas</string>
|
||||
@@ -1813,7 +1807,6 @@
|
||||
<string name="stats_fuel_t">Treibstoff, t</string>
|
||||
<string name="stats_fuel_main">Haupttank %1$s t</string>
|
||||
<string name="stats_fuel_reserve">Reserve %1$s t</string>
|
||||
<string name="stats_tap_hint">Tippe auf die obere Leiste oder die Instrumentenanzeige, um ihren Stil zu ändern.</string>
|
||||
<string name="bottom_telemetry_send_title">Dashboard-Daten senden</string>
|
||||
<string name="bottom_telemetry_send_auto">Automatisch</string>
|
||||
<string name="bottom_telemetry_send_auto_hint">Schaltet es für jedes hier aufgeführte Spiel ein, auch für später gefundene Spiele. Spiele, die du ausschaltest, bleiben aus.</string>
|
||||
@@ -1852,7 +1845,6 @@
|
||||
<string name="bottom_telemetry_balance">Guthaben</string>
|
||||
<string name="bottom_telemetry_war_thunder">War Thunder</string>
|
||||
<string name="bottom_telemetry_elite">Elite Dangerous</string>
|
||||
<string name="bottom_telemetry_help">Aktiviere die Datenausgabe eines Rennspiels (oft Telemetrie oder UDP genannt), um hier ein Live-Cockpit zu sehen. Die Daten bleiben auf diesem Gerät.</string>
|
||||
<string name="bottom_telemetry_unknown_format">Port %1$d empfängt Daten (%2$d Pakete mit %3$d Bytes), aber in einem Format, das diese Seite noch nicht lesen kann.</string>
|
||||
<string name="bottom_telemetry_port_busy">Ein anderes Programm verwendet bereits Port %1$s, daher kann dieses Spiel hier nicht angezeigt werden.</string>
|
||||
|
||||
@@ -3637,4 +3629,26 @@
|
||||
<string name="second_mode_desktop">Desktop</string>
|
||||
<string name="bottom_desktop_mode_trackpad">Trackpad</string>
|
||||
<string name="bottom_desktop_title">Desktop · %1$s</string>
|
||||
<string name="setup_dual_screen_launcher">Launcher auf zwei Bildschirmen</string>
|
||||
<string name="setup_dual_screen_launcher_hint">Automatisch zeigt die Launcher-Bereiche (Steam, Bibliothek, Stores und weitere) auf dem eingebauten zweiten Bildschirm. Ein nutzt jeden zweiten Bildschirm. Aus hält den Launcher auf einem Bildschirm.</string>
|
||||
<string name="setup_dual_screen_sessions">Sitzungen auf zwei Bildschirmen</string>
|
||||
<string name="setup_dual_screen_sessions_hint">Automatisch dockt das Spielmenü am eingebauten zweiten Bildschirm statt als Einblendung an. Ein nutzt jeden zweiten Bildschirm. Aus zeigt das Menü auf dem Spielbildschirm; dort kann weiterhin ein zweiter Bildschirm gewählt werden.</string>
|
||||
<string name="stats_hud_frametime">Framezeit</string>
|
||||
<string name="stats_hud_last_seconds">letzte %1$d s</string>
|
||||
<string name="stats_hud_avg_frame">mittlere Framezeit</string>
|
||||
<string name="stats_hud_low" formatted="false">1-%-Tiefstwert</string>
|
||||
<string name="stats_hud_slow_frames">Frames über 33 ms</string>
|
||||
<string name="stats_hud_per_minute">%1$d / Min.</string>
|
||||
<string name="stats_hud_playing_for">Spielzeit: %1$s</string>
|
||||
<string name="stats_hud_core_group">%1$d Kerne, bis %2$s GHz</string>
|
||||
<string name="stats_hud_core_group_unknown">%1$d Kerne</string>
|
||||
<string name="stats_hud_fps_of">FPS bei %1$d Hz</string>
|
||||
<string name="stats_hud_red_mark">An der roten Markierung zeigt das HUD die Temperatur rot an.</string>
|
||||
<string name="stats_hud_whole_device">gesamtes Gerät</string>
|
||||
<string name="bottom_hud_group">HUD auf unterem Bildschirm</string>
|
||||
<string name="bottom_strip_tabs">Leiste auf jedem Tab</string>
|
||||
<string name="bottom_strip_lows" formatted="false">1-%- und 0,1-%-Tiefstwerte</string>
|
||||
<string name="bottom_strip_cpu_temp">CPU-Temperatur</string>
|
||||
<string name="stats_hud_battery_temp">Akku</string>
|
||||
<string name="stats_hud_game_cores">Spiel</string>
|
||||
</resources>
|
||||
@@ -318,8 +318,8 @@
|
||||
<string name="mode_background_downloads_hint">Καθυστερεί την αναστολή παρασκηνίου όσο το Steam κάνει ενεργά λήψη.</string>
|
||||
<string name="mode_suspend_note">Το Αυτόματο θέτει σε παύση στο παρασκήνιο και συνεχίζει όταν εμφανίζεται. Το Μη αυτόματο περιμένει το Συνέχιση. Το Ποτέ συνεχίζει να εκτελείται.</string>
|
||||
<string name="mode_startup">Εκκίνηση</string>
|
||||
<string name="mode_steam_startup">Εκκίνηση του Steam με το DroidDeck</string>
|
||||
<string name="mode_steam_startup_hint">Ανοίγει τη συνεδρία Steam όταν εκκινείται το DroidDeck.</string>
|
||||
<string name="mode_startup_session">Άνοιγμα κατά την εκκίνηση</string>
|
||||
<string name="mode_startup_session_hint">Ξεκινά αυτή τη συνεδρία όταν ανοίγει το DroidDeck.</string>
|
||||
<string name="mode_decky">Decky</string>
|
||||
<string name="mode_decky_checking">Έλεγχος για εκδόσεις…</string>
|
||||
<string name="mode_decky_ready">Έτοιμο για εγκατάσταση %1$s.</string>
|
||||
@@ -881,13 +881,9 @@
|
||||
<string name="setup_fullscreen">Πλήρης οθόνη</string>
|
||||
<string name="setup_fullscreen_on">Αποκρύπτει τις γραμμές κατάστασης και πλοήγησης του Android</string>
|
||||
<string name="setup_fullscreen_off">Εμφανίζει τις γραμμές κατάστασης και πλοήγησης του Android</string>
|
||||
<string name="setup_dual_screen">Διπλή οθόνη</string>
|
||||
<string name="setup_dual_screen_hint">Το Αυτόματο χρησιμοποιεί τη δεύτερη οθόνη σε φορητές συσκευές με ενσωματωμένη. Το Ενεργό χρησιμοποιεί οποιαδήποτε δεύτερη οθόνη. Το Ανενεργό κρατά τα πάντα σε μία οθόνη.</string>
|
||||
<string name="setup_dual_screen_auto">Αυτόματο</string>
|
||||
<string name="setup_dual_screen_on">Ενεργό</string>
|
||||
<string name="setup_dual_screen_off">Ανενεργό</string>
|
||||
<string name="setup_dual_screen_menu">Μενού κάτω οθόνης</string>
|
||||
<string name="setup_dual_screen_menu_hint">Μεταφέρει τις ενότητες και το μενού του παιχνιδιού στη δεύτερη οθόνη και η πληκτρολόγηση γίνεται εκεί, ώστε η πάνω οθόνη να κρατά την πλήρη προβολή. Τα L2 και R2 αλλάζουν ενότητα.</string>
|
||||
<string name="setup_dual_screen_controls">Χειριστήρια κάτω οθόνης στα παιχνίδια</string>
|
||||
<string name="setup_dual_screen_controls_hint">Ανοίγει την επιφάνεια αφής και το πληκτρολόγιο στη δεύτερη οθόνη όταν ξεκινά μια συνεδρία.</string>
|
||||
<string name="dual_typing_title">Πληκτρολόγηση για την πάνω οθόνη</string>
|
||||
@@ -1802,8 +1798,6 @@
|
||||
<string name="stats_trace">Γκάζι και φρένο, τελευταία 10 s</string>
|
||||
<string name="stats_speed_chart">Ταχύτητα, %1$s</string>
|
||||
<string name="stats_gear">Σχέση</string>
|
||||
<string name="stats_speed_units">Μονάδες ταχύτητας</string>
|
||||
<string name="stats_speed_units_auto">Αυτόματα (%1$s)</string>
|
||||
<string name="stats_steam_client_arm">Πελάτης Steam ARM</string>
|
||||
<string name="stats_steam_client_x86">Πελάτης Steam x86</string>
|
||||
<string name="stats_throttle">Γκάζι</string>
|
||||
@@ -1821,7 +1815,6 @@
|
||||
<string name="stats_fuel_t">Καύσιμο, t</string>
|
||||
<string name="stats_fuel_main">Κύριο %1$s t</string>
|
||||
<string name="stats_fuel_reserve">Εφεδρικό %1$s t</string>
|
||||
<string name="stats_tap_hint">Πατήστε τη λωρίδα στο πάνω μέρος ή τον πίνακα οργάνων για να αλλάξετε το στυλ του.</string>
|
||||
<string name="bottom_telemetry_send_title">Αποστολή δεδομένων πίνακα</string>
|
||||
<string name="bottom_telemetry_send_auto">Αυτόματα</string>
|
||||
<string name="bottom_telemetry_send_auto_hint">Το ενεργοποιεί για κάθε παιχνίδι της λίστας, μαζί και όσα βρεθούν αργότερα. Τα παιχνίδια που απενεργοποιείτε παραμένουν ανενεργά.</string>
|
||||
@@ -1860,7 +1853,6 @@
|
||||
<string name="bottom_telemetry_balance">Υπόλοιπο</string>
|
||||
<string name="bottom_telemetry_war_thunder">War Thunder</string>
|
||||
<string name="bottom_telemetry_elite">Elite Dangerous</string>
|
||||
<string name="bottom_telemetry_help">Ενεργοποιήστε την έξοδο δεδομένων ενός αγωνιστικού παιχνιδιού (συχνά λέγεται τηλεμετρία ή UDP) για να δείτε εδώ ζωντανό πίνακα οργάνων. Τα δεδομένα μένουν σε αυτή τη συσκευή.</string>
|
||||
<string name="bottom_telemetry_unknown_format">Η θύρα %1$d λαμβάνει δεδομένα (%2$d πακέτα των %3$d byte), αλλά σε μορφή που αυτή η σελίδα δεν μπορεί ακόμα να διαβάσει.</string>
|
||||
<string name="bottom_telemetry_port_busy">Ένα άλλο πρόγραμμα χρησιμοποιεί ήδη τη θύρα %1$s, οπότε αυτό το παιχνίδι δεν μπορεί να εμφανιστεί εδώ.</string>
|
||||
|
||||
@@ -3637,4 +3629,26 @@
|
||||
<string name="second_mode_desktop">Επιφάνεια εργασίας</string>
|
||||
<string name="bottom_desktop_mode_trackpad">Πινακίδα αφής</string>
|
||||
<string name="bottom_desktop_title">Επιφάνεια εργασίας · %1$s</string>
|
||||
<string name="setup_dual_screen_launcher">Εκκινητής σε δύο οθόνες</string>
|
||||
<string name="setup_dual_screen_launcher_hint">Το Αυτόματο τοποθετεί τις ενότητες του εκκινητή (Steam, Βιβλιοθήκη, Καταστήματα και άλλες) στην ενσωματωμένη δεύτερη οθόνη. Το Ενεργό χρησιμοποιεί οποιαδήποτε δεύτερη οθόνη. Το Ανενεργό κρατά τον εκκινητή σε μία οθόνη.</string>
|
||||
<string name="setup_dual_screen_sessions">Συνεδρίες σε δύο οθόνες</string>
|
||||
<string name="setup_dual_screen_sessions_hint">Το Αυτόματο τοποθετεί το μενού παιχνιδιού στην ενσωματωμένη δεύτερη οθόνη αντί για επικάλυψη. Το Ενεργό χρησιμοποιεί οποιαδήποτε δεύτερη οθόνη. Το Ανενεργό κρατά το μενού στην οθόνη παιχνιδιού· μπορείτε ακόμη να επιλέξετε δεύτερη οθόνη από εκεί.</string>
|
||||
<string name="stats_hud_frametime">Χρόνος καρέ</string>
|
||||
<string name="stats_hud_last_seconds">τελευταία %1$d δ</string>
|
||||
<string name="stats_hud_avg_frame">μέσο καρέ</string>
|
||||
<string name="stats_hud_low" formatted="false">χαμηλότερο 1 %</string>
|
||||
<string name="stats_hud_slow_frames">καρέ πάνω από 33 ms</string>
|
||||
<string name="stats_hud_per_minute">%1$d / λεπτό</string>
|
||||
<string name="stats_hud_playing_for">Χρόνος παιχνιδιού: %1$s</string>
|
||||
<string name="stats_hud_core_group">%1$d πυρήνες, έως %2$s GHz</string>
|
||||
<string name="stats_hud_core_group_unknown">%1$d πυρήνες</string>
|
||||
<string name="stats_hud_fps_of">FPS στα %1$d Hz</string>
|
||||
<string name="stats_hud_red_mark">Η κόκκινη ένδειξη δείχνει πού το HUD εμφανίζει τη θερμοκρασία με κόκκινο.</string>
|
||||
<string name="stats_hud_whole_device">ολόκληρη συσκευή</string>
|
||||
<string name="bottom_hud_group">HUD κάτω οθόνης</string>
|
||||
<string name="bottom_strip_tabs">Λωρίδα σε κάθε καρτέλα</string>
|
||||
<string name="bottom_strip_lows" formatted="false">χαμηλότερα 1 % και 0,1 %</string>
|
||||
<string name="bottom_strip_cpu_temp">Θερμοκρασία CPU</string>
|
||||
<string name="stats_hud_battery_temp">Μπαταρία</string>
|
||||
<string name="stats_hud_game_cores">Παιχνίδι</string>
|
||||
</resources>
|
||||
@@ -318,8 +318,8 @@
|
||||
<string name="mode_background_downloads_hint">Retrasa la suspensión en segundo plano mientras Steam esté descargando.</string>
|
||||
<string name="mode_suspend_note">Auto pausa en segundo plano y reanuda al mostrarse. Manual espera a Reanudar. Nunca sigue ejecutándose.</string>
|
||||
<string name="mode_startup">Inicio</string>
|
||||
<string name="mode_steam_startup">Iniciar Steam con DroidDeck</string>
|
||||
<string name="mode_steam_startup_hint">Abre la sesión de Steam al iniciar DroidDeck.</string>
|
||||
<string name="mode_startup_session">Abrir al iniciar</string>
|
||||
<string name="mode_startup_session_hint">Inicia esta sesión cuando se abre DroidDeck.</string>
|
||||
<string name="mode_decky">Decky</string>
|
||||
<string name="mode_decky_checking">Buscando versiones…</string>
|
||||
<string name="mode_decky_ready">Listo para instalar %1$s.</string>
|
||||
@@ -884,13 +884,9 @@
|
||||
<string name="setup_fullscreen">Pantalla completa</string>
|
||||
<string name="setup_fullscreen_on">Oculta las barras de estado y navegación de Android</string>
|
||||
<string name="setup_fullscreen_off">Muestra las barras de estado y navegación de Android</string>
|
||||
<string name="setup_dual_screen">Pantalla doble</string>
|
||||
<string name="setup_dual_screen_hint">Automático usa la segunda pantalla en consolas portátiles que la llevan integrada. Activado usa cualquier segunda pantalla. Desactivado lo mantiene todo en una pantalla.</string>
|
||||
<string name="setup_dual_screen_auto">Automático</string>
|
||||
<string name="setup_dual_screen_on">Activado</string>
|
||||
<string name="setup_dual_screen_off">Desactivado</string>
|
||||
<string name="setup_dual_screen_menu">Menú en la pantalla inferior</string>
|
||||
<string name="setup_dual_screen_menu_hint">Mueve las secciones y el menú del juego a la segunda pantalla y se escribe allí, para que la pantalla superior mantenga la vista completa. L2 y R2 cambian de sección.</string>
|
||||
<string name="setup_dual_screen_controls">Controles en la pantalla inferior en juegos</string>
|
||||
<string name="setup_dual_screen_controls_hint">Abre el trackpad y el teclado en la segunda pantalla al iniciar una sesión.</string>
|
||||
<string name="dual_typing_title">Escribiendo en la pantalla superior</string>
|
||||
@@ -1824,8 +1820,6 @@
|
||||
<string name="stats_trace">Acelerador y freno, últimos 10 s</string>
|
||||
<string name="stats_speed_chart">Velocidad, %1$s</string>
|
||||
<string name="stats_gear">Marcha</string>
|
||||
<string name="stats_speed_units">Unidades de velocidad</string>
|
||||
<string name="stats_speed_units_auto">Automático (%1$s)</string>
|
||||
<string name="stats_steam_client_arm">Cliente de Steam ARM</string>
|
||||
<string name="stats_steam_client_x86">Cliente de Steam x86</string>
|
||||
<string name="stats_throttle">Acelerador</string>
|
||||
@@ -1843,7 +1837,6 @@
|
||||
<string name="stats_fuel_t">Combustible, t</string>
|
||||
<string name="stats_fuel_main">Principal %1$s t</string>
|
||||
<string name="stats_fuel_reserve">Reserva %1$s t</string>
|
||||
<string name="stats_tap_hint">Toca la franja superior o el cuadro de instrumentos para cambiar su estilo.</string>
|
||||
<string name="bottom_telemetry_send_title">Enviar datos al panel</string>
|
||||
<string name="bottom_telemetry_send_auto">Automático</string>
|
||||
<string name="bottom_telemetry_send_auto_hint">Lo activa en todos los juegos de esta lista, incluidos los que se encuentren más adelante. Los juegos que desactives seguirán desactivados.</string>
|
||||
@@ -1882,7 +1875,6 @@
|
||||
<string name="bottom_telemetry_balance">Saldo</string>
|
||||
<string name="bottom_telemetry_war_thunder">War Thunder</string>
|
||||
<string name="bottom_telemetry_elite">Elite Dangerous</string>
|
||||
<string name="bottom_telemetry_help">Activa la salida de datos de un juego de carreras (a menudo llamada telemetría o UDP) para ver aquí un panel en directo. Los datos no salen de este dispositivo.</string>
|
||||
<string name="bottom_telemetry_unknown_format">El puerto %1$d recibe datos (%2$d paquetes de %3$d bytes), pero en un formato que esta página aún no puede leer.</string>
|
||||
<string name="bottom_telemetry_port_busy">Otro programa ya usa el puerto %1$s, así que ese juego no se puede mostrar aquí.</string>
|
||||
|
||||
@@ -3674,4 +3666,26 @@
|
||||
<string name="second_mode_desktop">Escritorio</string>
|
||||
<string name="bottom_desktop_mode_trackpad">Trackpad</string>
|
||||
<string name="bottom_desktop_title">Escritorio · %1$s</string>
|
||||
<string name="setup_dual_screen_launcher">Lanzador en dos pantallas</string>
|
||||
<string name="setup_dual_screen_launcher_hint">Automático coloca las secciones del lanzador (Steam, Biblioteca, Tiendas y otras) en la segunda pantalla integrada. Activado usa cualquier segunda pantalla. Desactivado mantiene el lanzador en una sola pantalla.</string>
|
||||
<string name="setup_dual_screen_sessions">Sesiones en dos pantallas</string>
|
||||
<string name="setup_dual_screen_sessions_hint">Automático coloca el menú del juego en la segunda pantalla integrada en lugar de superponerlo. Activado usa cualquier segunda pantalla. Desactivado mantiene el menú en la pantalla del juego; aún puedes elegir una segunda pantalla desde él.</string>
|
||||
<string name="stats_hud_frametime">Tiempo de fotograma</string>
|
||||
<string name="stats_hud_last_seconds">últimos %1$d s</string>
|
||||
<string name="stats_hud_avg_frame">fotograma medio</string>
|
||||
<string name="stats_hud_low" formatted="false">1 % más bajo</string>
|
||||
<string name="stats_hud_slow_frames">fotogramas de más de 33 ms</string>
|
||||
<string name="stats_hud_per_minute">%1$d / min</string>
|
||||
<string name="stats_hud_playing_for">Tiempo de juego: %1$s</string>
|
||||
<string name="stats_hud_core_group">%1$d núcleos, hasta %2$s GHz</string>
|
||||
<string name="stats_hud_core_group_unknown">%1$d núcleos</string>
|
||||
<string name="stats_hud_fps_of">FPS de %1$d Hz</string>
|
||||
<string name="stats_hud_red_mark">La marca roja indica dónde el HUD muestra la temperatura en rojo.</string>
|
||||
<string name="stats_hud_whole_device">todo el dispositivo</string>
|
||||
<string name="bottom_hud_group">HUD de la pantalla inferior</string>
|
||||
<string name="bottom_strip_tabs">Franja en cada pestaña</string>
|
||||
<string name="bottom_strip_lows" formatted="false">1 % y 0,1 % más bajos</string>
|
||||
<string name="bottom_strip_cpu_temp">Temperatura de CPU</string>
|
||||
<string name="stats_hud_battery_temp">Batería</string>
|
||||
<string name="stats_hud_game_cores">Juego</string>
|
||||
</resources>
|
||||
@@ -318,8 +318,8 @@
|
||||
<string name="mode_background_downloads_hint">Viivästytä taustakeskeytystä, kun Steam lataa aktiivisesti.</string>
|
||||
<string name="mode_suspend_note">Automaattinen keskeyttää taustalla ja jatkaa, kun sovellus tulee näkyviin. Manuaalinen odottaa Jatka-painiketta. Ei koskaan pitää sovelluksen aina käynnissä.</string>
|
||||
<string name="mode_startup">Käynnistys</string>
|
||||
<string name="mode_steam_startup">Käynnistä Steam DroidDeckin kanssa</string>
|
||||
<string name="mode_steam_startup_hint">Avaa Steam-istunnon, kun DroidDeck käynnistyy.</string>
|
||||
<string name="mode_startup_session">Avaa käynnistyksessä</string>
|
||||
<string name="mode_startup_session_hint">Käynnistää tämän istunnon, kun DroidDeck avataan.</string>
|
||||
<string name="mode_decky">Decky</string>
|
||||
<string name="mode_decky_checking">Tarkistetaan julkaisuja…</string>
|
||||
<string name="mode_decky_ready">Valmiina asentamaan %1$s.</string>
|
||||
@@ -881,13 +881,9 @@
|
||||
<string name="setup_fullscreen">Koko näyttö</string>
|
||||
<string name="setup_fullscreen_on">Piilottaa Androidin tila- ja navigointipalkit</string>
|
||||
<string name="setup_fullscreen_off">Näyttää Androidin tila- ja navigointipalkit</string>
|
||||
<string name="setup_dual_screen">Kaksi näyttöä</string>
|
||||
<string name="setup_dual_screen_hint">Automaattinen käyttää toista näyttöä kämmenlaitteissa, joissa sellainen on sisäänrakennettuna. Päällä käyttää mitä tahansa toista näyttöä. Pois pitää kaiken yhdellä näytöllä.</string>
|
||||
<string name="setup_dual_screen_auto">Automaattinen</string>
|
||||
<string name="setup_dual_screen_on">Päällä</string>
|
||||
<string name="setup_dual_screen_off">Pois</string>
|
||||
<string name="setup_dual_screen_menu">Alanäytön valikko</string>
|
||||
<string name="setup_dual_screen_menu_hint">Siirtää osiot ja pelin aikaisen valikon toiselle näytölle ja kirjoitus tehdään siellä, joten yläruutu säilyttää koko näkymän. L2 ja R2 vaihtavat osiota.</string>
|
||||
<string name="setup_dual_screen_controls">Alanäytön ohjaimet peleissä</string>
|
||||
<string name="setup_dual_screen_controls_hint">Avaa kosketuslevyn ja näppäimistön toiselle näytölle, kun istunto alkaa.</string>
|
||||
<string name="dual_typing_title">Kirjoitus yläruudulle</string>
|
||||
@@ -1802,8 +1798,6 @@
|
||||
<string name="stats_trace">Kaasu ja jarru, viimeiset 10 s</string>
|
||||
<string name="stats_speed_chart">Nopeus, %1$s</string>
|
||||
<string name="stats_gear">Vaihde</string>
|
||||
<string name="stats_speed_units">Nopeuden yksiköt</string>
|
||||
<string name="stats_speed_units_auto">Automaattinen (%1$s)</string>
|
||||
<string name="stats_steam_client_arm">Steam-asiakasohjelma ARM</string>
|
||||
<string name="stats_steam_client_x86">Steam-asiakasohjelma x86</string>
|
||||
<string name="stats_throttle">Kaasu</string>
|
||||
@@ -1821,7 +1815,6 @@
|
||||
<string name="stats_fuel_t">Polttoaine, t</string>
|
||||
<string name="stats_fuel_main">Päätankki %1$s t</string>
|
||||
<string name="stats_fuel_reserve">Varatankki %1$s t</string>
|
||||
<string name="stats_tap_hint">Napauta yläpalkkia tai kojelautaa vaihtaaksesi sen tyyliä.</string>
|
||||
<string name="bottom_telemetry_send_title">Lähetä kojelaudan tiedot</string>
|
||||
<string name="bottom_telemetry_send_auto">Automaattinen</string>
|
||||
<string name="bottom_telemetry_send_auto_hint">Kytkee sen päälle jokaiselle tässä luetellulle pelille, myös myöhemmin löytyville. Pois kytkemäsi pelit pysyvät pois päältä.</string>
|
||||
@@ -1860,7 +1853,6 @@
|
||||
<string name="bottom_telemetry_balance">Saldo</string>
|
||||
<string name="bottom_telemetry_war_thunder">War Thunder</string>
|
||||
<string name="bottom_telemetry_elite">Elite Dangerous</string>
|
||||
<string name="bottom_telemetry_help">Kytke kilpa-ajopelin datan ulostulo päälle (usein nimeltään telemetria tai UDP), niin näet täällä reaaliaikaisen kojelaudan. Tiedot pysyvät tällä laitteella.</string>
|
||||
<string name="bottom_telemetry_unknown_format">Portti %1$d vastaanottaa dataa (%2$d pakettia, kukin %3$d tavua), mutta muodossa, jota tämä sivu ei vielä osaa lukea.</string>
|
||||
<string name="bottom_telemetry_port_busy">Toinen ohjelma käyttää jo porttia %1$s, joten peliä ei voida näyttää täällä.</string>
|
||||
|
||||
@@ -3637,4 +3629,26 @@
|
||||
<string name="second_mode_desktop">Työpöytä</string>
|
||||
<string name="bottom_desktop_mode_trackpad">Kosketuslevy</string>
|
||||
<string name="bottom_desktop_title">Työpöytä · %1$s</string>
|
||||
<string name="setup_dual_screen_launcher">Käynnistin kahdella näytöllä</string>
|
||||
<string name="setup_dual_screen_launcher_hint">Automaattinen näyttää käynnistimen osiot (Steam, Kirjasto, Kaupat ja muut) sisäisellä toisella näytöllä. Päällä käyttää mitä tahansa toista näyttöä. Pois pitää käynnistimen yhdellä näytöllä.</string>
|
||||
<string name="setup_dual_screen_sessions">Istunnot kahdella näytöllä</string>
|
||||
<string name="setup_dual_screen_sessions_hint">Automaattinen näyttää pelivalikon sisäisellä toisella näytöllä peittokuvan sijaan. Päällä käyttää mitä tahansa toista näyttöä. Pois pitää valikon pelinäytöllä; siitä voi silti valita toisen näytön.</string>
|
||||
<string name="stats_hud_frametime">Ruutuaika</string>
|
||||
<string name="stats_hud_last_seconds">viimeiset %1$d s</string>
|
||||
<string name="stats_hud_avg_frame">keskimääräinen ruutu</string>
|
||||
<string name="stats_hud_low" formatted="false">alin 1 %</string>
|
||||
<string name="stats_hud_slow_frames">yli 33 ms:n ruudut</string>
|
||||
<string name="stats_hud_per_minute">%1$d / min</string>
|
||||
<string name="stats_hud_playing_for">Peliaika: %1$s</string>
|
||||
<string name="stats_hud_core_group">%1$d ydintä, enintään %2$s GHz</string>
|
||||
<string name="stats_hud_core_group_unknown">%1$d ydintä</string>
|
||||
<string name="stats_hud_fps_of">FPS %1$d Hz:n taajuudella</string>
|
||||
<string name="stats_hud_red_mark">Punainen merkki näyttää kohdan, jossa HUD näyttää lämpötilan punaisena.</string>
|
||||
<string name="stats_hud_whole_device">koko laite</string>
|
||||
<string name="bottom_hud_group">Alanäytön HUD</string>
|
||||
<string name="bottom_strip_tabs">Palkki joka välilehdellä</string>
|
||||
<string name="bottom_strip_lows" formatted="false">alimmat 1 % ja 0,1 %</string>
|
||||
<string name="bottom_strip_cpu_temp">CPU:n lämpötila</string>
|
||||
<string name="stats_hud_battery_temp">Akku</string>
|
||||
<string name="stats_hud_game_cores">Peli</string>
|
||||
</resources>
|
||||
@@ -318,8 +318,8 @@
|
||||
<string name="mode_background_downloads_hint">Retarde la mise en pause en arrière-plan tant que Steam télécharge.</string>
|
||||
<string name="mode_suspend_note">Le mode automatique met en pause en arrière-plan et reprend à l\'affichage. Le mode manuel nécessite d\'appuyer sur « Reprendre ». Ne reste jamais en cours d\'exécution.</string>
|
||||
<string name="mode_startup">Démarrage</string>
|
||||
<string name="mode_steam_startup">Lancer Steam à l\'ouverture de DroidDeck</string>
|
||||
<string name="mode_steam_startup_hint">Ouvre une session Steam au démarrage de DroidDeck.</string>
|
||||
<string name="mode_startup_session">Ouvrir au démarrage</string>
|
||||
<string name="mode_startup_session_hint">Lance cette session au démarrage de DroidDeck.</string>
|
||||
<string name="mode_decky">Decky</string>
|
||||
<string name="mode_decky_checking">Vérification des versions…</string>
|
||||
<string name="mode_decky_ready">Prêt à installer %1$s.</string>
|
||||
@@ -884,13 +884,9 @@
|
||||
<string name="setup_fullscreen">Plein écran</string>
|
||||
<string name="setup_fullscreen_on">Masquer la barre d\'état et la barre de navigation Android</string>
|
||||
<string name="setup_fullscreen_off">Afficher la barre d\'état et la barre de navigation Android</string>
|
||||
<string name="setup_dual_screen">Double écran</string>
|
||||
<string name="setup_dual_screen_hint">Automatique utilise le second écran sur les consoles portables qui en ont un intégré. Activé utilise n\'importe quel second écran. Désactivé garde tout sur un seul écran.</string>
|
||||
<string name="setup_dual_screen_auto">Automatique</string>
|
||||
<string name="setup_dual_screen_on">Activé</string>
|
||||
<string name="setup_dual_screen_off">Désactivé</string>
|
||||
<string name="setup_dual_screen_menu">Menu sur l\'écran du bas</string>
|
||||
<string name="setup_dual_screen_menu_hint">Déplace les sections et le menu en jeu sur le second écran et la saisie s\'y fait, pour que l\'écran du haut garde toute la vue. L2 et R2 changent de section.</string>
|
||||
<string name="setup_dual_screen_controls">Commandes sur l\'écran du bas en jeu</string>
|
||||
<string name="setup_dual_screen_controls_hint">Ouvre le pavé tactile et le clavier sur le second écran au démarrage d\'une session.</string>
|
||||
<string name="dual_typing_title">Saisie pour l\'écran du haut</string>
|
||||
@@ -1824,8 +1820,6 @@
|
||||
<string name="stats_trace">Accélérateur et frein, 10 dernières s</string>
|
||||
<string name="stats_speed_chart">Vitesse, %1$s</string>
|
||||
<string name="stats_gear">Rapport</string>
|
||||
<string name="stats_speed_units">Unités de vitesse</string>
|
||||
<string name="stats_speed_units_auto">Automatique (%1$s)</string>
|
||||
<string name="stats_steam_client_arm">Client Steam ARM</string>
|
||||
<string name="stats_steam_client_x86">Client Steam x86</string>
|
||||
<string name="stats_throttle">Accélérateur</string>
|
||||
@@ -1843,7 +1837,6 @@
|
||||
<string name="stats_fuel_t">Carburant, t</string>
|
||||
<string name="stats_fuel_main">Principal %1$s t</string>
|
||||
<string name="stats_fuel_reserve">Réserve %1$s t</string>
|
||||
<string name="stats_tap_hint">Appuyez sur la bande supérieure ou sur le tableau de bord pour changer son style.</string>
|
||||
<string name="bottom_telemetry_send_title">Envoyer les données du tableau de bord</string>
|
||||
<string name="bottom_telemetry_send_auto">Automatique</string>
|
||||
<string name="bottom_telemetry_send_auto_hint">L\'active pour chaque jeu listé ici, y compris les jeux trouvés plus tard. Les jeux que vous désactivez restent désactivés.</string>
|
||||
@@ -1882,7 +1875,6 @@
|
||||
<string name="bottom_telemetry_balance">Solde</string>
|
||||
<string name="bottom_telemetry_war_thunder">War Thunder</string>
|
||||
<string name="bottom_telemetry_elite">Elite Dangerous</string>
|
||||
<string name="bottom_telemetry_help">Activez la sortie de données d\'un jeu de course (souvent appelée télémétrie ou UDP) pour voir ici un tableau de bord en direct. Les données restent sur cet appareil.</string>
|
||||
<string name="bottom_telemetry_unknown_format">Le port %1$d reçoit des données (%2$d paquets de %3$d octets), mais dans un format que cette page ne sait pas encore lire.</string>
|
||||
<string name="bottom_telemetry_port_busy">Un autre programme utilise déjà le port %1$s ; ce jeu ne peut donc pas s\'afficher ici.</string>
|
||||
|
||||
@@ -3674,4 +3666,26 @@
|
||||
<string name="second_mode_desktop">Bureau</string>
|
||||
<string name="bottom_desktop_mode_trackpad">Pavé tactile</string>
|
||||
<string name="bottom_desktop_title">Bureau · %1$s</string>
|
||||
<string name="setup_dual_screen_launcher">Lanceur sur deux écrans</string>
|
||||
<string name="setup_dual_screen_launcher_hint">Automatique place les sections du lanceur (Steam, Bibliothèque, Boutiques et autres) sur le second écran intégré. Activé utilise tout second écran. Désactivé garde le lanceur sur un seul écran.</string>
|
||||
<string name="setup_dual_screen_sessions">Sessions sur deux écrans</string>
|
||||
<string name="setup_dual_screen_sessions_hint">Automatique place le menu de jeu sur le second écran intégré au lieu de le superposer. Activé utilise tout second écran. Désactivé garde le menu sur l’écran du jeu ; vous pouvez toujours y choisir un second écran.</string>
|
||||
<string name="stats_hud_frametime">Durée d’image</string>
|
||||
<string name="stats_hud_last_seconds">%1$d dernières s</string>
|
||||
<string name="stats_hud_avg_frame">image moyenne</string>
|
||||
<string name="stats_hud_low" formatted="false">1 % le plus bas</string>
|
||||
<string name="stats_hud_slow_frames">images de plus de 33 ms</string>
|
||||
<string name="stats_hud_per_minute">%1$d / min</string>
|
||||
<string name="stats_hud_playing_for">Temps de jeu : %1$s</string>
|
||||
<string name="stats_hud_core_group">%1$d cœurs, jusqu’à %2$s GHz</string>
|
||||
<string name="stats_hud_core_group_unknown">%1$d cœurs</string>
|
||||
<string name="stats_hud_fps_of">FPS sur %1$d Hz</string>
|
||||
<string name="stats_hud_red_mark">Le repère rouge indique où le HUD affiche la température en rouge.</string>
|
||||
<string name="stats_hud_whole_device">appareil entier</string>
|
||||
<string name="bottom_hud_group">HUD de l’écran inférieur</string>
|
||||
<string name="bottom_strip_tabs">Bande sur chaque onglet</string>
|
||||
<string name="bottom_strip_lows" formatted="false">1 % et 0,1 % les plus bas</string>
|
||||
<string name="bottom_strip_cpu_temp">Température du processeur</string>
|
||||
<string name="stats_hud_battery_temp">Batterie</string>
|
||||
<string name="stats_hud_game_cores">Jeu</string>
|
||||
</resources>
|
||||
@@ -318,8 +318,8 @@
|
||||
<string name="mode_background_downloads_hint">Steam के सक्रिय रूप से डाउनलोड करते समय बैकग्राउंड सस्पेंशन को टालें।</string>
|
||||
<string name="mode_suspend_note">ऑटो बैकग्राउंड में रुकता है और दिखने पर फिर से शुरू होता है। मैनुअल फिर से शुरू करें का इंतज़ार करता है। कभी नहीं वाला चलता रहता है।</string>
|
||||
<string name="mode_startup">स्टार्टअप</string>
|
||||
<string name="mode_steam_startup">DroidDeck के साथ Steam शुरू करें</string>
|
||||
<string name="mode_steam_startup_hint">DroidDeck लॉन्च होने पर Steam सेशन खोलता है।</string>
|
||||
<string name="mode_startup_session">लॉन्च पर खोलें</string>
|
||||
<string name="mode_startup_session_hint">DroidDeck खुलने पर यह सेशन शुरू करता है।</string>
|
||||
<string name="mode_decky">Decky</string>
|
||||
<string name="mode_decky_checking">रिलीज़ देखी जा रही हैं…</string>
|
||||
<string name="mode_decky_ready">%1$s इंस्टॉल करने के लिए तैयार।</string>
|
||||
@@ -881,13 +881,9 @@
|
||||
<string name="setup_fullscreen">फ़ुलस्क्रीन</string>
|
||||
<string name="setup_fullscreen_on">Android स्टेटस और नेविगेशन बार छिपाता है</string>
|
||||
<string name="setup_fullscreen_off">Android स्टेटस और नेविगेशन बार दिखाता है</string>
|
||||
<string name="setup_dual_screen">डुअल स्क्रीन</string>
|
||||
<string name="setup_dual_screen_hint">ऑटोमैटिक, बिल्ट-इन दूसरी स्क्रीन वाले हैंडहेल्ड पर दूसरी स्क्रीन इस्तेमाल करता है। चालू किसी भी दूसरे डिस्प्ले का इस्तेमाल करता है। बंद सब कुछ एक स्क्रीन पर रखता है।</string>
|
||||
<string name="setup_dual_screen_auto">ऑटोमैटिक</string>
|
||||
<string name="setup_dual_screen_on">चालू</string>
|
||||
<string name="setup_dual_screen_off">बंद</string>
|
||||
<string name="setup_dual_screen_menu">नीचे की स्क्रीन पर मेनू</string>
|
||||
<string name="setup_dual_screen_menu_hint">सेक्शन और गेम के दौरान का मेनू दूसरी स्क्रीन पर ले जाता है और टाइपिंग वहीं होती है, ताकि ऊपर की स्क्रीन पूरा दृश्य दिखाए। L2 और R2 सेक्शन बदलते हैं।</string>
|
||||
<string name="setup_dual_screen_controls">गेम में नीचे की स्क्रीन के कंट्रोल</string>
|
||||
<string name="setup_dual_screen_controls_hint">सेशन शुरू होने पर दूसरी स्क्रीन पर ट्रैकपैड और कीबोर्ड खोलता है।</string>
|
||||
<string name="dual_typing_title">ऊपर की स्क्रीन के लिए टाइपिंग</string>
|
||||
@@ -1802,8 +1798,6 @@
|
||||
<string name="stats_trace">थ्रॉटल और ब्रेक, पिछले 10 s</string>
|
||||
<string name="stats_speed_chart">स्पीड, %1$s</string>
|
||||
<string name="stats_gear">गियर</string>
|
||||
<string name="stats_speed_units">स्पीड की इकाई</string>
|
||||
<string name="stats_speed_units_auto">ऑटोमैटिक (%1$s)</string>
|
||||
<string name="stats_steam_client_arm">Steam क्लाइंट ARM</string>
|
||||
<string name="stats_steam_client_x86">Steam क्लाइंट x86</string>
|
||||
<string name="stats_throttle">थ्रॉटल</string>
|
||||
@@ -1821,7 +1815,6 @@
|
||||
<string name="stats_fuel_t">फ़्यूल, t</string>
|
||||
<string name="stats_fuel_main">मुख्य %1$s t</string>
|
||||
<string name="stats_fuel_reserve">रिज़र्व %1$s t</string>
|
||||
<string name="stats_tap_hint">स्टाइल बदलने के लिए ऊपर की पट्टी या डैश पर टैप करें।</string>
|
||||
<string name="bottom_telemetry_send_title">डैशबोर्ड डेटा भेजें</string>
|
||||
<string name="bottom_telemetry_send_auto">ऑटोमैटिक</string>
|
||||
<string name="bottom_telemetry_send_auto_hint">यहां दिए हर गेम के लिए इसे चालू करता है, बाद में मिलने वाले गेम भी शामिल हैं। जिन गेम को आप बंद करते हैं, वे बंद ही रहते हैं।</string>
|
||||
@@ -1860,7 +1853,6 @@
|
||||
<string name="bottom_telemetry_balance">बैलेंस</string>
|
||||
<string name="bottom_telemetry_war_thunder">War Thunder</string>
|
||||
<string name="bottom_telemetry_elite">Elite Dangerous</string>
|
||||
<string name="bottom_telemetry_help">यहां लाइव डैश देखने के लिए किसी रेसिंग गेम का डेटा आउटपुट (अक्सर टेलीमेट्री या UDP कहलाता है) चालू करें। यह इसी डिवाइस पर रहता है।</string>
|
||||
<string name="bottom_telemetry_unknown_format">पोर्ट %1$d पर डेटा आ रहा है (%3$d बाइट के %2$d पैकेट), लेकिन ऐसे फ़ॉर्मैट में जिसे यह पेज अभी पढ़ नहीं सकता।</string>
|
||||
<string name="bottom_telemetry_port_busy">पोर्ट %1$s को कोई दूसरा प्रोग्राम पहले से इस्तेमाल कर रहा है, इसलिए वह गेम यहां नहीं दिख सकता।</string>
|
||||
|
||||
@@ -3637,4 +3629,26 @@
|
||||
<string name="second_mode_desktop">डेस्कटॉप</string>
|
||||
<string name="bottom_desktop_mode_trackpad">ट्रैकपैड</string>
|
||||
<string name="bottom_desktop_title">डेस्कटॉप · %1$s</string>
|
||||
<string name="setup_dual_screen_launcher">दो स्क्रीन पर लॉन्चर</string>
|
||||
<string name="setup_dual_screen_launcher_hint">स्वचालित, लॉन्चर के भागों (Steam, लाइब्रेरी, स्टोर और अन्य) को अंतर्निर्मित दूसरी स्क्रीन पर रखता है। चालू किसी भी दूसरी स्क्रीन का उपयोग करता है। बंद लॉन्चर को एक स्क्रीन पर रखता है।</string>
|
||||
<string name="setup_dual_screen_sessions">दो स्क्रीन पर सत्र</string>
|
||||
<string name="setup_dual_screen_sessions_hint">स्वचालित, गेम मेनू को ऊपर दिखाने के बजाय अंतर्निर्मित दूसरी स्क्रीन पर रखता है। चालू किसी भी दूसरी स्क्रीन का उपयोग करता है। बंद मेनू को गेम स्क्रीन पर रखता है; फिर भी मेनू से दूसरी स्क्रीन चुन सकते हैं।</string>
|
||||
<string name="stats_hud_frametime">फ़्रेम समय</string>
|
||||
<string name="stats_hud_last_seconds">पिछले %1$d सेकंड</string>
|
||||
<string name="stats_hud_avg_frame">औसत फ़्रेम</string>
|
||||
<string name="stats_hud_low" formatted="false">सबसे कम 1 %</string>
|
||||
<string name="stats_hud_slow_frames">33 ms से लंबे फ़्रेम</string>
|
||||
<string name="stats_hud_per_minute">%1$d / मिनट</string>
|
||||
<string name="stats_hud_playing_for">खेलने का समय: %1$s</string>
|
||||
<string name="stats_hud_core_group">%1$d कोर, अधिकतम %2$s GHz</string>
|
||||
<string name="stats_hud_core_group_unknown">%1$d कोर</string>
|
||||
<string name="stats_hud_fps_of">%1$d Hz पर FPS</string>
|
||||
<string name="stats_hud_red_mark">लाल निशान वह स्थान बताता है जहाँ HUD तापमान लाल रंग में दिखाता है।</string>
|
||||
<string name="stats_hud_whole_device">पूरा डिवाइस</string>
|
||||
<string name="bottom_hud_group">निचली स्क्रीन का HUD</string>
|
||||
<string name="bottom_strip_tabs">हर टैब पर पट्टी</string>
|
||||
<string name="bottom_strip_lows" formatted="false">सबसे कम 1 % और 0.1 %</string>
|
||||
<string name="bottom_strip_cpu_temp">CPU तापमान</string>
|
||||
<string name="stats_hud_battery_temp">बैटरी</string>
|
||||
<string name="stats_hud_game_cores">गेम</string>
|
||||
</resources>
|
||||
@@ -318,8 +318,8 @@
|
||||
<string name="mode_background_downloads_hint">Elhalasztja a háttérbeli felfüggesztést, amíg a Steam aktívan letölt.</string>
|
||||
<string name="mode_suspend_note">Az Automatikus szüneteltet a háttérben, és megjelenéskor folytatja. A Manuális megvárja a Folytatást. A Soha mindig futva tartja.</string>
|
||||
<string name="mode_startup">Indítás</string>
|
||||
<string name="mode_steam_startup">Steam indítása a DroidDeckkel</string>
|
||||
<string name="mode_steam_startup_hint">Megnyitja a Steam-munkamenetet a DroidDeck indulásakor.</string>
|
||||
<string name="mode_startup_session">Megnyitás indításkor</string>
|
||||
<string name="mode_startup_session_hint">A DroidDeck indulásakor elindítja ezt a munkamenetet.</string>
|
||||
<string name="mode_decky">Decky</string>
|
||||
<string name="mode_decky_checking">Kiadások keresése…</string>
|
||||
<string name="mode_decky_ready">Készen áll a %1$s telepítésére.</string>
|
||||
@@ -881,13 +881,9 @@
|
||||
<string name="setup_fullscreen">Teljes képernyő</string>
|
||||
<string name="setup_fullscreen_on">Elrejti az Android állapot- és navigációs sávokat</string>
|
||||
<string name="setup_fullscreen_off">Megjeleníti az Android állapot- és navigációs sávokat</string>
|
||||
<string name="setup_dual_screen">Két képernyő</string>
|
||||
<string name="setup_dual_screen_hint">Az Automatikus a beépített második képernyővel rendelkező kézikonzolokon használja a második képernyőt. A Be bármilyen második kijelzőt használ. A Ki mindent egy képernyőn tart.</string>
|
||||
<string name="setup_dual_screen_auto">Automatikus</string>
|
||||
<string name="setup_dual_screen_on">Be</string>
|
||||
<string name="setup_dual_screen_off">Ki</string>
|
||||
<string name="setup_dual_screen_menu">Menü az alsó képernyőn</string>
|
||||
<string name="setup_dual_screen_menu_hint">A szakaszokat és a játék közbeni menüt a második képernyőre helyezi, és ott történik a gépelés, így a felső képernyő teljes nézetet kap. Az L2 és R2 vált szakaszt.</string>
|
||||
<string name="setup_dual_screen_controls">Alsó képernyős vezérlők játékokban</string>
|
||||
<string name="setup_dual_screen_controls_hint">Munkamenet indításakor megnyitja az érintőpadot és a billentyűzetet a második képernyőn.</string>
|
||||
<string name="dual_typing_title">Gépelés a felső képernyőre</string>
|
||||
@@ -1802,8 +1798,6 @@
|
||||
<string name="stats_trace">Gáz és fék, utolsó 10 s</string>
|
||||
<string name="stats_speed_chart">Sebesség, %1$s</string>
|
||||
<string name="stats_gear">Fokozat</string>
|
||||
<string name="stats_speed_units">Sebesség mértékegysége</string>
|
||||
<string name="stats_speed_units_auto">Automatikus (%1$s)</string>
|
||||
<string name="stats_steam_client_arm">Steam-kliens ARM</string>
|
||||
<string name="stats_steam_client_x86">Steam-kliens x86</string>
|
||||
<string name="stats_throttle">Gáz</string>
|
||||
@@ -1821,7 +1815,6 @@
|
||||
<string name="stats_fuel_t">Üzemanyag, t</string>
|
||||
<string name="stats_fuel_main">Fő %1$s t</string>
|
||||
<string name="stats_fuel_reserve">Tartalék %1$s t</string>
|
||||
<string name="stats_tap_hint">Koppintson a felső sávra vagy a műszerfalra a stílus módosításához.</string>
|
||||
<string name="bottom_telemetry_send_title">Műszerfal-adatok küldése</string>
|
||||
<string name="bottom_telemetry_send_auto">Automatikus</string>
|
||||
<string name="bottom_telemetry_send_auto_hint">Bekapcsolja az itt felsorolt összes játéknál, a később talált játékokat is beleértve. A kikapcsolt játékok kikapcsolva maradnak.</string>
|
||||
@@ -1860,7 +1853,6 @@
|
||||
<string name="bottom_telemetry_balance">Egyenleg</string>
|
||||
<string name="bottom_telemetry_war_thunder">War Thunder</string>
|
||||
<string name="bottom_telemetry_elite">Elite Dangerous</string>
|
||||
<string name="bottom_telemetry_help">Kapcsolja be egy autóversenyes játék adatkimenetét (gyakran telemetria vagy UDP néven szerepel), hogy itt élő műszerfalat lásson. Az adatok ezen az eszközön maradnak.</string>
|
||||
<string name="bottom_telemetry_unknown_format">A(z) %1$d port adatokat fogad (%2$d csomag, egyenként %3$d bájt), de olyan formátumban, amelyet ez az oldal még nem tud olvasni.</string>
|
||||
<string name="bottom_telemetry_port_busy">A(z) %1$s portot már egy másik program használja, ezért az a játék itt nem jeleníthető meg.</string>
|
||||
|
||||
@@ -3637,4 +3629,26 @@
|
||||
<string name="second_mode_desktop">Asztal</string>
|
||||
<string name="bottom_desktop_mode_trackpad">Érintőpad</string>
|
||||
<string name="bottom_desktop_title">Asztal · %1$s</string>
|
||||
<string name="setup_dual_screen_launcher">Indító két kijelzőn</string>
|
||||
<string name="setup_dual_screen_launcher_hint">Az Automatikus az indító részeit (Steam, Könyvtár, Áruházak és mások) a beépített második kijelzőre helyezi. A Be bármely második kijelzőt használja. A Ki egy kijelzőn tartja az indítót.</string>
|
||||
<string name="setup_dual_screen_sessions">Munkamenetek két kijelzőn</string>
|
||||
<string name="setup_dual_screen_sessions_hint">Az Automatikus a játékmenüt a beépített második kijelzőre helyezi átfedés helyett. A Be bármely második kijelzőt használja. A Ki a játék kijelzőjén tartja a menüt; abból továbbra is választható második kijelző.</string>
|
||||
<string name="stats_hud_frametime">Képkockaidő</string>
|
||||
<string name="stats_hud_last_seconds">utolsó %1$d mp</string>
|
||||
<string name="stats_hud_avg_frame">átlagos képkocka</string>
|
||||
<string name="stats_hud_low" formatted="false">legalacsonyabb 1 %</string>
|
||||
<string name="stats_hud_slow_frames">33 ms feletti képkockák</string>
|
||||
<string name="stats_hud_per_minute">%1$d / perc</string>
|
||||
<string name="stats_hud_playing_for">Játékidő: %1$s</string>
|
||||
<string name="stats_hud_core_group">%1$d mag, legfeljebb %2$s GHz</string>
|
||||
<string name="stats_hud_core_group_unknown">%1$d mag</string>
|
||||
<string name="stats_hud_fps_of">FPS %1$d Hz mellett</string>
|
||||
<string name="stats_hud_red_mark">A piros jelzés mutatja, hol jeleníti meg a HUD pirossal a hőmérsékletet.</string>
|
||||
<string name="stats_hud_whole_device">teljes eszköz</string>
|
||||
<string name="bottom_hud_group">Alsó kijelző HUD</string>
|
||||
<string name="bottom_strip_tabs">Sáv minden lapon</string>
|
||||
<string name="bottom_strip_lows" formatted="false">legalacsonyabb 1 % és 0,1 %</string>
|
||||
<string name="bottom_strip_cpu_temp">CPU-hőmérséklet</string>
|
||||
<string name="stats_hud_battery_temp">Akkumulátor</string>
|
||||
<string name="stats_hud_game_cores">Játék</string>
|
||||
</resources>
|
||||
@@ -318,8 +318,8 @@
|
||||
<string name="mode_background_downloads_hint">Menunda penjedaan latar belakang saat Steam sedang aktif mengunduh.</string>
|
||||
<string name="mode_suspend_note">Otomatis menjeda di latar belakang dan melanjutkan saat ditampilkan. Manual menunggu Lanjutkan. Tidak pernah terus berjalan.</string>
|
||||
<string name="mode_startup">Mulai</string>
|
||||
<string name="mode_steam_startup">Mulai Steam bersama DroidDeck</string>
|
||||
<string name="mode_steam_startup_hint">Membuka sesi Steam saat DroidDeck diluncurkan.</string>
|
||||
<string name="mode_startup_session">Buka saat peluncuran</string>
|
||||
<string name="mode_startup_session_hint">Memulai sesi ini saat DroidDeck diluncurkan.</string>
|
||||
<string name="mode_decky">Decky</string>
|
||||
<string name="mode_decky_checking">Memeriksa rilis…</string>
|
||||
<string name="mode_decky_ready">Siap menginstal %1$s.</string>
|
||||
@@ -878,13 +878,9 @@
|
||||
<string name="setup_fullscreen">Layar penuh</string>
|
||||
<string name="setup_fullscreen_on">Menyembunyikan bilah status dan navigasi Android</string>
|
||||
<string name="setup_fullscreen_off">Menampilkan bilah status dan navigasi Android</string>
|
||||
<string name="setup_dual_screen">Layar ganda</string>
|
||||
<string name="setup_dual_screen_hint">Otomatis memakai layar kedua pada perangkat genggam yang memiliki layar bawaan. Aktif memakai layar kedua apa pun. Nonaktif membuat semuanya tetap di satu layar.</string>
|
||||
<string name="setup_dual_screen_auto">Otomatis</string>
|
||||
<string name="setup_dual_screen_on">Aktif</string>
|
||||
<string name="setup_dual_screen_off">Nonaktif</string>
|
||||
<string name="setup_dual_screen_menu">Menu layar bawah</string>
|
||||
<string name="setup_dual_screen_menu_hint">Memindahkan bagian dan menu dalam game ke layar kedua dan mengetik di sana, agar layar atas tetap menampilkan tampilan penuh. L2 dan R2 berpindah bagian.</string>
|
||||
<string name="setup_dual_screen_controls">Kontrol layar bawah dalam game</string>
|
||||
<string name="setup_dual_screen_controls_hint">Membuka trackpad dan keyboard di layar kedua saat sesi dimulai.</string>
|
||||
<string name="dual_typing_title">Mengetik untuk layar atas</string>
|
||||
@@ -1780,8 +1776,6 @@
|
||||
<string name="stats_trace">Gas dan rem, 10 s terakhir</string>
|
||||
<string name="stats_speed_chart">Kecepatan, %1$s</string>
|
||||
<string name="stats_gear">Gigi</string>
|
||||
<string name="stats_speed_units">Satuan kecepatan</string>
|
||||
<string name="stats_speed_units_auto">Otomatis (%1$s)</string>
|
||||
<string name="stats_steam_client_arm">Klien Steam ARM</string>
|
||||
<string name="stats_steam_client_x86">Klien Steam x86</string>
|
||||
<string name="stats_throttle">Gas</string>
|
||||
@@ -1799,7 +1793,6 @@
|
||||
<string name="stats_fuel_t">Bahan bakar, t</string>
|
||||
<string name="stats_fuel_main">Utama %1$s t</string>
|
||||
<string name="stats_fuel_reserve">Cadangan %1$s t</string>
|
||||
<string name="stats_tap_hint">Ketuk bilah atas atau dasbor untuk mengubah gayanya.</string>
|
||||
<string name="bottom_telemetry_send_title">Kirim data dasbor</string>
|
||||
<string name="bottom_telemetry_send_auto">Otomatis</string>
|
||||
<string name="bottom_telemetry_send_auto_hint">Mengaktifkannya untuk setiap gim yang tercantum di sini, termasuk gim yang ditemukan nanti. Gim yang Anda matikan tetap mati.</string>
|
||||
@@ -1838,7 +1831,6 @@
|
||||
<string name="bottom_telemetry_balance">Saldo</string>
|
||||
<string name="bottom_telemetry_war_thunder">War Thunder</string>
|
||||
<string name="bottom_telemetry_elite">Elite Dangerous</string>
|
||||
<string name="bottom_telemetry_help">Aktifkan keluaran data gim balap (sering disebut telemetri atau UDP) untuk melihat dasbor langsung di sini. Data tetap berada di perangkat ini.</string>
|
||||
<string name="bottom_telemetry_unknown_format">Port %1$d menerima data (%2$d paket berukuran %3$d byte), tetapi dalam format yang belum dapat dibaca halaman ini.</string>
|
||||
<string name="bottom_telemetry_port_busy">Program lain sudah menggunakan port %1$s, jadi gim tersebut tidak dapat ditampilkan di sini.</string>
|
||||
|
||||
@@ -3609,4 +3601,26 @@
|
||||
<string name="second_mode_desktop">Desktop</string>
|
||||
<string name="bottom_desktop_mode_trackpad">Trackpad</string>
|
||||
<string name="bottom_desktop_title">Desktop · %1$s</string>
|
||||
<string name="setup_dual_screen_launcher">Peluncur dua layar</string>
|
||||
<string name="setup_dual_screen_launcher_hint">Otomatis menempatkan bagian peluncur (Steam, Pustaka, Toko, dan lainnya) di layar kedua bawaan. Aktif memakai layar kedua apa pun. Nonaktif menjaga peluncur di satu layar.</string>
|
||||
<string name="setup_dual_screen_sessions">Sesi dua layar</string>
|
||||
<string name="setup_dual_screen_sessions_hint">Otomatis menempatkan menu gim di layar kedua bawaan, bukan sebagai lapisan. Aktif memakai layar kedua apa pun. Nonaktif menjaga menu di layar gim; Anda tetap dapat memilih layar kedua dari menu.</string>
|
||||
<string name="stats_hud_frametime">Waktu bingkai</string>
|
||||
<string name="stats_hud_last_seconds">%1$d dtk terakhir</string>
|
||||
<string name="stats_hud_avg_frame">bingkai rata-rata</string>
|
||||
<string name="stats_hud_low" formatted="false">1 % terendah</string>
|
||||
<string name="stats_hud_slow_frames">bingkai di atas 33 ms</string>
|
||||
<string name="stats_hud_per_minute">%1$d / mnt</string>
|
||||
<string name="stats_hud_playing_for">Waktu bermain: %1$s</string>
|
||||
<string name="stats_hud_core_group">%1$d inti, hingga %2$s GHz</string>
|
||||
<string name="stats_hud_core_group_unknown">%1$d inti</string>
|
||||
<string name="stats_hud_fps_of">FPS pada %1$d Hz</string>
|
||||
<string name="stats_hud_red_mark">Tanda merah menunjukkan tempat HUD menampilkan suhu dengan warna merah.</string>
|
||||
<string name="stats_hud_whole_device">seluruh perangkat</string>
|
||||
<string name="bottom_hud_group">HUD layar bawah</string>
|
||||
<string name="bottom_strip_tabs">Bilah di setiap tab</string>
|
||||
<string name="bottom_strip_lows" formatted="false">1 % dan 0,1 % terendah</string>
|
||||
<string name="bottom_strip_cpu_temp">Suhu CPU</string>
|
||||
<string name="stats_hud_battery_temp">Baterai</string>
|
||||
<string name="stats_hud_game_cores">Permainan</string>
|
||||
</resources>
|
||||
@@ -318,8 +318,8 @@
|
||||
<string name="mode_background_downloads_hint">Ritarda la sospensione in background mentre Steam sta scaricando.</string>
|
||||
<string name="mode_suspend_note">Auto mette in pausa in background e riprende quando l\'app torna visibile. Manuale attende Riprendi. Mai lascia tutto in esecuzione.</string>
|
||||
<string name="mode_startup">Avvio</string>
|
||||
<string name="mode_steam_startup">Avvia Steam con DroidDeck</string>
|
||||
<string name="mode_steam_startup_hint">Apre la sessione Steam all\'avvio di DroidDeck.</string>
|
||||
<string name="mode_startup_session">Apri all\'avvio</string>
|
||||
<string name="mode_startup_session_hint">Avvia questa sessione all\'avvio di DroidDeck.</string>
|
||||
<string name="mode_decky">Decky</string>
|
||||
<string name="mode_decky_checking">Ricerca di release…</string>
|
||||
<string name="mode_decky_ready">Pronto per installare %1$s.</string>
|
||||
@@ -876,13 +876,9 @@
|
||||
<string name="setup_fullscreen">Schermo intero</string>
|
||||
<string name="setup_fullscreen_on">Nasconde la barra di stato e la barra di navigazione di Android</string>
|
||||
<string name="setup_fullscreen_off">Mostra la barra di stato e la barra di navigazione di Android</string>
|
||||
<string name="setup_dual_screen">Doppio schermo</string>
|
||||
<string name="setup_dual_screen_hint">Automatico usa il secondo schermo sulle console portatili che ne hanno uno integrato. Attivo usa qualsiasi secondo schermo. Disattivo tiene tutto su un solo schermo.</string>
|
||||
<string name="setup_dual_screen_auto">Automatico</string>
|
||||
<string name="setup_dual_screen_on">Attivo</string>
|
||||
<string name="setup_dual_screen_off">Disattivo</string>
|
||||
<string name="setup_dual_screen_menu">Menu sullo schermo inferiore</string>
|
||||
<string name="setup_dual_screen_menu_hint">Sposta le sezioni e il menu di gioco sul secondo schermo e si digita lì, così lo schermo superiore mantiene la vista completa. L2 e R2 cambiano sezione.</string>
|
||||
<string name="setup_dual_screen_controls">Controlli sullo schermo inferiore nei giochi</string>
|
||||
<string name="setup_dual_screen_controls_hint">Apre il trackpad e la tastiera sul secondo schermo all\'avvio di una sessione.</string>
|
||||
<string name="dual_typing_title">Digitazione per lo schermo superiore</string>
|
||||
@@ -1816,8 +1812,6 @@
|
||||
<string name="stats_trace">Acceleratore e freno, ultimi 10 s</string>
|
||||
<string name="stats_speed_chart">Velocità, %1$s</string>
|
||||
<string name="stats_gear">Marcia</string>
|
||||
<string name="stats_speed_units">Unità di velocità</string>
|
||||
<string name="stats_speed_units_auto">Automatico (%1$s)</string>
|
||||
<string name="stats_steam_client_arm">Client Steam ARM</string>
|
||||
<string name="stats_steam_client_x86">Client Steam x86</string>
|
||||
<string name="stats_throttle">Acceleratore</string>
|
||||
@@ -1835,7 +1829,6 @@
|
||||
<string name="stats_fuel_t">Carburante, t</string>
|
||||
<string name="stats_fuel_main">Principale %1$s t</string>
|
||||
<string name="stats_fuel_reserve">Riserva %1$s t</string>
|
||||
<string name="stats_tap_hint">Tocca la barra superiore o il cruscotto per cambiarne lo stile.</string>
|
||||
<string name="bottom_telemetry_send_title">Invia dati al cruscotto</string>
|
||||
<string name="bottom_telemetry_send_auto">Automatico</string>
|
||||
<string name="bottom_telemetry_send_auto_hint">Lo attiva per ogni gioco elencato qui, compresi i giochi trovati in seguito. I giochi che disattivi restano disattivati.</string>
|
||||
@@ -1874,7 +1867,6 @@
|
||||
<string name="bottom_telemetry_balance">Saldo</string>
|
||||
<string name="bottom_telemetry_war_thunder">War Thunder</string>
|
||||
<string name="bottom_telemetry_elite">Elite Dangerous</string>
|
||||
<string name="bottom_telemetry_help">Attiva l\'output dati di un gioco di corse (spesso chiamato telemetria o UDP) per vedere qui un cruscotto in tempo reale. I dati restano su questo dispositivo.</string>
|
||||
<string name="bottom_telemetry_unknown_format">La porta %1$d riceve dati (%2$d pacchetti da %3$d byte), ma in un formato che questa pagina non sa ancora leggere.</string>
|
||||
<string name="bottom_telemetry_port_busy">Un altro programma usa già la porta %1$s, quindi quel gioco non può essere mostrato qui.</string>
|
||||
|
||||
@@ -3674,4 +3666,26 @@
|
||||
<string name="second_mode_desktop">Desktop</string>
|
||||
<string name="bottom_desktop_mode_trackpad">Trackpad</string>
|
||||
<string name="bottom_desktop_title">Desktop · %1$s</string>
|
||||
<string name="setup_dual_screen_launcher">Launcher su due schermi</string>
|
||||
<string name="setup_dual_screen_launcher_hint">Automatico mostra le sezioni del launcher (Steam, Libreria, Negozi e altre) sul secondo schermo integrato. Attivo usa qualsiasi secondo schermo. Disattivo mantiene il launcher su uno schermo.</string>
|
||||
<string name="setup_dual_screen_sessions">Sessioni su due schermi</string>
|
||||
<string name="setup_dual_screen_sessions_hint">Automatico mostra il menu di gioco sul secondo schermo integrato anziché in sovrimpressione. Attivo usa qualsiasi secondo schermo. Disattivo mantiene il menu sullo schermo di gioco; da lì puoi ancora scegliere un secondo schermo.</string>
|
||||
<string name="stats_hud_frametime">Tempo fotogramma</string>
|
||||
<string name="stats_hud_last_seconds">ultimi %1$d s</string>
|
||||
<string name="stats_hud_avg_frame">fotogramma medio</string>
|
||||
<string name="stats_hud_low" formatted="false">1 % più basso</string>
|
||||
<string name="stats_hud_slow_frames">fotogrammi oltre 33 ms</string>
|
||||
<string name="stats_hud_per_minute">%1$d / min</string>
|
||||
<string name="stats_hud_playing_for">Tempo di gioco: %1$s</string>
|
||||
<string name="stats_hud_core_group">%1$d core, fino a %2$s GHz</string>
|
||||
<string name="stats_hud_core_group_unknown">%1$d core</string>
|
||||
<string name="stats_hud_fps_of">FPS su %1$d Hz</string>
|
||||
<string name="stats_hud_red_mark">Il segno rosso indica dove il HUD mostra la temperatura in rosso.</string>
|
||||
<string name="stats_hud_whole_device">intero dispositivo</string>
|
||||
<string name="bottom_hud_group">HUD dello schermo inferiore</string>
|
||||
<string name="bottom_strip_tabs">Barra in ogni scheda</string>
|
||||
<string name="bottom_strip_lows" formatted="false">1 % e 0,1 % più bassi</string>
|
||||
<string name="bottom_strip_cpu_temp">Temperatura CPU</string>
|
||||
<string name="stats_hud_battery_temp">Batteria</string>
|
||||
<string name="stats_hud_game_cores">Gioco</string>
|
||||
</resources>
|
||||
@@ -318,8 +318,8 @@
|
||||
<string name="mode_background_downloads_hint">Steamがダウンロード中は、バックグラウンドでの一時停止を遅らせます。</string>
|
||||
<string name="mode_suspend_note">自動ではバックグラウンドで一時停止し、表示すると再開します。手動では「再開」をタップするまで待機します。「しない」では実行し続けます。</string>
|
||||
<string name="mode_startup">起動</string>
|
||||
<string name="mode_steam_startup">DroidDeck起動時にSteamを起動</string>
|
||||
<string name="mode_steam_startup_hint">DroidDeckの起動時にSteamセッションを開きます。</string>
|
||||
<string name="mode_startup_session">起動時に開く</string>
|
||||
<string name="mode_startup_session_hint">DroidDeck の起動時にこのセッションを開始します。</string>
|
||||
<string name="mode_decky">Decky</string>
|
||||
<string name="mode_decky_checking">リリースを確認中…</string>
|
||||
<string name="mode_decky_ready">%1$sをインストールする準備ができました。</string>
|
||||
@@ -878,13 +878,9 @@
|
||||
<string name="setup_fullscreen">全画面</string>
|
||||
<string name="setup_fullscreen_on">Androidのステータスバーとナビゲーションバーを非表示</string>
|
||||
<string name="setup_fullscreen_off">Androidのステータスバーとナビゲーションバーを表示</string>
|
||||
<string name="setup_dual_screen">デュアル画面</string>
|
||||
<string name="setup_dual_screen_hint">自動では、2画面目を内蔵した携帯機でその画面を使います。オンではどの2画面目でも使います。オフではすべてを1画面に表示します。</string>
|
||||
<string name="setup_dual_screen_auto">自動</string>
|
||||
<string name="setup_dual_screen_on">オン</string>
|
||||
<string name="setup_dual_screen_off">オフ</string>
|
||||
<string name="setup_dual_screen_menu">下画面メニュー</string>
|
||||
<string name="setup_dual_screen_menu_hint">セクションとゲーム中のメニューを2画面目に移し、入力もそこで行うため、上画面は全体表示のままになります。L2とR2でセクションを切り替えます。</string>
|
||||
<string name="setup_dual_screen_controls">ゲーム中の下画面コントロール</string>
|
||||
<string name="setup_dual_screen_controls_hint">セッション開始時に2画面目にトラックパッドとキーボードを開きます。</string>
|
||||
<string name="dual_typing_title">上画面に入力中</string>
|
||||
@@ -1780,8 +1776,6 @@
|
||||
<string name="stats_trace">スロットルとブレーキ(直近10 s)</string>
|
||||
<string name="stats_speed_chart">速度(%1$s)</string>
|
||||
<string name="stats_gear">ギア</string>
|
||||
<string name="stats_speed_units">速度の単位</string>
|
||||
<string name="stats_speed_units_auto">自動(%1$s)</string>
|
||||
<string name="stats_steam_client_arm">Steamクライアント ARM</string>
|
||||
<string name="stats_steam_client_x86">Steamクライアント x86</string>
|
||||
<string name="stats_throttle">スロットル</string>
|
||||
@@ -1799,7 +1793,6 @@
|
||||
<string name="stats_fuel_t">燃料(t)</string>
|
||||
<string name="stats_fuel_main">メイン %1$s t</string>
|
||||
<string name="stats_fuel_reserve">予備 %1$s t</string>
|
||||
<string name="stats_tap_hint">上部のバーまたはダッシュをタップすると、表示スタイルを変更できます。</string>
|
||||
<string name="bottom_telemetry_send_title">ダッシュボード用データを送信</string>
|
||||
<string name="bottom_telemetry_send_auto">自動</string>
|
||||
<string name="bottom_telemetry_send_auto_hint">ここに表示されているすべてのゲーム(後から見つかったゲームを含む)でオンにします。オフにしたゲームはオフのままです。</string>
|
||||
@@ -1838,7 +1831,6 @@
|
||||
<string name="bottom_telemetry_balance">残高</string>
|
||||
<string name="bottom_telemetry_war_thunder">War Thunder</string>
|
||||
<string name="bottom_telemetry_elite">Elite Dangerous</string>
|
||||
<string name="bottom_telemetry_help">レースゲームのデータ出力(テレメトリーやUDPと呼ばれることが多い)をオンにすると、ここにライブダッシュが表示されます。データはこのデバイス内にとどまります。</string>
|
||||
<string name="bottom_telemetry_unknown_format">ポート%1$dでデータを受信していますが(%3$dバイトのパケット%2$d個)、このページではまだ読み取れない形式です。</string>
|
||||
<string name="bottom_telemetry_port_busy">別のプログラムがすでにポート%1$sを使用しているため、そのゲームはここに表示できません。</string>
|
||||
|
||||
@@ -3609,4 +3601,26 @@
|
||||
<string name="second_mode_desktop">デスクトップ</string>
|
||||
<string name="bottom_desktop_mode_trackpad">トラックパッド</string>
|
||||
<string name="bottom_desktop_title">デスクトップ · %1$s</string>
|
||||
<string name="setup_dual_screen_launcher">2画面ランチャー</string>
|
||||
<string name="setup_dual_screen_launcher_hint">自動ではランチャーの各項目(Steam、ライブラリ、ストアなど)を内蔵の第2画面に表示します。オンでは任意の第2画面を使用します。オフではランチャーを1画面に表示します。</string>
|
||||
<string name="setup_dual_screen_sessions">2画面セッション</string>
|
||||
<string name="setup_dual_screen_sessions_hint">自動ではゲームメニューを重ねて表示せず、内蔵の第2画面に配置します。オンでは任意の第2画面を使用します。オフではゲーム画面にメニューを表示しますが、メニューから第2画面を選択することもできます。</string>
|
||||
<string name="stats_hud_frametime">フレーム時間</string>
|
||||
<string name="stats_hud_last_seconds">直近%1$d秒</string>
|
||||
<string name="stats_hud_avg_frame">平均フレーム</string>
|
||||
<string name="stats_hud_low" formatted="false">下位1 %</string>
|
||||
<string name="stats_hud_slow_frames">33 msを超えるフレーム</string>
|
||||
<string name="stats_hud_per_minute">%1$d / 分</string>
|
||||
<string name="stats_hud_playing_for">プレイ時間:%1$s</string>
|
||||
<string name="stats_hud_core_group">%1$dコア、最大%2$s GHz</string>
|
||||
<string name="stats_hud_core_group_unknown">%1$dコア</string>
|
||||
<string name="stats_hud_fps_of">%1$d Hzに対するFPS</string>
|
||||
<string name="stats_hud_red_mark">赤い印はHUDが温度を赤く表示する位置を示します。</string>
|
||||
<string name="stats_hud_whole_device">デバイス全体</string>
|
||||
<string name="bottom_hud_group">下画面HUD</string>
|
||||
<string name="bottom_strip_tabs">すべてのタブにストリップ</string>
|
||||
<string name="bottom_strip_lows" formatted="false">下位1 %と0.1 %</string>
|
||||
<string name="bottom_strip_cpu_temp">CPU温度</string>
|
||||
<string name="stats_hud_battery_temp">バッテリー</string>
|
||||
<string name="stats_hud_game_cores">ゲーム</string>
|
||||
</resources>
|
||||
@@ -318,8 +318,8 @@
|
||||
<string name="mode_background_downloads_hint">Steam이 다운로드 중일 때는 백그라운드 일시 중지를 미룹니다.</string>
|
||||
<string name="mode_suspend_note">자동: 백그라운드에서 일시 중지되고 다시 표시되면 재개됩니다. 수동: \"재개\"를 탭할 때까지 기다립니다. 안 함: 계속 실행됩니다.</string>
|
||||
<string name="mode_startup">시작</string>
|
||||
<string name="mode_steam_startup">DroidDeck을 열 때 Steam 시작</string>
|
||||
<string name="mode_steam_startup_hint">DroidDeck이 시작될 때 Steam 세션을 엽니다.</string>
|
||||
<string name="mode_startup_session">실행 시 열기</string>
|
||||
<string name="mode_startup_session_hint">DroidDeck을 실행할 때 이 세션을 시작합니다.</string>
|
||||
<string name="mode_decky">Decky</string>
|
||||
<string name="mode_decky_checking">릴리스 확인 중…</string>
|
||||
<string name="mode_decky_ready">%1$s을(를) 설치할 준비가 되었습니다.</string>
|
||||
@@ -878,13 +878,9 @@
|
||||
<string name="setup_fullscreen">전체 화면</string>
|
||||
<string name="setup_fullscreen_on">Android 상태 표시줄 및 탐색 표시줄 숨기기</string>
|
||||
<string name="setup_fullscreen_off">Android 상태 표시줄 및 탐색 표시줄 표시</string>
|
||||
<string name="setup_dual_screen">듀얼 화면</string>
|
||||
<string name="setup_dual_screen_hint">자동은 두 번째 화면이 내장된 휴대용 기기에서 그 화면을 사용합니다. 켜기는 모든 두 번째 디스플레이를 사용합니다. 끄기는 모든 것을 한 화면에 둡니다.</string>
|
||||
<string name="setup_dual_screen_auto">자동</string>
|
||||
<string name="setup_dual_screen_on">켜기</string>
|
||||
<string name="setup_dual_screen_off">끄기</string>
|
||||
<string name="setup_dual_screen_menu">하단 화면 메뉴</string>
|
||||
<string name="setup_dual_screen_menu_hint">섹션과 게임 중 메뉴를 두 번째 화면으로 옮기고 입력도 그곳에서 하므로 상단 화면은 전체 보기를 유지합니다. L2와 R2로 섹션을 전환합니다.</string>
|
||||
<string name="setup_dual_screen_controls">게임 중 하단 화면 컨트롤</string>
|
||||
<string name="setup_dual_screen_controls_hint">세션이 시작되면 두 번째 화면에 트랙패드와 키보드를 엽니다.</string>
|
||||
<string name="dual_typing_title">상단 화면에 입력 중</string>
|
||||
@@ -1780,8 +1776,6 @@
|
||||
<string name="stats_trace">스로틀 및 브레이크, 최근 10 s</string>
|
||||
<string name="stats_speed_chart">속도, %1$s</string>
|
||||
<string name="stats_gear">기어</string>
|
||||
<string name="stats_speed_units">속도 단위</string>
|
||||
<string name="stats_speed_units_auto">자동(%1$s)</string>
|
||||
<string name="stats_steam_client_arm">Steam 클라이언트 ARM</string>
|
||||
<string name="stats_steam_client_x86">Steam 클라이언트 x86</string>
|
||||
<string name="stats_throttle">스로틀</string>
|
||||
@@ -1799,7 +1793,6 @@
|
||||
<string name="stats_fuel_t">연료, t</string>
|
||||
<string name="stats_fuel_main">메인 %1$s t</string>
|
||||
<string name="stats_fuel_reserve">예비 %1$s t</string>
|
||||
<string name="stats_tap_hint">상단 막대나 계기판을 탭하면 스타일이 바뀝니다.</string>
|
||||
<string name="bottom_telemetry_send_title">계기판 데이터 보내기</string>
|
||||
<string name="bottom_telemetry_send_auto">자동</string>
|
||||
<string name="bottom_telemetry_send_auto_hint">나중에 발견된 게임을 포함해 여기에 나열된 모든 게임에서 켭니다. 직접 끈 게임은 꺼진 상태로 유지됩니다.</string>
|
||||
@@ -1838,7 +1831,6 @@
|
||||
<string name="bottom_telemetry_balance">잔액</string>
|
||||
<string name="bottom_telemetry_war_thunder">War Thunder</string>
|
||||
<string name="bottom_telemetry_elite">Elite Dangerous</string>
|
||||
<string name="bottom_telemetry_help">레이싱 게임의 데이터 출력(흔히 텔레메트리 또는 UDP라고 함)을 켜면 여기에서 실시간 계기판을 볼 수 있습니다. 데이터는 이 기기에만 남습니다.</string>
|
||||
<string name="bottom_telemetry_unknown_format">포트 %1$d에서 데이터를 받고 있지만(%3$d바이트 패킷 %2$d개), 이 페이지가 아직 읽을 수 없는 형식입니다.</string>
|
||||
<string name="bottom_telemetry_port_busy">다른 프로그램이 이미 포트 %1$s을(를) 사용하고 있어 해당 게임을 여기에 표시할 수 없습니다.</string>
|
||||
|
||||
@@ -3609,4 +3601,26 @@
|
||||
<string name="second_mode_desktop">데스크톱</string>
|
||||
<string name="bottom_desktop_mode_trackpad">터치패드</string>
|
||||
<string name="bottom_desktop_title">데스크톱 · %1$s</string>
|
||||
<string name="setup_dual_screen_launcher">듀얼 화면 런처</string>
|
||||
<string name="setup_dual_screen_launcher_hint">자동은 런처 항목(Steam, 라이브러리, 스토어 등)을 내장 보조 화면에 표시합니다. 켜기는 모든 보조 화면을 사용합니다. 끄기는 런처를 한 화면에 유지합니다.</string>
|
||||
<string name="setup_dual_screen_sessions">듀얼 화면 세션</string>
|
||||
<string name="setup_dual_screen_sessions_hint">자동은 게임 메뉴를 오버레이 대신 내장 보조 화면에 표시합니다. 켜기는 모든 보조 화면을 사용합니다. 끄기는 메뉴를 게임 화면에 유지하며 메뉴에서 보조 화면을 선택할 수도 있습니다.</string>
|
||||
<string name="stats_hud_frametime">프레임 시간</string>
|
||||
<string name="stats_hud_last_seconds">최근 %1$d초</string>
|
||||
<string name="stats_hud_avg_frame">평균 프레임</string>
|
||||
<string name="stats_hud_low" formatted="false">하위 1 %</string>
|
||||
<string name="stats_hud_slow_frames">33 ms 초과 프레임</string>
|
||||
<string name="stats_hud_per_minute">%1$d / 분</string>
|
||||
<string name="stats_hud_playing_for">플레이 시간: %1$s</string>
|
||||
<string name="stats_hud_core_group">%1$d개 코어, 최대 %2$s GHz</string>
|
||||
<string name="stats_hud_core_group_unknown">%1$d개 코어</string>
|
||||
<string name="stats_hud_fps_of">%1$d Hz에서의 FPS</string>
|
||||
<string name="stats_hud_red_mark">빨간 표시는 HUD가 온도를 빨간색으로 표시하는 지점입니다.</string>
|
||||
<string name="stats_hud_whole_device">기기 전체</string>
|
||||
<string name="bottom_hud_group">하단 화면 HUD</string>
|
||||
<string name="bottom_strip_tabs">모든 탭에 스트립</string>
|
||||
<string name="bottom_strip_lows" formatted="false">하위 1 % 및 0.1 %</string>
|
||||
<string name="bottom_strip_cpu_temp">CPU 온도</string>
|
||||
<string name="stats_hud_battery_temp">배터리</string>
|
||||
<string name="stats_hud_game_cores">게임</string>
|
||||
</resources>
|
||||
@@ -318,8 +318,8 @@
|
||||
<string name="mode_background_downloads_hint">Lambatkan penjedaan latar belakang semasa Steam aktif memuat turun.</string>
|
||||
<string name="mode_suspend_note">Auto menjeda di latar belakang dan menyambung semula apabila dipaparkan. Manual menunggu Sambung. Jangan sekali-kali terus berjalan.</string>
|
||||
<string name="mode_startup">Permulaan</string>
|
||||
<string name="mode_steam_startup">Mulakan Steam bersama DroidDeck</string>
|
||||
<string name="mode_steam_startup_hint">Membuka sesi Steam apabila DroidDeck dilancarkan.</string>
|
||||
<string name="mode_startup_session">Buka semasa pelancaran</string>
|
||||
<string name="mode_startup_session_hint">Memulakan sesi ini apabila DroidDeck dilancarkan.</string>
|
||||
<string name="mode_decky">Decky</string>
|
||||
<string name="mode_decky_checking">Menyemak keluaran…</string>
|
||||
<string name="mode_decky_ready">Sedia untuk memasang %1$s.</string>
|
||||
@@ -878,13 +878,9 @@
|
||||
<string name="setup_fullscreen">Skrin penuh</string>
|
||||
<string name="setup_fullscreen_on">Menyembunyikan bar status dan navigasi Android</string>
|
||||
<string name="setup_fullscreen_off">Memaparkan bar status dan navigasi Android</string>
|
||||
<string name="setup_dual_screen">Dwi skrin</string>
|
||||
<string name="setup_dual_screen_hint">Automatik menggunakan skrin kedua pada peranti pegang tangan yang mempunyai skrin terbina dalam. Hidup menggunakan sebarang paparan kedua. Mati mengekalkan semuanya pada satu skrin.</string>
|
||||
<string name="setup_dual_screen_auto">Automatik</string>
|
||||
<string name="setup_dual_screen_on">Hidup</string>
|
||||
<string name="setup_dual_screen_off">Mati</string>
|
||||
<string name="setup_dual_screen_menu">Menu skrin bawah</string>
|
||||
<string name="setup_dual_screen_menu_hint">Memindahkan bahagian dan menu dalam permainan ke skrin kedua dan menaip di sana, supaya skrin atas kekal dengan paparan penuh. L2 dan R2 menukar bahagian.</string>
|
||||
<string name="setup_dual_screen_controls">Kawalan skrin bawah dalam permainan</string>
|
||||
<string name="setup_dual_screen_controls_hint">Membuka pad jejak dan papan kekunci pada skrin kedua apabila sesi bermula.</string>
|
||||
<string name="dual_typing_title">Menaip untuk skrin atas</string>
|
||||
@@ -1780,8 +1776,6 @@
|
||||
<string name="stats_trace">Pendikit dan brek, 10 s terakhir</string>
|
||||
<string name="stats_speed_chart">Kelajuan, %1$s</string>
|
||||
<string name="stats_gear">Gear</string>
|
||||
<string name="stats_speed_units">Unit kelajuan</string>
|
||||
<string name="stats_speed_units_auto">Automatik (%1$s)</string>
|
||||
<string name="stats_steam_client_arm">Klien Steam ARM</string>
|
||||
<string name="stats_steam_client_x86">Klien Steam x86</string>
|
||||
<string name="stats_throttle">Pendikit</string>
|
||||
@@ -1799,7 +1793,6 @@
|
||||
<string name="stats_fuel_t">Bahan api, t</string>
|
||||
<string name="stats_fuel_main">Utama %1$s t</string>
|
||||
<string name="stats_fuel_reserve">Simpanan %1$s t</string>
|
||||
<string name="stats_tap_hint">Ketik jalur atas atau papan pemuka untuk menukar gayanya.</string>
|
||||
<string name="bottom_telemetry_send_title">Hantar data papan pemuka</string>
|
||||
<string name="bottom_telemetry_send_auto">Automatik</string>
|
||||
<string name="bottom_telemetry_send_auto_hint">Menghidupkannya untuk setiap permainan yang disenaraikan di sini, termasuk permainan yang ditemui kemudian. Permainan yang anda matikan kekal mati.</string>
|
||||
@@ -1838,7 +1831,6 @@
|
||||
<string name="bottom_telemetry_balance">Baki</string>
|
||||
<string name="bottom_telemetry_war_thunder">War Thunder</string>
|
||||
<string name="bottom_telemetry_elite">Elite Dangerous</string>
|
||||
<string name="bottom_telemetry_help">Hidupkan output data permainan lumba (sering dipanggil telemetri atau UDP) untuk melihat papan pemuka langsung di sini. Data kekal pada peranti ini.</string>
|
||||
<string name="bottom_telemetry_unknown_format">Port %1$d menerima data (%2$d paket bersaiz %3$d bait), tetapi dalam format yang belum dapat dibaca oleh halaman ini.</string>
|
||||
<string name="bottom_telemetry_port_busy">Program lain sudah menggunakan port %1$s, jadi permainan itu tidak dapat dipaparkan di sini.</string>
|
||||
|
||||
@@ -3609,4 +3601,26 @@
|
||||
<string name="second_mode_desktop">Desktop</string>
|
||||
<string name="bottom_desktop_mode_trackpad">Trackpad</string>
|
||||
<string name="bottom_desktop_title">Desktop · %1$s</string>
|
||||
<string name="setup_dual_screen_launcher">Pelancar dua skrin</string>
|
||||
<string name="setup_dual_screen_launcher_hint">Automatik meletakkan bahagian pelancar (Steam, Pustaka, Kedai dan lain-lain) pada skrin kedua terbina dalam. Hidup menggunakan mana-mana skrin kedua. Mati mengekalkan pelancar pada satu skrin.</string>
|
||||
<string name="setup_dual_screen_sessions">Sesi dua skrin</string>
|
||||
<string name="setup_dual_screen_sessions_hint">Automatik meletakkan menu permainan pada skrin kedua terbina dalam dan bukannya tindanan. Hidup menggunakan mana-mana skrin kedua. Mati mengekalkan menu pada skrin permainan; anda masih boleh memilih skrin kedua daripadanya.</string>
|
||||
<string name="stats_hud_frametime">Masa bingkai</string>
|
||||
<string name="stats_hud_last_seconds">%1$d s terakhir</string>
|
||||
<string name="stats_hud_avg_frame">bingkai purata</string>
|
||||
<string name="stats_hud_low" formatted="false">1 % terendah</string>
|
||||
<string name="stats_hud_slow_frames">bingkai melebihi 33 ms</string>
|
||||
<string name="stats_hud_per_minute">%1$d / min</string>
|
||||
<string name="stats_hud_playing_for">Masa bermain: %1$s</string>
|
||||
<string name="stats_hud_core_group">%1$d teras, sehingga %2$s GHz</string>
|
||||
<string name="stats_hud_core_group_unknown">%1$d teras</string>
|
||||
<string name="stats_hud_fps_of">FPS pada %1$d Hz</string>
|
||||
<string name="stats_hud_red_mark">Tanda merah menunjukkan tempat HUD memaparkan suhu dengan warna merah.</string>
|
||||
<string name="stats_hud_whole_device">seluruh peranti</string>
|
||||
<string name="bottom_hud_group">HUD skrin bawah</string>
|
||||
<string name="bottom_strip_tabs">Jalur pada setiap tab</string>
|
||||
<string name="bottom_strip_lows" formatted="false">1 % dan 0.1 % terendah</string>
|
||||
<string name="bottom_strip_cpu_temp">Suhu CPU</string>
|
||||
<string name="stats_hud_battery_temp">Bateri</string>
|
||||
<string name="stats_hud_game_cores">Permainan</string>
|
||||
</resources>
|
||||
@@ -318,8 +318,8 @@
|
||||
<string name="mode_background_downloads_hint">Utsetter bakgrunnspause mens Steam laster aktivt ned.</string>
|
||||
<string name="mode_suspend_note">Auto setter på pause i bakgrunnen og gjenopptar når den vises. Manuell venter på Gjenoppta. Aldri lar den fortsette å kjøre.</string>
|
||||
<string name="mode_startup">Oppstart</string>
|
||||
<string name="mode_steam_startup">Start Steam med DroidDeck</string>
|
||||
<string name="mode_steam_startup_hint">Åpner Steam-sesjonen når DroidDeck starter.</string>
|
||||
<string name="mode_startup_session">Åpne ved oppstart</string>
|
||||
<string name="mode_startup_session_hint">Starter denne økten når DroidDeck starter.</string>
|
||||
<string name="mode_decky">Decky</string>
|
||||
<string name="mode_decky_checking">Ser etter utgivelser…</string>
|
||||
<string name="mode_decky_ready">Klar til å installere %1$s.</string>
|
||||
@@ -881,13 +881,9 @@
|
||||
<string name="setup_fullscreen">Fullskjerm</string>
|
||||
<string name="setup_fullscreen_on">Skjuler Androids status- og navigasjonslinjer</string>
|
||||
<string name="setup_fullscreen_off">Viser Androids status- og navigasjonslinjer</string>
|
||||
<string name="setup_dual_screen">Dobbel skjerm</string>
|
||||
<string name="setup_dual_screen_hint">Automatisk bruker den andre skjermen på håndholdte enheter med innebygd skjerm. På bruker enhver annen skjerm. Av holder alt på én skjerm.</string>
|
||||
<string name="setup_dual_screen_auto">Automatisk</string>
|
||||
<string name="setup_dual_screen_on">På</string>
|
||||
<string name="setup_dual_screen_off">Av</string>
|
||||
<string name="setup_dual_screen_menu">Meny på nederste skjerm</string>
|
||||
<string name="setup_dual_screen_menu_hint">Flytter seksjonene og menyen i spillet til den andre skjermen og skriver der, så den øverste skjermen beholder hele visningen. L2 og R2 bytter seksjon.</string>
|
||||
<string name="setup_dual_screen_controls">Kontroller på nederste skjerm i spill</string>
|
||||
<string name="setup_dual_screen_controls_hint">Åpner styreflaten og tastaturet på den andre skjermen når en økt starter.</string>
|
||||
<string name="dual_typing_title">Skriver for den øverste skjermen</string>
|
||||
@@ -1802,8 +1798,6 @@
|
||||
<string name="stats_trace">Gass og brems, siste 10 s</string>
|
||||
<string name="stats_speed_chart">Fart, %1$s</string>
|
||||
<string name="stats_gear">Gir</string>
|
||||
<string name="stats_speed_units">Fartsenheter</string>
|
||||
<string name="stats_speed_units_auto">Automatisk (%1$s)</string>
|
||||
<string name="stats_steam_client_arm">Steam-klient ARM</string>
|
||||
<string name="stats_steam_client_x86">Steam-klient x86</string>
|
||||
<string name="stats_throttle">Gass</string>
|
||||
@@ -1821,7 +1815,6 @@
|
||||
<string name="stats_fuel_t">Drivstoff, t</string>
|
||||
<string name="stats_fuel_main">Hoved %1$s t</string>
|
||||
<string name="stats_fuel_reserve">Reserve %1$s t</string>
|
||||
<string name="stats_tap_hint">Trykk på den øverste stripen eller dashbordet for å endre stilen.</string>
|
||||
<string name="bottom_telemetry_send_title">Send dashborddata</string>
|
||||
<string name="bottom_telemetry_send_auto">Automatisk</string>
|
||||
<string name="bottom_telemetry_send_auto_hint">Slår det på for alle spill som er oppført her, også spill som blir funnet senere. Spill du slår av, forblir av.</string>
|
||||
@@ -1860,7 +1853,6 @@
|
||||
<string name="bottom_telemetry_balance">Saldo</string>
|
||||
<string name="bottom_telemetry_war_thunder">War Thunder</string>
|
||||
<string name="bottom_telemetry_elite">Elite Dangerous</string>
|
||||
<string name="bottom_telemetry_help">Slå på datautgangen i et racingspill (ofte kalt telemetri eller UDP) for å se et direkte dashbord her. Dataene blir på denne enheten.</string>
|
||||
<string name="bottom_telemetry_unknown_format">Port %1$d mottar data (%2$d pakker på %3$d byte), men i et format denne siden ikke kan lese ennå.</string>
|
||||
<string name="bottom_telemetry_port_busy">Et annet program bruker allerede port %1$s, så det spillet kan ikke vises her.</string>
|
||||
|
||||
@@ -3637,4 +3629,26 @@
|
||||
<string name="second_mode_desktop">Skrivebord</string>
|
||||
<string name="bottom_desktop_mode_trackpad">Styreflate</string>
|
||||
<string name="bottom_desktop_title">Skrivebord · %1$s</string>
|
||||
<string name="setup_dual_screen_launcher">Starter på to skjermer</string>
|
||||
<string name="setup_dual_screen_launcher_hint">Automatisk viser starterens seksjoner (Steam, Bibliotek, Butikker og andre) på den innebygde andre skjermen. På bruker enhver andre skjerm. Av holder starteren på én skjerm.</string>
|
||||
<string name="setup_dual_screen_sessions">Økter på to skjermer</string>
|
||||
<string name="setup_dual_screen_sessions_hint">Automatisk viser spillmenyen på den innebygde andre skjermen i stedet for som overlegg. På bruker enhver andre skjerm. Av holder menyen på spillskjermen; du kan fortsatt velge en andre skjerm fra den.</string>
|
||||
<string name="stats_hud_frametime">Bildetid</string>
|
||||
<string name="stats_hud_last_seconds">siste %1$d s</string>
|
||||
<string name="stats_hud_avg_frame">gjennomsnittlig bilde</string>
|
||||
<string name="stats_hud_low" formatted="false">laveste 1 %</string>
|
||||
<string name="stats_hud_slow_frames">bilder over 33 ms</string>
|
||||
<string name="stats_hud_per_minute">%1$d / min</string>
|
||||
<string name="stats_hud_playing_for">Spilletid: %1$s</string>
|
||||
<string name="stats_hud_core_group">%1$d kjerner, opptil %2$s GHz</string>
|
||||
<string name="stats_hud_core_group_unknown">%1$d kjerner</string>
|
||||
<string name="stats_hud_fps_of">FPS ved %1$d Hz</string>
|
||||
<string name="stats_hud_red_mark">Det røde merket viser hvor HUD viser temperaturen i rødt.</string>
|
||||
<string name="stats_hud_whole_device">hele enheten</string>
|
||||
<string name="bottom_hud_group">HUD på nederste skjerm</string>
|
||||
<string name="bottom_strip_tabs">Stripe på hver fane</string>
|
||||
<string name="bottom_strip_lows" formatted="false">laveste 1 % og 0,1 %</string>
|
||||
<string name="bottom_strip_cpu_temp">CPU-temperatur</string>
|
||||
<string name="stats_hud_battery_temp">Batteri</string>
|
||||
<string name="stats_hud_game_cores">Spill</string>
|
||||
</resources>
|
||||
@@ -318,8 +318,8 @@
|
||||
<string name="mode_background_downloads_hint">Stel onderbreken op de achtergrond uit zolang Steam actief downloadt.</string>
|
||||
<string name="mode_suspend_note">Automatisch pauzeert op de achtergrond en hervat zodra de app weer zichtbaar is. Handmatig wacht op Hervatten. Nooit laat alles doorlopen.</string>
|
||||
<string name="mode_startup">Opstarten</string>
|
||||
<string name="mode_steam_startup">Steam starten met DroidDeck</string>
|
||||
<string name="mode_steam_startup_hint">Opent de Steam-sessie als DroidDeck start.</string>
|
||||
<string name="mode_startup_session">Openen bij start</string>
|
||||
<string name="mode_startup_session_hint">Start deze sessie wanneer DroidDeck start.</string>
|
||||
<string name="mode_decky">Decky</string>
|
||||
<string name="mode_decky_checking">Zoeken naar releases…</string>
|
||||
<string name="mode_decky_ready">Klaar om %1$s te installeren.</string>
|
||||
@@ -881,13 +881,9 @@
|
||||
<string name="setup_fullscreen">Volledig scherm</string>
|
||||
<string name="setup_fullscreen_on">Verbergt de statusbalk en navigatiebalk van Android</string>
|
||||
<string name="setup_fullscreen_off">Toont de statusbalk en navigatiebalk van Android</string>
|
||||
<string name="setup_dual_screen">Dubbel scherm</string>
|
||||
<string name="setup_dual_screen_hint">Automatisch gebruikt het tweede scherm op handhelds met een ingebouwd scherm. Aan gebruikt elk tweede scherm. Uit houdt alles op één scherm.</string>
|
||||
<string name="setup_dual_screen_auto">Automatisch</string>
|
||||
<string name="setup_dual_screen_on">Aan</string>
|
||||
<string name="setup_dual_screen_off">Uit</string>
|
||||
<string name="setup_dual_screen_menu">Menu op het onderste scherm</string>
|
||||
<string name="setup_dual_screen_menu_hint">Verplaatst de onderdelen en het menu in het spel naar het tweede scherm en typt daar, zodat het bovenste scherm het volledige beeld houdt. L2 en R2 wisselen van onderdeel.</string>
|
||||
<string name="setup_dual_screen_controls">Bediening op het onderste scherm in games</string>
|
||||
<string name="setup_dual_screen_controls_hint">Opent het trackpad en toetsenbord op het tweede scherm wanneer een sessie start.</string>
|
||||
<string name="dual_typing_title">Typen voor het bovenste scherm</string>
|
||||
@@ -1802,8 +1798,6 @@
|
||||
<string name="stats_trace">Gas en rem, laatste 10 s</string>
|
||||
<string name="stats_speed_chart">Snelheid, %1$s</string>
|
||||
<string name="stats_gear">Versnelling</string>
|
||||
<string name="stats_speed_units">Snelheidseenheid</string>
|
||||
<string name="stats_speed_units_auto">Automatisch (%1$s)</string>
|
||||
<string name="stats_steam_client_arm">Steam-client ARM</string>
|
||||
<string name="stats_steam_client_x86">Steam-client x86</string>
|
||||
<string name="stats_throttle">Gas</string>
|
||||
@@ -1821,7 +1815,6 @@
|
||||
<string name="stats_fuel_t">Brandstof, t</string>
|
||||
<string name="stats_fuel_main">Hoofdtank %1$s t</string>
|
||||
<string name="stats_fuel_reserve">Reserve %1$s t</string>
|
||||
<string name="stats_tap_hint">Tik op de bovenste balk of het dashboard om de stijl te wijzigen.</string>
|
||||
<string name="bottom_telemetry_send_title">Dashboardgegevens verzenden</string>
|
||||
<string name="bottom_telemetry_send_auto">Automatisch</string>
|
||||
<string name="bottom_telemetry_send_auto_hint">Zet het aan voor elke game in deze lijst, ook voor games die later worden gevonden. Games die je uitzet, blijven uit.</string>
|
||||
@@ -1860,7 +1853,6 @@
|
||||
<string name="bottom_telemetry_balance">Saldo</string>
|
||||
<string name="bottom_telemetry_war_thunder">War Thunder</string>
|
||||
<string name="bottom_telemetry_elite">Elite Dangerous</string>
|
||||
<string name="bottom_telemetry_help">Zet de gegevensuitvoer van een racegame aan (vaak telemetrie of UDP genoemd) om hier een live dashboard te zien. De gegevens blijven op dit apparaat.</string>
|
||||
<string name="bottom_telemetry_unknown_format">Poort %1$d ontvangt gegevens (%2$d pakketten van %3$d bytes), maar in een formaat dat deze pagina nog niet kan lezen.</string>
|
||||
<string name="bottom_telemetry_port_busy">Een ander programma gebruikt poort %1$s al, dus die game kan hier niet worden getoond.</string>
|
||||
|
||||
@@ -3637,4 +3629,26 @@
|
||||
<string name="second_mode_desktop">Desktop</string>
|
||||
<string name="bottom_desktop_mode_trackpad">Trackpad</string>
|
||||
<string name="bottom_desktop_title">Desktop · %1$s</string>
|
||||
<string name="setup_dual_screen_launcher">Launcher op twee schermen</string>
|
||||
<string name="setup_dual_screen_launcher_hint">Automatisch zet de launcheronderdelen (Steam, Bibliotheek, Winkels en andere) op het ingebouwde tweede scherm. Aan gebruikt elk tweede scherm. Uit houdt de launcher op één scherm.</string>
|
||||
<string name="setup_dual_screen_sessions">Sessies op twee schermen</string>
|
||||
<string name="setup_dual_screen_sessions_hint">Automatisch zet het spelmenu op het ingebouwde tweede scherm in plaats van als overlay. Aan gebruikt elk tweede scherm. Uit houdt het menu op het spelscherm; daarin kun je nog een tweede scherm kiezen.</string>
|
||||
<string name="stats_hud_frametime">Frametijd</string>
|
||||
<string name="stats_hud_last_seconds">laatste %1$d s</string>
|
||||
<string name="stats_hud_avg_frame">gemiddeld frame</string>
|
||||
<string name="stats_hud_low" formatted="false">laagste 1 %</string>
|
||||
<string name="stats_hud_slow_frames">frames boven 33 ms</string>
|
||||
<string name="stats_hud_per_minute">%1$d / min</string>
|
||||
<string name="stats_hud_playing_for">Speeltijd: %1$s</string>
|
||||
<string name="stats_hud_core_group">%1$d kernen, tot %2$s GHz</string>
|
||||
<string name="stats_hud_core_group_unknown">%1$d kernen</string>
|
||||
<string name="stats_hud_fps_of">FPS bij %1$d Hz</string>
|
||||
<string name="stats_hud_red_mark">De rode markering geeft aan waar de HUD de temperatuur rood toont.</string>
|
||||
<string name="stats_hud_whole_device">hele apparaat</string>
|
||||
<string name="bottom_hud_group">HUD op onderste scherm</string>
|
||||
<string name="bottom_strip_tabs">Strook op elk tabblad</string>
|
||||
<string name="bottom_strip_lows" formatted="false">laagste 1 % en 0,1 %</string>
|
||||
<string name="bottom_strip_cpu_temp">CPU-temperatuur</string>
|
||||
<string name="stats_hud_battery_temp">Accu</string>
|
||||
<string name="stats_hud_game_cores">Spel</string>
|
||||
</resources>
|
||||
@@ -318,8 +318,8 @@
|
||||
<string name="mode_background_downloads_hint">Opóźnia wstrzymanie w tle, gdy Steam aktywnie pobiera.</string>
|
||||
<string name="mode_suspend_note">„Auto” wstrzymuje w tle i wznawia po powrocie. „Ręcznie” czeka na „Wznów”. „Nigdy” nie przerywa działania.</string>
|
||||
<string name="mode_startup">Uruchamianie</string>
|
||||
<string name="mode_steam_startup">Uruchamiaj Steam z DroidDeck</string>
|
||||
<string name="mode_steam_startup_hint">Otwiera sesję Steam przy uruchomieniu DroidDeck.</string>
|
||||
<string name="mode_startup_session">Otwórz przy uruchomieniu</string>
|
||||
<string name="mode_startup_session_hint">Uruchamia tę sesję po uruchomieniu DroidDeck.</string>
|
||||
<string name="mode_decky">Decky</string>
|
||||
<string name="mode_decky_checking">Sprawdzanie wydań…</string>
|
||||
<string name="mode_decky_ready">Gotowe do instalacji: %1$s.</string>
|
||||
@@ -887,13 +887,9 @@
|
||||
<string name="setup_fullscreen">Pełny ekran</string>
|
||||
<string name="setup_fullscreen_on">Ukrywa systemowe paski stanu i nawigacji</string>
|
||||
<string name="setup_fullscreen_off">Pokazuje systemowe paski stanu i nawigacji</string>
|
||||
<string name="setup_dual_screen">Dwa ekrany</string>
|
||||
<string name="setup_dual_screen_hint">Automatycznie używa drugiego ekranu w urządzeniach przenośnych, które mają go wbudowanego. Włączone używa dowolnego drugiego wyświetlacza. Wyłączone zostawia wszystko na jednym ekranie.</string>
|
||||
<string name="setup_dual_screen_auto">Automatycznie</string>
|
||||
<string name="setup_dual_screen_on">Włączone</string>
|
||||
<string name="setup_dual_screen_off">Wyłączone</string>
|
||||
<string name="setup_dual_screen_menu">Menu na dolnym ekranie</string>
|
||||
<string name="setup_dual_screen_menu_hint">Przenosi sekcje i menu w grze na drugi ekran i tam odbywa się pisanie, dzięki czemu górny ekran zachowuje pełny widok. L2 i R2 przełączają sekcje.</string>
|
||||
<string name="setup_dual_screen_controls">Sterowanie na dolnym ekranie w grach</string>
|
||||
<string name="setup_dual_screen_controls_hint">Otwiera gładzik i klawiaturę na drugim ekranie po rozpoczęciu sesji.</string>
|
||||
<string name="dual_typing_title">Pisanie dla górnego ekranu</string>
|
||||
@@ -1846,8 +1842,6 @@
|
||||
<string name="stats_trace">Gaz i hamulec, ostatnie 10 s</string>
|
||||
<string name="stats_speed_chart">Prędkość, %1$s</string>
|
||||
<string name="stats_gear">Bieg</string>
|
||||
<string name="stats_speed_units">Jednostki prędkości</string>
|
||||
<string name="stats_speed_units_auto">Automatycznie (%1$s)</string>
|
||||
<string name="stats_steam_client_arm">Klient Steam ARM</string>
|
||||
<string name="stats_steam_client_x86">Klient Steam x86</string>
|
||||
<string name="stats_throttle">Gaz</string>
|
||||
@@ -1865,7 +1859,6 @@
|
||||
<string name="stats_fuel_t">Paliwo, t</string>
|
||||
<string name="stats_fuel_main">Główne %1$s t</string>
|
||||
<string name="stats_fuel_reserve">Rezerwa %1$s t</string>
|
||||
<string name="stats_tap_hint">Dotknij górnego paska lub zegarów, aby zmienić ich wygląd.</string>
|
||||
<string name="bottom_telemetry_send_title">Wysyłaj dane do panelu</string>
|
||||
<string name="bottom_telemetry_send_auto">Automatycznie</string>
|
||||
<string name="bottom_telemetry_send_auto_hint">Włącza wysyłanie dla każdej gry z tej listy, także dla gier znalezionych później. Gry, które wyłączysz, pozostaną wyłączone.</string>
|
||||
@@ -1904,7 +1897,6 @@
|
||||
<string name="bottom_telemetry_balance">Saldo</string>
|
||||
<string name="bottom_telemetry_war_thunder">War Thunder</string>
|
||||
<string name="bottom_telemetry_elite">Elite Dangerous</string>
|
||||
<string name="bottom_telemetry_help">Włącz wyjście danych w grze wyścigowej (często nazywane telemetrią lub UDP), aby zobaczyć tu zegary na żywo. Dane nie opuszczają tego urządzenia.</string>
|
||||
<string name="bottom_telemetry_unknown_format">Port %1$d odbiera dane (%2$d pakietów po %3$d bajtów), ale w formacie, którego ta strona jeszcze nie odczytuje.</string>
|
||||
<string name="bottom_telemetry_port_busy">Port %1$s jest już używany przez inny program, więc tej gry nie da się tu pokazać.</string>
|
||||
|
||||
@@ -3711,4 +3703,26 @@
|
||||
<string name="second_mode_desktop">Pulpit</string>
|
||||
<string name="bottom_desktop_mode_trackpad">Trackpad</string>
|
||||
<string name="bottom_desktop_title">Pulpit · %1$s</string>
|
||||
<string name="setup_dual_screen_launcher">Launcher na dwóch ekranach</string>
|
||||
<string name="setup_dual_screen_launcher_hint">Automatycznie umieszcza sekcje launchera (Steam, Biblioteka, Sklepy i inne) na wbudowanym drugim ekranie. Włączone używa dowolnego drugiego ekranu. Wyłączone pozostawia launcher na jednym ekranie.</string>
|
||||
<string name="setup_dual_screen_sessions">Sesje na dwóch ekranach</string>
|
||||
<string name="setup_dual_screen_sessions_hint">Automatycznie umieszcza menu gry na wbudowanym drugim ekranie zamiast jako nakładkę. Włączone używa dowolnego drugiego ekranu. Wyłączone pozostawia menu na ekranie gry; nadal można w nim wybrać drugi ekran.</string>
|
||||
<string name="stats_hud_frametime">Czas klatki</string>
|
||||
<string name="stats_hud_last_seconds">ostatnie %1$d s</string>
|
||||
<string name="stats_hud_avg_frame">średnia klatka</string>
|
||||
<string name="stats_hud_low" formatted="false">najniższy 1 %</string>
|
||||
<string name="stats_hud_slow_frames">klatki powyżej 33 ms</string>
|
||||
<string name="stats_hud_per_minute">%1$d / min</string>
|
||||
<string name="stats_hud_playing_for">Czas gry: %1$s</string>
|
||||
<string name="stats_hud_core_group">%1$d rdzeni, do %2$s GHz</string>
|
||||
<string name="stats_hud_core_group_unknown">%1$d rdzeni</string>
|
||||
<string name="stats_hud_fps_of">FPS przy %1$d Hz</string>
|
||||
<string name="stats_hud_red_mark">Czerwony znacznik wskazuje, gdzie HUD wyświetla temperaturę na czerwono.</string>
|
||||
<string name="stats_hud_whole_device">całe urządzenie</string>
|
||||
<string name="bottom_hud_group">HUD dolnego ekranu</string>
|
||||
<string name="bottom_strip_tabs">Pasek na każdej karcie</string>
|
||||
<string name="bottom_strip_lows" formatted="false">najniższe 1 % i 0,1 %</string>
|
||||
<string name="bottom_strip_cpu_temp">Temperatura CPU</string>
|
||||
<string name="stats_hud_battery_temp">Bateria</string>
|
||||
<string name="stats_hud_game_cores">Gra</string>
|
||||
</resources>
|
||||
@@ -318,8 +318,8 @@
|
||||
<string name="mode_background_downloads_hint">Adia a suspensão em segundo plano enquanto o Steam está a transferir.</string>
|
||||
<string name="mode_suspend_note">Automático: pausa em segundo plano e retoma ao voltar a ser apresentada. Manual: aguarda por Retomar. Nunca: continua em execução.</string>
|
||||
<string name="mode_startup">Arranque</string>
|
||||
<string name="mode_steam_startup">Iniciar o Steam com o DroidDeck</string>
|
||||
<string name="mode_steam_startup_hint">Abre a sessão do Steam quando o DroidDeck é iniciado.</string>
|
||||
<string name="mode_startup_session">Abrir ao iniciar</string>
|
||||
<string name="mode_startup_session_hint">Inicia esta sessão quando o DroidDeck é aberto.</string>
|
||||
<string name="mode_decky">Decky</string>
|
||||
<string name="mode_decky_checking">A procurar versões…</string>
|
||||
<string name="mode_decky_ready">Pronto para instalar %1$s.</string>
|
||||
@@ -876,13 +876,9 @@
|
||||
<string name="setup_fullscreen">Ecrã inteiro</string>
|
||||
<string name="setup_fullscreen_on">Oculta as barras de estado e de navegação do Android</string>
|
||||
<string name="setup_fullscreen_off">Mostra as barras de estado e de navegação do Android</string>
|
||||
<string name="setup_dual_screen">Ecrã duplo</string>
|
||||
<string name="setup_dual_screen_hint">Automático usa o segundo ecrã em consolas portáteis com ecrã secundário integrado. Ativado usa qualquer segundo ecrã. Desativado mantém tudo num só ecrã.</string>
|
||||
<string name="setup_dual_screen_auto">Automático</string>
|
||||
<string name="setup_dual_screen_on">Ativado</string>
|
||||
<string name="setup_dual_screen_off">Desativado</string>
|
||||
<string name="setup_dual_screen_menu">Menu no ecrã inferior</string>
|
||||
<string name="setup_dual_screen_menu_hint">Move as secções e o menu do jogo para o segundo ecrã e a escrita é feita lá, para que o ecrã superior mantenha a vista completa. L2 e R2 mudam de secção.</string>
|
||||
<string name="setup_dual_screen_controls">Controlos no ecrã inferior nos jogos</string>
|
||||
<string name="setup_dual_screen_controls_hint">Abre o trackpad e o teclado no segundo ecrã quando uma sessão começa.</string>
|
||||
<string name="dual_typing_title">A escrever para o ecrã superior</string>
|
||||
@@ -1816,8 +1812,6 @@
|
||||
<string name="stats_trace">Acelerador e travão, últimos 10 s</string>
|
||||
<string name="stats_speed_chart">Velocidade, %1$s</string>
|
||||
<string name="stats_gear">Mudança</string>
|
||||
<string name="stats_speed_units">Unidade de velocidade</string>
|
||||
<string name="stats_speed_units_auto">Automático (%1$s)</string>
|
||||
<string name="stats_steam_client_arm">Cliente Steam ARM</string>
|
||||
<string name="stats_steam_client_x86">Cliente Steam x86</string>
|
||||
<string name="stats_throttle">Acelerador</string>
|
||||
@@ -1835,7 +1829,6 @@
|
||||
<string name="stats_fuel_t">Combustível, t</string>
|
||||
<string name="stats_fuel_main">Principal %1$s t</string>
|
||||
<string name="stats_fuel_reserve">Reserva %1$s t</string>
|
||||
<string name="stats_tap_hint">Toque na faixa superior ou no painel para alterar o estilo.</string>
|
||||
<string name="bottom_telemetry_send_title">Enviar dados para o painel</string>
|
||||
<string name="bottom_telemetry_send_auto">Automático</string>
|
||||
<string name="bottom_telemetry_send_auto_hint">Ativa para todos os jogos aqui listados, incluindo os encontrados mais tarde. Os jogos que desativar permanecem desativados.</string>
|
||||
@@ -1874,7 +1867,6 @@
|
||||
<string name="bottom_telemetry_balance">Saldo</string>
|
||||
<string name="bottom_telemetry_war_thunder">War Thunder</string>
|
||||
<string name="bottom_telemetry_elite">Elite Dangerous</string>
|
||||
<string name="bottom_telemetry_help">Ative a saída de dados de um jogo de corridas (muitas vezes chamada telemetria ou UDP) para ver aqui um painel em direto. Os dados ficam neste dispositivo.</string>
|
||||
<string name="bottom_telemetry_unknown_format">A porta %1$d está a receber dados (%2$d pacotes de %3$d bytes), mas num formato que esta página ainda não consegue ler.</string>
|
||||
<string name="bottom_telemetry_port_busy">Outro programa já utiliza a porta %1$s, por isso esse jogo não pode aparecer aqui.</string>
|
||||
|
||||
@@ -3674,4 +3666,26 @@
|
||||
<string name="second_mode_desktop">Ambiente de trabalho</string>
|
||||
<string name="bottom_desktop_mode_trackpad">Trackpad</string>
|
||||
<string name="bottom_desktop_title">Ambiente de trabalho · %1$s</string>
|
||||
<string name="setup_dual_screen_launcher">Iniciador em dois ecrãs</string>
|
||||
<string name="setup_dual_screen_launcher_hint">Automático coloca as secções do iniciador (Steam, Biblioteca, Lojas e outras) no segundo ecrã integrado. Ativado usa qualquer segundo ecrã. Desativado mantém o iniciador num ecrã.</string>
|
||||
<string name="setup_dual_screen_sessions">Sessões em dois ecrãs</string>
|
||||
<string name="setup_dual_screen_sessions_hint">Automático coloca o menu do jogo no segundo ecrã integrado em vez de o sobrepor. Ativado usa qualquer segundo ecrã. Desativado mantém o menu no ecrã do jogo; ainda pode escolher um segundo ecrã nele.</string>
|
||||
<string name="stats_hud_frametime">Tempo de fotograma</string>
|
||||
<string name="stats_hud_last_seconds">últimos %1$d s</string>
|
||||
<string name="stats_hud_avg_frame">fotograma médio</string>
|
||||
<string name="stats_hud_low" formatted="false">1 % mais baixo</string>
|
||||
<string name="stats_hud_slow_frames">fotogramas acima de 33 ms</string>
|
||||
<string name="stats_hud_per_minute">%1$d / min</string>
|
||||
<string name="stats_hud_playing_for">Tempo de jogo: %1$s</string>
|
||||
<string name="stats_hud_core_group">%1$d núcleos, até %2$s GHz</string>
|
||||
<string name="stats_hud_core_group_unknown">%1$d núcleos</string>
|
||||
<string name="stats_hud_fps_of">FPS de %1$d Hz</string>
|
||||
<string name="stats_hud_red_mark">A marca vermelha indica onde o HUD mostra a temperatura a vermelho.</string>
|
||||
<string name="stats_hud_whole_device">dispositivo inteiro</string>
|
||||
<string name="bottom_hud_group">HUD do ecrã inferior</string>
|
||||
<string name="bottom_strip_tabs">Faixa em todos os separadores</string>
|
||||
<string name="bottom_strip_lows" formatted="false">1 % e 0,1 % mais baixos</string>
|
||||
<string name="bottom_strip_cpu_temp">Temperatura da CPU</string>
|
||||
<string name="stats_hud_battery_temp">Bateria</string>
|
||||
<string name="stats_hud_game_cores">Jogo</string>
|
||||
</resources>
|
||||
@@ -318,8 +318,8 @@
|
||||
<string name="mode_background_downloads_hint">Adia a suspensão em segundo plano enquanto a Steam estiver baixando.</string>
|
||||
<string name="mode_suspend_note">Automático pausa em segundo plano e retoma quando o app reaparece. Manual aguarda Retomar. Nunca continua em execução.</string>
|
||||
<string name="mode_startup">Inicialização</string>
|
||||
<string name="mode_steam_startup">Iniciar a Steam com o DroidDeck</string>
|
||||
<string name="mode_steam_startup_hint">Abre a sessão da Steam quando o DroidDeck é iniciado.</string>
|
||||
<string name="mode_startup_session">Abrir ao iniciar</string>
|
||||
<string name="mode_startup_session_hint">Inicia esta sessão quando o DroidDeck é aberto.</string>
|
||||
<string name="mode_decky">Decky</string>
|
||||
<string name="mode_decky_checking">Procurando versões…</string>
|
||||
<string name="mode_decky_ready">Pronto para instalar %1$s.</string>
|
||||
@@ -876,13 +876,9 @@
|
||||
<string name="setup_fullscreen">Tela cheia</string>
|
||||
<string name="setup_fullscreen_on">Oculta as barras de status e de navegação do Android</string>
|
||||
<string name="setup_fullscreen_off">Mostra as barras de status e de navegação do Android</string>
|
||||
<string name="setup_dual_screen">Tela dupla</string>
|
||||
<string name="setup_dual_screen_hint">Automático usa a segunda tela em portáteis que têm uma integrada. Ativado usa qualquer segunda tela. Desativado mantém tudo em uma tela.</string>
|
||||
<string name="setup_dual_screen_auto">Automático</string>
|
||||
<string name="setup_dual_screen_on">Ativado</string>
|
||||
<string name="setup_dual_screen_off">Desativado</string>
|
||||
<string name="setup_dual_screen_menu">Menu na tela inferior</string>
|
||||
<string name="setup_dual_screen_menu_hint">Move as seções e o menu do jogo para a segunda tela e a digitação acontece lá, para que a tela superior mantenha a visão completa. L2 e R2 trocam de seção.</string>
|
||||
<string name="setup_dual_screen_controls">Controles na tela inferior nos jogos</string>
|
||||
<string name="setup_dual_screen_controls_hint">Abre o trackpad e o teclado na segunda tela quando uma sessão começa.</string>
|
||||
<string name="dual_typing_title">Digitando para a tela superior</string>
|
||||
@@ -1816,8 +1812,6 @@
|
||||
<string name="stats_trace">Acelerador e freio, últimos 10 s</string>
|
||||
<string name="stats_speed_chart">Velocidade, %1$s</string>
|
||||
<string name="stats_gear">Marcha</string>
|
||||
<string name="stats_speed_units">Unidade de velocidade</string>
|
||||
<string name="stats_speed_units_auto">Automático (%1$s)</string>
|
||||
<string name="stats_steam_client_arm">Cliente Steam ARM</string>
|
||||
<string name="stats_steam_client_x86">Cliente Steam x86</string>
|
||||
<string name="stats_throttle">Acelerador</string>
|
||||
@@ -1835,7 +1829,6 @@
|
||||
<string name="stats_fuel_t">Combustível, t</string>
|
||||
<string name="stats_fuel_main">Principal %1$s t</string>
|
||||
<string name="stats_fuel_reserve">Reserva %1$s t</string>
|
||||
<string name="stats_tap_hint">Toque na faixa superior ou no painel para mudar o estilo.</string>
|
||||
<string name="bottom_telemetry_send_title">Enviar dados para o painel</string>
|
||||
<string name="bottom_telemetry_send_auto">Automático</string>
|
||||
<string name="bottom_telemetry_send_auto_hint">Ativa para todos os jogos listados aqui, incluindo os encontrados depois. Os jogos que você desativar continuam desativados.</string>
|
||||
@@ -1874,7 +1867,6 @@
|
||||
<string name="bottom_telemetry_balance">Saldo</string>
|
||||
<string name="bottom_telemetry_war_thunder">War Thunder</string>
|
||||
<string name="bottom_telemetry_elite">Elite Dangerous</string>
|
||||
<string name="bottom_telemetry_help">Ative a saída de dados de um jogo de corrida (muitas vezes chamada de telemetria ou UDP) para ver um painel ao vivo aqui. Os dados ficam neste dispositivo.</string>
|
||||
<string name="bottom_telemetry_unknown_format">A porta %1$d está recebendo dados (%2$d pacotes de %3$d bytes), mas em um formato que esta página ainda não consegue ler.</string>
|
||||
<string name="bottom_telemetry_port_busy">Outro programa já usa a porta %1$s, então esse jogo não pode aparecer aqui.</string>
|
||||
|
||||
@@ -3674,4 +3666,26 @@
|
||||
<string name="second_mode_desktop">Área de trabalho</string>
|
||||
<string name="bottom_desktop_mode_trackpad">Trackpad</string>
|
||||
<string name="bottom_desktop_title">Área de trabalho · %1$s</string>
|
||||
<string name="setup_dual_screen_launcher">Iniciador em duas telas</string>
|
||||
<string name="setup_dual_screen_launcher_hint">Automático coloca as seções do iniciador (Steam, Biblioteca, Lojas e outras) na segunda tela integrada. Ativado usa qualquer segunda tela. Desativado mantém o iniciador em uma tela.</string>
|
||||
<string name="setup_dual_screen_sessions">Sessões em duas telas</string>
|
||||
<string name="setup_dual_screen_sessions_hint">Automático coloca o menu do jogo na segunda tela integrada em vez de sobrepô-lo. Ativado usa qualquer segunda tela. Desativado mantém o menu na tela do jogo; ainda é possível escolher uma segunda tela nele.</string>
|
||||
<string name="stats_hud_frametime">Tempo de quadro</string>
|
||||
<string name="stats_hud_last_seconds">últimos %1$d s</string>
|
||||
<string name="stats_hud_avg_frame">quadro médio</string>
|
||||
<string name="stats_hud_low" formatted="false">1 % mais baixo</string>
|
||||
<string name="stats_hud_slow_frames">quadros acima de 33 ms</string>
|
||||
<string name="stats_hud_per_minute">%1$d / min</string>
|
||||
<string name="stats_hud_playing_for">Tempo de jogo: %1$s</string>
|
||||
<string name="stats_hud_core_group">%1$d núcleos, até %2$s GHz</string>
|
||||
<string name="stats_hud_core_group_unknown">%1$d núcleos</string>
|
||||
<string name="stats_hud_fps_of">FPS de %1$d Hz</string>
|
||||
<string name="stats_hud_red_mark">A marca vermelha indica onde o HUD mostra a temperatura em vermelho.</string>
|
||||
<string name="stats_hud_whole_device">dispositivo inteiro</string>
|
||||
<string name="bottom_hud_group">HUD da tela inferior</string>
|
||||
<string name="bottom_strip_tabs">Faixa em todas as abas</string>
|
||||
<string name="bottom_strip_lows" formatted="false">1 % e 0,1 % mais baixos</string>
|
||||
<string name="bottom_strip_cpu_temp">Temperatura da CPU</string>
|
||||
<string name="stats_hud_battery_temp">Bateria</string>
|
||||
<string name="stats_hud_game_cores">Jogo</string>
|
||||
</resources>
|
||||
@@ -318,8 +318,8 @@
|
||||
<string name="mode_background_downloads_hint">Întârzie suspendarea în fundal cât timp Steam descarcă activ.</string>
|
||||
<string name="mode_suspend_note">Automat pune pe pauză în fundal și reia atunci când este afișată. Manual așteaptă Reluare. Niciodată continuă să ruleze.</string>
|
||||
<string name="mode_startup">Pornire</string>
|
||||
<string name="mode_steam_startup">Pornește Steam odată cu DroidDeck</string>
|
||||
<string name="mode_steam_startup_hint">Deschide sesiunea Steam la lansarea DroidDeck.</string>
|
||||
<string name="mode_startup_session">Deschide la pornire</string>
|
||||
<string name="mode_startup_session_hint">Pornește această sesiune când se lansează DroidDeck.</string>
|
||||
<string name="mode_decky">Decky</string>
|
||||
<string name="mode_decky_checking">Se verifică versiunile…</string>
|
||||
<string name="mode_decky_ready">Pregătit pentru instalarea %1$s.</string>
|
||||
@@ -884,13 +884,9 @@
|
||||
<string name="setup_fullscreen">Ecran complet</string>
|
||||
<string name="setup_fullscreen_on">Ascunde barele de stare și de navigare Android</string>
|
||||
<string name="setup_fullscreen_off">Arată barele de stare și de navigare Android</string>
|
||||
<string name="setup_dual_screen">Ecran dublu</string>
|
||||
<string name="setup_dual_screen_hint">Automat folosește al doilea ecran pe consolele portabile care au unul integrat. Pornit folosește orice al doilea afișaj. Oprit păstrează totul pe un singur ecran.</string>
|
||||
<string name="setup_dual_screen_auto">Automat</string>
|
||||
<string name="setup_dual_screen_on">Pornit</string>
|
||||
<string name="setup_dual_screen_off">Oprit</string>
|
||||
<string name="setup_dual_screen_menu">Meniu pe ecranul de jos</string>
|
||||
<string name="setup_dual_screen_menu_hint">Mută secțiunile și meniul din joc pe al doilea ecran și scrierea se face acolo, astfel încât ecranul de sus păstrează imaginea completă. L2 și R2 schimbă secțiunea.</string>
|
||||
<string name="setup_dual_screen_controls">Comenzi pe ecranul de jos în jocuri</string>
|
||||
<string name="setup_dual_screen_controls_hint">Deschide trackpadul și tastatura pe al doilea ecran când pornește o sesiune.</string>
|
||||
<string name="dual_typing_title">Scriere pentru ecranul de sus</string>
|
||||
@@ -1824,8 +1820,6 @@
|
||||
<string name="stats_trace">Accelerație și frână, ultimele 10 s</string>
|
||||
<string name="stats_speed_chart">Viteză, %1$s</string>
|
||||
<string name="stats_gear">Treaptă</string>
|
||||
<string name="stats_speed_units">Unități de viteză</string>
|
||||
<string name="stats_speed_units_auto">Automat (%1$s)</string>
|
||||
<string name="stats_steam_client_arm">Client Steam ARM</string>
|
||||
<string name="stats_steam_client_x86">Client Steam x86</string>
|
||||
<string name="stats_throttle">Accelerație</string>
|
||||
@@ -1843,7 +1837,6 @@
|
||||
<string name="stats_fuel_t">Combustibil, t</string>
|
||||
<string name="stats_fuel_main">Principal %1$s t</string>
|
||||
<string name="stats_fuel_reserve">Rezervă %1$s t</string>
|
||||
<string name="stats_tap_hint">Atinge banda de sus sau bordul pentru a-i schimba stilul.</string>
|
||||
<string name="bottom_telemetry_send_title">Trimite date pentru tabloul de bord</string>
|
||||
<string name="bottom_telemetry_send_auto">Automat</string>
|
||||
<string name="bottom_telemetry_send_auto_hint">Activează trimiterea pentru fiecare joc din listă, inclusiv pentru jocurile găsite mai târziu. Jocurile pe care le dezactivezi rămân dezactivate.</string>
|
||||
@@ -1882,7 +1875,6 @@
|
||||
<string name="bottom_telemetry_balance">Sold</string>
|
||||
<string name="bottom_telemetry_war_thunder">War Thunder</string>
|
||||
<string name="bottom_telemetry_elite">Elite Dangerous</string>
|
||||
<string name="bottom_telemetry_help">Activează ieșirea de date a unui joc de curse (numită adesea telemetrie sau UDP) pentru a vedea aici un bord în timp real. Datele rămân pe acest dispozitiv.</string>
|
||||
<string name="bottom_telemetry_unknown_format">Portul %1$d primește date (%2$d pachete de %3$d octeți), dar într-un format pe care această pagină nu îl poate citi încă.</string>
|
||||
<string name="bottom_telemetry_port_busy">Alt program folosește deja portul %1$s, așa că acel joc nu poate fi afișat aici.</string>
|
||||
|
||||
@@ -3674,4 +3666,26 @@
|
||||
<string name="second_mode_desktop">Desktop</string>
|
||||
<string name="bottom_desktop_mode_trackpad">Trackpad</string>
|
||||
<string name="bottom_desktop_title">Desktop · %1$s</string>
|
||||
<string name="setup_dual_screen_launcher">Lansator pe două ecrane</string>
|
||||
<string name="setup_dual_screen_launcher_hint">Automat pune secțiunile lansatorului (Steam, Bibliotecă, Magazine și altele) pe al doilea ecran încorporat. Activat folosește orice al doilea ecran. Dezactivat păstrează lansatorul pe un singur ecran.</string>
|
||||
<string name="setup_dual_screen_sessions">Sesiuni pe două ecrane</string>
|
||||
<string name="setup_dual_screen_sessions_hint">Automat pune meniul jocului pe al doilea ecran încorporat în loc să îl suprapună. Activat folosește orice al doilea ecran. Dezactivat păstrează meniul pe ecranul jocului; poți alege în continuare un al doilea ecran din meniu.</string>
|
||||
<string name="stats_hud_frametime">Durată cadru</string>
|
||||
<string name="stats_hud_last_seconds">ultimele %1$d s</string>
|
||||
<string name="stats_hud_avg_frame">cadru mediu</string>
|
||||
<string name="stats_hud_low" formatted="false">cele mai mici 1 %</string>
|
||||
<string name="stats_hud_slow_frames">cadre peste 33 ms</string>
|
||||
<string name="stats_hud_per_minute">%1$d / min</string>
|
||||
<string name="stats_hud_playing_for">Timp de joc: %1$s</string>
|
||||
<string name="stats_hud_core_group">%1$d nuclee, până la %2$s GHz</string>
|
||||
<string name="stats_hud_core_group_unknown">%1$d nuclee</string>
|
||||
<string name="stats_hud_fps_of">FPS la %1$d Hz</string>
|
||||
<string name="stats_hud_red_mark">Marcajul roșu indică unde HUD afișează temperatura cu roșu.</string>
|
||||
<string name="stats_hud_whole_device">întregul dispozitiv</string>
|
||||
<string name="bottom_hud_group">HUD pe ecranul inferior</string>
|
||||
<string name="bottom_strip_tabs">Bandă în fiecare filă</string>
|
||||
<string name="bottom_strip_lows" formatted="false">cele mai mici 1 % și 0,1 %</string>
|
||||
<string name="bottom_strip_cpu_temp">Temperatura CPU</string>
|
||||
<string name="stats_hud_battery_temp">Baterie</string>
|
||||
<string name="stats_hud_game_cores">Joc</string>
|
||||
</resources>
|
||||
@@ -318,8 +318,8 @@
|
||||
<string name="mode_background_downloads_hint">Откладывает приостановку в фоне, пока Steam что-то скачивает.</string>
|
||||
<string name="mode_suspend_note">Авто: пауза в фоне и возобновление при показе. Вручную: ждет нажатия «Продолжить». Никогда: сессия продолжает работать.</string>
|
||||
<string name="mode_startup">Запуск</string>
|
||||
<string name="mode_steam_startup">Запускать Steam при открытии DroidDeck</string>
|
||||
<string name="mode_steam_startup_hint">Открывает сессию Steam при запуске DroidDeck.</string>
|
||||
<string name="mode_startup_session">Открывать при запуске</string>
|
||||
<string name="mode_startup_session_hint">Запускает этот сеанс при запуске DroidDeck.</string>
|
||||
<string name="mode_decky">Decky</string>
|
||||
<string name="mode_decky_checking">Проверка выпусков…</string>
|
||||
<string name="mode_decky_ready">Готово к установке %1$s.</string>
|
||||
@@ -887,13 +887,9 @@
|
||||
<string name="setup_fullscreen">Полноэкранный режим</string>
|
||||
<string name="setup_fullscreen_on">Скрывает строку состояния и панель навигации Android</string>
|
||||
<string name="setup_fullscreen_off">Показывает строку состояния и панель навигации Android</string>
|
||||
<string name="setup_dual_screen">Два экрана</string>
|
||||
<string name="setup_dual_screen_hint">Автоматически — использует второй экран на портативных устройствах со встроенным вторым экраном. Вкл. — использует любой второй дисплей. Выкл. — всё остаётся на одном экране.</string>
|
||||
<string name="setup_dual_screen_auto">Автоматически</string>
|
||||
<string name="setup_dual_screen_on">Вкл.</string>
|
||||
<string name="setup_dual_screen_off">Выкл.</string>
|
||||
<string name="setup_dual_screen_menu">Меню на нижнем экране</string>
|
||||
<string name="setup_dual_screen_menu_hint">Переносит разделы и меню в игре на второй экран, и ввод выполняется там, поэтому верхний экран остаётся полностью свободным. L2 и R2 переключают разделы.</string>
|
||||
<string name="setup_dual_screen_controls">Управление на нижнем экране в играх</string>
|
||||
<string name="setup_dual_screen_controls_hint">Открывает тачпад и клавиатуру на втором экране при запуске сеанса.</string>
|
||||
<string name="dual_typing_title">Ввод для верхнего экрана</string>
|
||||
@@ -1846,8 +1842,6 @@
|
||||
<string name="stats_trace">Газ и тормоз за последние 10 s</string>
|
||||
<string name="stats_speed_chart">Скорость, %1$s</string>
|
||||
<string name="stats_gear">Передача</string>
|
||||
<string name="stats_speed_units">Единицы скорости</string>
|
||||
<string name="stats_speed_units_auto">Автоматически (%1$s)</string>
|
||||
<string name="stats_steam_client_arm">Клиент Steam ARM</string>
|
||||
<string name="stats_steam_client_x86">Клиент Steam x86</string>
|
||||
<string name="stats_throttle">Газ</string>
|
||||
@@ -1865,7 +1859,6 @@
|
||||
<string name="stats_fuel_t">Топливо, t</string>
|
||||
<string name="stats_fuel_main">Основное %1$s t</string>
|
||||
<string name="stats_fuel_reserve">Резерв %1$s t</string>
|
||||
<string name="stats_tap_hint">Коснитесь верхней полосы или панели, чтобы сменить ее стиль.</string>
|
||||
<string name="bottom_telemetry_send_title">Передавать данные на дашборд</string>
|
||||
<string name="bottom_telemetry_send_auto">Автоматически</string>
|
||||
<string name="bottom_telemetry_send_auto_hint">Включает передачу для всех игр в этом списке, в том числе найденных позже. Игры, для которых вы ее выключили, остаются выключенными.</string>
|
||||
@@ -1904,7 +1897,6 @@
|
||||
<string name="bottom_telemetry_balance">Баланс</string>
|
||||
<string name="bottom_telemetry_war_thunder">War Thunder</string>
|
||||
<string name="bottom_telemetry_elite">Elite Dangerous</string>
|
||||
<string name="bottom_telemetry_help">Включите вывод данных в гоночной игре (часто он называется телеметрией или UDP), чтобы видеть здесь приборную панель в реальном времени. Данные остаются на этом устройстве.</string>
|
||||
<string name="bottom_telemetry_unknown_format">Порт %1$d получает данные (пакетов: %2$d, размер: %3$d байт), но в формате, который эта страница пока не умеет читать.</string>
|
||||
<string name="bottom_telemetry_port_busy">Порт %1$s уже занят другой программой, поэтому эта игра не может отображаться здесь.</string>
|
||||
|
||||
@@ -3711,4 +3703,26 @@
|
||||
<string name="second_mode_desktop">Рабочий стол</string>
|
||||
<string name="bottom_desktop_mode_trackpad">Тачпад</string>
|
||||
<string name="bottom_desktop_title">Рабочий стол · %1$s</string>
|
||||
<string name="setup_dual_screen_launcher">Лаунчер на двух экранах</string>
|
||||
<string name="setup_dual_screen_launcher_hint">Автоматически размещает разделы лаунчера (Steam, Библиотека, Магазины и другие) на встроенном втором экране. Вкл. использует любой второй экран. Выкл. оставляет лаунчер на одном экране.</string>
|
||||
<string name="setup_dual_screen_sessions">Сеансы на двух экранах</string>
|
||||
<string name="setup_dual_screen_sessions_hint">Автоматически размещает меню игры на встроенном втором экране вместо наложения. Вкл. использует любой второй экран. Выкл. оставляет меню на экране игры; из него по-прежнему можно выбрать второй экран.</string>
|
||||
<string name="stats_hud_frametime">Время кадра</string>
|
||||
<string name="stats_hud_last_seconds">последние %1$d с</string>
|
||||
<string name="stats_hud_avg_frame">средний кадр</string>
|
||||
<string name="stats_hud_low" formatted="false">нижний 1 %</string>
|
||||
<string name="stats_hud_slow_frames">кадры дольше 33 мс</string>
|
||||
<string name="stats_hud_per_minute">%1$d / мин</string>
|
||||
<string name="stats_hud_playing_for">Время игры: %1$s</string>
|
||||
<string name="stats_hud_core_group">%1$d ядер, до %2$s ГГц</string>
|
||||
<string name="stats_hud_core_group_unknown">%1$d ядер</string>
|
||||
<string name="stats_hud_fps_of">FPS при %1$d Гц</string>
|
||||
<string name="stats_hud_red_mark">Красная отметка указывает, где HUD показывает температуру красным.</string>
|
||||
<string name="stats_hud_whole_device">всё устройство</string>
|
||||
<string name="bottom_hud_group">HUD нижнего экрана</string>
|
||||
<string name="bottom_strip_tabs">Полоса на каждой вкладке</string>
|
||||
<string name="bottom_strip_lows" formatted="false">нижние 1 % и 0,1 %</string>
|
||||
<string name="bottom_strip_cpu_temp">Температура ЦП</string>
|
||||
<string name="stats_hud_battery_temp">Батарея</string>
|
||||
<string name="stats_hud_game_cores">Игра</string>
|
||||
</resources>
|
||||
@@ -318,8 +318,8 @@
|
||||
<string name="mode_background_downloads_hint">Skjut upp bakgrundspausning medan Steam aktivt laddar ned.</string>
|
||||
<string name="mode_suspend_note">Auto pausar i bakgrunden och återupptar när den visas. Manuell väntar på Återuppta. Aldrig fortsätter köra.</string>
|
||||
<string name="mode_startup">Uppstart</string>
|
||||
<string name="mode_steam_startup">Starta Steam med DroidDeck</string>
|
||||
<string name="mode_steam_startup_hint">Öppnar Steam-sessionen när DroidDeck startar.</string>
|
||||
<string name="mode_startup_session">Öppna vid start</string>
|
||||
<string name="mode_startup_session_hint">Startar den här sessionen när DroidDeck startar.</string>
|
||||
<string name="mode_decky">Decky</string>
|
||||
<string name="mode_decky_checking">Söker efter utgåvor…</string>
|
||||
<string name="mode_decky_ready">Redo att installera %1$s.</string>
|
||||
@@ -881,13 +881,9 @@
|
||||
<string name="setup_fullscreen">Helskärm</string>
|
||||
<string name="setup_fullscreen_on">Döljer Androids statusfält och navigeringsfält</string>
|
||||
<string name="setup_fullscreen_off">Visar Androids statusfält och navigeringsfält</string>
|
||||
<string name="setup_dual_screen">Dubbelskärm</string>
|
||||
<string name="setup_dual_screen_hint">Automatiskt använder den andra skärmen på handhållna enheter med en inbyggd andra skärm. På använder vilken andra skärm som helst. Av håller allt på en skärm.</string>
|
||||
<string name="setup_dual_screen_auto">Automatiskt</string>
|
||||
<string name="setup_dual_screen_on">På</string>
|
||||
<string name="setup_dual_screen_off">Av</string>
|
||||
<string name="setup_dual_screen_menu">Meny på nedre skärmen</string>
|
||||
<string name="setup_dual_screen_menu_hint">Flyttar avsnitten och menyn i spelet till den andra skärmen och skrivandet sker där, så att den övre skärmen behåller hela vyn. L2 och R2 byter avsnitt.</string>
|
||||
<string name="setup_dual_screen_controls">Kontroller på nedre skärmen i spel</string>
|
||||
<string name="setup_dual_screen_controls_hint">Öppnar styrplattan och tangentbordet på den andra skärmen när en session startar.</string>
|
||||
<string name="dual_typing_title">Skriver för den övre skärmen</string>
|
||||
@@ -1802,8 +1798,6 @@
|
||||
<string name="stats_trace">Gas och broms, senaste 10 s</string>
|
||||
<string name="stats_speed_chart">Hastighet, %1$s</string>
|
||||
<string name="stats_gear">Växel</string>
|
||||
<string name="stats_speed_units">Hastighetsenhet</string>
|
||||
<string name="stats_speed_units_auto">Automatisk (%1$s)</string>
|
||||
<string name="stats_steam_client_arm">Steam-klient ARM</string>
|
||||
<string name="stats_steam_client_x86">Steam-klient x86</string>
|
||||
<string name="stats_throttle">Gas</string>
|
||||
@@ -1821,7 +1815,6 @@
|
||||
<string name="stats_fuel_t">Bränsle, t</string>
|
||||
<string name="stats_fuel_main">Huvudtank %1$s t</string>
|
||||
<string name="stats_fuel_reserve">Reserv %1$s t</string>
|
||||
<string name="stats_tap_hint">Tryck på den övre remsan eller instrumentpanelen för att byta stil.</string>
|
||||
<string name="bottom_telemetry_send_title">Skicka data till instrumentpanelen</string>
|
||||
<string name="bottom_telemetry_send_auto">Automatiskt</string>
|
||||
<string name="bottom_telemetry_send_auto_hint">Slår på det för alla spel i listan, även spel som hittas senare. Spel som du stänger av förblir avstängda.</string>
|
||||
@@ -1860,7 +1853,6 @@
|
||||
<string name="bottom_telemetry_balance">Saldo</string>
|
||||
<string name="bottom_telemetry_war_thunder">War Thunder</string>
|
||||
<string name="bottom_telemetry_elite">Elite Dangerous</string>
|
||||
<string name="bottom_telemetry_help">Slå på datautmatning i ett racingspel (kallas ofta telemetri eller UDP) för att se en liveinstrumentpanel här. Datan stannar på den här enheten.</string>
|
||||
<string name="bottom_telemetry_unknown_format">Port %1$d tar emot data (%2$d paket på %3$d byte), men i ett format som den här sidan inte kan läsa än.</string>
|
||||
<string name="bottom_telemetry_port_busy">Ett annat program använder redan port %1$s, så det spelet kan inte visas här.</string>
|
||||
|
||||
@@ -3637,4 +3629,26 @@
|
||||
<string name="second_mode_desktop">Skrivbord</string>
|
||||
<string name="bottom_desktop_mode_trackpad">Styrplatta</string>
|
||||
<string name="bottom_desktop_title">Skrivbord · %1$s</string>
|
||||
<string name="setup_dual_screen_launcher">Startare på två skärmar</string>
|
||||
<string name="setup_dual_screen_launcher_hint">Automatiskt visar startarens avsnitt (Steam, Bibliotek, Butiker och andra) på den inbyggda andra skärmen. På använder valfri andra skärm. Av håller startaren på en skärm.</string>
|
||||
<string name="setup_dual_screen_sessions">Sessioner på två skärmar</string>
|
||||
<string name="setup_dual_screen_sessions_hint">Automatiskt visar spelmenyn på den inbyggda andra skärmen i stället för som överlägg. På använder valfri andra skärm. Av håller menyn på spelskärmen; du kan fortfarande välja en andra skärm därifrån.</string>
|
||||
<string name="stats_hud_frametime">Bildrutetid</string>
|
||||
<string name="stats_hud_last_seconds">senaste %1$d s</string>
|
||||
<string name="stats_hud_avg_frame">genomsnittlig bildruta</string>
|
||||
<string name="stats_hud_low" formatted="false">lägsta 1 %</string>
|
||||
<string name="stats_hud_slow_frames">bildrutor över 33 ms</string>
|
||||
<string name="stats_hud_per_minute">%1$d / min</string>
|
||||
<string name="stats_hud_playing_for">Speltid: %1$s</string>
|
||||
<string name="stats_hud_core_group">%1$d kärnor, upp till %2$s GHz</string>
|
||||
<string name="stats_hud_core_group_unknown">%1$d kärnor</string>
|
||||
<string name="stats_hud_fps_of">FPS vid %1$d Hz</string>
|
||||
<string name="stats_hud_red_mark">Den röda markeringen visar var HUD visar temperaturen i rött.</string>
|
||||
<string name="stats_hud_whole_device">hela enheten</string>
|
||||
<string name="bottom_hud_group">HUD på nedre skärmen</string>
|
||||
<string name="bottom_strip_tabs">Remsa på varje flik</string>
|
||||
<string name="bottom_strip_lows" formatted="false">lägsta 1 % och 0,1 %</string>
|
||||
<string name="bottom_strip_cpu_temp">CPU-temperatur</string>
|
||||
<string name="stats_hud_battery_temp">Batteri</string>
|
||||
<string name="stats_hud_game_cores">Spel</string>
|
||||
</resources>
|
||||
@@ -318,8 +318,8 @@
|
||||
<string name="mode_background_downloads_hint">เลื่อนการหยุดพื้นหลังชั่วคราวออกไปขณะ Steam กำลังดาวน์โหลดอยู่</string>
|
||||
<string name="mode_suspend_note">อัตโนมัติจะหยุดชั่วคราวเมื่ออยู่ในพื้นหลังและดำเนินการต่อเมื่อแสดงผล กำหนดเองรอการกดดำเนินการต่อ ไม่มีวันทำงานต่อเนื่องตลอด</string>
|
||||
<string name="mode_startup">การเริ่มทำงาน</string>
|
||||
<string name="mode_steam_startup">เริ่ม Steam พร้อมกับ DroidDeck</string>
|
||||
<string name="mode_steam_startup_hint">เปิดเซสชัน Steam เมื่อ DroidDeck เริ่มทำงาน</string>
|
||||
<string name="mode_startup_session">เปิดเมื่อเริ่มต้น</string>
|
||||
<string name="mode_startup_session_hint">เริ่มเซสชันนี้เมื่อเปิด DroidDeck</string>
|
||||
<string name="mode_decky">Decky</string>
|
||||
<string name="mode_decky_checking">กำลังค้นหารุ่นที่เผยแพร่…</string>
|
||||
<string name="mode_decky_ready">พร้อมติดตั้ง %1$s</string>
|
||||
@@ -878,13 +878,9 @@
|
||||
<string name="setup_fullscreen">เต็มจอ</string>
|
||||
<string name="setup_fullscreen_on">ซ่อนแถบสถานะและแถบนำทางของ Android</string>
|
||||
<string name="setup_fullscreen_off">แสดงแถบสถานะและแถบนำทางของ Android</string>
|
||||
<string name="setup_dual_screen">หน้าจอคู่</string>
|
||||
<string name="setup_dual_screen_hint">อัตโนมัติจะใช้หน้าจอที่สองบนเครื่องพกพาที่มีหน้าจอที่สองในตัว เปิดจะใช้หน้าจอที่สองใดก็ได้ ปิดจะแสดงทุกอย่างบนหน้าจอเดียว</string>
|
||||
<string name="setup_dual_screen_auto">อัตโนมัติ</string>
|
||||
<string name="setup_dual_screen_on">เปิด</string>
|
||||
<string name="setup_dual_screen_off">ปิด</string>
|
||||
<string name="setup_dual_screen_menu">เมนูหน้าจอล่าง</string>
|
||||
<string name="setup_dual_screen_menu_hint">ย้ายส่วนต่าง ๆ และเมนูระหว่างเล่นเกมไปที่หน้าจอที่สองและพิมพ์ที่นั่น เพื่อให้หน้าจอบนแสดงเต็มพื้นที่ L2 และ R2 ใช้สลับส่วน</string>
|
||||
<string name="setup_dual_screen_controls">ปุ่มควบคุมหน้าจอล่างในเกม</string>
|
||||
<string name="setup_dual_screen_controls_hint">เปิดแทร็กแพดและแป้นพิมพ์บนหน้าจอที่สองเมื่อเริ่มเซสชัน</string>
|
||||
<string name="dual_typing_title">กำลังพิมพ์ให้หน้าจอบน</string>
|
||||
@@ -1780,8 +1776,6 @@
|
||||
<string name="stats_trace">คันเร่งและเบรก 10 s ล่าสุด</string>
|
||||
<string name="stats_speed_chart">ความเร็ว, %1$s</string>
|
||||
<string name="stats_gear">เกียร์</string>
|
||||
<string name="stats_speed_units">หน่วยความเร็ว</string>
|
||||
<string name="stats_speed_units_auto">อัตโนมัติ (%1$s)</string>
|
||||
<string name="stats_steam_client_arm">ไคลเอนต์ Steam ARM</string>
|
||||
<string name="stats_steam_client_x86">ไคลเอนต์ Steam x86</string>
|
||||
<string name="stats_throttle">คันเร่ง</string>
|
||||
@@ -1799,7 +1793,6 @@
|
||||
<string name="stats_fuel_t">เชื้อเพลิง, t</string>
|
||||
<string name="stats_fuel_main">หลัก %1$s t</string>
|
||||
<string name="stats_fuel_reserve">สำรอง %1$s t</string>
|
||||
<string name="stats_tap_hint">แตะแถบด้านบนหรือหน้าปัดเพื่อเปลี่ยนรูปแบบ</string>
|
||||
<string name="bottom_telemetry_send_title">ส่งข้อมูลแดชบอร์ด</string>
|
||||
<string name="bottom_telemetry_send_auto">อัตโนมัติ</string>
|
||||
<string name="bottom_telemetry_send_auto_hint">เปิดใช้สำหรับทุกเกมที่แสดงที่นี่ รวมถึงเกมที่พบในภายหลัง เกมที่คุณปิดไว้จะยังคงปิดอยู่</string>
|
||||
@@ -1838,7 +1831,6 @@
|
||||
<string name="bottom_telemetry_balance">ยอดเงิน</string>
|
||||
<string name="bottom_telemetry_war_thunder">War Thunder</string>
|
||||
<string name="bottom_telemetry_elite">Elite Dangerous</string>
|
||||
<string name="bottom_telemetry_help">เปิดการส่งออกข้อมูลของเกมแข่งรถ (มักเรียกว่าเทเลเมทรีหรือ UDP) เพื่อดูหน้าปัดแบบสดที่นี่ ข้อมูลจะอยู่บนอุปกรณ์นี้เท่านั้น</string>
|
||||
<string name="bottom_telemetry_unknown_format">พอร์ต %1$d ได้รับข้อมูล (%2$d แพ็กเก็ต ขนาด %3$d ไบต์) แต่อยู่ในรูปแบบที่หน้านี้ยังอ่านไม่ได้</string>
|
||||
<string name="bottom_telemetry_port_busy">มีโปรแกรมอื่นใช้พอร์ต %1$s อยู่แล้ว เกมนั้นจึงแสดงที่นี่ไม่ได้</string>
|
||||
|
||||
@@ -3609,4 +3601,26 @@
|
||||
<string name="second_mode_desktop">เดสก์ท็อป</string>
|
||||
<string name="bottom_desktop_mode_trackpad">แทร็กแพด</string>
|
||||
<string name="bottom_desktop_title">เดสก์ท็อป · %1$s</string>
|
||||
<string name="setup_dual_screen_launcher">ตัวเปิดใช้สองหน้าจอ</string>
|
||||
<string name="setup_dual_screen_launcher_hint">อัตโนมัติจะแสดงส่วนต่าง ๆ ของตัวเปิดใช้ (Steam คลัง ร้านค้า และอื่น ๆ) บนหน้าจอที่สองในตัว เปิดใช้หน้าจอที่สองใดก็ได้ ปิดแสดงตัวเปิดใช้บนหน้าจอเดียว</string>
|
||||
<string name="setup_dual_screen_sessions">เซสชันสองหน้าจอ</string>
|
||||
<string name="setup_dual_screen_sessions_hint">อัตโนมัติจะแสดงเมนูเกมบนหน้าจอที่สองในตัวแทนการแสดงทับ เปิดใช้หน้าจอที่สองใดก็ได้ ปิดแสดงเมนูบนหน้าจอเกม แต่ยังเลือกหน้าจอที่สองจากเมนูได้</string>
|
||||
<string name="stats_hud_frametime">เวลาเฟรม</string>
|
||||
<string name="stats_hud_last_seconds">%1$d วินาทีล่าสุด</string>
|
||||
<string name="stats_hud_avg_frame">เฟรมเฉลี่ย</string>
|
||||
<string name="stats_hud_low" formatted="false">1 % ต่ำสุด</string>
|
||||
<string name="stats_hud_slow_frames">เฟรมนานกว่า 33 ms</string>
|
||||
<string name="stats_hud_per_minute">%1$d / นาที</string>
|
||||
<string name="stats_hud_playing_for">เวลาเล่น: %1$s</string>
|
||||
<string name="stats_hud_core_group">%1$d คอร์ สูงสุด %2$s GHz</string>
|
||||
<string name="stats_hud_core_group_unknown">%1$d คอร์</string>
|
||||
<string name="stats_hud_fps_of">FPS ที่ %1$d Hz</string>
|
||||
<string name="stats_hud_red_mark">เครื่องหมายสีแดงระบุจุดที่ HUD แสดงอุณหภูมิเป็นสีแดง</string>
|
||||
<string name="stats_hud_whole_device">ทั้งอุปกรณ์</string>
|
||||
<string name="bottom_hud_group">HUD หน้าจอล่าง</string>
|
||||
<string name="bottom_strip_tabs">แถบในทุกแท็บ</string>
|
||||
<string name="bottom_strip_lows" formatted="false">1 % และ 0.1 % ต่ำสุด</string>
|
||||
<string name="bottom_strip_cpu_temp">อุณหภูมิ CPU</string>
|
||||
<string name="stats_hud_battery_temp">แบตเตอรี่</string>
|
||||
<string name="stats_hud_game_cores">เกม</string>
|
||||
</resources>
|
||||
@@ -318,8 +318,8 @@
|
||||
<string name="mode_background_downloads_hint">Steam etkin olarak indirme yaparken arka planda askıya almayı erteler.</string>
|
||||
<string name="mode_suspend_note">Otomatik: arka planda duraklatır, gösterilince devam eder. Manuel: Devam et\'i bekler. Asla: çalışmaya devam eder.</string>
|
||||
<string name="mode_startup">Başlangıç</string>
|
||||
<string name="mode_steam_startup">Steam\'i DroidDeck ile başlat</string>
|
||||
<string name="mode_steam_startup_hint">DroidDeck açıldığında Steam oturumunu açar.</string>
|
||||
<string name="mode_startup_session">Açılışta başlat</string>
|
||||
<string name="mode_startup_session_hint">DroidDeck açıldığında bu oturumu başlatır.</string>
|
||||
<string name="mode_decky">Decky</string>
|
||||
<string name="mode_decky_checking">Sürümler kontrol ediliyor…</string>
|
||||
<string name="mode_decky_ready">%1$s yüklenmeye hazır.</string>
|
||||
@@ -873,13 +873,9 @@
|
||||
<string name="setup_fullscreen">Tam ekran</string>
|
||||
<string name="setup_fullscreen_on">Android durum ve gezinme çubuklarını gizler</string>
|
||||
<string name="setup_fullscreen_off">Android durum ve gezinme çubuklarını gösterir</string>
|
||||
<string name="setup_dual_screen">Çift ekran</string>
|
||||
<string name="setup_dual_screen_hint">Otomatik, dahili ikinci ekranı olan el konsollarında ikinci ekranı kullanır. Açık, herhangi bir ikinci ekranı kullanır. Kapalı, her şeyi tek ekranda tutar.</string>
|
||||
<string name="setup_dual_screen_auto">Otomatik</string>
|
||||
<string name="setup_dual_screen_on">Açık</string>
|
||||
<string name="setup_dual_screen_off">Kapalı</string>
|
||||
<string name="setup_dual_screen_menu">Alt ekran menüsü</string>
|
||||
<string name="setup_dual_screen_menu_hint">Bölümleri ve oyun içi menüyü ikinci ekrana taşır ve yazma orada yapılır, böylece üst ekran tam görünümü korur. L2 ve R2 bölümler arasında geçiş yapar.</string>
|
||||
<string name="setup_dual_screen_controls">Oyunlarda alt ekran kontrolleri</string>
|
||||
<string name="setup_dual_screen_controls_hint">Bir oturum başladığında ikinci ekranda dokunmatik yüzeyi ve klavyeyi açar.</string>
|
||||
<string name="dual_typing_title">Üst ekran için yazılıyor</string>
|
||||
@@ -1794,8 +1790,6 @@
|
||||
<string name="stats_trace">Gaz ve fren, son 10 s</string>
|
||||
<string name="stats_speed_chart">Hız, %1$s</string>
|
||||
<string name="stats_gear">Vites</string>
|
||||
<string name="stats_speed_units">Hız birimi</string>
|
||||
<string name="stats_speed_units_auto">Otomatik (%1$s)</string>
|
||||
<string name="stats_steam_client_arm">Steam İstemcisi ARM</string>
|
||||
<string name="stats_steam_client_x86">Steam İstemcisi x86</string>
|
||||
<string name="stats_throttle">Gaz</string>
|
||||
@@ -1813,7 +1807,6 @@
|
||||
<string name="stats_fuel_t">Yakıt, t</string>
|
||||
<string name="stats_fuel_main">Ana %1$s t</string>
|
||||
<string name="stats_fuel_reserve">Yedek %1$s t</string>
|
||||
<string name="stats_tap_hint">Stilini değiştirmek için üst şeride veya göstergeye dokunun.</string>
|
||||
<string name="bottom_telemetry_send_title">Gösterge verilerini gönder</string>
|
||||
<string name="bottom_telemetry_send_auto">Otomatik</string>
|
||||
<string name="bottom_telemetry_send_auto_hint">Sonradan bulunanlar dahil, burada listelenen her oyun için açar. Kapattığınız oyunlar kapalı kalır.</string>
|
||||
@@ -1852,7 +1845,6 @@
|
||||
<string name="bottom_telemetry_balance">Bakiye</string>
|
||||
<string name="bottom_telemetry_war_thunder">War Thunder</string>
|
||||
<string name="bottom_telemetry_elite">Elite Dangerous</string>
|
||||
<string name="bottom_telemetry_help">Burada canlı bir gösterge görmek için yarış oyununun veri çıkışını (genellikle telemetri veya UDP olarak geçer) açın. Veriler bu cihazda kalır.</string>
|
||||
<string name="bottom_telemetry_unknown_format">%1$d portu veri alıyor (%3$d baytlık %2$d paket), ancak bu sayfanın henüz okuyamadığı bir biçimde.</string>
|
||||
<string name="bottom_telemetry_port_busy">%1$s portunu zaten başka bir program kullanıyor, bu yüzden o oyun burada gösterilemez.</string>
|
||||
|
||||
@@ -3637,4 +3629,26 @@
|
||||
<string name="second_mode_desktop">Masaüstü</string>
|
||||
<string name="bottom_desktop_mode_trackpad">Dokunmatik yüzey</string>
|
||||
<string name="bottom_desktop_title">Masaüstü · %1$s</string>
|
||||
<string name="setup_dual_screen_launcher">Çift ekran başlatıcı</string>
|
||||
<string name="setup_dual_screen_launcher_hint">Otomatik, başlatıcı bölümlerini (Steam, Kitaplık, Mağazalar ve diğerleri) yerleşik ikinci ekrana koyar. Açık, herhangi bir ikinci ekranı kullanır. Kapalı, başlatıcıyı tek ekranda tutar.</string>
|
||||
<string name="setup_dual_screen_sessions">Çift ekran oturumlar</string>
|
||||
<string name="setup_dual_screen_sessions_hint">Otomatik, oyun menüsünü üst üste bindirmek yerine yerleşik ikinci ekrana yerleştirir. Açık, herhangi bir ikinci ekranı kullanır. Kapalı, menüyü oyun ekranında tutar; menüden yine ikinci bir ekran seçebilirsiniz.</string>
|
||||
<string name="stats_hud_frametime">Kare süresi</string>
|
||||
<string name="stats_hud_last_seconds">son %1$d sn</string>
|
||||
<string name="stats_hud_avg_frame">ortalama kare</string>
|
||||
<string name="stats_hud_low" formatted="false">en düşük %1</string>
|
||||
<string name="stats_hud_slow_frames">33 ms üzeri kareler</string>
|
||||
<string name="stats_hud_per_minute">%1$d / dk</string>
|
||||
<string name="stats_hud_playing_for">Oyun süresi: %1$s</string>
|
||||
<string name="stats_hud_core_group">%1$d çekirdek, en fazla %2$s GHz</string>
|
||||
<string name="stats_hud_core_group_unknown">%1$d çekirdek</string>
|
||||
<string name="stats_hud_fps_of">%1$d Hz için FPS</string>
|
||||
<string name="stats_hud_red_mark">Kırmızı işaret, HUD’nin sıcaklığı kırmızı gösterdiği yeri belirtir.</string>
|
||||
<string name="stats_hud_whole_device">tüm cihaz</string>
|
||||
<string name="bottom_hud_group">Alt ekran HUD</string>
|
||||
<string name="bottom_strip_tabs">Her sekmede şerit</string>
|
||||
<string name="bottom_strip_lows" formatted="false">en düşük %1 ve %0,1</string>
|
||||
<string name="bottom_strip_cpu_temp">CPU sıcaklığı</string>
|
||||
<string name="stats_hud_battery_temp">Pil</string>
|
||||
<string name="stats_hud_game_cores">Oyun</string>
|
||||
</resources>
|
||||
@@ -318,8 +318,8 @@
|
||||
<string name="mode_background_downloads_hint">Відкладає призупинення у фоні, поки Steam активно щось завантажує.</string>
|
||||
<string name="mode_suspend_note">«Авто» призупиняє сеанс у фоні й відновлює, коли додаток знову на екрані. «Вручну» чекає на «Відновити». «Ніколи» — сеанс працює далі.</string>
|
||||
<string name="mode_startup">Запуск</string>
|
||||
<string name="mode_steam_startup">Запускати Steam разом із DroidDeck</string>
|
||||
<string name="mode_steam_startup_hint">Відкриває сеанс Steam під час запуску DroidDeck.</string>
|
||||
<string name="mode_startup_session">Відкривати під час запуску</string>
|
||||
<string name="mode_startup_session_hint">Запускає цей сеанс під час запуску DroidDeck.</string>
|
||||
<string name="mode_decky">Decky</string>
|
||||
<string name="mode_decky_checking">Пошук випусків…</string>
|
||||
<string name="mode_decky_ready">Можна встановити %1$s.</string>
|
||||
@@ -887,13 +887,9 @@
|
||||
<string name="setup_fullscreen">Повний екран</string>
|
||||
<string name="setup_fullscreen_on">Приховує рядок стану та панель навігації Android</string>
|
||||
<string name="setup_fullscreen_off">Показує рядок стану та панель навігації Android</string>
|
||||
<string name="setup_dual_screen">Два екрани</string>
|
||||
<string name="setup_dual_screen_hint">Автоматично — використовує другий екран на портативних пристроях із вбудованим другим екраном. Увімк. — використовує будь-який другий дисплей. Вимк. — усе лишається на одному екрані.</string>
|
||||
<string name="setup_dual_screen_auto">Автоматично</string>
|
||||
<string name="setup_dual_screen_on">Увімк.</string>
|
||||
<string name="setup_dual_screen_off">Вимк.</string>
|
||||
<string name="setup_dual_screen_menu">Меню на нижньому екрані</string>
|
||||
<string name="setup_dual_screen_menu_hint">Переносить розділи та меню в грі на другий екран, і введення відбувається там, тож верхній екран лишається повністю вільним. L2 і R2 перемикають розділи.</string>
|
||||
<string name="setup_dual_screen_controls">Керування на нижньому екрані в іграх</string>
|
||||
<string name="setup_dual_screen_controls_hint">Відкриває тачпад і клавіатуру на другому екрані під час запуску сеансу.</string>
|
||||
<string name="dual_typing_title">Введення для верхнього екрана</string>
|
||||
@@ -1846,8 +1842,6 @@
|
||||
<string name="stats_trace">Газ і гальмо, останні 10 s</string>
|
||||
<string name="stats_speed_chart">Швидкість, %1$s</string>
|
||||
<string name="stats_gear">Передача</string>
|
||||
<string name="stats_speed_units">Одиниці швидкості</string>
|
||||
<string name="stats_speed_units_auto">Автоматично (%1$s)</string>
|
||||
<string name="stats_steam_client_arm">Клієнт Steam ARM</string>
|
||||
<string name="stats_steam_client_x86">Клієнт Steam x86</string>
|
||||
<string name="stats_throttle">Газ</string>
|
||||
@@ -1865,7 +1859,6 @@
|
||||
<string name="stats_fuel_t">Пальне, t</string>
|
||||
<string name="stats_fuel_main">Основне %1$s t</string>
|
||||
<string name="stats_fuel_reserve">Резерв %1$s t</string>
|
||||
<string name="stats_tap_hint">Торкніться верхньої смуги або панелі, щоб змінити її стиль.</string>
|
||||
<string name="bottom_telemetry_send_title">Надсилати дані для панелі</string>
|
||||
<string name="bottom_telemetry_send_auto">Автоматично</string>
|
||||
<string name="bottom_telemetry_send_auto_hint">Вмикає це для кожної гри в цьому списку, зокрема для знайдених пізніше. Ігри, які ви вимкнете, лишаються вимкненими.</string>
|
||||
@@ -1904,7 +1897,6 @@
|
||||
<string name="bottom_telemetry_balance">Баланс</string>
|
||||
<string name="bottom_telemetry_war_thunder">War Thunder</string>
|
||||
<string name="bottom_telemetry_elite">Elite Dangerous</string>
|
||||
<string name="bottom_telemetry_help">Увімкніть виведення даних у гоночній грі (часто це називається телеметрією або UDP), щоб бачити тут живу панель. Дані лишаються на цьому пристрої.</string>
|
||||
<string name="bottom_telemetry_unknown_format">Порт %1$d отримує дані (пакетів: %2$d, по %3$d байт), але у форматі, який ця сторінка ще не вміє читати.</string>
|
||||
<string name="bottom_telemetry_port_busy">Порт %1$s уже використовує інша програма, тому ця гра не може відображатися тут.</string>
|
||||
|
||||
@@ -3711,4 +3703,26 @@
|
||||
<string name="second_mode_desktop">Робочий стіл</string>
|
||||
<string name="bottom_desktop_mode_trackpad">Трекпад</string>
|
||||
<string name="bottom_desktop_title">Робочий стіл · %1$s</string>
|
||||
<string name="setup_dual_screen_launcher">Лаунчер на двох екранах</string>
|
||||
<string name="setup_dual_screen_launcher_hint">Автоматично розміщує розділи лаунчера (Steam, Бібліотека, Крамниці та інші) на вбудованому другому екрані. Увімкнено використовує будь-який другий екран. Вимкнено залишає лаунчер на одному екрані.</string>
|
||||
<string name="setup_dual_screen_sessions">Сеанси на двох екранах</string>
|
||||
<string name="setup_dual_screen_sessions_hint">Автоматично розміщує меню гри на вбудованому другому екрані замість накладання. Увімкнено використовує будь-який другий екран. Вимкнено залишає меню на екрані гри; з нього й далі можна вибрати другий екран.</string>
|
||||
<string name="stats_hud_frametime">Час кадру</string>
|
||||
<string name="stats_hud_last_seconds">останні %1$d с</string>
|
||||
<string name="stats_hud_avg_frame">середній кадр</string>
|
||||
<string name="stats_hud_low" formatted="false">нижній 1 %</string>
|
||||
<string name="stats_hud_slow_frames">кадри понад 33 мс</string>
|
||||
<string name="stats_hud_per_minute">%1$d / хв</string>
|
||||
<string name="stats_hud_playing_for">Час гри: %1$s</string>
|
||||
<string name="stats_hud_core_group">%1$d ядер, до %2$s ГГц</string>
|
||||
<string name="stats_hud_core_group_unknown">%1$d ядер</string>
|
||||
<string name="stats_hud_fps_of">FPS за %1$d Гц</string>
|
||||
<string name="stats_hud_red_mark">Червона позначка вказує, де HUD показує температуру червоним.</string>
|
||||
<string name="stats_hud_whole_device">увесь пристрій</string>
|
||||
<string name="bottom_hud_group">HUD нижнього екрана</string>
|
||||
<string name="bottom_strip_tabs">Смуга на кожній вкладці</string>
|
||||
<string name="bottom_strip_lows" formatted="false">нижні 1 % і 0,1 %</string>
|
||||
<string name="bottom_strip_cpu_temp">Температура ЦП</string>
|
||||
<string name="stats_hud_battery_temp">Батарея</string>
|
||||
<string name="stats_hud_game_cores">Гра</string>
|
||||
</resources>
|
||||
@@ -318,8 +318,8 @@
|
||||
<string name="mode_background_downloads_hint">Trì hoãn tạm dừng nền khi Steam đang tải xuống.</string>
|
||||
<string name="mode_suspend_note">Tự động tạm dừng ở nền và tiếp tục khi hiển thị lại. Thủ công chờ nhấn Tiếp tục. Không bao giờ giữ nó luôn chạy.</string>
|
||||
<string name="mode_startup">Khởi động</string>
|
||||
<string name="mode_steam_startup">Khởi động Steam cùng DroidDeck</string>
|
||||
<string name="mode_steam_startup_hint">Mở phiên Steam khi DroidDeck khởi chạy.</string>
|
||||
<string name="mode_startup_session">Mở khi khởi chạy</string>
|
||||
<string name="mode_startup_session_hint">Bắt đầu phiên này khi DroidDeck khởi chạy.</string>
|
||||
<string name="mode_decky">Decky</string>
|
||||
<string name="mode_decky_checking">Đang kiểm tra bản phát hành…</string>
|
||||
<string name="mode_decky_ready">Sẵn sàng cài %1$s.</string>
|
||||
@@ -878,13 +878,9 @@
|
||||
<string name="setup_fullscreen">Toàn màn hình</string>
|
||||
<string name="setup_fullscreen_on">Ẩn thanh trạng thái và thanh điều hướng của Android</string>
|
||||
<string name="setup_fullscreen_off">Hiện thanh trạng thái và thanh điều hướng của Android</string>
|
||||
<string name="setup_dual_screen">Màn hình kép</string>
|
||||
<string name="setup_dual_screen_hint">Tự động dùng màn hình thứ hai trên máy cầm tay có màn hình thứ hai tích hợp. Bật dùng bất kỳ màn hình thứ hai nào. Tắt giữ mọi thứ trên một màn hình.</string>
|
||||
<string name="setup_dual_screen_auto">Tự động</string>
|
||||
<string name="setup_dual_screen_on">Bật</string>
|
||||
<string name="setup_dual_screen_off">Tắt</string>
|
||||
<string name="setup_dual_screen_menu">Menu ở màn hình dưới</string>
|
||||
<string name="setup_dual_screen_menu_hint">Chuyển các mục và menu trong trò chơi sang màn hình thứ hai và nhập liệu ở đó, để màn hình trên giữ toàn bộ khung nhìn. L2 và R2 chuyển mục.</string>
|
||||
<string name="setup_dual_screen_controls">Điều khiển màn hình dưới khi chơi game</string>
|
||||
<string name="setup_dual_screen_controls_hint">Mở bàn di chuột và bàn phím trên màn hình thứ hai khi phiên bắt đầu.</string>
|
||||
<string name="dual_typing_title">Đang nhập cho màn hình trên</string>
|
||||
@@ -1780,8 +1776,6 @@
|
||||
<string name="stats_trace">Ga và phanh, 10 s gần nhất</string>
|
||||
<string name="stats_speed_chart">Tốc độ, %1$s</string>
|
||||
<string name="stats_gear">Số</string>
|
||||
<string name="stats_speed_units">Đơn vị tốc độ</string>
|
||||
<string name="stats_speed_units_auto">Tự động (%1$s)</string>
|
||||
<string name="stats_steam_client_arm">Client Steam ARM</string>
|
||||
<string name="stats_steam_client_x86">Client Steam x86</string>
|
||||
<string name="stats_throttle">Ga</string>
|
||||
@@ -1799,7 +1793,6 @@
|
||||
<string name="stats_fuel_t">Nhiên liệu, t</string>
|
||||
<string name="stats_fuel_main">Chính %1$s t</string>
|
||||
<string name="stats_fuel_reserve">Dự trữ %1$s t</string>
|
||||
<string name="stats_tap_hint">Nhấn vào dải phía trên hoặc bảng đồng hồ để đổi kiểu hiển thị.</string>
|
||||
<string name="bottom_telemetry_send_title">Gửi dữ liệu bảng đồng hồ</string>
|
||||
<string name="bottom_telemetry_send_auto">Tự động</string>
|
||||
<string name="bottom_telemetry_send_auto_hint">Bật cho mọi game trong danh sách này, kể cả game được tìm thấy sau này. Game bạn đã tắt sẽ vẫn tắt.</string>
|
||||
@@ -1838,7 +1831,6 @@
|
||||
<string name="bottom_telemetry_balance">Số dư</string>
|
||||
<string name="bottom_telemetry_war_thunder">War Thunder</string>
|
||||
<string name="bottom_telemetry_elite">Elite Dangerous</string>
|
||||
<string name="bottom_telemetry_help">Bật xuất dữ liệu của game đua xe (thường gọi là telemetry hoặc UDP) để xem bảng đồng hồ trực tiếp ở đây. Dữ liệu chỉ ở trên thiết bị này.</string>
|
||||
<string name="bottom_telemetry_unknown_format">Cổng %1$d đang nhận dữ liệu (%2$d gói, mỗi gói %3$d byte), nhưng ở định dạng mà trang này chưa đọc được.</string>
|
||||
<string name="bottom_telemetry_port_busy">Một chương trình khác đang dùng cổng %1$s, nên game đó không thể hiển thị ở đây.</string>
|
||||
|
||||
@@ -3609,4 +3601,26 @@
|
||||
<string name="second_mode_desktop">Desktop</string>
|
||||
<string name="bottom_desktop_mode_trackpad">Trackpad</string>
|
||||
<string name="bottom_desktop_title">Desktop · %1$s</string>
|
||||
<string name="setup_dual_screen_launcher">Trình khởi chạy hai màn hình</string>
|
||||
<string name="setup_dual_screen_launcher_hint">Tự động đặt các mục của trình khởi chạy (Steam, Thư viện, Cửa hàng và các mục khác) trên màn hình phụ tích hợp. Bật dùng bất kỳ màn hình phụ nào. Tắt giữ trình khởi chạy trên một màn hình.</string>
|
||||
<string name="setup_dual_screen_sessions">Phiên hai màn hình</string>
|
||||
<string name="setup_dual_screen_sessions_hint">Tự động đặt menu trò chơi trên màn hình phụ tích hợp thay vì phủ lên trò chơi. Bật dùng bất kỳ màn hình phụ nào. Tắt giữ menu trên màn hình trò chơi; bạn vẫn có thể chọn màn hình phụ từ menu.</string>
|
||||
<string name="stats_hud_frametime">Thời gian khung hình</string>
|
||||
<string name="stats_hud_last_seconds">%1$d giây gần nhất</string>
|
||||
<string name="stats_hud_avg_frame">khung hình trung bình</string>
|
||||
<string name="stats_hud_low" formatted="false">1 % thấp nhất</string>
|
||||
<string name="stats_hud_slow_frames">khung hình trên 33 ms</string>
|
||||
<string name="stats_hud_per_minute">%1$d / phút</string>
|
||||
<string name="stats_hud_playing_for">Thời gian chơi: %1$s</string>
|
||||
<string name="stats_hud_core_group">%1$d lõi, tối đa %2$s GHz</string>
|
||||
<string name="stats_hud_core_group_unknown">%1$d lõi</string>
|
||||
<string name="stats_hud_fps_of">FPS ở %1$d Hz</string>
|
||||
<string name="stats_hud_red_mark">Vạch đỏ cho biết nơi HUD hiển thị nhiệt độ bằng màu đỏ.</string>
|
||||
<string name="stats_hud_whole_device">toàn bộ thiết bị</string>
|
||||
<string name="bottom_hud_group">HUD màn hình dưới</string>
|
||||
<string name="bottom_strip_tabs">Dải trên mọi thẻ</string>
|
||||
<string name="bottom_strip_lows" formatted="false">1 % và 0,1 % thấp nhất</string>
|
||||
<string name="bottom_strip_cpu_temp">Nhiệt độ CPU</string>
|
||||
<string name="stats_hud_battery_temp">Pin</string>
|
||||
<string name="stats_hud_game_cores">Trò chơi</string>
|
||||
</resources>
|
||||
@@ -318,8 +318,8 @@
|
||||
<string name="mode_background_downloads_hint">Steam 正在下载时推迟后台暂停。</string>
|
||||
<string name="mode_suspend_note">自动会在后台暂停并在显示时恢复。手动会等待点按恢复。“从不”则保持运行。</string>
|
||||
<string name="mode_startup">启动</string>
|
||||
<string name="mode_steam_startup">随 DroidDeck 启动 Steam</string>
|
||||
<string name="mode_steam_startup_hint">启动 DroidDeck 时打开 Steam 会话。</string>
|
||||
<string name="mode_startup_session">启动时打开</string>
|
||||
<string name="mode_startup_session_hint">DroidDeck 启动时开始此会话。</string>
|
||||
<string name="mode_decky">Decky</string>
|
||||
<string name="mode_decky_checking">正在检查发布版本…</string>
|
||||
<string name="mode_decky_ready">已就绪,可安装 %1$s。</string>
|
||||
@@ -878,13 +878,9 @@
|
||||
<string name="setup_fullscreen">全屏</string>
|
||||
<string name="setup_fullscreen_on">隐藏 Android 状态栏和导航栏</string>
|
||||
<string name="setup_fullscreen_off">显示 Android 状态栏和导航栏</string>
|
||||
<string name="setup_dual_screen">双屏</string>
|
||||
<string name="setup_dual_screen_hint">自动:在内置第二块屏幕的掌机上使用该屏幕。开启:使用任何第二块显示器。关闭:所有内容只显示在一块屏幕上。</string>
|
||||
<string name="setup_dual_screen_auto">自动</string>
|
||||
<string name="setup_dual_screen_on">开启</string>
|
||||
<string name="setup_dual_screen_off">关闭</string>
|
||||
<string name="setup_dual_screen_menu">下屏菜单</string>
|
||||
<string name="setup_dual_screen_menu_hint">将各个分区和游戏内菜单移到第二块屏幕并在那里输入,让上屏保持完整画面。L2 和 R2 切换分区。</string>
|
||||
<string name="setup_dual_screen_controls">游戏中的下屏控制</string>
|
||||
<string name="setup_dual_screen_controls_hint">会话开始时在第二块屏幕上打开触控板和键盘。</string>
|
||||
<string name="dual_typing_title">正在为上屏输入</string>
|
||||
@@ -1857,8 +1853,6 @@
|
||||
<string name="stats_trace">油门和刹车,最近 10 s</string>
|
||||
<string name="stats_speed_chart">速度,%1$s</string>
|
||||
<string name="stats_gear">挡位</string>
|
||||
<string name="stats_speed_units">速度单位</string>
|
||||
<string name="stats_speed_units_auto">自动(%1$s)</string>
|
||||
<string name="stats_steam_client_arm">Steam 客户端 ARM</string>
|
||||
<string name="stats_steam_client_x86">Steam 客户端 x86</string>
|
||||
<string name="stats_throttle">油门</string>
|
||||
@@ -1876,7 +1870,6 @@
|
||||
<string name="stats_fuel_t">燃料,t</string>
|
||||
<string name="stats_fuel_main">主油箱 %1$s t</string>
|
||||
<string name="stats_fuel_reserve">备用 %1$s t</string>
|
||||
<string name="stats_tap_hint">点按顶部横条或仪表可更改其样式。</string>
|
||||
<string name="bottom_telemetry_send_title">发送仪表盘数据</string>
|
||||
<string name="bottom_telemetry_send_auto">自动</string>
|
||||
<string name="bottom_telemetry_send_auto_hint">为此处列出的所有游戏开启,包括之后发现的游戏。你手动关闭的游戏会保持关闭。</string>
|
||||
@@ -1915,7 +1908,6 @@
|
||||
<string name="bottom_telemetry_balance">余额</string>
|
||||
<string name="bottom_telemetry_war_thunder">War Thunder</string>
|
||||
<string name="bottom_telemetry_elite">Elite Dangerous</string>
|
||||
<string name="bottom_telemetry_help">开启赛车游戏的数据输出(通常称为遥测或 UDP),即可在此查看实时仪表。数据仅保留在此设备上。</string>
|
||||
<string name="bottom_telemetry_unknown_format">端口 %1$d 正在接收数据(%2$d 个 %3$d 字节的数据包),但其格式本页面暂时无法读取。</string>
|
||||
<string name="bottom_telemetry_port_busy">端口 %1$s 已被其他程序占用,因此该游戏无法在此显示。</string>
|
||||
|
||||
@@ -3609,4 +3601,26 @@
|
||||
<string name="second_mode_desktop">桌面</string>
|
||||
<string name="bottom_desktop_mode_trackpad">触控板</string>
|
||||
<string name="bottom_desktop_title">桌面 · %1$s</string>
|
||||
<string name="setup_dual_screen_launcher">双屏启动器</string>
|
||||
<string name="setup_dual_screen_launcher_hint">自动会将启动器各分区(Steam、库、商店等)放到内置副屏。开启会使用任意副屏。关闭会将启动器保留在单个屏幕上。</string>
|
||||
<string name="setup_dual_screen_sessions">双屏会话</string>
|
||||
<string name="setup_dual_screen_sessions_hint">自动会将游戏菜单放到内置副屏,而不是叠加显示。开启会使用任意副屏。关闭会将菜单保留在游戏屏幕上;你仍可从菜单选择副屏。</string>
|
||||
<string name="stats_hud_frametime">帧时间</string>
|
||||
<string name="stats_hud_last_seconds">最近 %1$d 秒</string>
|
||||
<string name="stats_hud_avg_frame">平均帧</string>
|
||||
<string name="stats_hud_low" formatted="false">最低 1 %</string>
|
||||
<string name="stats_hud_slow_frames">超过 33 ms 的帧</string>
|
||||
<string name="stats_hud_per_minute">%1$d / 分钟</string>
|
||||
<string name="stats_hud_playing_for">游玩时间:%1$s</string>
|
||||
<string name="stats_hud_core_group">%1$d 核,最高 %2$s GHz</string>
|
||||
<string name="stats_hud_core_group_unknown">%1$d 核</string>
|
||||
<string name="stats_hud_fps_of">%1$d Hz 下的 FPS</string>
|
||||
<string name="stats_hud_red_mark">红色标记表示 HUD 将温度显示为红色的位置。</string>
|
||||
<string name="stats_hud_whole_device">整个设备</string>
|
||||
<string name="bottom_hud_group">下屏 HUD</string>
|
||||
<string name="bottom_strip_tabs">所有标签页显示条栏</string>
|
||||
<string name="bottom_strip_lows" formatted="false">最低 1 % 和 0.1 %</string>
|
||||
<string name="bottom_strip_cpu_temp">CPU 温度</string>
|
||||
<string name="stats_hud_battery_temp">电池</string>
|
||||
<string name="stats_hud_game_cores">游戏</string>
|
||||
</resources>
|
||||
@@ -318,8 +318,8 @@
|
||||
<string name="mode_background_downloads_hint">Steam 正在下載時,延遲在背景暫停。</string>
|
||||
<string name="mode_suspend_note">「自動」會在背景時暫停,並在重新顯示時繼續;「手動」會等你按「繼續」;「從不」會保持執行。</string>
|
||||
<string name="mode_startup">啟動</string>
|
||||
<string name="mode_steam_startup">隨 DroidDeck 啟動 Steam</string>
|
||||
<string name="mode_steam_startup_hint">DroidDeck 啟動時開啟 Steam 會話。</string>
|
||||
<string name="mode_startup_session">啟動時開啟</string>
|
||||
<string name="mode_startup_session_hint">DroidDeck 啟動時開始此工作階段。</string>
|
||||
<string name="mode_decky">Decky</string>
|
||||
<string name="mode_decky_checking">正在檢查發佈版本…</string>
|
||||
<string name="mode_decky_ready">可安裝 %1$s。</string>
|
||||
@@ -878,13 +878,9 @@
|
||||
<string name="setup_fullscreen">全螢幕</string>
|
||||
<string name="setup_fullscreen_on">隱藏 Android 狀態列和導覽列</string>
|
||||
<string name="setup_fullscreen_off">顯示 Android 狀態列和導覽列</string>
|
||||
<string name="setup_dual_screen">雙屏</string>
|
||||
<string name="setup_dual_screen_hint">自動:在內置第二塊螢幕的掌機上使用該螢幕。開啟:使用任何第二塊顯示器。關閉:所有內容只顯示在一塊螢幕上。</string>
|
||||
<string name="setup_dual_screen_auto">自動</string>
|
||||
<string name="setup_dual_screen_on">開啟</string>
|
||||
<string name="setup_dual_screen_off">關閉</string>
|
||||
<string name="setup_dual_screen_menu">下屏選單</string>
|
||||
<string name="setup_dual_screen_menu_hint">將各個分區和遊戲內選單移到第二塊螢幕並在那裡輸入,讓上屏保持完整畫面。L2 和 R2 切換分區。</string>
|
||||
<string name="setup_dual_screen_controls">遊戲中的下屏控制</string>
|
||||
<string name="setup_dual_screen_controls_hint">工作階段開始時在第二塊螢幕上開啟觸控板和鍵盤。</string>
|
||||
<string name="dual_typing_title">正在為上屏輸入</string>
|
||||
@@ -1780,8 +1776,6 @@
|
||||
<string name="stats_trace">油門和剎車,最近 10 s</string>
|
||||
<string name="stats_speed_chart">速度,%1$s</string>
|
||||
<string name="stats_gear">檔位</string>
|
||||
<string name="stats_speed_units">速度單位</string>
|
||||
<string name="stats_speed_units_auto">自動(%1$s)</string>
|
||||
<string name="stats_steam_client_arm">Steam 用戶端 ARM</string>
|
||||
<string name="stats_steam_client_x86">Steam 用戶端 x86</string>
|
||||
<string name="stats_throttle">油門</string>
|
||||
@@ -1799,7 +1793,6 @@
|
||||
<string name="stats_fuel_t">燃料,t</string>
|
||||
<string name="stats_fuel_main">主油箱 %1$s t</string>
|
||||
<string name="stats_fuel_reserve">備用 %1$s t</string>
|
||||
<string name="stats_tap_hint">點按頂部橫條或儀表即可變更其樣式。</string>
|
||||
<string name="bottom_telemetry_send_title">傳送儀表板資料</string>
|
||||
<string name="bottom_telemetry_send_auto">自動</string>
|
||||
<string name="bottom_telemetry_send_auto_hint">為這裡列出的所有遊戲開啟,包括之後找到的遊戲。你關閉的遊戲會維持關閉。</string>
|
||||
@@ -1838,7 +1831,6 @@
|
||||
<string name="bottom_telemetry_balance">餘額</string>
|
||||
<string name="bottom_telemetry_war_thunder">War Thunder</string>
|
||||
<string name="bottom_telemetry_elite">Elite Dangerous</string>
|
||||
<string name="bottom_telemetry_help">開啟賽車遊戲的資料輸出(通常稱為遙測或 UDP),即可在此查看即時儀表。資料只會留在這部裝置上。</string>
|
||||
<string name="bottom_telemetry_unknown_format">連接埠 %1$d 正在接收資料(%2$d 個 %3$d 位元組的封包),但此頁面目前還無法讀取這種格式。</string>
|
||||
<string name="bottom_telemetry_port_busy">其他程式已在使用連接埠 %1$s,因此該遊戲無法在此顯示。</string>
|
||||
|
||||
@@ -3609,4 +3601,26 @@
|
||||
<string name="second_mode_desktop">桌面</string>
|
||||
<string name="bottom_desktop_mode_trackpad">觸控板</string>
|
||||
<string name="bottom_desktop_title">桌面 · %1$s</string>
|
||||
<string name="setup_dual_screen_launcher">雙螢幕啟動器</string>
|
||||
<string name="setup_dual_screen_launcher_hint">自動會將啟動器各分區(Steam、收藏庫、商店等)放到內置副螢幕。開啟會使用任意副螢幕。關閉會將啟動器保留在單一螢幕上。</string>
|
||||
<string name="setup_dual_screen_sessions">雙螢幕工作階段</string>
|
||||
<string name="setup_dual_screen_sessions_hint">自動會將遊戲選單放到內置副螢幕,而不是疊加顯示。開啟會使用任意副螢幕。關閉會將選單保留在遊戲螢幕上;你仍可從選單選擇副螢幕。</string>
|
||||
<string name="stats_hud_frametime">影格時間</string>
|
||||
<string name="stats_hud_last_seconds">最近 %1$d 秒</string>
|
||||
<string name="stats_hud_avg_frame">平均影格</string>
|
||||
<string name="stats_hud_low" formatted="false">最低 1 %</string>
|
||||
<string name="stats_hud_slow_frames">超過 33 ms 的影格</string>
|
||||
<string name="stats_hud_per_minute">%1$d / 分鐘</string>
|
||||
<string name="stats_hud_playing_for">遊玩時間:%1$s</string>
|
||||
<string name="stats_hud_core_group">%1$d 核心,最高 %2$s GHz</string>
|
||||
<string name="stats_hud_core_group_unknown">%1$d 核心</string>
|
||||
<string name="stats_hud_fps_of">%1$d Hz 下的 FPS</string>
|
||||
<string name="stats_hud_red_mark">紅色標記表示 HUD 將溫度顯示為紅色的位置。</string>
|
||||
<string name="stats_hud_whole_device">整個裝置</string>
|
||||
<string name="bottom_hud_group">下螢幕 HUD</string>
|
||||
<string name="bottom_strip_tabs">所有分頁顯示條列</string>
|
||||
<string name="bottom_strip_lows" formatted="false">最低 1 % 和 0.1 %</string>
|
||||
<string name="bottom_strip_cpu_temp">CPU 溫度</string>
|
||||
<string name="stats_hud_battery_temp">電池</string>
|
||||
<string name="stats_hud_game_cores">遊戲</string>
|
||||
</resources>
|
||||
@@ -318,8 +318,8 @@
|
||||
<string name="mode_background_downloads_hint">Steam 正在下載時,延後在背景暫停。</string>
|
||||
<string name="mode_suspend_note">「自動」會在背景時暫停,並在重新顯示時繼續。「手動」會等你點擊「繼續」。「從不」則會持續執行。</string>
|
||||
<string name="mode_startup">啟動</string>
|
||||
<string name="mode_steam_startup">隨 DroidDeck 啟動 Steam</string>
|
||||
<string name="mode_steam_startup_hint">DroidDeck 啟動時開啟 Steam 工作階段。</string>
|
||||
<string name="mode_startup_session">啟動時開啟</string>
|
||||
<string name="mode_startup_session_hint">DroidDeck 啟動時開始此工作階段。</string>
|
||||
<string name="mode_decky">Decky</string>
|
||||
<string name="mode_decky_checking">正在檢查發行版本…</string>
|
||||
<string name="mode_decky_ready">已準備好安裝 %1$s。</string>
|
||||
@@ -878,13 +878,9 @@
|
||||
<string name="setup_fullscreen">全螢幕</string>
|
||||
<string name="setup_fullscreen_on">隱藏 Android 狀態列和導覽列</string>
|
||||
<string name="setup_fullscreen_off">顯示 Android 狀態列和導覽列</string>
|
||||
<string name="setup_dual_screen">雙螢幕</string>
|
||||
<string name="setup_dual_screen_hint">自動:在內建第二個螢幕的掌機上使用該螢幕。開啟:使用任何第二個顯示器。關閉:所有內容只顯示在一個螢幕上。</string>
|
||||
<string name="setup_dual_screen_auto">自動</string>
|
||||
<string name="setup_dual_screen_on">開啟</string>
|
||||
<string name="setup_dual_screen_off">關閉</string>
|
||||
<string name="setup_dual_screen_menu">下螢幕選單</string>
|
||||
<string name="setup_dual_screen_menu_hint">將各個區塊和遊戲內選單移到第二個螢幕並在那裡輸入,讓上螢幕保持完整畫面。L2 和 R2 切換區塊。</string>
|
||||
<string name="setup_dual_screen_controls">遊戲中的下螢幕控制</string>
|
||||
<string name="setup_dual_screen_controls_hint">工作階段開始時在第二個螢幕上開啟觸控板和鍵盤。</string>
|
||||
<string name="dual_typing_title">正在為上螢幕輸入</string>
|
||||
@@ -1780,8 +1776,6 @@
|
||||
<string name="stats_trace">油門和煞車,最近 10 s</string>
|
||||
<string name="stats_speed_chart">速度,%1$s</string>
|
||||
<string name="stats_gear">檔位</string>
|
||||
<string name="stats_speed_units">速度單位</string>
|
||||
<string name="stats_speed_units_auto">自動(%1$s)</string>
|
||||
<string name="stats_steam_client_arm">Steam 用戶端 ARM</string>
|
||||
<string name="stats_steam_client_x86">Steam 用戶端 x86</string>
|
||||
<string name="stats_throttle">油門</string>
|
||||
@@ -1799,7 +1793,6 @@
|
||||
<string name="stats_fuel_t">燃料,t</string>
|
||||
<string name="stats_fuel_main">主油箱 %1$s t</string>
|
||||
<string name="stats_fuel_reserve">備用 %1$s t</string>
|
||||
<string name="stats_tap_hint">點按頂部橫條或儀表即可變更其樣式。</string>
|
||||
<string name="bottom_telemetry_send_title">傳送儀表板資料</string>
|
||||
<string name="bottom_telemetry_send_auto">自動</string>
|
||||
<string name="bottom_telemetry_send_auto_hint">為這裡列出的所有遊戲開啟,包括之後找到的遊戲。你關閉的遊戲會維持關閉。</string>
|
||||
@@ -1838,7 +1831,6 @@
|
||||
<string name="bottom_telemetry_balance">餘額</string>
|
||||
<string name="bottom_telemetry_war_thunder">War Thunder</string>
|
||||
<string name="bottom_telemetry_elite">Elite Dangerous</string>
|
||||
<string name="bottom_telemetry_help">開啟賽車遊戲的資料輸出(通常稱為遙測或 UDP),即可在此查看即時儀表。資料只會留在這部裝置上。</string>
|
||||
<string name="bottom_telemetry_unknown_format">連接埠 %1$d 正在接收資料(%2$d 個 %3$d 位元組的封包),但此頁面目前還無法讀取這種格式。</string>
|
||||
<string name="bottom_telemetry_port_busy">其他程式已在使用連接埠 %1$s,因此該遊戲無法在此顯示。</string>
|
||||
|
||||
@@ -3609,4 +3601,26 @@
|
||||
<string name="second_mode_desktop">桌面</string>
|
||||
<string name="bottom_desktop_mode_trackpad">觸控板</string>
|
||||
<string name="bottom_desktop_title">桌面 · %1$s</string>
|
||||
<string name="setup_dual_screen_launcher">雙螢幕啟動器</string>
|
||||
<string name="setup_dual_screen_launcher_hint">自動會將啟動器各分區(Steam、收藏庫、商店等)放到內建副螢幕。開啟會使用任意副螢幕。關閉會將啟動器保留在單一螢幕上。</string>
|
||||
<string name="setup_dual_screen_sessions">雙螢幕工作階段</string>
|
||||
<string name="setup_dual_screen_sessions_hint">自動會將遊戲選單放到內建副螢幕,而不是疊加顯示。開啟會使用任意副螢幕。關閉會將選單保留在遊戲螢幕上;你仍可從選單選擇副螢幕。</string>
|
||||
<string name="stats_hud_frametime">影格時間</string>
|
||||
<string name="stats_hud_last_seconds">最近 %1$d 秒</string>
|
||||
<string name="stats_hud_avg_frame">平均影格</string>
|
||||
<string name="stats_hud_low" formatted="false">最低 1 %</string>
|
||||
<string name="stats_hud_slow_frames">超過 33 ms 的影格</string>
|
||||
<string name="stats_hud_per_minute">%1$d / 分鐘</string>
|
||||
<string name="stats_hud_playing_for">遊玩時間:%1$s</string>
|
||||
<string name="stats_hud_core_group">%1$d 核心,最高 %2$s GHz</string>
|
||||
<string name="stats_hud_core_group_unknown">%1$d 核心</string>
|
||||
<string name="stats_hud_fps_of">%1$d Hz 下的 FPS</string>
|
||||
<string name="stats_hud_red_mark">紅色標記表示 HUD 將溫度顯示為紅色的位置。</string>
|
||||
<string name="stats_hud_whole_device">整個裝置</string>
|
||||
<string name="bottom_hud_group">下螢幕 HUD</string>
|
||||
<string name="bottom_strip_tabs">所有分頁顯示條列</string>
|
||||
<string name="bottom_strip_lows" formatted="false">最低 1 % 和 0.1 %</string>
|
||||
<string name="bottom_strip_cpu_temp">CPU 溫度</string>
|
||||
<string name="stats_hud_battery_temp">電池</string>
|
||||
<string name="stats_hud_game_cores">遊戲</string>
|
||||
</resources>
|
||||
@@ -342,8 +342,8 @@
|
||||
<string name="mode_background_downloads_hint">Delay background suspension while Steam is actively downloading.</string>
|
||||
<string name="mode_suspend_note">Auto pauses in the background and resumes when shown. Manual waits for Resume. Never keeps running.</string>
|
||||
<string name="mode_startup">Startup</string>
|
||||
<string name="mode_steam_startup">Start Steam with DroidDeck</string>
|
||||
<string name="mode_steam_startup_hint">Opens the Steam session when DroidDeck launches.</string>
|
||||
<string name="mode_startup_session">Open at launch</string>
|
||||
<string name="mode_startup_session_hint">Starts this session when DroidDeck launches.</string>
|
||||
<string name="mode_decky">Decky</string>
|
||||
<string name="mode_decky_checking">Checking for releases…</string>
|
||||
<string name="mode_decky_ready">Ready to install %1$s.</string>
|
||||
@@ -942,13 +942,13 @@
|
||||
<string name="setup_fullscreen">Fullscreen</string>
|
||||
<string name="setup_fullscreen_on">Hides the Android status and navigation bars</string>
|
||||
<string name="setup_fullscreen_off">Shows the Android status and navigation bars</string>
|
||||
<string name="setup_dual_screen">Dual screen</string>
|
||||
<string name="setup_dual_screen_hint">Automatic uses the second screen on handhelds with a built-in one. On uses any second display. Off keeps the launcher and games on one screen; you can still pick a second screen from the session menu.</string>
|
||||
<string name="setup_dual_screen_launcher">Dual screen launcher</string>
|
||||
<string name="setup_dual_screen_launcher_hint">Automatic puts the launcher sections (Steam, Library, Stores and the rest) on the second screen of handhelds with a built-in one. On uses any second display. Off keeps the launcher on one screen.</string>
|
||||
<string name="setup_dual_screen_sessions">Dual screen sessions</string>
|
||||
<string name="setup_dual_screen_sessions_hint">Automatic docks the in-game menu on the built-in second screen instead of a pop-over. On uses any second display. Off keeps the menu on the game screen; you can still pick a second screen from it.</string>
|
||||
<string name="setup_dual_screen_auto">Automatic</string>
|
||||
<string name="setup_dual_screen_on">On</string>
|
||||
<string name="setup_dual_screen_off">Off</string>
|
||||
<string name="setup_dual_screen_menu">Bottom screen menu</string>
|
||||
<string name="setup_dual_screen_menu_hint">Moves the sections and the in-game menu to the second screen and types there, so the top screen keeps the full view. L2 and R2 switch sections.</string>
|
||||
<string name="setup_dual_screen_controls">Bottom screen controls in games</string>
|
||||
<string name="setup_dual_screen_controls_hint">Opens the trackpad and keyboard on the second screen when a session starts.</string>
|
||||
<string name="dual_typing_title">Typing on the top screen</string>
|
||||
@@ -1927,8 +1927,6 @@
|
||||
<string name="stats_trace">Throttle and brake, last 10 s</string>
|
||||
<string name="stats_speed_chart">Speed, %1$s</string>
|
||||
<string name="stats_gear">Gear</string>
|
||||
<string name="stats_speed_units">Speed units</string>
|
||||
<string name="stats_speed_units_auto">Automatic (%1$s)</string>
|
||||
<string name="stats_steam_client_arm">Steam Client ARM</string>
|
||||
<string name="stats_steam_client_x86">Steam Client x86</string>
|
||||
<string name="stats_throttle">Throttle</string>
|
||||
@@ -1946,7 +1944,20 @@
|
||||
<string name="stats_fuel_t">Fuel, t</string>
|
||||
<string name="stats_fuel_main">Main %1$s t</string>
|
||||
<string name="stats_fuel_reserve">Reserve %1$s t</string>
|
||||
<string name="stats_tap_hint">Tap the top strip or the dash to change its style.</string>
|
||||
<string name="stats_hud_frametime">Frame time</string>
|
||||
<string name="stats_hud_last_seconds">last %1$d s</string>
|
||||
<string name="stats_hud_avg_frame">avg frame</string>
|
||||
<string name="stats_hud_low" formatted="false">1 percent low</string>
|
||||
<string name="stats_hud_slow_frames">frames over 33 ms</string>
|
||||
<string name="stats_hud_per_minute">%1$d / min</string>
|
||||
<string name="stats_hud_playing_for">Playing for %1$s</string>
|
||||
<string name="stats_hud_core_group">%1$d cores, up to %2$s GHz</string>
|
||||
<string name="stats_hud_core_group_unknown">%1$d cores</string>
|
||||
<string name="stats_hud_game_cores">game</string>
|
||||
<string name="stats_hud_fps_of">FPS of %1$d Hz</string>
|
||||
<string name="stats_hud_red_mark">The red mark is where the HUD shows the temperature in red.</string>
|
||||
<string name="stats_hud_whole_device">whole device</string>
|
||||
<string name="stats_hud_battery_temp">battery</string>
|
||||
<string name="bottom_telemetry_send_title">Send dashboard data</string>
|
||||
<string name="bottom_telemetry_send_auto">Automatic</string>
|
||||
<string name="bottom_telemetry_send_auto_hint">Turns it on for every game listed here, including games found later. Games you switch off stay off.</string>
|
||||
@@ -1970,6 +1981,10 @@
|
||||
<string name="bottom_chat_mic_unavailable">Mic is off for this game. To talk, turn on Microphone in DroidDeck\'s Steam settings (Audio) and start the game again.</string>
|
||||
<string name="bottom_chat_volume">Volume %1$d%%</string>
|
||||
<string name="bottom_page_telemetry">In-game stats</string>
|
||||
<string name="bottom_hud_group">Bottom screen HUD</string>
|
||||
<string name="bottom_strip_tabs">Strip on every tab</string>
|
||||
<string name="bottom_strip_lows" formatted="false">1% and 0.1% lows</string>
|
||||
<string name="bottom_strip_cpu_temp">CPU temperature</string>
|
||||
<string name="bottom_telemetry_fps">FPS</string>
|
||||
<string name="bottom_telemetry_cpu">CPU</string>
|
||||
<string name="bottom_telemetry_gpu">GPU</string>
|
||||
@@ -1985,7 +2000,6 @@
|
||||
<string name="bottom_telemetry_balance">Balance</string>
|
||||
<string name="bottom_telemetry_war_thunder">War Thunder</string>
|
||||
<string name="bottom_telemetry_elite">Elite Dangerous</string>
|
||||
<string name="bottom_telemetry_help">Turn on a game\'s data output (often called telemetry or UDP) to see a live dash here. It stays on this device.</string>
|
||||
<string name="bottom_telemetry_unknown_format">Port %1$d receives data (%2$d packets of %3$d bytes), but in a format this page cannot read yet.</string>
|
||||
<string name="bottom_telemetry_port_busy">Another program already uses port %1$s, so that game cannot show here.</string>
|
||||
|
||||
|
||||
@@ -3,5 +3,5 @@
|
||||
android:accessibilityFeedbackType="feedbackGeneric"
|
||||
android:accessibilityFlags="flagRequestFilterKeyEvents"
|
||||
android:canRequestFilterKeyEvents="true"
|
||||
android:canRetrieveWindowContent="false"
|
||||
android:canRetrieveWindowContent="true"
|
||||
android:description="@string/integration_shortcuts_service_description" />
|
||||
@@ -68,6 +68,30 @@ class FrameStatsTest {
|
||||
assertFalse(feed.poll(now + 1))
|
||||
}
|
||||
|
||||
@Test fun slowFramesCountOnlyFramesOver33MsInTheWindow() {
|
||||
val stats = FrameStats()
|
||||
var now = 1_000_000_000L
|
||||
for (ft in listOf(16_666_667L, 40_000_000L, 16_666_667L, 50_000_000L, 2_000_000_000L, 16_666_667L)) {
|
||||
now += ft
|
||||
stats.add(ft, now)
|
||||
}
|
||||
assertEquals(2, stats.slowFrames(now, 60_000_000_000L))
|
||||
assertEquals(0, stats.slowFrames(now + 120_000_000_000L, 60_000_000_000L))
|
||||
stats.reset()
|
||||
assertEquals(0, stats.slowFrames(now, 60_000_000_000L))
|
||||
}
|
||||
|
||||
@Test fun compositorFramesDoNotCountAsLiveGamescopeFrames() {
|
||||
val feed = GameFrameFeed(java.io.File("/nonexistent/droiddeck-hud/frames"))
|
||||
var now = 1_000_000_000L
|
||||
assertFalse(feed.live)
|
||||
feed.onCompositorFrame(now)
|
||||
now += 16_666_667L
|
||||
feed.onCompositorFrame(now)
|
||||
feed.poll(now)
|
||||
assertFalse(feed.live)
|
||||
}
|
||||
|
||||
@Test fun aCompletedPeriodIsCountedWhenMangoHudWouldRedraw() {
|
||||
val stats = FrameStats()
|
||||
var now = 1_000_000_000L
|
||||
|
||||
@@ -0,0 +1,76 @@
|
||||
package com.droiddeck.launcher.input
|
||||
|
||||
import android.os.Looper
|
||||
import android.view.accessibility.AccessibilityWindowInfo
|
||||
import com.droiddeck.launcher.input.BottomScreenGuests.Top
|
||||
import org.junit.After
|
||||
import org.junit.Assert.assertEquals
|
||||
import org.junit.Assert.assertFalse
|
||||
import org.junit.Assert.assertTrue
|
||||
import org.junit.Test
|
||||
import org.junit.runner.RunWith
|
||||
import com.droiddeck.launcher.core.DroidDeckTestRunner as RobolectricTestRunner
|
||||
import org.robolectric.Shadows.shadowOf
|
||||
import org.robolectric.annotation.Config
|
||||
|
||||
@RunWith(RobolectricTestRunner::class)
|
||||
@Config(manifest = Config.NONE)
|
||||
class BottomScreenGuestsTest {
|
||||
private val homes = setOf("com.android.launcher3", "com.retroidpocket.gamelauncher")
|
||||
|
||||
@After fun clear() = DualScreen.setTaken(emptySet())
|
||||
|
||||
@Test fun onlyTheRetroidPocketDuoAndAynThorWatchTheirBottomScreen() {
|
||||
assertTrue(BottomScreenGuests.supported("Retroid Pocket Duo", 36))
|
||||
assertTrue(BottomScreenGuests.supported(" retroid pocket duo ", 33))
|
||||
assertFalse(BottomScreenGuests.supported("Retroid Pocket Duo", 32))
|
||||
assertTrue(BottomScreenGuests.supported("AYN Thor", 33))
|
||||
assertTrue(BottomScreenGuests.supported("AYN Thor Max", 34))
|
||||
assertFalse(BottomScreenGuests.supported("AYN Odin 2", 36))
|
||||
assertFalse(BottomScreenGuests.supported("Pixel 8", 36))
|
||||
}
|
||||
|
||||
@Test fun anyWindowButTheHomeScreenHoldsTheScreen() {
|
||||
assertEquals(Top.HOME, BottomScreenGuests.classify("com.android.launcher3", homes))
|
||||
assertEquals(Top.GUEST, BottomScreenGuests.classify("com.android.chrome", homes))
|
||||
assertEquals(Top.GUEST, BottomScreenGuests.classify("com.retroidpocket.dualscreen.assistant", homes))
|
||||
assertEquals(Top.NONE, BottomScreenGuests.classify(null, homes))
|
||||
}
|
||||
|
||||
@Test fun theWindowListDecidesWhenItSeesPastDroidDeck() {
|
||||
assertEquals("com.android.chrome", BottomScreenGuests.holder("com.android.chrome", null, homes))
|
||||
assertEquals(null, BottomScreenGuests.holder("com.android.launcher3", "com.android.chrome", homes))
|
||||
}
|
||||
|
||||
@Test fun anAppOpenedUnderDroidDeckHoldsTheScreenUntilTheListSeesIt() {
|
||||
assertEquals("com.google.android.youtube", BottomScreenGuests.holder(null, "com.google.android.youtube", homes))
|
||||
assertEquals(null, BottomScreenGuests.holder(null, "com.android.launcher3", homes))
|
||||
assertEquals(null, BottomScreenGuests.holder(null, null, homes))
|
||||
}
|
||||
|
||||
@Test fun keyboardsAndOverlaysNeverTakeTheScreen() {
|
||||
assertTrue(BottomScreenGuests.ignorable(AccessibilityWindowInfo.TYPE_INPUT_METHOD))
|
||||
assertTrue(BottomScreenGuests.ignorable(AccessibilityWindowInfo.TYPE_ACCESSIBILITY_OVERLAY))
|
||||
assertFalse(BottomScreenGuests.ignorable(AccessibilityWindowInfo.TYPE_APPLICATION))
|
||||
assertFalse(BottomScreenGuests.ignorable(AccessibilityWindowInfo.TYPE_SYSTEM))
|
||||
}
|
||||
|
||||
@Test fun listenersHearWhenTheScreenIsTakenAndFreed() {
|
||||
var calls = 0
|
||||
val listener: () -> Unit = { calls++ }
|
||||
DualScreen.watchTaken(listener)
|
||||
DualScreen.setTaken(setOf(2))
|
||||
shadowOf(Looper.getMainLooper()).idle()
|
||||
assertTrue(DualScreen.taken(2))
|
||||
assertFalse(DualScreen.taken(0))
|
||||
DualScreen.setTaken(setOf(2))
|
||||
DualScreen.setTaken(emptySet())
|
||||
shadowOf(Looper.getMainLooper()).idle()
|
||||
assertFalse(DualScreen.taken(2))
|
||||
assertEquals(2, calls)
|
||||
DualScreen.unwatchTaken(listener)
|
||||
DualScreen.setTaken(setOf(2))
|
||||
shadowOf(Looper.getMainLooper()).idle()
|
||||
assertEquals(2, calls)
|
||||
}
|
||||
}
|
||||
@@ -29,6 +29,7 @@ class DualScreenTest {
|
||||
@Test fun knownModelsCountWhenTheTypeIsHidden() {
|
||||
assertTrue(DualScreen.isBuiltIn(null, "Retroid Pocket Duo"))
|
||||
assertTrue(DualScreen.isBuiltIn(null, " AYN Thor "))
|
||||
assertTrue(DualScreen.isBuiltIn(null, "AYN Thor Max"))
|
||||
assertFalse(DualScreen.isBuiltIn(null, "Pixel 8"))
|
||||
}
|
||||
|
||||
@@ -65,4 +66,19 @@ class DualScreenTest {
|
||||
SessionPrefs.setDualScreenMode(context, DualScreen.MODE_OFF)
|
||||
assertEquals(DualScreen.MODE_OFF, SessionPrefs.dualScreenMode(context))
|
||||
}
|
||||
|
||||
@Test fun launcherAndSessionsKeepSeparateModes() {
|
||||
SessionPrefs.setDualScreenMode(context, DualScreen.MODE_OFF)
|
||||
assertEquals(DualScreen.MODE_OFF, SessionPrefs.dualScreenMode(context))
|
||||
assertEquals(DualScreen.MODE_AUTO, SessionPrefs.dualScreenSessionMode(context))
|
||||
SessionPrefs.setDualScreenSessionMode(context, DualScreen.MODE_ON)
|
||||
assertEquals(DualScreen.MODE_OFF, SessionPrefs.dualScreenMode(context))
|
||||
assertEquals(DualScreen.MODE_ON, SessionPrefs.dualScreenSessionMode(context))
|
||||
}
|
||||
|
||||
@Test fun anOldDisabledMenuToggleTurnsBothOff() {
|
||||
context.getSharedPreferences("session", Context.MODE_PRIVATE).edit().putBoolean("dualScreenLauncher", false).commit()
|
||||
assertEquals(DualScreen.MODE_OFF, SessionPrefs.dualScreenMode(context))
|
||||
assertEquals(DualScreen.MODE_OFF, SessionPrefs.dualScreenSessionMode(context))
|
||||
}
|
||||
}
|
||||
@@ -40,7 +40,7 @@ class RuntimeInstallRollbackTest {
|
||||
|
||||
private data class Attempt(val root: File, val staging: File, val old: File, val result: Boolean)
|
||||
|
||||
private fun attempt(failRollback: Boolean): Attempt {
|
||||
private fun attempt(failRollback: Boolean, cancelAfterExtraction: Boolean = false): Attempt {
|
||||
val files = tmp.newFolder("files")
|
||||
val cache = tmp.newFolder("cache")
|
||||
val context = object : ContextWrapper(RuntimeEnvironment.getApplication()) {
|
||||
@@ -68,8 +68,10 @@ class RuntimeInstallRollbackTest {
|
||||
val payload = ByteArrayOutputStream().also { bytes ->
|
||||
ZstdCompressorOutputStream(bytes).use { zstd ->
|
||||
TarArchiveOutputStream(zstd).use { tar ->
|
||||
for ((name, value) in listOf("root/.bashrc" to "shipped home", "usr/bin/new" to "new runtime",
|
||||
"opt/appimages/user/block" to "shipped app")) {
|
||||
val entries = mutableListOf("root/.bashrc" to "shipped home", "usr/bin/new" to "new runtime",
|
||||
"opt/appimages/user/block" to "shipped app")
|
||||
if (cancelAfterExtraction) repeat(1997) { entries += "usr/share/test-$it" to "data" }
|
||||
for ((name, value) in entries) {
|
||||
val data = value.toByteArray()
|
||||
tar.putArchiveEntry(TarArchiveEntry(name).apply { size = data.size.toLong() })
|
||||
tar.write(data)
|
||||
@@ -81,7 +83,7 @@ class RuntimeInstallRollbackTest {
|
||||
val digest = MessageDigest.getInstance("SHA-256").digest(payload).joinToString("") { "%02x".format(it) }
|
||||
val faultSeen = AtomicBoolean()
|
||||
RuntimeFileTree.refuseForTest = Predicate { path ->
|
||||
if (path == File(staging, "opt/appimages/user/block").toPath() && faultSeen.compareAndSet(false, true)) {
|
||||
if (!cancelAfterExtraction && path == File(staging, "opt/appimages/user/block").toPath() && faultSeen.compareAndSet(false, true)) {
|
||||
if (failRollback) put(old, "root/rollback-block", "concurrent data")
|
||||
true
|
||||
} else failRollback && path == File(old, "root/rollback-block").toPath()
|
||||
@@ -105,16 +107,35 @@ class RuntimeInstallRollbackTest {
|
||||
}.apply { isDaemon = true; start() }
|
||||
try {
|
||||
val release = LinuxRuntimeInstaller.Release("synthetic", "http://127.0.0.1:${server.localPort}/runtime.tar.zst", digest, payload.size.toLong())
|
||||
val result = LinuxRuntimeInstaller.install(context, release, null)
|
||||
assertTrue("the intended carry fault was not reached", faultSeen.get())
|
||||
var extractionUpdates = 0
|
||||
val result = LinuxRuntimeInstaller.install(context, release) { stage, _ ->
|
||||
if (cancelAfterExtraction && stage == context.getString(com.droiddeck.launcher.R.string.rtinst_extracting) &&
|
||||
++extractionUpdates == 2) Thread.currentThread().interrupt()
|
||||
}
|
||||
if (cancelAfterExtraction) {
|
||||
assertEquals(2, extractionUpdates)
|
||||
assertTrue(Thread.currentThread().isInterrupted)
|
||||
assertFalse(faultSeen.get())
|
||||
} else assertTrue("the intended carry fault was not reached", faultSeen.get())
|
||||
return Attempt(root, staging, old, result)
|
||||
} finally {
|
||||
Thread.interrupted()
|
||||
server.close()
|
||||
thread.join(1000)
|
||||
RuntimeFileTree.refuseForTest = null
|
||||
}
|
||||
}
|
||||
|
||||
@Test fun cancellationAfterExtractionRemovesTheCandidateAndKeepsTheOriginalRuntime() {
|
||||
val attempt = attempt(failRollback = false, cancelAfterExtraction = true)
|
||||
assertFalse(attempt.result)
|
||||
assertFalse(attempt.staging.exists())
|
||||
assertFalse(attempt.old.exists())
|
||||
assertEquals("original user data", File(attempt.root, "root/save.dat").readText())
|
||||
assertEquals("original app", File(attempt.root, "opt/appimages/user/game/app.AppImage").readText())
|
||||
assertEquals("previous runtime", File(attempt.root, "usr/bin/usable").readText())
|
||||
}
|
||||
|
||||
@Test fun aFailedCarryAndFailedRollbackKeepTheOnlyStagedCopy() {
|
||||
val attempt = attempt(failRollback = true)
|
||||
assertFalse(attempt.result)
|
||||
|
||||
@@ -0,0 +1,30 @@
|
||||
package com.droiddeck.launcher.session
|
||||
|
||||
import org.junit.After
|
||||
import org.junit.Assert.assertEquals
|
||||
import org.junit.Test
|
||||
|
||||
class GpuReaderStatusTest {
|
||||
@After fun reset() {
|
||||
KgslBusy.status = "not tried"
|
||||
KgslClock.status = "not tried"
|
||||
}
|
||||
|
||||
@Test fun emptyWhereTheReadersWereNotNeeded() {
|
||||
assertEquals("", GpuStatsComponent.readerStatus())
|
||||
}
|
||||
|
||||
@Test fun namesBothReaders() {
|
||||
KgslBusy.status = "per-context stamps (hardware-scheduled kernel)"
|
||||
KgslClock.status = "working (680 MHz at start)"
|
||||
assertEquals(
|
||||
"kgsl memstore per-context stamps (hardware-scheduled kernel); GPU cycle counter working (680 MHz at start)",
|
||||
GpuStatsComponent.readerStatus(),
|
||||
)
|
||||
}
|
||||
|
||||
@Test fun rangeCheckFailureIsNamed() {
|
||||
assertEquals("sample out of range", KgslErrors.describe(KgslClock.RANGE_CHECK_FAILED))
|
||||
assertEquals("no error code", KgslErrors.describe(0))
|
||||
}
|
||||
}
|
||||
@@ -5,16 +5,30 @@ import org.junit.Test
|
||||
|
||||
class StatsLayoutTest {
|
||||
@Test fun unknownIdsFallBackToTheDefaults() {
|
||||
assertEquals(StatsStrip.PANEL, StatsStrip.from(null))
|
||||
assertEquals(StatsStrip.LINE, StatsStrip.from(null))
|
||||
assertEquals(StatsStrip.LINE, StatsStrip.from("gone"))
|
||||
assertEquals(StatsStrip.PANEL, StatsStrip.from("panel"))
|
||||
assertEquals(StatsStrip.RINGS, StatsStrip.from("rings"))
|
||||
assertEquals(StatsDash.AUTO, StatsDash.from("gone"))
|
||||
assertEquals(StatsDash.TRACE, StatsDash.from("trace"))
|
||||
}
|
||||
|
||||
@Test fun tappingTheStripCyclesThroughEveryStyle() {
|
||||
assertEquals(StatsStrip.PANEL, StatsStrip.LINE.next())
|
||||
assertEquals(StatsStrip.TILES, StatsStrip.PANEL.next())
|
||||
assertEquals(StatsStrip.RINGS, StatsStrip.TILES.next())
|
||||
assertEquals(StatsStrip.PANEL, StatsStrip.RINGS.next())
|
||||
assertEquals(StatsStrip.LINE, StatsStrip.RINGS.next())
|
||||
}
|
||||
|
||||
@Test fun stripNumbersStartWithEverythingButTheGpuTemperature() {
|
||||
assertEquals(StripMetric.entries.toSet() - StripMetric.GPU_TEMP, StripMetric.parse(null))
|
||||
}
|
||||
|
||||
@Test fun stripNumbersSurviveSavingAndSkipUnknownIds() {
|
||||
val picked = setOf(StripMetric.GPU, StripMetric.RAM, StripMetric.CLOCK)
|
||||
assertEquals(picked, StripMetric.parse(StripMetric.encode(picked)))
|
||||
assertEquals(setOf(StripMetric.CPU), StripMetric.parse("cpu,gone, "))
|
||||
assertEquals(emptySet<StripMetric>(), StripMetric.parse(""))
|
||||
}
|
||||
|
||||
@Test fun autoPicksTheDashByTheGamesFormat() {
|
||||
|
||||
@@ -201,6 +201,15 @@ class StoreInstallRootTest {
|
||||
assertTrue(StoreInstallRoot.unfinished(app).none { it.folder.absolutePath == half.absolutePath })
|
||||
}
|
||||
|
||||
@Test fun aGameBeingUpdatedIsNotAnUnfinishedInstallForAnyStore() {
|
||||
SessionPrefs.setGameStorage(app, SessionPrefs.GAME_STORAGE_OFF, "")
|
||||
for (store in listOf(Store.EPIC, Store.ITCH, Store.XBOX, Store.AMAZON)) {
|
||||
val folder = File(StoreInstallRoot.storeDir(StoreInstallRoot.internalRoot(app), store), StoreInstallRoot.folderName("Updating", "u1")).apply { mkdirs() }
|
||||
StoreGameSidecar(store, "u1", "Updating", "Updating.exe", state = StoreGameSidecar.STATE_UPDATING).write(folder)
|
||||
assertTrue(StoreInstallRoot.unfinished(app).none { it.folder.absolutePath == folder.absolutePath })
|
||||
}
|
||||
}
|
||||
|
||||
@Test fun theScratchCacheIsPrivateAndNamedSafely() {
|
||||
val dir = StoreInstallRoot.scratchDir(app, Store.EPIC, "a/b:c")
|
||||
assertTrue(dir.absolutePath.startsWith(app.cacheDir.absolutePath))
|
||||
|
||||
@@ -0,0 +1,33 @@
|
||||
package com.droiddeck.launcher.ui
|
||||
|
||||
import org.junit.Assert.assertEquals
|
||||
import org.junit.Test
|
||||
|
||||
class HistoryTest {
|
||||
@Test fun theSpanIsTheTimeTheKeptReadingsCover() {
|
||||
val h = History(3)
|
||||
h.add(1f, 1_000L)
|
||||
h.add(2f, 1_500L)
|
||||
assertEquals(1, h.spanSeconds())
|
||||
h.add(3f, 2_000L)
|
||||
h.add(4f, 4_000L)
|
||||
assertEquals(listOf(2f, 3f, 4f), h.snapshot())
|
||||
assertEquals(3, h.spanSeconds())
|
||||
}
|
||||
|
||||
@Test fun aHistoryThatStopsBeingFedKeepsItsSpan() {
|
||||
val h = History(4)
|
||||
h.add(1f, 10_000L)
|
||||
h.add(2f, 20_000L)
|
||||
assertEquals(10, h.spanSeconds())
|
||||
assertEquals(10, h.spanSeconds())
|
||||
}
|
||||
|
||||
@Test fun readingsWithoutATimeHaveNoSpan() {
|
||||
val h = History(2)
|
||||
h.add(1f)
|
||||
assertEquals(0, h.spanSeconds())
|
||||
assertEquals(0, History(2).spanSeconds())
|
||||
assertEquals(0, History(2).apply { add(1f, 5_000L) }.spanSeconds())
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
package com.droiddeck.launcher.ui
|
||||
|
||||
import org.junit.Assert.assertArrayEquals
|
||||
import org.junit.Test
|
||||
|
||||
class StripFitTest {
|
||||
@Test fun everythingFitsOnAWideLine() {
|
||||
assertArrayEquals(booleanArrayOf(true, true, true), fitByPriority(intArrayOf(10, 20, 30), intArrayOf(0, 1, 2), 5, 100))
|
||||
}
|
||||
|
||||
@Test fun theLeastImportantItemsAreLeftOutFirst() {
|
||||
assertArrayEquals(booleanArrayOf(true, false, true), fitByPriority(intArrayOf(10, 20, 30), intArrayOf(0, 2, 1), 5, 60))
|
||||
}
|
||||
|
||||
@Test fun anItemThatDoesNotFitEndsTheLine() {
|
||||
assertArrayEquals(booleanArrayOf(true, false, false), fitByPriority(intArrayOf(10, 50, 5), intArrayOf(0, 1, 2), 2, 40))
|
||||
}
|
||||
|
||||
@Test fun equalPrioritiesKeepTheirOrder() {
|
||||
assertArrayEquals(booleanArrayOf(true, true, false), fitByPriority(intArrayOf(10, 10, 10), intArrayOf(0, 0, 0), 0, 25))
|
||||
}
|
||||
|
||||
@Test fun nothingFitsInNoRoom() {
|
||||
assertArrayEquals(booleanArrayOf(false, false), fitByPriority(intArrayOf(10, 10), intArrayOf(0, 1), 0, 5))
|
||||
}
|
||||
}
|
||||
@@ -113,6 +113,13 @@ same WSI layer, and with eight images and no frame limit it drew flat out
|
||||
(about 240 frames a second at 120 Hz) and heated the device. Values set in the
|
||||
launch options or the environment editor are kept.
|
||||
|
||||
DOOM (2016) is the exception (`DOOMx64vk.exe` beside the launched program, which
|
||||
is `DOOMx64.exe` for a GOG install). It asks for exactly two images and stops with
|
||||
`FATAL ERROR: getSwapchainImagesKHR failed with error (VK_SUCCESS)` when it gets
|
||||
more, so its fix removes both variables, also from its launch options, and sets
|
||||
`vk_x11_strict_image_count=true`, the option Mesa's driconf gives the game. A
|
||||
per-game value in the environment editor still wins.
|
||||
|
||||
## Defaults and available suggestions
|
||||
|
||||
The initial configuration enables Mesa shader caching with
|
||||
|
||||
@@ -124,6 +124,17 @@ def arkham_city_x87(game):
|
||||
|
||||
|
||||
|
||||
@fix
|
||||
def doom_2016_swapchain_images(game):
|
||||
if "doomx64vk.exe" in game.files and game.stem in ("doomx64", "doomx64vk"):
|
||||
images = {
|
||||
"GAMESCOPE_WSI_MIN_IMAGE_COUNT": None,
|
||||
"GAMESCOPE_WSI_ENSURE_MIN_IMAGE_COUNT": None,
|
||||
"vk_x11_strict_image_count": "true",
|
||||
}
|
||||
return images, [], "DOOM (2016): the swapchain keeps the image count the game asks for"
|
||||
|
||||
|
||||
@fix
|
||||
def teardown_vsync(game):
|
||||
if game.stem == "teardown":
|
||||
|
||||
@@ -18,6 +18,19 @@ COMPAT = runpy.run_path(os.path.join(HERE, "steam-compatibility"))
|
||||
BINDING = "droiddeckCompatChanged"
|
||||
BRIGHTNESS_BINDING = "droiddeckBrightnessChanged"
|
||||
CONTROLS_DIR = "/etc/droiddeck-device-controls"
|
||||
LIBRARY_ART_JS = r"""
|
||||
(() => {
|
||||
if (window.__droiddeckLibraryArt) return "present";
|
||||
const store = typeof appStore === "object" ? appStore : null;
|
||||
if (typeof store?.BHasCachedLibraryAsset !== "function") return "unavailable";
|
||||
// The client reports which cached assets exist only after a logon that reaches Steam; before that every app reads as
|
||||
// uncached and the UI asks the CDN, which is blank offline. The native check still runs, so misses are still reported.
|
||||
const native = store.BHasCachedLibraryAsset.bind(store);
|
||||
store.BHasCachedLibraryAsset = (appid, version, type) => native(appid, version, type) || !version;
|
||||
window.__droiddeckLibraryArt = true;
|
||||
return "installed";
|
||||
})()
|
||||
"""
|
||||
BRIGHTNESS_JS = r"""
|
||||
(() => {
|
||||
if (window.__droiddeckBrightness) return "present";
|
||||
@@ -851,6 +864,7 @@ class Helper:
|
||||
if self.files_only:
|
||||
page.call("Runtime.addBinding", {"name": FILE_BINDING})
|
||||
page.evaluate(FILE_JS)
|
||||
page.evaluate(LIBRARY_ART_JS)
|
||||
self.page = page
|
||||
self.controls_state = None
|
||||
self.controls_installed = False
|
||||
@@ -861,6 +875,7 @@ class Helper:
|
||||
page.evaluate(VR_JS)
|
||||
page.call("Runtime.addBinding", {"name": FILE_BINDING})
|
||||
page.evaluate(FILE_JS)
|
||||
page.evaluate(LIBRARY_ART_JS)
|
||||
try:
|
||||
page.evaluate(TOUR_JS)
|
||||
except RuntimeError:
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
#include <string.h>
|
||||
|
||||
#include "kgsl_busy.h"
|
||||
#include "kgsl_layout.h"
|
||||
|
||||
#define ENTRIES 819
|
||||
|
||||
@@ -52,5 +53,61 @@ int main(void) {
|
||||
assert(moved == 0);
|
||||
|
||||
puts("PASS: kgsl memstore busy");
|
||||
|
||||
// Hardware-scheduled kernels: ringbuffer entries stay 0, contexts carry the stamps.
|
||||
memset(memstore, 0, sizeof(memstore));
|
||||
static uint32_t seen[ENTRIES], ctx_last[ENTRIES];
|
||||
int live[KGSL_BUSY_MAX_LIVE];
|
||||
stamp(3, 50, 50);
|
||||
stamp(9, 21, 20);
|
||||
stamp(first - 1, 4, 4);
|
||||
int count = kgsl_busy_scan_contexts(memstore, first, seen, live, KGSL_BUSY_MAX_LIVE);
|
||||
assert(count == 3 && live[0] == 3 && live[1] == 9 && live[2] == first - 1);
|
||||
moved = 0;
|
||||
assert(kgsl_busy_sample_contexts(memstore, live, count, ctx_last, &moved) == 1);
|
||||
assert(moved == 1 && ctx_last[9] == 21);
|
||||
|
||||
// Busy or newly started contexts come first; idle ones that have run work stay in the list.
|
||||
stamp(9, 21, 21);
|
||||
stamp(5, 2, 1);
|
||||
count = kgsl_busy_scan_contexts(memstore, first, seen, live, KGSL_BUSY_MAX_LIVE);
|
||||
assert(count == 4 && live[0] == 5 && live[1] == 3 && live[2] == 9 && live[3] == first - 1);
|
||||
moved = 0;
|
||||
assert(kgsl_busy_sample_contexts(memstore, live, count, ctx_last, &moved) == 1);
|
||||
assert(moved == 1);
|
||||
moved = 0;
|
||||
assert(kgsl_busy_sample_contexts(memstore, live, count, ctx_last, &moved) == 1);
|
||||
assert(moved == 0);
|
||||
stamp(5, 2, 2);
|
||||
assert(kgsl_busy_sample_contexts(memstore, live, count, ctx_last, &moved) == 0);
|
||||
|
||||
// Entry 0 (global) and the ringbuffer entries are not contexts; the live list is capped.
|
||||
memset(memstore, 0, sizeof(memstore));
|
||||
memset(seen, 0, sizeof(seen));
|
||||
stamp(0, 8, 7);
|
||||
stamp(first, 8, 7);
|
||||
assert(kgsl_busy_scan_contexts(memstore, first, seen, live, KGSL_BUSY_MAX_LIVE) == 0);
|
||||
for (int id = 1; id <= KGSL_BUSY_MAX_LIVE + 10; id++) stamp(id, 1, 1);
|
||||
assert(kgsl_busy_scan_contexts(memstore, first, seen, live, KGSL_BUSY_MAX_LIVE) == KGSL_BUSY_MAX_LIVE);
|
||||
assert(kgsl_busy_scan_contexts(memstore, first, seen, live, KGSL_BUSY_MAX_LIVE) == KGSL_BUSY_MAX_LIVE);
|
||||
stamp(KGSL_BUSY_MAX_LIVE + 5, 2, 1);
|
||||
assert(kgsl_busy_scan_contexts(memstore, first, seen, live, KGSL_BUSY_MAX_LIVE) == KGSL_BUSY_MAX_LIVE);
|
||||
assert(live[0] == KGSL_BUSY_MAX_LIVE + 5);
|
||||
stamp(KGSL_BUSY_MAX_LIVE + 5, 0, 0);
|
||||
count = kgsl_busy_scan_contexts(memstore, first, seen, live, KGSL_BUSY_MAX_LIVE);
|
||||
count = kgsl_busy_scan_contexts(memstore, first, seen, live, KGSL_BUSY_MAX_LIVE);
|
||||
for (int i = 0; i < count; i++) assert(live[i] != KGSL_BUSY_MAX_LIVE + 5);
|
||||
puts("PASS: kgsl memstore contexts");
|
||||
|
||||
assert(kgsl_layout_for(0x44050001u) == &KGSL_LAYOUT_A8XX); // Adreno 830
|
||||
assert(kgsl_layout_for(0x44030a20u) == &KGSL_LAYOUT_A8XX); // Adreno 829
|
||||
assert(kgsl_layout_for(0x43020100u) == &KGSL_LAYOUT_A7XX); // Adreno 722
|
||||
assert(kgsl_layout_for(0x43050a01u) == &KGSL_LAYOUT_A7XX); // Adreno 740
|
||||
assert(kgsl_layout_for(0x07010000u) == &KGSL_LAYOUT_A7XX); // Adreno 710
|
||||
assert(kgsl_layout_for(0x06030001u) == &KGSL_LAYOUT_A7XX); // Adreno 630
|
||||
assert(kgsl_layout_for(0x45000000u) == NULL);
|
||||
assert(KGSL_LAYOUT_A8XX.always_on == 0x8e7u && KGSL_LAYOUT_A8XX.cycles == 1u);
|
||||
assert(KGSL_LAYOUT_A7XX.always_on == 0x980u && KGSL_LAYOUT_A7XX.cycles == 0u);
|
||||
puts("PASS: kgsl counter layout");
|
||||
return 0;
|
||||
}
|
||||
@@ -873,6 +873,38 @@ class GameEnvironmentTest(unittest.TestCase):
|
||||
(game / "D3DGear64.DLL").unlink()
|
||||
self.assertEqual(MODULE["engine_fixes"](str(game / "TheCrew2.exe"), []), ({}, [], []))
|
||||
|
||||
def test_doom_2016_keeps_the_swapchain_image_count_it_asks_for(self):
|
||||
with tempfile.TemporaryDirectory() as tmp:
|
||||
game = Path(tmp) / "DOOM (2016)"
|
||||
game.mkdir()
|
||||
for name in ("DOOMx64.exe", "DOOMx64vk.exe", "Galaxy64.dll"):
|
||||
(game / name).touch()
|
||||
images = {
|
||||
"GAMESCOPE_WSI_MIN_IMAGE_COUNT": None,
|
||||
"GAMESCOPE_WSI_ENSURE_MIN_IMAGE_COUNT": None,
|
||||
"vk_x11_strict_image_count": "true",
|
||||
}
|
||||
for exe in ("DOOMx64.exe", "DOOMx64vk.exe"):
|
||||
env, extra, notes = MODULE["engine_fixes"](str(game / exe), [])
|
||||
self.assertEqual((env, extra), (images, []))
|
||||
self.assertTrue(notes)
|
||||
launched = {"GAMESCOPE_WSI_MIN_IMAGE_COUNT": "8", "GAMESCOPE_WSI_ENSURE_MIN_IMAGE_COUNT": "1"}
|
||||
result = MODULE["apply_config"](launched, {"version": 1}, "3566086475", env)
|
||||
self.assertNotIn("GAMESCOPE_WSI_MIN_IMAGE_COUNT", result)
|
||||
self.assertNotIn("GAMESCOPE_WSI_ENSURE_MIN_IMAGE_COUNT", result)
|
||||
self.assertEqual(result["vk_x11_strict_image_count"], "true")
|
||||
config = {"version": 1, "games": {"3566086475": {"GAMESCOPE_WSI_MIN_IMAGE_COUNT": "3"}}}
|
||||
self.assertEqual(MODULE["apply_config"](launched, config, "3566086475", env)["GAMESCOPE_WSI_MIN_IMAGE_COUNT"], "3")
|
||||
eternal = Path(tmp) / "DOOMEternal"
|
||||
eternal.mkdir()
|
||||
(eternal / "DOOMEternalx64vk.exe").touch()
|
||||
self.assertEqual(MODULE["engine_fixes"](str(eternal / "DOOMEternalx64vk.exe"), []), ({}, [], []))
|
||||
other = Path(tmp) / "Other"
|
||||
other.mkdir()
|
||||
for name in ("Other.exe", "DOOMx64vk.exe"):
|
||||
(other / name).touch()
|
||||
self.assertEqual(MODULE["engine_fixes"](str(other / "Other.exe"), []), ({}, [], []))
|
||||
|
||||
def test_dll_overrides_from_several_fixes_are_all_kept(self):
|
||||
with tempfile.TemporaryDirectory() as tmp:
|
||||
for name in ("Game.exe", "Game.pck", "GodotSharp.dll", "libsentry.windows.release.x86_64.dll", "d3dgear64.dll"):
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
"""Exercise the HUD's kgsl memstore busy sampling (app/src/main/cpp/gpuclock/kgsl_busy.h)."""
|
||||
"""Exercise the HUD's kgsl memstore busy sampling and counter layout (app/src/main/cpp/gpuclock)."""
|
||||
import os
|
||||
from pathlib import Path
|
||||
import shutil
|
||||
@@ -27,6 +27,8 @@ class KgslBusyTest(unittest.TestCase):
|
||||
result = subprocess.run([str(self.binary)], capture_output=True, text=True)
|
||||
self.assertEqual(result.returncode, 0, result.stdout + result.stderr)
|
||||
self.assertIn('PASS: kgsl memstore busy', result.stdout)
|
||||
self.assertIn('PASS: kgsl memstore contexts', result.stdout)
|
||||
self.assertIn('PASS: kgsl counter layout', result.stdout)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
|
||||
@@ -0,0 +1,54 @@
|
||||
from pathlib import Path
|
||||
import runpy
|
||||
import shutil
|
||||
import subprocess
|
||||
import tempfile
|
||||
import unittest
|
||||
|
||||
BIN = Path(__file__).resolve().parents[1] / "linuxfs/overlay/usr/local/bin"
|
||||
LIVE = runpy.run_path(str(BIN / "droiddeck-steam-compat"))
|
||||
|
||||
HARNESS = """
|
||||
const reports = [];
|
||||
global.appStore = {
|
||||
BHasCachedLibraryAsset(appid, version, type) {
|
||||
if ((version ?? 0) & (1 << type)) return true;
|
||||
reports.push([appid, type]);
|
||||
return false;
|
||||
},
|
||||
};
|
||||
const first = eval(process.argv[2]);
|
||||
const second = eval(process.argv[2]);
|
||||
const check = (name, actual, expected) => { if (actual !== expected) throw new Error(name + ": " + actual); };
|
||||
check("install", first, "installed");
|
||||
check("reinstall", second, "present");
|
||||
check("no logon", appStore.BHasCachedLibraryAsset(10, undefined, 3), true);
|
||||
check("zero version", appStore.BHasCachedLibraryAsset(10, 0, 3), true);
|
||||
check("reported", reports.length, 2);
|
||||
check("bit set", appStore.BHasCachedLibraryAsset(10, 1 << 3, 3), true);
|
||||
check("bit missing", appStore.BHasCachedLibraryAsset(10, 1 << 2, 3), false);
|
||||
check("reported again", reports.length, 3);
|
||||
"""
|
||||
|
||||
|
||||
@unittest.skipUnless(shutil.which("node"), "node is not installed")
|
||||
class LibraryArtTest(unittest.TestCase):
|
||||
def run_node(self, harness):
|
||||
with tempfile.TemporaryDirectory() as directory:
|
||||
script = Path(directory, "harness.js")
|
||||
script.write_text("global.window = {};\n" + harness)
|
||||
return subprocess.run(["node", str(script), LIVE["LIBRARY_ART_JS"]], capture_output=True, text=True, timeout=20)
|
||||
|
||||
def test_apps_without_a_client_cache_report_read_from_the_local_library_cache(self):
|
||||
result = self.run_node(HARNESS)
|
||||
self.assertEqual(result.returncode, 0, result.stderr)
|
||||
|
||||
def test_a_page_without_the_app_store_is_left_alone(self):
|
||||
result = self.run_node("""
|
||||
if (eval(process.argv[2]) !== "unavailable") throw new Error("installed without an app store");
|
||||
""")
|
||||
self.assertEqual(result.returncode, 0, result.stderr)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
Reference in new issue
Block a user