Add ability to get a screen share track for a window

And also list windows
This commit is contained in:
Nathan Sobo 2022-09-06 21:19:00 -06:00 committed by Antonio Scandurra
parent 5347c7d678
commit 7bf64ec23e
5 changed files with 93 additions and 23 deletions

1
Cargo.lock generated
View file

@ -2941,6 +2941,7 @@ name = "live_kit"
version = "0.1.0" version = "0.1.0"
dependencies = [ dependencies = [
"core-foundation", "core-foundation",
"core-graphics",
"futures", "futures",
"serde", "serde",
"serde_json", "serde_json",

View file

@ -7,7 +7,7 @@ use gpui::{
platform::current::Surface, platform::current::Surface,
Menu, MenuItem, ViewContext, Menu, MenuItem, ViewContext,
}; };
use live_kit::Room; use live_kit::{LocalVideoTrack, Room};
use log::LevelFilter; use log::LevelFilter;
use media::core_video::CVImageBuffer; use media::core_video::CVImageBuffer;
use simplelog::SimpleLogger; use simplelog::SimpleLogger;
@ -42,13 +42,17 @@ fn main() {
.unwrap(); .unwrap();
let room = live_kit::Room::new(); let room = live_kit::Room::new();
cx.spawn(|cx| async move { cx.foreground()
println!("connecting..."); .spawn(async move {
room.connect("wss://zed.livekit.cloud", &token).await; println!("connecting...");
println!("connected!"); room.connect("wss://zed.livekit.cloud", &token).await;
drop(room); let windows = live_kit::list_windows();
}) println!("connected! {:?}", windows);
.detach();
let window_id = windows.iter().next().unwrap().id;
let track = LocalVideoTrack::screen_share_for_window(window_id);
})
.detach();
// cx.add_window(Default::default(), |cx| ScreenCaptureView::new(cx)); // cx.add_window(Default::default(), |cx| ScreenCaptureView::new(cx));
}); });

View file

@ -10,6 +10,7 @@ doctest = false
[dependencies] [dependencies]
core-foundation = "0.9.3" core-foundation = "0.9.3"
core-graphics = "0.22.3"
futures = "0.3" futures = "0.3"
[build-dependencies] [build-dependencies]

View file

@ -1,16 +1,16 @@
import Foundation import Foundation
import LiveKit import LiveKit
@_cdecl("LKRelease")
public func LKRelease(ptr: UnsafeRawPointer) {
let _ = Unmanaged<AnyObject>.fromOpaque(ptr).takeRetainedValue();
}
@_cdecl("LKRoomCreate") @_cdecl("LKRoomCreate")
public func LKRoomCreate() -> UnsafeMutableRawPointer { public func LKRoomCreate() -> UnsafeMutableRawPointer {
Unmanaged.passRetained(Room()).toOpaque() Unmanaged.passRetained(Room()).toOpaque()
} }
@_cdecl("LKRoomDestroy")
public func LKRoomDestroy(ptr: UnsafeRawPointer) {
let _ = Unmanaged<Room>.fromOpaque(ptr).takeRetainedValue();
}
@_cdecl("LKRoomConnect") @_cdecl("LKRoomConnect")
public func LKRoomConnect(room: UnsafeRawPointer, url: CFString, token: CFString, callback: @escaping @convention(c) (UnsafeRawPointer) -> Void, callback_data: UnsafeRawPointer) { public func LKRoomConnect(room: UnsafeRawPointer, url: CFString, token: CFString, callback: @escaping @convention(c) (UnsafeRawPointer) -> Void, callback_data: UnsafeRawPointer) {
let room = Unmanaged<Room>.fromOpaque(room).takeUnretainedValue(); let room = Unmanaged<Room>.fromOpaque(room).takeUnretainedValue();
@ -21,3 +21,9 @@ public func LKRoomConnect(room: UnsafeRawPointer, url: CFString, token: CFString
print(error); print(error);
}; };
} }
@_cdecl("LKCreateScreenShareTrackForWindow")
public func LKCreateScreenShareTrackForWindow(windowId: uint32) -> UnsafeMutableRawPointer {
let track = LocalVideoTrack.createMacOSScreenShareTrack(source: .window(id: windowId));
return Unmanaged.passRetained(track).toOpaque()
}

