From 5172cc5f8aa2487b8849e65d9b52ff466e12db1e Mon Sep 17 00:00:00 2001
From: Ian Douglas Scott <idscott@system76.com>
Date: Thu, 13 Jul 2023 12:56:54 -0700
Subject: [PATCH] Use `BorrowedFd` in arguments

Implementation of https://github.com/Smithay/wayland-rs/issues/592.

This is required for IO safety. Or perhaps `AsFd` generics would be
useful.

This will be a breaking API change, and require Rust 1.65.
---
 wayland-client/CHANGELOG.md                   |   1 +
 wayland-client/examples/simple_window.rs      |   5 +-
 wayland-client/src/conn.rs                    |   3 +-
 wayland-client/src/lib.rs                     |  15 +-
 wayland-cursor/src/lib.rs                     |   5 +-
 wayland-scanner/CHANGELOG.md                  |   1 +
 wayland-scanner/src/client_gen.rs             |  10 +-
 wayland-scanner/src/common.rs                 |  93 ++++++-----
 wayland-scanner/src/server_gen.rs             |   8 +-
 .../tests/scanner_assets/test-client-code.rs  | 151 +++++++++++-------
 .../tests/scanner_assets/test-server-code.rs  | 115 ++++++++-----
 wayland-server/CHANGELOG.md                   |   1 +
 wayland-server/src/display.rs                 |   9 +-
 wayland-server/src/lib.rs                     |  10 +-
 wayland-tests/tests/attach_to_surface.rs      |   5 +-
 15 files changed, 259 insertions(+), 173 deletions(-)

diff --git a/wayland-client/CHANGELOG.md b/wayland-client/CHANGELOG.md
index 560704961d1..2de7b619807 100644
--- a/wayland-client/CHANGELOG.md
+++ b/wayland-client/CHANGELOG.md
@@ -8,6 +8,7 @@
 - Updated wayland-backend to 0.2
 - Calloop integration is now removed, to avoid tying wayland-client to it you can use the
   `calloop-wayland-source` crate instead
+- Use `BorrowedFd<'_>` arguments instead of `RawFd`
 
 ## 0.30.2 -- 30/05/2023
 
