Support rendering and touch input for multiple windows

Change-Id: Ib4beb44f810247bee941edd0d757048527eb2304
This commit is contained in:
hikari_no_yume
2025-11-18 00:46:08 +01:00
parent a0847cec51
commit 1e106c0223
4 changed files with 71 additions and 47 deletions

View File

@@ -62,19 +62,11 @@ unsafe fn load_matrix(gles: &mut dyn GLES, matrix: Matrix<4>) {
///
/// Returns the time a recomposite is due, if any.
pub fn recomposite_if_necessary(env: &mut Environment, force: bool) -> Option<Instant> {
// Assumes the windows in the list are ordered back-to-front.
// TODO: this is not correct once we support windowLevel.
// FIXME: There can be windows smaller than the screen! We should draw all
// of them!
let windows = env.framework_state.uikit.ui_view.ui_window.windows.clone();
let Some(top_window) = windows
.into_iter()
.rev()
.find(|&window| !msg![env; window isHidden])
else {
log_dbg!("No visible window, skipping composition");
if !windows.iter().any(|&window| !msg![env; window isHidden]) {
log_dbg!("No visible windows, skipping composition");
return None;
};
}
if find_fullscreen_eagl_layer(env) != nil {
// No composition done, EAGLContext will present directly.
@@ -126,10 +118,15 @@ pub fn recomposite_if_necessary(env: &mut Environment, force: bool) -> Option<In
.composition
.recomposite_next = new_recomposite_next;
let root_layer: id = msg![env; top_window layer];
// Ensure layer bitmaps are up to date.
display_layers(env, root_layer);
let window_layers: Vec<id> = windows
.into_iter()
.map(|window| {
let layer: id = msg![env; window layer];
// Ensure layer bitmaps are up to date.
display_layers(env, layer);
layer
})
.collect();
let screen_bounds: CGRect = {
let screen: id = msg_class![env; UIScreen mainScreen];
@@ -325,17 +322,21 @@ pub fn recomposite_if_necessary(env: &mut Environment, force: bool) -> Option<In
gles.BindBuffer(gles11::ELEMENT_ARRAY_BUFFER, misc_gl_objects.index_buffer);
}
// Here's where the actual drawing happens
unsafe {
composite_layer_recursive(
gles,
&mut env.objc,
&env.mem,
misc_gl_objects,
root_layer,
cumulative_transform,
opacity,
);
// Assumes the windows in the list are ordered back-to-front.
// TODO: this may not be correct once we support windowLevel.
for root_layer in window_layers {
// Here's where the actual drawing happens
unsafe {
composite_layer_recursive(
gles,
&mut env.objc,
&env.mem,
misc_gl_objects,
root_layer,
cumulative_transform,
opacity,
);
}
}
// Clean up some GL state

View File

@@ -124,18 +124,6 @@ pub fn handle_event(env: &mut Environment, event: Event) {
}
fn handle_touches_down(env: &mut Environment, map: HashMap<FingerId, Coords>) {
// Assumes the last window in the list is the one on top and that it
// covers the whole screen. FIXME!
let windows = env.framework_state.uikit.ui_view.ui_window.windows.clone();
let Some(top_window) = windows
.into_iter()
.rev()
.find(|&window| !msg![env; window isHidden])
else {
log!("No visible window, touch events ignored");
return;
};
// UIKit creates and drains autorelease pools when handling events.
let pool: id = msg_class![env; NSAutoreleasePool new];
@@ -229,16 +217,31 @@ fn handle_touches_down(env: &mut Environment, map: HashMap<FingerId, Coords>) {
let touch: id = msg![env; touches_arr objectAtIndex:i];
let &UITouchHostObject { location, .. } = env.objc.borrow(touch);
// FIXME: handle possibly overlapping windows in hit testing.
// Assumes the windows in the list are ordered back-to-front.
// TODO: this may not be correct once we support windowLevel.
let windows = env.framework_state.uikit.ui_view.ui_window.windows.clone();
let Some((window, location_in_window)) = windows.into_iter().rev().find_map(|window| {
let location_in_window: CGPoint =
msg![env; window convertPoint:location fromWindow:nil];
if msg![env; window pointInside:location_in_window withEvent:event] {
Some((window, location_in_window))
} else {
None
}
}) else {
log!(
"Couldn't find a window for touch at {:?}, discarding",
location,
);
continue;
};
let location_in_window: CGPoint =
msg![env; top_window convertPoint:location fromWindow:nil];
let view: id = msg![env; top_window hitTest:location_in_window withEvent:event];
let view: id = msg![env; window hitTest:location_in_window withEvent:event];
if view == nil {
log!(
"Couldn't find a view for touch at {:?} in window {:?}, discarding",
location_in_window,
top_window,
window,
);
continue;
} else {
@@ -250,7 +253,7 @@ fn handle_touches_down(env: &mut Environment, map: HashMap<FingerId, Coords>) {
f
},
location_in_window,
top_window,
window,
);
}
@@ -291,11 +294,11 @@ fn handle_touches_down(env: &mut Environment, map: HashMap<FingerId, Coords>) {
let _: () = msg![env; touches addObject:touch];
retain(env, view);
retain(env, top_window);
retain(env, window);
{
let new_touch = env.objc.borrow_mut::<UITouchHostObject>(touch);
new_touch.view = view;
new_touch.window = top_window;
new_touch.window = window;
}
}

View File

@@ -15,6 +15,7 @@
UIView *ball;
CGFloat ballXVelocity;
CGFloat ballYVelocity;
UIWindow *window2;
- (instancetype)initWithFrame:(CGRect)frame {
[super initWithFrame:frame];
@@ -37,14 +38,23 @@ CGFloat ballYVelocity;
[button1 addTarget:self
action:@selector(goToCALayerTests)
forControlEvents:UIControlEventTouchUpInside];
[self addSubview:button1];
UIButton *button2 = [UIButton buttonWithType:UIButtonTypeRoundedRect];
[button2 setTitle:[NSString stringWithUTF8String:"Window 2"]
forState:UIControlStateNormal];
[button2 setFrame:CGRectMake(40, 140, 240, 40)];
[button2 addTarget:self
action:@selector(toggleWindow)
forControlEvents:UIControlEventTouchUpInside];
[self addSubview:button2];
return self;
}
- (void)dealloc {
[ball release];
[window2 release];
[super dealloc];
}
@@ -84,4 +94,12 @@ CGFloat ballYVelocity;
initWithFrame:[self frame]] autorelease]];
}
- (void)toggleWindow {
if (!window2) {
window2 = [[UIWindow alloc] initWithFrame:CGRectMake(80, 0, 80, 80)];
window2.backgroundColor = [UIColor magentaColor];
}
[window2 setHidden:![window2 isHidden]];
}
@end

View File

@@ -263,6 +263,8 @@ typedef enum {
- (void)setBackgroundColor:(UIColor *)color;
- (CGFloat)alpha;
- (void)setAlpha:(CGFloat)alpha;
- (BOOL)isHidden;
- (void)setHidden:(BOOL)hidden;
@end
@interface UIWindow : UIView
- (void)makeKeyAndVisible;