Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -601,6 +601,7 @@ static void onTextInputEvent(GameActivity* activity,
pthread_mutex_lock(&android_app->mutex);

android_app->textInputState = 1;
notifyInput(android_app);
pthread_mutex_unlock(&android_app->mutex);
}

Expand Down
6 changes: 6 additions & 0 deletions android-activity/src/game_activity/input.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,12 @@ use bitflags::bitflags;
pub enum InputEvent {
MotionEvent(MotionEvent),
KeyEvent(KeyEvent),
CharacterEvent(CharacterEvent),
}

#[derive(Debug, Clone)]
pub struct CharacterEvent {
pub character: char,
}

/// An enum representing the source of an [`MotionEvent`] or [`KeyEvent`]
Expand Down
50 changes: 48 additions & 2 deletions android-activity/src/game_activity/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,12 @@ use std::fs::File;
use std::io::{BufRead, BufReader};
use std::marker::PhantomData;
use std::ops::Deref;
use std::os::raw;
use std::os::raw::{self, c_void};
use std::os::unix::prelude::*;
use std::ptr::NonNull;
use std::sync::{Arc, RwLock};
use std::time::Duration;
use std::{ptr, thread};
use std::{ptr, slice, thread};

use log::{error, trace, Level};

Expand All @@ -23,6 +23,8 @@ use ndk::asset::AssetManager;
use ndk::configuration::Configuration;
use ndk::native_window::NativeWindow;

use crate::game_activity::ffi::{GameTextInputSpan, GameTextInputState};
use crate::input::CharacterEvent;
use crate::{util, AndroidApp, ConfigurationRef, MainEvent, PollEvent, Rect};

mod ffi;
Expand Down Expand Up @@ -309,6 +311,17 @@ impl AndroidAppInner {
}
}

pub fn set_soft_input(&self, show: bool) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think my preference would be for set_soft_input_visible(&self, visible: bool) (since 'soft input' isn't a property that's inherently boolean on it's own)

though we might need to consider the async nature of this. I've got the impression that this may conceptually be a request on Android, and the Android Java API optionally provides a way to get a notification of completion.

We might want to consider the future possibility that we'll have an event that notifies application of completion, and so then instead of an API that looks like a property getter/setter, maybe it should be more like a request with a verb like show_soft_input(&self).

If we can't create a reliable, symmetric getter for this like pub fn soft_input_visible(&self) -> bool maybe that'll be another indication that we should treat this like a request with a verb based name.

unsafe {
let activity = (*self.native_app.as_ptr()).activity;
if show {
ffi::GameActivity_showSoftInput(activity, 0);
} else {
ffi::GameActivity_hideSoftInput(activity, 0);
}
}
}

pub fn enable_motion_axis(&mut self, axis: Axis) {
unsafe { ffi::GameActivityPointerAxes_enableAxis(axis as i32) }
}
Expand Down Expand Up @@ -356,6 +369,39 @@ impl AndroidAppInner {
where
F: FnMut(&InputEvent),
{
let mut chars = String::new();
unsafe {
let activity = (*self.native_app.as_ptr()).activity;

unsafe extern "C" fn callback(context: *mut c_void, state: *const GameTextInputState) {
*(context as *mut String) = std::str::from_utf8_unchecked(slice::from_raw_parts(
(*state).text_UTF8,
(*state).text_length as usize,
))
.to_owned();
}
ffi::GameActivity_getTextInputState(
activity,
Some(callback),
&mut chars as *mut String as *mut c_void,
);
if !chars.is_empty() {
ffi::GameActivity_setTextInputState(
activity,
&GameTextInputState {
text_UTF8: ptr::null(),
text_length: 0,
selection: GameTextInputSpan { start: 0, end: 0 },
composingRegion: GameTextInputSpan { start: 0, end: 0 },
} as *const _,
);
}
}

for character in chars.chars() {
callback(&InputEvent::CharacterEvent(CharacterEvent { character }));
}

let buf = unsafe {
let app_ptr = self.native_app.as_ptr();
let input_buffer = ffi::android_app_swap_input_buffers(app_ptr);
Expand Down
4 changes: 4 additions & 0 deletions android-activity/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -241,6 +241,10 @@ impl AndroidApp {
self.inner.write().unwrap().disable_motion_axis(axis);
}

pub fn set_soft_input(&self, show: bool) {
self.inner.read().unwrap().set_soft_input(show);
}

pub fn input_events<'b, F>(&self, callback: F)
where
F: FnMut(&input::InputEvent),
Expand Down
4 changes: 4 additions & 0 deletions android-activity/src/native_activity/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -353,6 +353,10 @@ impl AndroidAppInner {
}
}

pub fn set_soft_input(&self, _show: bool) {
// Unsupported
}

pub fn enable_motion_axis(&self, _axis: input::Axis) {
// NOP - The InputQueue API doesn't let us optimize which axis values are read
}
Expand Down