1use std::cell::Cell;
2use std::fmt;
3use std::hash::Hash;
4use std::sync::atomic::{AtomicBool, Ordering};
5use std::sync::{Arc, Mutex};
6use std::time::{Duration, Instant};
7
8use android_activity::input::{InputEvent, KeyAction, Keycode, MotionAction};
9use android_activity::{
10 AndroidApp, AndroidAppWaker, ConfigurationRef, InputStatus, MainEvent, Rect,
11};
12use dpi::{PhysicalInsets, PhysicalPosition, PhysicalSize, Position, Size};
13use tracing::{debug, trace, warn};
14use wingy_core::application::ApplicationHandler;
15use wingy_core::cursor::{Cursor, CustomCursor, CustomCursorSource};
16use wingy_core::error::{
17 CreateWindowError, CustomCursorError, EventLoopError, NotSupportedError, RequestError,
18};
19use wingy_core::event::{self, DeviceId, FingerId, Force, StartCause, SurfaceSizeWriter};
20use wingy_core::event_loop::pump_events::PumpStatus;
21use wingy_core::event_loop::{
22 ActiveEventLoop as RootActiveEventLoop, ControlFlow, DeviceEvents, EventLoopProvider,
23 EventLoopProxy as CoreEventLoopProxy, EventLoopProxyProvider,
24 OwnedDisplayHandle as CoreOwnedDisplayHandle,
25};
26use wingy_core::monitor::{Fullscreen, MonitorHandle as CoreMonitorHandle};
27use wingy_core::window::{
28 self, CursorGrabMode, ImeCapabilities, ImePurpose, ImeRequest, ImeRequestError,
29 ResizeDirection, Theme, Window as CoreWindow, WindowAttributes, WindowButtons, WindowId,
30 WindowLevel,
31};
32
33use crate::keycodes;
34
35static HAS_FOCUS: AtomicBool = AtomicBool::new(true);
36
37fn min_timeout(a: Option<Duration>, b: Option<Duration>) -> Option<Duration> {
41 a.map_or(b, |a_timeout| b.map_or(Some(a_timeout), |b_timeout| Some(a_timeout.min(b_timeout))))
42}
43
44#[derive(Clone, Debug)]
45struct SharedFlagSetter {
46 flag: Arc<AtomicBool>,
47}
48impl SharedFlagSetter {
49 fn set(&self) -> bool {
50 self.flag.compare_exchange(false, true, Ordering::AcqRel, Ordering::Relaxed).is_ok()
51 }
52}
53
54#[derive(Debug)]
55struct SharedFlag {
56 flag: Arc<AtomicBool>,
57}
58
59impl SharedFlag {
64 fn new() -> Self {
65 Self { flag: Arc::new(AtomicBool::new(false)) }
66 }
67
68 fn setter(&self) -> SharedFlagSetter {
69 SharedFlagSetter { flag: self.flag.clone() }
70 }
71
72 fn get_and_reset(&self) -> bool {
73 self.flag.swap(false, std::sync::atomic::Ordering::AcqRel)
74 }
75}
76
77#[derive(Clone)]
78struct RedrawRequester {
79 flag: SharedFlagSetter,
80 waker: AndroidAppWaker,
81}
82
83impl fmt::Debug for RedrawRequester {
84 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
85 f.debug_struct("RedrawRequester").field("flag", &self.flag).finish_non_exhaustive()
86 }
87}
88
89impl RedrawRequester {
90 fn new(flag: &SharedFlag, waker: AndroidAppWaker) -> Self {
91 RedrawRequester { flag: flag.setter(), waker }
92 }
93
94 fn request_redraw(&self) {
95 if self.flag.set() {
96 self.waker.wake();
99 }
100 }
101}
102
103#[derive(Debug)]
104pub struct EventLoop {
105 pub android_app: AndroidApp,
106 window_target: ActiveEventLoop,
107 redraw_flag: SharedFlag,
108 loop_running: bool, running: bool,
110 pending_redraw: bool,
111 cause: StartCause,
112 primary_pointer: Option<FingerId>,
113 ignore_volume_keys: bool,
114 combining_accent: Option<char>,
115 _lifetime: crate::event_loop_lifetime::EventLoopLifetime,
117}
118
119#[derive(Debug, Clone, PartialEq, Eq, Hash)]
120pub struct PlatformSpecificEventLoopAttributes {
121 pub android_app: Option<AndroidApp>,
122 pub ignore_volume_keys: bool,
123}
124
125impl Default for PlatformSpecificEventLoopAttributes {
126 fn default() -> Self {
127 Self { android_app: Default::default(), ignore_volume_keys: true }
128 }
129}
130
131const GLOBAL_WINDOW: WindowId = WindowId::from_raw(0);
133
134impl EventLoop {
135 pub fn new(attributes: &PlatformSpecificEventLoopAttributes) -> Result<Self, EventLoopError> {
136 let android_app = attributes.android_app.as_ref().expect(
137 "An `AndroidApp` as passed to android_main() is required to create an `EventLoop` on \
138 Android",
139 );
140 let lifetime = crate::event_loop_lifetime::EventLoopLifetime::acquire()
141 .ok_or(EventLoopError::RecreationAttempt)?;
142
143 let event_loop_proxy = Arc::new(EventLoopProxy::new(android_app.create_waker()));
144
145 let redraw_flag = SharedFlag::new();
146
147 Ok(Self {
148 android_app: android_app.clone(),
149 primary_pointer: None,
150 window_target: ActiveEventLoop {
151 app: android_app.clone(),
152 control_flow: Cell::new(ControlFlow::default()),
153 exit: Cell::new(false),
154 redraw_requester: RedrawRequester::new(&redraw_flag, android_app.create_waker()),
155 event_loop_proxy,
156 #[cfg(feature = "game-activity")]
157 game_ime: Default::default(),
158 },
159 redraw_flag,
160 loop_running: false,
161 running: false,
162 pending_redraw: false,
163 cause: StartCause::Init,
164 ignore_volume_keys: attributes.ignore_volume_keys,
165 combining_accent: None,
166 _lifetime: lifetime,
167 })
168 }
169
170 pub fn window_target(&self) -> &dyn RootActiveEventLoop {
171 &self.window_target
172 }
173
174 fn single_iteration<A: ApplicationHandler>(
175 &mut self,
176 main_event: Option<MainEvent<'_>>,
177 app: &mut A,
178 ) {
179 trace!("Mainloop iteration");
180
181 let cause = self.cause;
182 let mut pending_redraw = self.pending_redraw;
183 let mut resized = false;
184
185 app.new_events(&self.window_target, cause);
186
187 if let Some(event) = main_event {
188 trace!("Handling main event {:?}", event);
189
190 match event {
191 MainEvent::InitWindow { .. } => {
192 app.can_create_surfaces(&self.window_target);
193 },
194 MainEvent::TerminateWindow { .. } => {
195 app.destroy_surfaces(&self.window_target);
196 },
197 MainEvent::WindowResized { .. } => resized = true,
198 MainEvent::RedrawNeeded { .. } => pending_redraw = true,
199 MainEvent::ContentRectChanged { .. } => {
200 warn!("TODO: find a way to notify application of content rect change");
201 },
202 MainEvent::GainedFocus => {
203 HAS_FOCUS.store(true, Ordering::Relaxed);
204 let event = event::WindowEvent::Focused(true);
205 app.window_event(&self.window_target, GLOBAL_WINDOW, event);
206 },
207 MainEvent::LostFocus => {
208 HAS_FOCUS.store(false, Ordering::Relaxed);
209 let event = event::WindowEvent::Focused(false);
210 app.window_event(&self.window_target, GLOBAL_WINDOW, event);
211 },
212 MainEvent::ConfigChanged { .. } => {
213 let old_scale_factor = scale_factor(&self.android_app);
214 let scale_factor = scale_factor(&self.android_app);
215 if (scale_factor - old_scale_factor).abs() < f64::EPSILON {
216 let new_surface_size = Arc::new(Mutex::new(screen_size(&self.android_app)));
217 let event = event::WindowEvent::ScaleFactorChanged {
218 surface_size_writer: SurfaceSizeWriter::new(Arc::downgrade(
219 &new_surface_size,
220 )),
221 scale_factor,
222 };
223
224 app.window_event(&self.window_target, GLOBAL_WINDOW, event);
225 }
226 },
227 MainEvent::LowMemory => {
228 app.memory_warning(&self.window_target);
229 },
230 MainEvent::Start => {
231 app.resumed(self.window_target());
232 },
233 MainEvent::Resume { .. } => {
234 debug!("App Resumed - is running");
235 self.running = true;
237 },
238 MainEvent::SaveState { .. } => {
239 warn!("TODO: forward saveState notification to application");
242 },
243 MainEvent::Pause => {
244 debug!("App Paused - stopped running");
245 self.running = false;
247 },
248 MainEvent::Stop => {
249 app.suspended(self.window_target());
250 },
251 MainEvent::Destroy => {
252 self.window_target.exit();
255 },
256 MainEvent::InsetsChanged { .. } => {
257 warn!("TODO: handle Android InsetsChanged notification");
259 },
260 unknown => {
261 trace!("Unknown MainEvent {unknown:?} (ignored)");
262 },
263 }
264 } else {
265 trace!("No main event to handle");
266 }
267
268 let android_app = self.android_app.clone();
271
272 match android_app.input_events_iter() {
274 Ok(mut input_iter) => loop {
275 let read_event =
276 input_iter.next(|event| self.handle_input_event(&android_app, event, app));
277
278 if !read_event {
279 break;
280 }
281 },
282 Err(err) => {
283 tracing::warn!("Failed to get input events iterator: {err:?}");
284 },
285 }
286
287 if self.window_target.event_loop_proxy.wake_up.swap(false, Ordering::Relaxed) {
288 app.proxy_wake_up(&self.window_target);
289 }
290
291 #[cfg(feature = "game-activity")]
292 self.dispatch_game_ime(app);
293
294 if self.running {
295 if resized {
296 let size = if let Some(native_window) = self.android_app.native_window().as_ref() {
297 let width = native_window.width() as _;
298 let height = native_window.height() as _;
299 PhysicalSize::new(width, height)
300 } else {
301 PhysicalSize::new(0, 0)
302 };
303 let event = event::WindowEvent::SurfaceResized(size);
304 app.window_event(&self.window_target, GLOBAL_WINDOW, event);
305 }
306
307 pending_redraw |= self.redraw_flag.get_and_reset();
308 if pending_redraw {
309 pending_redraw = false;
310 let event = event::WindowEvent::RedrawRequested;
311 app.window_event(&self.window_target, GLOBAL_WINDOW, event);
312 }
313 }
314
315 app.about_to_wait(&self.window_target);
317
318 self.pending_redraw = pending_redraw;
319 }
320
321 fn handle_input_event<A: ApplicationHandler>(
322 &mut self,
323 android_app: &AndroidApp,
324 event: &InputEvent<'_>,
325 app: &mut A,
326 ) -> InputStatus {
327 #[cfg(feature = "game-activity")]
328 self.dispatch_game_ime(app);
329 let mut input_status = InputStatus::Handled;
330 match event {
331 #[cfg(feature = "game-activity")]
332 InputEvent::TextEvent(state) => {
333 let effect = self.window_target.game_ime.lock().unwrap().receive(state);
334 effect.apply(android_app);
335 self.dispatch_game_ime(app);
336 },
337 #[cfg(feature = "game-activity")]
338 InputEvent::TextAction(_) => {
339 if self.window_target.game_ime.lock().unwrap().capabilities.is_some() {
340 for state in [event::ElementState::Pressed, event::ElementState::Released] {
341 app.window_event(
342 &self.window_target,
343 GLOBAL_WINDOW,
344 event::WindowEvent::KeyboardInput {
345 device_id: None,
346 is_synthetic: false,
347 event: event::KeyEvent {
348 state,
349 physical_key: keycodes::to_physical_key(Keycode::Enter),
350 logical_key: keycodes::to_logical(None, Keycode::Enter),
351 key_without_modifiers: keycodes::to_logical(
352 None,
353 Keycode::Enter,
354 ),
355 location: keycodes::to_location(Keycode::Enter),
356 repeat: false,
357 text: None,
358 text_with_all_modifiers: None,
359 },
360 },
361 );
362 }
363 }
364 },
365 InputEvent::MotionEvent(motion_event) => {
366 let device_id = Some(DeviceId::from_raw(motion_event.device_id() as i64));
367 let action = motion_event.action();
368
369 let pointers: Option<
370 Box<dyn Iterator<Item = android_activity::input::Pointer<'_>>>,
371 > = match action {
372 MotionAction::Down
373 | MotionAction::PointerDown
374 | MotionAction::Up
375 | MotionAction::PointerUp => Some(Box::new(std::iter::once(
376 motion_event.pointer_at_index(motion_event.pointer_index()),
377 ))),
378 MotionAction::Move | MotionAction::Cancel => {
379 Some(Box::new(motion_event.pointers()))
380 },
381 _ => None,
383 };
384
385 for pointer in pointers.into_iter().flatten() {
386 let tool_type = pointer.tool_type();
387 let position = PhysicalPosition { x: pointer.x() as _, y: pointer.y() as _ };
388 trace!(
389 "Input event {device_id:?}, {action:?}, loc={position:?}, \
390 pointer={pointer:?}, tool_type={tool_type:?}"
391 );
392 let finger_id = FingerId::from_raw(pointer.pointer_id() as usize);
393 let force = Some(Force::Normalized(pointer.pressure() as f64));
394
395 match action {
396 MotionAction::Down | MotionAction::PointerDown => {
397 let primary = action == MotionAction::Down;
398 if primary {
399 self.primary_pointer = Some(finger_id);
400 }
401 let event = event::WindowEvent::PointerEntered {
402 device_id,
403 primary,
404 position,
405 kind: match tool_type {
406 android_activity::input::ToolType::Finger => {
407 event::PointerKind::Touch(finger_id)
408 },
409 android_activity::input::ToolType::Mouse => continue,
411 _ => event::PointerKind::Unknown,
412 },
413 };
414 app.window_event(&self.window_target, GLOBAL_WINDOW, event);
415 let event = event::WindowEvent::PointerButton {
416 device_id,
417 primary,
418 state: event::ElementState::Pressed,
419 position,
420 button: match tool_type {
421 android_activity::input::ToolType::Finger => {
422 event::ButtonSource::Touch { finger_id, force }
423 },
424 android_activity::input::ToolType::Mouse => continue,
426 _ => event::ButtonSource::Unknown(0),
427 },
428 is_macos_activation_click: false,
429 };
430 app.window_event(&self.window_target, GLOBAL_WINDOW, event);
431 },
432 MotionAction::Move => {
433 let primary = self.primary_pointer == Some(finger_id);
434 let event = event::WindowEvent::PointerMoved {
435 device_id,
436 primary,
437 position,
438 source: match tool_type {
439 android_activity::input::ToolType::Finger => {
440 event::PointerSource::Touch { finger_id, force }
441 },
442 android_activity::input::ToolType::Mouse => continue,
444 _ => event::PointerSource::Unknown,
445 },
446 };
447 app.window_event(&self.window_target, GLOBAL_WINDOW, event);
448 },
449 MotionAction::Up | MotionAction::PointerUp | MotionAction::Cancel => {
450 let primary = action == MotionAction::Up
451 || (action == MotionAction::Cancel
452 && self.primary_pointer == Some(finger_id));
453
454 if primary {
455 self.primary_pointer = None;
456 }
457
458 if let MotionAction::Up | MotionAction::PointerUp = action {
459 let event = event::WindowEvent::PointerButton {
460 device_id,
461 primary,
462 state: event::ElementState::Released,
463 position,
464 button: match tool_type {
465 android_activity::input::ToolType::Finger => {
466 event::ButtonSource::Touch { finger_id, force }
467 },
468 android_activity::input::ToolType::Mouse => continue,
470 _ => event::ButtonSource::Unknown(0),
471 },
472 is_macos_activation_click: false,
473 };
474 app.window_event(&self.window_target, GLOBAL_WINDOW, event);
475 }
476
477 let event = event::WindowEvent::PointerLeft {
478 device_id,
479 primary,
480 position: Some(position),
481 kind: match tool_type {
482 android_activity::input::ToolType::Finger => {
483 event::PointerKind::Touch(finger_id)
484 },
485 android_activity::input::ToolType::Mouse => continue,
487 _ => event::PointerKind::Unknown,
488 },
489 };
490 app.window_event(&self.window_target, GLOBAL_WINDOW, event);
491 },
492 _ => unreachable!(),
493 }
494 }
495 },
496 InputEvent::KeyEvent(key) => {
497 match key.key_code() {
498 Keycode::VolumeUp | Keycode::VolumeDown | Keycode::VolumeMute
503 if self.ignore_volume_keys =>
504 {
505 input_status = InputStatus::Unhandled
506 },
507 keycode => {
508 let state = match key.action() {
509 KeyAction::Down => event::ElementState::Pressed,
510 KeyAction::Up => event::ElementState::Released,
511 _ => event::ElementState::Released,
512 };
513
514 let key_char = keycodes::character_map_and_combine_key(
515 android_app,
516 key,
517 &mut self.combining_accent,
518 );
519
520 let logical_key = keycodes::to_logical(key_char, keycode);
521 #[cfg(feature = "game-activity")]
522 if matches!(logical_key, wingy_core::keyboard::Key::Character(_))
523 && self.window_target.game_ime.lock().unwrap().capabilities.is_some()
524 {
525 return InputStatus::Handled;
526 }
527 let text = if state == event::ElementState::Pressed {
528 logical_key.to_text().map(smol_str::SmolStr::new)
529 } else {
530 None
531 };
532
533 let event = event::WindowEvent::KeyboardInput {
534 device_id: Some(DeviceId::from_raw(key.device_id() as i64)),
535 event: event::KeyEvent {
536 state,
537 physical_key: keycodes::to_physical_key(keycode),
538 logical_key,
539 location: keycodes::to_location(keycode),
540 repeat: key.repeat_count() > 0,
541 text: text.clone(),
542 text_with_all_modifiers: text,
543 key_without_modifiers: keycodes::to_logical(key_char, keycode),
544 },
545 is_synthetic: false,
546 };
547
548 app.window_event(&self.window_target, GLOBAL_WINDOW, event);
549 },
550 }
551 },
552 _ => {
553 warn!("Unknown android_activity input event {event:?}")
554 },
555 }
556
557 input_status
558 }
559
560 #[cfg(feature = "game-activity")]
561 fn dispatch_game_ime<A: ApplicationHandler>(&mut self, app: &mut A) {
562 let events = self.window_target.game_ime.lock().unwrap().drain();
563 for event in events {
564 app.window_event(&self.window_target, GLOBAL_WINDOW, event::WindowEvent::Ime(event));
565 }
566 }
567
568 pub fn run_app_on_demand<A: ApplicationHandler>(
569 &mut self,
570 mut app: A,
571 ) -> Result<(), EventLoopError> {
572 self.window_target.clear_exit();
573 loop {
574 match self.pump_app_events(None, &mut app) {
575 PumpStatus::Exit(0) => {
576 break Ok(());
577 },
578 PumpStatus::Exit(code) => {
579 break Err(EventLoopError::ExitFailure(code));
580 },
581 _ => {
582 continue;
583 },
584 }
585 }
586 }
587
588 pub fn pump_app_events<A: ApplicationHandler>(
589 &mut self,
590 timeout: Option<Duration>,
591 mut app: A,
592 ) -> PumpStatus {
593 if !self.loop_running {
594 self.loop_running = true;
595
596 self.pending_redraw = false;
600 self.cause = StartCause::Init;
601
602 self.single_iteration(None, &mut app);
604 }
605
606 if !self.exiting() {
609 self.poll_events_with_timeout(timeout, &mut app);
610 }
611 if self.exiting() {
612 self.loop_running = false;
613
614 PumpStatus::Exit(0)
615 } else {
616 PumpStatus::Continue
617 }
618 }
619
620 fn poll_events_with_timeout<A: ApplicationHandler>(
621 &mut self,
622 mut timeout: Option<Duration>,
623 app: &mut A,
624 ) {
625 let start = Instant::now();
626
627 self.pending_redraw |= self.redraw_flag.get_and_reset();
628
629 timeout = if self.running
630 && (self.pending_redraw
631 || self.window_target.event_loop_proxy.wake_up.load(Ordering::Relaxed))
632 {
633 Some(Duration::ZERO)
635 } else {
636 let control_flow_timeout = match self.control_flow() {
637 ControlFlow::Wait => None,
638 ControlFlow::Poll => Some(Duration::ZERO),
639 ControlFlow::WaitUntil(wait_deadline) => {
640 Some(wait_deadline.saturating_duration_since(start))
641 },
642 };
643
644 min_timeout(control_flow_timeout, timeout)
645 };
646
647 let android_app = self.android_app.clone(); android_app.poll_events(timeout, |poll_event| {
649 let mut main_event = None;
650
651 match poll_event {
652 android_activity::PollEvent::Wake => {
653 self.pending_redraw |= self.redraw_flag.get_and_reset();
662 if !self.running
663 || (!self.pending_redraw
664 && !self.window_target.event_loop_proxy.wake_up.load(Ordering::Relaxed))
665 {
666 return;
667 }
668 },
669 android_activity::PollEvent::Timeout => {},
670 android_activity::PollEvent::Main(event) => {
671 main_event = Some(event);
672 },
673 unknown_event => {
674 warn!("Unknown poll event {unknown_event:?} (ignored)");
675 },
676 }
677
678 self.cause = match self.control_flow() {
679 ControlFlow::Poll => StartCause::Poll,
680 ControlFlow::Wait => StartCause::WaitCancelled { start, requested_resume: None },
681 ControlFlow::WaitUntil(deadline) => {
682 if Instant::now() < deadline {
683 StartCause::WaitCancelled { start, requested_resume: Some(deadline) }
684 } else {
685 StartCause::ResumeTimeReached { start, requested_resume: deadline }
686 }
687 },
688 };
689
690 self.single_iteration(main_event, app);
691 });
692 }
693
694 fn control_flow(&self) -> ControlFlow {
695 self.window_target.control_flow()
696 }
697
698 fn exiting(&self) -> bool {
699 self.window_target.exiting()
700 }
701}
702
703impl EventLoopProvider for EventLoop {
704 fn run_app<A: ApplicationHandler + 'static>(
705 mut self,
706 mut app: A,
707 ) -> Result<(), EventLoopError> {
708 let result = self.run_app_on_demand(&mut app);
709 drop(app);
711 result
712 }
713
714 fn create_proxy(&self) -> CoreEventLoopProxy {
715 self.window_target().create_proxy()
716 }
717
718 fn owned_display_handle(&self) -> CoreOwnedDisplayHandle {
719 self.window_target().owned_display_handle()
720 }
721
722 fn listen_device_events(&self, allowed: DeviceEvents) {
723 self.window_target().listen_device_events(allowed);
724 }
725
726 fn set_control_flow(&self, control_flow: ControlFlow) {
727 self.window_target().set_control_flow(control_flow);
728 }
729
730 fn create_custom_cursor(
731 &self,
732 custom_cursor: CustomCursorSource,
733 ) -> Result<CustomCursor, CustomCursorError> {
734 self.window_target().create_custom_cursor(custom_cursor)
735 }
736}
737
738pub struct EventLoopProxy {
739 wake_up: AtomicBool,
740 waker: AndroidAppWaker,
741}
742
743impl fmt::Debug for EventLoopProxy {
744 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
745 f.debug_struct("EventLoopProxy").field("wake_up", &self.wake_up).finish_non_exhaustive()
746 }
747}
748
749impl EventLoopProxy {
750 fn new(waker: AndroidAppWaker) -> Self {
751 Self { wake_up: AtomicBool::new(false), waker }
752 }
753}
754
755impl EventLoopProxyProvider for EventLoopProxy {
756 fn wake_up(&self) {
757 self.wake_up.store(true, Ordering::Relaxed);
758 self.waker.wake();
759 }
760}
761
762#[derive(Debug)]
763pub struct ActiveEventLoop {
764 pub(crate) app: AndroidApp,
765 control_flow: Cell<ControlFlow>,
766 exit: Cell<bool>,
767 redraw_requester: RedrawRequester,
768 event_loop_proxy: Arc<EventLoopProxy>,
769 #[cfg(feature = "game-activity")]
770 game_ime: Arc<Mutex<crate::game_ime::GameIme>>,
771}
772
773impl ActiveEventLoop {
774 fn clear_exit(&self) {
775 self.exit.set(false);
776 }
777}
778
779impl RootActiveEventLoop for ActiveEventLoop {
780 fn create_proxy(&self) -> CoreEventLoopProxy {
781 CoreEventLoopProxy::new(self.event_loop_proxy.clone())
782 }
783
784 fn create_window(
785 &self,
786 window_attributes: WindowAttributes,
787 ) -> Result<Box<dyn CoreWindow>, CreateWindowError> {
788 Ok(Box::new(Window::new(self, window_attributes)?))
789 }
790
791 fn create_custom_cursor(
792 &self,
793 _source: CustomCursorSource,
794 ) -> Result<CustomCursor, CustomCursorError> {
795 Err(CustomCursorError::NotSupported)
796 }
797
798 fn available_monitors(&self) -> Box<dyn Iterator<Item = CoreMonitorHandle>> {
799 Box::new(std::iter::empty())
800 }
801
802 fn primary_monitor(&self) -> Option<CoreMonitorHandle> {
803 None
804 }
805
806 fn system_theme(&self) -> Option<Theme> {
807 None
808 }
809
810 fn listen_device_events(&self, _allowed: DeviceEvents) {}
811
812 fn set_control_flow(&self, control_flow: ControlFlow) {
813 self.control_flow.set(control_flow)
814 }
815
816 fn control_flow(&self) -> ControlFlow {
817 self.control_flow.get()
818 }
819
820 fn exit(&self) {
821 self.exit.set(true)
822 }
823
824 fn exiting(&self) -> bool {
825 self.exit.get()
826 }
827
828 fn owned_display_handle(&self) -> CoreOwnedDisplayHandle {
829 CoreOwnedDisplayHandle::new(Arc::new(OwnedDisplayHandle))
830 }
831
832 fn rwh_06_handle(&self) -> &dyn rwh_06::HasDisplayHandle {
833 self
834 }
835}
836
837impl rwh_06::HasDisplayHandle for ActiveEventLoop {
838 fn display_handle(&self) -> Result<rwh_06::DisplayHandle<'_>, rwh_06::HandleError> {
839 let raw = rwh_06::AndroidDisplayHandle::new();
840 Ok(unsafe { rwh_06::DisplayHandle::borrow_raw(raw.into()) })
841 }
842}
843
844#[derive(Clone, PartialEq, Eq)]
845pub(crate) struct OwnedDisplayHandle;
846
847impl rwh_06::HasDisplayHandle for OwnedDisplayHandle {
848 fn display_handle(&self) -> Result<rwh_06::DisplayHandle<'_>, rwh_06::HandleError> {
849 let raw = rwh_06::AndroidDisplayHandle::new();
850 Ok(unsafe { rwh_06::DisplayHandle::borrow_raw(raw.into()) })
851 }
852}
853
854#[derive(Clone, Copy, Debug, Default, Eq, PartialEq)]
855pub struct PlatformSpecificWindowAttributes;
856
857#[derive(Debug)]
858pub struct Window {
859 app: AndroidApp,
860 #[cfg(not(feature = "game-activity"))]
861 ime_capabilities: Mutex<Option<ImeCapabilities>>,
862 #[cfg(feature = "game-activity")]
863 game_ime: Arc<Mutex<crate::game_ime::GameIme>>,
864 redraw_requester: RedrawRequester,
865}
866
867impl Window {
868 pub(crate) fn new(
869 el: &ActiveEventLoop,
870 window_attrs: window::WindowAttributes,
871 ) -> Result<Self, CreateWindowError> {
872 match window_attrs.window_type() {
873 window::WindowType::Window => (),
874 window::WindowType::Popup => return Err(CreateWindowError::PopupNotSupported),
875 _ => panic!("Unknown WindowType"),
876 }
877
878 Ok(Self {
881 app: el.app.clone(),
882 #[cfg(not(feature = "game-activity"))]
883 ime_capabilities: Default::default(),
884 #[cfg(feature = "game-activity")]
885 game_ime: el.game_ime.clone(),
886 redraw_requester: el.redraw_requester.clone(),
887 })
888 }
889
890 pub(crate) fn config(&self) -> ConfigurationRef {
891 self.app.config()
892 }
893
894 pub(crate) fn content_rect(&self) -> Rect {
895 self.app.content_rect()
896 }
897
898 #[allow(deprecated)]
900 fn raw_window_handle_rwh_06(&self) -> Result<rwh_06::RawWindowHandle, rwh_06::HandleError> {
901 use rwh_06::HasRawWindowHandle;
902
903 if let Some(native_window) = self.app.native_window().as_ref() {
904 native_window.raw_window_handle()
905 } else {
906 tracing::error!(
907 "Cannot get the native window, it's null and will always be null before \
908 Event::Resumed and after Event::Suspended. Make sure you only call this function \
909 between those events."
910 );
911 Err(rwh_06::HandleError::Unavailable)
912 }
913 }
914
915 fn raw_display_handle_rwh_06(&self) -> Result<rwh_06::RawDisplayHandle, rwh_06::HandleError> {
916 Ok(rwh_06::RawDisplayHandle::Android(rwh_06::AndroidDisplayHandle::new()))
917 }
918}
919
920impl rwh_06::HasDisplayHandle for Window {
921 fn display_handle(&self) -> Result<rwh_06::DisplayHandle<'_>, rwh_06::HandleError> {
922 let raw = self.raw_display_handle_rwh_06()?;
923 unsafe { Ok(rwh_06::DisplayHandle::borrow_raw(raw)) }
924 }
925}
926
927impl rwh_06::HasWindowHandle for Window {
928 fn window_handle(&self) -> Result<rwh_06::WindowHandle<'_>, rwh_06::HandleError> {
929 let raw = self.raw_window_handle_rwh_06()?;
930 unsafe { Ok(rwh_06::WindowHandle::borrow_raw(raw)) }
931 }
932}
933
934impl CoreWindow for Window {
935 fn window_type(&self) -> window::WindowType {
936 window::WindowType::Window
937 }
938
939 fn id(&self) -> WindowId {
940 GLOBAL_WINDOW
941 }
942
943 fn primary_monitor(&self) -> Option<CoreMonitorHandle> {
944 None
945 }
946
947 fn available_monitors(&self) -> Box<dyn Iterator<Item = CoreMonitorHandle>> {
948 Box::new(std::iter::empty())
949 }
950
951 fn current_monitor(&self) -> Option<CoreMonitorHandle> {
952 None
953 }
954
955 fn scale_factor(&self) -> f64 {
956 scale_factor(&self.app)
957 }
958
959 fn request_redraw(&self) {
960 self.redraw_requester.request_redraw()
961 }
962
963 fn pre_present_notify(&self) {}
964
965 fn surface_position(&self) -> PhysicalPosition<i32> {
966 (0, 0).into()
967 }
968
969 fn outer_position(&self) -> Result<PhysicalPosition<i32>, RequestError> {
970 Err(NotSupportedError::new("outer_position is not supported").into())
971 }
972
973 fn set_outer_position(&self, _position: Position) {
974 }
976
977 fn surface_size(&self) -> PhysicalSize<u32> {
978 self.outer_size()
979 }
980
981 fn request_surface_size(&self, _size: Size) -> Option<PhysicalSize<u32>> {
982 Some(self.surface_size())
983 }
984
985 fn outer_size(&self) -> PhysicalSize<u32> {
986 screen_size(&self.app)
987 }
988
989 fn safe_area(&self) -> PhysicalInsets<u32> {
990 PhysicalInsets::new(0, 0, 0, 0)
991 }
992
993 fn set_min_surface_size(&self, _: Option<Size>) {}
994
995 fn set_max_surface_size(&self, _: Option<Size>) {}
996
997 fn surface_resize_increments(&self) -> Option<PhysicalSize<u32>> {
998 None
999 }
1000
1001 fn set_surface_resize_increments(&self, _increments: Option<Size>) {}
1002
1003 fn set_title(&self, _title: &str) {}
1004
1005 fn set_transparent(&self, _transparent: bool) {}
1006
1007 fn set_blur(&self, _blur: bool) {}
1008
1009 fn set_visible(&self, _visibility: bool) {}
1010
1011 fn is_visible(&self) -> Option<bool> {
1012 None
1013 }
1014
1015 fn set_resizable(&self, _resizeable: bool) {}
1016
1017 fn is_resizable(&self) -> bool {
1018 false
1019 }
1020
1021 fn set_enabled_buttons(&self, _buttons: WindowButtons) {}
1022
1023 fn enabled_buttons(&self) -> WindowButtons {
1024 WindowButtons::all()
1025 }
1026
1027 fn set_minimized(&self, _minimized: bool) {}
1028
1029 fn is_minimized(&self) -> Option<bool> {
1030 None
1031 }
1032
1033 fn set_maximized(&self, _maximized: bool) {}
1034
1035 fn is_maximized(&self) -> bool {
1036 false
1037 }
1038
1039 fn set_fullscreen(&self, _monitor: Option<Fullscreen>) {
1040 warn!("Cannot set fullscreen on Android");
1041 }
1042
1043 fn fullscreen(&self) -> Option<Fullscreen> {
1044 None
1045 }
1046
1047 fn set_decorations(&self, _decorations: bool) {}
1048
1049 fn is_decorated(&self) -> bool {
1050 true
1051 }
1052
1053 fn set_window_level(&self, _level: WindowLevel) {}
1054
1055 fn set_window_icon(&self, _window_icon: Option<wingy_core::icon::Icon>) {}
1056
1057 fn set_ime_cursor_area(&self, _position: Position, _size: Size) {}
1058
1059 fn request_ime_update(&self, request: ImeRequest) -> Result<(), ImeRequestError> {
1060 #[cfg(feature = "game-activity")]
1061 {
1062 let effect = self.game_ime.lock().unwrap().request(request)?;
1063 effect.apply(&self.app);
1064 Ok(())
1065 }
1066 #[cfg(not(feature = "game-activity"))]
1067 {
1068 let mut current_caps = self.ime_capabilities.lock().unwrap();
1069 match request {
1070 ImeRequest::Enable(enable) => {
1071 let (capabilities, _) = enable.into_raw();
1072 if current_caps.is_some() {
1073 return Err(ImeRequestError::AlreadyEnabled);
1074 }
1075 *current_caps = Some(capabilities);
1076 self.app.show_soft_input(true);
1077 },
1078 ImeRequest::Update(_) => {
1079 if current_caps.is_none() {
1080 return Err(ImeRequestError::NotEnabled);
1081 }
1082 },
1083 ImeRequest::Disable => {
1084 *current_caps = None;
1085 self.app.hide_soft_input(true);
1086 },
1087 _ => return Err(ImeRequestError::NotSupported),
1088 }
1089
1090 Ok(())
1091 }
1092 }
1093
1094 fn ime_capabilities(&self) -> Option<ImeCapabilities> {
1095 #[cfg(feature = "game-activity")]
1096 return self.game_ime.lock().unwrap().capabilities;
1097 #[cfg(not(feature = "game-activity"))]
1098 *self.ime_capabilities.lock().unwrap()
1099 }
1100
1101 fn set_ime_purpose(&self, _purpose: ImePurpose) {}
1102
1103 fn focus_window(&self) {}
1104
1105 fn request_user_attention(&self, _request_type: Option<window::UserAttentionType>) {}
1106
1107 fn set_cursor(&self, _: Cursor) {}
1108
1109 fn set_cursor_position(&self, _: Position) -> Result<(), RequestError> {
1110 Err(NotSupportedError::new("set_cursor_position is not supported").into())
1111 }
1112
1113 fn set_cursor_grab(&self, _: CursorGrabMode) -> Result<(), RequestError> {
1114 Err(NotSupportedError::new("set_cursor_grab is not supported").into())
1115 }
1116
1117 fn set_cursor_visible(&self, _: bool) {}
1118
1119 fn drag_window(&self) -> Result<(), RequestError> {
1120 Err(NotSupportedError::new("drag_window is not supported").into())
1121 }
1122
1123 fn drag_resize_window(&self, _direction: ResizeDirection) -> Result<(), RequestError> {
1124 Err(NotSupportedError::new("drag_resize_window is not supported").into())
1125 }
1126
1127 #[inline]
1128 fn show_window_menu(&self, _position: Position) {}
1129
1130 fn set_cursor_hittest(&self, _hittest: bool) -> Result<(), RequestError> {
1131 Err(NotSupportedError::new("set_cursor_hittest is not supported").into())
1132 }
1133
1134 fn set_theme(&self, _theme: Option<Theme>) {}
1135
1136 fn theme(&self) -> Option<Theme> {
1137 None
1138 }
1139
1140 fn set_content_protected(&self, _protected: bool) {}
1141
1142 fn has_focus(&self) -> bool {
1143 HAS_FOCUS.load(Ordering::Relaxed)
1144 }
1145
1146 fn title(&self) -> String {
1147 String::new()
1148 }
1149
1150 fn reset_dead_keys(&self) {}
1151
1152 fn rwh_06_display_handle(&self) -> &dyn rwh_06::HasDisplayHandle {
1153 self
1154 }
1155
1156 fn rwh_06_window_handle(&self) -> &dyn rwh_06::HasWindowHandle {
1157 self
1158 }
1159}
1160
1161fn screen_size(app: &AndroidApp) -> PhysicalSize<u32> {
1162 if let Some(native_window) = app.native_window() {
1163 PhysicalSize::new(native_window.width() as _, native_window.height() as _)
1164 } else {
1165 PhysicalSize::new(0, 0)
1166 }
1167}
1168
1169fn scale_factor(app: &AndroidApp) -> f64 {
1170 app.config().density().map(|dpi| dpi as f64 / 160.0).unwrap_or(1.0)
1171}