live_kit.rs

  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}