diff --git a/wayland-client/Cargo.toml b/wayland-client/Cargo.toml index 604fca8c713..6bacab864d2 100644 --- a/wayland-client/Cargo.toml +++ b/wayland-client/Cargo.toml @@ -25,3 +25,6 @@ log = { version = "0.4", optional = true } wayland-protocols = { path = "../wayland-protocols", features = ["client"] } futures-util = "0.3" tempfile = "3.2" +gtk4 = { version = "0.4.8" } +gdk4-wayland = { version = "0.4.8", features = ["wayland_crate", "v4_4", "egl"] } +wayland-backend = { path = "../wayland-backend", features = ["client_system"] } diff --git a/wayland-client/examples/gtk_list_globals.rs b/wayland-client/examples/gtk_list_globals.rs new file mode 100644 index 00000000000..9806726bb67 --- /dev/null +++ b/wayland-client/examples/gtk_list_globals.rs @@ -0,0 +1,76 @@ +use gtk4::{gdk, glib, prelude::*}; +use std::{collections::HashMap, future::poll_fn, os::unix::io::AsRawFd}; +use wayland_client::{backend::Backend, protocol::wl_registry, Connection, Dispatch, QueueHandle}; + +struct State { + list_box: gtk4::ListBox, + rows: HashMap, +} + +impl Dispatch for State { + fn event( + state: &mut Self, + _: &wl_registry::WlRegistry, + event: wl_registry::Event, + _: &(), + _: &Connection, + _: &QueueHandle, + ) { + match event { + wl_registry::Event::Global { name, interface, version } => { + // Add a row for the global to the `ListBox` + let text = format!("[{}] {} (v{})", name, interface, version); + let row = gtk4::Label::new(Some(&text)); + row.set_halign(gtk4::Align::Start); + state.list_box.append(&row); + } + wl_registry::Event::GlobalRemove { name } => { + // Remove the global's row from the `ListBox` + let row = state.rows.remove(&name).unwrap(); + state.list_box.remove(&row); + } + _ => {} + } + } +} + +fn main() { + // Initialize GTK + gtk4::init().unwrap(); + + // Create a GTK window with an empty `ListBox` + let list_box = gtk4::ListBox::new(); + let window = gtk4::Window::new(); + window.set_child(Some(&list_box)); + window.show(); + + // Create a connection from the `GdkWaylandDisplay` + let display = + gdk::Display::default().unwrap().downcast::().unwrap(); + let wl_display = display.wl_display().c_ptr(); + let connection = + Connection::from_backend(unsafe { Backend::from_foreign_display(wl_display as _) }); + + // Create an event queue and get registry GTK doesn't provide a way to get + // registry events from its copy. + let mut event_queue = connection.new_event_queue(); + let qh = event_queue.handle(); + let _registry = connection.display().get_registry(&qh, ()); + + // Read from connection + let fd = connection.prepare_read().unwrap().connection_fd().as_raw_fd(); + glib::source::unix_fd_add_local(fd, glib::IOCondition::IN, move |_, _| { + connection.prepare_read().unwrap().read().unwrap(); + glib::Continue(true) + }); + + // Dispatch events when reads occur. Async version must be used since + // GTK's types aren't thread safe. + let mut state = State { list_box, rows: HashMap::new() }; + glib::MainContext::default().spawn_local(async move { + poll_fn(|cx| event_queue.poll_dispatch_pending(cx, &mut state)).await.unwrap(); + }); + + // Run GLib main loop + glib::MainLoop::new(None, false).run(); +}