diff --git a/wayland-client/examples/simple_window.rs b/wayland-client/examples/simple_window.rs
index 8c09f03e398..84d41ad1286 100644
--- a/wayland-client/examples/simple_window.rs
+++ b/wayland-client/examples/simple_window.rs
@@ -1,4 +1,4 @@
-use std::{fs::File, os::unix::prelude::AsRawFd};
+use std::{fs::File, os::unix::prelude::AsFd};
 
 use wayland_client::{
     delegate_noop,
@@ -73,8 +73,7 @@ impl Dispatch<wl_registry::WlRegistry, ()> for State {
 
                     let mut file = tempfile::tempfile().unwrap();
                     draw(&mut file, (init_w, init_h));
-                    let pool =
-                        shm.create_pool(file.as_raw_fd(), (init_w * init_h * 4) as i32, qh, ());
+                    let pool = shm.create_pool(file.as_fd(), (init_w * init_h * 4) as i32, qh, ());
                     let buffer = pool.create_buffer(
                         0,
                         init_w as i32,
diff --git a/wayland-client/src/conn.rs b/wayland-client/src/conn.rs
index 8290099d203..6812bfc393f 100644
--- a/wayland-client/src/conn.rs
+++ b/wayland-client/src/conn.rs
@@ -196,10 +196,11 @@ impl Connection {
     pub fn send_request<I: Proxy>(
         &self,
         proxy: &I,
-        request: I::Request,
+        request: I::Request<'_>,
         data: Option<Arc<dyn ObjectData>>,
     ) -> Result<ObjectId, InvalidId> {
         let (msg, child_spec) = proxy.write_request(self, request)?;
+        let msg = msg.map_fd(|fd| fd.as_raw_fd());
         self.backend.send_request(msg, data, child_spec)
     }
 
diff --git a/wayland-client/src/lib.rs b/wayland-client/src/lib.rs
index 77e9fe22236..87c5629c704 100644
--- a/wayland-client/src/lib.rs
+++ b/wayland-client/src/lib.rs
@@ -169,8 +169,7 @@
 use std::{
     fmt,
     hash::{Hash, Hasher},
-    os::unix::io::OwnedFd,
-    os::unix::io::RawFd,
+    os::unix::io::{BorrowedFd, OwnedFd},
     sync::Arc,
 };
 use wayland_backend::{
@@ -226,7 +225,7 @@ pub trait Proxy: Clone + std::fmt::Debug + Sized {
     /// The event enum for this interface
     type Event;
     /// The request enum for this interface
-    type Request;
+    type Request<'a>;
 
     /// The interface description
     fn interface() -> &'static Interface;
@@ -277,7 +276,7 @@ pub trait Proxy: Clone + std::fmt::Debug + Sized {
     ///
     /// It is an error to use this function on requests that create objects; use
     /// [Proxy::send_constructor] for such requests.
-    fn send_request(&self, req: Self::Request) -> Result<(), InvalidId>;
+    fn send_request(&self, req: Self::Request<'_>) -> Result<(), InvalidId>;
 
     /// Send a request for this object that creates another object.
     ///
@@ -285,7 +284,7 @@ pub trait Proxy: Clone + std::fmt::Debug + Sized {
     /// [Proxy::send_request] for such requests.
     fn send_constructor<I: Proxy>(
         &self,
-        req: Self::Request,
+        req: Self::Request<'_>,
         data: Arc<dyn ObjectData>,
     ) -> Result<I, InvalidId>;
 
@@ -303,11 +302,11 @@ pub trait Proxy: Clone + std::fmt::Debug + Sized {
     /// **Note:** This method is mostly meant as an implementation detail to be
     /// used by code generated by wayland-scanner.
     #[allow(clippy::type_complexity)]
-    fn write_request(
+    fn write_request<'a>(
         &self,
         conn: &Connection,
-        req: Self::Request,
-    ) -> Result<(Message<ObjectId, RawFd>, Option<(&'static Interface, u32)>), InvalidId>;
+        req: Self::Request<'a>,
+    ) -> Result<(Message<ObjectId, BorrowedFd<'a>>, Option<(&'static Interface, u32)>), InvalidId>;
 
     /// Creates a weak handle to this object
     ///
diff --git a/wayland-cursor/src/lib.rs b/wayland-cursor/src/lib.rs
index 846c88025de..a7e3061dc6f 100644
--- a/wayland-cursor/src/lib.rs
+++ b/wayland-cursor/src/lib.rs
@@ -49,8 +49,7 @@ use std::env;
 use std::fs::File;
 use std::io::{Error as IoError, Read, Result as IoResult, Seek, SeekFrom, Write};
 use std::ops::{Deref, Index};
-use std::os::unix::io::OwnedFd;
-use std::os::unix::io::{AsRawFd, FromRawFd, RawFd};
+use std::os::unix::io::{AsFd, FromRawFd, OwnedFd, RawFd};
 use std::sync::Arc;
 use std::time::{SystemTime, UNIX_EPOCH};
 
@@ -145,7 +144,7 @@ impl CursorTheme {
 
         let pool_id = conn.send_request(
             &shm,
-            wl_shm::Request::CreatePool { fd: file.as_raw_fd(), size: INITIAL_POOL_SIZE },
+            wl_shm::Request::CreatePool { fd: file.as_fd(), size: INITIAL_POOL_SIZE },
             Some(Arc::new(IgnoreObjectData)),
         )?;
         let pool = WlShmPool::from_id(conn, pool_id)?;
diff --git a/wayland-scanner/CHANGELOG.md b/wayland-scanner/CHANGELOG.md
index 93f5bd7a04e..52b7e222f4d 100644
--- a/wayland-scanner/CHANGELOG.md
+++ b/wayland-scanner/CHANGELOG.md
@@ -6,6 +6,7 @@
 
 - Bump bitflags to 2.0
 - Remove `io-lifetimes` from the generated code following wayland-backend 0.3 dropping it.
+- Generate `BorrowedFd<'_>` arguments instead of `RawFd`
 
 # 0.30.1 -- 2023-06-17
 
diff --git a/wayland-scanner/src/client_gen.rs b/wayland-scanner/src/client_gen.rs
index 1415fc6cce5..b116f55879b 100644
--- a/wayland-scanner/src/client_gen.rs
+++ b/wayland-scanner/src/client_gen.rs
@@ -104,7 +104,7 @@ fn generate_objects_for(interface: &Interface) -> TokenStream {
             }
 
             impl super::wayland_client::Proxy for #iface_name {
-                type Request = Request;
+                type Request<'request> = Request<'request>;
                 type Event = Event;
 
                 #[inline]
@@ -135,14 +135,14 @@ fn generate_objects_for(interface: &Interface) -> TokenStream {
                     &self.backend
                 }
 
-                fn send_request(&self, req: Self::Request) -> Result<(), InvalidId> {
+                fn send_request(&self, req: Self::Request<'_>) -> Result<(), InvalidId> {
                     let conn = Connection::from_backend(self.backend.upgrade().ok_or(InvalidId)?);
                     let id = conn.send_request(self, req, None)?;
                     debug_assert!(id.is_null());
                     Ok(())
                 }
 
-                fn send_constructor<I: Proxy>(&self, req: Self::Request, data: Arc<dyn ObjectData>) -> Result<I, InvalidId> {
+                fn send_constructor<I: Proxy>(&self, req: Self::Request<'_>, data: Arc<dyn ObjectData>) -> Result<I, InvalidId> {
                     let conn = Connection::from_backend(self.backend.upgrade().ok_or(InvalidId)?);
                     let id = conn.send_request(self, req, Some(data))?;
                     Proxy::from_id(&conn, id)
@@ -168,7 +168,7 @@ fn generate_objects_for(interface: &Interface) -> TokenStream {
                     #parse_body
                 }
 
-                fn write_request(&self, conn: &Connection, msg: Self::Request) -> Result<(Message<ObjectId, std::os::unix::io::RawFd>, Option<(&'static Interface, u32)>), InvalidId> {
+                fn write_request<'a>(&self, conn: &Connection, msg: Self::Request<'a>) -> Result<(Message<ObjectId, std::os::unix::io::BorrowedFd<'a>>, Option<(&'static Interface, u32)>), InvalidId> {
                     #write_body
                 }
             }
@@ -212,7 +212,7 @@ fn gen_methods(interface: &Interface) -> TokenStream {
                     Type::Fixed => quote! { f64 },
                     Type::String => if arg.allow_null { quote!{ Option<String> } } else { quote!{ String } },
                     Type::Array => if arg.allow_null { quote!{ Option<Vec<u8>> } } else { quote!{ Vec<u8> } },
-                    Type::Fd => quote! { ::std::os::unix::io::RawFd },
+                    Type::Fd => quote! { ::std::os::unix::io::BorrowedFd<'_> },
                     Type::Object => {
                         let iface = arg.interface.as_ref().unwrap();
                         let iface_mod = Ident::new(iface, Span::call_site());
diff --git a/wayland-scanner/src/common.rs b/wayland-scanner/src/common.rs
index 494e5713a8b..ae7d3a8138d 100644
--- a/wayland-scanner/src/common.rs
+++ b/wayland-scanner/src/common.rs
@@ -160,29 +160,33 @@ pub(crate) fn gen_message_enum(
     receiver: bool,
     messages: &[Message],
 ) -> TokenStream {
-    let variants = messages.iter().map(|msg| {
-        let mut docs = String::new();
-        if let Some((ref short, ref long)) = msg.description {
-            write!(docs, "{}\n\n{}\n", short, long.trim()).unwrap();
-        }
-        if let Some(Type::Destructor) = msg.typ {
-            write!(
-                docs,
-                "\nThis is a destructor, once {} this object cannot be used any longer.",
-                if receiver { "received" } else { "sent" },
-            )
-            .unwrap()
-        }
-        if msg.since > 1 {
-            write!(docs, "\nOnly available since version {} of the interface", msg.since).unwrap();
-        }
+    let variants = messages
+        .iter()
+        .map(|msg| {
+            let mut docs = String::new();
+            if let Some((ref short, ref long)) = msg.description {
+                write!(docs, "{}\n\n{}\n", short, long.trim()).unwrap();
+            }
+            if let Some(Type::Destructor) = msg.typ {
+                write!(
+                    docs,
+                    "\nThis is a destructor, once {} this object cannot be used any longer.",
+                    if receiver { "received" } else { "sent" },
+                )
+                .unwrap()
+            }
+            if msg.since > 1 {
+                write!(docs, "\nOnly available since version {} of the interface", msg.since)
+                    .unwrap();
+            }
 
-        let doc_attr = to_doc_attr(&docs);
-        let msg_name = Ident::new(&snake_to_camel(&msg.name), Span::call_site());
-        let msg_variant_decl = if msg.args.is_empty() {
-            msg_name.into_token_stream()
-        } else {
-            let fields = msg.args.iter().flat_map(|arg| {
+            let doc_attr = to_doc_attr(&docs);
+            let msg_name = Ident::new(&snake_to_camel(&msg.name), Span::call_site());
+            let msg_variant_decl =
+                if msg.args.is_empty() {
+                    msg_name.into_token_stream()
+                } else {
+                    let fields = msg.args.iter().flat_map(|arg| {
                 let field_name =
                     format_ident!("{}{}", if is_keyword(&arg.name) { "_" } else { "" }, arg.name);
                 let field_type_inner = if let Some(ref enu) = arg.enum_ {
@@ -199,7 +203,7 @@ pub(crate) fn gen_message_enum(
                             if receiver {
                                 quote! { OwnedFd }
                             } else {
-                                quote! { std::os::unix::io::RawFd }
+                                quote! { std::os::unix::io::BorrowedFd<'a> }
                             }
                         }
                         Type::Object => {
@@ -264,18 +268,19 @@ pub(crate) fn gen_message_enum(
                 })
             });
 
+                    quote! {
+                        #msg_name {
+                            #(#fields,)*
+                        }
+                    }
+                };
+
             quote! {
-                #msg_name {
-                    #(#fields,)*
-                }
+                #doc_attr
+                #msg_variant_decl
             }
-        };
-
-        quote! {
-            #doc_attr
-            #msg_variant_decl
-        }
-    });
+        })
+        .collect::<Vec<_>>();
 
     let opcodes = messages.iter().enumerate().map(|(opcode, msg)| {
         let msg_name = Ident::new(&snake_to_camel(&msg.name), Span::call_site());
@@ -291,18 +296,33 @@ pub(crate) fn gen_message_enum(
         }
     });
 
+    // Placeholder to allow generic argument to be added later, without ABI
+    // break.
+    // TODO Use never type.
+    let (generic, phantom_variant, phantom_case) = if !receiver {
+        (
+            quote! { 'a },
+            quote! { #[doc(hidden)] __phantom_lifetime { phantom: std::marker::PhantomData<&'a ()> } },
+            quote! { #name::__phantom_lifetime { .. } => unreachable!() },
+        )
+    } else {
+        (quote! {}, quote! {}, quote! {})
+    };
+
     quote! {
         #[derive(Debug)]
         #[non_exhaustive]
-        pub enum #name {
+        pub enum #name<#generic> {
             #(#variants,)*
+            #phantom_variant
         }
 
-        impl #name {
+        impl<#generic> #name<#generic> {
             #[doc="Get the opcode number of this message"]
             pub fn opcode(&self) -> u16 {
                 match *self {
                     #(#opcodes,)*
+                    #phantom_case
                 }
             }
         }
@@ -604,7 +624,8 @@ pub(crate) fn gen_write_body(interface: &Interface, side: Side) -> TokenStream {
     });
     quote! {
         match msg {
-            #(#arms),*
+            #(#arms,)*
+            #msg_type::__phantom_lifetime { .. } => unreachable!()
         }
     }
 }
diff --git a/wayland-scanner/src/server_gen.rs b/wayland-scanner/src/server_gen.rs
index aa32ecd8072..8f4a27f4d12 100644
--- a/wayland-scanner/src/server_gen.rs
+++ b/wayland-scanner/src/server_gen.rs
@@ -110,7 +110,7 @@ fn generate_objects_for(interface: &Interface) -> TokenStream {
 
             impl super::wayland_server::Resource for #iface_name {
                 type Request = Request;
-                type Event = Event;
+                type Event<'event> = Event<'event>;
 
                 #[inline]
                 fn interface() -> &'static Interface{
@@ -151,7 +151,7 @@ fn generate_objects_for(interface: &Interface) -> TokenStream {
                     Ok(#iface_name { id, data, version, handle: conn.backend_handle().downgrade() })
                 }
 
-                fn send_event(&self, evt: Self::Event) -> Result<(), InvalidId> {
+                fn send_event(&self, evt: Self::Event<'_>) -> Result<(), InvalidId> {
                     let handle = DisplayHandle::from(self.handle.upgrade().ok_or(InvalidId)?);
                     handle.send_event(self, evt)
                 }
@@ -160,7 +160,7 @@ fn generate_objects_for(interface: &Interface) -> TokenStream {
                     #parse_body
                 }
 
-                fn write_event(&self, conn: &DisplayHandle, msg: Self::Event) -> Result<Message<ObjectId, std::os::unix::io::RawFd>, InvalidId> {
+                fn write_event<'a>(&self, conn: &DisplayHandle, msg: Self::Event<'a>) -> Result<Message<ObjectId, std::os::unix::io::BorrowedFd<'a>>, InvalidId> {
                     #write_body
                 }
 
@@ -214,7 +214,7 @@ fn gen_methods(interface: &Interface) -> TokenStream {
                                 quote! { Vec<u8> }
                             }
                         }
-                        Type::Fd => quote! { ::std::os::unix::io::RawFd },
+                        Type::Fd => quote! { ::std::os::unix::io::BorrowedFd<'_> },
                         Type::Object | Type::NewId => {
                             let iface = arg.interface.as_ref().unwrap();
                             let iface_mod = Ident::new(iface, Span::call_site());
diff --git a/wayland-scanner/tests/scanner_assets/test-client-code.rs b/wayland-scanner/tests/scanner_assets/test-client-code.rs
index 1500eae39b0..ac19b88956f 100644
--- a/wayland-scanner/tests/scanner_assets/test-client-code.rs
+++ b/wayland-scanner/tests/scanner_assets/test-client-code.rs
@@ -58,18 +58,21 @@ pub mod wl_display {
     pub const EVT_DELETE_ID_OPCODE: u16 = 1u16;
     #[derive(Debug)]
     #[non_exhaustive]
-    pub enum Request {
+    pub enum Request<'a> {
         #[doc = "asynchronous roundtrip\n\nThe sync request asks the server to emit the 'done' event\non the returned wl_callback object.  Since requests are\nhandled in-order and events are delivered in-order, this can\nbe used as a barrier to ensure all previous requests and the\nresulting events have been handled.\n\nThe object returned by this request will be destroyed by the\ncompositor after the callback is fired and as such the client must not\nattempt to use it after that point.\n\nThe callback_data passed in the callback is the event serial."]
         Sync {},
         #[doc = "get global registry object\n\nThis request creates a registry object that allows the client\nto list and bind the global objects available from the\ncompositor.\n\nIt should be noted that the server side resources consumed in\nresponse to a get_registry request can only be released when the\nclient disconnects, not when the client side proxy is destroyed.\nTherefore, clients should invoke get_registry as infrequently as\npossible to avoid wasting memory."]
         GetRegistry {},
+        #[doc(hidden)]
+        __phantom_lifetime { phantom: std::marker::PhantomData<&'a ()> },
     }
-    impl Request {
+    impl<'a> Request<'a> {
         #[doc = "Get the opcode number of this message"]
         pub fn opcode(&self) -> u16 {
             match *self {
                 Request::Sync { .. } => 0u16,
                 Request::GetRegistry { .. } => 1u16,
+                Request::__phantom_lifetime { .. } => unreachable!(),
             }
         }
     }
@@ -130,7 +133,7 @@ pub mod wl_display {
         }
     }
     impl super::wayland_client::Proxy for WlDisplay {
-        type Request = Request;
+        type Request<'request> = Request<'request>;
         type Event = Event;
         #[inline]
         fn interface() -> &'static Interface {
@@ -154,7 +157,7 @@ pub mod wl_display {
         fn backend(&self) -> &WeakBackend {
             &self.backend
         }
-        fn send_request(&self, req: Self::Request) -> Result<(), InvalidId> {
+        fn send_request(&self, req: Self::Request<'_>) -> Result<(), InvalidId> {
             let conn = Connection::from_backend(self.backend.upgrade().ok_or(InvalidId)?);
             let id = conn.send_request(self, req, None)?;
             debug_assert!(id.is_null());
@@ -162,7 +165,7 @@ pub mod wl_display {
         }
         fn send_constructor<I: Proxy>(
             &self,
-            req: Self::Request,
+            req: Self::Request<'_>,
             data: Arc<dyn ObjectData>,
         ) -> Result<I, InvalidId> {
             let conn = Connection::from_backend(self.backend.upgrade().ok_or(InvalidId)?);
@@ -234,12 +237,12 @@ pub mod wl_display {
                 }),
             }
         }
-        fn write_request(
+        fn write_request<'a>(
             &self,
             conn: &Connection,
-            msg: Self::Request,
+            msg: Self::Request<'a>,
         ) -> Result<
-            (Message<ObjectId, std::os::unix::io::RawFd>, Option<(&'static Interface, u32)>),
+            (Message<ObjectId, std::os::unix::io::BorrowedFd<'a>>, Option<(&'static Interface, u32)>),
             InvalidId,
         > {
             match msg {
@@ -267,6 +270,7 @@ pub mod wl_display {
                     };
                     Ok((Message { sender_id: self.id.clone(), opcode: 1u16, args }, child_spec))
                 }
+                Request::__phantom_lifetime { .. } => unreachable!(),
             }
         }
     }
@@ -330,7 +334,7 @@ pub mod wl_registry {
     pub const EVT_GLOBAL_REMOVE_OPCODE: u16 = 1u16;
     #[derive(Debug)]
     #[non_exhaustive]
-    pub enum Request {
+    pub enum Request<'a> {
         #[doc = "bind an object to the display\n\nBinds a new, client-created object to the server using the\nspecified name as the identifier."]
         Bind {
             #[doc = "unique numeric name of the object"]
@@ -338,12 +342,15 @@ pub mod wl_registry {
             #[doc = "bounded object"]
             id: (&'static Interface, u32),
         },
+        #[doc(hidden)]
+        __phantom_lifetime { phantom: std::marker::PhantomData<&'a ()> },
     }
-    impl Request {
+    impl<'a> Request<'a> {
         #[doc = "Get the opcode number of this message"]
         pub fn opcode(&self) -> u16 {
             match *self {
                 Request::Bind { .. } => 0u16,
+                Request::__phantom_lifetime { .. } => unreachable!(),
             }
         }
     }
@@ -404,7 +411,7 @@ pub mod wl_registry {
         }
     }
     impl super::wayland_client::Proxy for WlRegistry {
-        type Request = Request;
+        type Request<'request> = Request<'request>;
         type Event = Event;
         #[inline]
         fn interface() -> &'static Interface {
@@ -428,7 +435,7 @@ pub mod wl_registry {
         fn backend(&self) -> &WeakBackend {
             &self.backend
         }
-        fn send_request(&self, req: Self::Request) -> Result<(), InvalidId> {
+        fn send_request(&self, req: Self::Request<'_>) -> Result<(), InvalidId> {
             let conn = Connection::from_backend(self.backend.upgrade().ok_or(InvalidId)?);
             let id = conn.send_request(self, req, None)?;
             debug_assert!(id.is_null());
@@ -436,7 +443,7 @@ pub mod wl_registry {
         }
         fn send_constructor<I: Proxy>(
             &self,
-            req: Self::Request,
+            req: Self::Request<'_>,
             data: Arc<dyn ObjectData>,
         ) -> Result<I, InvalidId> {
             let conn = Connection::from_backend(self.backend.upgrade().ok_or(InvalidId)?);
@@ -508,12 +515,12 @@ pub mod wl_registry {
                 }),
             }
         }
-        fn write_request(
+        fn write_request<'a>(
             &self,
             conn: &Connection,
-            msg: Self::Request,
+            msg: Self::Request<'a>,
         ) -> Result<
-            (Message<ObjectId, std::os::unix::io::RawFd>, Option<(&'static Interface, u32)>),
+            (Message<ObjectId, std::os::unix::io::BorrowedFd<'a>>, Option<(&'static Interface, u32)>),
             InvalidId,
         > {
             match msg {
@@ -531,6 +538,7 @@ pub mod wl_registry {
                     };
                     Ok((Message { sender_id: self.id.clone(), opcode: 0u16, args }, child_spec))
                 }
+                Request::__phantom_lifetime { .. } => unreachable!(),
             }
         }
     }
@@ -569,11 +577,16 @@ pub mod wl_callback {
     pub const EVT_DONE_OPCODE: u16 = 0u16;
     #[derive(Debug)]
     #[non_exhaustive]
-    pub enum Request {}
-    impl Request {
+    pub enum Request<'a> {
+        #[doc(hidden)]
+        __phantom_lifetime { phantom: std::marker::PhantomData<&'a ()> },
+    }
+    impl<'a> Request<'a> {
         #[doc = "Get the opcode number of this message"]
         pub fn opcode(&self) -> u16 {
-            match *self {}
+            match *self {
+                Request::__phantom_lifetime { .. } => unreachable!(),
+            }
         }
     }
     #[derive(Debug)]
@@ -623,7 +636,7 @@ pub mod wl_callback {
         }
     }
     impl super::wayland_client::Proxy for WlCallback {
-        type Request = Request;
+        type Request<'request> = Request<'request>;
         type Event = Event;
         #[inline]
         fn interface() -> &'static Interface {
@@ -647,7 +660,7 @@ pub mod wl_callback {
         fn backend(&self) -> &WeakBackend {
             &self.backend
         }
-        fn send_request(&self, req: Self::Request) -> Result<(), InvalidId> {
+        fn send_request(&self, req: Self::Request<'_>) -> Result<(), InvalidId> {
             let conn = Connection::from_backend(self.backend.upgrade().ok_or(InvalidId)?);
             let id = conn.send_request(self, req, None)?;
             debug_assert!(id.is_null());
@@ -655,7 +668,7 @@ pub mod wl_callback {
         }
         fn send_constructor<I: Proxy>(
             &self,
-            req: Self::Request,
+            req: Self::Request<'_>,
             data: Arc<dyn ObjectData>,
         ) -> Result<I, InvalidId> {
             let conn = Connection::from_backend(self.backend.upgrade().ok_or(InvalidId)?);
@@ -701,15 +714,17 @@ pub mod wl_callback {
                 }),
             }
         }
-        fn write_request(
+        fn write_request<'a>(
             &self,
             conn: &Connection,
-            msg: Self::Request,
+            msg: Self::Request<'a>,
         ) -> Result<
-            (Message<ObjectId, std::os::unix::io::RawFd>, Option<(&'static Interface, u32)>),
+            (Message<ObjectId, std::os::unix::io::BorrowedFd<'a>>, Option<(&'static Interface, u32)>),
             InvalidId,
         > {
-            match msg {}
+            match msg {
+                Request::__phantom_lifetime { .. } => unreachable!(),
+            }
         }
     }
     impl WlCallback {}
@@ -766,7 +781,7 @@ pub mod test_global {
     pub const EVT_CYCLE_QUAD_OPCODE: u16 = 2u16;
     #[derive(Debug)]
     #[non_exhaustive]
-    pub enum Request {
+    pub enum Request<'a> {
         #[doc = "a request with every possible non-object arg"]
         ManyArgs {
             #[doc = "an unsigned int"]
@@ -780,7 +795,7 @@ pub mod test_global {
             #[doc = "some text"]
             some_text: String,
             #[doc = "a file descriptor"]
-            file_descriptor: std::os::unix::io::RawFd,
+            file_descriptor: std::os::unix::io::BorrowedFd<'a>,
         },
         #[doc = "Only available since version 2 of the interface"]
         GetSecondary {},
@@ -797,8 +812,10 @@ pub mod test_global {
             sec: Option<super::secondary::Secondary>,
             ter: super::tertiary::Tertiary,
         },
+        #[doc(hidden)]
+        __phantom_lifetime { phantom: std::marker::PhantomData<&'a ()> },
     }
-    impl Request {
+    impl<'a> Request<'a> {
         #[doc = "Get the opcode number of this message"]
         pub fn opcode(&self) -> u16 {
             match *self {
@@ -809,6 +826,7 @@ pub mod test_global {
                 Request::Destroy => 4u16,
                 Request::ReverseLink { .. } => 5u16,
                 Request::NewidAndAllowNull { .. } => 6u16,
+                Request::__phantom_lifetime { .. } => unreachable!(),
             }
         }
     }
@@ -875,7 +893,7 @@ pub mod test_global {
         }
     }
     impl super::wayland_client::Proxy for TestGlobal {
-        type Request = Request;
+        type Request<'request> = Request<'request>;
         type Event = Event;
         #[inline]
         fn interface() -> &'static Interface {
@@ -899,7 +917,7 @@ pub mod test_global {
         fn backend(&self) -> &WeakBackend {
             &self.backend
         }
-        fn send_request(&self, req: Self::Request) -> Result<(), InvalidId> {
+        fn send_request(&self, req: Self::Request<'_>) -> Result<(), InvalidId> {
             let conn = Connection::from_backend(self.backend.upgrade().ok_or(InvalidId)?);
             let id = conn.send_request(self, req, None)?;
             debug_assert!(id.is_null());
@@ -907,7 +925,7 @@ pub mod test_global {
         }
         fn send_constructor<I: Proxy>(
             &self,
-            req: Self::Request,
+            req: Self::Request<'_>,
             data: Arc<dyn ObjectData>,
         ) -> Result<I, InvalidId> {
             let conn = Connection::from_backend(self.backend.upgrade().ok_or(InvalidId)?);
@@ -1057,12 +1075,12 @@ pub mod test_global {
                 }),
             }
         }
-        fn write_request(
+        fn write_request<'a>(
             &self,
             conn: &Connection,
-            msg: Self::Request,
+            msg: Self::Request<'a>,
         ) -> Result<
-            (Message<ObjectId, std::os::unix::io::RawFd>, Option<(&'static Interface, u32)>),
+            (Message<ObjectId, std::os::unix::io::BorrowedFd<'a>>, Option<(&'static Interface, u32)>),
             InvalidId,
         > {
             match msg {
@@ -1161,6 +1179,7 @@ pub mod test_global {
                     };
                     Ok((Message { sender_id: self.id.clone(), opcode: 6u16, args }, child_spec))
                 }
+                Request::__phantom_lifetime { .. } => unreachable!(),
             }
         }
     }
@@ -1174,7 +1193,7 @@ pub mod test_global {
             fixed_point: f64,
             number_array: Vec<u8>,
             some_text: String,
-            file_descriptor: ::std::os::unix::io::RawFd,
+            file_descriptor: ::std::os::unix::io::BorrowedFd<'_>,
         ) {
             let backend = match self.backend.upgrade() {
                 Some(b) => b,
@@ -1306,15 +1325,18 @@ pub mod secondary {
     pub const REQ_DESTROY_OPCODE: u16 = 0u16;
     #[derive(Debug)]
     #[non_exhaustive]
-    pub enum Request {
+    pub enum Request<'a> {
         #[doc = "This is a destructor, once sent this object cannot be used any longer.\nOnly available since version 2 of the interface"]
         Destroy,
+        #[doc(hidden)]
+        __phantom_lifetime { phantom: std::marker::PhantomData<&'a ()> },
     }
-    impl Request {
+    impl<'a> Request<'a> {
         #[doc = "Get the opcode number of this message"]
         pub fn opcode(&self) -> u16 {
             match *self {
                 Request::Destroy => 0u16,
+                Request::__phantom_lifetime { .. } => unreachable!(),
             }
         }
     }
@@ -1357,7 +1379,7 @@ pub mod secondary {
         }
     }
     impl super::wayland_client::Proxy for Secondary {
-        type Request = Request;
+        type Request<'request> = Request<'request>;
         type Event = Event;
         #[inline]
         fn interface() -> &'static Interface {
@@ -1381,7 +1403,7 @@ pub mod secondary {
         fn backend(&self) -> &WeakBackend {
             &self.backend
         }
-        fn send_request(&self, req: Self::Request) -> Result<(), InvalidId> {
+        fn send_request(&self, req: Self::Request<'_>) -> Result<(), InvalidId> {
             let conn = Connection::from_backend(self.backend.upgrade().ok_or(InvalidId)?);
             let id = conn.send_request(self, req, None)?;
             debug_assert!(id.is_null());
@@ -1389,7 +1411,7 @@ pub mod secondary {
         }
         fn send_constructor<I: Proxy>(
             &self,
-            req: Self::Request,
+            req: Self::Request<'_>,
             data: Arc<dyn ObjectData>,
         ) -> Result<I, InvalidId> {
             let conn = Connection::from_backend(self.backend.upgrade().ok_or(InvalidId)?);
@@ -1424,12 +1446,12 @@ pub mod secondary {
                 }),
             }
         }
-        fn write_request(
+        fn write_request<'a>(
             &self,
             conn: &Connection,
-            msg: Self::Request,
+            msg: Self::Request<'a>,
         ) -> Result<
-            (Message<ObjectId, std::os::unix::io::RawFd>, Option<(&'static Interface, u32)>),
+            (Message<ObjectId, std::os::unix::io::BorrowedFd<'a>>, Option<(&'static Interface, u32)>),
             InvalidId,
         > {
             match msg {
@@ -1438,6 +1460,7 @@ pub mod secondary {
                     let args = smallvec::SmallVec::new();
                     Ok((Message { sender_id: self.id.clone(), opcode: 0u16, args }, child_spec))
                 }
+                Request::__phantom_lifetime { .. } => unreachable!(),
             }
         }
     }
@@ -1469,15 +1492,18 @@ pub mod tertiary {
     pub const REQ_DESTROY_OPCODE: u16 = 0u16;
     #[derive(Debug)]
     #[non_exhaustive]
-    pub enum Request {
+    pub enum Request<'a> {
         #[doc = "This is a destructor, once sent this object cannot be used any longer.\nOnly available since version 3 of the interface"]
         Destroy,
+        #[doc(hidden)]
+        __phantom_lifetime { phantom: std::marker::PhantomData<&'a ()> },
     }
-    impl Request {
+    impl<'a> Request<'a> {
         #[doc = "Get the opcode number of this message"]
         pub fn opcode(&self) -> u16 {
             match *self {
                 Request::Destroy => 0u16,
+                Request::__phantom_lifetime { .. } => unreachable!(),
             }
         }
     }
@@ -1520,7 +1546,7 @@ pub mod tertiary {
         }
     }
     impl super::wayland_client::Proxy for Tertiary {
-        type Request = Request;
+        type Request<'request> = Request<'request>;
         type Event = Event;
         #[inline]
         fn interface() -> &'static Interface {
@@ -1544,7 +1570,7 @@ pub mod tertiary {
         fn backend(&self) -> &WeakBackend {
             &self.backend
         }
-        fn send_request(&self, req: Self::Request) -> Result<(), InvalidId> {
+        fn send_request(&self, req: Self::Request<'_>) -> Result<(), InvalidId> {
             let conn = Connection::from_backend(self.backend.upgrade().ok_or(InvalidId)?);
             let id = conn.send_request(self, req, None)?;
             debug_assert!(id.is_null());
@@ -1552,7 +1578,7 @@ pub mod tertiary {
         }
         fn send_constructor<I: Proxy>(
             &self,
-            req: Self::Request,
+            req: Self::Request<'_>,
             data: Arc<dyn ObjectData>,
         ) -> Result<I, InvalidId> {
             let conn = Connection::from_backend(self.backend.upgrade().ok_or(InvalidId)?);
@@ -1587,12 +1613,12 @@ pub mod tertiary {
                 }),
             }
         }
-        fn write_request(
+        fn write_request<'a>(
             &self,
             conn: &Connection,
-            msg: Self::Request,
+            msg: Self::Request<'a>,
         ) -> Result<
-            (Message<ObjectId, std::os::unix::io::RawFd>, Option<(&'static Interface, u32)>),
+            (Message<ObjectId, std::os::unix::io::BorrowedFd<'a>>, Option<(&'static Interface, u32)>),
             InvalidId,
         > {
             match msg {
@@ -1601,6 +1627,7 @@ pub mod tertiary {
                     let args = smallvec::SmallVec::new();
                     Ok((Message { sender_id: self.id.clone(), opcode: 0u16, args }, child_spec))
                 }
+                Request::__phantom_lifetime { .. } => unreachable!(),
             }
         }
     }
@@ -1632,15 +1659,18 @@ pub mod quad {
     pub const REQ_DESTROY_OPCODE: u16 = 0u16;
     #[derive(Debug)]
     #[non_exhaustive]
-    pub enum Request {
+    pub enum Request<'a> {
         #[doc = "This is a destructor, once sent this object cannot be used any longer.\nOnly available since version 3 of the interface"]
         Destroy,
+        #[doc(hidden)]
+        __phantom_lifetime { phantom: std::marker::PhantomData<&'a ()> },
     }
-    impl Request {
+    impl<'a> Request<'a> {
         #[doc = "Get the opcode number of this message"]
         pub fn opcode(&self) -> u16 {
             match *self {
                 Request::Destroy => 0u16,
+                Request::__phantom_lifetime { .. } => unreachable!(),
             }
         }
     }
@@ -1683,7 +1713,7 @@ pub mod quad {
         }
     }
     impl super::wayland_client::Proxy for Quad {
-        type Request = Request;
+        type Request<'request> = Request<'request>;
         type Event = Event;
         #[inline]
         fn interface() -> &'static Interface {
@@ -1707,7 +1737,7 @@ pub mod quad {
         fn backend(&self) -> &WeakBackend {
             &self.backend
         }
-        fn send_request(&self, req: Self::Request) -> Result<(), InvalidId> {
+        fn send_request(&self, req: Self::Request<'_>) -> Result<(), InvalidId> {
             let conn = Connection::from_backend(self.backend.upgrade().ok_or(InvalidId)?);
             let id = conn.send_request(self, req, None)?;
             debug_assert!(id.is_null());
@@ -1715,7 +1745,7 @@ pub mod quad {
         }
         fn send_constructor<I: Proxy>(
             &self,
-            req: Self::Request,
+            req: Self::Request<'_>,
             data: Arc<dyn ObjectData>,
         ) -> Result<I, InvalidId> {
             let conn = Connection::from_backend(self.backend.upgrade().ok_or(InvalidId)?);
@@ -1750,12 +1780,12 @@ pub mod quad {
                 }),
             }
         }
-        fn write_request(
+        fn write_request<'a>(
             &self,
             conn: &Connection,
-            msg: Self::Request,
+            msg: Self::Request<'a>,
         ) -> Result<
-            (Message<ObjectId, std::os::unix::io::RawFd>, Option<(&'static Interface, u32)>),
+            (Message<ObjectId, std::os::unix::io::BorrowedFd<'a>>, Option<(&'static Interface, u32)>),
             InvalidId,
         > {
             match msg {
@@ -1764,6 +1794,7 @@ pub mod quad {
                     let args = smallvec::SmallVec::new();
                     Ok((Message { sender_id: self.id.clone(), opcode: 0u16, args }, child_spec))
                 }
+                Request::__phantom_lifetime { .. } => unreachable!(),
             }
         }
     }
diff --git a/wayland-scanner/tests/scanner_assets/test-server-code.rs b/wayland-scanner/tests/scanner_assets/test-server-code.rs
index 995a3521ee5..a493681a29a 100644
--- a/wayland-scanner/tests/scanner_assets/test-server-code.rs
+++ b/wayland-scanner/tests/scanner_assets/test-server-code.rs
@@ -24,18 +24,21 @@ pub mod wl_callback {
     }
     #[derive(Debug)]
     #[non_exhaustive]
-    pub enum Event {
+    pub enum Event<'a> {
         #[doc = "done event\n\nNotify the client when the related request is done.\n\nThis is a destructor, once sent this object cannot be used any longer."]
         Done {
             #[doc = "request-specific data for the callback"]
             callback_data: u32,
         },
+        #[doc(hidden)]
+        __phantom_lifetime { phantom: std::marker::PhantomData<&'a ()> },
     }
-    impl Event {
+    impl<'a> Event<'a> {
         #[doc = "Get the opcode number of this message"]
         pub fn opcode(&self) -> u16 {
             match *self {
                 Event::Done { .. } => 0u16,
+                Event::__phantom_lifetime { .. } => unreachable!(),
             }
         }
     }
@@ -70,7 +73,7 @@ pub mod wl_callback {
     }
     impl super::wayland_server::Resource for WlCallback {
         type Request = Request;
-        type Event = Event;
+        type Event<'event> = Event<'event>;
         #[inline]
         fn interface() -> &'static Interface {
             &super::WL_CALLBACK_INTERFACE
@@ -106,7 +109,7 @@ pub mod wl_callback {
             let data = conn.get_object_data(id.clone()).ok();
             Ok(WlCallback { id, data, version, handle: conn.backend_handle().downgrade() })
         }
-        fn send_event(&self, evt: Self::Event) -> Result<(), InvalidId> {
+        fn send_event(&self, evt: Self::Event<'_>) -> Result<(), InvalidId> {
             let handle = DisplayHandle::from(self.handle.upgrade().ok_or(InvalidId)?);
             handle.send_event(self, evt)
         }
@@ -124,11 +127,11 @@ pub mod wl_callback {
                 }),
             }
         }
-        fn write_event(
+        fn write_event<'a>(
             &self,
             conn: &DisplayHandle,
-            msg: Self::Event,
-        ) -> Result<Message<ObjectId, std::os::unix::io::RawFd>, InvalidId> {
+            msg: Self::Event<'a>,
+        ) -> Result<Message<ObjectId, std::os::unix::io::BorrowedFd<'a>>, InvalidId> {
             match msg {
                 Event::Done { callback_data } => Ok(Message {
                     sender_id: self.id.clone(),
@@ -139,6 +142,7 @@ pub mod wl_callback {
                         vec
                     },
                 }),
+                Event::__phantom_lifetime { .. } => unreachable!(),
             }
         }
         fn __set_object_data(
@@ -263,7 +267,7 @@ pub mod test_global {
     }
     #[derive(Debug)]
     #[non_exhaustive]
-    pub enum Event {
+    pub enum Event<'a> {
         #[doc = "an event with every possible non-object arg"]
         ManyArgsEvt {
             #[doc = "an unsigned int"]
@@ -277,20 +281,23 @@ pub mod test_global {
             #[doc = "some text"]
             some_text: String,
             #[doc = "a file descriptor"]
-            file_descriptor: std::os::unix::io::RawFd,
+            file_descriptor: std::os::unix::io::BorrowedFd<'a>,
         },
         #[doc = "acking the creation of a secondary"]
         AckSecondary { sec: super::secondary::Secondary },
         #[doc = "create a new quad optionally replacing a previous one"]
         CycleQuad { new_quad: super::quad::Quad, old_quad: Option<super::quad::Quad> },
+        #[doc(hidden)]
+        __phantom_lifetime { phantom: std::marker::PhantomData<&'a ()> },
     }
-    impl Event {
+    impl<'a> Event<'a> {
         #[doc = "Get the opcode number of this message"]
         pub fn opcode(&self) -> u16 {
             match *self {
                 Event::ManyArgsEvt { .. } => 0u16,
                 Event::AckSecondary { .. } => 1u16,
                 Event::CycleQuad { .. } => 2u16,
+                Event::__phantom_lifetime { .. } => unreachable!(),
             }
         }
     }
@@ -325,7 +332,7 @@ pub mod test_global {
     }
     impl super::wayland_server::Resource for TestGlobal {
         type Request = Request;
-        type Event = Event;
+        type Event<'event> = Event<'event>;
         #[inline]
         fn interface() -> &'static Interface {
             &super::TEST_GLOBAL_INTERFACE
@@ -361,7 +368,7 @@ pub mod test_global {
             let data = conn.get_object_data(id.clone()).ok();
             Ok(TestGlobal { id, data, version, handle: conn.backend_handle().downgrade() })
         }
-        fn send_event(&self, evt: Self::Event) -> Result<(), InvalidId> {
+        fn send_event(&self, evt: Self::Event<'_>) -> Result<(), InvalidId> {
             let handle = DisplayHandle::from(self.handle.upgrade().ok_or(InvalidId)?);
             handle.send_event(self, evt)
         }
@@ -657,11 +664,11 @@ pub mod test_global {
                 }),
             }
         }
-        fn write_event(
+        fn write_event<'a>(
             &self,
             conn: &DisplayHandle,
-            msg: Self::Event,
-        ) -> Result<Message<ObjectId, std::os::unix::io::RawFd>, InvalidId> {
+            msg: Self::Event<'a>,
+        ) -> Result<Message<ObjectId, std::os::unix::io::BorrowedFd<'a>>, InvalidId> {
             match msg {
                 Event::ManyArgsEvt {
                     unsigned_int,
@@ -705,6 +712,7 @@ pub mod test_global {
                         vec
                     },
                 }),
+                Event::__phantom_lifetime { .. } => unreachable!(),
             }
         }
         fn __set_object_data(
@@ -724,7 +732,7 @@ pub mod test_global {
             fixed_point: f64,
             number_array: Vec<u8>,
             some_text: String,
-            file_descriptor: ::std::os::unix::io::RawFd,
+            file_descriptor: ::std::os::unix::io::BorrowedFd<'_>,
         ) {
             let _ = self.send_event(Event::ManyArgsEvt {
                 unsigned_int,
@@ -784,11 +792,16 @@ pub mod secondary {
     }
     #[derive(Debug)]
     #[non_exhaustive]
-    pub enum Event {}
-    impl Event {
+    pub enum Event<'a> {
+        #[doc(hidden)]
+        __phantom_lifetime { phantom: std::marker::PhantomData<&'a ()> },
+    }
+    impl<'a> Event<'a> {
         #[doc = "Get the opcode number of this message"]
         pub fn opcode(&self) -> u16 {
-            match *self {}
+            match *self {
+                Event::__phantom_lifetime { .. } => unreachable!(),
+            }
         }
     }
     #[doc = "secondary\n\nSee also the [Request] enum for this interface."]
@@ -822,7 +835,7 @@ pub mod secondary {
     }
     impl super::wayland_server::Resource for Secondary {
         type Request = Request;
-        type Event = Event;
+        type Event<'event> = Event<'event>;
         #[inline]
         fn interface() -> &'static Interface {
             &super::SECONDARY_INTERFACE
@@ -858,7 +871,7 @@ pub mod secondary {
             let data = conn.get_object_data(id.clone()).ok();
             Ok(Secondary { id, data, version, handle: conn.backend_handle().downgrade() })
         }
-        fn send_event(&self, evt: Self::Event) -> Result<(), InvalidId> {
+        fn send_event(&self, evt: Self::Event<'_>) -> Result<(), InvalidId> {
             let handle = DisplayHandle::from(self.handle.upgrade().ok_or(InvalidId)?);
             handle.send_event(self, evt)
         }
@@ -887,12 +900,14 @@ pub mod secondary {
                 }),
             }
         }
-        fn write_event(
+        fn write_event<'a>(
             &self,
             conn: &DisplayHandle,
-            msg: Self::Event,
-        ) -> Result<Message<ObjectId, std::os::unix::io::RawFd>, InvalidId> {
-            match msg {}
+            msg: Self::Event<'a>,
+        ) -> Result<Message<ObjectId, std::os::unix::io::BorrowedFd<'a>>, InvalidId> {
+            match msg {
+                Event::__phantom_lifetime { .. } => unreachable!(),
+            }
         }
         fn __set_object_data(
             &mut self,
@@ -933,11 +948,16 @@ pub mod tertiary {
     }
     #[derive(Debug)]
     #[non_exhaustive]
-    pub enum Event {}
-    impl Event {
+    pub enum Event<'a> {
+        #[doc(hidden)]
+        __phantom_lifetime { phantom: std::marker::PhantomData<&'a ()> },
+    }
+    impl<'a> Event<'a> {
         #[doc = "Get the opcode number of this message"]
         pub fn opcode(&self) -> u16 {
-            match *self {}
+            match *self {
+                Event::__phantom_lifetime { .. } => unreachable!(),
+            }
         }
     }
     #[doc = "tertiary\n\nSee also the [Request] enum for this interface."]
@@ -971,7 +991,7 @@ pub mod tertiary {
     }
     impl super::wayland_server::Resource for Tertiary {
         type Request = Request;
-        type Event = Event;
+        type Event<'event> = Event<'event>;
         #[inline]
         fn interface() -> &'static Interface {
             &super::TERTIARY_INTERFACE
@@ -1007,7 +1027,7 @@ pub mod tertiary {
             let data = conn.get_object_data(id.clone()).ok();
             Ok(Tertiary { id, data, version, handle: conn.backend_handle().downgrade() })
         }
-        fn send_event(&self, evt: Self::Event) -> Result<(), InvalidId> {
+        fn send_event(&self, evt: Self::Event<'_>) -> Result<(), InvalidId> {
             let handle = DisplayHandle::from(self.handle.upgrade().ok_or(InvalidId)?);
             handle.send_event(self, evt)
         }
@@ -1036,12 +1056,14 @@ pub mod tertiary {
                 }),
             }
         }
-        fn write_event(
+        fn write_event<'a>(
             &self,
             conn: &DisplayHandle,
-            msg: Self::Event,
-        ) -> Result<Message<ObjectId, std::os::unix::io::RawFd>, InvalidId> {
-            match msg {}
+            msg: Self::Event<'a>,
+        ) -> Result<Message<ObjectId, std::os::unix::io::BorrowedFd<'a>>, InvalidId> {
+            match msg {
+                Event::__phantom_lifetime { .. } => unreachable!(),
+            }
         }
         fn __set_object_data(
             &mut self,
@@ -1082,11 +1104,16 @@ pub mod quad {
     }
     #[derive(Debug)]
     #[non_exhaustive]
-    pub enum Event {}
-    impl Event {
+    pub enum Event<'a> {
+        #[doc(hidden)]
+        __phantom_lifetime { phantom: std::marker::PhantomData<&'a ()> },
+    }
+    impl<'a> Event<'a> {
         #[doc = "Get the opcode number of this message"]
         pub fn opcode(&self) -> u16 {
-            match *self {}
+            match *self {
+                Event::__phantom_lifetime { .. } => unreachable!(),
+            }
         }
     }
     #[doc = "quad\n\nSee also the [Request] enum for this interface."]
@@ -1120,7 +1147,7 @@ pub mod quad {
     }
     impl super::wayland_server::Resource for Quad {
         type Request = Request;
-        type Event = Event;
+        type Event<'event> = Event<'event>;
         #[inline]
         fn interface() -> &'static Interface {
             &super::QUAD_INTERFACE
@@ -1156,7 +1183,7 @@ pub mod quad {
             let data = conn.get_object_data(id.clone()).ok();
             Ok(Quad { id, data, version, handle: conn.backend_handle().downgrade() })
         }
-        fn send_event(&self, evt: Self::Event) -> Result<(), InvalidId> {
+        fn send_event(&self, evt: Self::Event<'_>) -> Result<(), InvalidId> {
             let handle = DisplayHandle::from(self.handle.upgrade().ok_or(InvalidId)?);
             handle.send_event(self, evt)
         }
@@ -1185,12 +1212,14 @@ pub mod quad {
                 }),
             }
         }
-        fn write_event(
+        fn write_event<'a>(
             &self,
             conn: &DisplayHandle,
-            msg: Self::Event,
-        ) -> Result<Message<ObjectId, std::os::unix::io::RawFd>, InvalidId> {
-            match msg {}
+            msg: Self::Event<'a>,
+        ) -> Result<Message<ObjectId, std::os::unix::io::BorrowedFd<'a>>, InvalidId> {
+            match msg {
+                Event::__phantom_lifetime { .. } => unreachable!(),
+            }
         }
         fn __set_object_data(
             &mut self,
diff --git a/wayland-server/CHANGELOG.md b/wayland-server/CHANGELOG.md
index f8e717e4cf6..a4eaf305392 100644
--- a/wayland-server/CHANGELOG.md
+++ b/wayland-server/CHANGELOG.md
@@ -9,6 +9,7 @@
 
 - Bump bitflags to 2.0
 - Updated wayland-backend to 0.2
+- Use `BorrowedFd<'_>` arguments instead of `RawFd`
 
 #### Bugfixes
 
diff --git a/wayland-server/src/display.rs b/wayland-server/src/display.rs
index 33c6f5b9978..fb1fcaa2329 100644
--- a/wayland-server/src/display.rs
+++ b/wayland-server/src/display.rs
@@ -1,5 +1,5 @@
 use std::{
-    os::unix::io::{AsFd, BorrowedFd},
+    os::unix::io::{AsFd, AsRawFd, BorrowedFd},
     os::unix::net::UnixStream,
     sync::Arc,
 };
@@ -172,8 +172,13 @@ impl DisplayHandle {
     ///
     /// This is intended to be a low-level method. You can alternatively use the methods on the
     /// type representing your object, or [`Resource::send_event()`], which may be more convenient.
-    pub fn send_event<I: Resource>(&self, resource: &I, event: I::Event) -> Result<(), InvalidId> {
+    pub fn send_event<I: Resource>(
+        &self,
+        resource: &I,
+        event: I::Event<'_>,
+    ) -> Result<(), InvalidId> {
         let msg = resource.write_event(self, event)?;
+        let msg = msg.map_fd(|fd| fd.as_raw_fd());
         self.handle.send_event(msg)
     }
 
diff --git a/wayland-server/src/lib.rs b/wayland-server/src/lib.rs
index c6e7006dcbe..7526f430a46 100644
--- a/wayland-server/src/lib.rs
+++ b/wayland-server/src/lib.rs
@@ -136,7 +136,7 @@ use std::{
 /// Trait representing a Wayland interface
 pub trait Resource: Clone + std::fmt::Debug + Sized {
     /// The event enum for this interface
-    type Event;
+    type Event<'a>;
     /// The request enum for this interface
     type Request;
 
@@ -191,7 +191,7 @@ pub trait Resource: Clone + std::fmt::Debug + Sized {
     fn from_id(dh: &DisplayHandle, id: ObjectId) -> Result<Self, InvalidId>;
 
     /// Send an event to this object
-    fn send_event(&self, evt: Self::Event) -> Result<(), InvalidId>;
+    fn send_event(&self, evt: Self::Event<'_>) -> Result<(), InvalidId>;
 
     /// Trigger a protocol error on this object
     ///
@@ -218,11 +218,11 @@ pub trait Resource: Clone + std::fmt::Debug + Sized {
     ///
     /// **Note:** This method is mostly meant as an implementation detail to be used by code generated by
     /// wayland-scanner.
-    fn write_event(
+    fn write_event<'a>(
         &self,
         dh: &DisplayHandle,
-        req: Self::Event,
-    ) -> Result<Message<ObjectId, std::os::unix::io::RawFd>, InvalidId>;
+        req: Self::Event<'a>,
+    ) -> Result<Message<ObjectId, std::os::unix::io::BorrowedFd<'a>>, InvalidId>;
 
     /// Creates a weak handle to this object
     ///
diff --git a/wayland-tests/tests/attach_to_surface.rs b/wayland-tests/tests/attach_to_surface.rs
index 4e1966b1089..664f5c52c5c 100644
--- a/wayland-tests/tests/attach_to_surface.rs
+++ b/wayland-tests/tests/attach_to_surface.rs
@@ -2,8 +2,7 @@ extern crate tempfile;
 
 use std::fs::File;
 use std::io::{Read, Seek, Write};
-use std::os::fd::OwnedFd;
-use std::os::unix::io::AsRawFd;
+use std::os::unix::io::{AsFd, OwnedFd};
 
 #[macro_use]
 mod helpers;
@@ -87,7 +86,7 @@ fn attach_buffer() {
     let mut file = tempfile::tempfile().unwrap();
     write!(file, "I like trains!").unwrap();
     file.flush().unwrap();
-    let pool = shm.create_pool(file.as_raw_fd(), 42, &client.event_queue.handle(), ());
+    let pool = shm.create_pool(file.as_fd(), 42, &client.event_queue.handle(), ());
     let buffer = pool.create_buffer(0, 0, 0, 0, Format::Argb8888, &client.event_queue.handle(), ());
 
     let compositor = client_ddata