1 unstable release
Uses new Rust 2024
new 0.1.0 | Mar 21, 2025 |
---|
#186 in GUI
81 downloads per month
93KB
2K
SLoC
XDG Desktop Portal
XDG Desktop Portal allow Flatpak apps, and other desktop containment frameworks, to interact with the system in a secure and well defined way.
Features
impl | example | version | doc | description | |
---|---|---|---|---|---|
Account | ✅ | ✅ | 1 | Account.xml | query basic information about the user, like their name and avatar photo |
Background | ❌ | ❌ | 2 | Background.xml | let application run in the background or started automatically when the user logs in |
Camera | ✅ | ✅ | - | Camera.xml | access camera devices, such as web cams |
Clipboard | ❌ | ❌ | - | Clipboard.xml | access system clipboard |
Document | ❌ | ❌ | 5 | Documents.xml | make files from the outside world available to sandboxed applications in a controlled way |
Launcher | ❌ | ❌ | 1 | DynamicLauncher.xml | instal application launchers(.desktop files) which have an icon associated with them and which execute a command in the application |
✅ | ✅ | 4 | Email.xml | request to send an email, optionally providing an address, subject, body and attachments | |
File Chooser | ✅ | ✅ | 4 | FileChooser.xml | ask the user for access to files |
File Transfer | ❌ | ❌ | 1 | FileTransfer.xml | transfer files between apps |
Game Mode | ❌ | ❌ | 4 | GameMode.xml | access GameMode |
Global Shortcuts | ❌ | ❌ | 1 | GlobalShortcuts.xml | create global shortcuts sessions, and register shortcuts |
Inhibit | ❌ | ❌ | 3 | Inhibit.xml | inhibit the user session from ending, suspending, idling or getting switched away |
Input Capture | ❌ | ❌ | 1 | InputCapture.xml | capture input events from connected physical or logical devices |
Location | ✅ | ✅ | 1 | Location.xml | query basic information about the location |
Momory Monitor | ✅ | ✅ | 1 | MemoryMonitor.xml | provides information about low system memory |
Network Monitor | ❌ | ❌ | 3 | NetworkMonitor.xml | provides network status information |
Notification | ✅ | ✅ | 2 | Notification.xml | send and withdraw notifications |
OpenURI | ❌ | ❌ | 5 | OpenURI.xml | open URIs (e.g. a http: link to the applications homepage) under the control of the user |
Power Profile Monitor | ❌ | ❌ | 1 | PowerProfileMonitor.xml | provides information about the user-selected system-wide power profile |
❌ | ❌ | 3 | Print.xml | allows applications to print | |
Proxy Resolver | ❌ | ❌ | 1 | ProxyResolver.xml | provides network proxy information |
Realtime | ❌ | ❌ | 1 | Realtime.xml | set threads to realtime |
Remote Desktop | ❌ | ❌ | 2 | RemoteDesktop.xml | create remote desktop sessions |
Request | ✅ | ❌ | - | Request.xml | shared request interface |
Screencast | ✅ | ✅ | 5 | ScreenCast.xml | create screen cast sessions |
Screenshot | ✅ | ✅ | 2 | Screenshot.xml | request a screenshot |
Secret | ❌ | ❌ | 1 | Secret.xml | retrieve a per-application secret |
Session | ❌ | ❌ | - | Session.xml | shared session interface |
Settings | ❌ | ❌ | 2 | Settings.xml | provides read-only access to a small number of standardized host settings required for toolkits similar to XSettings |
Trash | ✅ | ✅ | 1 | Trash.xml | send files to the trashcan |
Usb | ❌ | ❌ | 1 | Usb.xml | monitor and request access to connected USB devices |
Wallpaper | ❌ | ❌ | 1 | Wallpaper.xml | set the user’s desktop background picture |
Example
1. Account
#[tokio::main(flavor = "current_thread")]
async fn main() {
let portal = Portal::new().await.unwrap();
let mut account_portal = portal.account().await.unwrap();
let req = GetUserInfoReq::default()
.reason("I want to get user info");
println!("{:?}", account_portal.get_user_information(req).await);
}
// Ok(AccountUserInformation { id: "dlzht", name: "", image: "file:///home/dlzht/.face" })

2. Background
3. Camera
#[tokio::main(flavor = "current_thread")]
async fn main() {
let portal = Portal::new().await.unwrap();
let camera_portal = portal.camera().await.unwrap();
if let Ok(res) = camera_portal.is_camera_present().await && res{
let fd = camera_portal.open_camera().await.unwrap();
println!("Opened camera: {:?}", fd);
}
}
// Opened camera: OwnedFd { fd: 11 }
4. Clipboard
5. Document
6. Launcher
7. Email
#[tokio::main(flavor = "current_thread")]
async fn main() {
let portal = Portal::new().await.unwrap();
let email_portal = portal.email().await.unwrap();
let req = ComposeEmailReq::default()
.subject("Email Subject")
.body("Hello")
.addresses(vec!["example@github.com".to_string()]);
email_portal.compose_email(req).await.unwrap();
}
// mailto:example@github.com?subject=Email Subject&body=Hello
8. File Chooser
open_file
async fn open_file() {
let portal = Portal::new().await.unwrap();
let mut file_chooser_portal = portal.file_chooser().await.unwrap();
let filter1 = FileFilterReq::new("image-jpg".to_string(), vec!["*.jpg".to_string()]);
let filter2 = FileFilterReq::new("image-png".to_string(), vec!["*.png".to_string()]);
let filters = vec![filter1.clone(), filter2.clone()];
let req = OpenFileReq::new()
.title("This is open file title".to_string())
.modal(true)
.multiple(true)
.filters(filters)
.current_filter(filter2)
.accept_label("This is accept label".to_string());
let res = file_chooser_portal.open_file(req).await;
println!("{:?}", res);
}
// Ok(OpenFileRes { uris: ["file:///home/dlzht/example.png"], current_filter: Some(FileFilterRes { name: "image-png", matches: ["*.png"] }) })

