Carry output information on visible command all the way to C
This commit is contained in:
@ -6,12 +6,14 @@
|
||||
|
||||
use std::time::Duration;
|
||||
|
||||
use crate::outputs::OutputId;
|
||||
|
||||
/// The keyboard should hide after this has elapsed to prevent flickering.
|
||||
pub const HIDING_TIMEOUT: Duration = Duration::from_millis(200);
|
||||
|
||||
/// The outwardly visible state of visibility
|
||||
#[derive(PartialEq, Debug, Clone)]
|
||||
pub enum Outcome {
|
||||
Visible,
|
||||
Visible(OutputId),
|
||||
Hidden,
|
||||
}
|
||||
|
||||
@ -153,6 +153,7 @@ mod test {
|
||||
use crate::imservice::{ ContentHint, ContentPurpose };
|
||||
use crate::main::PanelCommand;
|
||||
use crate::state::{ Application, InputMethod, InputMethodDetails, Presence, visibility };
|
||||
use crate::state::test::application_with_fake_output;
|
||||
|
||||
fn imdetails_new() -> InputMethodDetails {
|
||||
InputMethodDetails {
|
||||
@ -170,12 +171,12 @@ mod test {
|
||||
im: InputMethod::Active(imdetails_new()),
|
||||
physical_keyboard: Presence::Missing,
|
||||
visibility_override: visibility::State::NotForced,
|
||||
..Application::new(start)
|
||||
..application_with_fake_output(start)
|
||||
};
|
||||
|
||||
let l = State::new(state, now);
|
||||
let (l, commands) = handle_event(l, InputMethod::InactiveSince(now).into(), now);
|
||||
assert_eq!(commands.panel_visibility, Some(PanelCommand::Show));
|
||||
assert_matches!(commands.panel_visibility, Some(PanelCommand::Show(_)));
|
||||
assert_eq!(l.scheduled_wakeup, Some(now + animation::HIDING_TIMEOUT));
|
||||
|
||||
now += animation::HIDING_TIMEOUT;
|
||||
|
||||
@ -14,6 +14,9 @@ extern crate maplit;
|
||||
extern crate serde;
|
||||
extern crate xkbcommon;
|
||||
|
||||
#[cfg(test)]
|
||||
#[macro_use]
|
||||
mod assert_matches;
|
||||
#[macro_use]
|
||||
mod logging;
|
||||
|
||||
|
||||
11
src/main.rs
11
src/main.rs
@ -3,7 +3,7 @@
|
||||
*/
|
||||
|
||||
/*! Glue for the main loop. */
|
||||
|
||||
use crate::outputs::OutputId;
|
||||
use crate::state;
|
||||
use glib::{Continue, MainContext, PRIORITY_DEFAULT, Receiver};
|
||||
|
||||
@ -19,6 +19,7 @@ mod c {
|
||||
use crate::imservice::IMService;
|
||||
use crate::imservice::c::InputMethod;
|
||||
use crate::outputs::Outputs;
|
||||
use crate::outputs::c::WlOutput;
|
||||
use crate::state;
|
||||
use crate::submission::Submission;
|
||||
use crate::util::c::Wrapped;
|
||||
@ -74,7 +75,7 @@ mod c {
|
||||
extern "C" {
|
||||
#[allow(improper_ctypes)]
|
||||
fn init_wayland(wayland: *mut Wayland);
|
||||
fn server_context_service_real_show_keyboard(service: *const UIManager);
|
||||
fn server_context_service_real_show_keyboard(service: *const UIManager, output: WlOutput);
|
||||
fn server_context_service_real_hide_keyboard(service: *const UIManager);
|
||||
fn server_context_service_set_hint_purpose(service: *const UIManager, hint: u32, purpose: u32);
|
||||
// This should probably only get called from the gtk main loop,
|
||||
@ -148,8 +149,8 @@ mod c {
|
||||
dbus_handler: *const DBusHandler,
|
||||
) {
|
||||
match msg.panel_visibility {
|
||||
Some(PanelCommand::Show) => unsafe {
|
||||
server_context_service_real_show_keyboard(ui_manager);
|
||||
Some(PanelCommand::Show(output)) => unsafe {
|
||||
server_context_service_real_show_keyboard(ui_manager, output.0);
|
||||
},
|
||||
Some(PanelCommand::Hide) => unsafe {
|
||||
server_context_service_real_hide_keyboard(ui_manager);
|
||||
@ -175,7 +176,7 @@ mod c {
|
||||
|
||||
#[derive(Clone, PartialEq, Debug)]
|
||||
pub enum PanelCommand {
|
||||
Show,
|
||||
Show(OutputId),
|
||||
Hide,
|
||||
}
|
||||
|
||||
|
||||
@ -326,15 +326,15 @@ pub struct Size {
|
||||
|
||||
/// wl_output mode
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
struct Mode {
|
||||
pub struct Mode {
|
||||
width: i32,
|
||||
height: i32,
|
||||
}
|
||||
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
pub struct OutputState {
|
||||
current_mode: Option<Mode>,
|
||||
transform: Option<c::Transform>,
|
||||
pub current_mode: Option<Mode>,
|
||||
pub transform: Option<c::Transform>,
|
||||
pub scale: i32,
|
||||
}
|
||||
|
||||
@ -383,7 +383,7 @@ impl OutputState {
|
||||
/// Not guaranteed to exist,
|
||||
/// but can be used to look up state.
|
||||
#[derive(Clone, Copy, PartialEq, Debug, Eq, Hash)]
|
||||
pub struct OutputId(c::WlOutput);
|
||||
pub struct OutputId(pub c::WlOutput);
|
||||
|
||||
// WlOutput is a pointer,
|
||||
// but in the public interface,
|
||||
|
||||
@ -27,6 +27,7 @@
|
||||
#include "submission.h"
|
||||
#include "wayland.h"
|
||||
#include "server-context-service.h"
|
||||
#include "wayland-client-protocol.h"
|
||||
|
||||
enum {
|
||||
PROP_0,
|
||||
@ -205,7 +206,7 @@ make_widget (ServerContextService *self)
|
||||
|
||||
// Called from rust
|
||||
void
|
||||
server_context_service_real_show_keyboard (ServerContextService *self)
|
||||
server_context_service_real_show_keyboard (ServerContextService *self, struct wl_output *output)
|
||||
{
|
||||
if (!self->window) {
|
||||
make_window (self);
|
||||
|
||||
85
src/state.rs
85
src/state.rs
@ -93,12 +93,12 @@ impl Outcome {
|
||||
pub fn get_commands_to_reach(&self, new_state: &Self) -> Commands {
|
||||
let layout_hint_set = match new_state {
|
||||
Outcome {
|
||||
visibility: animation::Outcome::Visible,
|
||||
visibility: animation::Outcome::Visible(_),
|
||||
im: InputMethod::Active(hints),
|
||||
} => Some(hints.clone()),
|
||||
|
||||
Outcome {
|
||||
visibility: animation::Outcome::Visible,
|
||||
visibility: animation::Outcome::Visible(_),
|
||||
im: InputMethod::InactiveSince(_),
|
||||
} => Some(InputMethodDetails {
|
||||
hint: ContentHint::NONE,
|
||||
@ -110,9 +110,9 @@ impl Outcome {
|
||||
..
|
||||
} => None,
|
||||
};
|
||||
|
||||
// FIXME: handle switching outputs
|
||||
let (dbus_visible_set, panel_visibility) = match new_state.visibility {
|
||||
animation::Outcome::Visible => (Some(true), Some(PanelCommand::Show)),
|
||||
animation::Outcome::Visible(output) => (Some(true), Some(PanelCommand::Show(output))),
|
||||
animation::Outcome::Hidden => (Some(false), Some(PanelCommand::Hide)),
|
||||
};
|
||||
|
||||
@ -239,15 +239,18 @@ impl Application {
|
||||
pub fn get_outcome(&self, now: Instant) -> Outcome {
|
||||
// FIXME: include physical keyboard presence
|
||||
Outcome {
|
||||
visibility: match (self.physical_keyboard, self.visibility_override) {
|
||||
(_, visibility::State::ForcedHidden) => animation::Outcome::Hidden,
|
||||
(_, visibility::State::ForcedVisible) => animation::Outcome::Visible,
|
||||
(Presence::Present, visibility::State::NotForced) => animation::Outcome::Hidden,
|
||||
(Presence::Missing, visibility::State::NotForced) => match self.im {
|
||||
InputMethod::Active(_) => animation::Outcome::Visible,
|
||||
InputMethod::InactiveSince(since) => {
|
||||
if now < since + animation::HIDING_TIMEOUT { animation::Outcome::Visible }
|
||||
else { animation::Outcome::Hidden }
|
||||
visibility: match self.preferred_output {
|
||||
None => animation::Outcome::Hidden,
|
||||
Some(output) => match (self.physical_keyboard, self.visibility_override) {
|
||||
(_, visibility::State::ForcedHidden) => animation::Outcome::Hidden,
|
||||
(_, visibility::State::ForcedVisible) => animation::Outcome::Visible(output),
|
||||
(Presence::Present, visibility::State::NotForced) => animation::Outcome::Hidden,
|
||||
(Presence::Missing, visibility::State::NotForced) => match self.im {
|
||||
InputMethod::Active(_) => animation::Outcome::Visible(output),
|
||||
InputMethod::InactiveSince(since) => {
|
||||
if now < since + animation::HIDING_TIMEOUT { animation::Outcome::Visible(output) }
|
||||
else { animation::Outcome::Hidden }
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
@ -274,9 +277,9 @@ impl Application {
|
||||
|
||||
|
||||
#[cfg(test)]
|
||||
mod test {
|
||||
pub mod test {
|
||||
use super::*;
|
||||
|
||||
use crate::outputs::c::WlOutput;
|
||||
use std::time::Duration;
|
||||
|
||||
fn imdetails_new() -> InputMethodDetails {
|
||||
@ -286,6 +289,30 @@ mod test {
|
||||
}
|
||||
}
|
||||
|
||||
fn fake_output_id(id: usize) -> OutputId {
|
||||
OutputId(unsafe {
|
||||
std::mem::transmute::<_, WlOutput>(id)
|
||||
})
|
||||
}
|
||||
|
||||
pub fn application_with_fake_output(start: Instant) -> Application {
|
||||
let id = fake_output_id(1);
|
||||
let mut outputs = HashMap::new();
|
||||
outputs.insert(
|
||||
id,
|
||||
OutputState {
|
||||
current_mode: None,
|
||||
transform: None,
|
||||
scale: 1,
|
||||
},
|
||||
);
|
||||
Application {
|
||||
preferred_output: Some(id),
|
||||
outputs,
|
||||
..Application::new(start)
|
||||
}
|
||||
}
|
||||
|
||||
/// Test the original delay scenario: no flicker on quick switches.
|
||||
#[test]
|
||||
fn avoid_hide() {
|
||||
@ -295,16 +322,16 @@ mod test {
|
||||
im: InputMethod::Active(imdetails_new()),
|
||||
physical_keyboard: Presence::Missing,
|
||||
visibility_override: visibility::State::NotForced,
|
||||
..Application::new(start)
|
||||
..application_with_fake_output(start)
|
||||
};
|
||||
|
||||
let state = state.apply_event(Event::InputMethod(InputMethod::InactiveSince(now)), now);
|
||||
// Check 100ms at 1ms intervals. It should remain visible.
|
||||
for _i in 0..100 {
|
||||
now += Duration::from_millis(1);
|
||||
assert_eq!(
|
||||
assert_matches!(
|
||||
state.get_outcome(now).visibility,
|
||||
animation::Outcome::Visible,
|
||||
animation::Outcome::Visible(_),
|
||||
"Hidden when it should remain visible: {:?}",
|
||||
now.saturating_duration_since(start),
|
||||
)
|
||||
@ -312,7 +339,7 @@ mod test {
|
||||
|
||||
let state = state.apply_event(Event::InputMethod(InputMethod::Active(imdetails_new())), now);
|
||||
|
||||
assert_eq!(state.get_outcome(now).visibility, animation::Outcome::Visible);
|
||||
assert_matches!(state.get_outcome(now).visibility, animation::Outcome::Visible(_));
|
||||
}
|
||||
|
||||
/// Make sure that hiding works when input method goes away
|
||||
@ -324,12 +351,12 @@ mod test {
|
||||
im: InputMethod::Active(imdetails_new()),
|
||||
physical_keyboard: Presence::Missing,
|
||||
visibility_override: visibility::State::NotForced,
|
||||
..Application::new(start)
|
||||
..application_with_fake_output(start)
|
||||
};
|
||||
|
||||
let state = state.apply_event(Event::InputMethod(InputMethod::InactiveSince(now)), now);
|
||||
|
||||
while let animation::Outcome::Visible = state.get_outcome(now).visibility {
|
||||
while let animation::Outcome::Visible(_) = state.get_outcome(now).visibility {
|
||||
now += Duration::from_millis(1);
|
||||
assert!(
|
||||
now < start + Duration::from_millis(250),
|
||||
@ -349,7 +376,7 @@ mod test {
|
||||
im: InputMethod::Active(imdetails_new()),
|
||||
physical_keyboard: Presence::Missing,
|
||||
visibility_override: visibility::State::NotForced,
|
||||
..Application::new(start)
|
||||
..application_with_fake_output(start)
|
||||
};
|
||||
// This reflects the sequence from Wayland:
|
||||
// disable, disable, enable, disable
|
||||
@ -359,7 +386,7 @@ mod test {
|
||||
let state = state.apply_event(Event::InputMethod(InputMethod::Active(imdetails_new())), now);
|
||||
let state = state.apply_event(Event::InputMethod(InputMethod::InactiveSince(now)), now);
|
||||
|
||||
while let animation::Outcome::Visible = state.get_outcome(now).visibility {
|
||||
while let animation::Outcome::Visible(_) = state.get_outcome(now).visibility {
|
||||
now += Duration::from_millis(1);
|
||||
assert!(
|
||||
now < start + Duration::from_millis(250),
|
||||
@ -388,14 +415,14 @@ mod test {
|
||||
im: InputMethod::InactiveSince(now),
|
||||
physical_keyboard: Presence::Missing,
|
||||
visibility_override: visibility::State::NotForced,
|
||||
..Application::new(start)
|
||||
..application_with_fake_output(start)
|
||||
};
|
||||
now += Duration::from_secs(1);
|
||||
|
||||
let state = state.apply_event(Event::Visibility(visibility::Event::ForceVisible), now);
|
||||
assert_eq!(
|
||||
assert_matches!(
|
||||
state.get_outcome(now).visibility,
|
||||
animation::Outcome::Visible,
|
||||
animation::Outcome::Visible(_),
|
||||
"Failed to show: {:?}",
|
||||
now.saturating_duration_since(start),
|
||||
);
|
||||
@ -422,7 +449,7 @@ mod test {
|
||||
im: InputMethod::Active(imdetails_new()),
|
||||
physical_keyboard: Presence::Missing,
|
||||
visibility_override: visibility::State::NotForced,
|
||||
..Application::new(start)
|
||||
..application_with_fake_output(start)
|
||||
};
|
||||
now += Duration::from_secs(1);
|
||||
|
||||
@ -449,9 +476,9 @@ mod test {
|
||||
now += Duration::from_secs(1);
|
||||
let state = state.apply_event(Event::PhysicalKeyboard(Presence::Missing), now);
|
||||
|
||||
assert_eq!(
|
||||
assert_matches!(
|
||||
state.get_outcome(now).visibility,
|
||||
animation::Outcome::Visible,
|
||||
animation::Outcome::Visible(_),
|
||||
"Failed to appear: {:?}",
|
||||
now.saturating_duration_since(start),
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user