1 //! Utilities to work with pointers and their icons
2 
3 use std::ops::Deref;
4 
5 use wayland_client::protocol::{wl_compositor, wl_pointer, wl_seat, wl_shm};
6 use wayland_client::{NewProxy, Proxy, QueueToken};
7 
8 use wayland_client::protocol::wl_seat::RequestsTrait as SeatRequests;
9 
10 mod theme;
11 
12 pub use self::theme::{ThemeManager, ThemedPointer};
13 
14 /// Wrapper to gracefully handle a missing `libwayland-cursor`
15 ///
16 /// This wrapper has the same API as `ThemeManager`, but will
17 /// gracefully handle the case of a missing `libwayland-cursor`
18 /// by doing nothing.
19 ///
20 /// It is a convenience wrapper to handle systems where
21 /// `libwayland-client.so` is available but not `libwayland-cursor.so`.
22 pub enum AutoThemer {
23     /// The theme could be loaded
24     Themed(ThemeManager),
25     /// `libwayland-cursor.so` is not available
26     UnThemed,
27 }
28 
29 impl AutoThemer {
30     /// Load a system pointer theme
31     ///
32     /// Will use the default theme of the system if name is `None`.
33     ///
34     /// Falls back to `UnThemed` if `libwayland-cursor` is not available.
init( name: Option<&str>, compositor: Proxy<wl_compositor::WlCompositor>, shm: &Proxy<wl_shm::WlShm>, ) -> AutoThemer35     pub fn init(
36         name: Option<&str>,
37         compositor: Proxy<wl_compositor::WlCompositor>,
38         shm: &Proxy<wl_shm::WlShm>,
39     ) -> AutoThemer {
40         match ThemeManager::init(name, compositor, &shm) {
41             Ok(mgr) => AutoThemer::Themed(mgr),
42             Err(()) => AutoThemer::UnThemed,
43         }
44     }
45 
46     /// Wrap a pointer to theme it
theme_pointer(&self, pointer: Proxy<wl_pointer::WlPointer>) -> AutoPointer47     pub fn theme_pointer(&self, pointer: Proxy<wl_pointer::WlPointer>) -> AutoPointer {
48         match *self {
49             AutoThemer::Themed(ref mgr) => AutoPointer::Themed(mgr.theme_pointer(pointer)),
50             AutoThemer::UnThemed => AutoPointer::UnThemed(pointer),
51         }
52     }
53 
54     /// Initialize a new pointer as a ThemedPointer with an adapter implementation
55     ///
56     /// You need to provide an implementation as if implementing a `wl_pointer`, but
57     /// it will receive as `meta` argument an `AutoPointer` wrapping your pointer,
58     /// rather than a `Proxy<WlPointer>`.
theme_pointer_with_impl<Impl, UD>( &self, seat: &Proxy<wl_seat::WlSeat>, mut implementation: Impl, user_data: UD, ) -> AutoPointer where Impl: FnMut(wl_pointer::Event, AutoPointer) + Send + 'static, UD: Send + Sync + 'static,59     pub fn theme_pointer_with_impl<Impl, UD>(
60         &self,
61         seat: &Proxy<wl_seat::WlSeat>,
62         mut implementation: Impl,
63         user_data: UD,
64     ) -> AutoPointer
65     where
66         Impl: FnMut(wl_pointer::Event, AutoPointer) + Send + 'static,
67         UD: Send + Sync + 'static,
68     {
69         match *self {
70             AutoThemer::Themed(ref mgr) => {
71                 let pointer = mgr.theme_pointer_with_impl(
72                     seat,
73                     move |event, seat| implementation(event, AutoPointer::Themed(seat)),
74                     user_data,
75                 );
76                 AutoPointer::Themed(pointer)
77             }
78             AutoThemer::UnThemed => {
79                 let pointer = seat
80                     .get_pointer(|pointer| {
81                         pointer.implement(
82                             move |event, seat| implementation(event, AutoPointer::UnThemed(seat)),
83                             user_data,
84                         )
85                     })
86                     .unwrap();
87                 AutoPointer::UnThemed(pointer)
88             }
89         }
90     }
91 
92     /// Initialize a new pointer as a ThemedPointer with an adapter implementation
93     ///
94     /// Like `theme_pointer_with_impl` but allows you to have a non-`Send` implementation.
95     ///
96     /// **Unsafe** for the same reasons as `NewProxy::implement_nonsend`.
theme_pointer_with_nonsend_impl<Impl, UD>( &self, pointer: NewProxy<wl_pointer::WlPointer>, mut implementation: Impl, user_data: UD, token: &QueueToken, ) -> AutoPointer where Impl: FnMut(wl_pointer::Event, AutoPointer) + Send + 'static, UD: Send + Sync + 'static,97     pub unsafe fn theme_pointer_with_nonsend_impl<Impl, UD>(
98         &self,
99         pointer: NewProxy<wl_pointer::WlPointer>,
100         mut implementation: Impl,
101         user_data: UD,
102         token: &QueueToken,
103     ) -> AutoPointer
104     where
105         Impl: FnMut(wl_pointer::Event, AutoPointer) + Send + 'static,
106         UD: Send + Sync + 'static,
107     {
108         match *self {
109             AutoThemer::Themed(ref mgr) => {
110                 let pointer = mgr.theme_pointer_with_nonsend_impl(
111                     pointer,
112                     move |event, pointer| implementation(event, AutoPointer::Themed(pointer)),
113                     user_data,
114                     token,
115                 );
116                 AutoPointer::Themed(pointer)
117             }
118             AutoThemer::UnThemed => {
119                 let pointer = pointer.implement_nonsend(
120                     move |event, pointer| implementation(event, AutoPointer::UnThemed(pointer)),
121                     user_data,
122                     token,
123                 );
124                 AutoPointer::UnThemed(pointer)
125             }
126         }
127     }
128 }
129 
130 /// A pointer wrapper to gracefully handle a missing `libwayland-cursor`
131 ///
132 /// It has the same API as `ThemedPointer`, but falls back to doing nothing
133 /// in its `Unthemed` variant.
134 pub enum AutoPointer {
135     /// The `ThemedPointer`
136     Themed(ThemedPointer),
137     /// The regular pointer if theme capability is not available
138     UnThemed(Proxy<wl_pointer::WlPointer>),
139 }
140 
141 impl AutoPointer {
142     /// Change the cursor to the given cursor name
143     ///
144     /// Possible names depend on the theme. Does nothing and returns
145     /// `Err(())` if given name is not available.
146     ///
147     /// Does nothing an returns `Ok(())` if no theme is loaded (if
148     /// `wayland-cursor` is not available).
149     ///
150     /// If this is done as an answer to an input event, you need to provide
151     /// the associated serial otherwise the server may ignore the request.
set_cursor(&self, name: &str, serial: Option<u32>) -> Result<(), ()>152     pub fn set_cursor(&self, name: &str, serial: Option<u32>) -> Result<(), ()> {
153         match *self {
154             AutoPointer::Themed(ref themed) => themed.set_cursor(name, serial),
155             AutoPointer::UnThemed(_) => Ok(()),
156         }
157     }
158 }
159 
160 impl Deref for AutoPointer {
161     type Target = Proxy<wl_pointer::WlPointer>;
deref(&self) -> &Proxy<wl_pointer::WlPointer>162     fn deref(&self) -> &Proxy<wl_pointer::WlPointer> {
163         match *self {
164             AutoPointer::Themed(ref themed) => &**themed,
165             AutoPointer::UnThemed(ref ptr) => ptr,
166         }
167     }
168 }
169 
170 impl Clone for AutoPointer {
clone(&self) -> AutoPointer171     fn clone(&self) -> AutoPointer {
172         match *self {
173             AutoPointer::Themed(ref themed) => AutoPointer::Themed(themed.clone()),
174             AutoPointer::UnThemed(ref ptr) => AutoPointer::UnThemed(ptr.clone()),
175         }
176     }
177 }
178