diff --git a/app/src/main/cpp/fakeinput_steam.cpp b/app/src/main/cpp/fakeinput_steam.cpp index 3b16d9ac8..09428e442 100644 --- a/app/src/main/cpp/fakeinput_steam.cpp +++ b/app/src/main/cpp/fakeinput_steam.cpp @@ -3577,6 +3577,21 @@ EXPORT int close(int fd) { static constexpr int kMaxRingWaitMs = 2; static constexpr int kLowLatencyRingWaitUs = 250; +static ssize_t copy_input_events(void *buf, const struct input_event *events, size_t count) { + size_t bytes = count * sizeof(struct input_event); + struct iovec local = {const_cast(events), bytes}; + struct iovec remote = {buf, bytes}; + ssize_t copied = syscall(SYS_process_vm_writev, getpid(), &local, 1, &remote, 1, 0); + if (copied < 0) return -1; + if (copied != static_cast(bytes)) { + errno = EFAULT; + // evdev reads only report complete input_event records. + copied -= copied % sizeof(struct input_event); + if (!copied) return -1; + } + return copied; +} + static ssize_t read_input(int fd, void *buf, size_t count) { if (!watched(fd)) return syscall(SYS_read, fd, buf, count); std::unique_lock guard(controller_mutex()); @@ -3622,42 +3637,44 @@ static ssize_t read_input(int fd, void *buf, size_t count) { if (events == 0) break; const uint8_t *ring_events = reinterpret_cast(fake.ring) + FAKE_INPUT_RING_HEADER_SIZE; + std::vector input_events(events); for (size_t i = 0; i < events; ++i) { size_t index = (fake.read_seq + i) % FAKE_INPUT_RING_CAPACITY; RingEvent stored; memcpy(&stored, ring_events + index * FAKE_INPUT_EVENT_SIZE, FAKE_INPUT_EVENT_SIZE); - struct input_event ev = {}; + struct input_event &ev = input_events[i]; ev.time.tv_sec = stored.sec; ev.time.tv_usec = stored.usec; ev.type = stored.type; ev.code = stored.code; ev.value = stored.value; if (ev.type == EV_ABS) ev.code = presented_abs_code(fake, ev.code); - memcpy(static_cast(buf) + i * sizeof(ev), &ev, sizeof(ev)); } __atomic_thread_fence(__ATOMIC_ACQUIRE); if (snap.sequence != __atomic_load_n(&fake.ring->snapshot_seq, __ATOMIC_RELAXED)) continue; - fake.read_seq += events; - return static_cast(events * sizeof(struct input_event)); + ssize_t copied = copy_input_events(buf, input_events.data(), events); + if (copied > 0) fake.read_seq += copied / sizeof(struct input_event); + return copied; } } size_t events = std::min(requested, fake.keyframe_remaining); + struct input_event input_events[kNeutralEventCount] = {}; struct timeval now = {}; gettimeofday(&now, nullptr); for (size_t i = 0; i < events; ++i) { - size_t index = kNeutralEventCount - fake.keyframe_remaining; - struct input_event ev = {}; + size_t index = kNeutralEventCount - fake.keyframe_remaining + i; + struct input_event &ev = input_events[i]; ev.time = now; ev.type = kNeutralEvents[index].type; ev.code = kNeutralEvents[index].code; ev.value = keyframe_value(fake, ev.type, ev.code); if (ev.type == EV_ABS) ev.code = presented_abs_code(fake, ev.code); - memcpy(static_cast(buf) + i * sizeof(ev), &ev, sizeof(ev)); - --fake.keyframe_remaining; } - return static_cast(events * sizeof(struct input_event)); + ssize_t copied = copy_input_events(buf, input_events, events); + if (copied > 0) fake.keyframe_remaining -= copied / sizeof(struct input_event); + return copied; } if (nonblock) { errno = EAGAIN; @@ -3706,10 +3723,6 @@ EXPORT ssize_t readv(int fd, const struct iovec *iov, int iovcnt) { return -1; } size += iov[i].iov_len; - if (iov[i].iov_len && !iov[i].iov_base) { - errno = EFAULT; - return -1; - } } // evdev's readv calls its read handler per vector; Steam's ARM64 overlay uses this path too.