| 1 | // SPDX-License-Identifier: MIT OR Apache-2.0 OR Zlib |
| 2 | |
| 3 | #![cfg_attr (not(feature = "std" ), no_std)] |
| 4 | #![deny (rust_2018_idioms)] |
| 5 | #![deny (rustdoc::broken_intra_doc_links)] |
| 6 | #![deny (unsafe_op_in_unsafe_fn)] |
| 7 | #![deny (improper_ctypes, improper_ctypes_definitions)] |
| 8 | #![deny (clippy::all)] |
| 9 | #![deny (missing_debug_implementations)] |
| 10 | #![deny (missing_docs)] |
| 11 | #![forbid (unsafe_code)] |
| 12 | #![cfg_attr (feature = "cargo-clippy" , deny(warnings))] |
| 13 | #![cfg_attr (docsrs, feature(doc_auto_cfg))] |
| 14 | |
| 15 | //! The cross platform cursor icon type. |
| 16 | //! |
| 17 | //! This type is intended to be used as a standard interopability type between |
| 18 | //! GUI frameworks in order to convey the cursor icon type. |
| 19 | //! |
| 20 | //! # Example |
| 21 | //! |
| 22 | //! ``` |
| 23 | //! use cursor_icon::CursorIcon; |
| 24 | //! |
| 25 | //! # fn main() -> Result<(), Box<dyn std::error::Error>> { |
| 26 | //! // Parse a cursor icon from the string that describes it. |
| 27 | //! let cursor_name = "pointer" ; |
| 28 | //! let cursor_icon: CursorIcon = cursor_name.parse()?; |
| 29 | //! println!("The cursor icon is {:?}" , cursor_icon); |
| 30 | //! # Ok(()) |
| 31 | //! # } |
| 32 | //! ``` |
| 33 | |
| 34 | // This file contains a portion of the CSS Basic User Interface Module Level 3 |
| 35 | // specification. In particular, the names for the cursor from the #cursor |
| 36 | // section and documentation for some of the variants were taken. |
| 37 | // |
| 38 | // The original document is https://www.w3.org/TR/css-ui-3/#cursor. |
| 39 | // Copyright © 2018 W3C® (MIT, ERCIM, Keio, Beihang) |
| 40 | // |
| 41 | // These documents were used under the terms of the following license. This W3C |
| 42 | // license as well as the W3C short notice apply to the `CursorIcon` enum's |
| 43 | // variants and documentation attached to them. |
| 44 | |
| 45 | // --------- BEGGINING OF W3C LICENSE |
| 46 | // -------------------------------------------------------------- |
| 47 | // |
| 48 | // License |
| 49 | // |
| 50 | // By obtaining and/or copying this work, you (the licensee) agree that you have |
| 51 | // read, understood, and will comply with the following terms and conditions. |
| 52 | // |
| 53 | // Permission to copy, modify, and distribute this work, with or without |
| 54 | // modification, for any purpose and without fee or royalty is hereby granted, |
| 55 | // provided that you include the following on ALL copies of the work or portions |
| 56 | // thereof, including modifications: |
| 57 | // |
| 58 | // - The full text of this NOTICE in a location viewable to users of the |
| 59 | // redistributed or derivative work. |
| 60 | // - Any pre-existing intellectual property disclaimers, notices, or terms and |
| 61 | // conditions. If none exist, the W3C Software and Document Short Notice |
| 62 | // should be included. |
| 63 | // - Notice of any changes or modifications, through a copyright statement on |
| 64 | // the new code or document such as "This software or document includes |
| 65 | // material copied from or derived from [title and URI of the W3C document]. |
| 66 | // Copyright © [YEAR] W3C® (MIT, ERCIM, Keio, Beihang)." |
| 67 | // |
| 68 | // Disclaimers |
| 69 | // |
| 70 | // THIS WORK IS PROVIDED "AS IS," AND COPYRIGHT HOLDERS MAKE NO REPRESENTATIONS |
| 71 | // OR WARRANTIES, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO, WARRANTIES |
| 72 | // OF MERCHANTABILITY OR FITNESS FOR ANY PARTICULAR PURPOSE OR THAT THE USE OF |
| 73 | // THE SOFTWARE OR DOCUMENT WILL NOT INFRINGE ANY THIRD PARTY PATENTS, |
| 74 | // COPYRIGHTS, TRADEMARKS OR OTHER RIGHTS. |
| 75 | // |
| 76 | // COPYRIGHT HOLDERS WILL NOT BE LIABLE FOR ANY DIRECT, INDIRECT, SPECIAL OR |
| 77 | // CONSEQUENTIAL DAMAGES ARISING OUT OF ANY USE OF THE SOFTWARE OR DOCUMENT. |
| 78 | // |
| 79 | // The name and trademarks of copyright holders may NOT be used in advertising |
| 80 | // or publicity pertaining to the work without specific, written prior |
| 81 | // permission. Title to copyright in this work will at all times remain with |
| 82 | // copyright holders. |
| 83 | // |
| 84 | // --------- END OF W3C LICENSE |
| 85 | // -------------------------------------------------------------------- |
| 86 | |
| 87 | // --------- BEGGINING OF W3C SHORT NOTICE |
| 88 | // --------------------------------------------------------- |
| 89 | // |
| 90 | // winit: https://github.com/rust-windowing/cursor-icon |
| 91 | // |
| 92 | // Copyright © 2023 World Wide Web Consortium, (Massachusetts Institute of |
| 93 | // Technology, European Research Consortium for Informatics and Mathematics, |
| 94 | // Keio University, Beihang). All Rights Reserved. This work is distributed |
| 95 | // under the W3C® Software License [1] in the hope that it will be useful, but |
| 96 | // WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or |
| 97 | // FITNESS FOR A PARTICULAR PURPOSE. |
| 98 | // |
| 99 | // [1] http://www.w3.org/Consortium/Legal/copyright-software |
| 100 | // |
| 101 | // --------- END OF W3C SHORT NOTICE |
| 102 | // -------------------------------------------------------------- |
| 103 | |
| 104 | #[cfg (feature = "serde" )] |
| 105 | #[macro_use ] |
| 106 | extern crate serde; |
| 107 | |
| 108 | // XXX for forwards compatibility. |
| 109 | #[cfg (feature = "alloc" )] |
| 110 | extern crate alloc as _; |
| 111 | |
| 112 | /// Describes the appearance of the (usually mouse) cursor icon. |
| 113 | /// |
| 114 | /// The names are taken from the CSS W3C specification: |
| 115 | /// <https://www.w3.org/TR/css-ui-3/#cursor> |
| 116 | /// |
| 117 | /// # Examples |
| 118 | /// |
| 119 | /// ``` |
| 120 | /// use cursor_icon::CursorIcon; |
| 121 | /// |
| 122 | /// // Get the cursor icon for the default cursor. |
| 123 | /// let cursor_icon = CursorIcon::Default; |
| 124 | /// ``` |
| 125 | #[non_exhaustive ] |
| 126 | #[derive (Debug, Default, Copy, Clone, PartialEq, Eq, Hash)] |
| 127 | #[cfg_attr (feature = "serde" , derive(Serialize, Deserialize))] |
| 128 | pub enum CursorIcon { |
| 129 | /// The platform-dependent default cursor. Often rendered as arrow. |
| 130 | #[default] |
| 131 | Default, |
| 132 | |
| 133 | /// A context menu is available for the object under the cursor. Often |
| 134 | /// rendered as an arrow with a small menu-like graphic next to it. |
| 135 | ContextMenu, |
| 136 | |
| 137 | /// Help is available for the object under the cursor. Often rendered as a |
| 138 | /// question mark or a balloon. |
| 139 | Help, |
| 140 | |
| 141 | /// The cursor is a pointer that indicates a link. Often rendered as the |
| 142 | /// backside of a hand with the index finger extended. |
| 143 | Pointer, |
| 144 | |
| 145 | /// A progress indicator. The program is performing some processing, but is |
| 146 | /// different from [`CursorIcon::Wait`] in that the user may still interact |
| 147 | /// with the program. |
| 148 | Progress, |
| 149 | |
| 150 | /// Indicates that the program is busy and the user should wait. Often |
| 151 | /// rendered as a watch or hourglass. |
| 152 | Wait, |
| 153 | |
| 154 | /// Indicates that a cell or set of cells may be selected. Often rendered as |
| 155 | /// a thick plus-sign with a dot in the middle. |
| 156 | Cell, |
| 157 | |
| 158 | /// A simple crosshair (e.g., short line segments resembling a "+" sign). |
| 159 | /// Often used to indicate a two dimensional bitmap selection mode. |
| 160 | Crosshair, |
| 161 | |
| 162 | /// Indicates text that may be selected. Often rendered as an I-beam. |
| 163 | Text, |
| 164 | |
| 165 | /// Indicates vertical-text that may be selected. Often rendered as a |
| 166 | /// horizontal I-beam. |
| 167 | VerticalText, |
| 168 | |
| 169 | /// Indicates an alias of/shortcut to something is to be created. Often |
| 170 | /// rendered as an arrow with a small curved arrow next to it. |
| 171 | Alias, |
| 172 | |
| 173 | /// Indicates something is to be copied. Often rendered as an arrow with a |
| 174 | /// small plus sign next to it. |
| 175 | Copy, |
| 176 | |
| 177 | /// Indicates something is to be moved. |
| 178 | Move, |
| 179 | |
| 180 | /// Indicates that the dragged item cannot be dropped at the current cursor |
| 181 | /// location. Often rendered as a hand or pointer with a small circle with a |
| 182 | /// line through it. |
| 183 | NoDrop, |
| 184 | |
| 185 | /// Indicates that the requested action will not be carried out. Often |
| 186 | /// rendered as a circle with a line through it. |
| 187 | NotAllowed, |
| 188 | |
| 189 | /// Indicates that something can be grabbed (dragged to be moved). Often |
| 190 | /// rendered as the backside of an open hand. |
| 191 | Grab, |
| 192 | |
| 193 | /// Indicates that something is being grabbed (dragged to be moved). Often |
| 194 | /// rendered as the backside of a hand with fingers closed mostly out of |
| 195 | /// view. |
| 196 | Grabbing, |
| 197 | |
| 198 | /// The east border to be moved. |
| 199 | EResize, |
| 200 | |
| 201 | /// The north border to be moved. |
| 202 | NResize, |
| 203 | |
| 204 | /// The north-east corner to be moved. |
| 205 | NeResize, |
| 206 | |
| 207 | /// The north-west corner to be moved. |
| 208 | NwResize, |
| 209 | |
| 210 | /// The south border to be moved. |
| 211 | SResize, |
| 212 | |
| 213 | /// The south-east corner to be moved. |
| 214 | SeResize, |
| 215 | |
| 216 | /// The south-west corner to be moved. |
| 217 | SwResize, |
| 218 | |
| 219 | /// The west border to be moved. |
| 220 | WResize, |
| 221 | |
| 222 | /// The east and west borders to be moved. |
| 223 | EwResize, |
| 224 | |
| 225 | /// The south and north borders to be moved. |
| 226 | NsResize, |
| 227 | |
| 228 | /// The north-east and south-west corners to be moved. |
| 229 | NeswResize, |
| 230 | |
| 231 | /// The north-west and south-east corners to be moved. |
| 232 | NwseResize, |
| 233 | |
| 234 | /// Indicates that the item/column can be resized horizontally. Often |
| 235 | /// rendered as arrows pointing left and right with a vertical bar |
| 236 | /// separating them. |
| 237 | ColResize, |
| 238 | |
| 239 | /// Indicates that the item/row can be resized vertically. Often rendered as |
| 240 | /// arrows pointing up and down with a horizontal bar separating them. |
| 241 | RowResize, |
| 242 | |
| 243 | /// Indicates that the something can be scrolled in any direction. Often |
| 244 | /// rendered as arrows pointing up, down, left, and right with a dot in the |
| 245 | /// middle. |
| 246 | AllScroll, |
| 247 | |
| 248 | /// Indicates that something can be zoomed in. Often rendered as a |
| 249 | /// magnifying glass with a "+" in the center of the glass. |
| 250 | ZoomIn, |
| 251 | |
| 252 | /// Indicates that something can be zoomed in. Often rendered as a |
| 253 | /// magnifying glass with a "-" in the center of the glass. |
| 254 | ZoomOut, |
| 255 | } |
| 256 | |
| 257 | impl CursorIcon { |
| 258 | /// The name of the cursor icon as defined in w3c standard. |
| 259 | /// |
| 260 | /// This name most of the time could be passed as is to cursor loading |
| 261 | /// libraries on X11/Wayland and could be used as-is on web. |
| 262 | /// |
| 263 | /// # Examples |
| 264 | /// |
| 265 | /// ```no_run |
| 266 | /// use cursor_icon::CursorIcon; |
| 267 | /// use wayland_cursor::CursorTheme; |
| 268 | /// |
| 269 | /// # use wayland_client::Connection; |
| 270 | /// # use wayland_client::protocol::wl_shm::WlShm; |
| 271 | /// # fn test(conn: &Connection, shm: WlShm) -> Result<(), Box<dyn std::error::Error>> { |
| 272 | /// // Choose a cursor to load. |
| 273 | /// let cursor = CursorIcon::Help; |
| 274 | /// |
| 275 | /// // Load the Wayland cursor theme. |
| 276 | /// let mut cursor_theme = CursorTheme::load(conn, shm, 32)?; |
| 277 | /// |
| 278 | /// // Load the cursor. |
| 279 | /// let cursor = cursor_theme.get_cursor(cursor.name()); |
| 280 | /// if let Some(cursor) = cursor { |
| 281 | /// println!("Total number of images: {}" , cursor.image_count()); |
| 282 | /// } |
| 283 | /// # Ok(()) |
| 284 | /// # } |
| 285 | /// ``` |
| 286 | pub fn name(&self) -> &'static str { |
| 287 | match self { |
| 288 | CursorIcon::Default => "default" , |
| 289 | CursorIcon::ContextMenu => "context-menu" , |
| 290 | CursorIcon::Help => "help" , |
| 291 | CursorIcon::Pointer => "pointer" , |
| 292 | CursorIcon::Progress => "progress" , |
| 293 | CursorIcon::Wait => "wait" , |
| 294 | CursorIcon::Cell => "cell" , |
| 295 | CursorIcon::Crosshair => "crosshair" , |
| 296 | CursorIcon::Text => "text" , |
| 297 | CursorIcon::VerticalText => "vertical-text" , |
| 298 | CursorIcon::Alias => "alias" , |
| 299 | CursorIcon::Copy => "copy" , |
| 300 | CursorIcon::Move => "move" , |
| 301 | CursorIcon::NoDrop => "no-drop" , |
| 302 | CursorIcon::NotAllowed => "not-allowed" , |
| 303 | CursorIcon::Grab => "grab" , |
| 304 | CursorIcon::Grabbing => "grabbing" , |
| 305 | CursorIcon::EResize => "e-resize" , |
| 306 | CursorIcon::NResize => "n-resize" , |
| 307 | CursorIcon::NeResize => "ne-resize" , |
| 308 | CursorIcon::NwResize => "nw-resize" , |
| 309 | CursorIcon::SResize => "s-resize" , |
| 310 | CursorIcon::SeResize => "se-resize" , |
| 311 | CursorIcon::SwResize => "sw-resize" , |
| 312 | CursorIcon::WResize => "w-resize" , |
| 313 | CursorIcon::EwResize => "ew-resize" , |
| 314 | CursorIcon::NsResize => "ns-resize" , |
| 315 | CursorIcon::NeswResize => "nesw-resize" , |
| 316 | CursorIcon::NwseResize => "nwse-resize" , |
| 317 | CursorIcon::ColResize => "col-resize" , |
| 318 | CursorIcon::RowResize => "row-resize" , |
| 319 | CursorIcon::AllScroll => "all-scroll" , |
| 320 | CursorIcon::ZoomIn => "zoom-in" , |
| 321 | CursorIcon::ZoomOut => "zoom-out" , |
| 322 | } |
| 323 | } |
| 324 | |
| 325 | /// A list of alternative names for the cursor icon as commonly found in |
| 326 | /// legacy Xcursor themes. |
| 327 | /// |
| 328 | /// This should only be used as a fallback in case the cursor theme does not |
| 329 | /// adhere to the w3c standard. |
| 330 | pub fn alt_names(&self) -> &[&'static str] { |
| 331 | match self { |
| 332 | CursorIcon::Default => &["left_ptr" , "arrow" , "top_left_arrow" , "left_arrow" ], |
| 333 | CursorIcon::ContextMenu => &[], |
| 334 | CursorIcon::Help => &["question_arrow" , "whats_this" ], |
| 335 | CursorIcon::Pointer => &["hand2" , "hand1" , "hand" , "pointing_hand" ], |
| 336 | CursorIcon::Progress => &["left_ptr_watch" , "half-busy" ], |
| 337 | CursorIcon::Wait => &["watch" ], |
| 338 | CursorIcon::Cell => &["plus" ], |
| 339 | CursorIcon::Crosshair => &["cross" ], |
| 340 | CursorIcon::Text => &["xterm" , "ibeam" ], |
| 341 | CursorIcon::VerticalText => &[], |
| 342 | CursorIcon::Alias => &["link" ], |
| 343 | CursorIcon::Copy => &[], |
| 344 | CursorIcon::Move => &[], |
| 345 | CursorIcon::NoDrop => &["circle" ], |
| 346 | CursorIcon::NotAllowed => &["crossed_circle" , "forbidden" ], |
| 347 | CursorIcon::Grab => &["openhand" , "fleur" ], |
| 348 | CursorIcon::Grabbing => &["closedhand" ], |
| 349 | CursorIcon::EResize => &["right_side" ], |
| 350 | CursorIcon::NResize => &["top_side" ], |
| 351 | CursorIcon::NeResize => &["top_right_corner" ], |
| 352 | CursorIcon::NwResize => &["top_left_corner" ], |
| 353 | CursorIcon::SResize => &["bottom_side" ], |
| 354 | CursorIcon::SeResize => &["bottom_right_corner" ], |
| 355 | CursorIcon::SwResize => &["bottom_left_corner" ], |
| 356 | CursorIcon::WResize => &["left_side" ], |
| 357 | CursorIcon::EwResize => &["h_double_arrow" , "size_hor" ], |
| 358 | CursorIcon::NsResize => &["v_double_arrow" , "size_ver" ], |
| 359 | CursorIcon::NeswResize => &["fd_double_arrow" , "size_bdiag" ], |
| 360 | CursorIcon::NwseResize => &["bd_double_arrow" , "size_fdiag" ], |
| 361 | CursorIcon::ColResize => &["split_h" , "h_double_arrow" , "sb_h_double_arrow" ], |
| 362 | CursorIcon::RowResize => &["split_v" , "v_double_arrow" , "sb_v_double_arrow" ], |
| 363 | CursorIcon::AllScroll => &["size_all" ], |
| 364 | CursorIcon::ZoomIn => &[], |
| 365 | CursorIcon::ZoomOut => &[], |
| 366 | } |
| 367 | } |
| 368 | } |
| 369 | |
| 370 | impl core::fmt::Display for CursorIcon { |
| 371 | fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { |
| 372 | f.write_str(self.name()) |
| 373 | } |
| 374 | } |
| 375 | |
| 376 | impl core::str::FromStr for CursorIcon { |
| 377 | type Err = ParseError; |
| 378 | |
| 379 | /// Parse a string slice into [`CursorIcon`]. |
| 380 | /// |
| 381 | /// The `name` is a lower kebab case [`CursorIcon`] varaint name, e.g. |
| 382 | /// `nesw-resize`. The set of possible valid `name` values matches exactly |
| 383 | /// the set of [`CursorIcon::name`] outputs. |
| 384 | fn from_str(name: &str) -> Result<Self, Self::Err> { |
| 385 | match name { |
| 386 | "default" => Ok(CursorIcon::Default), |
| 387 | "context-menu" => Ok(CursorIcon::ContextMenu), |
| 388 | "help" => Ok(CursorIcon::Help), |
| 389 | "pointer" => Ok(CursorIcon::Pointer), |
| 390 | "progress" => Ok(CursorIcon::Progress), |
| 391 | "wait" => Ok(CursorIcon::Wait), |
| 392 | "cell" => Ok(CursorIcon::Cell), |
| 393 | "crosshair" => Ok(CursorIcon::Crosshair), |
| 394 | "text" => Ok(CursorIcon::Text), |
| 395 | "vertical-text" => Ok(CursorIcon::VerticalText), |
| 396 | "alias" => Ok(CursorIcon::Alias), |
| 397 | "copy" => Ok(CursorIcon::Copy), |
| 398 | "move" => Ok(CursorIcon::Move), |
| 399 | "no-drop" => Ok(CursorIcon::NoDrop), |
| 400 | "not-allowed" => Ok(CursorIcon::NotAllowed), |
| 401 | "grab" => Ok(CursorIcon::Grab), |
| 402 | "grabbing" => Ok(CursorIcon::Grabbing), |
| 403 | "e-resize" => Ok(CursorIcon::EResize), |
| 404 | "n-resize" => Ok(CursorIcon::NResize), |
| 405 | "ne-resize" => Ok(CursorIcon::NeResize), |
| 406 | "nw-resize" => Ok(CursorIcon::NwResize), |
| 407 | "s-resize" => Ok(CursorIcon::SResize), |
| 408 | "se-resize" => Ok(CursorIcon::SeResize), |
| 409 | "sw-resize" => Ok(CursorIcon::SwResize), |
| 410 | "w-resize" => Ok(CursorIcon::WResize), |
| 411 | "ew-resize" => Ok(CursorIcon::EwResize), |
| 412 | "ns-resize" => Ok(CursorIcon::NsResize), |
| 413 | "nesw-resize" => Ok(CursorIcon::NeswResize), |
| 414 | "nwse-resize" => Ok(CursorIcon::NwseResize), |
| 415 | "col-resize" => Ok(CursorIcon::ColResize), |
| 416 | "row-resize" => Ok(CursorIcon::RowResize), |
| 417 | "all-scroll" => Ok(CursorIcon::AllScroll), |
| 418 | "zoom-in" => Ok(CursorIcon::ZoomIn), |
| 419 | "zoom-out" => Ok(CursorIcon::ZoomOut), |
| 420 | _ => Err(ParseError { _private: () }), |
| 421 | } |
| 422 | } |
| 423 | } |
| 424 | |
| 425 | /// An error which could be returned when parsing [`CursorIcon`]. |
| 426 | /// |
| 427 | /// This occurs when the [`FromStr`] implementation of [`CursorIcon`] fails. |
| 428 | /// |
| 429 | /// [`FromStr`]: core::str::FromStr |
| 430 | #[derive (Debug, PartialEq, Eq)] |
| 431 | pub struct ParseError { |
| 432 | _private: (), |
| 433 | } |
| 434 | |
| 435 | impl core::fmt::Display for ParseError { |
| 436 | fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result { |
| 437 | f.write_str(data:"failed to parse cursor icon" ) |
| 438 | } |
| 439 | } |
| 440 | |
| 441 | #[cfg (feature = "std" )] |
| 442 | impl std::error::Error for ParseError {} |
| 443 | |