View file

@ -1,13 +1,20 @@
use core_foundation::{ use core_foundation::{
base::TCFType, array::CFArray,
base::{TCFType, TCFTypeRef},
dictionary::CFDictionary,
number::CFNumber,
string::{CFString, CFStringRef}, string::{CFString, CFStringRef},
}; };
use core_graphics::window::{
kCGNullWindowID, kCGWindowListOptionExcludeDesktopElements, kCGWindowListOptionOnScreenOnly,
kCGWindowNumber, kCGWindowOwnerName, kCGWindowOwnerPID, CGWindowListCopyWindowInfo,
};
use futures::{channel::oneshot, Future}; use futures::{channel::oneshot, Future};
use std::ffi::c_void; use std::ffi::c_void;
extern "C" { extern "C" {
fn LKRelease(object: *const c_void);
fn LKRoomCreate() -> *const c_void; fn LKRoomCreate() -> *const c_void;
fn LKRoomDestroy(room: *const c_void);
fn LKRoomConnect( fn LKRoomConnect(
room: *const c_void, room: *const c_void,
url: CFStringRef, url: CFStringRef,
@ -15,17 +22,14 @@ extern "C" {
callback: extern "C" fn(*mut c_void) -> (), callback: extern "C" fn(*mut c_void) -> (),
callback_data: *mut c_void, callback_data: *mut c_void,
); );
fn LKCreateScreenShareTrackForWindow(windowId: u32) -> *const c_void;
} }
pub struct Room { pub struct Room(*const c_void);
native_room: *const c_void,
}
impl Room { impl Room {
pub fn new() -> Self { pub fn new() -> Self {
Self { Self(unsafe { LKRoomCreate() })
native_room: unsafe { LKRoomCreate() },
}
} }
pub fn connect(&self, url: &str, token: &str) -> impl Future<Output = ()> { pub fn connect(&self, url: &str, token: &str) -> impl Future<Output = ()> {
@ -40,7 +44,7 @@ impl Room {
unsafe { unsafe {
LKRoomConnect( LKRoomConnect(
self.native_room, self.0,
url.as_concrete_TypeRef(), url.as_concrete_TypeRef(),
token.as_concrete_TypeRef(), token.as_concrete_TypeRef(),
did_connect, did_connect,
@ -54,6 +58,60 @@ impl Room {
impl Drop for Room { impl Drop for Room {
fn drop(&mut self) { fn drop(&mut self) {
unsafe { LKRoomDestroy(self.native_room) } unsafe { LKRelease(self.0) }
}
}
pub struct LocalVideoTrack(*const c_void);
impl LocalVideoTrack {
pub fn screen_share_for_window(window_id: u32) -> Self {
Self(unsafe { LKCreateScreenShareTrackForWindow(window_id) })
}
}
impl Drop for LocalVideoTrack {
fn drop(&mut self) {
unsafe { LKRelease(self.0) }
}
}
#[derive(Debug)]
pub struct WindowInfo {
pub id: u32,
pub owner_pid: i32,
pub owner_name: Option<String>,
}
pub fn list_windows() -> Vec<WindowInfo> {
unsafe {
let dicts = CFArray::<CFDictionary>::wrap_under_get_rule(CGWindowListCopyWindowInfo(
kCGWindowListOptionOnScreenOnly | kCGWindowListOptionExcludeDesktopElements,
kCGNullWindowID,
));
dicts
.iter()
.map(|dict| {
let id =
CFNumber::wrap_under_get_rule(*dict.get(kCGWindowNumber.as_void_ptr()) as _)
.to_i64()
.unwrap() as u32;
let owner_pid =
CFNumber::wrap_under_get_rule(*dict.get(kCGWindowOwnerPID.as_void_ptr()) as _)
.to_i32()
.unwrap();
let owner_name = dict
.find(kCGWindowOwnerName.as_void_ptr())
.map(|name| CFString::wrap_under_get_rule(*name as _).to_string());
WindowInfo {
id,
owner_pid,
owner_name,
}
})
.collect()
} }
} }