Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support downcasting #559

Open
tmandry opened this issue Jan 15, 2024 · 1 comment
Open

Support downcasting #559

tmandry opened this issue Jan 15, 2024 · 1 comment
Labels
A-objc2 Affects the `objc2`, `objc-sys` and/or `objc2-encode` crates enhancement New feature or request
Milestone

Comments

@tmandry
Copy link

tmandry commented Jan 15, 2024

I just had to write this.. monstrosity!!

fn ns_screens(&self) -> Vec<NSScreenInfo> {
    NSScreen::screens(self.mtm)
        .iter()
        .flat_map(|s| {
            let desc = s.deviceDescription();
            let cg_id = match desc.get(ns_string!("NSScreenNumber")) {
                Some(val) if unsafe { msg_send![val, isKindOfClass:NSNumber::class() ] } => {
                    let number: &NSNumber = unsafe { std::mem::transmute(val) }; // :(
                    number.as_u32()
                }
                val => {
                    warn!(
                        "Could not get NSScreenNumber for screen with name {:?}: {:?}",
                        unsafe { s.localizedName() },
                        val,
                    );
                    return None;
                }
            };
            Some(NSScreenInfo {
                frame: s.frame(),
                visible_frame: s.visibleFrame(),
                cg_id,
            })
        })
        .collect()
}
Original attempt (segfault)
fn ns_screens(&self) -> Vec<NSScreenInfo> {
    NSScreen::screens(self.mtm)
        .iter()
        .flat_map(|s| {
            let value = s
                .deviceDescription()
                .get(ns_string!("NSScreenNumber"))
                .map(|val: &AnyObject| -> &'_ CFType { unsafe { std::mem::transmute(val) } });
            //  oh, nooo                               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
            let number = value.and_then(|v| v.downcast::<CFNumber>());
            let Some(cg_id) = number else {
                return None;
            };
            Some(NSScreenInfo {
                frame: s.frame(),
                visible_frame: s.visibleFrame(),
                cg_id: cg_id.to_i32().unwrap().try_into().unwrap(), // :(
            })
        })
        .collect()
}

CFType and CFNumber are from core_foundation, which supports downcasting via type id. Its CFNumber interface is weirdly incomplete though, hence all the unwraps.

EDIT: I thought this would work because of toll-free bridging, but it segfaults. So I went with the approach in #474, which is a bit nicer.

I see this is being worked on in #474 but thought this would lend some motivation.

Thanks for your work on this crate!

@madsmtm madsmtm added enhancement New feature or request A-objc2 Affects the `objc2`, `objc-sys` and/or `objc2-encode` crates labels Jan 15, 2024
@madsmtm
Copy link
Owner

madsmtm commented Jan 15, 2024

Heh, yeah, I have to do that dance in Winit too, honestly it's mostly because the deviceDescription API here is terrible.

But I hear what you're saying, and will probably use this issue to track progress, instead of the PR.

I thought this would work because of toll-free bridging, but it segfaults

Yeah, that's because you used the wrong core-foundation type; you can't convert to &'_ CFType, that is basically the same as &Id<AnyObject> - instead, you should've returned CFType directly, but done the conversion using something like CFType::wrap_under_get_rule(val as *const AnyObject as *const c_void) (core-foundation doesn't actually seem to expose a way to do this without the extra retain/release).

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
A-objc2 Affects the `objc2`, `objc-sys` and/or `objc2-encode` crates enhancement New feature or request
Projects
None yet
Development

No branches or pull requests

2 participants