save_file
async fn save_file() {
let portal = Portal::new().await.unwrap();
let mut file_chooser_portal = portal.file_chooser().await.unwrap();
let filter1 = FileFilterReq::new("image-jpg".to_string(), vec!["*.jpg".to_string()]);
let filter2 = FileFilterReq::new("image-png".to_string(), vec!["*.png".to_string()]);
let filters = vec![filter1.clone(), filter2.clone()];
let req = SaveFileReq::new()
.title("This is save file title".to_string())
.modal(true)
.filters(filters)
.current_filter(filter2)
.current_name("FILE_NAME".to_string())
.accept_label("This is accept label".to_string());
let res = file_chooser_portal.save_file(req).await;
println!("{:?}", res);
}
// Ok(SaveFileRes { uris: ["file:///home/dlzht/FILE_NAME.png"], current_filter: Some(FileFilterRes { name: "image-png", matches: ["*.png"] }) })
save_files(nothing happened on my machine KDE/WAYLAND)
async fn save_files() {
let portal = Portal::new().await.unwrap();
let mut file_chooser_portal = portal.file_chooser().await.unwrap();
let path_buf1 = PathBuf::from("image-jpg1");
let path_buf2 = PathBuf::from("image-jpg2");
let req = SaveFilesReq::new()
.title("This is save files title".to_string())
.modal(true)
.files(vec![path_buf1.clone(), path_buf2.clone()])
.accept_label("This is accept label".to_string());
let res = file_chooser_portal.save_files(req).await;
println!("{:?}", res);
}

9. File Transfer
10. Game Mode
11. Global Shortcuts
12. Inhibit
13. Input Capture
14. Location
#[tokio::main(flavor = "current_thread")]
async fn main() {
let portal = Portal::new().await.unwrap();
let location_portal = portal.location().await.unwrap();
let req = GetLocationsReq::new();
let mut locations = location_portal.locations(req)
.await.unwrap();
while let Some(location) = locations.next().await {
println!("{:?}", location);
}
}
15. Memory Monitor
#[tokio::main(flavor = "current_thread")]
async fn main() {
let portal = Portal::new().await.unwrap();
let mut memory_monitor_portal = portal.memory_monitor().await.unwrap();
tokio::spawn(async move {
if let Ok(level) = memory_monitor_portal.low_memory_warning().await {
}
});
}
16. Network Monitor
17. Notification
#[tokio::main(flavor = "current_thread")]
async fn main() {
let portal = Portal::new().await.unwrap();
let notification_portal = portal.notification().await.unwrap();
let send_req = SendNotificationReq::new()
.id("1".to_string())
.title("This is title".to_string())
.body("this is a high priority notification".to_string())
.priority(NotificationPriority::High);
let _ = notification_portal.send_notification(send_req).await;
tokio::time::sleep(Duration::from_millis(1000)).await;
// remove notification which has id "1")
let remove_req = RemoveNotificationReq::new("1".to_string());
let _ = notification_portal.remove_notification(remove_req).await;
}

18. OpenURI
19. Power Profile Monitr
20. Print
21. Proxy Resolver
22. Realtime
23. Remote Desktop
24. Request
25. Screencast
#[tokio::main(flavor = "current_thread")]
async fn main() {
let portal = Portal::new().await.unwrap();
let mut screencast_portal = portal.screencast().await.unwrap();
let screencast_req = ScreencastReq::new()
.source_type(SourceType::Window | SourceType::Monitor);
let res = screencast_portal.screencast(screencast_req).await;
println!("screencast_portal returned: {:?}", res);
}
// screencast_portal returned: Ok(ScreencastRes { streams: [SelectedSource { node_id: 73, identifier: None, source_type: SourceType(Virtual), size: Some((1920, 1080)), position: None, mapping_id: None }], fd: OwnedFd { fd: 11 } })

26. Screenshot
screenshot
async fn screenshot() {
let portal = Portal::new().await.unwrap();
let mut screenshot_portal = portal.screenshot().await.unwrap();
let req = ScreenshotReq::new()
.interactive(true)
.modal(false);
let res = screenshot_portal.screenshot(req).await;
println!("{:?}", res);
}
// Ok(ScreenshotRes { uri: "file:///home/dlzht/Screenshot_20250321_121919.png" })

pick_color
async fn pick_color() {
let portal = Portal::new().await.unwrap();
let mut screenshot_portal = portal.screenshot().await.unwrap();
let req = PickColorReq::new();
let res = screenshot_portal.pick_color(req).await;
println!("{:?}", res);
}
// Ok(PickColorRes { r: 0.11764705926179886, g: 0.12156862765550613, b: 0.13333334028720856 })
27. Secret
28. Session
29. Settings
30. Trash
#[tokio::main(flavor = "current_thread")]
async fn main() {
let portal = Portal::new().await.unwrap();
let trash_portal = portal.trash().await.unwrap();
let file = tokio::fs::File::open("some_file").await.unwrap();
trash_portal.trash_file(file.as_fd()).await.unwrap();
}
31. Usb
32. Wallpaper
Dependencies
~8–19MB
~295K SLoC