1use core_foundation::{
2 array::CFArray,
3 base::{TCFType, TCFTypeRef},
4 dictionary::CFDictionary,
5 number::CFNumber,
6 string::{CFString, CFStringRef},
7};
8use core_graphics::window::{
9 kCGNullWindowID, kCGWindowListOptionExcludeDesktopElements, kCGWindowListOptionOnScreenOnly,
10 kCGWindowNumber, kCGWindowOwnerName, kCGWindowOwnerPID, CGWindowListCopyWindowInfo,
11};
12use futures::{channel::oneshot, Future};
13use std::ffi::c_void;
14
15extern "C" {
16 fn LKRelease(object: *const c_void);
17 fn LKRoomCreate() -> *const c_void;
18 fn LKRoomConnect(
19 room: *const c_void,
20 url: CFStringRef,
21 token: CFStringRef,
22 callback: extern "C" fn(*mut c_void) -> (),
23 callback_data: *mut c_void,
24 );
25 fn LKCreateScreenShareTrackForWindow(windowId: u32) -> *const c_void;
26}
27
28pub struct Room(*const c_void);
29
30impl Room {
31 pub fn new() -> Self {
32 Self(unsafe { LKRoomCreate() })
33 }
34
35 pub fn connect(&self, url: &str, token: &str) -> impl Future<Output = ()> {
36 let url = CFString::new(url);
37 let token = CFString::new(token);
38
39 let (tx, rx) = oneshot::channel();
40 extern "C" fn did_connect(tx: *mut c_void) {
41 let tx = unsafe { Box::from_raw(tx as *mut oneshot::Sender<()>) };
42 let _ = tx.send(());
43 }
44
45 unsafe {
46 LKRoomConnect(
47 self.0,
48 url.as_concrete_TypeRef(),
49 token.as_concrete_TypeRef(),
50 did_connect,
51 Box::into_raw(Box::new(tx)) as *mut c_void,
52 )
53 }
54
55 async { rx.await.unwrap() }
56 }
57}
58
59impl Drop for Room {
60 fn drop(&mut self) {
61 unsafe { LKRelease(self.0) }
62 }
63}
64
65pub struct LocalVideoTrack(*const c_void);
66
67impl LocalVideoTrack {
68 pub fn screen_share_for_window(window_id: u32) -> Self {
69 Self(unsafe { LKCreateScreenShareTrackForWindow(window_id) })
70 }
71}
72
73impl Drop for LocalVideoTrack {
74 fn drop(&mut self) {
75 unsafe { LKRelease(self.0) }
76 }
77}
78
79#[derive(Debug)]
80pub struct WindowInfo {
81 pub id: u32,
82 pub owner_pid: i32,
83 pub owner_name: Option<String>,
84}
85
86pub fn list_windows() -> Vec<WindowInfo> {
87 unsafe {
88 let dicts = CFArray::<CFDictionary>::wrap_under_get_rule(CGWindowListCopyWindowInfo(
89 kCGWindowListOptionOnScreenOnly | kCGWindowListOptionExcludeDesktopElements,
90 kCGNullWindowID,
91 ));
92
93 dicts
94 .iter()
95 .map(|dict| {
96 let id =
97 CFNumber::wrap_under_get_rule(*dict.get(kCGWindowNumber.as_void_ptr()) as _)
98 .to_i64()
99 .unwrap() as u32;
100
101 let owner_pid =
102 CFNumber::wrap_under_get_rule(*dict.get(kCGWindowOwnerPID.as_void_ptr()) as _)
103 .to_i32()
104 .unwrap();
105
106 let owner_name = dict
107 .find(kCGWindowOwnerName.as_void_ptr())
108 .map(|name| CFString::wrap_under_get_rule(*name as _).to_string());
109 WindowInfo {
110 id,
111 owner_pid,
112 owner_name,
113 }
114 })
115 .collect()
116 }
117}