Vendor things
This commit is contained in:
parent
5deceec006
commit
977e3c17e5
19434 changed files with 10682014 additions and 0 deletions
1
third-party/vendor/smithay-client-toolkit/.cargo-checksum.json
vendored
Normal file
1
third-party/vendor/smithay-client-toolkit/.cargo-checksum.json
vendored
Normal file
|
|
@ -0,0 +1 @@
|
|||
{"files":{"CHANGELOG.md":"073bbd845efa6507b06454dafaea8522cf770f79f4e4db1cd95184801647779e","CONTRIBUTING.md":"b3a310f25decb6d92da06a5db2df1a6d2e2a0843890e07c0bba0d517f83c60bf","Cargo.lock":"a7edf605d2a8036b36634623a060c2732f7069c4fc722fb232c442980be03f33","Cargo.toml":"4fc51a6dbd0d74cedd8e187244d806226d6d0c24312208feee787c0269510c4f","LICENSE.txt":"13c0147abe1c7607fde840e2dffcde75bf925aea504b2845b7be4869a5af2afc","README.md":"7cf1cde86f6c9af14eed44e4390231747532555d9fdec31e59bdf6794a4e0821","build.rs":"87437f55297a85ba986561397e0e3dc107ca4c0b1077957ef003589482702009","doc_index.html":"383be472c810a6ad7970779a37f2344735a6331ed21bd48de7a38a2772301621","examples/compositor_info.rs":"ad9037e9456e8696feb20b1084f577f3a02c4425275646a9a7a5ffed23a4d698","examples/image_viewer.rs":"45771bb9030bcfaf66d20aff0b9b9c8e5895f8e7d87f00aa82abb602e07ffeb7","examples/kbd_input.rs":"3675c611b1e0b7feffd5d8de02f2ec09aecc7b2618e825ae752a72aecb934b02","examples/layer_shell.rs":"caa7cfb1fa21653f7ea425fdb34d253c0b4cf3f6356e74258134916b53ef4c40","examples/pointer_input.rs":"6d892f86e93b5ecd5fd90f6ae4605c8ea25851b692b14ad40559c34f82936d5a","examples/selection.rs":"f8b80c424b7eadae64ecc8d97e7a4991e00ff0111568bef75b7e4b507481626f","rustfmt.toml":"96bdd19cb3977f491db46410865c40708050eb093d27fff129a19e4d19be3caf","src/data_device/device.rs":"d3fde91beaab377bc5d0b0010635a9fd11fd962c216b2d2d6eddcfe2d5e42030","src/data_device/mod.rs":"de16c3de6b45b50d299ec23c61a7d437026c9878bbb54ee2df7c8baedd08361d","src/data_device/offer.rs":"af3937c1f2b13d58b7d74688afcd32defbbbd4ca8f6b33833263da2509ec7ebb","src/data_device/source.rs":"65b5549c646494dfb3b025005f878f805d47e63fa0bc09755f5229a61a7b4ba3","src/environment.rs":"0ba441ac65e1869f856248eb76430a2fd664b23af4a3cd60f9bc227151526000","src/event_loop.rs":"7dc6b7a050eefebc3537a50278bbdc53d1eda4b990b79474eee6732e71476c6e","src/lazy_global.rs":"e43dbd2adcfa9d2da91a0aa535ae96782480a19733f055468dcc8761780e6c05","src/lib.rs":"ad76dc29c402645bc6b6b0ea442cd12b52484babf3764960bb42ade9ff21f095","src/output.rs":"57f62319c77244c051c637e820f6e5752cc51a492f6fb9bc7d73be774161e42b","src/primary_selection/device.rs":"cb14a5b58d1c7672dd314c64667c246db57456435dcd67bba47334098e0f18a5","src/primary_selection/mod.rs":"87756c1830667d661b97452a6cc7cf81402ac3807e37f16a4bfa8091b0815d43","src/primary_selection/offer.rs":"8897b89cb88eec65636aa2d98edf012784ed6dbd49c7979b21aea9d030768f0c","src/primary_selection/source.rs":"d3e8b88cc5db093edb31160965935ca8e51f001ef06435b749183e002695222c","src/seat/keyboard/ffi.rs":"3c557fc7129375d0ac473e0b1746931043fb3dd03b248e2e6c1b1b9d3c9be151","src/seat/keyboard/keysyms.rs":"9fa396664a5fa15969b113b2db20ec9458b319c252a3f6d6e4bb289ee9518e34","src/seat/keyboard/mod.rs":"70fb92d78f8330b9a392100cc3bd7fa3bc71ce6ad9bf8043a8e7578d2e5a8a31","src/seat/keyboard/state.rs":"919481632769f6c9329c8567ffb31faec3f2cb4e41e9a0902c013a88d8066bb9","src/seat/mod.rs":"c295052c84c36028c707fc71b73225d92408c10e724c691e6a74ed2813741381","src/seat/pointer/mod.rs":"f072cea6c05d1016fe14bdf15ae2dec3710074be27814b9d25823297bbc7f7b3","src/seat/pointer/theme.rs":"70152bb568bbfe8838d27c41d6f72581f5ac8badeb24eb771dcf5c48106905c6","src/shell/mod.rs":"19636f02068a5b764e1bf792cc0592472985e5f6999781e513828260409b866d","src/shell/wl.rs":"c60ac68102dddbf20e7b4221e5f680a218d34b01c7262c9477eb5ccadf74aa5b","src/shell/xdg.rs":"1a197abed488ac2d780d39d299ff25ebd435246f4811f6cadd2a8a38a01873a1","src/shell/zxdg.rs":"c185c9af4484f1fc6be5cb860aa599b31b461ea6bfb892d637c2fa6e0c50a52d","src/shm/mempool.rs":"8d780ec343c59c83ebfba276088508c0275fd7e9e4652c8a153dc650b6d9b137","src/shm/mod.rs":"e8433652b3220fd7a03065e6d543ae9f69af109ccc888125cea7d7e5370d9afe","src/surface.rs":"bee8c7ec826199a9e663362b0e3d1a3fefe49dfc9e22a879e8aa7161033810ad","src/window/fallback_frame.rs":"d8ff611b43a5ae7b85c22b6971c4b943fdd0a272a93d2588e7f588937a3b732e","src/window/mod.rs":"2c328506fa5a45dfd578b657dbc86ea1ee263c7e2225cd3376455f568345fb00","travis_install_wayland.sh":"02ba67cbc481d65b75440d04e059ab00fbd59bc1821d6206d28c6f4499e3368a","update_keysyms.sh":"2c764c93647847b96bc26d1daf2e3d904f5e128c78910c237e482484561f4301"},"package":"870427e30b8f2cbe64bf43ec4b86e88fe39b0a84b3f15efd9c9c2d020bc86eb9"}
|
||||
378
third-party/vendor/smithay-client-toolkit/CHANGELOG.md
vendored
Normal file
378
third-party/vendor/smithay-client-toolkit/CHANGELOG.md
vendored
Normal file
|
|
@ -0,0 +1,378 @@
|
|||
# Change Log
|
||||
|
||||
## Unreleased
|
||||
|
||||
## 0.16.1 - 2023-09-18
|
||||
|
||||
#### Bugfixes
|
||||
|
||||
- Restrict `SimpleGlobal` bindings to our max API version
|
||||
|
||||
## 0.16.0 - 2022-06-18
|
||||
|
||||
#### Breaking changes
|
||||
|
||||
- `calloop` is updated to version 0.10, and the keyboard handling API is slightly changed as a result.
|
||||
|
||||
#### Additions
|
||||
|
||||
- `DataDevice::with_dnd` and `DataOffer::receive_to_fd` allow more flexible interfaction with the data device abstraction
|
||||
- the output integration now supports version `4` of `wl_output`
|
||||
|
||||
## 0.15.4 - 2022-04-10
|
||||
|
||||
#### Bugfixes
|
||||
|
||||
- `Window`'s `wl_pointer` not being relased on `Drop`.
|
||||
|
||||
## 0.15.3 - 2021-12-27
|
||||
|
||||
#### Bugfixes
|
||||
|
||||
- SCTK now correctly interacts with the wayland socket being conccurently poolled from
|
||||
other threads.
|
||||
|
||||
## 0.15.2 - 2021-10-27
|
||||
|
||||
- Most types are now `Debug`
|
||||
|
||||
## 0.15.1 - 2021-08-23
|
||||
|
||||
#### Bugfixes
|
||||
|
||||
- when not using `dlopen` feature, `xkbdcommon` library is linked using `pkg-config`
|
||||
|
||||
## 0.15.0 - 2021-08-10
|
||||
|
||||
#### Breaking Changes
|
||||
|
||||
- Update `wayland-client` to 0.29
|
||||
|
||||
#### Additions
|
||||
|
||||
- `AutoMemPool` now guarantees a minimum alignment of returned buffers
|
||||
|
||||
## 0.14.0 - 2021-05-07
|
||||
|
||||
#### Breaking Changes
|
||||
|
||||
- `ConceptFrame` is removed, as well as the `frames` cargo feature, and replaced by a more minimalistic
|
||||
`FallbackFrame`. Dependency on `andrew` and `fontconfig` is dropped in the process. If fancier
|
||||
decorations are needed, they should be implemented using the `Frame` trait.
|
||||
- Update to calloop 0.7: `calloop::Source` is replaced by `calloop::RegistrationToken`
|
||||
|
||||
#### Additions
|
||||
|
||||
- `AutoMemPool` added as an alternative to the existing SHM pools
|
||||
|
||||
## 0.13.0 - 2021-03-04
|
||||
|
||||
#### Breaking Changes
|
||||
|
||||
- Mark OutputInfo as `#[non_exhaustive]` to allow future expansion without
|
||||
breaking API.
|
||||
- Batch output information updates instead of potentially making multiple
|
||||
callbacks for one logical change
|
||||
- Add name and description fields to OutputInfo.
|
||||
|
||||
#### Additions
|
||||
|
||||
- `Window::start_interactive_move` to enable dragging the window with a user action
|
||||
|
||||
#### Bugfixes
|
||||
|
||||
- `ConceptFrame` now correctly loads fonts using fontconfig
|
||||
|
||||
## 0.12.2 -- 2020-12-30
|
||||
|
||||
#### Changes
|
||||
|
||||
- Dependency on `byteorder` was replaced with `u32::from_ne_bytes()`
|
||||
|
||||
#### Bugfixes
|
||||
|
||||
- Don't crash when the font cannot be loaded to draw decorations
|
||||
|
||||
## 0.12.1 -- 2020-12-08
|
||||
|
||||
#### Changes
|
||||
|
||||
- Unmaintained `memmap` dependency is replaced with `memmap2`
|
||||
|
||||
## 0.12.0 -- 2020-09-30
|
||||
|
||||
#### Breaking Changes
|
||||
|
||||
- Update `wayland-client` to version 0.28
|
||||
- `Environment::init` was renamed to `Environment::new_pending`
|
||||
- `init_default_environment!` macro was renamed to `new_default_environment!`
|
||||
|
||||
#### Additions
|
||||
|
||||
- `Environment::new` method to fully bootstrap environment
|
||||
|
||||
## 0.11.0 -- 2020-08-30
|
||||
|
||||
#### Breaking Changes
|
||||
|
||||
- `window.set_decorate` is now taking mutable reference
|
||||
- Added `show_window_menu` on a `Frame` trait to request a window menu for a window.
|
||||
- `ShowMenu` enum variant to `FrameRequest`
|
||||
- `create_window` now also takes `Option<ThemeManager>`
|
||||
- `Frame::init` now also takes `Option<ThemeManager>` to reuse users' `ThemeManager`
|
||||
|
||||
#### Additions
|
||||
|
||||
- `WaylandSource::queue` to access the `EventQueue` underlying a `WaylandSource`
|
||||
- A window menu could be shown on right click on decorations for `ConceptFrame`
|
||||
- `ConceptFrame` will no longer change cursor over base surface if `ThemeManager` was provided
|
||||
|
||||
#### Changes
|
||||
|
||||
- `Window::set_title` now truncates the provided string to 1024 bytes, to avoid blowing up
|
||||
the Wayland connection
|
||||
- `ConceptFrame` is now hiding decorations for `State::Fullscreen`
|
||||
- Restore original size of fullscreened window on unfullscreen
|
||||
- Explicitly setting `ClientSide` decorations will result in `ServerSide` ones being destroyed
|
||||
- Requesting `ServerSide` decorations in `set_decorate` will now fallback to `ClientSide`
|
||||
if the former are not available
|
||||
- Requesting `None` decorations if `ServerSide` are presented will result in setting
|
||||
`ClientSide` decorations with hidden frame
|
||||
- `ConceptFrame` will use `Disabled` style for maximized button for non-resizeable frame
|
||||
- `ConceptFrame` will create subsurfaces for client side decorations only if a frame is visible
|
||||
- `Window` will restore original size after being tiled
|
||||
|
||||
#### Bugfixes
|
||||
|
||||
- Toggling between `ServerSide` and `None` decorations raising protocol error
|
||||
- Precision in a rate of key repeat events
|
||||
- `ThemeManager` not being clone-able even if it was stated in docs
|
||||
- Repeat rate not being disabled when receiving zero for `rate` in `wl_keyboard.repeat_info`
|
||||
|
||||
## 0.10.0 -- 2020-07-10
|
||||
|
||||
#### Breaking Changes
|
||||
|
||||
- `create_surface` and `create_surface_with_scale_callback` now return `Attached<WlSurface>`
|
||||
- Update `wayland-client` to `0.27`
|
||||
|
||||
#### Changes
|
||||
|
||||
- `andrew` is updated to `0.3`.
|
||||
|
||||
#### Bugfixes
|
||||
|
||||
- seat: Seats with an empty name are no longer filtered out
|
||||
|
||||
## 0.9.1 -- 2020-05-03
|
||||
|
||||
#### Additions
|
||||
|
||||
- keyboard: Update the keysyms list with new symbols
|
||||
- Add primary selection helpers, which are included as part of default `Environment`.
|
||||
|
||||
#### Changes
|
||||
|
||||
- surfaces: dpi-aware surface will no longer believe their DPI factor reverts to 1 when they
|
||||
become hidden.
|
||||
|
||||
#### BugFixes
|
||||
|
||||
- keyboard: Remove the unnecessary type parameter of `map_keyboard`
|
||||
|
||||
## 0.9.0 -- 2020-04-22
|
||||
|
||||
#### Breaking Changes
|
||||
|
||||
- `AutoThemer` is removed as it is no longer necessary with `wayland-cursor` 0.26
|
||||
- `calloop` is updated to 0.6, and the adapters are modified in consequence
|
||||
|
||||
#### Additions
|
||||
|
||||
- Add `clone_seat_data()` method as a shorthand to get `SeatData`
|
||||
|
||||
#### Bugfixes
|
||||
|
||||
- Surface lock held across scale factor callback deadlocks scale factor API.
|
||||
|
||||
## 0.8.1 -- 2020-04-09
|
||||
|
||||
#### Additions
|
||||
|
||||
- Add `listen_for_outputs()` which calls a provided callback on creation/removal of outputs.
|
||||
- Add an `OutputHandling` trait making `listen_for_outputs()` available on `Environment`.
|
||||
- Introduce the `calloop` cargo feature, enabled by default, controlling the support for the calloop event
|
||||
loop
|
||||
- Introduce the `frames` cargo feature, enabled by default, controlling the existence of provided `Frame`
|
||||
implementations (currently `ConceptFrame`) and the dependency on `andrew`
|
||||
|
||||
## 0.8.0 -- 2020-02-27
|
||||
|
||||
#### Breaking Changes
|
||||
|
||||
- `Frame` configuration is now done through a `Frame::Config` associated type and the `Theme` trait is removed.
|
||||
- Merge `Frame::set_active` and `Frame::set_maximized` into `Frame::set_states`
|
||||
|
||||
#### Additions
|
||||
|
||||
- HiDPI scaling for decorations
|
||||
|
||||
#### Bugfixes
|
||||
|
||||
- HiDPI cursor icon position
|
||||
- Fix graphical glitches in `ConceptFrame` decoration drawing
|
||||
- Black pixel on left-bottom corner on CSD
|
||||
- Remove a deadlock when trying to access the seat data from within the seat callback
|
||||
|
||||
## 0.7.0 -- 2020-02-07
|
||||
|
||||
#### Breaking changes
|
||||
|
||||
- Upgrade to `wayland-client` 0.25. This changes the prototype of most callbacks by
|
||||
adding the `DispatchData` mechanism for state sharing
|
||||
- Re-structure the lib API around the new `Environment` type as an entry point (breaks a lot of things).
|
||||
This makes the crate follow a monolithic-modular structure centered on this type.
|
||||
- `keyboard` is now a submodule of `seat`
|
||||
- `keyboard` key repetition is now handled as a calloop event source
|
||||
- `pointer` is now a submodule of `seat`
|
||||
- The initialization of `pointer` theming utilities now require a `ThemeSpec` argument
|
||||
instead of just a theme name, allowing control over the size of the cursors as well
|
||||
- Pointer theming utilities can no longer be shared across threads, as it was racy.
|
||||
- `Window` now tracks new seats automatically (the `new_seat` method is removed)
|
||||
- `Window` can no longer be shared across threads, as it was racy.
|
||||
- Decorations management is now handled with the `Decorations` enum, for full control to clients.
|
||||
|
||||
#### Additions
|
||||
|
||||
- The `pointer` theming will now read the `XCURSOR_THEME` and `XCURSOR_SIZE` environment
|
||||
variables to figure the default theme
|
||||
- `pointer` theming utilities now handle HiDPI monitors
|
||||
- SCTK now uses the `log` crate to log its warning and error messages
|
||||
- Data offers `ReadPipe`scan be inserted in a calloop event loop as an event source
|
||||
- The `WaylandSource` wrapper allows a `wayland-client` `EventQueue` to be inserted into
|
||||
a calloop event source.
|
||||
|
||||
## 0.6.4 -- 2019-08-27
|
||||
|
||||
#### Bugfixes
|
||||
|
||||
- Keyboard input breaking when `LC_ALL`, `LC_CTYPE` or `LANG` are set to an empty string
|
||||
- UTF8 interpretation no longer stops working if loading the compose table failed
|
||||
|
||||
## 0.6.3 -- 2019-06-29
|
||||
|
||||
- Keyboard: fix extra key repeat when using also releasing a modifier
|
||||
|
||||
## 0.6.2 -- 2019-06-13
|
||||
|
||||
- Update `Nix` to 0.14
|
||||
|
||||
## 0.6.1 -- 2019-04-07
|
||||
|
||||
- Additional theming capability on `ConceptFrame` via the `Theme` trait:
|
||||
optional methods `get_<button-name>_button_icon_color` allows the stroke
|
||||
color on the buttons to be customized beyond what the secondary color allows.
|
||||
Button color methods now affect the `ConceptFrame`'s fill behind the buttons.
|
||||
- Fix the firing of `Configure` events in window abstraction.
|
||||
|
||||
## 0.6.0 -- 2019-02-18
|
||||
|
||||
#### Breaking changes
|
||||
|
||||
- Upgrade to `wayland-client` version 0.23
|
||||
|
||||
## 0.5.0 -- 2019-02-05
|
||||
|
||||
#### Breaking changes
|
||||
|
||||
- Update the crate to `wayland-client` version 0.22
|
||||
- Window: `set_title()` now requires a manual `refresh()` for the change to take effect
|
||||
|
||||
#### Bugfixes
|
||||
|
||||
- Keyboard: fix system repeat rate as repeats per second rather then millisecond delay between repeats
|
||||
- Surface: fix panic in `compute_dpi_factor()` by only computing the dpi factor on surfaces known to the OutputMgr
|
||||
|
||||
## 0.4.4 -- 2018-12-27
|
||||
|
||||
- Shell: expose shell interface and add `create_shell_surface` to `Environment`.
|
||||
- Fix build failure on big endian targets
|
||||
|
||||
## 0.4.3 -- 2018-12-03
|
||||
|
||||
- Update dependencies: rand, memmap, nix and image
|
||||
- Surface: `create_surface` and `get_dpi_factor` utilities for creating dpi aware surfaces.
|
||||
|
||||
## 0.4.2 -- 2018-11-14
|
||||
|
||||
- Fix compilation on BSD systems
|
||||
|
||||
## 0.4.1 -- 2018-11-06
|
||||
|
||||
- Window: always request server-side decorations if available, otherwise ther compositor never configures us
|
||||
- keyboard: only compute utf8 value on keypress, not key release. Otherwise it confuses `xkb_compose`.
|
||||
|
||||
## 0.4.0 -- 2018-10-09
|
||||
|
||||
- BasicFrame: Display the title of the window in the window header
|
||||
- Pass `set_selection()` `Option<DataSource>` and `AutoThemer::init()` `Proxy<WlShm>` by reference
|
||||
- Window: add `set_theme()` function which takes an object implementing the trait `Theme` to adjust the look of window decorations
|
||||
- Window: add new `ConceptFrame` which provides an alternative to the `BasicFrame` window decorations
|
||||
- MemPool: add `mmap` method
|
||||
- **[Breaking]** Keyboard: remove `modifiers` field from `keyboard::Event::Enter`, `keyboard::Event::Key` and `keyboard::KeyRepeatEvent`
|
||||
- **[Breaking]** Keyboard: add `keyboard::Event::Modifiers`
|
||||
- **[Breaking]** Upgrade to wayland-rs 0.21
|
||||
- Keyboard: end key repetition when the keyboard loses focus
|
||||
|
||||
## 0.3.0 -- 2018-08-17
|
||||
|
||||
- Window: the minimum window width is set to 2 pixels to circumvent a bug in mutter - https://gitlab.gnome.org/GNOME/mutter/issues/259
|
||||
- **[Breaking]** MemPool: MemPool now requires an implementation to be called when the pool becomes free
|
||||
- **[Breaking]** DoubleMemPool: DoubleMemPool now requires an implementation to be called when one of its pools becomes free
|
||||
- **[Breaking]** DoubleMemPool: `swap()` is removed as `pool()` will now automatically track and return any free pools avaliable or return None
|
||||
- Keyboard: add key repetition with 'map_keyboard_auto_with_repeat' and 'map_keyboard_rmlvo_with_repeat'
|
||||
- Window: add `init_with_decorations` to allow the use of server-side decorations
|
||||
|
||||
## 0.2.6 -- 2018-07-14
|
||||
|
||||
Big thanks to @trimental for improving the visual look of the window decorations:
|
||||
|
||||
- BasicFrame: remove side and bottom border decorations
|
||||
- BasicFrame: round window corners
|
||||
|
||||
## 0.2.5 -- 2018-07-10
|
||||
|
||||
- Keyboard: try to load `libxkbcommon.so.0` as well to improve compatibility
|
||||
|
||||
## 0.2.4 -- 2018-06-26
|
||||
|
||||
- Window: notify the compositor of our dimensions to avoid placement glitches
|
||||
|
||||
## 0.2.3 -- 2018-06-08
|
||||
|
||||
- Update `nix` dependency to be fix build on FreeBSD (even if we can't run)
|
||||
|
||||
## 0.2.2 -- 2018-06-08
|
||||
|
||||
- BasicFrame: don't desync the subsurface from the main one. This avoids
|
||||
graphical glitches where the borders are not drawn exactly the same size
|
||||
as the contents.
|
||||
- Window: add `set_resizable`, (minor **breaking change** of the `Frame` trait by
|
||||
adding a new method)
|
||||
|
||||
## 0.2.1 -- 2018-05-03
|
||||
|
||||
- Add `DoubleMemPool` for double buffering, and use it to
|
||||
improve the drawing performance of `BasicFrame`.
|
||||
|
||||
## 0.2.0 -- 2018-04-29
|
||||
|
||||
- *Breaking* OutputMgr: expose wl_output global id
|
||||
|
||||
## 0.1.0 -- 2018-04-26
|
||||
|
||||
Initial version, including:
|
||||
|
||||
- basic environment manager
|
||||
- keyboard keymap handling
|
||||
- basic window decoration
|
||||
42
third-party/vendor/smithay-client-toolkit/CONTRIBUTING.md
vendored
Normal file
42
third-party/vendor/smithay-client-toolkit/CONTRIBUTING.md
vendored
Normal file
|
|
@ -0,0 +1,42 @@
|
|||
# Contributing
|
||||
|
||||
Smithay's Client ToolKit (SCTK) is open to contributions from anyone.
|
||||
|
||||
## Coordination
|
||||
|
||||
Most discussion about features and their implementations takes place on github.
|
||||
If you have questions, suggestions, ideas, you can open an issue to discuss it, or add your message
|
||||
in an already existing issue if it fits its scope.
|
||||
|
||||
If you want a more realtime discussion there is a a Matrix room dedicated to the Smithay project:
|
||||
[#smithay:matrix.org](https://matrix.to/#/#smithay:matrix.org). If you don't want to use matrix, this room is
|
||||
also bridged to gitter: https://gitter.im/smithay/Lobby.
|
||||
|
||||
## Scope & Structure
|
||||
|
||||
SCTK aims to provide generic building blocks to write wayland clients, abstracting away the boilerplate of the
|
||||
wayland protocol while allowing direct control when wanted. As such, it is composed of several loosely-coupled
|
||||
modules, which can be used independenly of each other. This given, if you want to contribute a new feature to
|
||||
SCTK, please consider these design points:
|
||||
|
||||
- The feature should be designed it is most general form, allowing it to be used by other projects, probaby
|
||||
different from the exact use-case you have in mind.
|
||||
- This new feature should not heavily depend on the other parts of SCTK if it can avoid it. As much as
|
||||
possible, SCTK users should be able to use your feature alone.
|
||||
|
||||
## Pull requests & commits organisation
|
||||
|
||||
The development branch is the `master` branch, and it should be the target of your pull requests.
|
||||
|
||||
In general, single-purpose pull requests are prefered. If you have two independent contributions to make,
|
||||
please open two different pull requests.
|
||||
|
||||
On the other hand, if you have changes that could technically be separated, but really belong together (for
|
||||
example a new feature, that first require some refactoring before being introduced), it is okay to ship them
|
||||
in the same pull request. However, to simplify the review work (and future reference to the commit history),
|
||||
these changes should be separated in different commits. This will allow the reviewers to review each commit
|
||||
independently, reducing the cognitive load.
|
||||
|
||||
At merge time, pull requests consisting of a single commit or of a few well-scoped commits will be rebased on
|
||||
master. Pull requests which have accumulated several review-addressing commits will be squashed.
|
||||
|
||||
731
third-party/vendor/smithay-client-toolkit/Cargo.lock
generated
vendored
Normal file
731
third-party/vendor/smithay-client-toolkit/Cargo.lock
generated
vendored
Normal file
|
|
@ -0,0 +1,731 @@
|
|||
# This file is automatically @generated by Cargo.
|
||||
# It is not intended for manual editing.
|
||||
version = 3
|
||||
|
||||
[[package]]
|
||||
name = "adler"
|
||||
version = "1.0.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe"
|
||||
|
||||
[[package]]
|
||||
name = "autocfg"
|
||||
version = "1.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d468802bab17cbc0cc575e9b053f41e72aa36bfa6b7f55e3529ffa43161b97fa"
|
||||
|
||||
[[package]]
|
||||
name = "bit_field"
|
||||
version = "0.10.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "dc827186963e592360843fb5ba4b973e145841266c1357f7180c43526f2e5b61"
|
||||
|
||||
[[package]]
|
||||
name = "bitflags"
|
||||
version = "1.3.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a"
|
||||
|
||||
[[package]]
|
||||
name = "bytemuck"
|
||||
version = "1.13.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "17febce684fd15d89027105661fec94afb475cb995fbc59d2865198446ba2eea"
|
||||
|
||||
[[package]]
|
||||
name = "byteorder"
|
||||
version = "1.4.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610"
|
||||
|
||||
[[package]]
|
||||
name = "calloop"
|
||||
version = "0.10.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1a59225be45a478d772ce015d9743e49e92798ece9e34eda9a6aa2a6a7f40192"
|
||||
dependencies = [
|
||||
"log",
|
||||
"nix 0.25.1",
|
||||
"slotmap",
|
||||
"thiserror",
|
||||
"vec_map",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cfg-if"
|
||||
version = "1.0.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
|
||||
|
||||
[[package]]
|
||||
name = "color_quant"
|
||||
version = "1.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3d7b894f5411737b7867f4827955924d7c254fc9f4d91a6aad6b097804b1018b"
|
||||
|
||||
[[package]]
|
||||
name = "crc32fast"
|
||||
version = "1.3.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b540bd8bc810d3885c6ea91e2018302f68baba2129ab3e88f32389ee9370880d"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "crossbeam-channel"
|
||||
version = "0.5.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "cf2b3e8478797446514c91ef04bafcb59faba183e621ad488df88983cc14128c"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"crossbeam-utils",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "crossbeam-deque"
|
||||
version = "0.8.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ce6fd6f855243022dcecf8702fef0c297d4338e226845fe067f6341ad9fa0cef"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"crossbeam-epoch",
|
||||
"crossbeam-utils",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "crossbeam-epoch"
|
||||
version = "0.9.14"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "46bd5f3f85273295a9d14aedfb86f6aadbff6d8f5295c4a9edb08e819dcf5695"
|
||||
dependencies = [
|
||||
"autocfg",
|
||||
"cfg-if",
|
||||
"crossbeam-utils",
|
||||
"memoffset 0.8.0",
|
||||
"scopeguard",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "crossbeam-utils"
|
||||
version = "0.8.15"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3c063cd8cc95f5c377ed0d4b49a4b21f632396ff690e8470c29b3359b346984b"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "crunchy"
|
||||
version = "0.2.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7a81dae078cea95a014a339291cec439d2f232ebe854a9d672b796c6afafa9b7"
|
||||
|
||||
[[package]]
|
||||
name = "dlib"
|
||||
version = "0.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ac1b7517328c04c2aa68422fc60a41b92208182142ed04a25879c26c8f878794"
|
||||
dependencies = [
|
||||
"libloading",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "downcast-rs"
|
||||
version = "1.2.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9ea835d29036a4087793836fa931b08837ad5e957da9e23886b29586fb9b6650"
|
||||
|
||||
[[package]]
|
||||
name = "either"
|
||||
version = "1.8.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7fcaabb2fef8c910e7f4c7ce9f67a1283a1715879a7c230ca9d6d1ae31f16d91"
|
||||
|
||||
[[package]]
|
||||
name = "exr"
|
||||
version = "1.71.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "832a761f35ab3e6664babfbdc6cef35a4860e816ec3916dcfd0882954e98a8a8"
|
||||
dependencies = [
|
||||
"bit_field",
|
||||
"flume",
|
||||
"half",
|
||||
"lebe",
|
||||
"miniz_oxide 0.7.1",
|
||||
"rayon-core",
|
||||
"smallvec",
|
||||
"zune-inflate",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "flate2"
|
||||
version = "1.0.25"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a8a2db397cb1c8772f31494cb8917e48cd1e64f0fa7efac59fbd741a0a8ce841"
|
||||
dependencies = [
|
||||
"crc32fast",
|
||||
"miniz_oxide 0.6.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "flume"
|
||||
version = "0.11.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "55ac459de2512911e4b674ce33cf20befaba382d05b62b008afc1c8b57cbf181"
|
||||
dependencies = [
|
||||
"spin",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "gif"
|
||||
version = "0.12.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "80792593675e051cf94a4b111980da2ba60d4a83e43e0048c5693baab3977045"
|
||||
dependencies = [
|
||||
"color_quant",
|
||||
"weezl",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "half"
|
||||
version = "2.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "02b4af3693f1b705df946e9fe5631932443781d0aabb423b62fcd4d73f6d2fd0"
|
||||
dependencies = [
|
||||
"crunchy",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "hermit-abi"
|
||||
version = "0.2.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ee512640fe35acbfb4bb779db6f0d80704c2cacfa2e39b601ef3e3f47d1ae4c7"
|
||||
dependencies = [
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "image"
|
||||
version = "0.24.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6f3dfdbdd72063086ff443e297b61695500514b1e41095b6fb9a5ab48a70a711"
|
||||
dependencies = [
|
||||
"bytemuck",
|
||||
"byteorder",
|
||||
"color_quant",
|
||||
"exr",
|
||||
"gif",
|
||||
"jpeg-decoder",
|
||||
"num-rational",
|
||||
"num-traits",
|
||||
"png",
|
||||
"qoi",
|
||||
"tiff",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "jpeg-decoder"
|
||||
version = "0.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bc0000e42512c92e31c2252315bda326620a4e034105e900c98ec492fa077b3e"
|
||||
dependencies = [
|
||||
"rayon",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "lazy_static"
|
||||
version = "1.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646"
|
||||
|
||||
[[package]]
|
||||
name = "lebe"
|
||||
version = "0.5.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "03087c2bad5e1034e8cace5926dec053fb3790248370865f5117a7d0213354c8"
|
||||
|
||||
[[package]]
|
||||
name = "libc"
|
||||
version = "0.2.140"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "99227334921fae1a979cf0bfdfcc6b3e5ce376ef57e16fb6fb3ea2ed6095f80c"
|
||||
|
||||
[[package]]
|
||||
name = "libloading"
|
||||
version = "0.7.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b67380fd3b2fbe7527a606e18729d21c6f3951633d0500574c4dc22d2d638b9f"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
"winapi",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "lock_api"
|
||||
version = "0.4.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "435011366fe56583b16cf956f9df0095b405b82d76425bc8981c0e22e60ec4df"
|
||||
dependencies = [
|
||||
"autocfg",
|
||||
"scopeguard",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "log"
|
||||
version = "0.4.17"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "abb12e687cfb44aa40f41fc3978ef76448f9b6038cad6aef4259d3c095a2382e"
|
||||
dependencies = [
|
||||
"cfg-if",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "memchr"
|
||||
version = "2.5.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d"
|
||||
|
||||
[[package]]
|
||||
name = "memmap2"
|
||||
version = "0.5.10"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "83faa42c0a078c393f6b29d5db232d8be22776a891f8f56e5284faee4a20b327"
|
||||
dependencies = [
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "memoffset"
|
||||
version = "0.6.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5aa361d4faea93603064a027415f07bd8e1d5c88c9fbf68bf56a285428fd79ce"
|
||||
dependencies = [
|
||||
"autocfg",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "memoffset"
|
||||
version = "0.8.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d61c719bcfbcf5d62b3a09efa6088de8c54bc0bfcd3ea7ae39fcc186108b8de1"
|
||||
dependencies = [
|
||||
"autocfg",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "minimal-lexical"
|
||||
version = "0.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "68354c5c6bd36d73ff3feceb05efa59b6acb7626617f4962be322a825e61f79a"
|
||||
|
||||
[[package]]
|
||||
name = "miniz_oxide"
|
||||
version = "0.6.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b275950c28b37e794e8c55d88aeb5e139d0ce23fdbbeda68f8d7174abdf9e8fa"
|
||||
dependencies = [
|
||||
"adler",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "miniz_oxide"
|
||||
version = "0.7.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e7810e0be55b428ada41041c41f32c9f1a42817901b4ccf45fa3d4b6561e74c7"
|
||||
dependencies = [
|
||||
"adler",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "nix"
|
||||
version = "0.24.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "fa52e972a9a719cecb6864fb88568781eb706bac2cd1d4f04a648542dbf78069"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"cfg-if",
|
||||
"libc",
|
||||
"memoffset 0.6.5",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "nix"
|
||||
version = "0.25.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f346ff70e7dbfd675fe90590b92d59ef2de15a8779ae305ebcbfd3f0caf59be4"
|
||||
dependencies = [
|
||||
"autocfg",
|
||||
"bitflags",
|
||||
"cfg-if",
|
||||
"libc",
|
||||
"memoffset 0.6.5",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "nom"
|
||||
version = "7.1.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d273983c5a657a70a3e8f2a01329822f3b8c8172b73826411a55751e404a0a4a"
|
||||
dependencies = [
|
||||
"memchr",
|
||||
"minimal-lexical",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "num-integer"
|
||||
version = "0.1.45"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "225d3389fb3509a24c93f5c29eb6bde2586b98d9f016636dff58d7c6f7569cd9"
|
||||
dependencies = [
|
||||
"autocfg",
|
||||
"num-traits",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "num-rational"
|
||||
version = "0.4.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0638a1c9d0a3c0914158145bc76cff373a75a627e6ecbfb71cbe6f453a5a19b0"
|
||||
dependencies = [
|
||||
"autocfg",
|
||||
"num-integer",
|
||||
"num-traits",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "num-traits"
|
||||
version = "0.2.15"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "578ede34cf02f8924ab9447f50c28075b4d3e5b269972345e7e0372b38c6cdcd"
|
||||
dependencies = [
|
||||
"autocfg",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "num_cpus"
|
||||
version = "1.15.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "0fac9e2da13b5eb447a6ce3d392f23a29d8694bff781bf03a16cd9ac8697593b"
|
||||
dependencies = [
|
||||
"hermit-abi",
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "once_cell"
|
||||
version = "1.17.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b7e5500299e16ebb147ae15a00a942af264cf3688f47923b8fc2cd5858f23ad3"
|
||||
|
||||
[[package]]
|
||||
name = "pkg-config"
|
||||
version = "0.3.26"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6ac9a59f73473f1b8d852421e59e64809f025994837ef743615c6d0c5b305160"
|
||||
|
||||
[[package]]
|
||||
name = "png"
|
||||
version = "0.17.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5d708eaf860a19b19ce538740d2b4bdeeb8337fa53f7738455e706623ad5c638"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"crc32fast",
|
||||
"flate2",
|
||||
"miniz_oxide 0.6.2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "proc-macro2"
|
||||
version = "1.0.53"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ba466839c78239c09faf015484e5cc04860f88242cff4d03eb038f04b4699b73"
|
||||
dependencies = [
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "qoi"
|
||||
version = "0.4.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "7f6d64c71eb498fe9eae14ce4ec935c555749aef511cca85b5568910d6e48001"
|
||||
dependencies = [
|
||||
"bytemuck",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "quote"
|
||||
version = "1.0.26"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4424af4bf778aae2051a77b60283332f386554255d722233d09fbfc7e30da2fc"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rayon"
|
||||
version = "1.7.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "1d2df5196e37bcc87abebc0053e20787d73847bb33134a69841207dd0a47f03b"
|
||||
dependencies = [
|
||||
"either",
|
||||
"rayon-core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "rayon-core"
|
||||
version = "1.11.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "4b8f95bd6966f5c87776639160a66bd8ab9895d9d4ab01ddba9fc60661aebe8d"
|
||||
dependencies = [
|
||||
"crossbeam-channel",
|
||||
"crossbeam-deque",
|
||||
"crossbeam-utils",
|
||||
"num_cpus",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "scoped-tls"
|
||||
version = "1.0.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e1cf6437eb19a8f4a6cc0f7dca544973b0b78843adbfeb3683d1a94a0024a294"
|
||||
|
||||
[[package]]
|
||||
name = "scopeguard"
|
||||
version = "1.1.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d29ab0c6d3fc0ee92fe66e2d99f700eab17a8d57d1c1d3b748380fb20baa78cd"
|
||||
|
||||
[[package]]
|
||||
name = "simd-adler32"
|
||||
version = "0.3.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "d66dc143e6b11c1eddc06d5c423cfc97062865baf299914ab64caa38182078fe"
|
||||
|
||||
[[package]]
|
||||
name = "slotmap"
|
||||
version = "1.0.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e1e08e261d0e8f5c43123b7adf3e4ca1690d655377ac93a03b2c9d3e98de1342"
|
||||
dependencies = [
|
||||
"version_check",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "smallvec"
|
||||
version = "1.10.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a507befe795404456341dfab10cef66ead4c041f62b8b11bbb92bffe5d0953e0"
|
||||
|
||||
[[package]]
|
||||
name = "smithay-client-toolkit"
|
||||
version = "0.16.1"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"calloop",
|
||||
"dlib",
|
||||
"image",
|
||||
"lazy_static",
|
||||
"log",
|
||||
"memmap2",
|
||||
"nix 0.24.3",
|
||||
"pkg-config",
|
||||
"wayland-client",
|
||||
"wayland-cursor",
|
||||
"wayland-protocols",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "spin"
|
||||
version = "0.9.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6980e8d7511241f8acf4aebddbb1ff938df5eebe98691418c4468d0b72a96a67"
|
||||
dependencies = [
|
||||
"lock_api",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "syn"
|
||||
version = "2.0.6"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ece519cfaf36269ea69d16c363fa1d59ceba8296bbfbfc003c3176d01f2816ee"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"unicode-ident",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "thiserror"
|
||||
version = "1.0.40"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "978c9a314bd8dc99be594bc3c175faaa9794be04a5a5e153caba6915336cebac"
|
||||
dependencies = [
|
||||
"thiserror-impl",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "thiserror-impl"
|
||||
version = "1.0.40"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f9456a42c5b0d803c8cd86e73dd7cc9edd429499f37a3550d286d5e86720569f"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"syn",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "tiff"
|
||||
version = "0.9.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6d172b0f4d3fba17ba89811858b9d3d97f928aece846475bbda076ca46736211"
|
||||
dependencies = [
|
||||
"flate2",
|
||||
"jpeg-decoder",
|
||||
"weezl",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "unicode-ident"
|
||||
version = "1.0.8"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "e5464a87b239f13a63a501f2701565754bae92d243d4bb7eb12f6d57d2269bf4"
|
||||
|
||||
[[package]]
|
||||
name = "vec_map"
|
||||
version = "0.8.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "f1bddf1187be692e79c5ffeab891132dfb0f236ed36a43c7ed39f1165ee20191"
|
||||
|
||||
[[package]]
|
||||
name = "version_check"
|
||||
version = "0.9.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "49874b5167b65d7193b8aba1567f5c7d93d001cafc34600cee003eda787e483f"
|
||||
|
||||
[[package]]
|
||||
name = "wayland-client"
|
||||
version = "0.29.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "3f3b068c05a039c9f755f881dc50f01732214f5685e379829759088967c46715"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"downcast-rs",
|
||||
"libc",
|
||||
"nix 0.24.3",
|
||||
"scoped-tls",
|
||||
"wayland-commons",
|
||||
"wayland-scanner",
|
||||
"wayland-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wayland-commons"
|
||||
version = "0.29.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8691f134d584a33a6606d9d717b95c4fa20065605f798a3f350d78dced02a902"
|
||||
dependencies = [
|
||||
"nix 0.24.3",
|
||||
"once_cell",
|
||||
"smallvec",
|
||||
"wayland-sys",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wayland-cursor"
|
||||
version = "0.29.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6865c6b66f13d6257bef1cd40cbfe8ef2f150fb8ebbdb1e8e873455931377661"
|
||||
dependencies = [
|
||||
"nix 0.24.3",
|
||||
"wayland-client",
|
||||
"xcursor",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wayland-protocols"
|
||||
version = "0.29.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "b950621f9354b322ee817a23474e479b34be96c2e909c14f7bc0100e9a970bc6"
|
||||
dependencies = [
|
||||
"bitflags",
|
||||
"wayland-client",
|
||||
"wayland-commons",
|
||||
"wayland-scanner",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wayland-scanner"
|
||||
version = "0.29.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "8f4303d8fa22ab852f789e75a967f0a2cdc430a607751c0499bada3e451cbd53"
|
||||
dependencies = [
|
||||
"proc-macro2",
|
||||
"quote",
|
||||
"xml-rs",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "wayland-sys"
|
||||
version = "0.29.5"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "be12ce1a3c39ec7dba25594b97b42cb3195d54953ddb9d3d95a7c3902bc6e9d4"
|
||||
dependencies = [
|
||||
"dlib",
|
||||
"lazy_static",
|
||||
"pkg-config",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "weezl"
|
||||
version = "0.1.7"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "9193164d4de03a926d909d3bc7c30543cecb35400c02114792c2cae20d5e2dbb"
|
||||
|
||||
[[package]]
|
||||
name = "winapi"
|
||||
version = "0.3.9"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419"
|
||||
dependencies = [
|
||||
"winapi-i686-pc-windows-gnu",
|
||||
"winapi-x86_64-pc-windows-gnu",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "winapi-i686-pc-windows-gnu"
|
||||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
|
||||
|
||||
[[package]]
|
||||
name = "winapi-x86_64-pc-windows-gnu"
|
||||
version = "0.4.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
|
||||
|
||||
[[package]]
|
||||
name = "xcursor"
|
||||
version = "0.3.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "463705a63313cd4301184381c5e8042f0a7e9b4bb63653f216311d4ae74690b7"
|
||||
dependencies = [
|
||||
"nom",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "xml-rs"
|
||||
version = "0.8.18"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "bab77e97b50aee93da431f2cee7cd0f43b4d1da3c408042f2d7d164187774f0a"
|
||||
|
||||
[[package]]
|
||||
name = "zune-inflate"
|
||||
version = "0.2.54"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "73ab332fe2f6680068f3582b16a24f90ad7096d5d39b974d1c0aff0125116f02"
|
||||
dependencies = [
|
||||
"simd-adler32",
|
||||
]
|
||||
79
third-party/vendor/smithay-client-toolkit/Cargo.toml
vendored
Normal file
79
third-party/vendor/smithay-client-toolkit/Cargo.toml
vendored
Normal file
|
|
@ -0,0 +1,79 @@
|
|||
# THIS FILE IS AUTOMATICALLY GENERATED BY CARGO
|
||||
#
|
||||
# When uploading crates to the registry Cargo will automatically
|
||||
# "normalize" Cargo.toml files for maximal compatibility
|
||||
# with all versions of Cargo and also rewrite `path` dependencies
|
||||
# to registry (e.g., crates.io) dependencies.
|
||||
#
|
||||
# If you are reading this file be aware that the original Cargo.toml
|
||||
# will likely look very different (and much more reasonable).
|
||||
# See Cargo.toml.orig for the original contents.
|
||||
|
||||
[package]
|
||||
edition = "2018"
|
||||
name = "smithay-client-toolkit"
|
||||
version = "0.16.1"
|
||||
authors = ["Victor Berger <victor.berger@m4x.org>"]
|
||||
description = "Toolkit for making client wayland applications."
|
||||
documentation = "https://smithay.github.io/client-toolkit"
|
||||
readme = "README.md"
|
||||
keywords = [
|
||||
"wayland",
|
||||
"client",
|
||||
]
|
||||
categories = ["gui"]
|
||||
license = "MIT"
|
||||
repository = "https://github.com/smithay/client-toolkit"
|
||||
|
||||
[dependencies.bitflags]
|
||||
version = "1.0"
|
||||
|
||||
[dependencies.calloop]
|
||||
version = "0.10"
|
||||
optional = true
|
||||
|
||||
[dependencies.dlib]
|
||||
version = "0.5"
|
||||
|
||||
[dependencies.lazy_static]
|
||||
version = "1.0"
|
||||
|
||||
[dependencies.log]
|
||||
version = "0.4"
|
||||
|
||||
[dependencies.memmap2]
|
||||
version = "0.5.0"
|
||||
|
||||
[dependencies.nix]
|
||||
version = "0.24"
|
||||
features = [
|
||||
"mman",
|
||||
"fs",
|
||||
]
|
||||
default-features = false
|
||||
|
||||
[dependencies.wayland-client]
|
||||
version = "0.29"
|
||||
|
||||
[dependencies.wayland-cursor]
|
||||
version = "0.29"
|
||||
|
||||
[dependencies.wayland-protocols]
|
||||
version = "0.29"
|
||||
features = [
|
||||
"client",
|
||||
"unstable_protocols",
|
||||
]
|
||||
|
||||
[dev-dependencies.image]
|
||||
version = "0.24"
|
||||
|
||||
[build-dependencies.pkg-config]
|
||||
version = "0.3"
|
||||
|
||||
[features]
|
||||
default = [
|
||||
"calloop",
|
||||
"dlopen",
|
||||
]
|
||||
dlopen = ["wayland-client/dlopen"]
|
||||
19
third-party/vendor/smithay-client-toolkit/LICENSE.txt
vendored
Normal file
19
third-party/vendor/smithay-client-toolkit/LICENSE.txt
vendored
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
Copyright (c) 2018 Victor Berger
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
||||
26
third-party/vendor/smithay-client-toolkit/README.md
vendored
Normal file
26
third-party/vendor/smithay-client-toolkit/README.md
vendored
Normal file
|
|
@ -0,0 +1,26 @@
|
|||
[](https://crates.io/crates/smithay-client-toolkit)
|
||||
[](https://docs.rs/smithay-client-toolkit)
|
||||
[](https://github.com/Smithay/client-toolkit/actions?query=workflow%3A%22Continuous+Integration%22)
|
||||
|
||||
# Smithay's Client Toolkit
|
||||
|
||||
This crate is a toolkit for writing wayland clients in rust, on top of [wayland-client](https://crates.io/crates/wayland-client).
|
||||
|
||||
Currently a work in progress, it currently provides the following utilities:
|
||||
|
||||
- Automatic binding of general wayland globals (`wl_compositor`, `wl_shm`, etc..)
|
||||
- Abstraction to create windows (aka toplevel surfaces), abstracting the interaction
|
||||
with the shell (`xdg_shell` or `wl_shell`) and the drawing of decorations
|
||||
- Wrapper for `wl_keyboard` for automatic keymap interpretation using `libxkbcommon.so`.
|
||||
- Utilites for creating dpi aware surfaces.
|
||||
|
||||
## Documentation
|
||||
|
||||
The documentation for the master branch is [available online](https://smithay.github.io/client-toolkit/).
|
||||
|
||||
The documentation for the releases can be found on [docs.rs](https://docs.rs/smithay-client-toolkit).
|
||||
|
||||
## Requirements
|
||||
|
||||
Requires at least rust 1.61 to be used and version 1.12 of the wayland system
|
||||
libraries.
|
||||
6
third-party/vendor/smithay-client-toolkit/build.rs
vendored
Normal file
6
third-party/vendor/smithay-client-toolkit/build.rs
vendored
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
extern crate pkg_config;
|
||||
|
||||
fn main() {
|
||||
#[cfg(not(feature = "dlopen"))]
|
||||
pkg_config::Config::new().find("xkbcommon").unwrap();
|
||||
}
|
||||
6
third-party/vendor/smithay-client-toolkit/doc_index.html
vendored
Normal file
6
third-party/vendor/smithay-client-toolkit/doc_index.html
vendored
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta http-equiv=refresh content=0;url=smithay_client_toolkit/index.html />
|
||||
</head>
|
||||
</html>
|
||||
80
third-party/vendor/smithay-client-toolkit/examples/compositor_info.rs
vendored
Normal file
80
third-party/vendor/smithay-client-toolkit/examples/compositor_info.rs
vendored
Normal file
|
|
@ -0,0 +1,80 @@
|
|||
extern crate smithay_client_toolkit as sctk;
|
||||
|
||||
use sctk::shell::Shell;
|
||||
|
||||
// This is a small program that queries the compositor for
|
||||
// various information and prints them on the console before exiting.
|
||||
|
||||
sctk::default_environment!(CompInfo, desktop);
|
||||
|
||||
fn main() -> Result<(), ()> {
|
||||
let (env, _display, _queue) = sctk::new_default_environment!(CompInfo, desktop)
|
||||
.expect("Unable to connect to a Wayland compositor");
|
||||
|
||||
println!("== Smithay's compositor info tool ==\n");
|
||||
|
||||
// print the best supported shell
|
||||
println!(
|
||||
"-> Most recent shell supported by the compositor is {}.",
|
||||
match env.get_shell() {
|
||||
Some(Shell::Wl(_)) => "the legacy wl_shell",
|
||||
Some(Shell::Zxdg(_)) => "the old unstable xdg_shell (zxdg_shell_v6)",
|
||||
Some(Shell::Xdg(_)) => "the current xdg_shell",
|
||||
None => "nothing",
|
||||
}
|
||||
);
|
||||
println!();
|
||||
|
||||
// print the outputs
|
||||
let outputs = env.get_all_outputs();
|
||||
println!("-> Compositor advertised {} outputs:", outputs.len());
|
||||
for output in outputs {
|
||||
sctk::output::with_output_info(&output, |info| {
|
||||
println!(
|
||||
" -> #{}: {} ({}), with scale factor of {}",
|
||||
info.id, info.model, info.make, info.scale_factor
|
||||
);
|
||||
println!(" Possible modes are:");
|
||||
for mode in &info.modes {
|
||||
println!(
|
||||
" -> [{}{}] {} x {} @ {}.{} Hz",
|
||||
if mode.is_preferred { "p" } else { " " },
|
||||
if mode.is_current { "c" } else { " " },
|
||||
mode.dimensions.0,
|
||||
mode.dimensions.1,
|
||||
mode.refresh_rate / 1000,
|
||||
mode.refresh_rate % 1000
|
||||
);
|
||||
}
|
||||
});
|
||||
}
|
||||
println!();
|
||||
|
||||
// print the seats
|
||||
let seats = env.get_all_seats();
|
||||
println!("-> Compositor advertised {} seats:", seats.len());
|
||||
for seat in seats {
|
||||
sctk::seat::with_seat_data(&seat, |data| {
|
||||
print!(" -> {} with capabilities: ", data.name);
|
||||
if data.has_pointer {
|
||||
print!("pointer ");
|
||||
}
|
||||
if data.has_keyboard {
|
||||
print!("keyboard ");
|
||||
}
|
||||
if data.has_touch {
|
||||
print!("touch ");
|
||||
}
|
||||
println!();
|
||||
});
|
||||
}
|
||||
|
||||
/*
|
||||
if env.decorations_mgr.is_some() {
|
||||
println!("-> Compositor supports server-side decorations.")
|
||||
} else {
|
||||
println!("-> Compositor does not support server-side decorations.")
|
||||
}
|
||||
*/
|
||||
Ok(())
|
||||
}
|
||||
294
third-party/vendor/smithay-client-toolkit/examples/image_viewer.rs
vendored
Normal file
294
third-party/vendor/smithay-client-toolkit/examples/image_viewer.rs
vendored
Normal file
|
|
@ -0,0 +1,294 @@
|
|||
extern crate image;
|
||||
extern crate smithay_client_toolkit as sctk;
|
||||
|
||||
use std::env;
|
||||
|
||||
use sctk::reexports::client::protocol::{wl_shm, wl_surface};
|
||||
use sctk::shm::AutoMemPool;
|
||||
use sctk::window::{Event as WEvent, FallbackFrame, State};
|
||||
|
||||
sctk::default_environment!(ImViewerExample, desktop);
|
||||
|
||||
fn main() {
|
||||
// First of all, retrieve the path from the program arguments:
|
||||
let path = match env::args_os().nth(1) {
|
||||
Some(p) => p,
|
||||
None => {
|
||||
println!("USAGE: ./image_wiewer <PATH>");
|
||||
return;
|
||||
}
|
||||
};
|
||||
// now, try to open the image
|
||||
// the image crate will take care of auto-detecting the file format
|
||||
let image = match image::open(&path) {
|
||||
Ok(i) => i,
|
||||
Err(e) => {
|
||||
println!("Failed to open image {}.", path.to_string_lossy());
|
||||
println!("Error was: {:?}", e);
|
||||
return;
|
||||
}
|
||||
};
|
||||
// We'll need the image in RGBA for drawing it
|
||||
let image = image.to_rgba8();
|
||||
|
||||
/*
|
||||
* Initalize the wayland connection
|
||||
*/
|
||||
let (env, _display, mut queue) = sctk::new_default_environment!(ImViewerExample, desktop)
|
||||
.expect("Unable to connect to a Wayland compositor");
|
||||
|
||||
// Use the compositor global to create a new surface
|
||||
let surface = env
|
||||
.create_surface_with_scale_callback(|dpi, _surface, _dispatch_data| {
|
||||
println!("dpi changed to {}", dpi);
|
||||
})
|
||||
.detach();
|
||||
|
||||
/*
|
||||
* Init the window
|
||||
*/
|
||||
|
||||
// First of all, this Option<WEvent> will store
|
||||
// any event from the window that we'll need to process. We
|
||||
// store them and will process them later in the event loop
|
||||
// rather that process them directly because in a batch of
|
||||
// generated events, often only the last one needs to actually
|
||||
// be processed, and some events may render other obsoletes.
|
||||
// See the closure a few lines below for details
|
||||
let mut next_action = None::<WEvent>;
|
||||
|
||||
// Now we actually create the window. The type parameter `ConceptFrame` here
|
||||
// specifies the type we want to use to draw the borders. To create your own
|
||||
// decorations you just need an object to implement the `Frame` trait.
|
||||
let mut window = env
|
||||
.create_window::<FallbackFrame, _>(
|
||||
surface, // the wl_surface that serves as the basis of this window
|
||||
None, // None for theme_manager, since we don't theme pointer outself
|
||||
image.dimensions(), // the initial internal dimensions of the window
|
||||
move |evt, mut dispatch_data| {
|
||||
// This is the closure that process the Window events.
|
||||
// There are 3 possible events:
|
||||
// - Close: the user requested the window to be closed, we'll then quit
|
||||
// - Configure: the server suggested a new state for the window (possibly
|
||||
// a new size if a resize is in progress). We'll likely need to redraw
|
||||
// our contents
|
||||
// - Refresh: the frame itself needs to be redrawn. SCTK does not do this
|
||||
// automatically because it has a cost and should only be done in periods
|
||||
// of the event loop where the client actually wants to draw
|
||||
// Here we actually only keep the last event receive according to a priority
|
||||
// order of Close > Configure > Refresh.
|
||||
// Indeed, if we received a Close, there is not point drawing anything more as
|
||||
// we will exit. A new Configure overrides a previous one, and if we received
|
||||
// a Configure we will refresh the frame anyway.
|
||||
|
||||
// We access the next_action Option via the dispatch_data provided by wayland-rs.
|
||||
let next_action = dispatch_data.get::<Option<WEvent>>().unwrap();
|
||||
// Check if we need to replace the old event by the new one
|
||||
let replace = matches!(
|
||||
(&evt, &*next_action),
|
||||
// replace if there is no old event
|
||||
(_, &None)
|
||||
// or the old event is refresh
|
||||
| (_, &Some(WEvent::Refresh))
|
||||
// or we had a configure and received a new one
|
||||
| (&WEvent::Configure { .. }, &Some(WEvent::Configure { .. }))
|
||||
// or the new event is close
|
||||
| (&WEvent::Close, _)
|
||||
);
|
||||
if replace {
|
||||
*next_action = Some(evt);
|
||||
}
|
||||
},
|
||||
// creating the window may fail if the code drawing the frame
|
||||
// fails to initialize itself. For ConceptFrame this should not happen
|
||||
// unless the system is utterly broken, though.
|
||||
)
|
||||
.expect("Failed to create a window !");
|
||||
|
||||
// Setting the windows title allows the compositor to know what your
|
||||
// window should be called and the title will be display on the header bar
|
||||
// of the windows decorations
|
||||
window.set_title("Image Viewer".to_string());
|
||||
|
||||
/*
|
||||
* Initialization of the memory pool
|
||||
*/
|
||||
let mut pool = env.create_auto_pool().expect("Failed to create the memory pool.");
|
||||
|
||||
/*
|
||||
* Event Loop preparation and running
|
||||
*/
|
||||
|
||||
// First, we initialize a few boolean flags that we'll use to track our state:
|
||||
// - the window needs to be redrawn
|
||||
let mut need_redraw = false;
|
||||
// - are we currently in the process of being resized? (to draw the image or
|
||||
// black content)
|
||||
let mut resizing = false;
|
||||
// - the size of our contents
|
||||
let mut dimensions = image.dimensions();
|
||||
|
||||
// if our shell does not need to wait for a configure event, we draw right away.
|
||||
//
|
||||
// Note that this is only the case for the old wl_shell protocol, which is now
|
||||
// deprecated. This code is only for compatibility with old server that do not
|
||||
// support the new standard xdg_shell protocol.
|
||||
//
|
||||
// But if we have fallbacked to wl_shell, we need to draw right away because we'll
|
||||
// never receive a configure event if we don't draw something...
|
||||
if !env.get_shell().unwrap().needs_configure() {
|
||||
// initial draw to bootstrap on wl_shell
|
||||
redraw(&mut pool, window.surface(), dimensions, if resizing { None } else { Some(&image) })
|
||||
.expect("Failed to draw");
|
||||
window.refresh();
|
||||
}
|
||||
|
||||
// We can now actually enter the event loop!
|
||||
loop {
|
||||
// First, check if any pending action was received by the
|
||||
// Window implementation:
|
||||
match next_action.take() {
|
||||
// We received a Close event, just break from the loop
|
||||
// and let the app quit
|
||||
Some(WEvent::Close) => break,
|
||||
// We receive a Refresh event, store that we need to refresh the
|
||||
// frame
|
||||
Some(WEvent::Refresh) => {
|
||||
window.refresh();
|
||||
window.surface().commit();
|
||||
}
|
||||
// We received a configure event, our action depends on its
|
||||
// contents
|
||||
Some(WEvent::Configure { new_size, states }) => {
|
||||
// the configure event contains a suggested size,
|
||||
// if it is different from our current size, we need to
|
||||
// update it and redraw
|
||||
if let Some((w, h)) = new_size {
|
||||
if dimensions != (w, h) {
|
||||
dimensions = (w, h);
|
||||
}
|
||||
}
|
||||
window.resize(dimensions.0, dimensions.1);
|
||||
window.refresh();
|
||||
// Are we currently resizing ?
|
||||
// We check if a resizing just started or stopped,
|
||||
// because in this case we'll swap between drawing black
|
||||
// and drawing the window (or the reverse), and thus we need to
|
||||
// redraw
|
||||
let new_resizing = states.contains(&State::Resizing);
|
||||
resizing = new_resizing;
|
||||
|
||||
need_redraw = true;
|
||||
}
|
||||
// No event, nothing new to do.
|
||||
None => {}
|
||||
}
|
||||
|
||||
if need_redraw {
|
||||
// We don't need to redraw or refresh anymore =)
|
||||
need_redraw = false;
|
||||
redraw(
|
||||
&mut pool,
|
||||
window.surface(),
|
||||
dimensions,
|
||||
if resizing { None } else { Some(&image) },
|
||||
)
|
||||
.expect("Failed to draw")
|
||||
}
|
||||
|
||||
// Finally, dispatch the event queue. This method blocks until a message
|
||||
// sends all our request to the server, then blocks until an event arrives
|
||||
// from it. It then processes all events by calling the implementation of
|
||||
// the target object for each, and only return once all pending messages
|
||||
// have been processed.
|
||||
queue.dispatch(&mut next_action, |_, _, _| {}).unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
// The draw function, which drawn `base_image` in the provided `MemPool`,
|
||||
// at given dimensions.
|
||||
//
|
||||
// If `base_image` is `None`, it'll just draw black contents. This is to
|
||||
// improve performance during resizing: we need to redraw the window frequently
|
||||
// so that its dimensions follow the pointer during the resizing, but resizing the
|
||||
// image is costly and long. So during an interactive resize of the window we'll
|
||||
// just draw black contents to not feel laggy.
|
||||
fn redraw(
|
||||
pool: &mut AutoMemPool,
|
||||
surface: &wl_surface::WlSurface,
|
||||
(buf_x, buf_y): (u32, u32),
|
||||
base_image: Option<&image::ImageBuffer<image::Rgba<u8>, Vec<u8>>>,
|
||||
) -> Result<(), ::std::io::Error> {
|
||||
// We allocate a new buffer from the memory pool with the appropriate dimensions
|
||||
// This function automatically finds an unused space of the correct size in the memory
|
||||
// pool and returns it as a `&mut [u8]`, as well as a `wl_buffer` matching it.
|
||||
let (canvas, new_buffer) = pool.buffer(
|
||||
buf_x as i32, // width of the buffer, in pixels
|
||||
buf_y as i32, // height of the buffer, in pixels
|
||||
4 * buf_x as i32, // stride: number of bytes between the start of two
|
||||
// consecutive rows of pixels
|
||||
wl_shm::Format::Argb8888, // the pixel format we wrote in
|
||||
)?;
|
||||
|
||||
if let Some(base_image) = base_image {
|
||||
// We have an image to draw
|
||||
|
||||
// first, resize it to the requested size. We just use the function provided
|
||||
// by the image crate here.
|
||||
let image =
|
||||
image::imageops::resize(base_image, buf_x, buf_y, image::imageops::FilterType::Nearest);
|
||||
|
||||
// Now, we'll write the pixels of the image to the MemPool.
|
||||
//
|
||||
// We do this in an horribly inefficient manner, for the sake of simplicity.
|
||||
// We'll send pixels to the server in ARGB8888 format (this is one of the only
|
||||
// formats that are guaranteed to be supported), but image provides it in
|
||||
// RGBA8888, so we need to do the conversion.
|
||||
//
|
||||
// Additionally, if the image has some transparent parts, we'll blend them into
|
||||
// a white background, otherwise the server will draw our window with a
|
||||
// transparent background!
|
||||
for (src_pixel, dst_pixel) in image.pixels().zip(canvas.chunks_exact_mut(4)) {
|
||||
// retrieve the pixel values
|
||||
let r = src_pixel.0[0] as u32;
|
||||
let g = src_pixel.0[1] as u32;
|
||||
let b = src_pixel.0[2] as u32;
|
||||
let a = src_pixel.0[3] as u32;
|
||||
// blend them
|
||||
let r = ::std::cmp::min(0xFF, (0xFF * (0xFF - a) + a * r) / 0xFF);
|
||||
let g = ::std::cmp::min(0xFF, (0xFF * (0xFF - a) + a * g) / 0xFF);
|
||||
let b = ::std::cmp::min(0xFF, (0xFF * (0xFF - a) + a * b) / 0xFF);
|
||||
// write the pixel
|
||||
let pixel: [u8; 4] = ((0xFF << 24) + (r << 16) + (g << 8) + b).to_ne_bytes();
|
||||
dst_pixel[0] = pixel[0];
|
||||
dst_pixel[1] = pixel[1];
|
||||
dst_pixel[2] = pixel[2];
|
||||
dst_pixel[3] = pixel[3];
|
||||
}
|
||||
} else {
|
||||
// We do not have any image to draw, so we draw black contents
|
||||
for dst_pixel in canvas.chunks_exact_mut(4) {
|
||||
dst_pixel[0] = 0x00;
|
||||
dst_pixel[1] = 0x00;
|
||||
dst_pixel[2] = 0x00;
|
||||
dst_pixel[3] = 0xFF;
|
||||
}
|
||||
}
|
||||
|
||||
surface.attach(Some(&new_buffer), 0, 0);
|
||||
// damage the surface so that the compositor knows it needs to redraw it
|
||||
if surface.as_ref().version() >= 4 {
|
||||
// If our server is recent enough and supports at least version 4 of the
|
||||
// wl_surface interface, we can specify the damage in buffer coordinates.
|
||||
// This is obviously the best and do that if possible.
|
||||
surface.damage_buffer(0, 0, buf_x as i32, buf_y as i32);
|
||||
} else {
|
||||
// Otherwise, we fallback to compatilibity mode. Here we specify damage
|
||||
// in surface coordinates, which would have been different if we had drawn
|
||||
// our buffer at HiDPI resolution. We didn't though, so it is ok.
|
||||
// Using `damage_buffer` in general is better though.
|
||||
surface.damage(0, 0, buf_x as i32, buf_y as i32);
|
||||
}
|
||||
surface.commit();
|
||||
Ok(())
|
||||
}
|
||||
226
third-party/vendor/smithay-client-toolkit/examples/kbd_input.rs
vendored
Normal file
226
third-party/vendor/smithay-client-toolkit/examples/kbd_input.rs
vendored
Normal file
|
|
@ -0,0 +1,226 @@
|
|||
extern crate smithay_client_toolkit as sctk;
|
||||
|
||||
use std::cmp::min;
|
||||
|
||||
use sctk::reexports::calloop;
|
||||
use sctk::reexports::client::protocol::{wl_keyboard, wl_shm, wl_surface};
|
||||
use sctk::seat::keyboard::{map_keyboard_repeat, Event as KbEvent, RepeatKind};
|
||||
use sctk::shm::AutoMemPool;
|
||||
use sctk::window::{Event as WEvent, FallbackFrame};
|
||||
|
||||
sctk::default_environment!(KbdInputExample, desktop);
|
||||
|
||||
fn main() {
|
||||
/*
|
||||
* Initial setup
|
||||
*/
|
||||
let (env, display, queue) = sctk::new_default_environment!(KbdInputExample, desktop)
|
||||
.expect("Unable to connect to a Wayland compositor");
|
||||
|
||||
/*
|
||||
* Prepare a calloop event loop to handle key repetion
|
||||
*/
|
||||
// Here `Option<WEvent>` is the type of a global value that will be shared by
|
||||
// all callbacks invoked by the event loop.
|
||||
let mut event_loop = calloop::EventLoop::<Option<WEvent>>::try_new().unwrap();
|
||||
|
||||
/*
|
||||
* Create a buffer with window contents
|
||||
*/
|
||||
|
||||
let mut dimensions = (320u32, 240u32);
|
||||
|
||||
/*
|
||||
* Init wayland objects
|
||||
*/
|
||||
|
||||
let surface = env.create_surface().detach();
|
||||
|
||||
let mut window = env
|
||||
.create_window::<FallbackFrame, _>(
|
||||
surface,
|
||||
None,
|
||||
dimensions,
|
||||
move |evt, mut dispatch_data| {
|
||||
let next_action = dispatch_data.get::<Option<WEvent>>().unwrap();
|
||||
// Keep last event in priority order : Close > Configure > Refresh
|
||||
let replace = matches!(
|
||||
(&evt, &*next_action),
|
||||
(_, &None)
|
||||
| (_, &Some(WEvent::Refresh))
|
||||
| (&WEvent::Configure { .. }, &Some(WEvent::Configure { .. }))
|
||||
| (&WEvent::Close, _)
|
||||
);
|
||||
if replace {
|
||||
*next_action = Some(evt);
|
||||
}
|
||||
},
|
||||
)
|
||||
.expect("Failed to create a window !");
|
||||
|
||||
window.set_title("Kbd Input".to_string());
|
||||
|
||||
let mut pool = env.create_auto_pool().expect("Failed to create a memory pool !");
|
||||
|
||||
/*
|
||||
* Keyboard initialization
|
||||
*/
|
||||
|
||||
let mut seats = Vec::<(String, Option<wl_keyboard::WlKeyboard>)>::new();
|
||||
|
||||
// first process already existing seats
|
||||
for seat in env.get_all_seats() {
|
||||
if let Some((has_kbd, name)) = sctk::seat::with_seat_data(&seat, |seat_data| {
|
||||
(seat_data.has_keyboard && !seat_data.defunct, seat_data.name.clone())
|
||||
}) {
|
||||
if has_kbd {
|
||||
let seat_name = name.clone();
|
||||
match map_keyboard_repeat(
|
||||
event_loop.handle(),
|
||||
&seat,
|
||||
None,
|
||||
RepeatKind::System,
|
||||
move |event, _, _| print_keyboard_event(event, &seat_name),
|
||||
) {
|
||||
Ok(kbd) => {
|
||||
seats.push((name, Some(kbd)));
|
||||
}
|
||||
Err(e) => {
|
||||
eprintln!("Failed to map keyboard on seat {} : {:?}.", name, e);
|
||||
seats.push((name, None));
|
||||
}
|
||||
}
|
||||
} else {
|
||||
seats.push((name, None));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// then setup a listener for changes
|
||||
let loop_handle = event_loop.handle();
|
||||
let _seat_listener = env.listen_for_seats(move |seat, seat_data, _| {
|
||||
// find the seat in the vec of seats, or insert it if it is unknown
|
||||
let idx = seats.iter().position(|(name, _)| name == &seat_data.name);
|
||||
let idx = idx.unwrap_or_else(|| {
|
||||
seats.push((seat_data.name.clone(), None));
|
||||
seats.len() - 1
|
||||
});
|
||||
|
||||
let (_, ref mut opt_kbd) = &mut seats[idx];
|
||||
// we should map a keyboard if the seat has the capability & is not defunct
|
||||
if seat_data.has_keyboard && !seat_data.defunct {
|
||||
if opt_kbd.is_none() {
|
||||
// we should initalize a keyboard
|
||||
let seat_name = seat_data.name.clone();
|
||||
match map_keyboard_repeat(
|
||||
loop_handle.clone(),
|
||||
&seat,
|
||||
None,
|
||||
RepeatKind::System,
|
||||
move |event, _, _| print_keyboard_event(event, &seat_name),
|
||||
) {
|
||||
Ok(kbd) => {
|
||||
*opt_kbd = Some(kbd);
|
||||
}
|
||||
Err(e) => {
|
||||
eprintln!("Failed to map keyboard on seat {} : {:?}.", seat_data.name, e)
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if let Some(kbd) = opt_kbd.take() {
|
||||
// the keyboard has been removed, cleanup
|
||||
kbd.release();
|
||||
}
|
||||
});
|
||||
|
||||
if !env.get_shell().unwrap().needs_configure() {
|
||||
// initial draw to bootstrap on wl_shell
|
||||
redraw(&mut pool, window.surface(), dimensions).expect("Failed to draw");
|
||||
window.refresh();
|
||||
}
|
||||
|
||||
let mut next_action = None;
|
||||
|
||||
sctk::WaylandSource::new(queue).quick_insert(event_loop.handle()).unwrap();
|
||||
|
||||
loop {
|
||||
match next_action.take() {
|
||||
Some(WEvent::Close) => break,
|
||||
Some(WEvent::Refresh) => {
|
||||
window.refresh();
|
||||
window.surface().commit();
|
||||
}
|
||||
Some(WEvent::Configure { new_size, states }) => {
|
||||
if let Some((w, h)) = new_size {
|
||||
window.resize(w, h);
|
||||
dimensions = (w, h)
|
||||
}
|
||||
println!("Window states: {:?}", states);
|
||||
window.refresh();
|
||||
redraw(&mut pool, window.surface(), dimensions).expect("Failed to draw");
|
||||
}
|
||||
None => {}
|
||||
}
|
||||
|
||||
// always flush the connection before going to sleep waiting for events
|
||||
display.flush().unwrap();
|
||||
|
||||
event_loop.dispatch(None, &mut next_action).unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
fn print_keyboard_event(event: KbEvent, seat_name: &str) {
|
||||
match event {
|
||||
KbEvent::Enter { keysyms, .. } => {
|
||||
println!("Gained focus on seat '{}' while {} keys pressed.", seat_name, keysyms.len(),);
|
||||
}
|
||||
KbEvent::Leave { .. } => {
|
||||
println!("Lost focus on seat '{}'.", seat_name);
|
||||
}
|
||||
KbEvent::Key { keysym, state, utf8, .. } => {
|
||||
println!("Key {:?}: {:x} on seat '{}'.", state, keysym, seat_name);
|
||||
if let Some(txt) = utf8 {
|
||||
println!(" -> Received text \"{}\".", txt);
|
||||
}
|
||||
}
|
||||
KbEvent::Modifiers { modifiers } => {
|
||||
println!("Modifiers changed to {:?} on seat '{}'.", modifiers, seat_name);
|
||||
}
|
||||
KbEvent::Repeat { keysym, utf8, .. } => {
|
||||
println!("Key repetition {:x} on seat '{}'.", keysym, seat_name);
|
||||
if let Some(txt) = utf8 {
|
||||
println!(" -> Received text \"{}\".", txt);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(clippy::many_single_char_names)]
|
||||
fn redraw(
|
||||
pool: &mut AutoMemPool,
|
||||
surface: &wl_surface::WlSurface,
|
||||
(buf_x, buf_y): (u32, u32),
|
||||
) -> Result<(), ::std::io::Error> {
|
||||
let (canvas, new_buffer) =
|
||||
pool.buffer(buf_x as i32, buf_y as i32, 4 * buf_x as i32, wl_shm::Format::Argb8888)?;
|
||||
for (i, dst_pixel) in canvas.chunks_exact_mut(4).enumerate() {
|
||||
let x = i as u32 % buf_x;
|
||||
let y = i as u32 / buf_x;
|
||||
let r: u32 = min(((buf_x - x) * 0xFF) / buf_x, ((buf_y - y) * 0xFF) / buf_y);
|
||||
let g: u32 = min((x * 0xFF) / buf_x, ((buf_y - y) * 0xFF) / buf_y);
|
||||
let b: u32 = min(((buf_x - x) * 0xFF) / buf_x, (y * 0xFF) / buf_y);
|
||||
let pixel: [u8; 4] = ((0xFF << 24) + (r << 16) + (g << 8) + b).to_ne_bytes();
|
||||
dst_pixel[0] = pixel[0];
|
||||
dst_pixel[1] = pixel[1];
|
||||
dst_pixel[2] = pixel[2];
|
||||
dst_pixel[3] = pixel[3];
|
||||
}
|
||||
surface.attach(Some(&new_buffer), 0, 0);
|
||||
if surface.as_ref().version() >= 4 {
|
||||
surface.damage_buffer(0, 0, buf_x as i32, buf_y as i32);
|
||||
} else {
|
||||
surface.damage(0, 0, buf_x as i32, buf_y as i32);
|
||||
}
|
||||
surface.commit();
|
||||
Ok(())
|
||||
}
|
||||
187
third-party/vendor/smithay-client-toolkit/examples/layer_shell.rs
vendored
Normal file
187
third-party/vendor/smithay-client-toolkit/examples/layer_shell.rs
vendored
Normal file
|
|
@ -0,0 +1,187 @@
|
|||
use smithay_client_toolkit::{
|
||||
default_environment,
|
||||
environment::SimpleGlobal,
|
||||
new_default_environment,
|
||||
output::{with_output_info, OutputInfo},
|
||||
reexports::{
|
||||
calloop,
|
||||
client::protocol::{wl_output, wl_shm, wl_surface},
|
||||
client::{Attached, Main},
|
||||
protocols::wlr::unstable::layer_shell::v1::client::{
|
||||
zwlr_layer_shell_v1, zwlr_layer_surface_v1,
|
||||
},
|
||||
},
|
||||
shm::AutoMemPool,
|
||||
WaylandSource,
|
||||
};
|
||||
|
||||
use std::cell::{Cell, RefCell};
|
||||
use std::rc::Rc;
|
||||
|
||||
default_environment!(Env,
|
||||
fields = [
|
||||
layer_shell: SimpleGlobal<zwlr_layer_shell_v1::ZwlrLayerShellV1>,
|
||||
],
|
||||
singles = [
|
||||
zwlr_layer_shell_v1::ZwlrLayerShellV1 => layer_shell
|
||||
],
|
||||
);
|
||||
|
||||
#[derive(PartialEq, Copy, Clone)]
|
||||
enum RenderEvent {
|
||||
Configure { width: u32, height: u32 },
|
||||
Closed,
|
||||
}
|
||||
|
||||
struct Surface {
|
||||
surface: wl_surface::WlSurface,
|
||||
layer_surface: Main<zwlr_layer_surface_v1::ZwlrLayerSurfaceV1>,
|
||||
next_render_event: Rc<Cell<Option<RenderEvent>>>,
|
||||
pool: AutoMemPool,
|
||||
dimensions: (u32, u32),
|
||||
}
|
||||
|
||||
impl Surface {
|
||||
fn new(
|
||||
output: &wl_output::WlOutput,
|
||||
surface: wl_surface::WlSurface,
|
||||
layer_shell: &Attached<zwlr_layer_shell_v1::ZwlrLayerShellV1>,
|
||||
pool: AutoMemPool,
|
||||
) -> Self {
|
||||
let layer_surface = layer_shell.get_layer_surface(
|
||||
&surface,
|
||||
Some(output),
|
||||
zwlr_layer_shell_v1::Layer::Overlay,
|
||||
"example".to_owned(),
|
||||
);
|
||||
|
||||
layer_surface.set_size(32, 32);
|
||||
// Anchor to the top left corner of the output
|
||||
layer_surface
|
||||
.set_anchor(zwlr_layer_surface_v1::Anchor::Top | zwlr_layer_surface_v1::Anchor::Left);
|
||||
|
||||
let next_render_event = Rc::new(Cell::new(None::<RenderEvent>));
|
||||
let next_render_event_handle = Rc::clone(&next_render_event);
|
||||
layer_surface.quick_assign(move |layer_surface, event, _| {
|
||||
match (event, next_render_event_handle.get()) {
|
||||
(zwlr_layer_surface_v1::Event::Closed, _) => {
|
||||
next_render_event_handle.set(Some(RenderEvent::Closed));
|
||||
}
|
||||
(zwlr_layer_surface_v1::Event::Configure { serial, width, height }, next)
|
||||
if next != Some(RenderEvent::Closed) =>
|
||||
{
|
||||
layer_surface.ack_configure(serial);
|
||||
next_render_event_handle.set(Some(RenderEvent::Configure { width, height }));
|
||||
}
|
||||
(_, _) => {}
|
||||
}
|
||||
});
|
||||
|
||||
// Commit so that the server will send a configure event
|
||||
surface.commit();
|
||||
|
||||
Self { surface, layer_surface, next_render_event, pool, dimensions: (0, 0) }
|
||||
}
|
||||
|
||||
/// Handles any events that have occurred since the last call, redrawing if needed.
|
||||
/// Returns true if the surface should be dropped.
|
||||
fn handle_events(&mut self) -> bool {
|
||||
match self.next_render_event.take() {
|
||||
Some(RenderEvent::Closed) => true,
|
||||
Some(RenderEvent::Configure { width, height }) => {
|
||||
if self.dimensions != (width, height) {
|
||||
self.dimensions = (width, height);
|
||||
self.draw();
|
||||
}
|
||||
false
|
||||
}
|
||||
None => false,
|
||||
}
|
||||
}
|
||||
|
||||
fn draw(&mut self) {
|
||||
let stride = 4 * self.dimensions.0 as i32;
|
||||
let width = self.dimensions.0 as i32;
|
||||
let height = self.dimensions.1 as i32;
|
||||
|
||||
// Note: unwrap() is only used here in the interest of simplicity of the example.
|
||||
// A "real" application should handle the case where both pools are still in use by the
|
||||
// compositor.
|
||||
let (canvas, buffer) =
|
||||
self.pool.buffer(width, height, stride, wl_shm::Format::Argb8888).unwrap();
|
||||
|
||||
for dst_pixel in canvas.chunks_exact_mut(4) {
|
||||
let pixel = 0xff00ff00u32.to_ne_bytes();
|
||||
dst_pixel[0] = pixel[0];
|
||||
dst_pixel[1] = pixel[1];
|
||||
dst_pixel[2] = pixel[2];
|
||||
dst_pixel[3] = pixel[3];
|
||||
}
|
||||
|
||||
// Attach the buffer to the surface and mark the entire surface as damaged
|
||||
self.surface.attach(Some(&buffer), 0, 0);
|
||||
self.surface.damage_buffer(0, 0, width as i32, height as i32);
|
||||
|
||||
// Finally, commit the surface
|
||||
self.surface.commit();
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for Surface {
|
||||
fn drop(&mut self) {
|
||||
self.layer_surface.destroy();
|
||||
self.surface.destroy();
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let (env, display, queue) =
|
||||
new_default_environment!(Env, fields = [layer_shell: SimpleGlobal::new(),])
|
||||
.expect("Initial roundtrip failed!");
|
||||
|
||||
let surfaces = Rc::new(RefCell::new(Vec::new()));
|
||||
|
||||
let layer_shell = env.require_global::<zwlr_layer_shell_v1::ZwlrLayerShellV1>();
|
||||
|
||||
let env_handle = env.clone();
|
||||
let surfaces_handle = Rc::clone(&surfaces);
|
||||
let output_handler = move |output: wl_output::WlOutput, info: &OutputInfo| {
|
||||
if info.obsolete {
|
||||
// an output has been removed, release it
|
||||
surfaces_handle.borrow_mut().retain(|(i, _)| *i != info.id);
|
||||
output.release();
|
||||
} else {
|
||||
// an output has been created, construct a surface for it
|
||||
let surface = env_handle.create_surface().detach();
|
||||
let pool = env_handle.create_auto_pool().expect("Failed to create a memory pool!");
|
||||
(*surfaces_handle.borrow_mut())
|
||||
.push((info.id, Surface::new(&output, surface, &layer_shell.clone(), pool)));
|
||||
}
|
||||
};
|
||||
|
||||
// Process currently existing outputs
|
||||
for output in env.get_all_outputs() {
|
||||
if let Some(info) = with_output_info(&output, Clone::clone) {
|
||||
output_handler(output, &info);
|
||||
}
|
||||
}
|
||||
|
||||
// Setup a listener for changes
|
||||
// The listener will live for as long as we keep this handle alive
|
||||
let _listner_handle =
|
||||
env.listen_for_outputs(move |output, info, _| output_handler(output, info));
|
||||
|
||||
let mut event_loop = calloop::EventLoop::<()>::try_new().unwrap();
|
||||
|
||||
WaylandSource::new(queue).quick_insert(event_loop.handle()).unwrap();
|
||||
|
||||
loop {
|
||||
{
|
||||
// Using a new scope so that `surfaces` reference gets dropped
|
||||
surfaces.borrow_mut().retain_mut(|surface| !surface.1.handle_events());
|
||||
}
|
||||
|
||||
display.flush().unwrap();
|
||||
event_loop.dispatch(None, &mut ()).unwrap();
|
||||
}
|
||||
}
|
||||
246
third-party/vendor/smithay-client-toolkit/examples/pointer_input.rs
vendored
Normal file
246
third-party/vendor/smithay-client-toolkit/examples/pointer_input.rs
vendored
Normal file
|
|
@ -0,0 +1,246 @@
|
|||
extern crate smithay_client_toolkit as sctk;
|
||||
|
||||
use std::cmp::min;
|
||||
|
||||
use sctk::reexports::client::protocol::{wl_pointer, wl_shm, wl_surface};
|
||||
use sctk::shm::AutoMemPool;
|
||||
use sctk::window::{Event as WEvent, FallbackFrame};
|
||||
|
||||
#[derive(Debug)]
|
||||
enum NextAction {
|
||||
Refresh,
|
||||
Redraw,
|
||||
Exit,
|
||||
}
|
||||
|
||||
struct WindowConfig {
|
||||
width: u32,
|
||||
height: u32,
|
||||
dpi_scale: i32,
|
||||
next_action: Option<NextAction>,
|
||||
has_drawn_once: bool,
|
||||
}
|
||||
|
||||
impl WindowConfig {
|
||||
pub fn new() -> Self {
|
||||
WindowConfig {
|
||||
width: 320,
|
||||
height: 240,
|
||||
dpi_scale: 1,
|
||||
next_action: None,
|
||||
has_drawn_once: false,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn dimensions(&self) -> (u32, u32) {
|
||||
(self.width * self.dpi_scale as u32, self.height * self.dpi_scale as u32)
|
||||
}
|
||||
|
||||
pub fn handle_action(&mut self, new_action: NextAction) {
|
||||
let replace = matches!(
|
||||
(&self.next_action, &new_action),
|
||||
(&None, _)
|
||||
| (&Some(NextAction::Refresh), _)
|
||||
| (&Some(NextAction::Redraw), &NextAction::Exit)
|
||||
);
|
||||
if replace {
|
||||
self.next_action = Some(new_action);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
sctk::default_environment!(PtrInputExample, desktop);
|
||||
|
||||
fn main() {
|
||||
/*
|
||||
* Initial setup
|
||||
*/
|
||||
let (env, _display, mut queue) = sctk::new_default_environment!(PtrInputExample, desktop)
|
||||
.expect("Unable to connect to a Wayland compositor");
|
||||
|
||||
/*
|
||||
* Init wayland objects
|
||||
*/
|
||||
|
||||
let mut window_config = WindowConfig::new();
|
||||
|
||||
let surface = env
|
||||
.create_surface_with_scale_callback(move |dpi, surface, mut dispatch_data| {
|
||||
let config = dispatch_data.get::<WindowConfig>().unwrap();
|
||||
surface.set_buffer_scale(dpi);
|
||||
config.dpi_scale = dpi;
|
||||
config.handle_action(NextAction::Redraw);
|
||||
})
|
||||
.detach();
|
||||
|
||||
let mut window = env
|
||||
.create_window::<FallbackFrame, _>(
|
||||
surface,
|
||||
None,
|
||||
window_config.dimensions(),
|
||||
move |event, mut dispatch_data| {
|
||||
let mut config = dispatch_data.get::<WindowConfig>().unwrap();
|
||||
match event {
|
||||
WEvent::Refresh => config.handle_action(NextAction::Refresh),
|
||||
WEvent::Configure { new_size: Some((w, h)), .. } => {
|
||||
if config.dimensions() != (w, h) || !config.has_drawn_once {
|
||||
config.width = w;
|
||||
config.height = h;
|
||||
config.handle_action(NextAction::Redraw);
|
||||
} else {
|
||||
config.handle_action(NextAction::Refresh);
|
||||
}
|
||||
}
|
||||
WEvent::Configure { new_size: None, .. } => {
|
||||
if config.has_drawn_once {
|
||||
config.handle_action(NextAction::Refresh)
|
||||
} else {
|
||||
config.handle_action(NextAction::Redraw)
|
||||
}
|
||||
}
|
||||
WEvent::Close => config.handle_action(NextAction::Exit),
|
||||
}
|
||||
},
|
||||
)
|
||||
.expect("Failed to create a window !");
|
||||
|
||||
let mut pool = env.create_auto_pool().expect("Failed to create a memory pool !");
|
||||
|
||||
/*
|
||||
* Pointer initialization
|
||||
*/
|
||||
let mut seats = Vec::<(String, Option<wl_pointer::WlPointer>)>::new();
|
||||
|
||||
// first process already existing seats
|
||||
for seat in env.get_all_seats() {
|
||||
if let Some((has_ptr, name)) = sctk::seat::with_seat_data(&seat, |seat_data| {
|
||||
(seat_data.has_pointer && !seat_data.defunct, seat_data.name.clone())
|
||||
}) {
|
||||
if has_ptr {
|
||||
let seat_name = name.clone();
|
||||
let pointer = seat.get_pointer();
|
||||
let surface = window.surface().clone();
|
||||
pointer.quick_assign(move |_, event, _| {
|
||||
print_pointer_event(event, &seat_name, &surface)
|
||||
});
|
||||
} else {
|
||||
seats.push((name, None));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// then setup a listener for changes
|
||||
let main_surface = window.surface().clone();
|
||||
let _seat_listener = env.listen_for_seats(move |seat, seat_data, _| {
|
||||
// find the seat in the vec of seats, or insert it if it is unknown
|
||||
let idx = seats.iter().position(|(name, _)| name == &seat_data.name);
|
||||
let idx = idx.unwrap_or_else(|| {
|
||||
seats.push((seat_data.name.clone(), None));
|
||||
seats.len() - 1
|
||||
});
|
||||
|
||||
let (_, ref mut opt_ptr) = &mut seats[idx];
|
||||
// we should map a keyboard if the seat has the capability & is not defunct
|
||||
if seat_data.has_keyboard && !seat_data.defunct {
|
||||
if opt_ptr.is_none() {
|
||||
// we should initalize a keyboard
|
||||
let seat_name = seat_data.name.clone();
|
||||
let pointer = seat.get_pointer();
|
||||
let surface = main_surface.clone();
|
||||
pointer.quick_assign(move |_, event, _| {
|
||||
print_pointer_event(event, &seat_name, &surface)
|
||||
});
|
||||
*opt_ptr = Some(pointer.detach());
|
||||
}
|
||||
} else if let Some(ptr) = opt_ptr.take() {
|
||||
// the pointer has been removed, cleanup
|
||||
ptr.release();
|
||||
}
|
||||
});
|
||||
|
||||
if !env.get_shell().unwrap().needs_configure() {
|
||||
window_config.handle_action(NextAction::Redraw);
|
||||
}
|
||||
|
||||
loop {
|
||||
let next_action = window_config.next_action.take();
|
||||
println!("{:?}", next_action);
|
||||
match next_action {
|
||||
Some(NextAction::Exit) => break,
|
||||
Some(NextAction::Refresh) => {
|
||||
window.refresh();
|
||||
window.surface().commit();
|
||||
}
|
||||
Some(NextAction::Redraw) => {
|
||||
window_config.has_drawn_once = true;
|
||||
let (w, h) = window_config.dimensions();
|
||||
window.resize(w, h);
|
||||
window.refresh();
|
||||
redraw(&mut pool, window.surface(), window_config.dimensions())
|
||||
.expect("Failed to draw");
|
||||
}
|
||||
None => {}
|
||||
}
|
||||
|
||||
queue.dispatch(&mut window_config, |_, _, _| {}).unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
#[allow(clippy::many_single_char_names)]
|
||||
fn redraw(
|
||||
pool: &mut AutoMemPool,
|
||||
surface: &wl_surface::WlSurface,
|
||||
(buf_x, buf_y): (u32, u32),
|
||||
) -> Result<(), ::std::io::Error> {
|
||||
let (canvas, new_buffer) =
|
||||
pool.buffer(buf_x as i32, buf_y as i32, 4 * buf_x as i32, wl_shm::Format::Argb8888)?;
|
||||
for (i, dst_pixel) in canvas.chunks_exact_mut(4).enumerate() {
|
||||
let x = i as u32 % buf_x;
|
||||
let y = i as u32 / buf_x;
|
||||
let r: u32 = min(((buf_x - x) * 0xFF) / buf_x, ((buf_y - y) * 0xFF) / buf_y);
|
||||
let g: u32 = min((x * 0xFF) / buf_x, ((buf_y - y) * 0xFF) / buf_y);
|
||||
let b: u32 = min(((buf_x - x) * 0xFF) / buf_x, (y * 0xFF) / buf_y);
|
||||
let pixel: [u8; 4] = ((0xFF << 24) + (r << 16) + (g << 8) + b).to_ne_bytes();
|
||||
dst_pixel[0] = pixel[0];
|
||||
dst_pixel[1] = pixel[1];
|
||||
dst_pixel[2] = pixel[2];
|
||||
dst_pixel[3] = pixel[3];
|
||||
}
|
||||
surface.attach(Some(&new_buffer), 0, 0);
|
||||
if surface.as_ref().version() >= 4 {
|
||||
surface.damage_buffer(0, 0, buf_x as i32, buf_y as i32);
|
||||
} else {
|
||||
surface.damage(0, 0, buf_x as i32, buf_y as i32);
|
||||
}
|
||||
surface.commit();
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn print_pointer_event(
|
||||
event: wl_pointer::Event,
|
||||
seat_name: &str,
|
||||
main_surface: &wl_surface::WlSurface,
|
||||
) {
|
||||
match event {
|
||||
wl_pointer::Event::Enter { surface, surface_x, surface_y, .. } => {
|
||||
if main_surface == &surface {
|
||||
println!(
|
||||
"Pointer of seat '{}' entered at ({}, {})",
|
||||
seat_name, surface_x, surface_y
|
||||
);
|
||||
}
|
||||
}
|
||||
wl_pointer::Event::Leave { surface, .. } => {
|
||||
if main_surface == &surface {
|
||||
println!("Pointer of seat '{}' left", seat_name);
|
||||
}
|
||||
}
|
||||
wl_pointer::Event::Button { button, state, .. } => {
|
||||
println!("Button {:?} of seat '{}' was {:?}", button, seat_name, state);
|
||||
}
|
||||
wl_pointer::Event::Motion { surface_x, surface_y, .. } => {
|
||||
println!("Pointer motion to ({}, {}) on seat '{}'", surface_x, surface_y, seat_name)
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
340
third-party/vendor/smithay-client-toolkit/examples/selection.rs
vendored
Normal file
340
third-party/vendor/smithay-client-toolkit/examples/selection.rs
vendored
Normal file
|
|
@ -0,0 +1,340 @@
|
|||
extern crate smithay_client_toolkit as sctk;
|
||||
|
||||
use std::io::{Read, Write};
|
||||
|
||||
use sctk::{
|
||||
data_device::DataSourceEvent,
|
||||
environment::Environment,
|
||||
primary_selection::PrimarySelectionSourceEvent,
|
||||
seat::keyboard::{map_keyboard_repeat, Event as KbEvent, KeyState, RepeatKind},
|
||||
shm::AutoMemPool,
|
||||
window::{Event as WEvent, FallbackFrame},
|
||||
};
|
||||
|
||||
use sctk::reexports::{
|
||||
calloop::{LoopHandle, RegistrationToken},
|
||||
client::{
|
||||
protocol::{wl_keyboard, wl_seat, wl_shm, wl_surface},
|
||||
DispatchData,
|
||||
},
|
||||
};
|
||||
|
||||
sctk::default_environment!(SelectionExample, desktop);
|
||||
|
||||
// Here the type parameter is a global value that will be shared by
|
||||
// all callbacks invoked by the event loop.
|
||||
type DData = (Environment<SelectionExample>, Option<WEvent>, Option<RegistrationToken>);
|
||||
|
||||
fn main() {
|
||||
/*
|
||||
* Initial setup
|
||||
*/
|
||||
let (env, display, queue) = sctk::new_default_environment!(SelectionExample, desktop)
|
||||
.expect("Unable to connect to a Wayland compositor");
|
||||
|
||||
/*
|
||||
* Prepare a calloop event loop to handle clipboard reading
|
||||
*/
|
||||
let mut event_loop = sctk::reexports::calloop::EventLoop::<DData>::try_new().unwrap();
|
||||
|
||||
// we need a window to receive things actually
|
||||
let mut dimensions = (320u32, 240u32);
|
||||
let surface = env.create_surface().detach();
|
||||
|
||||
let mut window = env
|
||||
.create_window::<FallbackFrame, _>(
|
||||
surface,
|
||||
None,
|
||||
dimensions,
|
||||
move |evt, mut dispatch_data| {
|
||||
let (_, next_action, _) = dispatch_data.get::<DData>().unwrap();
|
||||
// Keep last event in priority order : Close > Configure > Refresh
|
||||
let replace = matches!(
|
||||
(&evt, &*next_action),
|
||||
(_, &None)
|
||||
| (_, &Some(WEvent::Refresh))
|
||||
| (&WEvent::Configure { .. }, &Some(WEvent::Configure { .. }))
|
||||
| (&WEvent::Close, _)
|
||||
);
|
||||
if replace {
|
||||
*next_action = Some(evt);
|
||||
}
|
||||
},
|
||||
)
|
||||
.expect("Failed to create a window !");
|
||||
window.set_title("Selection example".to_string());
|
||||
|
||||
println!("Press c/C p/P to copy/paste from selection/primary clipboard respectively.");
|
||||
|
||||
let mut pool = env.create_auto_pool().expect("Failed to create a memory pool !");
|
||||
|
||||
let mut seats = Vec::<(String, Option<wl_keyboard::WlKeyboard>)>::new();
|
||||
|
||||
// first process already existing seats
|
||||
for seat in env.get_all_seats() {
|
||||
if let Some((has_kbd, name)) = sctk::seat::with_seat_data(&seat, |seat_data| {
|
||||
(seat_data.has_keyboard && !seat_data.defunct, seat_data.name.clone())
|
||||
}) {
|
||||
if has_kbd {
|
||||
let my_seat = seat.clone();
|
||||
let handle = event_loop.handle();
|
||||
match map_keyboard_repeat(
|
||||
event_loop.handle(),
|
||||
&seat,
|
||||
None,
|
||||
RepeatKind::System,
|
||||
move |event, _, ddata| process_keyboard_event(event, &my_seat, &handle, ddata),
|
||||
) {
|
||||
Ok(kbd) => {
|
||||
seats.push((name, Some(kbd)));
|
||||
}
|
||||
Err(e) => {
|
||||
eprintln!("Failed to map keyboard on seat {} : {:?}.", name, e);
|
||||
seats.push((name, None));
|
||||
}
|
||||
}
|
||||
} else {
|
||||
seats.push((name, None));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// then setup a listener for changes
|
||||
let loop_handle = event_loop.handle();
|
||||
let _seat_listener = env.listen_for_seats(move |seat, seat_data, _| {
|
||||
// find the seat in the vec of seats, or insert it if it is unknown
|
||||
let idx = seats.iter().position(|(name, _)| name == &seat_data.name);
|
||||
let idx = idx.unwrap_or_else(|| {
|
||||
seats.push((seat_data.name.clone(), None));
|
||||
seats.len() - 1
|
||||
});
|
||||
|
||||
let (_, ref mut opt_kbd) = &mut seats[idx];
|
||||
// we should map a keyboard if the seat has the capability & is not defunct
|
||||
if seat_data.has_keyboard && !seat_data.defunct {
|
||||
if opt_kbd.is_none() {
|
||||
// we should initalize a keyboard
|
||||
let my_seat = seat.clone();
|
||||
let handle = loop_handle.clone();
|
||||
match map_keyboard_repeat(
|
||||
handle.clone(),
|
||||
&seat,
|
||||
None,
|
||||
RepeatKind::System,
|
||||
move |event, _, ddata| process_keyboard_event(event, &my_seat, &handle, ddata),
|
||||
) {
|
||||
Ok(kbd) => {
|
||||
*opt_kbd = Some(kbd);
|
||||
}
|
||||
Err(e) => {
|
||||
eprintln!("Failed to map keyboard on seat {} : {:?}.", seat_data.name, e)
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if let Some(kbd) = opt_kbd.take() {
|
||||
// the keyboard has been removed, cleanup
|
||||
kbd.release();
|
||||
}
|
||||
});
|
||||
|
||||
if !env.get_shell().unwrap().needs_configure() {
|
||||
// initial draw to bootstrap on wl_shell
|
||||
redraw(&mut pool, window.surface(), dimensions).expect("Failed to draw");
|
||||
window.refresh();
|
||||
}
|
||||
|
||||
// the data that will be shared to all callbacks
|
||||
let mut data: DData = (env, None, None);
|
||||
|
||||
sctk::WaylandSource::new(queue).quick_insert(event_loop.handle()).unwrap();
|
||||
|
||||
loop {
|
||||
match data.1.take() {
|
||||
Some(WEvent::Close) => break,
|
||||
Some(WEvent::Refresh) => {
|
||||
window.refresh();
|
||||
window.surface().commit();
|
||||
}
|
||||
Some(WEvent::Configure { new_size, .. }) => {
|
||||
if let Some((w, h)) = new_size {
|
||||
window.resize(w, h);
|
||||
dimensions = (w, h)
|
||||
}
|
||||
window.refresh();
|
||||
redraw(&mut pool, window.surface(), dimensions).expect("Failed to draw");
|
||||
}
|
||||
None => {}
|
||||
}
|
||||
|
||||
// always flush the connection before going to sleep waiting for events
|
||||
display.flush().unwrap();
|
||||
|
||||
event_loop.dispatch(None, &mut data).unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
fn process_keyboard_event(
|
||||
event: KbEvent,
|
||||
seat: &wl_seat::WlSeat,
|
||||
handle: &LoopHandle<DData>,
|
||||
mut ddata: DispatchData,
|
||||
) {
|
||||
let (env, _, opt_source) = ddata.get::<DData>().unwrap();
|
||||
if let KbEvent::Key { state, utf8: Some(text), serial, .. } = event {
|
||||
if text == "p" && state == KeyState::Pressed {
|
||||
// pressed the 'p' key, try to read contents !
|
||||
env.with_data_device(seat, |device| {
|
||||
device.with_selection(|offer| {
|
||||
let offer = match offer {
|
||||
Some(offer) => offer,
|
||||
None => {
|
||||
println!("No current selection buffer!");
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
let seat_name =
|
||||
sctk::seat::with_seat_data(seat, |data| data.name.clone()).unwrap();
|
||||
print!("Current selection buffer mime types on seat '{}': [ ", seat_name);
|
||||
let mut has_text = false;
|
||||
offer.with_mime_types(|types| {
|
||||
for t in types {
|
||||
print!("\"{}\", ", t);
|
||||
if t == "text/plain;charset=utf-8" {
|
||||
has_text = true;
|
||||
}
|
||||
}
|
||||
});
|
||||
println!("]");
|
||||
if has_text {
|
||||
println!("Buffer contains text, going to read it...");
|
||||
let reader = offer.receive("text/plain;charset=utf-8".into()).unwrap();
|
||||
let src_handle = handle.clone();
|
||||
let source = handle
|
||||
.insert_source(reader, move |(), file, ddata| {
|
||||
let mut txt = String::new();
|
||||
file.read_to_string(&mut txt).unwrap();
|
||||
println!("Selection contents are: \"{}\"", txt);
|
||||
if let Some(src) = ddata.2.take() {
|
||||
src_handle.remove(src);
|
||||
}
|
||||
})
|
||||
.unwrap();
|
||||
*opt_source = Some(source);
|
||||
}
|
||||
});
|
||||
})
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
if text == "P" && state == KeyState::Pressed {
|
||||
env.with_primary_selection(seat, |primary_selection| {
|
||||
println!("In primary selection closure");
|
||||
primary_selection.with_selection(|offer| {
|
||||
let offer = match offer {
|
||||
Some(offer) => offer,
|
||||
None => {
|
||||
println!("No current primary selection buffer!");
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
let seat_name =
|
||||
sctk::seat::with_seat_data(seat, |data| data.name.clone()).unwrap();
|
||||
print!(
|
||||
"Current primary selection buffer mime type on seat '{}': [ ",
|
||||
seat_name
|
||||
);
|
||||
|
||||
let mut has_text = false;
|
||||
offer.with_mime_types(|types| {
|
||||
for t in types {
|
||||
print!("\"{}\", ", t);
|
||||
if t == "text/plain;charset=utf-8" {
|
||||
has_text = true;
|
||||
}
|
||||
}
|
||||
});
|
||||
println!("]");
|
||||
if has_text {
|
||||
println!("Buffer contains text, going to read it...");
|
||||
let reader = offer.receive("text/plain;charset=utf-8".into()).unwrap();
|
||||
let src_handle = handle.clone();
|
||||
let source = handle
|
||||
.insert_source(reader, move |(), file, ddata| {
|
||||
let mut txt = String::new();
|
||||
file.read_to_string(&mut txt).unwrap();
|
||||
println!("Selection contents are: \"{}\"", txt);
|
||||
if let Some(src) = ddata.2.take() {
|
||||
src_handle.remove(src);
|
||||
}
|
||||
})
|
||||
.unwrap();
|
||||
|
||||
*opt_source = Some(source);
|
||||
}
|
||||
})
|
||||
})
|
||||
.unwrap()
|
||||
}
|
||||
|
||||
if text == "c" && state == KeyState::Pressed {
|
||||
let data_source =
|
||||
env.new_data_source(vec!["text/plain;charset=utf-8".into()], move |event, _| {
|
||||
if let DataSourceEvent::Send { mut pipe, .. } = event {
|
||||
let contents = "Hello from clipboard";
|
||||
println!("Setting clipboard to: {}", &contents);
|
||||
write!(pipe, "{}", contents).unwrap();
|
||||
}
|
||||
});
|
||||
|
||||
env.with_data_device(seat, |device| {
|
||||
println!("Set selection source");
|
||||
device.set_selection(&Some(data_source), serial);
|
||||
})
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
if text == "C" && state == KeyState::Pressed {
|
||||
let data_source = env.new_primary_selection_source(
|
||||
vec!["text/plain;charset=utf-8".into()],
|
||||
move |event, _| {
|
||||
if let PrimarySelectionSourceEvent::Send { mut pipe, .. } = event {
|
||||
let contents = "Hello from primary selection";
|
||||
println!("Setting clipboard primary clipboard to {}", &contents);
|
||||
write!(pipe, "{}", contents).unwrap();
|
||||
}
|
||||
},
|
||||
);
|
||||
|
||||
env.with_primary_selection(seat, |device| {
|
||||
println!("Set primary selection source");
|
||||
device.set_selection(&Some(data_source), serial);
|
||||
})
|
||||
.unwrap();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn redraw(
|
||||
pool: &mut AutoMemPool,
|
||||
surface: &wl_surface::WlSurface,
|
||||
(buf_x, buf_y): (u32, u32),
|
||||
) -> Result<(), ::std::io::Error> {
|
||||
let (canvas, new_buffer) =
|
||||
pool.buffer(buf_x as i32, buf_y as i32, 4 * buf_x as i32, wl_shm::Format::Argb8888)?;
|
||||
for dst_pixel in canvas.chunks_exact_mut(4) {
|
||||
dst_pixel[0] = 0x00;
|
||||
dst_pixel[1] = 0x00;
|
||||
dst_pixel[2] = 0x00;
|
||||
dst_pixel[3] = 0xFF;
|
||||
}
|
||||
surface.attach(Some(&new_buffer), 0, 0);
|
||||
if surface.as_ref().version() >= 4 {
|
||||
surface.damage_buffer(0, 0, buf_x as i32, buf_y as i32);
|
||||
} else {
|
||||
surface.damage(0, 0, buf_x as i32, buf_y as i32);
|
||||
}
|
||||
surface.commit();
|
||||
Ok(())
|
||||
}
|
||||
4
third-party/vendor/smithay-client-toolkit/rustfmt.toml
vendored
Normal file
4
third-party/vendor/smithay-client-toolkit/rustfmt.toml
vendored
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
use_small_heuristics = "Max"
|
||||
use_field_init_shorthand = true
|
||||
newline_style = "Unix"
|
||||
edition = "2018"
|
||||
227
third-party/vendor/smithay-client-toolkit/src/data_device/device.rs
vendored
Normal file
227
third-party/vendor/smithay-client-toolkit/src/data_device/device.rs
vendored
Normal file
|
|
@ -0,0 +1,227 @@
|
|||
use wayland_client::{
|
||||
protocol::{wl_data_device, wl_data_device_manager, wl_data_offer, wl_seat, wl_surface},
|
||||
DispatchData, Main,
|
||||
};
|
||||
|
||||
use std::sync::{Arc, Mutex};
|
||||
|
||||
use super::{DataOffer, DataSource, DndAction};
|
||||
|
||||
#[derive(Debug)]
|
||||
struct Inner {
|
||||
selection: Option<DataOffer>,
|
||||
current_dnd: Option<DataOffer>,
|
||||
known_offers: Vec<DataOffer>,
|
||||
}
|
||||
|
||||
impl Inner {
|
||||
fn new_offer(&mut self, offer: Main<wl_data_offer::WlDataOffer>) {
|
||||
self.known_offers.push(DataOffer::new(offer));
|
||||
}
|
||||
|
||||
fn set_selection(&mut self, offer: Option<wl_data_offer::WlDataOffer>) {
|
||||
if let Some(offer) = offer {
|
||||
if let Some(id) = self.known_offers.iter().position(|o| o.offer == offer) {
|
||||
self.selection = Some(self.known_offers.swap_remove(id));
|
||||
} else {
|
||||
panic!("Compositor set an unknown data_offer for selection.");
|
||||
}
|
||||
} else {
|
||||
// drop the current offer if any
|
||||
self.selection = None;
|
||||
}
|
||||
}
|
||||
|
||||
fn set_dnd(&mut self, offer: Option<wl_data_offer::WlDataOffer>) {
|
||||
if let Some(offer) = offer {
|
||||
if let Some(id) = self.known_offers.iter().position(|o| o.offer == offer) {
|
||||
self.current_dnd = Some(self.known_offers.swap_remove(id));
|
||||
} else {
|
||||
panic!("Compositor set an unknown data_offer for selection.");
|
||||
}
|
||||
} else {
|
||||
// drop the current offer if any
|
||||
self.current_dnd = None;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Handle to support data exchange on a given seat
|
||||
///
|
||||
/// This type provides you with functionality to send and receive
|
||||
/// data through drag'n'drop or copy/paste actions. It is associated
|
||||
/// with a seat upon creation.
|
||||
#[derive(Debug)]
|
||||
pub struct DataDevice {
|
||||
device: wl_data_device::WlDataDevice,
|
||||
inner: Arc<Mutex<Inner>>,
|
||||
}
|
||||
|
||||
/// Possible events generated during a drag'n'drop session
|
||||
#[derive(Debug)]
|
||||
pub enum DndEvent<'a> {
|
||||
/// A new drag'n'drop entered your surfaces
|
||||
Enter {
|
||||
/// The associated data offer
|
||||
///
|
||||
/// Is None if it is an internal drag'n'drop you started with
|
||||
/// no source. See `DataDevice::start_drag` for details.
|
||||
offer: Option<&'a DataOffer>,
|
||||
/// A serial associated with the entry of this dnd
|
||||
serial: u32,
|
||||
/// The entered surface
|
||||
surface: wl_surface::WlSurface,
|
||||
/// horizontal location on the surface
|
||||
x: f64,
|
||||
/// vertical location on the surface
|
||||
y: f64,
|
||||
},
|
||||
/// The drag'n'drop offer moved on the surface
|
||||
Motion {
|
||||
/// The associated data offer
|
||||
///
|
||||
/// Is None if it is an internal drag'n'drop you started with
|
||||
/// no source. See `DataDevice::start_drag` for details.
|
||||
offer: Option<&'a DataOffer>,
|
||||
/// The time of this motion
|
||||
time: u32,
|
||||
/// new horizontal location
|
||||
x: f64,
|
||||
/// new vertical location
|
||||
y: f64,
|
||||
},
|
||||
/// The drag'n'drop offer left your surface
|
||||
Leave,
|
||||
/// The drag'n'drop was dropped on your surface
|
||||
Drop {
|
||||
/// The associated data offer
|
||||
///
|
||||
/// Is None if it is an internal drag'n'drop you started with
|
||||
/// no source. See `DataDevice::start_drag` for details.
|
||||
offer: Option<&'a DataOffer>,
|
||||
},
|
||||
}
|
||||
|
||||
fn data_device_implem<F>(
|
||||
event: wl_data_device::Event,
|
||||
inner: &mut Inner,
|
||||
implem: &mut F,
|
||||
ddata: DispatchData,
|
||||
) where
|
||||
for<'a> F: FnMut(DndEvent<'a>, DispatchData),
|
||||
{
|
||||
use self::wl_data_device::Event;
|
||||
|
||||
match event {
|
||||
Event::DataOffer { id } => inner.new_offer(id),
|
||||
Event::Enter { serial, surface, x, y, id } => {
|
||||
inner.set_dnd(id);
|
||||
implem(
|
||||
DndEvent::Enter { serial, surface, x, y, offer: inner.current_dnd.as_ref() },
|
||||
ddata,
|
||||
);
|
||||
}
|
||||
Event::Motion { time, x, y } => {
|
||||
implem(DndEvent::Motion { x, y, time, offer: inner.current_dnd.as_ref() }, ddata);
|
||||
}
|
||||
Event::Leave => implem(DndEvent::Leave, ddata),
|
||||
Event::Drop => {
|
||||
implem(DndEvent::Drop { offer: inner.current_dnd.as_ref() }, ddata);
|
||||
}
|
||||
Event::Selection { id } => inner.set_selection(id),
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
|
||||
impl DataDevice {
|
||||
/// Create the DataDevice helper for this seat.
|
||||
///
|
||||
/// You need to provide an implementation that will handle drag'n'drop
|
||||
/// events.
|
||||
pub fn init_for_seat<F>(
|
||||
manager: &wl_data_device_manager::WlDataDeviceManager,
|
||||
seat: &wl_seat::WlSeat,
|
||||
mut callback: F,
|
||||
) -> DataDevice
|
||||
where
|
||||
for<'a> F: FnMut(DndEvent<'a>, DispatchData) + 'static,
|
||||
{
|
||||
let inner = Arc::new(Mutex::new(Inner {
|
||||
selection: None,
|
||||
current_dnd: None,
|
||||
known_offers: Vec::new(),
|
||||
}));
|
||||
|
||||
let inner2 = inner.clone();
|
||||
let device = manager.get_data_device(seat);
|
||||
device.quick_assign(move |_, evt, ddata| {
|
||||
let mut inner = inner2.lock().unwrap();
|
||||
data_device_implem(evt, &mut *inner, &mut callback, ddata);
|
||||
});
|
||||
|
||||
DataDevice { device: device.detach(), inner }
|
||||
}
|
||||
|
||||
/// Start a drag'n'drop offer
|
||||
///
|
||||
/// You need to specify the origin surface, as well a serial associated
|
||||
/// to an implicit grab on this surface (for example received by a pointer click).
|
||||
///
|
||||
/// An optional `DataSource` can be provided. If it is `None`, this drag'n'drop will
|
||||
/// be considered as internal to your application, and other applications will not be
|
||||
/// notified of it. You are then responsible for acting accordingly on drop.
|
||||
///
|
||||
/// You also need to specify which possible drag'n'drop actions are associated to this
|
||||
/// drag (copy, move, or ask), the final action will be chosen by the target and/or
|
||||
/// compositor.
|
||||
///
|
||||
/// You can finally provide a surface that will be used as an icon associated with
|
||||
/// this drag'n'drop for user visibility.
|
||||
pub fn start_drag(
|
||||
&self,
|
||||
origin: &wl_surface::WlSurface,
|
||||
source: Option<DataSource>,
|
||||
actions: DndAction,
|
||||
icon: Option<&wl_surface::WlSurface>,
|
||||
serial: u32,
|
||||
) {
|
||||
if let Some(source) = source {
|
||||
source.source.set_actions(actions);
|
||||
self.device.start_drag(Some(&source.source), origin, icon, serial);
|
||||
} else {
|
||||
self.device.start_drag(None, origin, icon, serial);
|
||||
}
|
||||
}
|
||||
|
||||
/// Provide a data source as the new content for the selection
|
||||
///
|
||||
/// Correspond to traditional copy/paste behavior. Setting the
|
||||
/// source to `None` will clear the selection.
|
||||
pub fn set_selection(&self, source: &Option<DataSource>, serial: u32) {
|
||||
self.device.set_selection(source.as_ref().map(|s| &s.source), serial);
|
||||
}
|
||||
|
||||
/// Access the `DataOffer` currently associated with the selection buffer
|
||||
pub fn with_selection<F, T>(&self, f: F) -> T
|
||||
where
|
||||
F: FnOnce(Option<&DataOffer>) -> T,
|
||||
{
|
||||
let inner = self.inner.lock().unwrap();
|
||||
f(inner.selection.as_ref())
|
||||
}
|
||||
|
||||
/// Access the `DataOffer` currently associated with current DnD
|
||||
pub fn with_dnd<F, T>(&self, f: F) -> T
|
||||
where
|
||||
F: FnOnce(Option<&DataOffer>) -> T,
|
||||
{
|
||||
let inner = self.inner.lock().unwrap();
|
||||
f(inner.current_dnd.as_ref())
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for DataDevice {
|
||||
fn drop(&mut self) {
|
||||
self.device.release();
|
||||
}
|
||||
}
|
||||
294
third-party/vendor/smithay-client-toolkit/src/data_device/mod.rs
vendored
Normal file
294
third-party/vendor/smithay-client-toolkit/src/data_device/mod.rs
vendored
Normal file
|
|
@ -0,0 +1,294 @@
|
|||
//! Helpers to handle data device related actions
|
||||
|
||||
use std::{cell::RefCell, fmt, rc::Rc};
|
||||
|
||||
use wayland_client::{
|
||||
protocol::{wl_data_device_manager, wl_registry, wl_seat},
|
||||
Attached, DispatchData,
|
||||
};
|
||||
|
||||
pub use wayland_client::protocol::wl_data_device_manager::DndAction;
|
||||
|
||||
use crate::MissingGlobal;
|
||||
|
||||
mod device;
|
||||
mod offer;
|
||||
mod source;
|
||||
|
||||
pub use self::device::{DataDevice, DndEvent};
|
||||
pub use self::offer::{DataOffer, ReadPipe};
|
||||
pub use self::source::{DataSource, DataSourceEvent, WritePipe};
|
||||
|
||||
type DDCallback = dyn FnMut(wl_seat::WlSeat, DndEvent, DispatchData);
|
||||
|
||||
enum DDInner {
|
||||
Ready {
|
||||
mgr: Attached<wl_data_device_manager::WlDataDeviceManager>,
|
||||
devices: Vec<(wl_seat::WlSeat, DataDevice)>,
|
||||
callback: Rc<RefCell<Box<DDCallback>>>,
|
||||
},
|
||||
Pending {
|
||||
seats: Vec<wl_seat::WlSeat>,
|
||||
},
|
||||
}
|
||||
|
||||
impl DDInner {
|
||||
fn init_dd_mgr(&mut self, mgr: Attached<wl_data_device_manager::WlDataDeviceManager>) {
|
||||
let seats = if let DDInner::Pending { seats } = self {
|
||||
::std::mem::take(seats)
|
||||
} else {
|
||||
log::warn!("Ignoring second wl_data_device_manager.");
|
||||
return;
|
||||
};
|
||||
|
||||
let mut devices = Vec::new();
|
||||
|
||||
let callback = Rc::new(RefCell::new(Box::new(|_, _: DndEvent, _: DispatchData| {})
|
||||
as Box<dyn FnMut(_, DndEvent, DispatchData)>));
|
||||
|
||||
for seat in seats {
|
||||
let cb = callback.clone();
|
||||
let my_seat = seat.clone();
|
||||
let device = DataDevice::init_for_seat(&mgr, &seat, move |event, dispatch_data| {
|
||||
(cb.borrow_mut())(my_seat.clone(), event, dispatch_data);
|
||||
});
|
||||
devices.push((seat.clone(), device));
|
||||
}
|
||||
|
||||
*self = DDInner::Ready { mgr, devices, callback };
|
||||
}
|
||||
|
||||
// A potential new seat is seen
|
||||
//
|
||||
// should do nothing if the seat is already known
|
||||
fn new_seat(&mut self, seat: &wl_seat::WlSeat) {
|
||||
match self {
|
||||
DDInner::Ready { mgr, devices, callback } => {
|
||||
if devices.iter().any(|(s, _)| s == seat) {
|
||||
// the seat already exists, nothing to do
|
||||
return;
|
||||
}
|
||||
let cb = callback.clone();
|
||||
let my_seat = seat.clone();
|
||||
let device = DataDevice::init_for_seat(mgr, seat, move |event, dispatch_data| {
|
||||
(cb.borrow_mut())(my_seat.clone(), event, dispatch_data);
|
||||
});
|
||||
devices.push((seat.clone(), device));
|
||||
}
|
||||
DDInner::Pending { seats } => {
|
||||
seats.push(seat.clone());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn remove_seat(&mut self, seat: &wl_seat::WlSeat) {
|
||||
match self {
|
||||
DDInner::Ready { devices, .. } => devices.retain(|(s, _)| s != seat),
|
||||
DDInner::Pending { seats } => seats.retain(|s| s != seat),
|
||||
}
|
||||
}
|
||||
|
||||
fn get_mgr(&self) -> Option<Attached<wl_data_device_manager::WlDataDeviceManager>> {
|
||||
match self {
|
||||
DDInner::Ready { mgr, .. } => Some(mgr.clone()),
|
||||
DDInner::Pending { .. } => None,
|
||||
}
|
||||
}
|
||||
|
||||
fn set_callback<F: FnMut(wl_seat::WlSeat, DndEvent, DispatchData) + 'static>(
|
||||
&mut self,
|
||||
cb: F,
|
||||
) -> Result<(), MissingGlobal> {
|
||||
match self {
|
||||
DDInner::Ready { callback, .. } => {
|
||||
*(callback.borrow_mut()) = Box::new(cb);
|
||||
Ok(())
|
||||
}
|
||||
DDInner::Pending { .. } => Err(MissingGlobal),
|
||||
}
|
||||
}
|
||||
|
||||
fn with_device<F: FnOnce(&DataDevice)>(
|
||||
&self,
|
||||
seat: &wl_seat::WlSeat,
|
||||
f: F,
|
||||
) -> Result<(), MissingGlobal> {
|
||||
match self {
|
||||
DDInner::Pending { .. } => Err(MissingGlobal),
|
||||
DDInner::Ready { devices, .. } => {
|
||||
for (s, device) in devices {
|
||||
if s == seat {
|
||||
f(device);
|
||||
return Ok(());
|
||||
}
|
||||
}
|
||||
Err(MissingGlobal)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Debug for DDInner {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
match self {
|
||||
Self::Ready { mgr, devices, .. } => f
|
||||
.debug_struct("Ready")
|
||||
.field("mgr", mgr)
|
||||
.field("devices", devices)
|
||||
.field("callback", &"Fn() -> { ... }")
|
||||
.finish(),
|
||||
Self::Pending { seats } => f.debug_struct("Pending").field("seats", seats).finish(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// A handler for data devices
|
||||
///
|
||||
/// It provides automatic tracking of data device for each available seat,
|
||||
/// allowing you to manipulate selection clipboard and drag&drop manipulations.
|
||||
///
|
||||
/// It is automatically included in the [`default_environment!`](../macro.default_environment.html).
|
||||
#[derive(Debug)]
|
||||
pub struct DataDeviceHandler {
|
||||
inner: Rc<RefCell<DDInner>>,
|
||||
_listener: crate::seat::SeatListener,
|
||||
}
|
||||
|
||||
impl DataDeviceHandler {
|
||||
/// Initialize a data device handler
|
||||
///
|
||||
/// It needs access to a seat handler in order to track
|
||||
/// the creation and removal of seats.
|
||||
pub fn init<S>(seat_handler: &mut S) -> DataDeviceHandler
|
||||
where
|
||||
S: crate::seat::SeatHandling,
|
||||
{
|
||||
let inner = Rc::new(RefCell::new(DDInner::Pending { seats: Vec::new() }));
|
||||
|
||||
let seat_inner = inner.clone();
|
||||
let listener = seat_handler.listen(move |seat, seat_data, _| {
|
||||
if seat_data.defunct {
|
||||
seat_inner.borrow_mut().remove_seat(&seat);
|
||||
} else {
|
||||
seat_inner.borrow_mut().new_seat(&seat)
|
||||
}
|
||||
});
|
||||
|
||||
DataDeviceHandler { inner, _listener: listener }
|
||||
}
|
||||
}
|
||||
|
||||
impl crate::environment::GlobalHandler<wl_data_device_manager::WlDataDeviceManager>
|
||||
for DataDeviceHandler
|
||||
{
|
||||
fn created(
|
||||
&mut self,
|
||||
registry: Attached<wl_registry::WlRegistry>,
|
||||
id: u32,
|
||||
version: u32,
|
||||
_: DispatchData,
|
||||
) {
|
||||
// data device manager is supported until version 3
|
||||
let version = std::cmp::min(version, 3);
|
||||
let ddmgr = registry.bind::<wl_data_device_manager::WlDataDeviceManager>(version, id);
|
||||
self.inner.borrow_mut().init_dd_mgr((*ddmgr).clone());
|
||||
}
|
||||
fn get(&self) -> Option<Attached<wl_data_device_manager::WlDataDeviceManager>> {
|
||||
self.inner.borrow().get_mgr()
|
||||
}
|
||||
}
|
||||
|
||||
/// An interface trait to forward the data device handler capability
|
||||
///
|
||||
/// You need to implement this trait for your environment struct, by
|
||||
/// delegating it to its `DataDeviceHandler` field in order to get the
|
||||
/// associated methods on your [`Environment`](../environment/struct.environment.html).
|
||||
pub trait DataDeviceHandling {
|
||||
/// Set the global drag'n'drop callback
|
||||
///
|
||||
/// Returns an error if the `wl_data_device_manager` global is missing.
|
||||
fn set_callback<F: FnMut(wl_seat::WlSeat, DndEvent, DispatchData) + 'static>(
|
||||
&mut self,
|
||||
callback: F,
|
||||
) -> Result<(), MissingGlobal>;
|
||||
|
||||
/// Access the data device associated with a seat
|
||||
///
|
||||
/// Returns an error if the seat is not found (for example if it has since been removed by
|
||||
/// the server) or if the `wl_data_device_manager` global is missing.
|
||||
fn with_device<F: FnOnce(&DataDevice)>(
|
||||
&self,
|
||||
seat: &wl_seat::WlSeat,
|
||||
f: F,
|
||||
) -> Result<(), MissingGlobal>;
|
||||
}
|
||||
|
||||
impl DataDeviceHandling for DataDeviceHandler {
|
||||
fn set_callback<F: FnMut(wl_seat::WlSeat, DndEvent, DispatchData) + 'static>(
|
||||
&mut self,
|
||||
callback: F,
|
||||
) -> Result<(), MissingGlobal> {
|
||||
self.inner.borrow_mut().set_callback(callback)
|
||||
}
|
||||
|
||||
fn with_device<F: FnOnce(&DataDevice)>(
|
||||
&self,
|
||||
seat: &wl_seat::WlSeat,
|
||||
f: F,
|
||||
) -> Result<(), MissingGlobal> {
|
||||
self.inner.borrow().with_device(seat, f)
|
||||
}
|
||||
}
|
||||
|
||||
impl<E> crate::environment::Environment<E>
|
||||
where
|
||||
E: crate::environment::GlobalHandler<wl_data_device_manager::WlDataDeviceManager>,
|
||||
{
|
||||
/// Create a new data source
|
||||
///
|
||||
/// This data source is the basic object for offering content to other clients,
|
||||
/// be it for clipboard selection or as drag'n'drop content.
|
||||
///
|
||||
/// Once this source is created, you will need to give it to a
|
||||
/// [`DataDevice`](../data_device/struct.DataDevice.html)
|
||||
/// to start interaction.
|
||||
pub fn new_data_source<F>(&self, mime_types: Vec<String>, callback: F) -> DataSource
|
||||
where
|
||||
F: FnMut(DataSourceEvent, DispatchData) + 'static,
|
||||
{
|
||||
let ddmgr = self.require_global::<wl_data_device_manager::WlDataDeviceManager>();
|
||||
DataSource::new(&ddmgr, mime_types, callback)
|
||||
}
|
||||
}
|
||||
|
||||
impl<E> crate::environment::Environment<E>
|
||||
where
|
||||
E: DataDeviceHandling,
|
||||
{
|
||||
/// Set the data device callback
|
||||
///
|
||||
/// This callback will be invoked whenever some drag'n'drop action is done onto one of
|
||||
/// your surfaces.
|
||||
///
|
||||
/// You should set it before entering your main loop, to ensure you will not miss any events.
|
||||
///
|
||||
/// Returns an error if the compositor did not advertise a data device capability.
|
||||
pub fn set_data_device_callback<F: FnMut(wl_seat::WlSeat, DndEvent, DispatchData) + 'static>(
|
||||
&mut self,
|
||||
callback: F,
|
||||
) -> Result<(), MissingGlobal> {
|
||||
self.with_inner(|inner| inner.set_callback(callback))
|
||||
}
|
||||
|
||||
/// Access the data device associated with a seat
|
||||
///
|
||||
/// Returns an error if the seat is not found (for example if it has since been removed by
|
||||
/// the server) or if the `wl_data_device_manager` global is missing.
|
||||
pub fn with_data_device<F: FnOnce(&DataDevice)>(
|
||||
&self,
|
||||
seat: &wl_seat::WlSeat,
|
||||
f: F,
|
||||
) -> Result<(), MissingGlobal> {
|
||||
self.with_inner(|inner| inner.with_device(seat, f))
|
||||
}
|
||||
}
|
||||
274
third-party/vendor/smithay-client-toolkit/src/data_device/offer.rs
vendored
Normal file
274
third-party/vendor/smithay-client-toolkit/src/data_device/offer.rs
vendored
Normal file
|
|
@ -0,0 +1,274 @@
|
|||
use std::{
|
||||
fs, io,
|
||||
os::unix::io::{AsRawFd, FromRawFd, IntoRawFd, RawFd},
|
||||
sync::{Arc, Mutex},
|
||||
};
|
||||
|
||||
use wayland_client::protocol::wl_data_device_manager::DndAction;
|
||||
use wayland_client::protocol::wl_data_offer;
|
||||
use wayland_client::Main;
|
||||
|
||||
#[derive(Debug)]
|
||||
struct Inner {
|
||||
mime_types: Vec<String>,
|
||||
actions: DndAction,
|
||||
current_action: DndAction,
|
||||
serial: u32,
|
||||
}
|
||||
|
||||
/// A data offer for receiving data though copy/paste or
|
||||
/// drag and drop
|
||||
#[derive(Debug)]
|
||||
pub struct DataOffer {
|
||||
pub(crate) offer: wl_data_offer::WlDataOffer,
|
||||
inner: Arc<Mutex<Inner>>,
|
||||
}
|
||||
|
||||
impl DataOffer {
|
||||
pub(crate) fn new(offer: Main<wl_data_offer::WlDataOffer>) -> DataOffer {
|
||||
let inner = Arc::new(Mutex::new(Inner {
|
||||
mime_types: Vec::new(),
|
||||
actions: DndAction::None,
|
||||
current_action: DndAction::None,
|
||||
serial: 0,
|
||||
}));
|
||||
let inner2 = inner.clone();
|
||||
offer.quick_assign(move |_, event, _| {
|
||||
use self::wl_data_offer::Event;
|
||||
let mut inner = inner2.lock().unwrap();
|
||||
match event {
|
||||
Event::Offer { mime_type } => {
|
||||
inner.mime_types.push(mime_type);
|
||||
}
|
||||
Event::SourceActions { source_actions } => {
|
||||
inner.actions = source_actions;
|
||||
}
|
||||
Event::Action { dnd_action } => {
|
||||
inner.current_action = dnd_action;
|
||||
}
|
||||
_ => unreachable!(),
|
||||
}
|
||||
});
|
||||
|
||||
DataOffer { offer: offer.detach(), inner }
|
||||
}
|
||||
|
||||
/// Access the list of mime types proposed by this offer
|
||||
pub fn with_mime_types<F, T>(&self, f: F) -> T
|
||||
where
|
||||
F: FnOnce(&[String]) -> T,
|
||||
{
|
||||
let inner = self.inner.lock().unwrap();
|
||||
f(&inner.mime_types)
|
||||
}
|
||||
|
||||
/// Get the list of available actions for this offer
|
||||
pub fn get_available_actions(&self) -> DndAction {
|
||||
self.inner.lock().unwrap().actions
|
||||
}
|
||||
|
||||
/// Get the currently set final action for this offer
|
||||
pub fn get_current_action(&self) -> DndAction {
|
||||
self.inner.lock().unwrap().current_action
|
||||
}
|
||||
|
||||
/// Accept a mime type for receiving data through this offer
|
||||
pub fn accept(&self, mime_type: Option<String>) {
|
||||
let serial = self.inner.lock().unwrap().serial;
|
||||
self.offer.accept(serial, mime_type);
|
||||
}
|
||||
|
||||
/// Request to receive the data of a given mime type
|
||||
///
|
||||
/// You can do this several times, as a reaction to motion of
|
||||
/// the dnd cursor, or to inspect the data in order to choose your
|
||||
/// response.
|
||||
///
|
||||
/// Note that you should *not* read the contents right away in a
|
||||
/// blocking way, as you may deadlock your application doing so.
|
||||
/// At least make sure you flush your events to the server before
|
||||
/// doing so.
|
||||
///
|
||||
/// Fails if too many file descriptors were already open and a pipe
|
||||
/// could not be created.
|
||||
pub fn receive(&self, mime_type: String) -> std::io::Result<ReadPipe> {
|
||||
use nix::fcntl::OFlag;
|
||||
use nix::unistd::{close, pipe2};
|
||||
// create a pipe
|
||||
let (readfd, writefd) = pipe2(OFlag::O_CLOEXEC)?;
|
||||
|
||||
self.offer.receive(mime_type, writefd);
|
||||
|
||||
if let Err(err) = close(writefd) {
|
||||
log::warn!("Failed to close write pipe: {}", err);
|
||||
}
|
||||
|
||||
Ok(unsafe { FromRawFd::from_raw_fd(readfd) })
|
||||
}
|
||||
|
||||
/// Receive data to the write end of a raw file descriptor. If you have the read end, you can read from it.
|
||||
///
|
||||
/// You can do this several times, as a reaction to motion of
|
||||
/// the dnd cursor, or to inspect the data in order to choose your
|
||||
/// response.
|
||||
///
|
||||
/// Note that you should *not* read the contents right away in a
|
||||
/// blocking way, as you may deadlock your application doing so.
|
||||
/// At least make sure you flush your events to the server before
|
||||
/// doing so.
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// The provided file destructor must be a valid FD for writing, and will be closed
|
||||
/// once the contents are written.
|
||||
pub unsafe fn receive_to_fd(&self, mime_type: String, writefd: RawFd) {
|
||||
use nix::unistd::close;
|
||||
|
||||
self.offer.receive(mime_type, writefd);
|
||||
|
||||
if let Err(err) = close(writefd) {
|
||||
log::warn!("Failed to close write pipe: {}", err);
|
||||
}
|
||||
}
|
||||
|
||||
/// Notify the send and compositor of the dnd actions you accept
|
||||
///
|
||||
/// You need to provide the set of supported actions, as well as
|
||||
/// a single preferred action.
|
||||
pub fn set_actions(&self, supported: DndAction, preferred: DndAction) {
|
||||
self.offer.set_actions(supported, preferred);
|
||||
}
|
||||
|
||||
/// Notify that you are finished with this offer, and will no longer
|
||||
/// be using it
|
||||
///
|
||||
/// Note that it is a protocol error to finish if no action or mime
|
||||
/// type was accepted.
|
||||
pub fn finish(&self) {
|
||||
self.offer.finish();
|
||||
self.offer.destroy();
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for DataOffer {
|
||||
fn drop(&mut self) {
|
||||
self.offer.destroy();
|
||||
}
|
||||
}
|
||||
|
||||
/// A file descriptor that can only be read from
|
||||
///
|
||||
/// If the `calloop` cargo feature is enabled, this can be used
|
||||
/// as an `EventSource` in a calloop event loop.
|
||||
#[derive(Debug)]
|
||||
pub struct ReadPipe {
|
||||
#[cfg(feature = "calloop")]
|
||||
file: calloop::generic::Generic<fs::File>,
|
||||
#[cfg(not(feature = "calloop"))]
|
||||
file: fs::File,
|
||||
}
|
||||
|
||||
#[cfg(feature = "calloop")]
|
||||
impl io::Read for ReadPipe {
|
||||
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
|
||||
self.file.file.read(buf)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "calloop"))]
|
||||
impl io::Read for ReadPipe {
|
||||
fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
|
||||
self.file.read(buf)
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "calloop")]
|
||||
impl FromRawFd for ReadPipe {
|
||||
unsafe fn from_raw_fd(fd: RawFd) -> ReadPipe {
|
||||
ReadPipe {
|
||||
file: calloop::generic::Generic::new(
|
||||
FromRawFd::from_raw_fd(fd),
|
||||
calloop::Interest::READ,
|
||||
calloop::Mode::Level,
|
||||
),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "calloop"))]
|
||||
impl FromRawFd for ReadPipe {
|
||||
unsafe fn from_raw_fd(fd: RawFd) -> ReadPipe {
|
||||
ReadPipe { file: FromRawFd::from_raw_fd(fd) }
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "calloop")]
|
||||
impl AsRawFd for ReadPipe {
|
||||
fn as_raw_fd(&self) -> RawFd {
|
||||
self.file.file.as_raw_fd()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "calloop"))]
|
||||
impl AsRawFd for ReadPipe {
|
||||
fn as_raw_fd(&self) -> RawFd {
|
||||
self.file.as_raw_fd()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "calloop")]
|
||||
impl IntoRawFd for ReadPipe {
|
||||
fn into_raw_fd(self) -> RawFd {
|
||||
self.file.file.into_raw_fd()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(not(feature = "calloop"))]
|
||||
impl IntoRawFd for ReadPipe {
|
||||
fn into_raw_fd(self) -> RawFd {
|
||||
self.file.into_raw_fd()
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "calloop")]
|
||||
impl calloop::EventSource for ReadPipe {
|
||||
type Event = ();
|
||||
type Error = std::io::Error;
|
||||
type Metadata = fs::File;
|
||||
type Ret = ();
|
||||
|
||||
fn process_events<F>(
|
||||
&mut self,
|
||||
readiness: calloop::Readiness,
|
||||
token: calloop::Token,
|
||||
mut callback: F,
|
||||
) -> std::io::Result<calloop::PostAction>
|
||||
where
|
||||
F: FnMut((), &mut fs::File),
|
||||
{
|
||||
self.file.process_events(readiness, token, |_, file| {
|
||||
callback((), file);
|
||||
Ok(calloop::PostAction::Continue)
|
||||
})
|
||||
}
|
||||
|
||||
fn register(
|
||||
&mut self,
|
||||
poll: &mut calloop::Poll,
|
||||
token_factory: &mut calloop::TokenFactory,
|
||||
) -> calloop::Result<()> {
|
||||
self.file.register(poll, token_factory)
|
||||
}
|
||||
|
||||
fn reregister(
|
||||
&mut self,
|
||||
poll: &mut calloop::Poll,
|
||||
token_factory: &mut calloop::TokenFactory,
|
||||
) -> calloop::Result<()> {
|
||||
self.file.reregister(poll, token_factory)
|
||||
}
|
||||
|
||||
fn unregister(&mut self, poll: &mut calloop::Poll) -> calloop::Result<()> {
|
||||
self.file.unregister(poll)
|
||||
}
|
||||
}
|
||||
170
third-party/vendor/smithay-client-toolkit/src/data_device/source.rs
vendored
Normal file
170
third-party/vendor/smithay-client-toolkit/src/data_device/source.rs
vendored
Normal file
|
|
@ -0,0 +1,170 @@
|
|||
use wayland_client::{
|
||||
protocol::{wl_data_device_manager, wl_data_source},
|
||||
Attached, DispatchData,
|
||||
};
|
||||
|
||||
use std::os::unix::io::{AsRawFd, FromRawFd, IntoRawFd, RawFd};
|
||||
use std::{fs, io};
|
||||
|
||||
/// A data source for sending data though copy/paste or
|
||||
/// drag and drop
|
||||
#[derive(Debug)]
|
||||
pub struct DataSource {
|
||||
pub(crate) source: wl_data_source::WlDataSource,
|
||||
}
|
||||
|
||||
/// Possible events a data source needs to react to
|
||||
#[derive(Debug)]
|
||||
pub enum DataSourceEvent {
|
||||
/// Write the offered data for selected mime type
|
||||
///
|
||||
/// This can happen several times during a dnd setup,
|
||||
/// and does not mean the action is finished.
|
||||
Send {
|
||||
/// Requested mime type
|
||||
mime_type: String,
|
||||
/// Pipe to write into
|
||||
pipe: WritePipe,
|
||||
},
|
||||
/// Target mime type
|
||||
///
|
||||
/// Notifies that the target accepted a given mime type.
|
||||
/// You can use it to provide feedback (changing the icon
|
||||
/// of the drag'n'drop for example).
|
||||
///
|
||||
/// Can be `None` if the current target does not accept any of the
|
||||
/// proposed mime types.
|
||||
///
|
||||
/// This event can be emitted several times during the process
|
||||
Target {
|
||||
/// The type accepted by the target
|
||||
mime_type: Option<String>,
|
||||
},
|
||||
/// Notifies of the current selected action for the drag'n'drop
|
||||
///
|
||||
/// Can only happen for data sources used during a drag'n'drop.
|
||||
///
|
||||
/// This can change several times, the last received defines which action
|
||||
/// should actually be taken.
|
||||
Action {
|
||||
/// The action chosen by the target
|
||||
action: wl_data_device_manager::DndAction,
|
||||
},
|
||||
/// The action using this data source was cancelled.
|
||||
///
|
||||
/// Once this event is received, the `DataSource` can not be used any more,
|
||||
/// and you should drop it for cleanup.
|
||||
///
|
||||
/// Happens if the user cancels the current drag'n'drop, or replaces the
|
||||
/// selection buffer.
|
||||
Cancelled,
|
||||
/// The user performed the "drop" during a drag'n'drop
|
||||
///
|
||||
/// This does not mean the operation is finished (the operation can still
|
||||
/// be cancelled afterwards).
|
||||
///
|
||||
/// You are not guaranteed to receive this event at some point, as the compositor
|
||||
/// may cancel the action before the user drops.
|
||||
///
|
||||
/// This event can only be generated on sources used for drag'n'drop, not
|
||||
/// selection sources.
|
||||
Dropped,
|
||||
/// The action is finished, this data source will not be used any more
|
||||
///
|
||||
/// If the selected drag'n'drop action was "move", you can now delete the
|
||||
/// underlying resource.
|
||||
///
|
||||
/// This event can only be generated on sources used for drag'n'drop, not
|
||||
/// selection sources.
|
||||
Finished,
|
||||
}
|
||||
|
||||
fn data_source_impl<Impl>(
|
||||
evt: wl_data_source::Event,
|
||||
source: &wl_data_source::WlDataSource,
|
||||
implem: &mut Impl,
|
||||
ddata: DispatchData,
|
||||
) where
|
||||
Impl: FnMut(DataSourceEvent, DispatchData),
|
||||
{
|
||||
use self::wl_data_source::Event;
|
||||
let event = match evt {
|
||||
Event::Target { mime_type } => DataSourceEvent::Target { mime_type },
|
||||
Event::Send { mime_type, fd } => {
|
||||
DataSourceEvent::Send { mime_type, pipe: unsafe { FromRawFd::from_raw_fd(fd) } }
|
||||
}
|
||||
Event::Action { dnd_action } => DataSourceEvent::Action { action: dnd_action },
|
||||
Event::Cancelled => {
|
||||
source.destroy();
|
||||
DataSourceEvent::Cancelled
|
||||
}
|
||||
Event::DndDropPerformed => DataSourceEvent::Dropped,
|
||||
Event::DndFinished => {
|
||||
source.destroy();
|
||||
DataSourceEvent::Finished
|
||||
}
|
||||
_ => unreachable!(),
|
||||
};
|
||||
implem(event, ddata);
|
||||
}
|
||||
|
||||
impl DataSource {
|
||||
/// Create a new data source
|
||||
///
|
||||
/// You'll then need to provide it to a data device to send it
|
||||
/// either via selection (aka copy/paste) or via a drag and drop.
|
||||
pub fn new<F, S, It>(
|
||||
mgr: &Attached<wl_data_device_manager::WlDataDeviceManager>,
|
||||
mime_types: It,
|
||||
mut callback: F,
|
||||
) -> DataSource
|
||||
where
|
||||
F: FnMut(DataSourceEvent, DispatchData) + 'static,
|
||||
S: Into<String>,
|
||||
It: IntoIterator<Item = S>,
|
||||
{
|
||||
let source = mgr.create_data_source();
|
||||
source.quick_assign(move |source, evt, dispatch_data| {
|
||||
data_source_impl(evt, &source, &mut callback, dispatch_data)
|
||||
});
|
||||
|
||||
for mime in mime_types {
|
||||
source.offer(mime.into());
|
||||
}
|
||||
|
||||
DataSource { source: source.detach() }
|
||||
}
|
||||
}
|
||||
|
||||
/// A file descriptor that can only be written to
|
||||
#[derive(Debug)]
|
||||
pub struct WritePipe {
|
||||
file: fs::File,
|
||||
}
|
||||
|
||||
impl io::Write for WritePipe {
|
||||
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
|
||||
self.file.write(buf)
|
||||
}
|
||||
fn flush(&mut self) -> io::Result<()> {
|
||||
self.file.flush()
|
||||
}
|
||||
}
|
||||
|
||||
impl FromRawFd for WritePipe {
|
||||
unsafe fn from_raw_fd(fd: RawFd) -> WritePipe {
|
||||
WritePipe { file: FromRawFd::from_raw_fd(fd) }
|
||||
}
|
||||
}
|
||||
|
||||
impl AsRawFd for WritePipe {
|
||||
fn as_raw_fd(&self) -> RawFd {
|
||||
self.file.as_raw_fd()
|
||||
}
|
||||
}
|
||||
|
||||
impl IntoRawFd for WritePipe {
|
||||
fn into_raw_fd(self) -> RawFd {
|
||||
self.file.into_raw_fd()
|
||||
}
|
||||
}
|
||||
395
third-party/vendor/smithay-client-toolkit/src/environment.rs
vendored
Normal file
395
third-party/vendor/smithay-client-toolkit/src/environment.rs
vendored
Normal file
|
|
@ -0,0 +1,395 @@
|
|||
//! Environment management utilities
|
||||
//!
|
||||
//! This module provide the tools to automatically bind the wayland global objects you need in your program.
|
||||
//!
|
||||
//! At the heart of this is the `environment!` macro, which allows you to signal the globals you need
|
||||
//! and a struct to manage them as they are signaled in the registry.
|
||||
//!
|
||||
//! ## Global handlers
|
||||
//!
|
||||
//! Wayland globals are split in two kinds, that we will call here "single" globals and "multi" globals.
|
||||
//!
|
||||
//! - "single" globals represent a capability of the server. They are generally signaled in the registry
|
||||
//! from the start and never removed. They are signaled a single time. Examples of these globals are
|
||||
//! `wl_compositor`, `wl_shm` or `xdg_wm_base`.
|
||||
//! - "multi" globals represent a resource that the server gives you access to. These globals can be
|
||||
//! created or removed during the run of the program, and may exist as more than one instance, each
|
||||
//! representing a different physical resource. Examples of such globals are `wl_output` or `wl_seat`.
|
||||
//!
|
||||
//! The objects you need to handle these globals must implement one the two traits
|
||||
//! [`GlobalHandler<I>`](trait.GlobalHandler.html) or [`MultiGlobalHandler<I>`](trait.MultiGlobalHandler.html),
|
||||
//! depending on the kind of globals it will handle. These objects are responsible for binding the globals
|
||||
//! from the registry, and assigning them to filters to receive their events as necessary.
|
||||
//!
|
||||
//! This module provides a generic implementation of the [`GlobalHandler<I>`](trait.GlobalHandler.html) trait
|
||||
//! as [`SimpleGlobal<I>`](struct.SimpleGlobal.html). It can manage "single" globals that do not generate
|
||||
//! events, and thus require no filter.
|
||||
//!
|
||||
//! ## the `environment!` macro
|
||||
//!
|
||||
//! This macro is at the core of this module. See its documentation for details about how to
|
||||
//! use it: [`environment!`](../macro.environment.html). You can alternatively use the
|
||||
//! [`default_environment!`](../macro.default_environment.html) macro to quickly setup things and bring
|
||||
//! in all SCTK modules.
|
||||
|
||||
use std::io::Result;
|
||||
use std::rc::Rc;
|
||||
use std::{cell::RefCell, fmt};
|
||||
|
||||
use wayland_client::{
|
||||
protocol::{wl_display, wl_registry},
|
||||
Attached, DispatchData, EventQueue, GlobalEvent, GlobalManager, Interface, Proxy,
|
||||
};
|
||||
|
||||
/*
|
||||
* Traits definitions
|
||||
*/
|
||||
|
||||
/// Required trait for implementing a handler for "single" globals
|
||||
pub trait GlobalHandler<I: Interface> {
|
||||
/// This global was created and signaled in the registry with given id and version
|
||||
fn created(
|
||||
&mut self,
|
||||
registry: Attached<wl_registry::WlRegistry>,
|
||||
id: u32,
|
||||
version: u32,
|
||||
ddata: DispatchData,
|
||||
);
|
||||
/// Access the global if it was signaled
|
||||
fn get(&self) -> Option<Attached<I>>;
|
||||
}
|
||||
|
||||
/// Required trait for implementing a handler for "multi" globals
|
||||
pub trait MultiGlobalHandler<I: Interface> {
|
||||
/// A new instance of this global was created with given id and version
|
||||
fn created(
|
||||
&mut self,
|
||||
registry: Attached<wl_registry::WlRegistry>,
|
||||
id: u32,
|
||||
version: u32,
|
||||
ddata: DispatchData,
|
||||
);
|
||||
/// The instance with given id was removed
|
||||
fn removed(&mut self, id: u32, ddata: DispatchData);
|
||||
/// Access all the currently existing instances
|
||||
fn get_all(&self) -> Vec<Attached<I>>;
|
||||
}
|
||||
|
||||
/*
|
||||
* General Environment<E>
|
||||
*/
|
||||
|
||||
/// A Wayland Environment
|
||||
///
|
||||
/// This struct is generated by the `environment!` macro, see module-level documentation
|
||||
/// for more details about this.
|
||||
///
|
||||
/// This is the central point for accessing globals for your Wayland app. Any global that has
|
||||
/// previously been declared in the `environment!` macro can be access from this type via the
|
||||
/// `get_global`, `required_global` and `get_all_globals` methods.
|
||||
///
|
||||
/// This `Environment` is a handle that can be cloned.
|
||||
pub struct Environment<E> {
|
||||
/// The underlying `GlobalManager`, if you need to do manual interaction with the
|
||||
/// registry. See `wayland-client` documentation for details.
|
||||
pub manager: GlobalManager,
|
||||
inner: Rc<RefCell<E>>,
|
||||
}
|
||||
|
||||
impl<E: InnerEnv + 'static> Environment<E> {
|
||||
/// Create new `Environment`
|
||||
///
|
||||
/// This requires access to a `wl_display` attached to the `event_queue`.
|
||||
/// You also need to provide an instance of the inner environment type declared
|
||||
/// using the [`environment!`](../macro.environment.html) macro.
|
||||
///
|
||||
/// If you instead used the [`default_environment!`](../macro.default_environment.html), then
|
||||
/// you need to initialize your `Environment` using the
|
||||
/// [`new_default_environment!`](../macro.new_default_environment.html) macro.
|
||||
///
|
||||
/// `std::io::Error` could be returned if initial roundtrips to the server failed.
|
||||
///
|
||||
/// If this call indefinitely blocks when doing initial roundtrips this can only be
|
||||
/// caused by server bugs.
|
||||
pub fn new(
|
||||
display: &Attached<wl_display::WlDisplay>,
|
||||
queue: &mut EventQueue,
|
||||
env: E,
|
||||
) -> Result<Environment<E>> {
|
||||
let environment = Self::new_pending(display, env);
|
||||
|
||||
// Fully initialize the environment.
|
||||
queue.sync_roundtrip(&mut (), |event, _, _| {
|
||||
panic!(
|
||||
"Encountered unhandled event during initial roundtrip ({}::{})",
|
||||
event.interface, event.name
|
||||
);
|
||||
})?;
|
||||
queue.sync_roundtrip(&mut (), |event, _, _| {
|
||||
panic!(
|
||||
"Encountered unhandled event during initial roundtrip ({}::{})",
|
||||
event.interface, event.name
|
||||
);
|
||||
})?;
|
||||
|
||||
Ok(environment)
|
||||
}
|
||||
|
||||
/// Create new pending `Environment`
|
||||
///
|
||||
/// This requires access to a `wl_display` attached to an event queue (on which the main SCTK logic
|
||||
/// will be attached). You also need to provide an instance of the inner environment type declared
|
||||
/// using the [`environment!`](../macro.environment.html) macro.
|
||||
///
|
||||
/// If you instead used the [`default_environment!`](../macro.default_environment.html), then you need
|
||||
/// to initialize your `Environment` using the
|
||||
/// [`new_default_environment!`](../macro.new_default_environment.html) macro.
|
||||
///
|
||||
/// You should prefer to use `Environment::new`, unless you want to control initialization
|
||||
/// manually or you create additional environment meaning that the initialization may be fine
|
||||
/// with just `dispatch_pending` of the event queue, instead of two roundtrips to
|
||||
/// fully initialize environment. If you manually initialize your environment two sync
|
||||
/// roundtrips are required.
|
||||
pub fn new_pending(display: &Attached<wl_display::WlDisplay>, env: E) -> Environment<E> {
|
||||
let inner = Rc::new(RefCell::new(env));
|
||||
|
||||
let my_inner = inner.clone();
|
||||
let my_cb = move |event, registry, ddata: DispatchData| {
|
||||
let mut inner = my_inner.borrow_mut();
|
||||
inner.process_event(event, registry, ddata);
|
||||
};
|
||||
|
||||
let manager = GlobalManager::new_with_cb(display, my_cb);
|
||||
|
||||
Self { manager, inner }
|
||||
}
|
||||
}
|
||||
|
||||
impl<E> Environment<E> {
|
||||
/// Access a "single" global
|
||||
///
|
||||
/// This method allows you to access any "single" global that has previously
|
||||
/// been declared in the `environment!` macro. It is forwarded to the `get()`
|
||||
/// method of the appropriate `GlobalHandler`.
|
||||
///
|
||||
/// It returns `None` if the global has not (yet) been signaled by the registry.
|
||||
pub fn get_global<I: Interface>(&self) -> Option<Attached<I>>
|
||||
where
|
||||
E: GlobalHandler<I>,
|
||||
{
|
||||
self.inner.borrow().get()
|
||||
}
|
||||
|
||||
/// Access a "single" global or panic
|
||||
///
|
||||
/// This method is similar to `get_global`, but will panic with a detailed error
|
||||
/// message if the requested global was not advertized by the server.
|
||||
pub fn require_global<I: Interface>(&self) -> Attached<I>
|
||||
where
|
||||
E: GlobalHandler<I>,
|
||||
{
|
||||
match self.inner.borrow().get() {
|
||||
Some(g) => g,
|
||||
None => panic!("[SCTK] A missing global was required: {}", I::NAME),
|
||||
}
|
||||
}
|
||||
|
||||
/// Access all instances of a "multi" global
|
||||
///
|
||||
/// This will return a `Vec` containing all currently existing instances of the
|
||||
/// requested "multi" global that has been previously declared in the `environment!`
|
||||
/// macro. It is forwarded to the `get_all()` method of the appropriate
|
||||
/// `MultiGlobalHandler`.
|
||||
pub fn get_all_globals<I: Interface>(&self) -> Vec<Attached<I>>
|
||||
where
|
||||
E: MultiGlobalHandler<I>,
|
||||
{
|
||||
self.inner.borrow().get_all()
|
||||
}
|
||||
|
||||
/// Access the inner environment
|
||||
///
|
||||
/// This gives your access, via a closure, to the inner type you declared
|
||||
/// via the [`environment!`](../macro.environment.html) or
|
||||
/// [`default_environment!`](../macro.default_environment.html) macro.
|
||||
///
|
||||
/// This method returns the return value of your closure.
|
||||
pub fn with_inner<T, F: FnOnce(&mut E) -> T>(&self, f: F) -> T {
|
||||
let mut inner = self.inner.borrow_mut();
|
||||
f(&mut *inner)
|
||||
}
|
||||
}
|
||||
|
||||
impl<E> Clone for Environment<E> {
|
||||
fn clone(&self) -> Environment<E> {
|
||||
Environment { manager: self.manager.clone(), inner: self.inner.clone() }
|
||||
}
|
||||
}
|
||||
|
||||
impl<E> fmt::Debug for Environment<E>
|
||||
where
|
||||
E: fmt::Debug,
|
||||
{
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
f.debug_struct("Environment")
|
||||
.field("manager", &self.manager)
|
||||
.field("inner", &self.inner)
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
|
||||
/// Internal trait for the `Environment` logic
|
||||
///
|
||||
/// This trait is automatically implemented by the [`environment!`](../macro.environment.html)
|
||||
/// macro, you should not implement it manually unless you seriously want to.
|
||||
pub trait InnerEnv {
|
||||
/// Process a `GlobalEvent`
|
||||
fn process_event(
|
||||
&mut self,
|
||||
event: GlobalEvent,
|
||||
registry: Attached<wl_registry::WlRegistry>,
|
||||
data: DispatchData,
|
||||
);
|
||||
}
|
||||
|
||||
/*
|
||||
* Simple handlers
|
||||
*/
|
||||
|
||||
/// A minimalist global handler for "single" globals
|
||||
///
|
||||
/// This handler will simply register the global as soon as the registry signals
|
||||
/// it, and do nothing more.
|
||||
///
|
||||
/// It is appropriate for globals that never generate events, like `wl_compositor`
|
||||
/// or `wl_data_device_manager`.
|
||||
#[derive(Debug)]
|
||||
pub struct SimpleGlobal<I: Interface> {
|
||||
global: Option<Attached<I>>,
|
||||
}
|
||||
|
||||
impl<I: Interface> SimpleGlobal<I> {
|
||||
/// Create a new handler
|
||||
pub fn new() -> SimpleGlobal<I> {
|
||||
SimpleGlobal { global: None }
|
||||
}
|
||||
}
|
||||
|
||||
impl<I: Interface + Clone + From<Proxy<I>> + AsRef<Proxy<I>>> GlobalHandler<I> for SimpleGlobal<I> {
|
||||
fn created(
|
||||
&mut self,
|
||||
registry: Attached<wl_registry::WlRegistry>,
|
||||
id: u32,
|
||||
version: u32,
|
||||
_: DispatchData,
|
||||
) {
|
||||
let version = I::VERSION.min(version);
|
||||
self.global = Some((*registry.bind::<I>(version, id)).clone())
|
||||
}
|
||||
fn get(&self) -> Option<Attached<I>> {
|
||||
self.global.clone()
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* environment! macro
|
||||
*/
|
||||
|
||||
/// Macro for declaring an environment
|
||||
///
|
||||
/// It needs to be used in conjunction with a a `struct` you declared, which will serve as the inner
|
||||
/// environment and hold the handlers for your globals.
|
||||
///
|
||||
/// The macro is invoked as such:
|
||||
///
|
||||
/// ```no_run
|
||||
/// # extern crate smithay_client_toolkit as sctk;
|
||||
/// # use sctk::reexports::client::protocol::{wl_compositor::WlCompositor, wl_subcompositor::WlSubcompositor, wl_output::WlOutput};
|
||||
/// # use sctk::environment::SimpleGlobal;
|
||||
/// # use sctk::environment;
|
||||
/// # use sctk::output::OutputHandler;
|
||||
/// struct MyEnv {
|
||||
/// compositor: SimpleGlobal<WlCompositor>,
|
||||
/// subcompositor: SimpleGlobal<WlSubcompositor>,
|
||||
/// outputs: OutputHandler
|
||||
/// }
|
||||
///
|
||||
/// environment!(MyEnv,
|
||||
/// singles = [
|
||||
/// WlCompositor => compositor,
|
||||
/// WlSubcompositor => subcompositor,
|
||||
/// ],
|
||||
/// multis = [
|
||||
/// WlOutput => outputs,
|
||||
/// ]
|
||||
/// );
|
||||
/// ```
|
||||
///
|
||||
/// This will define how your `MyEnv` struct is able to manage the `WlCompositor`, `WlSubcompositor` and
|
||||
/// `WlOutput` globals. For each global, you need to provide a pattern
|
||||
/// `$type => $name` where:
|
||||
///
|
||||
/// - `$type` is the type (implementing the `Interface` trait from `wayland-client`) representing a global
|
||||
/// - `$name` is the name of the field of `MyEnv` that is in charge of managing this global, implementing the
|
||||
/// appropriate `GlobalHandler` or `MultiGlobalHandler` trait
|
||||
///
|
||||
/// It is possible to route several globals to the same field as long as it implements all the appropriate traits.
|
||||
#[macro_export]
|
||||
macro_rules! environment {
|
||||
($env_name:ident,
|
||||
singles = [$($sty:ty => $sname:ident),* $(,)?],
|
||||
multis = [$($mty:ty => $mname:ident),* $(,)?]$(,)?
|
||||
) => {
|
||||
impl $crate::environment::InnerEnv for $env_name {
|
||||
fn process_event(
|
||||
&mut self,
|
||||
event: $crate::reexports::client::GlobalEvent,
|
||||
registry: $crate::reexports::client::Attached<$crate::reexports::client::protocol::wl_registry::WlRegistry>,
|
||||
ddata: $crate::reexports::client::DispatchData,
|
||||
) {
|
||||
match event {
|
||||
$crate::reexports::client::GlobalEvent::New { id, interface, version } => match &interface[..] {
|
||||
$(
|
||||
<$sty as $crate::reexports::client::Interface>::NAME => $crate::environment::GlobalHandler::<$sty>::created(&mut self.$sname, registry, id, version, ddata),
|
||||
)*
|
||||
$(
|
||||
<$mty as $crate::reexports::client::Interface>::NAME => $crate::environment::MultiGlobalHandler::<$mty>::created(&mut self.$mname, registry, id, version, ddata),
|
||||
)*
|
||||
_ => { /* ignore unkown globals */ }
|
||||
},
|
||||
$crate::reexports::client::GlobalEvent::Removed { id, interface } => match &interface[..] {
|
||||
$(
|
||||
<$mty as $crate::reexports::client::Interface>::NAME => $crate::environment::MultiGlobalHandler::<$mty>::removed(&mut self.$mname, id, ddata),
|
||||
)*
|
||||
_ => { /* ignore unknown globals */ }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$(
|
||||
impl $crate::environment::GlobalHandler<$sty> for $env_name {
|
||||
fn created(&mut self, registry: $crate::reexports::client::Attached<$crate::reexports::client::protocol::wl_registry::WlRegistry>, id: u32, version: u32, ddata: $crate::reexports::client::DispatchData) {
|
||||
$crate::environment::GlobalHandler::<$sty>::created(&mut self.$sname, registry, id, version, ddata)
|
||||
}
|
||||
fn get(&self) -> Option<$crate::reexports::client::Attached<$sty>> {
|
||||
$crate::environment::GlobalHandler::<$sty>::get(&self.$sname)
|
||||
}
|
||||
}
|
||||
)*
|
||||
|
||||
$(
|
||||
impl $crate::environment::MultiGlobalHandler<$mty> for $env_name {
|
||||
fn created(&mut self, registry: $crate::reexports::client::Attached<$crate::reexports::client::protocol::wl_registry::WlRegistry>, id: u32, version: u32, ddata: $crate::reexports::client::DispatchData) {
|
||||
$crate::environment::MultiGlobalHandler::<$mty>::created(&mut self.$mname, registry, id, version, ddata)
|
||||
}
|
||||
fn removed(&mut self, id: u32, ddata: $crate::reexports::client::DispatchData) {
|
||||
$crate::environment::MultiGlobalHandler::<$mty>::removed(&mut self.$mname, id, ddata)
|
||||
}
|
||||
fn get_all(&self) -> Vec<$crate::reexports::client::Attached<$mty>> {
|
||||
$crate::environment::MultiGlobalHandler::<$mty>::get_all(&self.$mname)
|
||||
}
|
||||
}
|
||||
)*
|
||||
};
|
||||
}
|
||||
178
third-party/vendor/smithay-client-toolkit/src/event_loop.rs
vendored
Normal file
178
third-party/vendor/smithay-client-toolkit/src/event_loop.rs
vendored
Normal file
|
|
@ -0,0 +1,178 @@
|
|||
use std::{io, os::unix::io::RawFd};
|
||||
|
||||
use calloop::{
|
||||
generic::Generic, EventSource, InsertError, Interest, LoopHandle, Mode, PostAction,
|
||||
RegistrationToken, TokenFactory,
|
||||
};
|
||||
|
||||
use wayland_client::{EventQueue, ReadEventsGuard};
|
||||
|
||||
/// An adapter to insert a Wayland `EventQueue` into a calloop event loop
|
||||
///
|
||||
/// This is a struct that implements `calloop::EventSource`. It generates an
|
||||
/// event whenever events need to be dispatched. At this point your calloop callback
|
||||
/// will be given access to the `EventQueue` and you should call `.dispatch_pending()`
|
||||
/// and forward its return value, allowing you to handle orphan events as you prefer.
|
||||
///
|
||||
/// If you don't use orphan events, the `quick_insert` method will directly
|
||||
/// insert the source into a provided `LoopHandle` with an adapter which will panic
|
||||
/// whenever an oprhan event is encountered.
|
||||
#[derive(Debug)]
|
||||
pub struct WaylandSource {
|
||||
queue: EventQueue,
|
||||
fd: Generic<RawFd>,
|
||||
read_guard: Option<ReadEventsGuard>,
|
||||
}
|
||||
|
||||
impl WaylandSource {
|
||||
/// Wrap an `EventQueue` as a `WaylandSource`.
|
||||
pub fn new(queue: EventQueue) -> WaylandSource {
|
||||
let fd = queue.display().get_connection_fd();
|
||||
WaylandSource { queue, fd: Generic::new(fd, Interest::READ, Mode::Level), read_guard: None }
|
||||
}
|
||||
|
||||
/// Insert this source into given event loop with an adapter that panics on orphan events
|
||||
///
|
||||
/// The adapter will pass the event loop's global shared data as `dispatch_data` too all
|
||||
/// callbacks.
|
||||
pub fn quick_insert<Data: 'static>(
|
||||
self,
|
||||
handle: LoopHandle<Data>,
|
||||
) -> Result<RegistrationToken, InsertError<WaylandSource>> {
|
||||
handle.insert_source(self, |(), queue, ddata| {
|
||||
queue.dispatch_pending(ddata, |event, object, _| {
|
||||
panic!(
|
||||
"[calloop] Encountered an orphan event: {}@{} : {}",
|
||||
event.interface,
|
||||
object.as_ref().id(),
|
||||
event.name
|
||||
);
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
/// Access the underlying event queue
|
||||
///
|
||||
/// This method can be used if you need to access the underlying `EventQueue` while this
|
||||
/// `WaylandSource` is currently inserted in an event loop.
|
||||
///
|
||||
/// Note that you should be careful when interacting with it if you invoke methods that
|
||||
/// interact with the wayland socket (such as `dispatch()` or `prepare_read()`). These may
|
||||
/// interefere with the proper waking up of this event source in the event loop.
|
||||
pub fn queue(&mut self) -> &mut EventQueue {
|
||||
&mut self.queue
|
||||
}
|
||||
}
|
||||
|
||||
impl EventSource for WaylandSource {
|
||||
type Event = ();
|
||||
type Error = std::io::Error;
|
||||
type Metadata = EventQueue;
|
||||
type Ret = std::io::Result<u32>;
|
||||
|
||||
fn process_events<F>(
|
||||
&mut self,
|
||||
readiness: calloop::Readiness,
|
||||
token: calloop::Token,
|
||||
mut callback: F,
|
||||
) -> std::io::Result<PostAction>
|
||||
where
|
||||
F: FnMut((), &mut EventQueue) -> std::io::Result<u32>,
|
||||
{
|
||||
let queue = &mut self.queue;
|
||||
let read_guard = &mut self.read_guard;
|
||||
self.fd.process_events(readiness, token, |_, _| {
|
||||
// 1. read events from the socket if any are available
|
||||
if let Some(guard) = read_guard.take() {
|
||||
// might be None if some other thread read events before us, concurently
|
||||
if let Err(e) = guard.read_events() {
|
||||
if e.kind() != io::ErrorKind::WouldBlock {
|
||||
return Err(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
// 2. dispatch any pending event in the queue (that's callback's job)
|
||||
loop {
|
||||
match queue.prepare_read() {
|
||||
Some(guard) => {
|
||||
*read_guard = Some(guard);
|
||||
break;
|
||||
}
|
||||
None => {
|
||||
callback((), queue)?;
|
||||
}
|
||||
}
|
||||
}
|
||||
// 3. Once dispatching is finished, flush the responses to the compositor
|
||||
if let Err(e) = queue.display().flush() {
|
||||
if e.kind() != io::ErrorKind::WouldBlock {
|
||||
// in case of error, forward it and fast-exit
|
||||
return Err(e);
|
||||
}
|
||||
// WouldBlock error means the compositor could not process all our messages
|
||||
// quickly. Either it is slowed down or we are a spammer.
|
||||
// Should not really happen, if it does we do nothing and will flush again later
|
||||
}
|
||||
Ok(PostAction::Continue)
|
||||
})
|
||||
}
|
||||
|
||||
fn register(
|
||||
&mut self,
|
||||
poll: &mut calloop::Poll,
|
||||
token_factory: &mut TokenFactory,
|
||||
) -> calloop::Result<()> {
|
||||
self.fd.register(poll, token_factory)
|
||||
}
|
||||
|
||||
fn reregister(
|
||||
&mut self,
|
||||
poll: &mut calloop::Poll,
|
||||
token_factory: &mut TokenFactory,
|
||||
) -> calloop::Result<()> {
|
||||
self.fd.reregister(poll, token_factory)
|
||||
}
|
||||
|
||||
fn unregister(&mut self, poll: &mut calloop::Poll) -> calloop::Result<()> {
|
||||
self.fd.unregister(poll)
|
||||
}
|
||||
|
||||
fn pre_run<F>(&mut self, mut callback: F) -> calloop::Result<()>
|
||||
where
|
||||
F: FnMut((), &mut EventQueue) -> std::io::Result<u32>,
|
||||
{
|
||||
debug_assert!(self.read_guard.is_none());
|
||||
// flush the display before starting to poll
|
||||
if let Err(e) = self.queue.display().flush() {
|
||||
if e.kind() != io::ErrorKind::WouldBlock {
|
||||
// in case of error, don't prepare a read, if the error is persitent,
|
||||
// it'll trigger in other wayland methods anyway
|
||||
log::error!("Error trying to flush the wayland display: {}", e);
|
||||
return Err(e.into());
|
||||
}
|
||||
}
|
||||
|
||||
loop {
|
||||
match self.queue.prepare_read() {
|
||||
Some(guard) => {
|
||||
self.read_guard = Some(guard);
|
||||
break;
|
||||
}
|
||||
None => {
|
||||
callback((), &mut self.queue)?;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn post_run<F>(&mut self, _: F) -> calloop::Result<()>
|
||||
where
|
||||
F: FnMut((), &mut EventQueue) -> std::io::Result<u32>,
|
||||
{
|
||||
// the destructor of ReadEventsGuard does the cleanup
|
||||
self.read_guard = None;
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
9
third-party/vendor/smithay-client-toolkit/src/lazy_global.rs
vendored
Normal file
9
third-party/vendor/smithay-client-toolkit/src/lazy_global.rs
vendored
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
use wayland_client::{Attached, Interface};
|
||||
|
||||
/// An utility for lazy-loading globals.
|
||||
#[derive(Debug)]
|
||||
pub enum LazyGlobal<I: Interface> {
|
||||
Unknown,
|
||||
Seen { id: u32, version: u32 },
|
||||
Bound(Attached<I>),
|
||||
}
|
||||
411
third-party/vendor/smithay-client-toolkit/src/lib.rs
vendored
Normal file
411
third-party/vendor/smithay-client-toolkit/src/lib.rs
vendored
Normal file
|
|
@ -0,0 +1,411 @@
|
|||
//! Smithay Client Toolkit
|
||||
//!
|
||||
//! Provides various utilities and abstractions for comunicating with various
|
||||
//! Wayland compositors.
|
||||
//!
|
||||
//! ## `Environment`
|
||||
//!
|
||||
//! The crate is structured around the [`Environment`](environment/struct.Environment.html) type,
|
||||
//! which binds the wayland globals for you using a set of modular handlers. This type is used in conjunction
|
||||
//! with the [`environment!`](macro.environment.html) if you want full control, or by using the
|
||||
//! [`default_environment!`](macro.default_environment.html) macro to automatically bring in all
|
||||
//! SCTK modules.
|
||||
//!
|
||||
//! The various modules work by adding methods to the [`Environment`](environment/struct.Environment.html)
|
||||
//! type, giving you more capabilities as more modules are activated.
|
||||
//!
|
||||
//! ## Event Loops
|
||||
//!
|
||||
//! SCTK integrates with `calloop` to provide an event loop abstraction. Indeed most Wayland
|
||||
//! apps will need to handle more event sources than the single Wayland connection. These are
|
||||
//! necessary to handle things like keyboard repetition, copy-paste, or animated cursors.
|
||||
//!
|
||||
//! [`WaylandSource`](struct.WaylandSource.html) is an adapter to insert a Wayland `EventQueue` into
|
||||
//! a calloop event loop. And some of the modules of SCTK will provide you with other event sources
|
||||
//! that you need to insert into calloop for them to work correctly.
|
||||
#![warn(missing_docs, missing_debug_implementations)]
|
||||
#![allow(clippy::new_without_default)]
|
||||
|
||||
#[macro_use]
|
||||
extern crate dlib;
|
||||
|
||||
/// Re-exports of some crates, for convenience
|
||||
pub mod reexports {
|
||||
#[cfg(feature = "calloop")]
|
||||
pub use calloop;
|
||||
pub use wayland_client as client;
|
||||
pub use wayland_protocols as protocols;
|
||||
}
|
||||
|
||||
pub mod data_device;
|
||||
pub mod environment;
|
||||
mod lazy_global;
|
||||
pub mod output;
|
||||
pub mod primary_selection;
|
||||
pub mod seat;
|
||||
pub mod shell;
|
||||
pub mod shm;
|
||||
pub mod window;
|
||||
|
||||
#[cfg(feature = "calloop")]
|
||||
mod event_loop;
|
||||
mod surface;
|
||||
|
||||
#[cfg(feature = "calloop")]
|
||||
pub use event_loop::WaylandSource;
|
||||
pub use surface::{get_surface_outputs, get_surface_scale_factor};
|
||||
|
||||
#[macro_export]
|
||||
/// Declare a batteries-included SCTK environment
|
||||
///
|
||||
/// Similar to the [`environment!`](macro.environment.html) macro, but creates the type for you and
|
||||
/// includes all the handlers provided by SCTK, for use with the rest of the library. Its sister
|
||||
/// macro [`new_default_environment!`](macro.new_default_environment.html) needs to be used to
|
||||
/// initialize it.
|
||||
///
|
||||
/// This includes handlers for the following globals:
|
||||
///
|
||||
/// - `wl_compositor` as a [`SimpleGlobal`](environment/struct.SimpleGlobal.html)
|
||||
/// - `wl_data_device_manager` as a [`DataDeviceHandler`](data_device/struct.DataDeviceHandler.html)
|
||||
/// - `wl_output` with the [`OutputHandler`](output/struct.OutputHandler.html)
|
||||
/// - `wl_seat` with the [`SeatHandler`](seat/struct.SeatHandler.html)
|
||||
/// - `wl_subcompositor` as a [`SimpleGlobal`](environment/struct.SimpleGlobal.html)
|
||||
/// - `wl_shm` as a [`ShmHandler`](shm/struct.ShmHandler.html)
|
||||
/// - `zwp` and `gtk` primary selection device manager as a [`PrimarySelectionHandler`](primary_selection/struct.PrimarySelectionHandler.html)
|
||||
///
|
||||
/// If you don't need to add anything more, using it is as simple as:
|
||||
///
|
||||
/// ```no_run
|
||||
/// # use smithay_client_toolkit::default_environment;
|
||||
/// default_environment!(MyEnv);
|
||||
/// ```
|
||||
///
|
||||
/// The macro also provides some presets including more globals depending on your use-case:
|
||||
///
|
||||
/// - the `desktop` preset, invoked as `default_environment!(MyEnv, desktop);` additionally
|
||||
/// includes:
|
||||
/// - `xdg_shell` and `wl_shell` with the [`ShellHandler`](shell/struct.ShellHandler.html)
|
||||
/// - `xdg_decoration_manager` as a [`SimpleGlobal`](environment/struct.SimpleGlobal.html)
|
||||
///
|
||||
/// You can also add the `fields` argument to add additional fields to the generated struct, and
|
||||
/// the `singles` and `multis` arguments to route additional globals like with the
|
||||
/// [`environment!`](macro.environment.html) macro. These three fields are optional, but they must
|
||||
/// appear in this order, and after the optional preset
|
||||
///
|
||||
/// ```no_run
|
||||
/// # use smithay_client_toolkit::default_environment;
|
||||
/// default_environment!(MyEnv,
|
||||
/// desktop, // the chosen preset, can be ommited
|
||||
/// fields=[
|
||||
/// somefield: u32,
|
||||
/// otherfield: String,
|
||||
/// ],
|
||||
/// singles=[
|
||||
/// // Add some routing here
|
||||
/// ],
|
||||
/// multis=[
|
||||
/// // add some routing here
|
||||
/// ]
|
||||
/// );
|
||||
/// ```
|
||||
macro_rules! default_environment {
|
||||
($env_name:ident, desktop
|
||||
$(,fields = [$($fname:ident : $fty:ty),* $(,)?])?
|
||||
$(,singles = [$($sty:ty => $sname: ident),* $(,)?])?
|
||||
$(,multis = [$($mty:ty => $mname:ident),* $(,)?])?
|
||||
$(,)?
|
||||
) => {
|
||||
$crate::default_environment!($env_name,
|
||||
fields=[
|
||||
// shell
|
||||
sctk_shell: $crate::shell::ShellHandler,
|
||||
// decoration
|
||||
sctk_decoration_mgr: $crate::environment::SimpleGlobal<$crate::reexports::protocols::unstable::xdg_decoration::v1::client::zxdg_decoration_manager_v1::ZxdgDecorationManagerV1>,
|
||||
// others
|
||||
$($($fname : $fty,)*)?
|
||||
],
|
||||
singles = [
|
||||
// shell globals
|
||||
$crate::reexports::client::protocol::wl_shell::WlShell => sctk_shell,
|
||||
$crate::reexports::protocols::xdg_shell::client::xdg_wm_base::XdgWmBase => sctk_shell,
|
||||
$crate::reexports::protocols::unstable::xdg_shell::v6::client::zxdg_shell_v6::ZxdgShellV6 => sctk_shell,
|
||||
// decoration
|
||||
$crate::reexports::protocols::unstable::xdg_decoration::v1::client::zxdg_decoration_manager_v1::ZxdgDecorationManagerV1 => sctk_decoration_mgr,
|
||||
// others
|
||||
$($($sty => $sname,)*)?
|
||||
],
|
||||
multis = [ $($($mty => $mname,)*)? ],
|
||||
);
|
||||
|
||||
// Shell utility
|
||||
impl $crate::shell::ShellHandling for $env_name {
|
||||
fn get_shell(&self) -> Option<$crate::shell::Shell> {
|
||||
self.sctk_shell.get_shell()
|
||||
}
|
||||
}
|
||||
};
|
||||
($env_name:ident
|
||||
$(,fields = [$($fname:ident : $fty:ty),* $(,)?])?
|
||||
$(,singles = [$($sty:ty => $sname:ident),* $(,)?])?
|
||||
$(,multis = [$($mty:ty => $mname:ident),* $(,)?])?
|
||||
$(,)?
|
||||
) => {
|
||||
/*
|
||||
* Declare the type
|
||||
*/
|
||||
pub struct $env_name {
|
||||
// SimpleGlobals
|
||||
sctk_compositor: $crate::environment::SimpleGlobal<$crate::reexports::client::protocol::wl_compositor::WlCompositor>,
|
||||
sctk_subcompositor: $crate::environment::SimpleGlobal<$crate::reexports::client::protocol::wl_subcompositor::WlSubcompositor>,
|
||||
// shm
|
||||
sctk_shm: $crate::shm::ShmHandler,
|
||||
// output
|
||||
sctk_outputs: $crate::output::OutputHandler,
|
||||
// seat
|
||||
sctk_seats: $crate::seat::SeatHandler,
|
||||
// data device
|
||||
sctk_data_device_manager: $crate::data_device::DataDeviceHandler,
|
||||
// primary selection
|
||||
sctk_primary_selection_manager: $crate::primary_selection::PrimarySelectionHandler,
|
||||
// user added
|
||||
$($(
|
||||
$fname : $fty,
|
||||
)*)?
|
||||
}
|
||||
|
||||
// SHM utility
|
||||
impl $crate::shm::ShmHandling for $env_name {
|
||||
fn shm_formats(&self) -> Vec<$crate::reexports::client::protocol::wl_shm::Format> {
|
||||
self.sctk_shm.shm_formats()
|
||||
}
|
||||
}
|
||||
|
||||
// Seat utility
|
||||
impl $crate::seat::SeatHandling for $env_name {
|
||||
fn listen<F>(&mut self, f: F) -> $crate::seat::SeatListener
|
||||
where F: FnMut(
|
||||
$crate::reexports::client::Attached<$crate::reexports::client::protocol::wl_seat::WlSeat>,
|
||||
&$crate::seat::SeatData,
|
||||
$crate::reexports::client::DispatchData
|
||||
) + 'static
|
||||
{
|
||||
self.sctk_seats.listen(f)
|
||||
}
|
||||
}
|
||||
|
||||
// Output utility
|
||||
impl $crate::output::OutputHandling for $env_name {
|
||||
fn listen<F>(&mut self, f: F) -> $crate::output::OutputStatusListener
|
||||
where F: FnMut(
|
||||
$crate::reexports::client::protocol::wl_output::WlOutput,
|
||||
&$crate::output::OutputInfo,
|
||||
$crate::reexports::client::DispatchData,
|
||||
) + 'static
|
||||
{
|
||||
self.sctk_outputs.listen(f)
|
||||
}
|
||||
}
|
||||
|
||||
// Data device utility
|
||||
impl $crate::data_device::DataDeviceHandling for $env_name {
|
||||
fn set_callback<F>(&mut self, callback: F) -> ::std::result::Result<(), $crate::MissingGlobal>
|
||||
where F: FnMut(
|
||||
$crate::reexports::client::protocol::wl_seat::WlSeat,
|
||||
$crate::data_device::DndEvent,
|
||||
$crate::reexports::client::DispatchData
|
||||
) + 'static
|
||||
{
|
||||
self.sctk_data_device_manager.set_callback(callback)
|
||||
}
|
||||
|
||||
fn with_device<F: FnOnce(&$crate::data_device::DataDevice)>(
|
||||
&self,
|
||||
seat: &$crate::reexports::client::protocol::wl_seat::WlSeat,
|
||||
f: F
|
||||
) -> ::std::result::Result<(), $crate::MissingGlobal> {
|
||||
self.sctk_data_device_manager.with_device(seat, f)
|
||||
}
|
||||
}
|
||||
|
||||
// Primary selection utility
|
||||
impl $crate::primary_selection::PrimarySelectionHandling for $env_name {
|
||||
fn with_primary_selection<F>(
|
||||
&self,
|
||||
seat: &$crate::reexports::client::protocol::wl_seat::WlSeat,
|
||||
f: F,
|
||||
) -> ::std::result::Result<(), $crate::MissingGlobal>
|
||||
where F: FnOnce(&$crate::primary_selection::PrimarySelectionDevice)
|
||||
{
|
||||
self.sctk_primary_selection_manager.with_primary_selection(seat, f)
|
||||
}
|
||||
|
||||
fn get_primary_selection_manager(&self) -> Option<$crate::primary_selection::PrimarySelectionDeviceManager> {
|
||||
self.sctk_primary_selection_manager.get_primary_selection_manager()
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
// Final macro delegation
|
||||
//
|
||||
$crate::environment!($env_name,
|
||||
singles = [
|
||||
// SimpleGlobals
|
||||
$crate::reexports::client::protocol::wl_compositor::WlCompositor => sctk_compositor,
|
||||
$crate::reexports::client::protocol::wl_subcompositor::WlSubcompositor => sctk_subcompositor,
|
||||
// shm
|
||||
$crate::reexports::client::protocol::wl_shm::WlShm => sctk_shm,
|
||||
// data device
|
||||
$crate::reexports::client::protocol::wl_data_device_manager::WlDataDeviceManager => sctk_data_device_manager,
|
||||
// primary selection
|
||||
$crate::reexports::protocols::unstable::primary_selection::v1::client::zwp_primary_selection_device_manager_v1::ZwpPrimarySelectionDeviceManagerV1 => sctk_primary_selection_manager,
|
||||
$crate::reexports::protocols::misc::gtk_primary_selection::client::gtk_primary_selection_device_manager::GtkPrimarySelectionDeviceManager => sctk_primary_selection_manager,
|
||||
// user added
|
||||
$($($sty => $sname),*)?
|
||||
],
|
||||
multis = [
|
||||
// output globals
|
||||
$crate::reexports::client::protocol::wl_output::WlOutput => sctk_outputs,
|
||||
// seat globals
|
||||
$crate::reexports::client::protocol::wl_seat::WlSeat => sctk_seats,
|
||||
// user added
|
||||
$($($mty => $mname),*)?
|
||||
]
|
||||
);
|
||||
};
|
||||
}
|
||||
|
||||
#[macro_export]
|
||||
/// Initialize a batteries-included SCTK environment
|
||||
///
|
||||
/// Sister macro of [`default_environment!`](macro.default_environment.html). You need
|
||||
/// to use it to initialize the environment instead of
|
||||
/// [`Envrionment::init`](environment/struct.Environment.html). It has the same semantics.
|
||||
///
|
||||
/// If a preset was used for [`default_environment!`](macro.default_environment.html), it
|
||||
/// must be provided here as well.
|
||||
///
|
||||
/// The macro will automatically setup a Wayland connection and evaluate to a `Result`
|
||||
/// containing either `Ok((env, display, queue))`, providing you the initialized `Environment`
|
||||
/// as well as the wayland `Display` and `EventQueue` associated to it, or to an error
|
||||
/// if the connection failed.
|
||||
///
|
||||
/// ```no_run
|
||||
/// # use smithay_client_toolkit::{default_environment, new_default_environment};
|
||||
/// # default_environment!(MyEnv, desktop, fields=[somefield: u32, otherfield: String]);
|
||||
/// let (env, display, queue) = new_default_environment!(MyEnv,
|
||||
/// desktop, // the optional preset
|
||||
/// /* initializers for your extra fields if any, can be ommited if no fields are added */
|
||||
/// fields=[
|
||||
/// somefield: 42,
|
||||
/// otherfield: String::from("Hello World"),
|
||||
/// ]
|
||||
/// ).expect("Unable to connect to the wayland compositor");
|
||||
/// ```
|
||||
///
|
||||
/// If you instead want the macro to use some pre-existing display and event queue, you can
|
||||
/// add the `with` argument providing them. In that case the macro will evaluate to
|
||||
/// a `Result<Environment, io::Error>`, forwarding to you any error that may have occured
|
||||
/// during the initial roundtrips.
|
||||
///
|
||||
/// ```no_run
|
||||
/// # use smithay_client_toolkit::{default_environment, new_default_environment};
|
||||
/// # default_environment!(MyEnv, desktop, fields=[somefield: u32, otherfield: String]);
|
||||
/// # let display = smithay_client_toolkit::reexports::client::Display::connect_to_env().unwrap();
|
||||
/// # let mut queue = display.create_event_queue();
|
||||
/// let env = new_default_environment!(MyEnv,
|
||||
/// desktop, // the optional preset
|
||||
/// with=(display, queue), // the display and event queue to use
|
||||
/// /* initializers for your extra fields if any, can be ommited if no fields are added */
|
||||
/// fields=[
|
||||
/// somefield: 42,
|
||||
/// otherfield: String::from("Hello World"),
|
||||
/// ]
|
||||
/// ).expect("Initial roundtrips failed!");
|
||||
/// ```
|
||||
macro_rules! new_default_environment {
|
||||
($env_name:ident, desktop
|
||||
$(, with=($display:expr, $queue:expr))?
|
||||
$(,fields = [$($fname:ident : $fval:expr),* $(,)?])?
|
||||
$(,)?
|
||||
) => {
|
||||
$crate::new_default_environment!($env_name,
|
||||
$(with=($display, $queue),)?
|
||||
fields = [
|
||||
sctk_shell: $crate::shell::ShellHandler::new(),
|
||||
sctk_decoration_mgr: $crate::environment::SimpleGlobal::new(),
|
||||
$($(
|
||||
$fname: $fval,
|
||||
)*)?
|
||||
]
|
||||
)
|
||||
};
|
||||
($env_name:ident, with=($display:expr, $queue:expr)
|
||||
$(,fields = [$($fname:ident : $fval:expr),* $(,)?])?
|
||||
$(,)?
|
||||
) => {
|
||||
{
|
||||
let mut sctk_seats = $crate::seat::SeatHandler::new();
|
||||
let sctk_data_device_manager = $crate::data_device::DataDeviceHandler::init(&mut sctk_seats);
|
||||
let sctk_primary_selection_manager = $crate::primary_selection::PrimarySelectionHandler::init(&mut sctk_seats);
|
||||
|
||||
let display = $crate::reexports::client::Proxy::clone(&$display);
|
||||
let env = $crate::environment::Environment::new(&display.attach($queue.token()), &mut $queue,$env_name {
|
||||
sctk_compositor: $crate::environment::SimpleGlobal::new(),
|
||||
sctk_subcompositor: $crate::environment::SimpleGlobal::new(),
|
||||
sctk_shm: $crate::shm::ShmHandler::new(),
|
||||
sctk_outputs: $crate::output::OutputHandler::new(),
|
||||
sctk_seats,
|
||||
sctk_data_device_manager,
|
||||
sctk_primary_selection_manager,
|
||||
$($(
|
||||
$fname: $fval,
|
||||
)*)?
|
||||
});
|
||||
|
||||
if let Ok(env) = env.as_ref() {
|
||||
// Bind primary selection manager.
|
||||
let _psm = env.get_primary_selection_manager();
|
||||
}
|
||||
|
||||
env
|
||||
}
|
||||
};
|
||||
($env_name:ident
|
||||
$(,fields = [$($fname:ident : $fval:expr),* $(,)?])?
|
||||
$(,)?
|
||||
) => {
|
||||
$crate::reexports::client::Display::connect_to_env().and_then(|display| {
|
||||
let mut queue = display.create_event_queue();
|
||||
let ret = $crate::new_default_environment!(
|
||||
$env_name,
|
||||
with=(display, queue),
|
||||
fields=[$($($fname: $fval),*)?],
|
||||
);
|
||||
match ret {
|
||||
Ok(env) => Ok((env, display, queue)),
|
||||
Err(e) => {
|
||||
if let Some(perr) = display.protocol_error() {
|
||||
panic!("[SCTK] A protocol error occured during initial setup: {}", perr);
|
||||
} else {
|
||||
// For some other reason the connection with the compositor was lost
|
||||
// This should not arrive unless maybe the compositor was shutdown during
|
||||
// the initial setup...
|
||||
Err($crate::reexports::client::ConnectError::NoCompositorListening)
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
};
|
||||
}
|
||||
|
||||
/// An error representing the fact that a required global was missing
|
||||
#[derive(Debug, Copy, Clone)]
|
||||
pub struct MissingGlobal;
|
||||
|
||||
impl std::error::Error for MissingGlobal {}
|
||||
|
||||
impl std::fmt::Display for MissingGlobal {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
f.write_str("missing global")
|
||||
}
|
||||
}
|
||||
698
third-party/vendor/smithay-client-toolkit/src/output.rs
vendored
Normal file
698
third-party/vendor/smithay-client-toolkit/src/output.rs
vendored
Normal file
|
|
@ -0,0 +1,698 @@
|
|||
//! Types and functions related to graphical outputs
|
||||
//!
|
||||
//! This modules provides two main elements. The first is the
|
||||
//! [`OutputHandler`](struct.OutputHandler.html) type, which is a
|
||||
//! [`MultiGlobalHandler`](../environment/trait.MultiGlobalHandler.html) for
|
||||
//! use with the [`init_environment!`](../macro.init_environment.html) macro. It is automatically
|
||||
//! included if you use the [`new_default_environment!`](../macro.new_default_environment.html).
|
||||
//!
|
||||
//! The second is the [`with_output_info`](fn.with_output_info.html) with allows you to
|
||||
//! access the information associated to this output, as an [`OutputInfo`](struct.OutputInfo.html).
|
||||
|
||||
use std::{
|
||||
cell::RefCell,
|
||||
fmt,
|
||||
rc::{self, Rc},
|
||||
sync::{self, Arc, Mutex},
|
||||
};
|
||||
|
||||
use wayland_client::{
|
||||
protocol::{
|
||||
wl_output::{self, Event, WlOutput},
|
||||
wl_registry,
|
||||
},
|
||||
Attached, DispatchData, Main,
|
||||
};
|
||||
|
||||
use wayland_protocols::unstable::xdg_output::v1::client::{
|
||||
zxdg_output_manager_v1::ZxdgOutputManagerV1,
|
||||
zxdg_output_v1::{self, ZxdgOutputV1},
|
||||
};
|
||||
|
||||
pub use wayland_client::protocol::wl_output::{Subpixel, Transform};
|
||||
|
||||
/// A possible mode for an output
|
||||
#[derive(Copy, Clone, Debug)]
|
||||
pub struct Mode {
|
||||
/// Number of pixels of this mode in format `(width, height)`
|
||||
///
|
||||
/// for example `(1920, 1080)`
|
||||
pub dimensions: (i32, i32),
|
||||
/// Refresh rate for this mode, in mHz
|
||||
pub refresh_rate: i32,
|
||||
/// Whether this is the current mode for this output
|
||||
pub is_current: bool,
|
||||
/// Whether this is the preferred mode for this output
|
||||
pub is_preferred: bool,
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
#[non_exhaustive]
|
||||
/// Compiled information about an output
|
||||
pub struct OutputInfo {
|
||||
/// The ID of this output as a global
|
||||
pub id: u32,
|
||||
/// The model name of this output as advertised by the server
|
||||
pub model: String,
|
||||
/// The make name of this output as advertised by the server
|
||||
pub make: String,
|
||||
/// The name of this output as advertised by the server
|
||||
///
|
||||
/// Each name is unique among all wl_output globals, but if a wl_output
|
||||
/// global is destroyed the same name may be reused later. The names will
|
||||
/// also remain consistent across sessions with the same hardware and
|
||||
/// software configuration.
|
||||
///
|
||||
/// Examples of names include 'HDMI-A-1', 'WL-1', 'X11-1', etc. However, do
|
||||
/// not assume that the name is a reflection of an underlying DRM connector,
|
||||
/// X11 connection, etc.
|
||||
///
|
||||
/// Note that this is not filled in by version 3 of the wl_output protocol,
|
||||
/// but it has been proposed for inclusion in version 4. Until then, it is
|
||||
/// only filled in if your environment has an [XdgOutputHandler] global
|
||||
/// handler for [ZxdgOutputManagerV1].
|
||||
pub name: String,
|
||||
/// The description of this output as advertised by the server
|
||||
///
|
||||
/// The description is a UTF-8 string with no convention defined for its
|
||||
/// contents. The description is not guaranteed to be unique among all
|
||||
/// wl_output globals. Examples might include 'Foocorp 11" Display' or
|
||||
/// 'Virtual X11 output via :1'.
|
||||
///
|
||||
/// Note that this is not filled in by version 3 of the wl_output protocol,
|
||||
/// but it has been proposed for inclusion in version 4. Until then, it is
|
||||
/// only filled in if your environment has an [XdgOutputHandler] global
|
||||
/// handler for [ZxdgOutputManagerV1].
|
||||
pub description: String,
|
||||
/// Location of the top-left corner of this output in compositor
|
||||
/// space
|
||||
///
|
||||
/// Note that the compositor may decide to always report (0,0) if
|
||||
/// it decides clients are not allowed to know this information.
|
||||
pub location: (i32, i32),
|
||||
/// Physical dimensions of this output, in unspecified units
|
||||
pub physical_size: (i32, i32),
|
||||
/// The subpixel layout for this output
|
||||
pub subpixel: Subpixel,
|
||||
/// The current transformation applied to this output
|
||||
///
|
||||
/// You can pre-render your buffers taking this information
|
||||
/// into account and advertising it via `wl_buffer.set_tranform`
|
||||
/// for better performances.
|
||||
pub transform: Transform,
|
||||
/// The scaling factor of this output
|
||||
///
|
||||
/// Any buffer whose scaling factor does not match the one
|
||||
/// of the output it is displayed on will be rescaled accordingly.
|
||||
///
|
||||
/// For example, a buffer of scaling factor 1 will be doubled in
|
||||
/// size if the output scaling factor is 2.
|
||||
pub scale_factor: i32,
|
||||
/// Possible modes for an output
|
||||
pub modes: Vec<Mode>,
|
||||
/// Has this output been unadvertized by the registry
|
||||
///
|
||||
/// If this is the case, it has become inert, you might want to
|
||||
/// call its `release()` method if you don't plan to use it any
|
||||
/// longer.
|
||||
pub obsolete: bool,
|
||||
}
|
||||
|
||||
impl OutputInfo {
|
||||
fn new(id: u32) -> OutputInfo {
|
||||
OutputInfo {
|
||||
id,
|
||||
model: String::new(),
|
||||
make: String::new(),
|
||||
name: String::new(),
|
||||
description: String::new(),
|
||||
location: (0, 0),
|
||||
physical_size: (0, 0),
|
||||
subpixel: Subpixel::Unknown,
|
||||
transform: Transform::Normal,
|
||||
scale_factor: 1,
|
||||
modes: Vec::new(),
|
||||
obsolete: false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
type OutputCallback = dyn Fn(WlOutput, &OutputInfo, DispatchData) + Send + Sync;
|
||||
|
||||
enum OutputData {
|
||||
Ready {
|
||||
info: OutputInfo,
|
||||
callbacks: Vec<sync::Weak<OutputCallback>>,
|
||||
},
|
||||
Pending {
|
||||
id: u32,
|
||||
has_xdg: bool,
|
||||
events: Vec<Event>,
|
||||
callbacks: Vec<sync::Weak<OutputCallback>>,
|
||||
},
|
||||
PendingXDG {
|
||||
info: OutputInfo,
|
||||
callbacks: Vec<sync::Weak<OutputCallback>>,
|
||||
},
|
||||
}
|
||||
|
||||
type OutputStatusCallback = dyn FnMut(WlOutput, &OutputInfo, DispatchData) + 'static;
|
||||
|
||||
/// A handler for `wl_output`
|
||||
///
|
||||
/// This handler can be used for managing `wl_output` in the
|
||||
/// [`init_environment!`](../macro.init_environment.html) macro, and is automatically
|
||||
/// included in [`new_default_environment!`](../macro.new_default_environment.html).
|
||||
///
|
||||
/// It aggregates the output information and makes it available via the
|
||||
/// [`with_output_info`](fn.with_output_info.html) function.
|
||||
pub struct OutputHandler {
|
||||
outputs: Vec<(u32, Attached<WlOutput>)>,
|
||||
status_listeners: Rc<RefCell<Vec<rc::Weak<RefCell<OutputStatusCallback>>>>>,
|
||||
xdg_listener: Option<rc::Weak<RefCell<XdgOutputHandlerInner>>>,
|
||||
}
|
||||
|
||||
impl OutputHandler {
|
||||
/// Create a new instance of this handler
|
||||
pub fn new() -> OutputHandler {
|
||||
OutputHandler {
|
||||
outputs: Vec::new(),
|
||||
status_listeners: Rc::new(RefCell::new(Vec::new())),
|
||||
xdg_listener: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl crate::environment::MultiGlobalHandler<WlOutput> for OutputHandler {
|
||||
fn created(
|
||||
&mut self,
|
||||
registry: Attached<wl_registry::WlRegistry>,
|
||||
id: u32,
|
||||
version: u32,
|
||||
_: DispatchData,
|
||||
) {
|
||||
// We currently support wl_output up to version 4
|
||||
let version = std::cmp::min(version, 4);
|
||||
let output = registry.bind::<WlOutput>(version, id);
|
||||
let has_xdg = if let Some(xdg) = self.xdg_listener.as_ref().and_then(rc::Weak::upgrade) {
|
||||
xdg.borrow_mut().new_xdg_output(&output, &self.status_listeners)
|
||||
} else {
|
||||
false
|
||||
};
|
||||
if version > 1 {
|
||||
// wl_output.done event was only added at version 2
|
||||
// In case of an old version 1, we just behave as if it was send at the start
|
||||
output.as_ref().user_data().set_threadsafe(|| {
|
||||
Mutex::new(OutputData::Pending { id, has_xdg, events: vec![], callbacks: vec![] })
|
||||
});
|
||||
} else {
|
||||
output.as_ref().user_data().set_threadsafe(|| {
|
||||
Mutex::new(OutputData::Ready { info: OutputInfo::new(id), callbacks: vec![] })
|
||||
});
|
||||
}
|
||||
let status_listeners_handle = self.status_listeners.clone();
|
||||
let xdg_listener_handle = self.xdg_listener.clone();
|
||||
output.quick_assign(move |output, event, ddata| {
|
||||
process_output_event(
|
||||
output,
|
||||
event,
|
||||
ddata,
|
||||
&status_listeners_handle,
|
||||
&xdg_listener_handle,
|
||||
)
|
||||
});
|
||||
self.outputs.push((id, (*output).clone()));
|
||||
}
|
||||
fn removed(&mut self, id: u32, mut ddata: DispatchData) {
|
||||
let status_listeners_handle = &self.status_listeners;
|
||||
let xdg_listener_handle = &self.xdg_listener;
|
||||
self.outputs.retain(|(i, o)| {
|
||||
if *i != id {
|
||||
true
|
||||
} else {
|
||||
make_obsolete(o, ddata.reborrow(), status_listeners_handle, xdg_listener_handle);
|
||||
false
|
||||
}
|
||||
});
|
||||
}
|
||||
fn get_all(&self) -> Vec<Attached<WlOutput>> {
|
||||
self.outputs.iter().map(|(_, o)| o.clone()).collect()
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Debug for OutputHandler {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
f.debug_struct("OutputHandler")
|
||||
.field("outputs", &self.outputs)
|
||||
.field("status_listeners", &"Fn() -> { ... }")
|
||||
.field("xdg_listener", &self.xdg_listener)
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
|
||||
fn process_output_event(
|
||||
output: Main<WlOutput>,
|
||||
event: Event,
|
||||
mut ddata: DispatchData,
|
||||
listeners: &Rc<RefCell<Vec<rc::Weak<RefCell<OutputStatusCallback>>>>>,
|
||||
xdg_listener: &Option<rc::Weak<RefCell<XdgOutputHandlerInner>>>,
|
||||
) {
|
||||
let udata_mutex = output
|
||||
.as_ref()
|
||||
.user_data()
|
||||
.get::<Mutex<OutputData>>()
|
||||
.expect("SCTK: wl_output has invalid UserData");
|
||||
let mut udata = udata_mutex.lock().unwrap();
|
||||
if let Event::Done = event {
|
||||
let (id, has_xdg, pending_events, mut callbacks) = match *udata {
|
||||
OutputData::Pending { id, has_xdg, events: ref mut v, callbacks: ref mut cb } => {
|
||||
(id, has_xdg, std::mem::take(v), std::mem::take(cb))
|
||||
}
|
||||
OutputData::PendingXDG { ref mut info, ref mut callbacks } => {
|
||||
notify(&output, info, ddata.reborrow(), callbacks);
|
||||
notify_status_listeners(&output, info, ddata, listeners);
|
||||
let info = info.clone();
|
||||
let callbacks = std::mem::take(callbacks);
|
||||
*udata = OutputData::Ready { info, callbacks };
|
||||
return;
|
||||
}
|
||||
OutputData::Ready { ref mut info, ref mut callbacks } => {
|
||||
// a Done event on an output that is already ready was due to a
|
||||
// status change (which was already merged)
|
||||
notify(&output, info, ddata, callbacks);
|
||||
return;
|
||||
}
|
||||
};
|
||||
let mut info = OutputInfo::new(id);
|
||||
for evt in pending_events {
|
||||
merge_event(&mut info, evt);
|
||||
}
|
||||
notify(&output, &info, ddata.reborrow(), &mut callbacks);
|
||||
if let Some(xdg) = xdg_listener.as_ref().and_then(rc::Weak::upgrade) {
|
||||
if has_xdg || xdg.borrow_mut().new_xdg_output(&output, listeners) {
|
||||
*udata = OutputData::PendingXDG { info, callbacks };
|
||||
return;
|
||||
}
|
||||
}
|
||||
notify_status_listeners(&output, &info, ddata, listeners);
|
||||
*udata = OutputData::Ready { info, callbacks };
|
||||
} else {
|
||||
match *udata {
|
||||
OutputData::Pending { events: ref mut v, .. } => v.push(event),
|
||||
OutputData::PendingXDG { ref mut info, .. }
|
||||
| OutputData::Ready { ref mut info, .. } => {
|
||||
merge_event(info, event);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn make_obsolete(
|
||||
output: &Attached<WlOutput>,
|
||||
mut ddata: DispatchData,
|
||||
listeners: &RefCell<Vec<rc::Weak<RefCell<OutputStatusCallback>>>>,
|
||||
xdg_listener: &Option<rc::Weak<RefCell<XdgOutputHandlerInner>>>,
|
||||
) {
|
||||
let udata_mutex = output
|
||||
.as_ref()
|
||||
.user_data()
|
||||
.get::<Mutex<OutputData>>()
|
||||
.expect("SCTK: wl_output has invalid UserData");
|
||||
let mut udata = udata_mutex.lock().unwrap();
|
||||
if let Some(xdg) = xdg_listener.as_ref().and_then(rc::Weak::upgrade) {
|
||||
xdg.borrow_mut().destroy_xdg_output(output);
|
||||
}
|
||||
let (id, mut callbacks) = match *udata {
|
||||
OutputData::PendingXDG { ref mut info, ref mut callbacks }
|
||||
| OutputData::Ready { ref mut info, ref mut callbacks } => {
|
||||
info.obsolete = true;
|
||||
notify(output, info, ddata.reborrow(), callbacks);
|
||||
notify_status_listeners(output, info, ddata, listeners);
|
||||
return;
|
||||
}
|
||||
OutputData::Pending { id, callbacks: ref mut cb, .. } => (id, std::mem::take(cb)),
|
||||
};
|
||||
let mut info = OutputInfo::new(id);
|
||||
info.obsolete = true;
|
||||
notify(output, &info, ddata.reborrow(), &mut callbacks);
|
||||
notify_status_listeners(output, &info, ddata, listeners);
|
||||
*udata = OutputData::Ready { info, callbacks };
|
||||
}
|
||||
|
||||
fn merge_event(info: &mut OutputInfo, event: Event) {
|
||||
match event {
|
||||
Event::Geometry {
|
||||
x,
|
||||
y,
|
||||
physical_width,
|
||||
physical_height,
|
||||
subpixel,
|
||||
model,
|
||||
make,
|
||||
transform,
|
||||
} => {
|
||||
info.location = (x, y);
|
||||
info.physical_size = (physical_width, physical_height);
|
||||
info.subpixel = subpixel;
|
||||
info.transform = transform;
|
||||
info.model = model;
|
||||
info.make = make;
|
||||
}
|
||||
Event::Scale { factor } => {
|
||||
info.scale_factor = factor;
|
||||
}
|
||||
Event::Mode { width, height, refresh, flags } => {
|
||||
let mut found = false;
|
||||
if let Some(mode) = info
|
||||
.modes
|
||||
.iter_mut()
|
||||
.find(|m| m.dimensions == (width, height) && m.refresh_rate == refresh)
|
||||
{
|
||||
// this mode already exists, update it
|
||||
mode.is_preferred = flags.contains(wl_output::Mode::Preferred);
|
||||
mode.is_current = flags.contains(wl_output::Mode::Current);
|
||||
found = true;
|
||||
}
|
||||
if !found {
|
||||
// otherwise, add it
|
||||
info.modes.push(Mode {
|
||||
dimensions: (width, height),
|
||||
refresh_rate: refresh,
|
||||
is_preferred: flags.contains(wl_output::Mode::Preferred),
|
||||
is_current: flags.contains(wl_output::Mode::Current),
|
||||
})
|
||||
}
|
||||
}
|
||||
Event::Name { name } => {
|
||||
info.name = name;
|
||||
}
|
||||
Event::Description { description } => {
|
||||
info.description = description;
|
||||
}
|
||||
// ignore all other events
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
|
||||
fn notify(
|
||||
output: &WlOutput,
|
||||
info: &OutputInfo,
|
||||
mut ddata: DispatchData,
|
||||
callbacks: &mut Vec<sync::Weak<OutputCallback>>,
|
||||
) {
|
||||
callbacks.retain(|weak| {
|
||||
if let Some(arc) = sync::Weak::upgrade(weak) {
|
||||
(*arc)(output.clone(), info, ddata.reborrow());
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
fn notify_status_listeners(
|
||||
output: &WlOutput,
|
||||
info: &OutputInfo,
|
||||
mut ddata: DispatchData,
|
||||
listeners: &RefCell<Vec<rc::Weak<RefCell<OutputStatusCallback>>>>,
|
||||
) {
|
||||
// Notify the callbacks listening for new outputs
|
||||
listeners.borrow_mut().retain(|lst| {
|
||||
if let Some(cb) = rc::Weak::upgrade(lst) {
|
||||
(cb.borrow_mut())(output.clone(), info, ddata.reborrow());
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/// Access the info associated with this output
|
||||
///
|
||||
/// The provided closure is given the [`OutputInfo`](struct.OutputInfo.html) as argument,
|
||||
/// and its return value is returned from this function.
|
||||
///
|
||||
/// If the provided `WlOutput` has not yet been initialized or is not managed by SCTK, `None` is returned.
|
||||
///
|
||||
/// If the output has been removed by the compositor, the `obsolete` field of the `OutputInfo`
|
||||
/// will be set to `true`. This handler will not automatically detroy the output by calling its
|
||||
/// `release` method, to avoid interfering with your logic.
|
||||
pub fn with_output_info<T, F: FnOnce(&OutputInfo) -> T>(output: &WlOutput, f: F) -> Option<T> {
|
||||
if let Some(udata_mutex) = output.as_ref().user_data().get::<Mutex<OutputData>>() {
|
||||
let udata = udata_mutex.lock().unwrap();
|
||||
match *udata {
|
||||
OutputData::PendingXDG { ref info, .. } | OutputData::Ready { ref info, .. } => {
|
||||
Some(f(info))
|
||||
}
|
||||
OutputData::Pending { .. } => None,
|
||||
}
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
/// Add a listener to this output
|
||||
///
|
||||
/// The provided closure will be called whenever a property of the output changes,
|
||||
/// including when it is removed by the compositor (in this case it'll be marked as
|
||||
/// obsolete).
|
||||
///
|
||||
/// The returned [`OutputListener`](struct.OutputListener) keeps your callback alive,
|
||||
/// dropping it will disable the callback and free the closure.
|
||||
pub fn add_output_listener<F: Fn(WlOutput, &OutputInfo, DispatchData) + Send + Sync + 'static>(
|
||||
output: &WlOutput,
|
||||
f: F,
|
||||
) -> OutputListener {
|
||||
let arc = Arc::new(f) as Arc<_>;
|
||||
|
||||
if let Some(udata_mutex) = output.as_ref().user_data().get::<Mutex<OutputData>>() {
|
||||
let mut udata = udata_mutex.lock().unwrap();
|
||||
|
||||
match *udata {
|
||||
OutputData::Pending { ref mut callbacks, .. }
|
||||
| OutputData::PendingXDG { ref mut callbacks, .. }
|
||||
| OutputData::Ready { ref mut callbacks, .. } => {
|
||||
callbacks.push(Arc::downgrade(&arc));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
OutputListener { _cb: arc }
|
||||
}
|
||||
|
||||
/// A handle to an output listener callback
|
||||
///
|
||||
/// Dropping it disables the associated callback and frees the closure.
|
||||
pub struct OutputListener {
|
||||
_cb: Arc<dyn Fn(WlOutput, &OutputInfo, DispatchData) + Send + Sync + 'static>,
|
||||
}
|
||||
|
||||
impl fmt::Debug for OutputListener {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
f.debug_struct("OutputListener").field("_cb", &"fn() -> { ... }").finish()
|
||||
}
|
||||
}
|
||||
|
||||
/// A handle to an output status callback
|
||||
///
|
||||
/// Dropping it disables the associated callback and frees the closure.
|
||||
pub struct OutputStatusListener {
|
||||
_cb: Rc<RefCell<OutputStatusCallback>>,
|
||||
}
|
||||
|
||||
impl fmt::Debug for OutputStatusListener {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
f.debug_struct("OutputStatusListener").field("_cb", &"fn() -> { ... }").finish()
|
||||
}
|
||||
}
|
||||
|
||||
/// Trait representing the OutputHandler functions
|
||||
///
|
||||
/// Implementing this trait on your inner environment struct used with the
|
||||
/// [`environment!`](../macro.environment.html) by delegating it to its
|
||||
/// [`OutputHandler`](struct.OutputHandler.html) field will make available the output-associated
|
||||
/// method on your [`Environment`](../environment/struct.Environment.html).
|
||||
pub trait OutputHandling {
|
||||
/// Insert a listener for output creation and removal events
|
||||
fn listen<F: FnMut(WlOutput, &OutputInfo, DispatchData) + 'static>(
|
||||
&mut self,
|
||||
f: F,
|
||||
) -> OutputStatusListener;
|
||||
}
|
||||
|
||||
impl OutputHandling for OutputHandler {
|
||||
fn listen<F: FnMut(WlOutput, &OutputInfo, DispatchData) + 'static>(
|
||||
&mut self,
|
||||
f: F,
|
||||
) -> OutputStatusListener {
|
||||
let rc = Rc::new(RefCell::new(f)) as Rc<_>;
|
||||
self.status_listeners.borrow_mut().push(Rc::downgrade(&rc));
|
||||
OutputStatusListener { _cb: rc }
|
||||
}
|
||||
}
|
||||
|
||||
impl<E: OutputHandling> crate::environment::Environment<E> {
|
||||
/// Insert a new listener for outputs
|
||||
///
|
||||
/// The provided closure will be invoked whenever a `wl_output` is created or removed.
|
||||
///
|
||||
/// Note that if outputs already exist when this callback is setup, it'll not be invoked on them.
|
||||
/// For you to be notified of them as well, you need to first process them manually by calling
|
||||
/// `.get_all_outputs()`.
|
||||
///
|
||||
/// The returned [`OutputStatusListener`](../output/struct.OutputStatusListener.hmtl) keeps your
|
||||
/// callback alive, dropping it will disable it.
|
||||
#[must_use = "the returned OutputStatusListener keeps your callback alive, dropping it will disable it"]
|
||||
pub fn listen_for_outputs<F: FnMut(WlOutput, &OutputInfo, DispatchData) + 'static>(
|
||||
&self,
|
||||
f: F,
|
||||
) -> OutputStatusListener {
|
||||
self.with_inner(move |inner| OutputHandling::listen(inner, f))
|
||||
}
|
||||
}
|
||||
|
||||
impl<E: crate::environment::MultiGlobalHandler<WlOutput>> crate::environment::Environment<E> {
|
||||
/// Shorthand method to retrieve the list of outputs
|
||||
pub fn get_all_outputs(&self) -> Vec<WlOutput> {
|
||||
self.get_all_globals::<WlOutput>().into_iter().map(|o| o.detach()).collect()
|
||||
}
|
||||
}
|
||||
|
||||
/// A handler for `zxdg_output_manager_v1`
|
||||
///
|
||||
/// This handler adds additional information to the OutputInfo struct that is
|
||||
/// available through the xdg_output interface. Because this requires binding
|
||||
/// the two handlers together when they are being created, it does not work with
|
||||
/// [`new_default_environment!`](../macro.new_default_environment.html); you
|
||||
/// must use [`default_environment!`](../macro.default_environment.html) and
|
||||
/// create the [OutputHandler] outside the constructor.
|
||||
///
|
||||
/// ```no_compile
|
||||
/// let (sctk_outputs, sctk_xdg_out) = smithay_client_toolkit::output::XdgOutputHandler::new_output_handlers();
|
||||
///
|
||||
/// let env = smithay_client_toolkit::environment::Environment::new(&wl_display, &mut wl_queue, Globals {
|
||||
/// sctk_compositor: SimpleGlobal::new(),
|
||||
/// sctk_shm: smithay_client_toolkit::shm::ShmHandler::new(),
|
||||
/// sctk_seats : smithay_client_toolkit::seat::SeatHandler::new(),
|
||||
/// sctk_shell : smithay_client_toolkit::shell::ShellHandler::new(),
|
||||
/// sctk_outputs,
|
||||
/// sctk_xdg_out,
|
||||
/// // ...
|
||||
/// })?;
|
||||
///
|
||||
/// ```
|
||||
#[derive(Debug)]
|
||||
pub struct XdgOutputHandler {
|
||||
inner: Rc<RefCell<XdgOutputHandlerInner>>,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
struct XdgOutputHandlerInner {
|
||||
xdg_manager: Option<Attached<ZxdgOutputManagerV1>>,
|
||||
outputs: Vec<(WlOutput, Attached<ZxdgOutputV1>)>,
|
||||
}
|
||||
|
||||
impl XdgOutputHandler {
|
||||
/// Create a new instance of this handler bound to the given OutputHandler.
|
||||
pub fn new(output_handler: &mut OutputHandler) -> Self {
|
||||
let inner =
|
||||
Rc::new(RefCell::new(XdgOutputHandlerInner { xdg_manager: None, outputs: Vec::new() }));
|
||||
output_handler.xdg_listener = Some(Rc::downgrade(&inner));
|
||||
XdgOutputHandler { inner }
|
||||
}
|
||||
|
||||
/// Helper function to create a bound pair of OutputHandler and XdgOutputHandler.
|
||||
pub fn new_output_handlers() -> (OutputHandler, Self) {
|
||||
let mut oh = OutputHandler::new();
|
||||
let xh = XdgOutputHandler::new(&mut oh);
|
||||
(oh, xh)
|
||||
}
|
||||
}
|
||||
|
||||
impl XdgOutputHandlerInner {
|
||||
fn new_xdg_output(
|
||||
&mut self,
|
||||
output: &WlOutput,
|
||||
listeners: &Rc<RefCell<Vec<rc::Weak<RefCell<OutputStatusCallback>>>>>,
|
||||
) -> bool {
|
||||
if let Some(xdg_manager) = &self.xdg_manager {
|
||||
let xdg_main = xdg_manager.get_xdg_output(output);
|
||||
let wl_out = output.clone();
|
||||
let listeners = listeners.clone();
|
||||
xdg_main.quick_assign(move |_xdg_out, event, ddata| {
|
||||
process_xdg_event(&wl_out, event, ddata, &listeners)
|
||||
});
|
||||
self.outputs.push((output.clone(), xdg_main.into()));
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
fn destroy_xdg_output(&mut self, output: &WlOutput) {
|
||||
self.outputs.retain(|(out, xdg_out)| {
|
||||
if out.as_ref().is_alive() && out != output {
|
||||
true
|
||||
} else {
|
||||
xdg_out.destroy();
|
||||
false
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
fn process_xdg_event(
|
||||
wl_out: &WlOutput,
|
||||
event: zxdg_output_v1::Event,
|
||||
mut ddata: DispatchData,
|
||||
listeners: &RefCell<Vec<rc::Weak<RefCell<OutputStatusCallback>>>>,
|
||||
) {
|
||||
use zxdg_output_v1::Event;
|
||||
let udata_mutex = wl_out
|
||||
.as_ref()
|
||||
.user_data()
|
||||
.get::<Mutex<OutputData>>()
|
||||
.expect("SCTK: wl_output has invalid UserData");
|
||||
let mut udata = udata_mutex.lock().unwrap();
|
||||
let (info, callbacks, pending) = match &mut *udata {
|
||||
OutputData::Ready { info, callbacks } => (info, callbacks, false),
|
||||
OutputData::PendingXDG { info, callbacks } => (info, callbacks, true),
|
||||
OutputData::Pending { .. } => unreachable!(),
|
||||
};
|
||||
match event {
|
||||
Event::Name { name } => {
|
||||
info.name = name;
|
||||
}
|
||||
Event::Description { description } => {
|
||||
info.description = description;
|
||||
}
|
||||
Event::Done => {
|
||||
notify(wl_out, info, ddata.reborrow(), callbacks);
|
||||
if pending {
|
||||
notify_status_listeners(wl_out, info, ddata, listeners);
|
||||
let info = info.clone();
|
||||
let callbacks = std::mem::take(callbacks);
|
||||
*udata = OutputData::Ready { info, callbacks };
|
||||
}
|
||||
}
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
|
||||
impl crate::environment::GlobalHandler<ZxdgOutputManagerV1> for XdgOutputHandler {
|
||||
fn created(
|
||||
&mut self,
|
||||
registry: Attached<wl_registry::WlRegistry>,
|
||||
id: u32,
|
||||
version: u32,
|
||||
_: DispatchData,
|
||||
) {
|
||||
let version = std::cmp::min(version, 3);
|
||||
let mut inner = self.inner.borrow_mut();
|
||||
let xdg_manager: Main<ZxdgOutputManagerV1> = registry.bind(version, id);
|
||||
inner.xdg_manager = Some(xdg_manager.into());
|
||||
}
|
||||
fn get(&self) -> Option<Attached<ZxdgOutputManagerV1>> {
|
||||
let inner = self.inner.borrow();
|
||||
inner.xdg_manager.clone()
|
||||
}
|
||||
}
|
||||
166
third-party/vendor/smithay-client-toolkit/src/primary_selection/device.rs
vendored
Normal file
166
third-party/vendor/smithay-client-toolkit/src/primary_selection/device.rs
vendored
Normal file
|
|
@ -0,0 +1,166 @@
|
|||
use std::sync::{Arc, Mutex};
|
||||
|
||||
use wayland_protocols::{
|
||||
misc::gtk_primary_selection::client::gtk_primary_selection_device::{
|
||||
self, GtkPrimarySelectionDevice,
|
||||
},
|
||||
unstable::primary_selection::v1::client::zwp_primary_selection_device_v1::{
|
||||
self, ZwpPrimarySelectionDeviceV1,
|
||||
},
|
||||
};
|
||||
|
||||
use wayland_client::protocol::wl_seat::WlSeat;
|
||||
|
||||
use crate::primary_selection::offer::PrimarySelectionOfferImpl;
|
||||
use crate::primary_selection::source::PrimarySelectionSourceImpl;
|
||||
|
||||
use super::PrimarySelectionDeviceManager;
|
||||
use super::PrimarySelectionOffer;
|
||||
use super::PrimarySelectionSource;
|
||||
|
||||
/// Handle to support primary selection on a given seat.
|
||||
///
|
||||
/// This type provides you with copy/paste actions. It is associated with a seat upon creation.
|
||||
#[derive(Debug)]
|
||||
pub struct PrimarySelectionDevice {
|
||||
device: PrimarySelectionDeviceImpl,
|
||||
inner: Arc<Mutex<PrimarySelectionDeviceInner>>,
|
||||
}
|
||||
|
||||
/// Possible supported primary selection devices.
|
||||
#[derive(Debug)]
|
||||
enum PrimarySelectionDeviceImpl {
|
||||
Zwp(ZwpPrimarySelectionDeviceV1),
|
||||
Gtk(GtkPrimarySelectionDevice),
|
||||
}
|
||||
|
||||
/// Inner state for `PrimarySelectionDevice`.
|
||||
#[derive(Debug)]
|
||||
struct PrimarySelectionDeviceInner {
|
||||
/// Current selection.
|
||||
selection: Option<PrimarySelectionOffer>,
|
||||
|
||||
/// List of known offers.
|
||||
know_offers: Vec<PrimarySelectionOffer>,
|
||||
}
|
||||
|
||||
impl PrimarySelectionDeviceInner {
|
||||
/// Provide a primary selection source as the new content for the primary selection.
|
||||
///
|
||||
/// Correspond to traditional copy/paste behavior. Setting the source to `None` will clear
|
||||
/// the selection.
|
||||
fn set_selection(&mut self, offer: Option<PrimarySelectionOfferImpl>) {
|
||||
let offer = match offer {
|
||||
Some(offer) => offer,
|
||||
None => {
|
||||
// Drop the current offer if any.
|
||||
self.selection = None;
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
if let Some(id) = self.know_offers.iter().position(|o| o.offer == offer) {
|
||||
self.selection = Some(self.know_offers.swap_remove(id));
|
||||
} else {
|
||||
panic!("Compositor set an unknown primary offer for a primary selection.")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for PrimarySelectionDevice {
|
||||
fn drop(&mut self) {
|
||||
match self.device {
|
||||
PrimarySelectionDeviceImpl::Zwp(ref device) => device.destroy(),
|
||||
PrimarySelectionDeviceImpl::Gtk(ref device) => device.destroy(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl PrimarySelectionDevice {
|
||||
/// Create the `PrimarySelectionDevice` helper for this seat.
|
||||
pub fn init_for_seat(manager: &PrimarySelectionDeviceManager, seat: &WlSeat) -> Self {
|
||||
let inner = Arc::new(Mutex::new(PrimarySelectionDeviceInner {
|
||||
selection: None,
|
||||
know_offers: Vec::new(),
|
||||
}));
|
||||
|
||||
let inner2 = inner.clone();
|
||||
|
||||
let device = match manager {
|
||||
PrimarySelectionDeviceManager::Zwp(zwp_manager) => {
|
||||
let device = zwp_manager.get_device(seat);
|
||||
|
||||
device.quick_assign(move |_, event, _| {
|
||||
let mut inner = inner2.lock().unwrap();
|
||||
|
||||
use zwp_primary_selection_device_v1::Event;
|
||||
match event {
|
||||
Event::DataOffer { offer } => {
|
||||
inner.know_offers.push(PrimarySelectionOffer::from_zwp(offer))
|
||||
}
|
||||
Event::Selection { id } => {
|
||||
let id = id.map(PrimarySelectionOfferImpl::Zwp);
|
||||
inner.set_selection(id);
|
||||
}
|
||||
_ => unreachable!(),
|
||||
}
|
||||
});
|
||||
|
||||
PrimarySelectionDeviceImpl::Zwp(device.detach())
|
||||
}
|
||||
PrimarySelectionDeviceManager::Gtk(gtk_manager) => {
|
||||
let device = gtk_manager.get_device(seat);
|
||||
|
||||
device.quick_assign(move |_, event, _| {
|
||||
let mut inner = inner2.lock().unwrap();
|
||||
|
||||
use gtk_primary_selection_device::Event;
|
||||
match event {
|
||||
Event::DataOffer { offer } => {
|
||||
inner.know_offers.push(PrimarySelectionOffer::from_gtk(offer))
|
||||
}
|
||||
Event::Selection { id } => {
|
||||
let id = id.map(PrimarySelectionOfferImpl::Gtk);
|
||||
inner.set_selection(id);
|
||||
}
|
||||
_ => unreachable!(),
|
||||
}
|
||||
});
|
||||
PrimarySelectionDeviceImpl::Gtk(device.detach())
|
||||
}
|
||||
};
|
||||
|
||||
Self { device, inner }
|
||||
}
|
||||
|
||||
/// Provide a primary selection source as the new content for the primary selection.
|
||||
///
|
||||
/// Correspond to traditional copy/paste behavior. Setting the source to `None` will clear
|
||||
/// the selection.
|
||||
pub fn set_selection(&self, source: &Option<PrimarySelectionSource>, serial: u32) {
|
||||
match self.device {
|
||||
PrimarySelectionDeviceImpl::Zwp(ref device) => {
|
||||
let source = source.as_ref().map(|source| match source.source {
|
||||
PrimarySelectionSourceImpl::Zwp(ref source) => source,
|
||||
// We can't reach `Gtk` source in `Zwp`.
|
||||
_ => unreachable!(),
|
||||
});
|
||||
device.set_selection(source, serial);
|
||||
}
|
||||
PrimarySelectionDeviceImpl::Gtk(ref device) => {
|
||||
let source = source.as_ref().map(|source| match source.source {
|
||||
PrimarySelectionSourceImpl::Gtk(ref source) => source,
|
||||
// We can't reach `Zwp` source in `Gtk`.
|
||||
_ => unreachable!(),
|
||||
});
|
||||
device.set_selection(source, serial);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Access the `PrimarySelectionOffer` currently associated with the primary selection buffer.
|
||||
pub fn with_selection<F: FnOnce(Option<&PrimarySelectionOffer>) -> T, T>(&self, f: F) -> T {
|
||||
let inner = self.inner.lock().unwrap();
|
||||
f(inner.selection.as_ref())
|
||||
}
|
||||
}
|
||||
351
third-party/vendor/smithay-client-toolkit/src/primary_selection/mod.rs
vendored
Normal file
351
third-party/vendor/smithay-client-toolkit/src/primary_selection/mod.rs
vendored
Normal file
|
|
@ -0,0 +1,351 @@
|
|||
//! Helpers to handle primary selection related actions.
|
||||
//!
|
||||
//! If you're not using [`default_environment!`](../macro.default_environment.html) you should
|
||||
//! call `get_primary_selection_manager` to bind proper primary selection manager.
|
||||
|
||||
use std::{cell::RefCell, rc::Rc};
|
||||
|
||||
use wayland_protocols::misc::gtk_primary_selection::client::gtk_primary_selection_device_manager::GtkPrimarySelectionDeviceManager;
|
||||
use wayland_protocols::unstable::primary_selection::v1::client::zwp_primary_selection_device_manager_v1::ZwpPrimarySelectionDeviceManagerV1;
|
||||
|
||||
use wayland_client::{
|
||||
protocol::{wl_registry::WlRegistry, wl_seat::WlSeat},
|
||||
Attached, DispatchData,
|
||||
};
|
||||
|
||||
use crate::lazy_global::LazyGlobal;
|
||||
use crate::seat::{SeatHandling, SeatListener};
|
||||
use crate::{environment::GlobalHandler, MissingGlobal};
|
||||
|
||||
mod device;
|
||||
mod offer;
|
||||
mod source;
|
||||
|
||||
pub use self::device::PrimarySelectionDevice;
|
||||
pub use self::offer::PrimarySelectionOffer;
|
||||
pub use self::source::{PrimarySelectionSource, PrimarySelectionSourceEvent};
|
||||
|
||||
/// A handler for primary selection.
|
||||
///
|
||||
/// It provides automatic tracking of primary selection device for each available seat,
|
||||
/// allowing you to manipulate the primary selection clipboard.
|
||||
///
|
||||
/// It's automatically included in the [`default_environment!`](../macro.default_environment.html).
|
||||
#[derive(Debug)]
|
||||
pub struct PrimarySelectionHandler {
|
||||
inner: Rc<RefCell<PrimarySelectionDeviceManagerInner>>,
|
||||
_listener: SeatListener,
|
||||
}
|
||||
|
||||
/// Possible supported primary selection protocols
|
||||
#[derive(Debug)]
|
||||
pub enum PrimarySelectionDeviceManager {
|
||||
/// The current standard `primary_selection` protocol.
|
||||
Zwp(Attached<ZwpPrimarySelectionDeviceManagerV1>),
|
||||
/// The old `gtk_primary_selection` protocol, which is still used by GTK.
|
||||
Gtk(Attached<GtkPrimarySelectionDeviceManager>),
|
||||
}
|
||||
|
||||
impl PrimarySelectionHandler {
|
||||
/// Initialize a primary selection handler.
|
||||
///
|
||||
/// In requires the access to the seat handler in order to track the creation and removal of
|
||||
/// seats.
|
||||
pub fn init<S: SeatHandling>(seat_handler: &mut S) -> Self {
|
||||
let inner = Rc::new(RefCell::new(PrimarySelectionDeviceManagerInner {
|
||||
registry: None,
|
||||
zwp_mgr: LazyGlobal::Unknown,
|
||||
gtk_mgr: LazyGlobal::Unknown,
|
||||
state: PrimarySelectionDeviceManagerInitState::Pending { seats: Vec::new() },
|
||||
}));
|
||||
|
||||
// Listen for a new seat events to add new primary selection devices on the fly.
|
||||
let seat_inner = inner.clone();
|
||||
let listener = seat_handler.listen(move |seat, seat_data, _| {
|
||||
if seat_data.defunct {
|
||||
seat_inner.borrow_mut().remove_seat(&seat);
|
||||
} else {
|
||||
seat_inner.borrow_mut().new_seat(&seat);
|
||||
}
|
||||
});
|
||||
|
||||
Self { inner, _listener: listener }
|
||||
}
|
||||
}
|
||||
|
||||
/// An interface trait to forward the primary selection device handler capability.
|
||||
///
|
||||
/// You need to implement this trait for your environment struct, by delegating it
|
||||
/// to its `PrimarySelectionHandler` field in order to get the associated methods
|
||||
/// on your [`Environment`](../environment/struct.environment.html).
|
||||
pub trait PrimarySelectionHandling {
|
||||
/// Access the primary selection associated with a seat.
|
||||
///
|
||||
/// Returns an error if the seat is not found (for example if it has since been removed by
|
||||
/// the server) or if the `zwp_primary_selection_device_manager_v1` or
|
||||
/// `gtk_primary_selection_device_manager` globals are missing.
|
||||
fn with_primary_selection<F: FnOnce(&PrimarySelectionDevice)>(
|
||||
&self,
|
||||
seat: &WlSeat,
|
||||
f: F,
|
||||
) -> Result<(), MissingGlobal>;
|
||||
|
||||
/// Get the best available primary selection device manager protocol.
|
||||
///
|
||||
/// Returns `None` if no primary selection device manager was advertised.
|
||||
fn get_primary_selection_manager(&self) -> Option<PrimarySelectionDeviceManager>;
|
||||
}
|
||||
|
||||
impl<E: PrimarySelectionHandling> crate::environment::Environment<E> {
|
||||
/// Get the best available primary selection device manager protocol.
|
||||
///
|
||||
/// Returns `None` if no primary selection device manager was advertised.
|
||||
pub fn get_primary_selection_manager(&self) -> Option<PrimarySelectionDeviceManager> {
|
||||
self.with_inner(|manager| manager.get_primary_selection_manager())
|
||||
}
|
||||
|
||||
/// Access the primary selection associated with a seat.
|
||||
///
|
||||
/// Returns an error if the seat is not found (for example if it has since been removed by
|
||||
/// the server) of if the `zwp_primary_selection_device_manager_v1` or
|
||||
/// `gtk_primary_selection_device_manager` globals are missing.
|
||||
pub fn with_primary_selection<F: FnOnce(&PrimarySelectionDevice)>(
|
||||
&self,
|
||||
seat: &WlSeat,
|
||||
f: F,
|
||||
) -> Result<(), MissingGlobal> {
|
||||
self.with_inner(|inner| inner.with_primary_selection(seat, f))
|
||||
}
|
||||
|
||||
/// Create a new primary selection source.
|
||||
///
|
||||
/// This primary selection source is the basic object for offering primary selection clipboard
|
||||
/// to other clients.
|
||||
///
|
||||
/// Once this source is created, you will need to give it to a
|
||||
/// [`PrimarySelectionDevice`](../primary_selection/struct.PrimarySelectionDevice.html)
|
||||
/// to start interaction.
|
||||
pub fn new_primary_selection_source<F>(
|
||||
&self,
|
||||
mime_types: Vec<String>,
|
||||
callback: F,
|
||||
) -> PrimarySelectionSource
|
||||
where
|
||||
F: FnMut(PrimarySelectionSourceEvent, DispatchData) + 'static,
|
||||
{
|
||||
let manager = match self.get_primary_selection_manager() {
|
||||
Some(manager) => manager,
|
||||
None => panic!("[SCTK] primary selection was required"),
|
||||
};
|
||||
|
||||
PrimarySelectionSource::new(&manager, mime_types, callback)
|
||||
}
|
||||
}
|
||||
|
||||
impl PrimarySelectionHandling for PrimarySelectionHandler {
|
||||
/// Get the best available primary selection device manager protocol.
|
||||
///
|
||||
/// Returns `None` if no primary selection device manager was advertised.
|
||||
fn get_primary_selection_manager(&self) -> Option<PrimarySelectionDeviceManager> {
|
||||
GlobalHandler::<ZwpPrimarySelectionDeviceManagerV1>::get(self)
|
||||
.map(PrimarySelectionDeviceManager::Zwp)
|
||||
.or_else(|| {
|
||||
GlobalHandler::<GtkPrimarySelectionDeviceManager>::get(self)
|
||||
.map(PrimarySelectionDeviceManager::Gtk)
|
||||
})
|
||||
}
|
||||
|
||||
/// Access the primary selection associated with a seat.
|
||||
///
|
||||
/// Returns an error if the seat is not found (for example if it has since been removed by
|
||||
/// the server) of if the `zwp_primary_selection_device_manager_v1` or
|
||||
/// `gtk_primary_selection_device_manager` globals are missing.
|
||||
fn with_primary_selection<F: FnOnce(&PrimarySelectionDevice)>(
|
||||
&self,
|
||||
seat: &WlSeat,
|
||||
f: F,
|
||||
) -> Result<(), MissingGlobal> {
|
||||
self.inner.borrow().with_primary_selection(seat, f)
|
||||
}
|
||||
}
|
||||
|
||||
/// Initialization phase of `PrimarySelectionDeviceManagerInner`.
|
||||
#[derive(Debug)]
|
||||
enum PrimarySelectionDeviceManagerInitState {
|
||||
Ready { manager: PrimarySelectionDeviceManager, devices: Vec<(WlSeat, PrimarySelectionDevice)> },
|
||||
Pending { seats: Vec<WlSeat> },
|
||||
}
|
||||
|
||||
/// Inner mutable state for `PrimarySelectionHandler`.
|
||||
#[derive(Debug)]
|
||||
struct PrimarySelectionDeviceManagerInner {
|
||||
registry: Option<Attached<WlRegistry>>,
|
||||
zwp_mgr: LazyGlobal<ZwpPrimarySelectionDeviceManagerV1>,
|
||||
gtk_mgr: LazyGlobal<GtkPrimarySelectionDeviceManager>,
|
||||
pub state: PrimarySelectionDeviceManagerInitState,
|
||||
}
|
||||
|
||||
impl PrimarySelectionDeviceManagerInner {
|
||||
/// Initialize `PrimarySelectionDeviceManager` and setup `PrimarySelectionDevice` for a
|
||||
/// registered seats, if we have pending `PrimarySelectionDeviceManager`.
|
||||
fn init_selection_manager(&mut self, manager: PrimarySelectionDeviceManager) {
|
||||
let seats =
|
||||
if let PrimarySelectionDeviceManagerInitState::Pending { seats } = &mut self.state {
|
||||
std::mem::take(seats)
|
||||
} else {
|
||||
log::warn!("Ignoring second primary selection manager.");
|
||||
return;
|
||||
};
|
||||
|
||||
let mut devices = Vec::new();
|
||||
|
||||
// Create primary selection devices for each seat.
|
||||
for seat in seats {
|
||||
let device = PrimarySelectionDevice::init_for_seat(&manager, &seat);
|
||||
devices.push((seat.clone(), device));
|
||||
}
|
||||
|
||||
// Mark the state as `Ready`, so we can use our primary selection manager.
|
||||
self.state = PrimarySelectionDeviceManagerInitState::Ready { devices, manager }
|
||||
}
|
||||
|
||||
/// Handle addition of a new seat.
|
||||
fn new_seat(&mut self, seat: &WlSeat) {
|
||||
match &mut self.state {
|
||||
PrimarySelectionDeviceManagerInitState::Ready { devices, manager } => {
|
||||
if devices.iter().any(|(s, _)| s == seat) {
|
||||
// The seat already exists, nothing to do
|
||||
return;
|
||||
}
|
||||
|
||||
// Initialize primary selection device for a new seat.
|
||||
let device = PrimarySelectionDevice::init_for_seat(manager, seat);
|
||||
|
||||
devices.push((seat.clone(), device));
|
||||
}
|
||||
PrimarySelectionDeviceManagerInitState::Pending { seats } => {
|
||||
seats.push(seat.clone());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Handle removal of a seat.
|
||||
fn remove_seat(&mut self, seat: &WlSeat) {
|
||||
match &mut self.state {
|
||||
PrimarySelectionDeviceManagerInitState::Ready { devices, .. } => {
|
||||
devices.retain(|(s, _)| s != seat)
|
||||
}
|
||||
PrimarySelectionDeviceManagerInitState::Pending { seats } => {
|
||||
seats.retain(|s| s != seat)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Access the primary selection associated with a seat.
|
||||
///
|
||||
/// Returns an error if the seat is not found (for example if it has since been removed by
|
||||
/// the server) of if the `zwp_primary_selection_device_manager_v1` or
|
||||
/// `gtk_primary_selection_device_manager` globals are missing.
|
||||
fn with_primary_selection<F: FnOnce(&PrimarySelectionDevice)>(
|
||||
&self,
|
||||
seat: &WlSeat,
|
||||
f: F,
|
||||
) -> Result<(), MissingGlobal> {
|
||||
match &self.state {
|
||||
PrimarySelectionDeviceManagerInitState::Pending { .. } => Err(MissingGlobal),
|
||||
PrimarySelectionDeviceManagerInitState::Ready { devices, .. } => {
|
||||
for (s, device) in devices {
|
||||
if s == seat {
|
||||
f(device);
|
||||
return Ok(());
|
||||
}
|
||||
}
|
||||
|
||||
Err(MissingGlobal)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl GlobalHandler<ZwpPrimarySelectionDeviceManagerV1> for PrimarySelectionHandler {
|
||||
fn created(&mut self, registry: Attached<WlRegistry>, id: u32, version: u32, _: DispatchData) {
|
||||
let mut inner = self.inner.borrow_mut();
|
||||
if inner.registry.is_none() {
|
||||
inner.registry = Some(registry);
|
||||
}
|
||||
|
||||
if let LazyGlobal::Unknown = inner.zwp_mgr {
|
||||
// Mark global as seen.
|
||||
inner.zwp_mgr = LazyGlobal::Seen { id, version };
|
||||
} else {
|
||||
log::warn!(
|
||||
"Compositor advertised zwp_primary_selection_device_manager_v1 multiple\
|
||||
times, ignoring."
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
fn get(&self) -> Option<Attached<ZwpPrimarySelectionDeviceManagerV1>> {
|
||||
let mut inner = self.inner.borrow_mut();
|
||||
match inner.zwp_mgr {
|
||||
LazyGlobal::Bound(ref mgr) => Some(mgr.clone()),
|
||||
LazyGlobal::Unknown => None,
|
||||
LazyGlobal::Seen { id, version } => {
|
||||
// Registry cannot be `None` if we've seen the global.
|
||||
let registry = inner.registry.as_ref().unwrap();
|
||||
|
||||
// Bind zwp primary selection.
|
||||
let version = std::cmp::min(1, version);
|
||||
let mgr = registry.bind::<ZwpPrimarySelectionDeviceManagerV1>(version, id);
|
||||
let manager = PrimarySelectionDeviceManager::Zwp((*mgr).clone());
|
||||
|
||||
// Init zwp selection manager.
|
||||
inner.init_selection_manager(manager);
|
||||
|
||||
inner.zwp_mgr = LazyGlobal::Bound((*mgr).clone());
|
||||
Some((*mgr).clone())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl GlobalHandler<GtkPrimarySelectionDeviceManager> for PrimarySelectionHandler {
|
||||
fn created(&mut self, registry: Attached<WlRegistry>, id: u32, version: u32, _: DispatchData) {
|
||||
let mut inner = self.inner.borrow_mut();
|
||||
if inner.registry.is_none() {
|
||||
inner.registry = Some(registry);
|
||||
}
|
||||
if let LazyGlobal::Unknown = inner.gtk_mgr {
|
||||
// Mark global as seen.
|
||||
inner.gtk_mgr = LazyGlobal::Seen { id, version };
|
||||
} else {
|
||||
log::warn!(
|
||||
"Compositor advertised gtk_primary_selection_device_manager multiple times,\
|
||||
ignoring."
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
fn get(&self) -> Option<Attached<GtkPrimarySelectionDeviceManager>> {
|
||||
let mut inner = self.inner.borrow_mut();
|
||||
match inner.gtk_mgr {
|
||||
LazyGlobal::Bound(ref mgr) => Some(mgr.clone()),
|
||||
LazyGlobal::Unknown => None,
|
||||
LazyGlobal::Seen { id, version } => {
|
||||
// Registry cannot be `None` if we've seen the global.
|
||||
let registry = inner.registry.as_ref().unwrap();
|
||||
|
||||
// Bind gtk primary selection.
|
||||
let version = std::cmp::min(1, version);
|
||||
let mgr = registry.bind::<GtkPrimarySelectionDeviceManager>(version, id);
|
||||
let manager = PrimarySelectionDeviceManager::Gtk((*mgr).clone());
|
||||
|
||||
// Init gtk selection manager.
|
||||
inner.init_selection_manager(manager);
|
||||
|
||||
inner.gtk_mgr = LazyGlobal::Bound((*mgr).clone());
|
||||
Some((*mgr).clone())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
125
third-party/vendor/smithay-client-toolkit/src/primary_selection/offer.rs
vendored
Normal file
125
third-party/vendor/smithay-client-toolkit/src/primary_selection/offer.rs
vendored
Normal file
|
|
@ -0,0 +1,125 @@
|
|||
use std::os::unix::io::FromRawFd;
|
||||
use std::sync::{Arc, Mutex};
|
||||
|
||||
use wayland_client::Main;
|
||||
|
||||
use wayland_protocols::{
|
||||
misc::gtk_primary_selection::client::gtk_primary_selection_offer::{
|
||||
self, GtkPrimarySelectionOffer,
|
||||
},
|
||||
unstable::primary_selection::v1::client::zwp_primary_selection_offer_v1::{
|
||||
self, ZwpPrimarySelectionOfferV1,
|
||||
},
|
||||
};
|
||||
|
||||
use crate::data_device::ReadPipe;
|
||||
|
||||
/// A primary selection offer for receiving data through copy/paste.
|
||||
#[derive(Debug)]
|
||||
pub struct PrimarySelectionOffer {
|
||||
pub(crate) offer: PrimarySelectionOfferImpl,
|
||||
inner: Arc<Mutex<PrimarySelectionOfferInner>>,
|
||||
}
|
||||
|
||||
impl PrimarySelectionOffer {
|
||||
/// Access the list of mime types proposed by this offer.
|
||||
pub fn with_mime_types<F, T>(&self, f: F) -> T
|
||||
where
|
||||
F: FnOnce(&[String]) -> T,
|
||||
{
|
||||
let inner = self.inner.lock().unwrap();
|
||||
f(&inner.mime_types)
|
||||
}
|
||||
|
||||
/// Request to receive the data of a given mime type.
|
||||
///
|
||||
/// Note that you should **not** read the contents right away in a blocking way,
|
||||
/// as you may deadlock your application.
|
||||
pub fn receive(&self, mime_type: String) -> Result<ReadPipe, std::io::Error> {
|
||||
use nix::fcntl::OFlag;
|
||||
use nix::unistd::{close, pipe2};
|
||||
// create a pipe
|
||||
let (readfd, writefd) = pipe2(OFlag::O_CLOEXEC)?;
|
||||
|
||||
match &self.offer {
|
||||
PrimarySelectionOfferImpl::Zwp(offer) => {
|
||||
offer.receive(mime_type, writefd);
|
||||
}
|
||||
PrimarySelectionOfferImpl::Gtk(offer) => {
|
||||
offer.receive(mime_type, writefd);
|
||||
}
|
||||
}
|
||||
|
||||
if let Err(err) = close(writefd) {
|
||||
log::warn!("Failed to close write pipe: {}", err);
|
||||
}
|
||||
|
||||
Ok(unsafe { FromRawFd::from_raw_fd(readfd) })
|
||||
}
|
||||
|
||||
/// Initialize `PrimarySelectionOffer` from the `Zwp` offer.
|
||||
pub(crate) fn from_zwp(offer: Main<ZwpPrimarySelectionOfferV1>) -> Self {
|
||||
let inner = Arc::new(Mutex::new(PrimarySelectionOfferInner::new()));
|
||||
let inner2 = inner.clone();
|
||||
|
||||
offer.quick_assign(move |_, event, _| {
|
||||
use zwp_primary_selection_offer_v1::Event;
|
||||
let mut inner = inner2.lock().unwrap();
|
||||
match event {
|
||||
Event::Offer { mime_type } => {
|
||||
inner.mime_types.push(mime_type);
|
||||
}
|
||||
_ => unreachable!(),
|
||||
}
|
||||
});
|
||||
|
||||
Self { offer: PrimarySelectionOfferImpl::Zwp(offer.detach()), inner }
|
||||
}
|
||||
|
||||
/// Initialize `PrimarySelectionOffer` from the `Gtk` offer.
|
||||
pub(crate) fn from_gtk(offer: Main<GtkPrimarySelectionOffer>) -> Self {
|
||||
let inner = Arc::new(Mutex::new(PrimarySelectionOfferInner::new()));
|
||||
let inner2 = inner.clone();
|
||||
|
||||
offer.quick_assign(move |_, event, _| {
|
||||
use gtk_primary_selection_offer::Event;
|
||||
let mut inner = inner2.lock().unwrap();
|
||||
match event {
|
||||
Event::Offer { mime_type } => {
|
||||
inner.mime_types.push(mime_type);
|
||||
}
|
||||
_ => unreachable!(),
|
||||
}
|
||||
});
|
||||
|
||||
Self { offer: PrimarySelectionOfferImpl::Gtk(offer.detach()), inner }
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for PrimarySelectionOffer {
|
||||
fn drop(&mut self) {
|
||||
match &self.offer {
|
||||
PrimarySelectionOfferImpl::Zwp(offer) => offer.destroy(),
|
||||
PrimarySelectionOfferImpl::Gtk(offer) => offer.destroy(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Inner state for `PrimarySelectionOffer`.
|
||||
#[derive(Debug, Default)]
|
||||
struct PrimarySelectionOfferInner {
|
||||
mime_types: Vec<String>,
|
||||
}
|
||||
|
||||
impl PrimarySelectionOfferInner {
|
||||
fn new() -> Self {
|
||||
Self::default()
|
||||
}
|
||||
}
|
||||
|
||||
/// Possible supported primary selection offers.
|
||||
#[derive(Debug, Eq, PartialEq)]
|
||||
pub(crate) enum PrimarySelectionOfferImpl {
|
||||
Zwp(ZwpPrimarySelectionOfferV1),
|
||||
Gtk(GtkPrimarySelectionOffer),
|
||||
}
|
||||
138
third-party/vendor/smithay-client-toolkit/src/primary_selection/source.rs
vendored
Normal file
138
third-party/vendor/smithay-client-toolkit/src/primary_selection/source.rs
vendored
Normal file
|
|
@ -0,0 +1,138 @@
|
|||
use wayland_protocols::unstable::primary_selection::v1::client::zwp_primary_selection_source_v1::{
|
||||
self, ZwpPrimarySelectionSourceV1,
|
||||
};
|
||||
|
||||
use wayland_protocols::misc::gtk_primary_selection::client::gtk_primary_selection_source::{
|
||||
self, GtkPrimarySelectionSource,
|
||||
};
|
||||
|
||||
use crate::data_device::WritePipe;
|
||||
|
||||
use std::os::unix::io::FromRawFd;
|
||||
|
||||
use wayland_client::DispatchData;
|
||||
|
||||
use super::PrimarySelectionDeviceManager;
|
||||
|
||||
/// A primary selection source for sending data through copy/paste.
|
||||
#[derive(Debug)]
|
||||
pub struct PrimarySelectionSource {
|
||||
pub(crate) source: PrimarySelectionSourceImpl,
|
||||
}
|
||||
|
||||
/// Possible events a primary selection source needs to react to.
|
||||
#[derive(Debug)]
|
||||
pub enum PrimarySelectionSourceEvent {
|
||||
/// Write the offered data for selected mime type.
|
||||
Send {
|
||||
/// Requested mime type.
|
||||
mime_type: String,
|
||||
/// Pipe to write into.
|
||||
pipe: WritePipe,
|
||||
},
|
||||
|
||||
/// The action using the primary selection source was cancelled.
|
||||
///
|
||||
/// Once this event is received, the `PrimarySelectionSource` can not be used any more,
|
||||
/// and you should drop it for cleanup.
|
||||
///
|
||||
/// Happens if the user replaces primary selection buffer.
|
||||
Cancelled,
|
||||
}
|
||||
|
||||
impl PrimarySelectionSource {
|
||||
/// Create a new primary selection source.
|
||||
///
|
||||
/// You'll then need to provide a primary selection device to send via selection.
|
||||
pub fn new<F, S, It>(
|
||||
manager: &PrimarySelectionDeviceManager,
|
||||
mime_types: It,
|
||||
mut callback: F,
|
||||
) -> Self
|
||||
where
|
||||
F: FnMut(PrimarySelectionSourceEvent, DispatchData) + 'static,
|
||||
S: Into<String>,
|
||||
It: IntoIterator<Item = S>,
|
||||
{
|
||||
match manager {
|
||||
PrimarySelectionDeviceManager::Zwp(ref manager) => {
|
||||
let source = manager.create_source();
|
||||
source.quick_assign(move |source, event, dispatch_data| {
|
||||
zwp_primary_source_imp(&source, event, dispatch_data, &mut callback);
|
||||
});
|
||||
|
||||
for mime in mime_types {
|
||||
source.offer(mime.into());
|
||||
}
|
||||
|
||||
Self { source: PrimarySelectionSourceImpl::Zwp(source.detach()) }
|
||||
}
|
||||
PrimarySelectionDeviceManager::Gtk(ref manager) => {
|
||||
let source = manager.create_source();
|
||||
source.quick_assign(move |source, event, dispatch_data| {
|
||||
gtk_primary_source_imp(&source, event, dispatch_data, &mut callback);
|
||||
});
|
||||
|
||||
for mime in mime_types {
|
||||
source.offer(mime.into());
|
||||
}
|
||||
|
||||
Self { source: PrimarySelectionSourceImpl::Gtk(source.detach()) }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Possible supported primary selection sources.
|
||||
#[derive(Debug)]
|
||||
pub(crate) enum PrimarySelectionSourceImpl {
|
||||
Zwp(ZwpPrimarySelectionSourceV1),
|
||||
Gtk(GtkPrimarySelectionSource),
|
||||
}
|
||||
|
||||
fn gtk_primary_source_imp<Impl>(
|
||||
source: &GtkPrimarySelectionSource,
|
||||
event: gtk_primary_selection_source::Event,
|
||||
dispatch_data: DispatchData,
|
||||
implem: &mut Impl,
|
||||
) where
|
||||
Impl: FnMut(PrimarySelectionSourceEvent, DispatchData),
|
||||
{
|
||||
use gtk_primary_selection_source::Event;
|
||||
let event = match event {
|
||||
Event::Send { mime_type, fd } => PrimarySelectionSourceEvent::Send {
|
||||
mime_type,
|
||||
pipe: unsafe { FromRawFd::from_raw_fd(fd) },
|
||||
},
|
||||
Event::Cancelled => {
|
||||
source.destroy();
|
||||
PrimarySelectionSourceEvent::Cancelled
|
||||
}
|
||||
_ => unreachable!(),
|
||||
};
|
||||
|
||||
implem(event, dispatch_data);
|
||||
}
|
||||
|
||||
fn zwp_primary_source_imp<Impl>(
|
||||
source: &ZwpPrimarySelectionSourceV1,
|
||||
event: zwp_primary_selection_source_v1::Event,
|
||||
dispatch_data: DispatchData,
|
||||
implem: &mut Impl,
|
||||
) where
|
||||
Impl: FnMut(PrimarySelectionSourceEvent, DispatchData),
|
||||
{
|
||||
use zwp_primary_selection_source_v1::Event;
|
||||
let event = match event {
|
||||
Event::Send { mime_type, fd } => PrimarySelectionSourceEvent::Send {
|
||||
mime_type,
|
||||
pipe: unsafe { FromRawFd::from_raw_fd(fd) },
|
||||
},
|
||||
Event::Cancelled => {
|
||||
source.destroy();
|
||||
PrimarySelectionSourceEvent::Cancelled
|
||||
}
|
||||
_ => unreachable!(),
|
||||
};
|
||||
implem(event, dispatch_data);
|
||||
}
|
||||
266
third-party/vendor/smithay-client-toolkit/src/seat/keyboard/ffi.rs
vendored
Normal file
266
third-party/vendor/smithay-client-toolkit/src/seat/keyboard/ffi.rs
vendored
Normal file
|
|
@ -0,0 +1,266 @@
|
|||
#![allow(dead_code, non_camel_case_types, clippy::identity_op)]
|
||||
|
||||
use std::os::raw::{c_char, c_int, c_void, c_uint};
|
||||
|
||||
pub const XKB_MOD_NAME_SHIFT : &[u8] = b"Shift\0";
|
||||
pub const XKB_MOD_NAME_CAPS : &[u8] = b"Lock\0";
|
||||
pub const XKB_MOD_NAME_CTRL : &[u8] = b"Control\0";
|
||||
pub const XKB_MOD_NAME_ALT : &[u8] = b"Mod1\0";
|
||||
pub const XKB_MOD_NAME_NUM : &[u8] = b"Mod2\0";
|
||||
pub const XKB_MOD_NAME_LOGO : &[u8] = b"Mod4\0";
|
||||
|
||||
pub const XKB_LED_NAME_CAPS : &[u8] = b"Caps Lock\0";
|
||||
pub const XKB_LED_NAME_NUM : &[u8] = b"Num Lock\0";
|
||||
pub const XKB_LED_NAME_SCROLL : &[u8] = b"Scroll Lock\0";
|
||||
|
||||
pub struct xkb_context;
|
||||
pub struct xkb_keymap;
|
||||
pub struct xkb_state;
|
||||
pub struct xkb_compose_table;
|
||||
pub struct xkb_compose_state;
|
||||
|
||||
pub type xkb_keycode_t = u32;
|
||||
pub type xkb_keysym_t = u32;
|
||||
pub type xkb_layout_index_t = u32;
|
||||
pub type xkb_layout_mask_t = u32;
|
||||
pub type xkb_level_index_t = u32;
|
||||
pub type xkb_mod_index_t = u32;
|
||||
pub type xkb_mod_mask_t = u32;
|
||||
pub type xkb_led_index_t = u32;
|
||||
pub type xkb_led_mask_t = u32;
|
||||
|
||||
pub const XKB_KEYCODE_INVALID :u32 = 0xffff_ffff;
|
||||
pub const XKB_LAYOUT_INVALID :u32 = 0xffff_ffff;
|
||||
pub const XKB_LEVEL_INVALID :u32 = 0xffff_ffff;
|
||||
pub const XKB_MOD_INVALID :u32 = 0xffff_ffff;
|
||||
pub const XKB_LED_INVALID :u32 = 0xffff_ffff;
|
||||
pub const XKB_KEYCODE_MAX :u32 = 0xffff_fffe;
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Copy, Clone)]
|
||||
pub struct xkb_rule_names {
|
||||
pub rules: *const c_char,
|
||||
pub model: *const c_char ,
|
||||
pub layout: *const c_char,
|
||||
pub variant: *const c_char,
|
||||
pub options: *const c_char,
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Copy, Clone, Debug, PartialEq)]
|
||||
pub enum xkb_keysym_flags {
|
||||
/** Do not apply any flags. */
|
||||
XKB_KEYSYM_NO_FLAGS = 0,
|
||||
/** Find keysym by case-insensitive search. */
|
||||
XKB_KEYSYM_CASE_INSENSITIVE = 1 << 0
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Copy, Clone, Debug, PartialEq)]
|
||||
pub enum xkb_context_flags {
|
||||
/** Do not apply any context flags. */
|
||||
XKB_CONTEXT_NO_FLAGS = 0,
|
||||
/** Create this context with an empty include path. */
|
||||
XKB_CONTEXT_NO_DEFAULT_INCLUDES = 1 << 0,
|
||||
/**
|
||||
* Don't take RMLVO names from the environment.
|
||||
* @since 0.3.0
|
||||
*/
|
||||
XKB_CONTEXT_NO_ENVIRONMENT_NAMES = 1 << 1
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Copy, Clone, Debug, PartialEq)]
|
||||
pub enum xkb_log_level {
|
||||
/** Log critical internal errors only. */
|
||||
XKB_LOG_LEVEL_CRITICAL = 10,
|
||||
/** Log all errors. */
|
||||
XKB_LOG_LEVEL_ERROR = 20,
|
||||
/** Log warnings and errors. */
|
||||
XKB_LOG_LEVEL_WARNING = 30,
|
||||
/** Log information, warnings, and errors. */
|
||||
XKB_LOG_LEVEL_INFO = 40,
|
||||
/** Log everything. */
|
||||
XKB_LOG_LEVEL_DEBUG = 50
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Copy, Clone, Debug, PartialEq)]
|
||||
pub enum xkb_keymap_compile_flags {
|
||||
/** Do not apply any flags. */
|
||||
XKB_KEYMAP_COMPILE_NO_FLAGS = 0,
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Copy, Clone, Debug, PartialEq)]
|
||||
pub enum xkb_keymap_format {
|
||||
/** Cannot be used for creation */
|
||||
XKB_KEYMAP_USE_ORIGINAL_FORMAT = 0,
|
||||
/** The current/classic XKB text format, as generated by xkbcomp -xkb. */
|
||||
XKB_KEYMAP_FORMAT_TEXT_V1 = 1,
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Copy, Clone, Debug, PartialEq)]
|
||||
pub enum xkb_key_direction {
|
||||
/** The key was released. */
|
||||
XKB_KEY_UP,
|
||||
/** The key was pressed. */
|
||||
XKB_KEY_DOWN
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Copy, Clone, Debug, PartialEq)]
|
||||
pub enum xkb_compose_compile_flags {
|
||||
XKB_COMPOSE_COMPILE_NO_FLAGS = 0
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Copy, Clone, Debug, PartialEq)]
|
||||
pub enum xkb_compose_format {
|
||||
XKB_COMPOSE_FORMAT_TEXT_V1 = 1
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Copy, Clone, Debug, PartialEq)]
|
||||
pub enum xkb_compose_state_flags {
|
||||
XKB_COMPOSE_STATE_NO_FLAGS = 0
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Copy, Clone, Debug, PartialEq)]
|
||||
pub enum xkb_compose_status {
|
||||
XKB_COMPOSE_NOTHING,
|
||||
XKB_COMPOSE_COMPOSING,
|
||||
XKB_COMPOSE_COMPOSED,
|
||||
XKB_COMPOSE_CANCELLED
|
||||
}
|
||||
|
||||
#[repr(C)]
|
||||
#[derive(Copy, Clone, Debug, PartialEq)]
|
||||
pub enum xkb_compose_feed_result {
|
||||
XKB_COMPOSE_FEED_IGNORED,
|
||||
XKB_COMPOSE_FEED_ACCEPTED
|
||||
}
|
||||
|
||||
bitflags::bitflags!(
|
||||
pub struct xkb_state_component: u32 {
|
||||
/** Depressed modifiers, i.e. a key is physically holding them. */
|
||||
const XKB_STATE_MODS_DEPRESSED = (1 << 0);
|
||||
/** Latched modifiers, i.e. will be unset after the next non-modifier
|
||||
* key press. */
|
||||
const XKB_STATE_MODS_LATCHED = (1 << 1);
|
||||
/** Locked modifiers, i.e. will be unset after the key provoking the
|
||||
* lock has been pressed again. */
|
||||
const XKB_STATE_MODS_LOCKED = (1 << 2);
|
||||
/** Effective modifiers, i.e. currently active and affect key
|
||||
* processing (derived from the other state components).
|
||||
* Use this unless you explictly care how the state came about. */
|
||||
const XKB_STATE_MODS_EFFECTIVE = (1 << 3);
|
||||
/** Depressed layout, i.e. a key is physically holding it. */
|
||||
const XKB_STATE_LAYOUT_DEPRESSED = (1 << 4);
|
||||
/** Latched layout, i.e. will be unset after the next non-modifier
|
||||
* key press. */
|
||||
const XKB_STATE_LAYOUT_LATCHED = (1 << 5);
|
||||
/** Locked layout, i.e. will be unset after the key provoking the lock
|
||||
* has been pressed again. */
|
||||
const XKB_STATE_LAYOUT_LOCKED = (1 << 6);
|
||||
/** Effective layout, i.e. currently active and affects key processing
|
||||
* (derived from the other state components).
|
||||
* Use this unless you explictly care how the state came about. */
|
||||
const XKB_STATE_LAYOUT_EFFECTIVE = (1 << 7);
|
||||
/** LEDs (derived from the other state components). */
|
||||
const XKB_STATE_LEDS = (1 << 8);
|
||||
}
|
||||
);
|
||||
|
||||
external_library!(XkbCommon, "xkbcommon",
|
||||
functions:
|
||||
fn xkb_keysym_get_name(xkb_keysym_t, *mut c_char, usize) -> c_int,
|
||||
fn xkb_keysym_from_name(*const c_char, xkb_keysym_flags) -> xkb_keysym_t,
|
||||
fn xkb_keysym_to_utf8(xkb_keysym_t, *mut c_char, usize) -> c_int,
|
||||
fn xkb_keysym_to_utf32(xkb_keysym_t) -> u32,
|
||||
fn xkb_context_new(xkb_context_flags) -> *mut xkb_context,
|
||||
fn xkb_context_ref(*mut xkb_context) -> *mut xkb_context,
|
||||
fn xkb_context_unref(*mut xkb_context) -> (),
|
||||
fn xkb_context_set_user_data(*mut xkb_context, *mut c_void) -> (),
|
||||
fn xkb_context_get_user_data(*mut xkb_context) -> *mut c_void,
|
||||
fn xkb_context_include_path_append(*mut xkb_context, *const c_char) -> c_int,
|
||||
fn xkb_context_include_path_append_default(*mut xkb_context) -> c_int,
|
||||
fn xkb_context_include_path_reset_defaults(*mut xkb_context) -> c_int,
|
||||
fn xkb_context_include_path_clear(*mut xkb_context) -> (),
|
||||
fn xkb_context_num_include_paths(*mut xkb_context) -> c_uint,
|
||||
fn xkb_context_include_path_get(*mut xkb_context, c_uint) -> *const c_char,
|
||||
fn xkb_context_set_log_level(*mut xkb_context, xkb_log_level) -> (),
|
||||
fn xkb_context_get_log_level(*mut xkb_context) -> xkb_log_level,
|
||||
fn xkb_context_set_log_verbosity(*mut xkb_context, c_int) -> (),
|
||||
fn xkb_context_get_log_verbosity(*mut xkb_context) -> c_int,
|
||||
fn xkb_keymap_new_from_names(*mut xkb_context,
|
||||
*const xkb_rule_names,
|
||||
xkb_keymap_compile_flags
|
||||
) -> *mut xkb_keymap,
|
||||
fn xkb_keymap_new_from_string(*mut xkb_context,
|
||||
*const c_char,
|
||||
xkb_keymap_format,
|
||||
xkb_keymap_compile_flags
|
||||
) -> *mut xkb_keymap,
|
||||
fn xkb_keymap_new_from_buffer(*mut xkb_context,
|
||||
*const c_char,
|
||||
usize,
|
||||
xkb_keymap_format,
|
||||
xkb_keymap_compile_flags
|
||||
) -> *mut xkb_keymap,
|
||||
fn xkb_keymap_ref(*mut xkb_keymap) -> *mut xkb_keymap,
|
||||
fn xkb_keymap_unref(*mut xkb_keymap) -> (),
|
||||
fn xkb_keymap_get_as_string(*mut xkb_keymap, xkb_keymap_format) -> *const c_char,
|
||||
fn xkb_keymap_key_repeats(*mut xkb_keymap, xkb_keycode_t) -> c_int,
|
||||
|
||||
fn xkb_state_new(*mut xkb_keymap) -> *mut xkb_state,
|
||||
fn xkb_state_ref(*mut xkb_state) -> *mut xkb_state,
|
||||
fn xkb_state_unref(*mut xkb_state) -> (),
|
||||
fn xkb_state_update_mask(*mut xkb_state,
|
||||
xkb_mod_mask_t,
|
||||
xkb_mod_mask_t,
|
||||
xkb_mod_mask_t,
|
||||
xkb_layout_index_t,
|
||||
xkb_layout_index_t,
|
||||
xkb_layout_index_t
|
||||
) -> xkb_state_component,
|
||||
fn xkb_state_update_key(*mut xkb_state,
|
||||
xkb_keycode_t,
|
||||
xkb_key_direction
|
||||
) -> xkb_state_component,
|
||||
fn xkb_state_key_get_syms(*mut xkb_state,
|
||||
xkb_keycode_t,
|
||||
*const *mut xkb_keysym_t
|
||||
) -> c_int,
|
||||
fn xkb_state_key_get_utf8(*mut xkb_state,
|
||||
xkb_keycode_t,
|
||||
*mut c_char,
|
||||
usize
|
||||
) -> c_int,
|
||||
fn xkb_state_key_get_utf32(*mut xkb_state, xkb_keycode_t) -> u32,
|
||||
fn xkb_state_key_get_one_sym(*mut xkb_state, xkb_keycode_t) -> xkb_keysym_t,
|
||||
fn xkb_state_mod_name_is_active(*mut xkb_state, *const c_char, xkb_state_component) -> c_int,
|
||||
fn xkb_compose_table_new_from_locale(*mut xkb_context, *const c_char, xkb_compose_compile_flags) -> *mut xkb_compose_table,
|
||||
fn xkb_compose_table_unref(*mut xkb_compose_table) -> (),
|
||||
fn xkb_compose_state_new(*mut xkb_compose_table, xkb_compose_state_flags) -> *mut xkb_compose_state,
|
||||
fn xkb_compose_state_unref(*mut xkb_compose_state) -> (),
|
||||
fn xkb_compose_state_feed(*mut xkb_compose_state, xkb_keysym_t) -> xkb_compose_feed_result,
|
||||
fn xkb_compose_state_reset(*mut xkb_compose_state) -> (),
|
||||
fn xkb_compose_state_get_status(*mut xkb_compose_state) -> xkb_compose_status,
|
||||
fn xkb_compose_state_get_utf8(*mut xkb_compose_state, *mut c_char, usize) -> c_int,
|
||||
fn xkb_compose_state_get_one_sym(*mut xkb_compose_state) -> xkb_keysym_t,
|
||||
);
|
||||
|
||||
#[cfg(feature = "dlopen")]
|
||||
lazy_static::lazy_static!(
|
||||
pub static ref XKBCOMMON_OPTION: Option<XkbCommon> = unsafe {
|
||||
XkbCommon::open("libxkbcommon.so.0")
|
||||
.or_else(|_| XkbCommon::open("libxkbcommon.so"))
|
||||
.ok()
|
||||
};
|
||||
pub static ref XKBCOMMON_HANDLE: &'static XkbCommon = {
|
||||
XKBCOMMON_OPTION.as_ref().expect("Library libxkbcommon.so could not be loaded.")
|
||||
};
|
||||
);
|
||||
3005
third-party/vendor/smithay-client-toolkit/src/seat/keyboard/keysyms.rs
vendored
Normal file
3005
third-party/vendor/smithay-client-toolkit/src/seat/keyboard/keysyms.rs
vendored
Normal file
File diff suppressed because it is too large
Load diff
647
third-party/vendor/smithay-client-toolkit/src/seat/keyboard/mod.rs
vendored
Normal file
647
third-party/vendor/smithay-client-toolkit/src/seat/keyboard/mod.rs
vendored
Normal file
|
|
@ -0,0 +1,647 @@
|
|||
//! Utilities for keymap interpretation of keyboard input
|
||||
//!
|
||||
//! This module provides an implementation for `wl_keyboard`
|
||||
//! objects using `libxkbcommon` to interpret the keyboard input
|
||||
//! given the user keymap.
|
||||
//!
|
||||
//! The entry point of this module is the [`map_keyboard`](fn.map_keyboard.html)
|
||||
//! function which, given a `wl_seat` and a callback, setup keymap interpretation
|
||||
//! and key repetition for the `wl_keyboard` of this seat.
|
||||
//!
|
||||
//! Key repetition relies on an event source, that needs to be inserted in your
|
||||
//! calloop event loop. Not doing so will prevent key repetition to work
|
||||
//! (but the rest of the functionnality will not be affected).
|
||||
|
||||
#[cfg(feature = "calloop")]
|
||||
use calloop::{timer::Timer, RegistrationToken};
|
||||
#[cfg(feature = "calloop")]
|
||||
use std::num::NonZeroU32;
|
||||
#[cfg(feature = "calloop")]
|
||||
use std::time::Duration;
|
||||
use std::{
|
||||
cell::{Cell, RefCell},
|
||||
convert::TryInto,
|
||||
fs::File,
|
||||
os::unix::io::{FromRawFd, RawFd},
|
||||
rc::Rc,
|
||||
time::Instant,
|
||||
};
|
||||
pub use wayland_client::protocol::wl_keyboard::KeyState;
|
||||
use wayland_client::{
|
||||
protocol::{wl_keyboard, wl_seat, wl_surface},
|
||||
Attached,
|
||||
};
|
||||
|
||||
#[rustfmt::skip]
|
||||
mod ffi;
|
||||
mod state;
|
||||
#[rustfmt::skip]
|
||||
pub mod keysyms;
|
||||
|
||||
use self::state::KbState;
|
||||
pub use self::state::{ModifiersState, RMLVO};
|
||||
|
||||
#[cfg(feature = "calloop")]
|
||||
const MICROS_IN_SECOND: u32 = 1000000;
|
||||
|
||||
/// Possible kinds of key repetition
|
||||
#[derive(Debug)]
|
||||
pub enum RepeatKind {
|
||||
/// keys will be repeated at a set rate and delay
|
||||
Fixed {
|
||||
/// The number of repetitions per second that should occur.
|
||||
rate: u32,
|
||||
/// delay (in milliseconds) between a key press and the start of repetition
|
||||
delay: u32,
|
||||
},
|
||||
/// keys will be repeated at a rate and delay set by the wayland server
|
||||
System,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
/// An error that occurred while trying to initialize a mapped keyboard
|
||||
pub enum Error {
|
||||
/// libxkbcommon is not available
|
||||
XKBNotFound,
|
||||
/// Provided RMLVO specified a keymap that would not be loaded
|
||||
BadNames,
|
||||
/// The provided seat does not have the keyboard capability
|
||||
NoKeyboard,
|
||||
/// Failed to init timers for repetition
|
||||
TimerError(std::io::Error),
|
||||
}
|
||||
|
||||
/// Events received from a mapped keyboard
|
||||
#[derive(Debug)]
|
||||
pub enum Event<'a> {
|
||||
/// The keyboard focus has entered a surface
|
||||
Enter {
|
||||
/// serial number of the event
|
||||
serial: u32,
|
||||
/// surface that was entered
|
||||
surface: wl_surface::WlSurface,
|
||||
/// raw values of the currently pressed keys
|
||||
rawkeys: &'a [u32],
|
||||
/// interpreted symbols of the currently pressed keys
|
||||
keysyms: &'a [u32],
|
||||
},
|
||||
/// The keyboard focus has left a surface
|
||||
Leave {
|
||||
/// serial number of the event
|
||||
serial: u32,
|
||||
/// surface that was left
|
||||
surface: wl_surface::WlSurface,
|
||||
},
|
||||
/// The key modifiers have changed state
|
||||
Modifiers {
|
||||
/// current state of the modifiers
|
||||
modifiers: ModifiersState,
|
||||
},
|
||||
/// A key event occurred
|
||||
Key {
|
||||
/// serial number of the event
|
||||
serial: u32,
|
||||
/// time at which the keypress occurred
|
||||
time: u32,
|
||||
/// raw value of the key
|
||||
rawkey: u32,
|
||||
/// interpreted symbol of the key
|
||||
keysym: u32,
|
||||
/// new state of the key
|
||||
state: KeyState,
|
||||
/// utf8 interpretation of the entered text
|
||||
///
|
||||
/// will always be `None` on key release events
|
||||
utf8: Option<String>,
|
||||
},
|
||||
/// A key repetition event
|
||||
Repeat {
|
||||
/// time at which the repetition occured
|
||||
time: u32,
|
||||
/// raw value of the key
|
||||
rawkey: u32,
|
||||
/// interpreted symbol of the key
|
||||
keysym: u32,
|
||||
/// utf8 interpretation of the entered text
|
||||
utf8: Option<String>,
|
||||
},
|
||||
}
|
||||
|
||||
/// Implement a keyboard for keymap translation with key repetition
|
||||
///
|
||||
/// This requires you to provide a callback to receive the events after they
|
||||
/// have been interpreted with the keymap.
|
||||
///
|
||||
/// The keymap will be loaded from the provided RMLVO rules, or from the compositor
|
||||
/// provided keymap if `None`.
|
||||
///
|
||||
/// Returns an error if xkbcommon could not be initialized, the RMLVO specification
|
||||
/// contained invalid values, or if the provided seat does not have keyboard capability.
|
||||
///
|
||||
/// **Note:** This adapter does not handle key repetition. See `map_keyboard_repeat` for that.
|
||||
pub fn map_keyboard<F>(
|
||||
seat: &Attached<wl_seat::WlSeat>,
|
||||
rmlvo: Option<RMLVO>,
|
||||
callback: F,
|
||||
) -> Result<wl_keyboard::WlKeyboard, Error>
|
||||
where
|
||||
F: FnMut(Event<'_>, wl_keyboard::WlKeyboard, wayland_client::DispatchData<'_>) + 'static,
|
||||
{
|
||||
let has_kbd = super::with_seat_data(seat, |data| data.has_keyboard).unwrap_or(false);
|
||||
let keyboard = if has_kbd {
|
||||
seat.get_keyboard()
|
||||
} else {
|
||||
return Err(Error::NoKeyboard);
|
||||
};
|
||||
|
||||
let state = Rc::new(RefCell::new(rmlvo.map(KbState::from_rmlvo).unwrap_or_else(KbState::new)?));
|
||||
|
||||
let callback = Rc::new(RefCell::new(callback)) as Rc<RefCell<_>>;
|
||||
|
||||
// prepare the handler
|
||||
let mut kbd_handler = KbdHandler {
|
||||
callback,
|
||||
state,
|
||||
#[cfg(feature = "calloop")]
|
||||
repeat: None,
|
||||
};
|
||||
|
||||
keyboard.quick_assign(move |keyboard, event, data| {
|
||||
kbd_handler.event(keyboard.detach(), event, data)
|
||||
});
|
||||
|
||||
Ok(keyboard.detach())
|
||||
}
|
||||
|
||||
/// Implement a keyboard for keymap translation with key repetition
|
||||
///
|
||||
/// This requires you to provide a callback to receive the events after they
|
||||
/// have been interpreted with the keymap.
|
||||
///
|
||||
/// The keymap will be loaded from the provided RMLVO rules, or from the compositor
|
||||
/// provided keymap if `None`.
|
||||
///
|
||||
/// Returns an error if xkbcommon could not be initialized, the RMLVO specification
|
||||
/// contained invalid values, or if the provided seat does not have keyboard capability.
|
||||
///
|
||||
/// **Note:** The keyboard repetition handling requires the `calloop` cargo feature.
|
||||
#[cfg(feature = "calloop")]
|
||||
pub fn map_keyboard_repeat<F, Data: 'static>(
|
||||
loop_handle: calloop::LoopHandle<'static, Data>,
|
||||
seat: &Attached<wl_seat::WlSeat>,
|
||||
rmlvo: Option<RMLVO>,
|
||||
repeatkind: RepeatKind,
|
||||
callback: F,
|
||||
) -> Result<wl_keyboard::WlKeyboard, Error>
|
||||
where
|
||||
F: FnMut(Event<'_>, wl_keyboard::WlKeyboard, wayland_client::DispatchData<'_>) + 'static,
|
||||
{
|
||||
let has_kbd = super::with_seat_data(seat, |data| data.has_keyboard).unwrap_or(false);
|
||||
let keyboard = if has_kbd {
|
||||
seat.get_keyboard()
|
||||
} else {
|
||||
return Err(Error::NoKeyboard);
|
||||
};
|
||||
|
||||
let state = Rc::new(RefCell::new(rmlvo.map(KbState::from_rmlvo).unwrap_or_else(KbState::new)?));
|
||||
|
||||
let callback = Rc::new(RefCell::new(callback)) as Rc<RefCell<_>>;
|
||||
|
||||
let repeat = match repeatkind {
|
||||
RepeatKind::System => RepeatDetails { locked: false, gap: None, delay: 200 },
|
||||
RepeatKind::Fixed { rate, delay } => {
|
||||
let gap = rate_to_gap(rate as i32);
|
||||
RepeatDetails { locked: true, gap, delay }
|
||||
}
|
||||
};
|
||||
|
||||
// Prepare the repetition handling.
|
||||
let mut handler = KbdHandler {
|
||||
callback: callback.clone(),
|
||||
state,
|
||||
repeat: Some(KbdRepeat {
|
||||
start_timer: {
|
||||
let my_loop_handle = loop_handle.clone();
|
||||
Box::new(move |source| {
|
||||
let my_callback = callback.clone();
|
||||
my_loop_handle
|
||||
.insert_source(source, move |event, kbd, ddata| {
|
||||
(my_callback.borrow_mut())(
|
||||
event,
|
||||
kbd.clone(),
|
||||
wayland_client::DispatchData::wrap(ddata),
|
||||
)
|
||||
})
|
||||
.unwrap()
|
||||
})
|
||||
},
|
||||
stop_timer: Box::new(move |token| loop_handle.remove(token)),
|
||||
current_repeat: Rc::new(RefCell::new(None)),
|
||||
current_timer: Cell::new(None),
|
||||
details: repeat,
|
||||
}),
|
||||
};
|
||||
|
||||
keyboard
|
||||
.quick_assign(move |keyboard, event, data| handler.event(keyboard.detach(), event, data));
|
||||
|
||||
Ok(keyboard.detach())
|
||||
}
|
||||
|
||||
#[cfg(feature = "calloop")]
|
||||
fn rate_to_gap(rate: i32) -> Option<NonZeroU32> {
|
||||
if rate <= 0 {
|
||||
None
|
||||
} else if MICROS_IN_SECOND < rate as u32 {
|
||||
NonZeroU32::new(1)
|
||||
} else {
|
||||
NonZeroU32::new(MICROS_IN_SECOND / rate as u32)
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Classic handling
|
||||
*/
|
||||
|
||||
type KbdCallback = dyn FnMut(Event<'_>, wl_keyboard::WlKeyboard, wayland_client::DispatchData<'_>);
|
||||
|
||||
#[cfg(feature = "calloop")]
|
||||
struct RepeatDetails {
|
||||
locked: bool,
|
||||
/// Gap between key presses in microseconds.
|
||||
///
|
||||
/// If the `gap` is `None`, it means that repeat is disabled.
|
||||
gap: Option<NonZeroU32>,
|
||||
/// Delay before starting key repeat in milliseconds.
|
||||
delay: u32,
|
||||
}
|
||||
|
||||
struct KbdHandler {
|
||||
state: Rc<RefCell<KbState>>,
|
||||
callback: Rc<RefCell<KbdCallback>>,
|
||||
#[cfg(feature = "calloop")]
|
||||
repeat: Option<KbdRepeat>,
|
||||
}
|
||||
|
||||
#[cfg(feature = "calloop")]
|
||||
struct KbdRepeat {
|
||||
start_timer: Box<dyn Fn(RepeatSource) -> RegistrationToken>,
|
||||
stop_timer: Box<dyn Fn(RegistrationToken)>,
|
||||
current_timer: Cell<Option<RegistrationToken>>,
|
||||
current_repeat: Rc<RefCell<Option<RepeatData>>>,
|
||||
details: RepeatDetails,
|
||||
}
|
||||
|
||||
#[cfg(feature = "calloop")]
|
||||
impl KbdRepeat {
|
||||
fn start_repeat(
|
||||
&self,
|
||||
key: u32,
|
||||
keyboard: wl_keyboard::WlKeyboard,
|
||||
time: u32,
|
||||
state: Rc<RefCell<KbState>>,
|
||||
) {
|
||||
// Start a new repetition, overwriting the previous ones
|
||||
if let Some(timer) = self.current_timer.replace(None) {
|
||||
(self.stop_timer)(timer);
|
||||
}
|
||||
|
||||
// Handle disabled repeat rate.
|
||||
let gap = match self.details.gap {
|
||||
Some(gap) => Duration::from_micros(gap.get() as u64),
|
||||
None => return,
|
||||
};
|
||||
|
||||
let now = Instant::now();
|
||||
*self.current_repeat.borrow_mut() = Some(RepeatData {
|
||||
keyboard,
|
||||
keycode: key,
|
||||
gap,
|
||||
start_protocol_time: time,
|
||||
start_instant: now,
|
||||
});
|
||||
let token = (self.start_timer)(RepeatSource {
|
||||
timer: Timer::from_deadline(now + Duration::from_millis(self.details.delay as u64)),
|
||||
current_repeat: self.current_repeat.clone(),
|
||||
state,
|
||||
});
|
||||
self.current_timer.set(Some(token));
|
||||
}
|
||||
|
||||
fn stop_repeat(&self, key: u32) {
|
||||
// only cancel if the released key is the currently repeating key
|
||||
let mut guard = self.current_repeat.borrow_mut();
|
||||
let stop = (*guard).as_ref().map(|d| d.keycode == key).unwrap_or(false);
|
||||
if stop {
|
||||
if let Some(timer) = self.current_timer.replace(None) {
|
||||
(self.stop_timer)(timer);
|
||||
}
|
||||
*guard = None;
|
||||
}
|
||||
}
|
||||
|
||||
fn stop_all_repeat(&self) {
|
||||
if let Some(timer) = self.current_timer.replace(None) {
|
||||
(self.stop_timer)(timer);
|
||||
}
|
||||
*self.current_repeat.borrow_mut() = None;
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(feature = "calloop")]
|
||||
impl Drop for KbdRepeat {
|
||||
fn drop(&mut self) {
|
||||
self.stop_all_repeat();
|
||||
}
|
||||
}
|
||||
|
||||
impl KbdHandler {
|
||||
fn event(
|
||||
&mut self,
|
||||
kbd: wl_keyboard::WlKeyboard,
|
||||
event: wl_keyboard::Event,
|
||||
dispatch_data: wayland_client::DispatchData,
|
||||
) {
|
||||
use wl_keyboard::Event;
|
||||
|
||||
match event {
|
||||
Event::Keymap { format, fd, size } => self.keymap(kbd, format, fd, size),
|
||||
Event::Enter { serial, surface, keys } => {
|
||||
self.enter(kbd, serial, surface, keys, dispatch_data)
|
||||
}
|
||||
Event::Leave { serial, surface } => self.leave(kbd, serial, surface, dispatch_data),
|
||||
Event::Key { serial, time, key, state } => {
|
||||
self.key(kbd, serial, time, key, state, dispatch_data)
|
||||
}
|
||||
Event::Modifiers { mods_depressed, mods_latched, mods_locked, group, .. } => {
|
||||
self.modifiers(kbd, mods_depressed, mods_latched, mods_locked, group, dispatch_data)
|
||||
}
|
||||
Event::RepeatInfo { rate, delay } => self.repeat_info(kbd, rate, delay),
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
|
||||
fn keymap(
|
||||
&mut self,
|
||||
_: wl_keyboard::WlKeyboard,
|
||||
format: wl_keyboard::KeymapFormat,
|
||||
fd: RawFd,
|
||||
size: u32,
|
||||
) {
|
||||
let fd = unsafe { File::from_raw_fd(fd) };
|
||||
let mut state = self.state.borrow_mut();
|
||||
if state.locked() {
|
||||
// state is locked, ignore keymap updates
|
||||
return;
|
||||
}
|
||||
if state.ready() {
|
||||
// new keymap, we first deinit to free resources
|
||||
unsafe {
|
||||
state.de_init();
|
||||
}
|
||||
}
|
||||
match format {
|
||||
wl_keyboard::KeymapFormat::XkbV1 => unsafe {
|
||||
state.init_with_fd(fd, size as usize);
|
||||
},
|
||||
wl_keyboard::KeymapFormat::NoKeymap => {
|
||||
// TODO: how to handle this (hopefully never occuring) case?
|
||||
}
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
|
||||
fn enter(
|
||||
&mut self,
|
||||
object: wl_keyboard::WlKeyboard,
|
||||
serial: u32,
|
||||
surface: wl_surface::WlSurface,
|
||||
keys: Vec<u8>,
|
||||
dispatch_data: wayland_client::DispatchData,
|
||||
) {
|
||||
let mut state = self.state.borrow_mut();
|
||||
let rawkeys = keys
|
||||
.chunks_exact(4)
|
||||
.map(|c| u32::from_ne_bytes(c.try_into().unwrap()))
|
||||
.collect::<Vec<_>>();
|
||||
let keys: Vec<u32> = rawkeys.iter().map(|k| state.get_one_sym_raw(*k)).collect();
|
||||
(self.callback.borrow_mut())(
|
||||
Event::Enter { serial, surface, rawkeys: &rawkeys, keysyms: &keys },
|
||||
object,
|
||||
dispatch_data,
|
||||
);
|
||||
}
|
||||
|
||||
fn leave(
|
||||
&mut self,
|
||||
object: wl_keyboard::WlKeyboard,
|
||||
serial: u32,
|
||||
surface: wl_surface::WlSurface,
|
||||
dispatch_data: wayland_client::DispatchData,
|
||||
) {
|
||||
#[cfg(feature = "calloop")]
|
||||
{
|
||||
if let Some(ref mut repeat) = self.repeat {
|
||||
repeat.stop_all_repeat();
|
||||
}
|
||||
}
|
||||
(self.callback.borrow_mut())(Event::Leave { serial, surface }, object, dispatch_data);
|
||||
}
|
||||
|
||||
#[cfg_attr(not(feature = "calloop"), allow(unused_variables))]
|
||||
fn key(
|
||||
&mut self,
|
||||
object: wl_keyboard::WlKeyboard,
|
||||
serial: u32,
|
||||
time: u32,
|
||||
key: u32,
|
||||
key_state: wl_keyboard::KeyState,
|
||||
dispatch_data: wayland_client::DispatchData,
|
||||
) {
|
||||
let (sym, utf8, repeats) = {
|
||||
let mut state = self.state.borrow_mut();
|
||||
// Get the values to generate a key event
|
||||
let sym = state.get_one_sym_raw(key);
|
||||
let utf8 = if key_state == wl_keyboard::KeyState::Pressed {
|
||||
match state.compose_feed(sym) {
|
||||
Some(ffi::xkb_compose_feed_result::XKB_COMPOSE_FEED_ACCEPTED) => {
|
||||
if let Some(status) = state.compose_status() {
|
||||
match status {
|
||||
ffi::xkb_compose_status::XKB_COMPOSE_COMPOSED => {
|
||||
state.compose_get_utf8()
|
||||
}
|
||||
ffi::xkb_compose_status::XKB_COMPOSE_NOTHING => {
|
||||
state.get_utf8_raw(key)
|
||||
}
|
||||
_ => None,
|
||||
}
|
||||
} else {
|
||||
state.get_utf8_raw(key)
|
||||
}
|
||||
}
|
||||
Some(_) => {
|
||||
// XKB_COMPOSE_FEED_IGNORED
|
||||
None
|
||||
}
|
||||
None => {
|
||||
// XKB COMPOSE is not initialized
|
||||
state.get_utf8_raw(key)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
None
|
||||
};
|
||||
let repeats = unsafe { state.key_repeats(key + 8) };
|
||||
(sym, utf8, repeats)
|
||||
};
|
||||
|
||||
#[cfg(feature = "calloop")]
|
||||
{
|
||||
if let Some(ref mut repeat_handle) = self.repeat {
|
||||
if repeats {
|
||||
if key_state == wl_keyboard::KeyState::Pressed {
|
||||
repeat_handle.start_repeat(key, object.clone(), time, self.state.clone());
|
||||
} else {
|
||||
repeat_handle.stop_repeat(key);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
(self.callback.borrow_mut())(
|
||||
Event::Key { serial, time, rawkey: key, keysym: sym, state: key_state, utf8 },
|
||||
object,
|
||||
dispatch_data,
|
||||
);
|
||||
}
|
||||
|
||||
fn modifiers(
|
||||
&mut self,
|
||||
object: wl_keyboard::WlKeyboard,
|
||||
mods_depressed: u32,
|
||||
mods_latched: u32,
|
||||
mods_locked: u32,
|
||||
group: u32,
|
||||
dispatch_data: wayland_client::DispatchData,
|
||||
) {
|
||||
{
|
||||
let mut state = self.state.borrow_mut();
|
||||
state.update_modifiers(mods_depressed, mods_latched, mods_locked, group);
|
||||
(self.callback.borrow_mut())(
|
||||
Event::Modifiers { modifiers: state.mods_state() },
|
||||
object,
|
||||
dispatch_data,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg_attr(not(feature = "calloop"), allow(unused_variables))]
|
||||
fn repeat_info(&mut self, _: wl_keyboard::WlKeyboard, rate: i32, delay: i32) {
|
||||
#[cfg(feature = "calloop")]
|
||||
{
|
||||
if let Some(ref mut repeat_handle) = self.repeat {
|
||||
if !repeat_handle.details.locked {
|
||||
repeat_handle.details.gap = rate_to_gap(rate);
|
||||
repeat_handle.details.delay = delay as u32;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*
|
||||
* Repeat handling
|
||||
*/
|
||||
|
||||
#[derive(Debug)]
|
||||
#[cfg(feature = "calloop")]
|
||||
struct RepeatData {
|
||||
keyboard: wl_keyboard::WlKeyboard,
|
||||
keycode: u32,
|
||||
/// Gap between key presses
|
||||
gap: Duration,
|
||||
start_protocol_time: u32,
|
||||
start_instant: Instant,
|
||||
}
|
||||
|
||||
/// An event source managing the key repetition of a keyboard
|
||||
///
|
||||
/// It is given to you from [`map_keyboard`](fn.map_keyboard.html), and you need to
|
||||
/// insert it in your calloop event loop if you want to have functionning key repetition.
|
||||
///
|
||||
/// If don't want key repetition you can just drop it.
|
||||
///
|
||||
/// This source will not directly generate calloop events, and the callback provided to
|
||||
/// `EventLoopHandle::insert_source()` will be ignored. Instead it triggers the
|
||||
/// callback you provided to [`map_keyboard`](fn.map_keyboard.html).
|
||||
#[cfg(feature = "calloop")]
|
||||
#[derive(Debug)]
|
||||
pub struct RepeatSource {
|
||||
timer: calloop::timer::Timer,
|
||||
state: Rc<RefCell<KbState>>,
|
||||
current_repeat: Rc<RefCell<Option<RepeatData>>>,
|
||||
}
|
||||
|
||||
#[cfg(feature = "calloop")]
|
||||
impl calloop::EventSource for RepeatSource {
|
||||
type Event = Event<'static>;
|
||||
type Metadata = wl_keyboard::WlKeyboard;
|
||||
type Error = <calloop::timer::Timer as calloop::EventSource>::Error;
|
||||
type Ret = ();
|
||||
|
||||
fn process_events<F>(
|
||||
&mut self,
|
||||
readiness: calloop::Readiness,
|
||||
token: calloop::Token,
|
||||
mut callback: F,
|
||||
) -> std::io::Result<calloop::PostAction>
|
||||
where
|
||||
F: FnMut(Event<'static>, &mut wl_keyboard::WlKeyboard),
|
||||
{
|
||||
let current_repeat = &self.current_repeat;
|
||||
let state = &self.state;
|
||||
self.timer.process_events(readiness, token, |last_trigger, &mut ()| {
|
||||
if let Some(ref mut data) = *current_repeat.borrow_mut() {
|
||||
// there is something to repeat
|
||||
let mut state = state.borrow_mut();
|
||||
let keysym = state.get_one_sym_raw(data.keycode);
|
||||
let utf8 = state.get_utf8_raw(data.keycode);
|
||||
// Notify the callback.
|
||||
callback(
|
||||
Event::Repeat {
|
||||
time: data.start_protocol_time
|
||||
+ (last_trigger - data.start_instant).as_millis() as u32,
|
||||
rawkey: data.keycode,
|
||||
keysym,
|
||||
utf8,
|
||||
},
|
||||
&mut data.keyboard,
|
||||
);
|
||||
// Schedule the next timeout.
|
||||
calloop::timer::TimeoutAction::ToInstant(last_trigger + data.gap)
|
||||
} else {
|
||||
calloop::timer::TimeoutAction::Drop
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
fn register(
|
||||
&mut self,
|
||||
poll: &mut calloop::Poll,
|
||||
token_factory: &mut calloop::TokenFactory,
|
||||
) -> calloop::Result<()> {
|
||||
self.timer.register(poll, token_factory)
|
||||
}
|
||||
|
||||
fn reregister(
|
||||
&mut self,
|
||||
poll: &mut calloop::Poll,
|
||||
token_factory: &mut calloop::TokenFactory,
|
||||
) -> calloop::Result<()> {
|
||||
self.timer.reregister(poll, token_factory)
|
||||
}
|
||||
|
||||
fn unregister(&mut self, poll: &mut calloop::Poll) -> calloop::Result<()> {
|
||||
self.timer.unregister(poll)
|
||||
}
|
||||
}
|
||||
433
third-party/vendor/smithay-client-toolkit/src/seat/keyboard/state.rs
vendored
Normal file
433
third-party/vendor/smithay-client-toolkit/src/seat/keyboard/state.rs
vendored
Normal file
|
|
@ -0,0 +1,433 @@
|
|||
use memmap2::MmapOptions;
|
||||
use std::{env, ffi::CString, fs::File, os::raw::c_char, os::unix::ffi::OsStringExt, ptr};
|
||||
|
||||
#[cfg(feature = "dlopen")]
|
||||
use super::ffi::XKBCOMMON_HANDLE as XKBH;
|
||||
#[cfg(not(feature = "dlopen"))]
|
||||
use super::ffi::*;
|
||||
use super::ffi::{self, xkb_state_component};
|
||||
use super::Error;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub(crate) struct KbState {
|
||||
xkb_context: *mut ffi::xkb_context,
|
||||
xkb_keymap: *mut ffi::xkb_keymap,
|
||||
xkb_state: *mut ffi::xkb_state,
|
||||
xkb_compose_table: *mut ffi::xkb_compose_table,
|
||||
xkb_compose_state: *mut ffi::xkb_compose_state,
|
||||
mods_state: ModifiersState,
|
||||
locked: bool,
|
||||
}
|
||||
|
||||
/// The RMLVO description of a keymap
|
||||
///
|
||||
/// All fields are optional, and the system default
|
||||
/// will be used if set to `None`.
|
||||
#[derive(Debug)]
|
||||
#[allow(clippy::upper_case_acronyms)]
|
||||
pub struct RMLVO {
|
||||
/// The rules file to use
|
||||
pub rules: Option<String>,
|
||||
/// The keyboard model by which to interpret keycodes and LEDs
|
||||
pub model: Option<String>,
|
||||
/// A comma separated list of layouts (languages) to include in the keymap
|
||||
pub layout: Option<String>,
|
||||
/// A comma separated list of variants, one per layout, which may modify or
|
||||
/// augment the respective layout in various ways
|
||||
pub variant: Option<String>,
|
||||
/// A comma separated list of options, through which the user specifies
|
||||
/// non-layout related preferences, like which key combinations are
|
||||
/// used for switching layouts, or which key is the Compose key.
|
||||
pub options: Option<String>,
|
||||
}
|
||||
|
||||
/// Represents the current state of the keyboard modifiers
|
||||
///
|
||||
/// Each field of this struct represents a modifier and is `true` if this modifier is active.
|
||||
///
|
||||
/// For some modifiers, this means that the key is currently pressed, others are toggled
|
||||
/// (like caps lock).
|
||||
#[derive(Copy, Clone, Debug, Default)]
|
||||
pub struct ModifiersState {
|
||||
/// The "control" key
|
||||
pub ctrl: bool,
|
||||
/// The "alt" key
|
||||
pub alt: bool,
|
||||
/// The "shift" key
|
||||
pub shift: bool,
|
||||
/// The "Caps lock" key
|
||||
pub caps_lock: bool,
|
||||
/// The "logo" key
|
||||
///
|
||||
/// Also known as the "windows" key on most keyboards
|
||||
pub logo: bool,
|
||||
/// The "Num lock" key
|
||||
pub num_lock: bool,
|
||||
}
|
||||
|
||||
impl ModifiersState {
|
||||
fn new() -> ModifiersState {
|
||||
ModifiersState::default()
|
||||
}
|
||||
|
||||
fn update_with(&mut self, state: *mut ffi::xkb_state) {
|
||||
self.ctrl = unsafe {
|
||||
ffi_dispatch!(
|
||||
XKBH,
|
||||
xkb_state_mod_name_is_active,
|
||||
state,
|
||||
ffi::XKB_MOD_NAME_CTRL.as_ptr() as *const c_char,
|
||||
xkb_state_component::XKB_STATE_MODS_EFFECTIVE
|
||||
) > 0
|
||||
};
|
||||
self.alt = unsafe {
|
||||
ffi_dispatch!(
|
||||
XKBH,
|
||||
xkb_state_mod_name_is_active,
|
||||
state,
|
||||
ffi::XKB_MOD_NAME_ALT.as_ptr() as *const c_char,
|
||||
xkb_state_component::XKB_STATE_MODS_EFFECTIVE
|
||||
) > 0
|
||||
};
|
||||
self.shift = unsafe {
|
||||
ffi_dispatch!(
|
||||
XKBH,
|
||||
xkb_state_mod_name_is_active,
|
||||
state,
|
||||
ffi::XKB_MOD_NAME_SHIFT.as_ptr() as *const c_char,
|
||||
xkb_state_component::XKB_STATE_MODS_EFFECTIVE
|
||||
) > 0
|
||||
};
|
||||
self.caps_lock = unsafe {
|
||||
ffi_dispatch!(
|
||||
XKBH,
|
||||
xkb_state_mod_name_is_active,
|
||||
state,
|
||||
ffi::XKB_MOD_NAME_CAPS.as_ptr() as *const c_char,
|
||||
xkb_state_component::XKB_STATE_MODS_EFFECTIVE
|
||||
) > 0
|
||||
};
|
||||
self.logo = unsafe {
|
||||
ffi_dispatch!(
|
||||
XKBH,
|
||||
xkb_state_mod_name_is_active,
|
||||
state,
|
||||
ffi::XKB_MOD_NAME_LOGO.as_ptr() as *const c_char,
|
||||
xkb_state_component::XKB_STATE_MODS_EFFECTIVE
|
||||
) > 0
|
||||
};
|
||||
self.num_lock = unsafe {
|
||||
ffi_dispatch!(
|
||||
XKBH,
|
||||
xkb_state_mod_name_is_active,
|
||||
state,
|
||||
ffi::XKB_MOD_NAME_NUM.as_ptr() as *const c_char,
|
||||
xkb_state_component::XKB_STATE_MODS_EFFECTIVE
|
||||
) > 0
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
impl KbState {
|
||||
pub(crate) fn update_modifiers(
|
||||
&mut self,
|
||||
mods_depressed: u32,
|
||||
mods_latched: u32,
|
||||
mods_locked: u32,
|
||||
group: u32,
|
||||
) {
|
||||
if !self.ready() {
|
||||
return;
|
||||
}
|
||||
let mask = unsafe {
|
||||
ffi_dispatch!(
|
||||
XKBH,
|
||||
xkb_state_update_mask,
|
||||
self.xkb_state,
|
||||
mods_depressed,
|
||||
mods_latched,
|
||||
mods_locked,
|
||||
0,
|
||||
0,
|
||||
group
|
||||
)
|
||||
};
|
||||
if mask.contains(xkb_state_component::XKB_STATE_MODS_EFFECTIVE) {
|
||||
// effective value of mods have changed, we need to update our state
|
||||
self.mods_state.update_with(self.xkb_state);
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn get_one_sym_raw(&mut self, keycode: u32) -> u32 {
|
||||
if !self.ready() {
|
||||
return 0;
|
||||
}
|
||||
unsafe { ffi_dispatch!(XKBH, xkb_state_key_get_one_sym, self.xkb_state, keycode + 8) }
|
||||
}
|
||||
|
||||
pub(crate) fn get_utf8_raw(&mut self, keycode: u32) -> Option<String> {
|
||||
if !self.ready() {
|
||||
return None;
|
||||
}
|
||||
let size = unsafe {
|
||||
ffi_dispatch!(
|
||||
XKBH,
|
||||
xkb_state_key_get_utf8,
|
||||
self.xkb_state,
|
||||
keycode + 8,
|
||||
ptr::null_mut(),
|
||||
0
|
||||
)
|
||||
} + 1;
|
||||
if size <= 1 {
|
||||
return None;
|
||||
};
|
||||
let mut buffer = vec![0; size as usize];
|
||||
unsafe {
|
||||
ffi_dispatch!(
|
||||
XKBH,
|
||||
xkb_state_key_get_utf8,
|
||||
self.xkb_state,
|
||||
keycode + 8,
|
||||
buffer.as_mut_ptr() as *mut _,
|
||||
size as usize
|
||||
);
|
||||
};
|
||||
// remove the final `\0`
|
||||
buffer.pop();
|
||||
// libxkbcommon will always provide valid UTF8
|
||||
Some(unsafe { String::from_utf8_unchecked(buffer) })
|
||||
}
|
||||
|
||||
pub(crate) fn compose_feed(&mut self, keysym: u32) -> Option<ffi::xkb_compose_feed_result> {
|
||||
if !self.ready() || self.xkb_compose_state.is_null() {
|
||||
return None;
|
||||
}
|
||||
Some(unsafe { ffi_dispatch!(XKBH, xkb_compose_state_feed, self.xkb_compose_state, keysym) })
|
||||
}
|
||||
|
||||
pub(crate) fn compose_status(&mut self) -> Option<ffi::xkb_compose_status> {
|
||||
if !self.ready() || self.xkb_compose_state.is_null() {
|
||||
return None;
|
||||
}
|
||||
Some(unsafe { ffi_dispatch!(XKBH, xkb_compose_state_get_status, self.xkb_compose_state) })
|
||||
}
|
||||
|
||||
pub(crate) fn compose_get_utf8(&mut self) -> Option<String> {
|
||||
if !self.ready() || self.xkb_compose_state.is_null() {
|
||||
return None;
|
||||
}
|
||||
let size = unsafe {
|
||||
ffi_dispatch!(
|
||||
XKBH,
|
||||
xkb_compose_state_get_utf8,
|
||||
self.xkb_compose_state,
|
||||
ptr::null_mut(),
|
||||
0
|
||||
)
|
||||
} + 1;
|
||||
if size <= 1 {
|
||||
return None;
|
||||
};
|
||||
let mut buffer = vec![0; size as usize];
|
||||
unsafe {
|
||||
buffer.set_len(size as usize);
|
||||
ffi_dispatch!(
|
||||
XKBH,
|
||||
xkb_compose_state_get_utf8,
|
||||
self.xkb_compose_state,
|
||||
buffer.as_mut_ptr() as *mut _,
|
||||
size as usize
|
||||
);
|
||||
};
|
||||
// remove the final `\0`
|
||||
buffer.pop();
|
||||
// libxkbcommon will always provide valid UTF8
|
||||
Some(unsafe { String::from_utf8_unchecked(buffer) })
|
||||
}
|
||||
|
||||
pub(crate) fn new() -> Result<KbState, Error> {
|
||||
#[cfg(feature = "dlopen")]
|
||||
{
|
||||
if ffi::XKBCOMMON_OPTION.as_ref().is_none() {
|
||||
return Err(Error::XKBNotFound);
|
||||
}
|
||||
}
|
||||
let context = unsafe {
|
||||
ffi_dispatch!(XKBH, xkb_context_new, ffi::xkb_context_flags::XKB_CONTEXT_NO_FLAGS)
|
||||
};
|
||||
if context.is_null() {
|
||||
return Err(Error::XKBNotFound);
|
||||
}
|
||||
|
||||
let mut me = KbState {
|
||||
xkb_context: context,
|
||||
xkb_keymap: ptr::null_mut(),
|
||||
xkb_state: ptr::null_mut(),
|
||||
xkb_compose_table: ptr::null_mut(),
|
||||
xkb_compose_state: ptr::null_mut(),
|
||||
mods_state: ModifiersState::new(),
|
||||
locked: false,
|
||||
};
|
||||
|
||||
unsafe {
|
||||
me.init_compose();
|
||||
}
|
||||
|
||||
Ok(me)
|
||||
}
|
||||
|
||||
pub(crate) fn from_rmlvo(rmlvo: RMLVO) -> Result<KbState, Error> {
|
||||
fn to_cstring(s: Option<String>) -> Result<Option<CString>, Error> {
|
||||
s.map_or(Ok(None), |s| CString::new(s).map(Option::Some)).map_err(|_| Error::BadNames)
|
||||
}
|
||||
|
||||
let mut state = KbState::new()?;
|
||||
|
||||
let rules = to_cstring(rmlvo.rules)?;
|
||||
let model = to_cstring(rmlvo.model)?;
|
||||
let layout = to_cstring(rmlvo.layout)?;
|
||||
let variant = to_cstring(rmlvo.variant)?;
|
||||
let options = to_cstring(rmlvo.options)?;
|
||||
|
||||
let xkb_names = ffi::xkb_rule_names {
|
||||
rules: rules.map_or(ptr::null(), |s| s.as_ptr()),
|
||||
model: model.map_or(ptr::null(), |s| s.as_ptr()),
|
||||
layout: layout.map_or(ptr::null(), |s| s.as_ptr()),
|
||||
variant: variant.map_or(ptr::null(), |s| s.as_ptr()),
|
||||
options: options.map_or(ptr::null(), |s| s.as_ptr()),
|
||||
};
|
||||
|
||||
unsafe {
|
||||
state.init_with_rmlvo(xkb_names)?;
|
||||
}
|
||||
|
||||
state.locked = true;
|
||||
Ok(state)
|
||||
}
|
||||
|
||||
pub(crate) unsafe fn init_compose(&mut self) {
|
||||
let locale = env::var_os("LC_ALL")
|
||||
.and_then(|v| if v.is_empty() { None } else { Some(v) })
|
||||
.or_else(|| env::var_os("LC_CTYPE"))
|
||||
.and_then(|v| if v.is_empty() { None } else { Some(v) })
|
||||
.or_else(|| env::var_os("LANG"))
|
||||
.and_then(|v| if v.is_empty() { None } else { Some(v) })
|
||||
.unwrap_or_else(|| "C".into());
|
||||
let locale = CString::new(locale.into_vec()).unwrap();
|
||||
|
||||
let compose_table = ffi_dispatch!(
|
||||
XKBH,
|
||||
xkb_compose_table_new_from_locale,
|
||||
self.xkb_context,
|
||||
locale.as_ptr(),
|
||||
ffi::xkb_compose_compile_flags::XKB_COMPOSE_COMPILE_NO_FLAGS
|
||||
);
|
||||
|
||||
if compose_table.is_null() {
|
||||
// init of compose table failed, continue without compose
|
||||
return;
|
||||
}
|
||||
|
||||
let compose_state = ffi_dispatch!(
|
||||
XKBH,
|
||||
xkb_compose_state_new,
|
||||
compose_table,
|
||||
ffi::xkb_compose_state_flags::XKB_COMPOSE_STATE_NO_FLAGS
|
||||
);
|
||||
|
||||
if compose_state.is_null() {
|
||||
// init of compose state failed, continue without compose
|
||||
ffi_dispatch!(XKBH, xkb_compose_table_unref, compose_table);
|
||||
return;
|
||||
}
|
||||
|
||||
self.xkb_compose_table = compose_table;
|
||||
self.xkb_compose_state = compose_state;
|
||||
}
|
||||
|
||||
pub(crate) unsafe fn post_init(&mut self, keymap: *mut ffi::xkb_keymap) {
|
||||
let state = ffi_dispatch!(XKBH, xkb_state_new, keymap);
|
||||
self.xkb_keymap = keymap;
|
||||
self.xkb_state = state;
|
||||
self.mods_state.update_with(state);
|
||||
}
|
||||
|
||||
pub(crate) unsafe fn de_init(&mut self) {
|
||||
ffi_dispatch!(XKBH, xkb_state_unref, self.xkb_state);
|
||||
self.xkb_state = ptr::null_mut();
|
||||
ffi_dispatch!(XKBH, xkb_keymap_unref, self.xkb_keymap);
|
||||
self.xkb_keymap = ptr::null_mut();
|
||||
}
|
||||
|
||||
pub(crate) unsafe fn init_with_fd(&mut self, fd: File, size: usize) {
|
||||
let map = MmapOptions::new().len(size).map(&fd).unwrap();
|
||||
|
||||
let keymap = ffi_dispatch!(
|
||||
XKBH,
|
||||
xkb_keymap_new_from_string,
|
||||
self.xkb_context,
|
||||
map.as_ptr() as *const _,
|
||||
ffi::xkb_keymap_format::XKB_KEYMAP_FORMAT_TEXT_V1,
|
||||
ffi::xkb_keymap_compile_flags::XKB_KEYMAP_COMPILE_NO_FLAGS
|
||||
);
|
||||
|
||||
if keymap.is_null() {
|
||||
panic!("Received invalid keymap from compositor.");
|
||||
}
|
||||
|
||||
self.post_init(keymap);
|
||||
}
|
||||
|
||||
pub(crate) unsafe fn init_with_rmlvo(
|
||||
&mut self,
|
||||
names: ffi::xkb_rule_names,
|
||||
) -> Result<(), Error> {
|
||||
let keymap = ffi_dispatch!(
|
||||
XKBH,
|
||||
xkb_keymap_new_from_names,
|
||||
self.xkb_context,
|
||||
&names,
|
||||
ffi::xkb_keymap_compile_flags::XKB_KEYMAP_COMPILE_NO_FLAGS
|
||||
);
|
||||
|
||||
if keymap.is_null() {
|
||||
return Err(Error::BadNames);
|
||||
}
|
||||
|
||||
self.post_init(keymap);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
pub(crate) unsafe fn key_repeats(&mut self, xkb_keycode_t: ffi::xkb_keycode_t) -> bool {
|
||||
ffi_dispatch!(XKBH, xkb_keymap_key_repeats, self.xkb_keymap, xkb_keycode_t) == 1
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub(crate) fn ready(&self) -> bool {
|
||||
!self.xkb_state.is_null()
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub(crate) fn locked(&self) -> bool {
|
||||
self.locked
|
||||
}
|
||||
|
||||
#[inline]
|
||||
pub(crate) fn mods_state(&self) -> ModifiersState {
|
||||
self.mods_state
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for KbState {
|
||||
fn drop(&mut self) {
|
||||
unsafe {
|
||||
ffi_dispatch!(XKBH, xkb_compose_state_unref, self.xkb_compose_state);
|
||||
ffi_dispatch!(XKBH, xkb_compose_table_unref, self.xkb_compose_table);
|
||||
ffi_dispatch!(XKBH, xkb_state_unref, self.xkb_state);
|
||||
ffi_dispatch!(XKBH, xkb_keymap_unref, self.xkb_keymap);
|
||||
ffi_dispatch!(XKBH, xkb_context_unref, self.xkb_context);
|
||||
}
|
||||
}
|
||||
}
|
||||
308
third-party/vendor/smithay-client-toolkit/src/seat/mod.rs
vendored
Normal file
308
third-party/vendor/smithay-client-toolkit/src/seat/mod.rs
vendored
Normal file
|
|
@ -0,0 +1,308 @@
|
|||
//! Types for automatically handling seats
|
||||
//!
|
||||
//! This modules provides a `SeatHandler` for use with the
|
||||
//! [`environment!`](../macro.environment.html) macro. It is automatically inserted
|
||||
//! in the [`default_environment!`](../macro.default_environment.html).
|
||||
//!
|
||||
//! This handler tracks the capability of the seats declared by the compositor,
|
||||
//! and gives you the possibility to register callbacks that will be invoked whenever
|
||||
//! a new seat is created of the state of a seat changes, via the
|
||||
//! [`Environment::listen_for_seats`](../environment/struct.Environment.html) method.
|
||||
//!
|
||||
//! **Note:** if you don't use the [`default_environment!`](../macro.default_environment.html),
|
||||
//! you'll need to implement the [`SeatHandling`](trait.SeatHandling.hmtl) on your
|
||||
//! environment struct to access the added methods on
|
||||
//! [`Environment`](../environment/struct.Environment.html).
|
||||
|
||||
use std::{
|
||||
cell::RefCell,
|
||||
fmt::{self, Debug, Formatter},
|
||||
rc::{Rc, Weak},
|
||||
sync::Mutex,
|
||||
};
|
||||
|
||||
use bitflags::bitflags;
|
||||
|
||||
use wayland_client::{
|
||||
protocol::{wl_registry, wl_seat},
|
||||
Attached, DispatchData, Main,
|
||||
};
|
||||
|
||||
pub mod keyboard;
|
||||
pub mod pointer;
|
||||
|
||||
type SeatCallback = dyn FnMut(Attached<wl_seat::WlSeat>, &SeatData, DispatchData) + 'static;
|
||||
|
||||
/// The metadata associated with a seat
|
||||
#[derive(Clone)]
|
||||
pub struct SeatData {
|
||||
/// The name of this seat
|
||||
///
|
||||
/// It can be used as an identifier for the seat
|
||||
pub name: String,
|
||||
/// Whether this seat has a pointer available
|
||||
pub has_pointer: bool,
|
||||
/// Whether this seat has a keyboard available
|
||||
pub has_keyboard: bool,
|
||||
/// Whether this seat has a touchscreen available
|
||||
pub has_touch: bool,
|
||||
/// Whether this seat has been removed from the registry
|
||||
///
|
||||
/// Once a seat is removed, you will no longer receive any
|
||||
/// event on any of its associated devices (pointer, keyboard or touch).
|
||||
///
|
||||
/// You can thus cleanup all your state associated with this seat.
|
||||
pub defunct: bool,
|
||||
|
||||
/// State of readiness of the data.
|
||||
state: SeatDataState,
|
||||
}
|
||||
|
||||
bitflags! {
|
||||
struct SeatDataState: u8 {
|
||||
const NEW = 0b00000000;
|
||||
const GOT_NAME = 0b00000001;
|
||||
const GOT_CAPABILITIES = 0b00000010;
|
||||
const READY = Self::GOT_NAME.bits | Self::GOT_CAPABILITIES.bits;
|
||||
}
|
||||
}
|
||||
|
||||
impl Debug for SeatData {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
|
||||
f.debug_struct("SeatData")
|
||||
.field("name", &self.name)
|
||||
.field("has_pointer", &self.has_pointer)
|
||||
.field("has_keyboard", &self.has_keyboard)
|
||||
.field("has_touch", &self.has_touch)
|
||||
.field("defunct", &self.defunct)
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
|
||||
impl SeatData {
|
||||
fn new() -> SeatData {
|
||||
SeatData {
|
||||
name: String::new(),
|
||||
has_pointer: false,
|
||||
has_keyboard: false,
|
||||
has_touch: false,
|
||||
defunct: false,
|
||||
state: SeatDataState::NEW,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// A simple handler for seats
|
||||
///
|
||||
/// This handler will manage seats and track their capabilities.
|
||||
///
|
||||
/// You can register callbacks using the [`SeatHandling::listen`](trait.SeatHandling.html)
|
||||
/// to be notified whenever a seat is created, destroyed, or its capabilities change.
|
||||
pub struct SeatHandler {
|
||||
seats: Vec<(u32, Attached<wl_seat::WlSeat>)>,
|
||||
listeners: Rc<RefCell<Vec<Weak<RefCell<SeatCallback>>>>>,
|
||||
}
|
||||
|
||||
impl SeatHandler {
|
||||
/// Create a new SeatHandler
|
||||
pub fn new() -> SeatHandler {
|
||||
SeatHandler { seats: Vec::new(), listeners: Rc::new(RefCell::new(Vec::new())) }
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Debug for SeatHandler {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
|
||||
f.debug_struct("SeatHandler")
|
||||
.field("seats", &self.seats)
|
||||
.field("listeners", &"Fn(..) -> { ... }")
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
|
||||
/// A handle to an seat listener callback
|
||||
///
|
||||
/// Dropping it disables the associated callback and frees the closure.
|
||||
pub struct SeatListener {
|
||||
_cb: Rc<RefCell<SeatCallback>>,
|
||||
}
|
||||
|
||||
impl crate::environment::MultiGlobalHandler<wl_seat::WlSeat> for SeatHandler {
|
||||
fn created(
|
||||
&mut self,
|
||||
registry: Attached<wl_registry::WlRegistry>,
|
||||
id: u32,
|
||||
version: u32,
|
||||
_: DispatchData,
|
||||
) {
|
||||
// Seat is supported up to version 6
|
||||
let version = std::cmp::min(version, 6);
|
||||
let seat = registry.bind::<wl_seat::WlSeat>(version, id);
|
||||
seat.as_ref().user_data().set_threadsafe(|| Mutex::new(SeatData::new()));
|
||||
let cb_listeners = self.listeners.clone();
|
||||
seat.quick_assign(move |seat, event, ddata| {
|
||||
process_seat_event(seat, event, &cb_listeners, ddata)
|
||||
});
|
||||
self.seats.push((id, (*seat).clone()));
|
||||
}
|
||||
fn removed(&mut self, id: u32, mut ddata: DispatchData) {
|
||||
let mut listeners = self.listeners.borrow_mut();
|
||||
self.seats.retain(|&(i, ref seat)| {
|
||||
if i != id {
|
||||
true
|
||||
} else {
|
||||
// This data must be `Mutex<SeatData>` if this seat is in our vec
|
||||
let data = seat.as_ref().user_data().get::<Mutex<SeatData>>().unwrap();
|
||||
let mut guard = data.lock().unwrap();
|
||||
guard.defunct = true;
|
||||
// notify the listeners that the seat is dead
|
||||
listeners.retain(|lst| {
|
||||
if let Some(cb) = Weak::upgrade(lst) {
|
||||
(cb.borrow_mut())(seat.clone(), &*guard, ddata.reborrow());
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
});
|
||||
false
|
||||
}
|
||||
});
|
||||
}
|
||||
fn get_all(&self) -> Vec<Attached<wl_seat::WlSeat>> {
|
||||
self.seats.iter().map(|(_, s)| s.clone()).collect()
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Debug for SeatListener {
|
||||
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
|
||||
f.debug_struct("SeatListener").field("_cb", &"Fn(..) -> { ... }").finish()
|
||||
}
|
||||
}
|
||||
|
||||
fn process_seat_event(
|
||||
seat: Main<wl_seat::WlSeat>,
|
||||
event: wl_seat::Event,
|
||||
listeners: &RefCell<Vec<Weak<RefCell<SeatCallback>>>>,
|
||||
mut ddata: DispatchData,
|
||||
) {
|
||||
let new_data = {
|
||||
let data = seat.as_ref().user_data().get::<Mutex<SeatData>>().unwrap();
|
||||
let mut guard = data.lock().unwrap();
|
||||
match event {
|
||||
wl_seat::Event::Name { name } => {
|
||||
guard.state.set(SeatDataState::GOT_NAME, true);
|
||||
guard.name = name;
|
||||
}
|
||||
wl_seat::Event::Capabilities { capabilities } => {
|
||||
guard.state.set(SeatDataState::GOT_CAPABILITIES, true);
|
||||
guard.has_pointer = capabilities.contains(wl_seat::Capability::Pointer);
|
||||
guard.has_keyboard = capabilities.contains(wl_seat::Capability::Keyboard);
|
||||
guard.has_touch = capabilities.contains(wl_seat::Capability::Touch);
|
||||
}
|
||||
_ => unreachable!(),
|
||||
}
|
||||
guard.clone()
|
||||
};
|
||||
|
||||
if new_data.state.contains(SeatDataState::READY) {
|
||||
listeners.borrow_mut().retain(|lst| {
|
||||
if let Some(cb) = Weak::upgrade(lst) {
|
||||
(cb.borrow_mut())((*seat).clone(), &new_data, ddata.reborrow());
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
/// Get the copy of the data associated with this seat
|
||||
///
|
||||
/// If the provided `WlSeat` has not yet been initialized or is not managed by SCTK, `None` is returned.
|
||||
///
|
||||
/// If the seat has been removed by the compositor, the `defunct` field of the `SeatData`
|
||||
/// will be set to `true`. This handler will not automatically detroy the output by calling its
|
||||
/// `release` method, to avoid interfering with your logic.
|
||||
pub fn clone_seat_data(seat: &wl_seat::WlSeat) -> Option<SeatData> {
|
||||
if let Some(udata_mutex) = seat.as_ref().user_data().get::<Mutex<SeatData>>() {
|
||||
let udata = udata_mutex.lock().unwrap();
|
||||
Some(udata.clone())
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
/// Access the data associated with this seat
|
||||
///
|
||||
/// The provided closure is given the [`SeatData`](struct.SeatData.html) as argument,
|
||||
/// and its return value is returned from this function.
|
||||
///
|
||||
/// If the provided `WlSeat` has not yet been initialized or is not managed by SCTK, `None` is returned.
|
||||
///
|
||||
/// If the seat has been removed by the compositor, the `defunct` field of the `SeatData`
|
||||
/// will be set to `true`. This handler will not automatically detroy the output by calling its
|
||||
/// `release` method, to avoid interfering with your logic.
|
||||
pub fn with_seat_data<T, F: FnOnce(&SeatData) -> T>(seat: &wl_seat::WlSeat, f: F) -> Option<T> {
|
||||
if let Some(udata_mutex) = seat.as_ref().user_data().get::<Mutex<SeatData>>() {
|
||||
let udata = udata_mutex.lock().unwrap();
|
||||
Some(f(&*udata))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
/// Trait representing the SeatHandler functions
|
||||
///
|
||||
/// Implementing this trait on your inner environment struct used with the
|
||||
/// [`environment!`](../macro.environment.html) by delegating it to its
|
||||
/// [`SeatHandler`](struct.SeatHandler.html) field will make available the seat-associated
|
||||
/// method on your [`Environment`](../environment/struct.Environment.html).
|
||||
pub trait SeatHandling {
|
||||
/// Insert a listener for seat events
|
||||
fn listen<F: FnMut(Attached<wl_seat::WlSeat>, &SeatData, DispatchData) + 'static>(
|
||||
&mut self,
|
||||
f: F,
|
||||
) -> SeatListener;
|
||||
}
|
||||
|
||||
impl SeatHandling for SeatHandler {
|
||||
fn listen<F: FnMut(Attached<wl_seat::WlSeat>, &SeatData, DispatchData) + 'static>(
|
||||
&mut self,
|
||||
f: F,
|
||||
) -> SeatListener {
|
||||
let rc = Rc::new(RefCell::new(f)) as Rc<_>;
|
||||
self.listeners.borrow_mut().push(Rc::downgrade(&rc));
|
||||
SeatListener { _cb: rc }
|
||||
}
|
||||
}
|
||||
|
||||
impl<E: SeatHandling> crate::environment::Environment<E> {
|
||||
/// Insert a new listener for seats
|
||||
///
|
||||
/// The provided closure will be invoked whenever a `wl_seat` is made available,
|
||||
/// removed, or see its capabilities changed.
|
||||
///
|
||||
/// Note that if seats already exist when this callback is setup, it'll not be invoked on them.
|
||||
/// For you to be notified of them as well, you need to first process them manually by calling
|
||||
/// `.get_all_seats()`.
|
||||
///
|
||||
/// The returned [`SeatListener`](../seat/struct.SeatListener.hmtl) keeps your callback alive,
|
||||
/// dropping it will disable it.
|
||||
#[must_use = "the returned SeatListener keeps your callback alive, dropping it will disable it"]
|
||||
pub fn listen_for_seats<
|
||||
F: FnMut(Attached<wl_seat::WlSeat>, &SeatData, DispatchData) + 'static,
|
||||
>(
|
||||
&self,
|
||||
f: F,
|
||||
) -> SeatListener {
|
||||
self.with_inner(move |inner| SeatHandling::listen(inner, f))
|
||||
}
|
||||
}
|
||||
|
||||
impl<E: crate::environment::MultiGlobalHandler<wl_seat::WlSeat>>
|
||||
crate::environment::Environment<E>
|
||||
{
|
||||
/// Shorthand method to retrieve the list of seats
|
||||
pub fn get_all_seats(&self) -> Vec<Attached<wl_seat::WlSeat>> {
|
||||
self.get_all_globals::<wl_seat::WlSeat>().into_iter().collect()
|
||||
}
|
||||
}
|
||||
5
third-party/vendor/smithay-client-toolkit/src/seat/pointer/mod.rs
vendored
Normal file
5
third-party/vendor/smithay-client-toolkit/src/seat/pointer/mod.rs
vendored
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
//! Utilities to work with pointers and their icons
|
||||
|
||||
mod theme;
|
||||
|
||||
pub use self::theme::{ThemeManager, ThemeSpec, ThemedPointer};
|
||||
275
third-party/vendor/smithay-client-toolkit/src/seat/pointer/theme.rs
vendored
Normal file
275
third-party/vendor/smithay-client-toolkit/src/seat/pointer/theme.rs
vendored
Normal file
|
|
@ -0,0 +1,275 @@
|
|||
use std::{
|
||||
cell::RefCell,
|
||||
fmt,
|
||||
ops::Deref,
|
||||
rc::{Rc, Weak},
|
||||
};
|
||||
use wayland_client::{
|
||||
protocol::{wl_compositor, wl_pointer, wl_seat, wl_shm, wl_surface},
|
||||
Attached, DispatchData,
|
||||
};
|
||||
use wayland_cursor::{Cursor, CursorTheme};
|
||||
|
||||
/// The specification of a cursor theme to be used by the ThemeManager
|
||||
#[derive(Debug)]
|
||||
pub enum ThemeSpec<'a> {
|
||||
/// Use this specific theme with given base size
|
||||
Precise {
|
||||
/// Name of the cursor theme to use
|
||||
name: &'a str,
|
||||
/// Base size of the cursor images
|
||||
///
|
||||
/// This is the size that will be used on monitors with a scale
|
||||
/// factor of 1. Cursor images sizes will be multiples of this
|
||||
/// base size on HiDPI outputs.
|
||||
size: u32,
|
||||
},
|
||||
/// Use the system provided theme
|
||||
///
|
||||
/// In this case SCTK will read the `XCURSOR_THEME` and
|
||||
/// `XCURSOR_SIZE` environment variables to figure out the
|
||||
/// theme to use.
|
||||
System,
|
||||
}
|
||||
|
||||
/// Wrapper managing a system theme for pointer images
|
||||
///
|
||||
/// You can use it to initialize new pointers in order
|
||||
/// to theme them.
|
||||
///
|
||||
/// Is is also clone-able in case you need to handle several
|
||||
/// pointer theming from different places.
|
||||
///
|
||||
/// Note that it is however neither `Send` nor `Sync`
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct ThemeManager {
|
||||
themes: Rc<RefCell<ScaledThemeList>>,
|
||||
compositor: Attached<wl_compositor::WlCompositor>,
|
||||
}
|
||||
|
||||
impl ThemeManager {
|
||||
/// Load a system pointer theme
|
||||
///
|
||||
/// Will use the default theme of the system if name is `None`.
|
||||
pub fn init(
|
||||
theme: ThemeSpec,
|
||||
compositor: Attached<wl_compositor::WlCompositor>,
|
||||
shm: Attached<wl_shm::WlShm>,
|
||||
) -> ThemeManager {
|
||||
ThemeManager { compositor, themes: Rc::new(RefCell::new(ScaledThemeList::new(theme, shm))) }
|
||||
}
|
||||
|
||||
/// Wrap a pointer to theme it
|
||||
pub fn theme_pointer(&self, pointer: wl_pointer::WlPointer) -> ThemedPointer {
|
||||
let surface = self.compositor.create_surface();
|
||||
let inner = Rc::new(RefCell::new(PointerInner {
|
||||
surface: surface.detach(),
|
||||
themes: self.themes.clone(),
|
||||
last_serial: 0,
|
||||
current_cursor: "left_ptr".into(),
|
||||
scale_factor: 1,
|
||||
}));
|
||||
let my_pointer = pointer.clone();
|
||||
let winner = Rc::downgrade(&inner);
|
||||
crate::surface::setup_surface(
|
||||
surface,
|
||||
Some(move |scale_factor, _, _: DispatchData| {
|
||||
if let Some(inner) = Weak::upgrade(&winner) {
|
||||
let mut inner = inner.borrow_mut();
|
||||
inner.scale_factor = scale_factor;
|
||||
// we can't handle errors here, so ignore it
|
||||
// worst that can happen is cursor drawn with the wrong
|
||||
// scale factor
|
||||
let _ = inner.update_cursor(&my_pointer);
|
||||
}
|
||||
}),
|
||||
);
|
||||
ThemedPointer { pointer, inner }
|
||||
}
|
||||
|
||||
/// Initialize a new pointer as a ThemedPointer with an adapter implementation
|
||||
///
|
||||
/// You need to provide an implementation as if implementing a `wl_pointer`, but
|
||||
/// it will receive as `meta` argument a `ThemedPointer` wrapping your pointer,
|
||||
/// rather than a `WlPointer`.
|
||||
pub fn theme_pointer_with_impl<F>(
|
||||
&self,
|
||||
seat: &Attached<wl_seat::WlSeat>,
|
||||
mut callback: F,
|
||||
) -> ThemedPointer
|
||||
where
|
||||
F: FnMut(wl_pointer::Event, ThemedPointer, DispatchData) + 'static,
|
||||
{
|
||||
let surface = self.compositor.create_surface();
|
||||
let inner = Rc::new(RefCell::new(PointerInner {
|
||||
surface: surface.detach(),
|
||||
themes: self.themes.clone(),
|
||||
last_serial: 0,
|
||||
current_cursor: "left_ptr".into(),
|
||||
scale_factor: 1,
|
||||
}));
|
||||
|
||||
let inner2 = inner.clone();
|
||||
let pointer = seat.get_pointer();
|
||||
pointer.quick_assign(move |ptr, event, ddata| {
|
||||
callback(event, ThemedPointer { pointer: ptr.detach(), inner: inner2.clone() }, ddata)
|
||||
});
|
||||
|
||||
let winner = Rc::downgrade(&inner);
|
||||
let my_pointer = pointer.clone();
|
||||
crate::surface::setup_surface(
|
||||
surface,
|
||||
Some(move |scale_factor, _, _: DispatchData| {
|
||||
if let Some(inner) = Weak::upgrade(&winner) {
|
||||
let mut inner = inner.borrow_mut();
|
||||
inner.scale_factor = scale_factor;
|
||||
// we can't handle errors here, so ignore it
|
||||
// worst that can happen is cursor drawn with the wrong
|
||||
// scale factor
|
||||
let _ = inner.update_cursor(&my_pointer);
|
||||
}
|
||||
}),
|
||||
);
|
||||
|
||||
ThemedPointer { pointer: pointer.detach(), inner }
|
||||
}
|
||||
}
|
||||
|
||||
struct ScaledThemeList {
|
||||
shm: Attached<wl_shm::WlShm>,
|
||||
name: String,
|
||||
size: u32,
|
||||
themes: Vec<(u32, CursorTheme)>,
|
||||
}
|
||||
|
||||
impl ScaledThemeList {
|
||||
fn new(theme: ThemeSpec, shm: Attached<wl_shm::WlShm>) -> ScaledThemeList {
|
||||
let (name, size) = match theme {
|
||||
ThemeSpec::Precise { name, size } => (name.into(), size),
|
||||
ThemeSpec::System => {
|
||||
let name = std::env::var("XCURSOR_THEME").ok().unwrap_or_else(|| "default".into());
|
||||
let size =
|
||||
std::env::var("XCURSOR_SIZE").ok().and_then(|s| s.parse().ok()).unwrap_or(24);
|
||||
(name, size)
|
||||
}
|
||||
};
|
||||
ScaledThemeList { shm, name, size, themes: vec![] }
|
||||
}
|
||||
|
||||
fn get_cursor(&mut self, name: &str, scale: u32) -> Option<&Cursor> {
|
||||
// Check if we already loaded the theme for this scale factor
|
||||
let opt_index = self.themes.iter().position(|&(s, _)| s == scale);
|
||||
if let Some(idx) = opt_index {
|
||||
self.themes[idx].1.get_cursor(name)
|
||||
} else {
|
||||
let new_theme = CursorTheme::load_from_name(&self.name, self.size * scale, &self.shm);
|
||||
self.themes.push((scale, new_theme));
|
||||
self.themes.last_mut().unwrap().1.get_cursor(name)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Debug for ScaledThemeList {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
f.debug_struct("ScaledThemeList")
|
||||
.field("shm", &self.shm)
|
||||
.field("name", &self.name)
|
||||
.field("size", &self.size)
|
||||
// Wayland-cursor needs to implement debug
|
||||
.field("themes", &"[...]")
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
struct PointerInner {
|
||||
surface: wl_surface::WlSurface,
|
||||
themes: Rc<RefCell<ScaledThemeList>>,
|
||||
current_cursor: String,
|
||||
last_serial: u32,
|
||||
scale_factor: i32,
|
||||
}
|
||||
|
||||
impl PointerInner {
|
||||
fn update_cursor(&self, pointer: &wl_pointer::WlPointer) -> Result<(), CursorNotFound> {
|
||||
let mut themes = self.themes.borrow_mut();
|
||||
let scale = self.scale_factor as u32;
|
||||
let cursor = themes.get_cursor(&self.current_cursor, scale).ok_or(CursorNotFound)?;
|
||||
let image = &cursor[0];
|
||||
let (w, h) = image.dimensions();
|
||||
let (hx, hy) = image.hotspot();
|
||||
self.surface.set_buffer_scale(scale as i32);
|
||||
self.surface.attach(Some(image), 0, 0);
|
||||
if self.surface.as_ref().version() >= 4 {
|
||||
self.surface.damage_buffer(0, 0, w as i32, h as i32);
|
||||
} else {
|
||||
// surface is old and does not support damage_buffer, so we damage
|
||||
// in surface coordinates and hope it is not rescaled
|
||||
self.surface.damage(0, 0, w as i32 / scale as i32, h as i32 / scale as i32);
|
||||
}
|
||||
self.surface.commit();
|
||||
pointer.set_cursor(
|
||||
self.last_serial,
|
||||
Some(&self.surface),
|
||||
hx as i32 / scale as i32,
|
||||
hy as i32 / scale as i32,
|
||||
);
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
/// Wrapper of a themed pointer
|
||||
///
|
||||
/// You can access the underlying `wl_pointer::WlPointer` via
|
||||
/// deref. It will *not* release the proxy when dropped.
|
||||
///
|
||||
/// Just like `Proxy`, this is a `Rc`-like wrapper. You can clone it
|
||||
/// to have several handles to the same theming machinery of a pointer.
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct ThemedPointer {
|
||||
pointer: wl_pointer::WlPointer,
|
||||
inner: Rc<RefCell<PointerInner>>,
|
||||
}
|
||||
|
||||
impl ThemedPointer {
|
||||
/// Change the cursor to the given cursor name
|
||||
///
|
||||
/// Possible names depend on the theme. Does nothing and returns
|
||||
/// `Err` if given name is not available.
|
||||
///
|
||||
/// If this is done as an answer to an input event, you need to provide
|
||||
/// the associated serial otherwise the server may ignore the request.
|
||||
pub fn set_cursor(&self, name: &str, serial: Option<u32>) -> Result<(), CursorNotFound> {
|
||||
let mut inner = self.inner.borrow_mut();
|
||||
if let Some(s) = serial {
|
||||
inner.last_serial = s;
|
||||
}
|
||||
inner.current_cursor = name.into();
|
||||
inner.update_cursor(&self.pointer)
|
||||
}
|
||||
}
|
||||
|
||||
impl Deref for ThemedPointer {
|
||||
type Target = wl_pointer::WlPointer;
|
||||
fn deref(&self) -> &wl_pointer::WlPointer {
|
||||
&self.pointer
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for PointerInner {
|
||||
fn drop(&mut self) {
|
||||
self.surface.destroy();
|
||||
}
|
||||
}
|
||||
|
||||
/// An error representing the fact that the required cursor was not found
|
||||
#[derive(Debug, Copy, Clone)]
|
||||
pub struct CursorNotFound;
|
||||
|
||||
impl std::error::Error for CursorNotFound {}
|
||||
|
||||
impl std::fmt::Display for CursorNotFound {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
f.write_str("cursor not found")
|
||||
}
|
||||
}
|
||||
381
third-party/vendor/smithay-client-toolkit/src/shell/mod.rs
vendored
Normal file
381
third-party/vendor/smithay-client-toolkit/src/shell/mod.rs
vendored
Normal file
|
|
@ -0,0 +1,381 @@
|
|||
//! Unified shell surface handling
|
||||
//!
|
||||
//! This module provides an abstraction unifying the various iterations of
|
||||
//! the shell surface protocols (`wl_shell`, `zxdg_shell_v6` and `xdg_shell`,
|
||||
//! the current standard).
|
||||
//!
|
||||
//! This abstraction only manages the protocol part of shell surfaces. If you're
|
||||
//! looking for a more battery-included abstraction for creating windows,
|
||||
//! consider the `Window` type.
|
||||
use std::{cell::RefCell, fmt};
|
||||
|
||||
use wayland_client::{
|
||||
protocol::{wl_output, wl_registry, wl_seat, wl_shell, wl_surface},
|
||||
Attached, DispatchData,
|
||||
};
|
||||
|
||||
pub use wayland_protocols::xdg_shell::client::xdg_toplevel::State;
|
||||
use wayland_protocols::{
|
||||
unstable::xdg_shell::v6::client::zxdg_shell_v6,
|
||||
xdg_shell::client::{xdg_toplevel, xdg_wm_base},
|
||||
};
|
||||
|
||||
use crate::environment::{Environment, GlobalHandler};
|
||||
|
||||
mod wl;
|
||||
mod xdg;
|
||||
mod zxdg;
|
||||
|
||||
use crate::lazy_global::LazyGlobal;
|
||||
|
||||
/// Possible events generated by a shell surface that you need to handle
|
||||
#[derive(Clone, Debug)]
|
||||
pub enum Event {
|
||||
/// The state of your window has been changed
|
||||
Configure {
|
||||
/// Optional new size for your shell surface
|
||||
///
|
||||
/// This is the new size of the contents of your shell surface
|
||||
/// as suggested by the server. You can ignore it and choose
|
||||
/// a new size if you want better control on the possible
|
||||
/// sizes of your shell surface.
|
||||
///
|
||||
/// In all cases, these events can be generated in large batches
|
||||
/// during an interactive resize, and you should buffer them before
|
||||
/// processing them. You only need to handle the last one of a batch.
|
||||
new_size: Option<(u32, u32)>,
|
||||
/// New combination of states of your window
|
||||
///
|
||||
/// Typically tells you if your surface is active/inactive, maximized,
|
||||
/// etc...
|
||||
states: Vec<State>,
|
||||
},
|
||||
/// A close request has been received
|
||||
///
|
||||
/// Most likely the user has clicked on the close button of the decorations
|
||||
/// or something equivalent
|
||||
Close,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
/// Possible supported shell protocols
|
||||
pub enum Shell {
|
||||
/// The current standard `xdg_shell` protocol.
|
||||
Xdg(Attached<xdg_wm_base::XdgWmBase>),
|
||||
/// A previous iteration of the `xdg_shell` protocol.
|
||||
///
|
||||
/// It has been replaced by the stable `xdg_shell`, and is only present here for
|
||||
/// compatibility purposes.
|
||||
Zxdg(Attached<zxdg_shell_v6::ZxdgShellV6>),
|
||||
/// The legacy `wl_shell`.
|
||||
///
|
||||
/// It is deprecated and only present here for compatibility purposes.
|
||||
Wl(Attached<wl_shell::WlShell>),
|
||||
}
|
||||
|
||||
impl Shell {
|
||||
/// Check if the shell in use needs you to wait for a `configure` event
|
||||
/// before you are allowed to draw.
|
||||
pub fn needs_configure(&self) -> bool {
|
||||
match self {
|
||||
Shell::Wl(_) => false,
|
||||
Shell::Xdg(_) => true,
|
||||
Shell::Zxdg(_) => true,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn create_shell_surface<F>(
|
||||
shell: &Shell,
|
||||
surface: &wl_surface::WlSurface,
|
||||
callback: F,
|
||||
) -> Box<dyn ShellSurface>
|
||||
where
|
||||
F: FnMut(Event, DispatchData) + 'static,
|
||||
{
|
||||
match *shell {
|
||||
Shell::Wl(ref shell) => Box::new(wl::Wl::create(surface, shell, callback)) as Box<_>,
|
||||
Shell::Xdg(ref shell) => Box::new(xdg::Xdg::create(surface, shell, callback)) as Box<_>,
|
||||
Shell::Zxdg(ref shell) => Box::new(zxdg::Zxdg::create(surface, shell, callback)) as Box<_>,
|
||||
}
|
||||
}
|
||||
|
||||
/// Trait abstracting over shell surface protocols
|
||||
///
|
||||
/// This trait's API is designed to reflect the behavior of the current standard
|
||||
/// shell surface protocol: `xdg_shell`. Compatibility implementations are
|
||||
/// provided for older protocols.
|
||||
pub trait ShellSurface: fmt::Debug + Send + Sync {
|
||||
/// Resizes the shell surface
|
||||
fn resize(&self, seat: &wl_seat::WlSeat, serial: u32, edges: xdg_toplevel::ResizeEdge);
|
||||
/// Moves the shell surface
|
||||
fn move_(&self, seat: &wl_seat::WlSeat, serial: u32);
|
||||
/// Set the title of the shell surface
|
||||
fn set_title(&self, title: String);
|
||||
/// Set the app id of the shell surface
|
||||
fn set_app_id(&self, app_id: String);
|
||||
/// Make fullscreen
|
||||
fn set_fullscreen(&self, output: Option<&wl_output::WlOutput>);
|
||||
/// Unset fullscreen
|
||||
fn unset_fullscreen(&self);
|
||||
/// Maximize surface
|
||||
fn set_maximized(&self);
|
||||
/// Unmaximize surface
|
||||
fn unset_maximized(&self);
|
||||
/// Minimize surface
|
||||
fn set_minimized(&self);
|
||||
/// Set geometry
|
||||
fn set_geometry(&self, x: i32, y: i32, width: i32, height: i32);
|
||||
/// Set minimum surface size
|
||||
fn set_min_size(&self, size: Option<(i32, i32)>);
|
||||
/// Set maximum surface size
|
||||
fn set_max_size(&self, size: Option<(i32, i32)>);
|
||||
/// Show window menu.
|
||||
fn show_window_menu(&self, seat: &wl_seat::WlSeat, serial: u32, x: i32, y: i32);
|
||||
/// Retrive the `XdgToplevel` proxy if the underlying shell surface
|
||||
/// uses the `xdg_shell` protocol.
|
||||
///
|
||||
/// This allows interactions with other protocol extensions, like
|
||||
/// `xdg_decoratins` for example.
|
||||
fn get_xdg(&self) -> Option<&xdg_toplevel::XdgToplevel>;
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
struct ShellInner {
|
||||
registry: Option<Attached<wl_registry::WlRegistry>>,
|
||||
wl_shell: LazyGlobal<wl_shell::WlShell>,
|
||||
xdg_shell: LazyGlobal<xdg_wm_base::XdgWmBase>,
|
||||
zxdg_shell: LazyGlobal<zxdg_shell_v6::ZxdgShellV6>,
|
||||
}
|
||||
|
||||
/// A handler for shells
|
||||
///
|
||||
/// For use with the [`environment!`](../macro.environment.html) macro. It is already
|
||||
/// automatically included if you use the [`default_environment!`](../macro.default_environment.hmtl).
|
||||
///
|
||||
/// To use it, you need to set it as a handler for the shells you want to support (`xdg_wm_base`,
|
||||
/// `zxdg_shell_v6` and/or `wl_shell`). You can then implement the
|
||||
/// [`ShellHandling`](trait.ShellHandling.html) by delegating it, to get the shell-related methods on
|
||||
/// [`Environment`](../environment/struct.environment.html)
|
||||
///
|
||||
/// ```no_run
|
||||
/// # extern crate smithay_client_toolkit as sctk;
|
||||
/// # use sctk::environment;
|
||||
/// # use sctk::environment::Environment;
|
||||
/// # use sctk::shell::*;
|
||||
/// # use sctk::reexports::client::protocol::wl_shell;
|
||||
/// # use sctk::reexports::protocols::xdg_shell::client::xdg_wm_base;
|
||||
/// # use sctk::reexports::protocols::unstable::xdg_shell::v6::client::zxdg_shell_v6;
|
||||
/// # let display = sctk::reexports::client::Display::connect_to_env().unwrap();
|
||||
/// # let mut queue = display.create_event_queue();
|
||||
/// # let attached_display = display.attach(queue.token());
|
||||
/// struct MyEnv {
|
||||
/// my_shell: ShellHandler
|
||||
/// }
|
||||
///
|
||||
/// environment!(MyEnv,
|
||||
/// singles=[
|
||||
/// wl_shell::WlShell => my_shell,
|
||||
/// xdg_wm_base::XdgWmBase => my_shell,
|
||||
/// zxdg_shell_v6::ZxdgShellV6 => my_shell
|
||||
/// ],
|
||||
/// multis=[],
|
||||
/// );
|
||||
///
|
||||
/// impl ShellHandling for MyEnv {
|
||||
/// fn get_shell(&self) -> Option<Shell> {
|
||||
/// // delegate the impl to the stored handler
|
||||
/// self.my_shell.get_shell()
|
||||
/// }
|
||||
/// }
|
||||
///
|
||||
/// let env = Environment::new(&attached_display, &mut queue, MyEnv {
|
||||
/// my_shell: ShellHandler::new()
|
||||
/// });
|
||||
/// ```
|
||||
#[derive(Debug)]
|
||||
pub struct ShellHandler {
|
||||
inner: RefCell<ShellInner>,
|
||||
}
|
||||
|
||||
impl ShellHandler {
|
||||
/// Create a new handler
|
||||
pub fn new() -> ShellHandler {
|
||||
ShellHandler {
|
||||
inner: RefCell::new(ShellInner {
|
||||
registry: None,
|
||||
wl_shell: LazyGlobal::Unknown,
|
||||
xdg_shell: LazyGlobal::Unknown,
|
||||
zxdg_shell: LazyGlobal::Unknown,
|
||||
}),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl GlobalHandler<wl_shell::WlShell> for ShellHandler {
|
||||
fn created(
|
||||
&mut self,
|
||||
registry: Attached<wl_registry::WlRegistry>,
|
||||
id: u32,
|
||||
version: u32,
|
||||
_: DispatchData,
|
||||
) {
|
||||
let mut inner = self.inner.borrow_mut();
|
||||
if inner.registry.is_none() {
|
||||
inner.registry = Some(registry);
|
||||
}
|
||||
if let LazyGlobal::Unknown = inner.wl_shell {
|
||||
inner.wl_shell = LazyGlobal::Seen { id, version };
|
||||
} else {
|
||||
log::warn!("Compositor advertised wl_shell multiple times, ignoring.")
|
||||
}
|
||||
}
|
||||
fn get(&self) -> Option<Attached<wl_shell::WlShell>> {
|
||||
let mut inner = self.inner.borrow_mut();
|
||||
match inner.wl_shell {
|
||||
LazyGlobal::Bound(ref shell) => Some(shell.clone()),
|
||||
LazyGlobal::Unknown => None,
|
||||
LazyGlobal::Seen { id, .. } => {
|
||||
// registry cannot be None if we have seen the global
|
||||
let registry = inner.registry.as_ref().unwrap();
|
||||
// only version 1 of wl_shell is supported
|
||||
let shell = registry.bind::<wl_shell::WlShell>(1, id);
|
||||
inner.wl_shell = LazyGlobal::Bound((*shell).clone());
|
||||
Some((*shell).clone())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl GlobalHandler<xdg_wm_base::XdgWmBase> for ShellHandler {
|
||||
fn created(
|
||||
&mut self,
|
||||
registry: Attached<wl_registry::WlRegistry>,
|
||||
id: u32,
|
||||
version: u32,
|
||||
_: DispatchData,
|
||||
) {
|
||||
let mut inner = self.inner.borrow_mut();
|
||||
if inner.registry.is_none() {
|
||||
inner.registry = Some(registry);
|
||||
}
|
||||
if let LazyGlobal::Unknown = inner.xdg_shell {
|
||||
inner.xdg_shell = LazyGlobal::Seen { id, version };
|
||||
} else {
|
||||
log::warn!("Compositor advertised xdg_wm_base multiple times, ignoring.")
|
||||
}
|
||||
}
|
||||
fn get(&self) -> Option<Attached<xdg_wm_base::XdgWmBase>> {
|
||||
let mut inner = self.inner.borrow_mut();
|
||||
match inner.xdg_shell {
|
||||
LazyGlobal::Bound(ref shell) => Some(shell.clone()),
|
||||
LazyGlobal::Unknown => None,
|
||||
LazyGlobal::Seen { version, id } => {
|
||||
// registry cannot be None if we have seen the global
|
||||
let registry = inner.registry.as_ref().unwrap();
|
||||
// we currently support xdg_shell up to version 2
|
||||
let version = std::cmp::min(2, version);
|
||||
let shell = registry.bind::<xdg_wm_base::XdgWmBase>(version, id);
|
||||
shell.quick_assign(|shell, event, _| {
|
||||
if let xdg_wm_base::Event::Ping { serial } = event {
|
||||
shell.pong(serial);
|
||||
}
|
||||
});
|
||||
inner.xdg_shell = LazyGlobal::Bound((*shell).clone());
|
||||
Some((*shell).clone())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl GlobalHandler<zxdg_shell_v6::ZxdgShellV6> for ShellHandler {
|
||||
fn created(
|
||||
&mut self,
|
||||
registry: Attached<wl_registry::WlRegistry>,
|
||||
id: u32,
|
||||
version: u32,
|
||||
_: DispatchData,
|
||||
) {
|
||||
let mut inner = self.inner.borrow_mut();
|
||||
if inner.registry.is_none() {
|
||||
inner.registry = Some(registry);
|
||||
}
|
||||
if let LazyGlobal::Unknown = inner.zxdg_shell {
|
||||
inner.zxdg_shell = LazyGlobal::Seen { id, version };
|
||||
} else {
|
||||
log::warn!("Compositor advertised zxdg_shell_v6 multiple times, ignoring.")
|
||||
}
|
||||
}
|
||||
fn get(&self) -> Option<Attached<zxdg_shell_v6::ZxdgShellV6>> {
|
||||
let mut inner = self.inner.borrow_mut();
|
||||
match inner.zxdg_shell {
|
||||
LazyGlobal::Bound(ref shell) => Some(shell.clone()),
|
||||
LazyGlobal::Unknown => None,
|
||||
LazyGlobal::Seen { id, .. } => {
|
||||
// registry cannot be None if we have seen the global
|
||||
let registry = inner.registry.as_ref().unwrap();
|
||||
// only version 1 of zxdg_shell_v6 is supported
|
||||
let shell = registry.bind::<zxdg_shell_v6::ZxdgShellV6>(1, id);
|
||||
shell.quick_assign(|shell, event, _| {
|
||||
if let zxdg_shell_v6::Event::Ping { serial } = event {
|
||||
shell.pong(serial);
|
||||
}
|
||||
});
|
||||
inner.zxdg_shell = LazyGlobal::Bound((*shell).clone());
|
||||
Some((*shell).clone())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl ShellHandling for ShellHandler {
|
||||
fn get_shell(&self) -> Option<Shell> {
|
||||
GlobalHandler::<xdg_wm_base::XdgWmBase>::get(self)
|
||||
.map(Shell::Xdg)
|
||||
.or_else(|| GlobalHandler::<zxdg_shell_v6::ZxdgShellV6>::get(self).map(Shell::Zxdg))
|
||||
.or_else(|| GlobalHandler::<wl_shell::WlShell>::get(self).map(Shell::Wl))
|
||||
}
|
||||
}
|
||||
|
||||
/// A helper trait for delegating shell handling
|
||||
///
|
||||
/// If you don't use [`declare_default_environment!`](../macro.declare_default_environment.html) but still
|
||||
/// want to use the shell helpers provided here, you need to implement this trait for your
|
||||
/// [`declare_environment!`](../macro.declare_environment.html)-generated type, by delegating it to one
|
||||
/// of the handlers you provided for the different shells.
|
||||
pub trait ShellHandling {
|
||||
/// Get the best available shell
|
||||
fn get_shell(&self) -> Option<Shell>;
|
||||
}
|
||||
|
||||
impl<E: ShellHandling> Environment<E> {
|
||||
/// Get the best available shell protocol
|
||||
///
|
||||
/// Returns `None` if no shell was advertised.
|
||||
pub fn get_shell(&self) -> Option<Shell> {
|
||||
self.with_inner(|extras| extras.get_shell())
|
||||
}
|
||||
/// Create a new shell surface for this surface
|
||||
///
|
||||
/// This helper abstracts over the `xdg_shell` protocol and its precursors (`zxdg_shell_v6`
|
||||
/// and `wl_shell`) for retro-compatibility. It'll attempt to use them in this order.
|
||||
///
|
||||
/// You need to provide a closure that will process the events generated by the shell surface.
|
||||
///
|
||||
/// *Panic*
|
||||
///
|
||||
/// This function will panic if no supported shell was advertised by the compositor.
|
||||
pub fn create_shell_surface<F>(
|
||||
&self,
|
||||
surface: &wl_surface::WlSurface,
|
||||
f: F,
|
||||
) -> Box<dyn ShellSurface>
|
||||
where
|
||||
F: FnMut(Event, DispatchData) + 'static,
|
||||
{
|
||||
let shell = self
|
||||
.get_shell()
|
||||
.expect("SCTK: trying to create a shell surface without any supported shell.");
|
||||
create_shell_surface(&shell, surface, f)
|
||||
}
|
||||
}
|
||||
116
third-party/vendor/smithay-client-toolkit/src/shell/wl.rs
vendored
Normal file
116
third-party/vendor/smithay-client-toolkit/src/shell/wl.rs
vendored
Normal file
|
|
@ -0,0 +1,116 @@
|
|||
use wayland_client::{
|
||||
protocol::{wl_output, wl_seat, wl_shell, wl_shell_surface, wl_surface},
|
||||
DispatchData,
|
||||
};
|
||||
|
||||
use wayland_protocols::xdg_shell::client::xdg_toplevel;
|
||||
|
||||
use super::{Event, ShellSurface};
|
||||
|
||||
#[derive(Debug)]
|
||||
pub(crate) struct Wl {
|
||||
shell_surface: wl_shell_surface::WlShellSurface,
|
||||
}
|
||||
|
||||
impl Wl {
|
||||
pub(crate) fn create<Impl>(
|
||||
surface: &wl_surface::WlSurface,
|
||||
shell: &wl_shell::WlShell,
|
||||
mut implementation: Impl,
|
||||
) -> Wl
|
||||
where
|
||||
Impl: FnMut(Event, DispatchData) + 'static,
|
||||
{
|
||||
let shell_surface = shell.get_shell_surface(surface);
|
||||
shell_surface.quick_assign(move |shell_surface, event, ddata| match event {
|
||||
wl_shell_surface::Event::Ping { serial } => {
|
||||
shell_surface.pong(serial);
|
||||
}
|
||||
wl_shell_surface::Event::Configure { width, height, .. } => {
|
||||
use std::cmp::max;
|
||||
implementation(
|
||||
Event::Configure {
|
||||
new_size: Some((max(width, 1) as u32, max(height, 1) as u32)),
|
||||
states: Vec::new(),
|
||||
},
|
||||
ddata,
|
||||
);
|
||||
}
|
||||
wl_shell_surface::Event::PopupDone => {
|
||||
unreachable!();
|
||||
}
|
||||
_ => unreachable!(),
|
||||
});
|
||||
shell_surface.set_toplevel();
|
||||
Wl { shell_surface: shell_surface.detach() }
|
||||
}
|
||||
}
|
||||
|
||||
impl ShellSurface for Wl {
|
||||
fn resize(&self, seat: &wl_seat::WlSeat, serial: u32, edges: xdg_toplevel::ResizeEdge) {
|
||||
let edges = match edges {
|
||||
xdg_toplevel::ResizeEdge::None => wl_shell_surface::Resize::None,
|
||||
xdg_toplevel::ResizeEdge::Top => wl_shell_surface::Resize::Top,
|
||||
xdg_toplevel::ResizeEdge::Left => wl_shell_surface::Resize::Left,
|
||||
xdg_toplevel::ResizeEdge::Right => wl_shell_surface::Resize::Right,
|
||||
xdg_toplevel::ResizeEdge::Bottom => wl_shell_surface::Resize::Bottom,
|
||||
xdg_toplevel::ResizeEdge::TopLeft => wl_shell_surface::Resize::TopLeft,
|
||||
xdg_toplevel::ResizeEdge::TopRight => wl_shell_surface::Resize::TopRight,
|
||||
xdg_toplevel::ResizeEdge::BottomLeft => wl_shell_surface::Resize::BottomLeft,
|
||||
xdg_toplevel::ResizeEdge::BottomRight => wl_shell_surface::Resize::BottomRight,
|
||||
_ => unreachable!(),
|
||||
};
|
||||
self.shell_surface.resize(seat, serial, edges);
|
||||
}
|
||||
|
||||
fn move_(&self, seat: &wl_seat::WlSeat, serial: u32) {
|
||||
self.shell_surface._move(seat, serial);
|
||||
}
|
||||
|
||||
fn set_title(&self, title: String) {
|
||||
self.shell_surface.set_title(title);
|
||||
}
|
||||
|
||||
fn set_app_id(&self, app_id: String) {
|
||||
self.shell_surface.set_class(app_id);
|
||||
}
|
||||
fn set_fullscreen(&self, output: Option<&wl_output::WlOutput>) {
|
||||
self.shell_surface.set_fullscreen(wl_shell_surface::FullscreenMethod::Default, 0, output)
|
||||
}
|
||||
|
||||
fn unset_fullscreen(&self) {
|
||||
self.shell_surface.set_toplevel();
|
||||
}
|
||||
|
||||
fn set_maximized(&self) {
|
||||
self.shell_surface.set_maximized(None);
|
||||
}
|
||||
|
||||
fn unset_maximized(&self) {
|
||||
self.shell_surface.set_toplevel();
|
||||
}
|
||||
|
||||
fn show_window_menu(&self, _: &wl_seat::WlSeat, _: u32, _: i32, _: i32) {
|
||||
/* not available */
|
||||
}
|
||||
|
||||
fn set_minimized(&self) {
|
||||
/* not available */
|
||||
}
|
||||
|
||||
fn set_geometry(&self, _: i32, _: i32, _: i32, _: i32) {
|
||||
/* not available */
|
||||
}
|
||||
|
||||
fn set_min_size(&self, _: Option<(i32, i32)>) {
|
||||
/* not available */
|
||||
}
|
||||
|
||||
fn set_max_size(&self, _: Option<(i32, i32)>) {
|
||||
/* not available */
|
||||
}
|
||||
|
||||
fn get_xdg(&self) -> Option<&xdg_toplevel::XdgToplevel> {
|
||||
None
|
||||
}
|
||||
}
|
||||
141
third-party/vendor/smithay-client-toolkit/src/shell/xdg.rs
vendored
Normal file
141
third-party/vendor/smithay-client-toolkit/src/shell/xdg.rs
vendored
Normal file
|
|
@ -0,0 +1,141 @@
|
|||
use std::{cell::RefCell, convert::TryInto, rc::Rc};
|
||||
|
||||
use wayland_client::{
|
||||
protocol::{wl_output, wl_seat, wl_surface},
|
||||
DispatchData,
|
||||
};
|
||||
|
||||
use wayland_protocols::xdg_shell::client::{xdg_surface, xdg_toplevel, xdg_wm_base};
|
||||
|
||||
use super::{Event, ShellSurface};
|
||||
|
||||
#[derive(Debug)]
|
||||
pub(crate) struct Xdg {
|
||||
surface: xdg_surface::XdgSurface,
|
||||
toplevel: xdg_toplevel::XdgToplevel,
|
||||
}
|
||||
|
||||
impl Xdg {
|
||||
pub(crate) fn create<Impl>(
|
||||
surface: &wl_surface::WlSurface,
|
||||
shell: &xdg_wm_base::XdgWmBase,
|
||||
implementation: Impl,
|
||||
) -> Xdg
|
||||
where
|
||||
Impl: FnMut(Event, DispatchData) + 'static,
|
||||
{
|
||||
let pending_configure = Rc::new(RefCell::new(None));
|
||||
let pending_configure_2 = pending_configure.clone();
|
||||
|
||||
let implementation = Rc::new(RefCell::new(implementation));
|
||||
let implementation_2 = implementation.clone();
|
||||
let xdgs = shell.get_xdg_surface(surface);
|
||||
xdgs.quick_assign(move |xdgs, evt, ddata| match evt {
|
||||
xdg_surface::Event::Configure { serial } => {
|
||||
xdgs.ack_configure(serial);
|
||||
if let Some((new_size, states)) = pending_configure_2.borrow_mut().take() {
|
||||
(implementation_2.borrow_mut())(Event::Configure { new_size, states }, ddata);
|
||||
}
|
||||
}
|
||||
_ => unreachable!(),
|
||||
});
|
||||
let toplevel = xdgs.get_toplevel();
|
||||
toplevel.quick_assign(move |_, evt, ddata| {
|
||||
match evt {
|
||||
xdg_toplevel::Event::Close => (implementation.borrow_mut())(Event::Close, ddata),
|
||||
xdg_toplevel::Event::Configure { width, height, states } => {
|
||||
use std::cmp::max;
|
||||
let new_size = if width == 0 || height == 0 {
|
||||
// if either w or h is zero, then we get to choose our size
|
||||
None
|
||||
} else {
|
||||
Some((max(width, 1) as u32, max(height, 1) as u32))
|
||||
};
|
||||
let translated_states = states
|
||||
.chunks_exact(4)
|
||||
.map(|c| u32::from_ne_bytes(c.try_into().unwrap()))
|
||||
.flat_map(xdg_toplevel::State::from_raw)
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
*pending_configure.borrow_mut() = Some((new_size, translated_states));
|
||||
}
|
||||
_ => unreachable!(),
|
||||
}
|
||||
});
|
||||
surface.commit();
|
||||
Xdg { surface: xdgs.detach(), toplevel: toplevel.detach() }
|
||||
}
|
||||
}
|
||||
|
||||
impl ShellSurface for Xdg {
|
||||
fn resize(&self, seat: &wl_seat::WlSeat, serial: u32, edges: xdg_toplevel::ResizeEdge) {
|
||||
self.toplevel.resize(seat, serial, edges);
|
||||
}
|
||||
|
||||
fn move_(&self, seat: &wl_seat::WlSeat, serial: u32) {
|
||||
self.toplevel._move(seat, serial);
|
||||
}
|
||||
|
||||
fn set_title(&self, title: String) {
|
||||
self.toplevel.set_title(title);
|
||||
}
|
||||
|
||||
fn set_app_id(&self, app_id: String) {
|
||||
self.toplevel.set_app_id(app_id);
|
||||
}
|
||||
|
||||
fn set_fullscreen(&self, output: Option<&wl_output::WlOutput>) {
|
||||
self.toplevel.set_fullscreen(output)
|
||||
}
|
||||
|
||||
fn unset_fullscreen(&self) {
|
||||
self.toplevel.unset_fullscreen();
|
||||
}
|
||||
|
||||
fn set_maximized(&self) {
|
||||
self.toplevel.set_maximized();
|
||||
}
|
||||
|
||||
fn unset_maximized(&self) {
|
||||
self.toplevel.unset_maximized();
|
||||
}
|
||||
|
||||
fn set_minimized(&self) {
|
||||
self.toplevel.set_minimized();
|
||||
}
|
||||
|
||||
fn show_window_menu(&self, seat: &wl_seat::WlSeat, serial: u32, x: i32, y: i32) {
|
||||
self.toplevel.show_window_menu(seat, serial, x, y);
|
||||
}
|
||||
|
||||
fn set_geometry(&self, x: i32, y: i32, width: i32, height: i32) {
|
||||
self.surface.set_window_geometry(x, y, width, height);
|
||||
}
|
||||
|
||||
fn set_min_size(&self, size: Option<(i32, i32)>) {
|
||||
if let Some((w, h)) = size {
|
||||
self.toplevel.set_min_size(w, h);
|
||||
} else {
|
||||
self.toplevel.set_min_size(0, 0);
|
||||
}
|
||||
}
|
||||
|
||||
fn set_max_size(&self, size: Option<(i32, i32)>) {
|
||||
if let Some((w, h)) = size {
|
||||
self.toplevel.set_max_size(w, h);
|
||||
} else {
|
||||
self.toplevel.set_max_size(0, 0);
|
||||
}
|
||||
}
|
||||
|
||||
fn get_xdg(&self) -> Option<&xdg_toplevel::XdgToplevel> {
|
||||
Some(&self.toplevel)
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for Xdg {
|
||||
fn drop(&mut self) {
|
||||
self.toplevel.destroy();
|
||||
self.surface.destroy();
|
||||
}
|
||||
}
|
||||
146
third-party/vendor/smithay-client-toolkit/src/shell/zxdg.rs
vendored
Normal file
146
third-party/vendor/smithay-client-toolkit/src/shell/zxdg.rs
vendored
Normal file
|
|
@ -0,0 +1,146 @@
|
|||
use std::{cell::RefCell, convert::TryInto, rc::Rc};
|
||||
|
||||
use wayland_client::{
|
||||
protocol::{wl_output, wl_seat, wl_surface},
|
||||
DispatchData,
|
||||
};
|
||||
|
||||
use wayland_protocols::{
|
||||
unstable::xdg_shell::v6::client::{zxdg_shell_v6, zxdg_surface_v6, zxdg_toplevel_v6},
|
||||
xdg_shell::client::xdg_toplevel,
|
||||
};
|
||||
|
||||
use super::{Event, ShellSurface};
|
||||
|
||||
#[derive(Debug)]
|
||||
pub(crate) struct Zxdg {
|
||||
surface: zxdg_surface_v6::ZxdgSurfaceV6,
|
||||
toplevel: zxdg_toplevel_v6::ZxdgToplevelV6,
|
||||
}
|
||||
|
||||
impl Zxdg {
|
||||
pub(crate) fn create<Impl>(
|
||||
surface: &wl_surface::WlSurface,
|
||||
shell: &zxdg_shell_v6::ZxdgShellV6,
|
||||
implementation: Impl,
|
||||
) -> Zxdg
|
||||
where
|
||||
Impl: FnMut(Event, DispatchData) + 'static,
|
||||
{
|
||||
let pending_configure = Rc::new(RefCell::new(None));
|
||||
let pending_configure_2 = pending_configure.clone();
|
||||
|
||||
let implementation = Rc::new(RefCell::new(implementation));
|
||||
let implementation_2 = implementation.clone();
|
||||
let xdgs = shell.get_xdg_surface(surface);
|
||||
xdgs.quick_assign(move |xdgs, evt, ddata| match evt {
|
||||
zxdg_surface_v6::Event::Configure { serial } => {
|
||||
xdgs.ack_configure(serial);
|
||||
if let Some((new_size, states)) = pending_configure_2.borrow_mut().take() {
|
||||
(implementation_2.borrow_mut())(Event::Configure { new_size, states }, ddata);
|
||||
}
|
||||
}
|
||||
_ => unreachable!(),
|
||||
});
|
||||
let toplevel = xdgs.get_toplevel();
|
||||
toplevel.quick_assign(move |_, evt, ddata| {
|
||||
match evt {
|
||||
zxdg_toplevel_v6::Event::Close => {
|
||||
(implementation.borrow_mut())(Event::Close, ddata)
|
||||
}
|
||||
zxdg_toplevel_v6::Event::Configure { width, height, states } => {
|
||||
use std::cmp::max;
|
||||
let new_size = if width == 0 || height == 0 {
|
||||
// if either w or h is zero, then we get to choose our size
|
||||
None
|
||||
} else {
|
||||
Some((max(width, 1) as u32, max(height, 1) as u32))
|
||||
};
|
||||
let translated_states = states
|
||||
.chunks_exact(4)
|
||||
.map(|c| u32::from_ne_bytes(c.try_into().unwrap()))
|
||||
.flat_map(xdg_toplevel::State::from_raw)
|
||||
.collect::<Vec<_>>();
|
||||
|
||||
*pending_configure.borrow_mut() = Some((new_size, translated_states));
|
||||
}
|
||||
_ => unreachable!(),
|
||||
}
|
||||
});
|
||||
surface.commit();
|
||||
Zxdg { surface: xdgs.detach(), toplevel: toplevel.detach() }
|
||||
}
|
||||
}
|
||||
|
||||
impl ShellSurface for Zxdg {
|
||||
fn resize(&self, seat: &wl_seat::WlSeat, serial: u32, edges: xdg_toplevel::ResizeEdge) {
|
||||
self.toplevel.resize(seat, serial, edges as u32);
|
||||
}
|
||||
|
||||
fn move_(&self, seat: &wl_seat::WlSeat, serial: u32) {
|
||||
self.toplevel._move(seat, serial);
|
||||
}
|
||||
|
||||
fn set_title(&self, title: String) {
|
||||
self.toplevel.set_title(title);
|
||||
}
|
||||
|
||||
fn set_app_id(&self, app_id: String) {
|
||||
self.toplevel.set_app_id(app_id);
|
||||
}
|
||||
|
||||
fn set_fullscreen(&self, output: Option<&wl_output::WlOutput>) {
|
||||
self.toplevel.set_fullscreen(output)
|
||||
}
|
||||
|
||||
fn unset_fullscreen(&self) {
|
||||
self.toplevel.unset_fullscreen();
|
||||
}
|
||||
|
||||
fn set_maximized(&self) {
|
||||
self.toplevel.set_maximized();
|
||||
}
|
||||
|
||||
fn unset_maximized(&self) {
|
||||
self.toplevel.unset_maximized();
|
||||
}
|
||||
|
||||
fn set_minimized(&self) {
|
||||
self.toplevel.set_minimized();
|
||||
}
|
||||
|
||||
fn show_window_menu(&self, seat: &wl_seat::WlSeat, serial: u32, x: i32, y: i32) {
|
||||
self.toplevel.show_window_menu(seat, serial, x, y);
|
||||
}
|
||||
|
||||
fn set_geometry(&self, x: i32, y: i32, width: i32, height: i32) {
|
||||
self.surface.set_window_geometry(x, y, width, height);
|
||||
}
|
||||
|
||||
fn set_min_size(&self, size: Option<(i32, i32)>) {
|
||||
if let Some((w, h)) = size {
|
||||
self.toplevel.set_min_size(w, h);
|
||||
} else {
|
||||
self.toplevel.set_min_size(0, 0);
|
||||
}
|
||||
}
|
||||
|
||||
fn set_max_size(&self, size: Option<(i32, i32)>) {
|
||||
if let Some((w, h)) = size {
|
||||
self.toplevel.set_max_size(w, h);
|
||||
} else {
|
||||
self.toplevel.set_max_size(0, 0);
|
||||
}
|
||||
}
|
||||
|
||||
fn get_xdg(&self) -> Option<&xdg_toplevel::XdgToplevel> {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for Zxdg {
|
||||
fn drop(&mut self) {
|
||||
self.toplevel.destroy();
|
||||
self.surface.destroy();
|
||||
}
|
||||
}
|
||||
557
third-party/vendor/smithay-client-toolkit/src/shm/mempool.rs
vendored
Normal file
557
third-party/vendor/smithay-client-toolkit/src/shm/mempool.rs
vendored
Normal file
|
|
@ -0,0 +1,557 @@
|
|||
use std::{
|
||||
cell::RefCell,
|
||||
ffi::CStr,
|
||||
fmt,
|
||||
fs::File,
|
||||
io,
|
||||
os::unix::io::{FromRawFd, RawFd},
|
||||
rc::Rc,
|
||||
time::SystemTime,
|
||||
time::UNIX_EPOCH,
|
||||
};
|
||||
|
||||
#[cfg(target_os = "linux")]
|
||||
use nix::sys::memfd;
|
||||
use nix::{
|
||||
errno::Errno,
|
||||
fcntl,
|
||||
sys::{mman, stat},
|
||||
unistd,
|
||||
};
|
||||
|
||||
use memmap2::MmapMut;
|
||||
|
||||
use wayland_client::{
|
||||
protocol::{wl_buffer, wl_shm, wl_shm_pool},
|
||||
Attached, Main,
|
||||
};
|
||||
|
||||
/// A Double memory pool, for convenient double-buffering
|
||||
///
|
||||
/// This type wraps two internal memory pool, and can be
|
||||
/// use for conveniently implementing double-buffering in your
|
||||
/// apps.
|
||||
///
|
||||
/// DoubleMemPool requires a implementation that is called when
|
||||
/// one of the two internal memory pools becomes free after None
|
||||
/// was returned from the `pool()` method.
|
||||
#[derive(Debug)]
|
||||
pub struct DoubleMemPool {
|
||||
pool1: MemPool,
|
||||
pool2: MemPool,
|
||||
free: Rc<RefCell<bool>>,
|
||||
}
|
||||
|
||||
impl DoubleMemPool {
|
||||
/// Create a double memory pool
|
||||
pub fn new<F>(shm: Attached<wl_shm::WlShm>, callback: F) -> io::Result<DoubleMemPool>
|
||||
where
|
||||
F: FnMut(wayland_client::DispatchData) + 'static,
|
||||
{
|
||||
let free = Rc::new(RefCell::new(true));
|
||||
let callback = Rc::new(RefCell::new(callback));
|
||||
let my_free = free.clone();
|
||||
let my_callback = callback.clone();
|
||||
let pool1 = MemPool::new(shm.clone(), move |ddata| {
|
||||
let signal = {
|
||||
let mut my_free = my_free.borrow_mut();
|
||||
if !*my_free {
|
||||
*my_free = true;
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
};
|
||||
if signal {
|
||||
(my_callback.borrow_mut())(ddata);
|
||||
}
|
||||
})?;
|
||||
let my_free = free.clone();
|
||||
let pool2 = MemPool::new(shm, move |ddata| {
|
||||
let signal = {
|
||||
let mut my_free = my_free.borrow_mut();
|
||||
if !*my_free {
|
||||
*my_free = true;
|
||||
true
|
||||
} else {
|
||||
false
|
||||
}
|
||||
};
|
||||
if signal {
|
||||
(callback.borrow_mut())(ddata);
|
||||
}
|
||||
})?;
|
||||
Ok(DoubleMemPool { pool1, pool2, free })
|
||||
}
|
||||
|
||||
/// This method checks both its internal memory pools and returns
|
||||
/// one if that pool does not contain any buffers that are still in use
|
||||
/// by the server. If both the memory pools contain buffers that are currently
|
||||
/// in use by the server None will be returned.
|
||||
pub fn pool(&mut self) -> Option<&mut MemPool> {
|
||||
if !self.pool1.is_used() {
|
||||
Some(&mut self.pool1)
|
||||
} else if !self.pool2.is_used() {
|
||||
Some(&mut self.pool2)
|
||||
} else {
|
||||
*self.free.borrow_mut() = false;
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
struct Inner {
|
||||
file: File,
|
||||
len: usize,
|
||||
pool: Main<wl_shm_pool::WlShmPool>,
|
||||
mmap: MmapMut,
|
||||
}
|
||||
|
||||
impl Inner {
|
||||
fn new(shm: Attached<wl_shm::WlShm>) -> io::Result<Self> {
|
||||
let mem_fd = create_shm_fd()?;
|
||||
let mem_file = unsafe { File::from_raw_fd(mem_fd) };
|
||||
mem_file.set_len(4096)?;
|
||||
|
||||
let pool = shm.create_pool(mem_fd, 4096);
|
||||
|
||||
let mmap = unsafe { MmapMut::map_mut(&mem_file).unwrap() };
|
||||
|
||||
Ok(Inner { file: mem_file, len: 4096, pool, mmap })
|
||||
}
|
||||
|
||||
fn resize(&mut self, newsize: usize) -> io::Result<()> {
|
||||
if newsize > self.len {
|
||||
self.file.set_len(newsize as u64)?;
|
||||
self.pool.resize(newsize as i32);
|
||||
self.len = newsize;
|
||||
self.mmap = unsafe { MmapMut::map_mut(&self.file).unwrap() };
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for Inner {
|
||||
fn drop(&mut self) {
|
||||
self.pool.destroy();
|
||||
}
|
||||
}
|
||||
|
||||
/// A wrapper handling an SHM memory pool backed by a shared memory file
|
||||
///
|
||||
/// This wrapper handles for you the creation of the shared memory file and its synchronization
|
||||
/// with the protocol.
|
||||
///
|
||||
/// Mempool internally tracks the release of the buffers by the compositor. As such, creating a buffer
|
||||
/// that is not commited to a surface (and then never released by the server) would cause the Mempool
|
||||
/// to be stuck believing it is still in use.
|
||||
///
|
||||
/// Mempool will also handle the destruction of buffers and as such the `destroy()` method should not
|
||||
/// be used on buffers created from Mempool.
|
||||
///
|
||||
/// Overwriting the contents of the memory pool before it is completely freed may cause graphical
|
||||
/// glitches due to the possible corruption of data while the compositor is reading it.
|
||||
///
|
||||
/// Mempool requires a callback that will be called when the pool becomes free, this
|
||||
/// happens when all the pools buffers are released by the server.
|
||||
pub struct MemPool {
|
||||
inner: Inner,
|
||||
buffer_count: Rc<RefCell<u32>>,
|
||||
callback: Rc<RefCell<dyn FnMut(wayland_client::DispatchData)>>,
|
||||
}
|
||||
|
||||
impl MemPool {
|
||||
/// Create a new memory pool associated with given shm
|
||||
pub fn new<F>(shm: Attached<wl_shm::WlShm>, callback: F) -> io::Result<MemPool>
|
||||
where
|
||||
F: FnMut(wayland_client::DispatchData) + 'static,
|
||||
{
|
||||
Ok(MemPool {
|
||||
inner: Inner::new(shm)?,
|
||||
buffer_count: Rc::new(RefCell::new(0)),
|
||||
callback: Rc::new(RefCell::new(callback)) as Rc<RefCell<_>>,
|
||||
})
|
||||
}
|
||||
|
||||
/// Resize the memory pool
|
||||
///
|
||||
/// This affect the size as it is seen by the wayland server. Even
|
||||
/// if you extend the temporary file size by writing to it, you need to
|
||||
/// call this method otherwise the server won't see the new size.
|
||||
///
|
||||
/// Memory pools can only be extented, as such this method will do nothing
|
||||
/// if the requested new size is smaller than the current size.
|
||||
///
|
||||
/// This method allows you to ensure the underlying pool is large enough to
|
||||
/// hold what you want to write to it.
|
||||
pub fn resize(&mut self, newsize: usize) -> io::Result<()> {
|
||||
self.inner.resize(newsize)
|
||||
}
|
||||
|
||||
/// Create a new buffer to this pool
|
||||
///
|
||||
/// The parameters are:
|
||||
///
|
||||
/// - `offset`: the offset (in bytes) from the beginning of the pool at which this
|
||||
/// buffer starts
|
||||
/// - `width`: the width of this buffer (in pixels)
|
||||
/// - `height`: the height of this buffer (in pixels)
|
||||
/// - `stride`: distance (in bytes) between the beginning of a row and the next one
|
||||
/// - `format`: the encoding format of the pixels. Using a format that was not
|
||||
/// advertised to the `wl_shm` global by the server is a protocol error and will
|
||||
/// terminate your connection
|
||||
pub fn buffer(
|
||||
&self,
|
||||
offset: i32,
|
||||
width: i32,
|
||||
height: i32,
|
||||
stride: i32,
|
||||
format: wl_shm::Format,
|
||||
) -> wl_buffer::WlBuffer {
|
||||
*self.buffer_count.borrow_mut() += 1;
|
||||
let my_buffer_count = self.buffer_count.clone();
|
||||
let my_callback = self.callback.clone();
|
||||
let buffer = self.inner.pool.create_buffer(offset, width, height, stride, format);
|
||||
buffer.quick_assign(move |buffer, event, dispatch_data| match event {
|
||||
wl_buffer::Event::Release => {
|
||||
buffer.destroy();
|
||||
let new_count = {
|
||||
// borrow the buffer_count for as short as possible, in case
|
||||
// the user wants to create a new buffer from the callback
|
||||
let mut my_buffer_count = my_buffer_count.borrow_mut();
|
||||
*my_buffer_count -= 1;
|
||||
*my_buffer_count
|
||||
};
|
||||
if new_count == 0 {
|
||||
(my_callback.borrow_mut())(dispatch_data);
|
||||
}
|
||||
}
|
||||
_ => unreachable!(),
|
||||
});
|
||||
(*buffer).clone().detach()
|
||||
}
|
||||
|
||||
/// Uses the memmap2 crate to map the underlying shared memory file
|
||||
pub fn mmap(&mut self) -> &mut MmapMut {
|
||||
&mut self.inner.mmap
|
||||
}
|
||||
|
||||
/// Returns true if the pool contains buffers that are currently in use by the server
|
||||
pub fn is_used(&self) -> bool {
|
||||
*self.buffer_count.borrow() != 0
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Debug for MemPool {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
f.debug_struct("MemPool")
|
||||
.field("inner", &self.inner)
|
||||
.field("buffer_count", &self.buffer_count)
|
||||
.field("callback", &"Fn() -> { ... }")
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
|
||||
impl io::Write for MemPool {
|
||||
fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
|
||||
io::Write::write(&mut self.inner.file, buf)
|
||||
}
|
||||
fn flush(&mut self) -> io::Result<()> {
|
||||
io::Write::flush(&mut self.inner.file)
|
||||
}
|
||||
}
|
||||
|
||||
impl io::Seek for MemPool {
|
||||
fn seek(&mut self, pos: io::SeekFrom) -> io::Result<u64> {
|
||||
io::Seek::seek(&mut self.inner.file, pos)
|
||||
}
|
||||
}
|
||||
|
||||
/// A wrapper handling an SHM memory pool backed by a shared memory file
|
||||
///
|
||||
/// This wrapper handles the creation of the shared memory file, its synchronization with the
|
||||
/// protocol, and the allocation of buffers within the pool.
|
||||
///
|
||||
/// AutoMemPool internally tracks the release of the buffers by the compositor. As such, creating a
|
||||
/// buffer that is not committed to a surface (and then never released by the server) would result
|
||||
/// in that memory being unavailable for the rest of the pool's lifetime.
|
||||
///
|
||||
/// AutoMemPool will also handle the destruction of buffers; do not call destroy() on the returned
|
||||
/// WlBuffer objects.
|
||||
///
|
||||
/// The default alignment of returned buffers is 16 bytes; this can be changed by using the
|
||||
/// explicit with_min_align constructor.
|
||||
#[derive(Debug)]
|
||||
pub struct AutoMemPool {
|
||||
inner: Inner,
|
||||
align: usize,
|
||||
free_list: Rc<RefCell<Vec<(usize, usize)>>>,
|
||||
}
|
||||
|
||||
impl AutoMemPool {
|
||||
/// Create a new memory pool associated with the given shm
|
||||
pub fn new(shm: Attached<wl_shm::WlShm>) -> io::Result<AutoMemPool> {
|
||||
Self::with_min_align(shm, 16)
|
||||
}
|
||||
|
||||
/// Create a new memory pool associated with the given shm.
|
||||
///
|
||||
/// All buffers will be aligned to at least the value of (align), which must be a power of two
|
||||
/// not greater than 4096.
|
||||
pub fn with_min_align(shm: Attached<wl_shm::WlShm>, align: usize) -> io::Result<AutoMemPool> {
|
||||
assert!(align.is_power_of_two());
|
||||
assert!(align <= 4096);
|
||||
let inner = Inner::new(shm)?;
|
||||
let free_list = Rc::new(RefCell::new(vec![(0, inner.len)]));
|
||||
Ok(AutoMemPool { inner, align, free_list })
|
||||
}
|
||||
|
||||
/// Resize the memory pool
|
||||
///
|
||||
/// This is normally done automatically, but can be used to avoid multiple resizes.
|
||||
pub fn resize(&mut self, new_size: usize) -> io::Result<()> {
|
||||
let old_size = self.inner.len;
|
||||
if old_size >= new_size {
|
||||
return Ok(());
|
||||
}
|
||||
self.inner.resize(new_size)?;
|
||||
// add the new memory to the freelist
|
||||
let mut free = self.free_list.borrow_mut();
|
||||
if let Some((off, len)) = free.last_mut() {
|
||||
if *off + *len == old_size {
|
||||
*len += new_size - old_size;
|
||||
return Ok(());
|
||||
}
|
||||
}
|
||||
free.push((old_size, new_size - old_size));
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn alloc(&mut self, size: usize) -> io::Result<usize> {
|
||||
let mut free = self.free_list.borrow_mut();
|
||||
for (offset, len) in free.iter_mut() {
|
||||
if *len >= size {
|
||||
let rv = *offset;
|
||||
*len -= size;
|
||||
*offset += size;
|
||||
return Ok(rv);
|
||||
}
|
||||
}
|
||||
let mut rv = self.inner.len;
|
||||
let mut pop_tail = false;
|
||||
if let Some((start, len)) = free.last() {
|
||||
if start + len == self.inner.len {
|
||||
rv -= len;
|
||||
pop_tail = true;
|
||||
}
|
||||
}
|
||||
// resize like Vec::reserve, always at least doubling
|
||||
let target = std::cmp::max(rv + size, self.inner.len * 2);
|
||||
self.inner.resize(target)?;
|
||||
// adjust the end of the freelist here
|
||||
if pop_tail {
|
||||
free.pop();
|
||||
}
|
||||
if target > rv + size {
|
||||
free.push((rv + size, target - rv - size));
|
||||
}
|
||||
Ok(rv)
|
||||
}
|
||||
|
||||
fn free(free_list: &RefCell<Vec<(usize, usize)>>, mut offset: usize, mut len: usize) {
|
||||
let mut free = free_list.borrow_mut();
|
||||
let mut nf = Vec::with_capacity(free.len() + 1);
|
||||
for &(ioff, ilen) in free.iter() {
|
||||
if ioff + ilen == offset {
|
||||
offset = ioff;
|
||||
len += ilen;
|
||||
continue;
|
||||
}
|
||||
if ioff == offset + len {
|
||||
len += ilen;
|
||||
continue;
|
||||
}
|
||||
if ioff > offset + len && len != 0 {
|
||||
nf.push((offset, len));
|
||||
len = 0;
|
||||
}
|
||||
if ilen != 0 {
|
||||
nf.push((ioff, ilen));
|
||||
}
|
||||
}
|
||||
if len != 0 {
|
||||
nf.push((offset, len));
|
||||
}
|
||||
*free = nf;
|
||||
}
|
||||
|
||||
/// Create a new buffer in this pool
|
||||
///
|
||||
/// The parameters are:
|
||||
///
|
||||
/// - `width`: the width of this buffer (in pixels)
|
||||
/// - `height`: the height of this buffer (in pixels)
|
||||
/// - `stride`: distance (in bytes) between the beginning of a row and the next one
|
||||
/// - `format`: the encoding format of the pixels. Using a format that was not
|
||||
/// advertised to the `wl_shm` global by the server is a protocol error and will
|
||||
/// terminate your connection
|
||||
pub fn buffer(
|
||||
&mut self,
|
||||
width: i32,
|
||||
height: i32,
|
||||
stride: i32,
|
||||
format: wl_shm::Format,
|
||||
) -> io::Result<(&mut [u8], wl_buffer::WlBuffer)> {
|
||||
let len = (height as usize) * (stride as usize);
|
||||
let alloc_len = (len + self.align - 1) & !(self.align - 1);
|
||||
let offset = self.alloc(alloc_len)?;
|
||||
let offset_i = offset as i32;
|
||||
let buffer = self.inner.pool.create_buffer(offset_i, width, height, stride, format);
|
||||
let free_list = self.free_list.clone();
|
||||
buffer.quick_assign(move |buffer, event, _| match event {
|
||||
wl_buffer::Event::Release => {
|
||||
buffer.destroy();
|
||||
Self::free(&free_list, offset, alloc_len);
|
||||
}
|
||||
_ => unreachable!(),
|
||||
});
|
||||
Ok((&mut self.inner.mmap[offset..][..len], buffer.detach()))
|
||||
}
|
||||
|
||||
/// Try drawing with the given closure
|
||||
///
|
||||
/// This is identical to buffer(), but will only actually create the WlBuffer if the draw
|
||||
/// closure succeeds. Otherwise, the buffer is freed immediately instead of waiting for a
|
||||
/// Release event that will never be sent if the WlBuffer is not used.
|
||||
pub fn try_draw<F, E>(
|
||||
&mut self,
|
||||
width: i32,
|
||||
height: i32,
|
||||
stride: i32,
|
||||
format: wl_shm::Format,
|
||||
draw: F,
|
||||
) -> Result<wl_buffer::WlBuffer, E>
|
||||
where
|
||||
F: FnOnce(&mut [u8]) -> Result<(), E>,
|
||||
E: From<io::Error>,
|
||||
{
|
||||
let len = (height as usize) * (stride as usize);
|
||||
let alloc_len = (len + self.align - 1) & !(self.align - 1);
|
||||
let offset = self.alloc(alloc_len)?;
|
||||
let offset_i = offset as i32;
|
||||
if let Err(e) = draw(&mut self.inner.mmap[offset..][..len]) {
|
||||
Self::free(&self.free_list, offset, alloc_len);
|
||||
return Err(e);
|
||||
}
|
||||
let buffer = self.inner.pool.create_buffer(offset_i, width, height, stride, format);
|
||||
let free_list = self.free_list.clone();
|
||||
buffer.quick_assign(move |buffer, event, _| match event {
|
||||
wl_buffer::Event::Release => {
|
||||
buffer.destroy();
|
||||
Self::free(&free_list, offset, alloc_len);
|
||||
}
|
||||
_ => unreachable!(),
|
||||
});
|
||||
Ok(buffer.detach())
|
||||
}
|
||||
}
|
||||
|
||||
fn create_shm_fd() -> io::Result<RawFd> {
|
||||
// Only try memfd on linux
|
||||
#[cfg(target_os = "linux")]
|
||||
loop {
|
||||
match memfd::memfd_create(
|
||||
CStr::from_bytes_with_nul(b"smithay-client-toolkit\0").unwrap(),
|
||||
memfd::MemFdCreateFlag::MFD_CLOEXEC | memfd::MemFdCreateFlag::MFD_ALLOW_SEALING,
|
||||
) {
|
||||
Ok(fd) => {
|
||||
// this is only an optimization, so ignore errors
|
||||
let _ = fcntl::fcntl(
|
||||
fd,
|
||||
fcntl::F_ADD_SEALS(
|
||||
fcntl::SealFlag::F_SEAL_SHRINK | fcntl::SealFlag::F_SEAL_SEAL,
|
||||
),
|
||||
);
|
||||
return Ok(fd);
|
||||
}
|
||||
Err(Errno::EINTR) => continue,
|
||||
Err(Errno::ENOSYS) => break,
|
||||
Err(errno) => return Err(errno.into()),
|
||||
}
|
||||
}
|
||||
|
||||
// Fallback to using shm_open
|
||||
let sys_time = SystemTime::now();
|
||||
let mut mem_file_handle = format!(
|
||||
"/smithay-client-toolkit-{}",
|
||||
sys_time.duration_since(UNIX_EPOCH).unwrap().subsec_nanos()
|
||||
);
|
||||
loop {
|
||||
match mman::shm_open(
|
||||
mem_file_handle.as_str(),
|
||||
fcntl::OFlag::O_CREAT
|
||||
| fcntl::OFlag::O_EXCL
|
||||
| fcntl::OFlag::O_RDWR
|
||||
| fcntl::OFlag::O_CLOEXEC,
|
||||
stat::Mode::S_IRUSR | stat::Mode::S_IWUSR,
|
||||
) {
|
||||
Ok(fd) => match mman::shm_unlink(mem_file_handle.as_str()) {
|
||||
Ok(_) => return Ok(fd),
|
||||
Err(errno) => match unistd::close(fd) {
|
||||
Ok(_) => return Err(errno.into()),
|
||||
Err(errno) => return Err(errno.into()),
|
||||
},
|
||||
},
|
||||
Err(Errno::EEXIST) => {
|
||||
// If a file with that handle exists then change the handle
|
||||
mem_file_handle = format!(
|
||||
"/smithay-client-toolkit-{}",
|
||||
sys_time.duration_since(UNIX_EPOCH).unwrap().subsec_nanos()
|
||||
);
|
||||
continue;
|
||||
}
|
||||
Err(Errno::EINTR) => continue,
|
||||
Err(errno) => return Err(errno.into()),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<E> crate::environment::Environment<E>
|
||||
where
|
||||
E: crate::environment::GlobalHandler<wl_shm::WlShm>,
|
||||
{
|
||||
/// Create a simple memory pool
|
||||
///
|
||||
/// This memory pool track the usage of the buffers created from it,
|
||||
/// and invokes your callback when the compositor has finished using
|
||||
/// all of them.
|
||||
pub fn create_simple_pool<F>(&self, callback: F) -> io::Result<MemPool>
|
||||
where
|
||||
F: FnMut(wayland_client::DispatchData) + 'static,
|
||||
{
|
||||
MemPool::new(self.require_global::<wl_shm::WlShm>(), callback)
|
||||
}
|
||||
|
||||
/// Create a double memory pool
|
||||
///
|
||||
/// This can be used for double-buffered drawing. The memory pool
|
||||
/// is backed by two different SHM segments, which are used in alternance.
|
||||
///
|
||||
/// The provided callback is triggered when one of the pools becomes unused again
|
||||
/// after you tried to draw while both where in use.
|
||||
pub fn create_double_pool<F>(&self, callback: F) -> io::Result<DoubleMemPool>
|
||||
where
|
||||
F: FnMut(wayland_client::DispatchData) + 'static,
|
||||
{
|
||||
DoubleMemPool::new(self.require_global::<wl_shm::WlShm>(), callback)
|
||||
}
|
||||
|
||||
/// Create an automatic memory pool
|
||||
///
|
||||
/// This pool will allocate more memory as needed in order to satisfy buffer requests, and will
|
||||
/// return memory to the pool when the compositor has finished using the memory.
|
||||
pub fn create_auto_pool(&self) -> io::Result<AutoMemPool> {
|
||||
AutoMemPool::new(self.require_global::<wl_shm::WlShm>())
|
||||
}
|
||||
}
|
||||
80
third-party/vendor/smithay-client-toolkit/src/shm/mod.rs
vendored
Normal file
80
third-party/vendor/smithay-client-toolkit/src/shm/mod.rs
vendored
Normal file
|
|
@ -0,0 +1,80 @@
|
|||
//! Various small utilities helping you to write clients
|
||||
|
||||
use std::{cell::RefCell, rc::Rc};
|
||||
|
||||
use wayland_client::{
|
||||
protocol::{wl_registry, wl_shm},
|
||||
Attached, DispatchData,
|
||||
};
|
||||
|
||||
mod mempool;
|
||||
|
||||
pub use self::mempool::{AutoMemPool, DoubleMemPool, MemPool};
|
||||
pub use wl_shm::Format;
|
||||
|
||||
/// A handler for the `wl_shm` global
|
||||
///
|
||||
/// This handler is automatically included in the
|
||||
/// [`default_environment!`](../macro.default_environment.html).
|
||||
#[derive(Debug)]
|
||||
pub struct ShmHandler {
|
||||
shm: Option<Attached<wl_shm::WlShm>>,
|
||||
formats: Rc<RefCell<Vec<wl_shm::Format>>>,
|
||||
}
|
||||
|
||||
impl ShmHandler {
|
||||
/// Create a new ShmHandler
|
||||
pub fn new() -> ShmHandler {
|
||||
ShmHandler { shm: None, formats: Rc::new(RefCell::new(vec![])) }
|
||||
}
|
||||
}
|
||||
|
||||
impl crate::environment::GlobalHandler<wl_shm::WlShm> for ShmHandler {
|
||||
fn created(
|
||||
&mut self,
|
||||
registry: Attached<wl_registry::WlRegistry>,
|
||||
id: u32,
|
||||
_version: u32,
|
||||
_: DispatchData,
|
||||
) {
|
||||
// only shm verison 1 is supported
|
||||
let shm = registry.bind::<wl_shm::WlShm>(1, id);
|
||||
let my_formats = self.formats.clone();
|
||||
shm.quick_assign(move |_, event, _| match event {
|
||||
wl_shm::Event::Format { format } => {
|
||||
my_formats.borrow_mut().push(format);
|
||||
}
|
||||
_ => unreachable!(),
|
||||
});
|
||||
self.shm = Some((*shm).clone());
|
||||
}
|
||||
fn get(&self) -> Option<Attached<wl_shm::WlShm>> {
|
||||
self.shm.clone()
|
||||
}
|
||||
}
|
||||
|
||||
/// An interface trait to forward the shm handler capability
|
||||
///
|
||||
/// You need to implement this trait for you environment struct, by
|
||||
/// delegating it to its `ShmHandler` field in order to get the
|
||||
/// associated methods on your [`Environment`](../environment/struct.environment.html).
|
||||
pub trait ShmHandling {
|
||||
/// Access the list of SHM formats supported by the compositor
|
||||
fn shm_formats(&self) -> Vec<wl_shm::Format>;
|
||||
}
|
||||
|
||||
impl ShmHandling for ShmHandler {
|
||||
fn shm_formats(&self) -> Vec<wl_shm::Format> {
|
||||
self.formats.borrow().clone()
|
||||
}
|
||||
}
|
||||
|
||||
impl<E> crate::environment::Environment<E>
|
||||
where
|
||||
E: ShmHandling,
|
||||
{
|
||||
/// Access the list of SHM formats supported by the compositor
|
||||
pub fn shm_formats(&self) -> Vec<wl_shm::Format> {
|
||||
self.with_inner(|inner| inner.shm_formats())
|
||||
}
|
||||
}
|
||||
188
third-party/vendor/smithay-client-toolkit/src/surface.rs
vendored
Normal file
188
third-party/vendor/smithay-client-toolkit/src/surface.rs
vendored
Normal file
|
|
@ -0,0 +1,188 @@
|
|||
use std::{cell::RefCell, rc::Rc, sync::Mutex};
|
||||
|
||||
use wayland_client::{
|
||||
protocol::{wl_compositor, wl_output, wl_surface},
|
||||
Attached, DispatchData, Main,
|
||||
};
|
||||
|
||||
use crate::output::{add_output_listener, with_output_info, OutputListener};
|
||||
|
||||
pub(crate) struct SurfaceUserData {
|
||||
scale_factor: i32,
|
||||
outputs: Vec<(wl_output::WlOutput, i32, OutputListener)>,
|
||||
}
|
||||
|
||||
impl SurfaceUserData {
|
||||
fn new() -> Self {
|
||||
SurfaceUserData { scale_factor: 1, outputs: Vec::new() }
|
||||
}
|
||||
|
||||
pub(crate) fn enter<F>(
|
||||
&mut self,
|
||||
output: wl_output::WlOutput,
|
||||
surface: wl_surface::WlSurface,
|
||||
callback: &Option<Rc<RefCell<F>>>,
|
||||
) where
|
||||
F: FnMut(i32, wl_surface::WlSurface, DispatchData) + 'static,
|
||||
{
|
||||
let output_scale = with_output_info(&output, |info| info.scale_factor).unwrap_or(1);
|
||||
let my_surface = surface.clone();
|
||||
// Use a UserData to safely share the callback with the other thread
|
||||
let my_callback = wayland_client::UserData::new();
|
||||
if let Some(ref cb) = callback {
|
||||
my_callback.set(|| cb.clone());
|
||||
}
|
||||
let listener = add_output_listener(&output, move |output, info, ddata| {
|
||||
let mut user_data = my_surface
|
||||
.as_ref()
|
||||
.user_data()
|
||||
.get::<Mutex<SurfaceUserData>>()
|
||||
.unwrap()
|
||||
.lock()
|
||||
.unwrap();
|
||||
// update the scale factor of the relevant output
|
||||
for (ref o, ref mut factor, _) in user_data.outputs.iter_mut() {
|
||||
if o.as_ref().equals(output.as_ref()) {
|
||||
if info.obsolete {
|
||||
// an output that no longer exists is marked by a scale factor of -1
|
||||
*factor = -1;
|
||||
} else {
|
||||
*factor = info.scale_factor;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
// recompute the scale factor with the new info
|
||||
let callback = my_callback.get::<Rc<RefCell<F>>>().cloned();
|
||||
let old_scale_factor = user_data.scale_factor;
|
||||
let new_scale_factor = user_data.recompute_scale_factor();
|
||||
drop(user_data);
|
||||
if let Some(ref cb) = callback {
|
||||
if old_scale_factor != new_scale_factor {
|
||||
(cb.borrow_mut())(new_scale_factor, surface.clone(), ddata);
|
||||
}
|
||||
}
|
||||
});
|
||||
self.outputs.push((output, output_scale, listener));
|
||||
}
|
||||
|
||||
pub(crate) fn leave(&mut self, output: &wl_output::WlOutput) {
|
||||
self.outputs.retain(|(ref output2, _, _)| !output.as_ref().equals(output2.as_ref()));
|
||||
}
|
||||
|
||||
fn recompute_scale_factor(&mut self) -> i32 {
|
||||
let mut new_scale_factor = 1;
|
||||
self.outputs.retain(|&(_, output_scale, _)| {
|
||||
if output_scale > 0 {
|
||||
new_scale_factor = ::std::cmp::max(new_scale_factor, output_scale);
|
||||
true
|
||||
} else {
|
||||
// cleanup obsolete output
|
||||
false
|
||||
}
|
||||
});
|
||||
if self.outputs.is_empty() {
|
||||
// don't update the scale factor if we are not displayed on any output
|
||||
return self.scale_factor;
|
||||
}
|
||||
self.scale_factor = new_scale_factor;
|
||||
new_scale_factor
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn setup_surface<F>(
|
||||
surface: Main<wl_surface::WlSurface>,
|
||||
callback: Option<F>,
|
||||
) -> Attached<wl_surface::WlSurface>
|
||||
where
|
||||
F: FnMut(i32, wl_surface::WlSurface, DispatchData) + 'static,
|
||||
{
|
||||
let callback = callback.map(|c| Rc::new(RefCell::new(c)));
|
||||
surface.quick_assign(move |surface, event, ddata| {
|
||||
let mut user_data =
|
||||
surface.as_ref().user_data().get::<Mutex<SurfaceUserData>>().unwrap().lock().unwrap();
|
||||
match event {
|
||||
wl_surface::Event::Enter { output } => {
|
||||
// Passing the callback to be added to output listener
|
||||
user_data.enter(output, surface.detach(), &callback);
|
||||
}
|
||||
wl_surface::Event::Leave { output } => {
|
||||
user_data.leave(&output);
|
||||
}
|
||||
_ => unreachable!(),
|
||||
};
|
||||
let old_scale_factor = user_data.scale_factor;
|
||||
let new_scale_factor = user_data.recompute_scale_factor();
|
||||
drop(user_data);
|
||||
if let Some(ref cb) = callback {
|
||||
if old_scale_factor != new_scale_factor {
|
||||
(cb.borrow_mut())(new_scale_factor, surface.detach(), ddata);
|
||||
}
|
||||
}
|
||||
});
|
||||
surface.as_ref().user_data().set_threadsafe(|| Mutex::new(SurfaceUserData::new()));
|
||||
surface.into()
|
||||
}
|
||||
|
||||
impl<E: crate::environment::GlobalHandler<wl_compositor::WlCompositor>>
|
||||
crate::environment::Environment<E>
|
||||
{
|
||||
/// Create a DPI-aware surface
|
||||
///
|
||||
/// This surface will track the outputs it is being displayed on, and compute the
|
||||
/// optimal scale factor for these. You can access them using
|
||||
/// [`get_surface_scale_factor`](../fn.get_surface_scale_factor.html) and
|
||||
/// [`get_surface_outputs`](../fn.get_surface_outputs.html).
|
||||
pub fn create_surface(&self) -> Attached<wl_surface::WlSurface> {
|
||||
let compositor = self.require_global::<wl_compositor::WlCompositor>();
|
||||
setup_surface(compositor.create_surface(), None::<fn(_, _, DispatchData)>)
|
||||
}
|
||||
|
||||
/// Create a DPI-aware surface with callbacks
|
||||
///
|
||||
/// This method is like `create_surface`, but the provided callback will also be
|
||||
/// notified whenever the scale factor of this surface change, if you don't want to have to
|
||||
/// periodically check it.
|
||||
pub fn create_surface_with_scale_callback<
|
||||
F: FnMut(i32, wl_surface::WlSurface, DispatchData) + 'static,
|
||||
>(
|
||||
&self,
|
||||
f: F,
|
||||
) -> Attached<wl_surface::WlSurface> {
|
||||
let compositor = self.require_global::<wl_compositor::WlCompositor>();
|
||||
setup_surface(compositor.create_surface(), Some(f))
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the current suggested scale factor of a surface.
|
||||
///
|
||||
/// Panics if the surface was not created using `Environment::create_surface` or
|
||||
/// `Environment::create_surface_with_dpi_callback`.
|
||||
pub fn get_surface_scale_factor(surface: &wl_surface::WlSurface) -> i32 {
|
||||
surface
|
||||
.as_ref()
|
||||
.user_data()
|
||||
.get::<Mutex<SurfaceUserData>>()
|
||||
.expect("SCTK: Surface was not created by SCTK.")
|
||||
.lock()
|
||||
.unwrap()
|
||||
.scale_factor
|
||||
}
|
||||
|
||||
/// Returns a list of outputs the surface is displayed on.
|
||||
///
|
||||
/// Panics if the surface was not created using `Environment::create_surface` or
|
||||
/// `Environment::create_surface_with_dpi_callback`.
|
||||
pub fn get_surface_outputs(surface: &wl_surface::WlSurface) -> Vec<wl_output::WlOutput> {
|
||||
surface
|
||||
.as_ref()
|
||||
.user_data()
|
||||
.get::<Mutex<SurfaceUserData>>()
|
||||
.expect("SCTK: Surface was not created by SCTK.")
|
||||
.lock()
|
||||
.unwrap()
|
||||
.outputs
|
||||
.iter()
|
||||
.map(|(ref output, _, _)| output.clone())
|
||||
.collect()
|
||||
}
|
||||
972
third-party/vendor/smithay-client-toolkit/src/window/fallback_frame.rs
vendored
Normal file
972
third-party/vendor/smithay-client-toolkit/src/window/fallback_frame.rs
vendored
Normal file
|
|
@ -0,0 +1,972 @@
|
|||
use std::cell::RefCell;
|
||||
use std::fmt;
|
||||
use std::rc::Rc;
|
||||
|
||||
use wayland_client::protocol::{
|
||||
wl_compositor, wl_pointer, wl_seat, wl_shm, wl_subcompositor, wl_subsurface, wl_surface,
|
||||
};
|
||||
use wayland_client::{Attached, DispatchData};
|
||||
|
||||
use log::error;
|
||||
|
||||
use super::{ButtonState, Frame, FrameRequest, State, WindowState};
|
||||
use crate::seat::pointer::{ThemeManager, ThemeSpec, ThemedPointer};
|
||||
use crate::shm::AutoMemPool;
|
||||
|
||||
/*
|
||||
* Drawing theme definitions
|
||||
*/
|
||||
|
||||
const BORDER_SIZE: u32 = 4;
|
||||
const HEADER_SIZE: u32 = 24;
|
||||
|
||||
const BTN_ICON_COLOR: u32 = 0xFF1E1E1E;
|
||||
const BTN_HOVER_BG: u32 = 0xFFA8A8A8;
|
||||
|
||||
const PRIMARY_COLOR_ACTIVE: u32 = 0xFFE6E6E6;
|
||||
const PRIMARY_COLOR_INACTIVE: u32 = 0xFFDCDCDC;
|
||||
|
||||
/*
|
||||
* Utilities
|
||||
*/
|
||||
|
||||
const HEAD: usize = 0;
|
||||
const TOP: usize = 1;
|
||||
const BOTTOM: usize = 2;
|
||||
const LEFT: usize = 3;
|
||||
const RIGHT: usize = 4;
|
||||
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
|
||||
enum Location {
|
||||
None,
|
||||
Head,
|
||||
Top,
|
||||
TopRight,
|
||||
Right,
|
||||
BottomRight,
|
||||
Bottom,
|
||||
BottomLeft,
|
||||
Left,
|
||||
TopLeft,
|
||||
Button(UIButton),
|
||||
}
|
||||
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
|
||||
enum UIButton {
|
||||
Minimize,
|
||||
Maximize,
|
||||
Close,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
struct Part {
|
||||
surface: wl_surface::WlSurface,
|
||||
subsurface: wl_subsurface::WlSubsurface,
|
||||
}
|
||||
|
||||
impl Part {
|
||||
fn new(
|
||||
parent: &wl_surface::WlSurface,
|
||||
compositor: &Attached<wl_compositor::WlCompositor>,
|
||||
subcompositor: &Attached<wl_subcompositor::WlSubcompositor>,
|
||||
inner: Option<Rc<RefCell<Inner>>>,
|
||||
) -> Part {
|
||||
let surface = if let Some(inner) = inner {
|
||||
crate::surface::setup_surface(
|
||||
compositor.create_surface(),
|
||||
Some(move |dpi, surface: wl_surface::WlSurface, ddata: DispatchData| {
|
||||
surface.set_buffer_scale(dpi);
|
||||
surface.commit();
|
||||
(inner.borrow_mut().implem)(FrameRequest::Refresh, 0, ddata);
|
||||
}),
|
||||
)
|
||||
} else {
|
||||
crate::surface::setup_surface(
|
||||
compositor.create_surface(),
|
||||
Some(move |dpi, surface: wl_surface::WlSurface, _ddata: DispatchData| {
|
||||
surface.set_buffer_scale(dpi);
|
||||
surface.commit();
|
||||
}),
|
||||
)
|
||||
};
|
||||
|
||||
let surface = surface.detach();
|
||||
|
||||
let subsurface = subcompositor.get_subsurface(&surface, parent);
|
||||
|
||||
Part { surface, subsurface: subsurface.detach() }
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for Part {
|
||||
fn drop(&mut self) {
|
||||
self.subsurface.destroy();
|
||||
self.surface.destroy();
|
||||
}
|
||||
}
|
||||
|
||||
struct PointerUserData {
|
||||
location: Location,
|
||||
position: (f64, f64),
|
||||
seat: wl_seat::WlSeat,
|
||||
}
|
||||
|
||||
/*
|
||||
* The core frame
|
||||
*/
|
||||
|
||||
struct Inner {
|
||||
parts: Vec<Part>,
|
||||
size: (u32, u32),
|
||||
resizable: bool,
|
||||
theme_over_surface: bool,
|
||||
implem: Box<dyn FnMut(FrameRequest, u32, DispatchData)>,
|
||||
maximized: bool,
|
||||
fullscreened: bool,
|
||||
}
|
||||
|
||||
impl Inner {
|
||||
fn find_surface(&self, surface: &wl_surface::WlSurface) -> Location {
|
||||
if self.parts.is_empty() {
|
||||
return Location::None;
|
||||
}
|
||||
|
||||
if surface.as_ref().equals(self.parts[HEAD].surface.as_ref()) {
|
||||
Location::Head
|
||||
} else if surface.as_ref().equals(self.parts[TOP].surface.as_ref()) {
|
||||
Location::Top
|
||||
} else if surface.as_ref().equals(self.parts[BOTTOM].surface.as_ref()) {
|
||||
Location::Bottom
|
||||
} else if surface.as_ref().equals(self.parts[LEFT].surface.as_ref()) {
|
||||
Location::Left
|
||||
} else if surface.as_ref().equals(self.parts[RIGHT].surface.as_ref()) {
|
||||
Location::Right
|
||||
} else {
|
||||
Location::None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl fmt::Debug for Inner {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
f.debug_struct("Inner")
|
||||
.field("parts", &self.parts)
|
||||
.field("size", &self.size)
|
||||
.field("resizable", &self.resizable)
|
||||
.field("theme_over_surface", &self.theme_over_surface)
|
||||
.field("implem", &"FnMut(FrameRequest, u32, DispatchData) -> { ... }")
|
||||
.field("maximized", &self.maximized)
|
||||
.field("fullscreened", &self.fullscreened)
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
|
||||
fn precise_location(old: Location, width: u32, x: f64, y: f64) -> Location {
|
||||
match old {
|
||||
Location::Head | Location::Button(_) => find_button(x, y, width),
|
||||
|
||||
Location::Top | Location::TopLeft | Location::TopRight => {
|
||||
if x <= f64::from(BORDER_SIZE) {
|
||||
Location::TopLeft
|
||||
} else if x >= f64::from(width + BORDER_SIZE) {
|
||||
Location::TopRight
|
||||
} else {
|
||||
Location::Top
|
||||
}
|
||||
}
|
||||
|
||||
Location::Bottom | Location::BottomLeft | Location::BottomRight => {
|
||||
if x <= f64::from(BORDER_SIZE) {
|
||||
Location::BottomLeft
|
||||
} else if x >= f64::from(width + BORDER_SIZE) {
|
||||
Location::BottomRight
|
||||
} else {
|
||||
Location::Bottom
|
||||
}
|
||||
}
|
||||
|
||||
other => other,
|
||||
}
|
||||
}
|
||||
|
||||
fn find_button(x: f64, y: f64, w: u32) -> Location {
|
||||
if (w >= HEADER_SIZE)
|
||||
&& (x >= f64::from(w - HEADER_SIZE))
|
||||
&& (x <= f64::from(w))
|
||||
&& (y <= f64::from(HEADER_SIZE))
|
||||
&& (y >= f64::from(0))
|
||||
{
|
||||
// first button
|
||||
Location::Button(UIButton::Close)
|
||||
} else if (w >= 2 * HEADER_SIZE)
|
||||
&& (x >= f64::from(w - 2 * HEADER_SIZE))
|
||||
&& (x <= f64::from(w - HEADER_SIZE))
|
||||
&& (y <= f64::from(HEADER_SIZE))
|
||||
&& (y >= f64::from(0))
|
||||
{
|
||||
// second button
|
||||
Location::Button(UIButton::Maximize)
|
||||
} else if (w >= 3 * HEADER_SIZE)
|
||||
&& (x >= f64::from(w - 3 * HEADER_SIZE))
|
||||
&& (x <= f64::from(w - 2 * HEADER_SIZE))
|
||||
&& (y <= f64::from(HEADER_SIZE))
|
||||
&& (y >= f64::from(0))
|
||||
{
|
||||
// third button
|
||||
Location::Button(UIButton::Minimize)
|
||||
} else {
|
||||
Location::Head
|
||||
}
|
||||
}
|
||||
|
||||
/// A simple set of decorations that can be used as a fallback
|
||||
///
|
||||
/// This class drawn some simple and minimalistic decorations around
|
||||
/// a window so that it remains possible to interact with the window
|
||||
/// even when server-side decorations are not available.
|
||||
///
|
||||
/// `FallbackFrame` is hiding its `ClientSide` decorations
|
||||
/// in a `Fullscreen` state and brings them back if those are
|
||||
/// visible when unsetting `Fullscreen` state.
|
||||
#[derive(Debug)]
|
||||
pub struct FallbackFrame {
|
||||
base_surface: wl_surface::WlSurface,
|
||||
compositor: Attached<wl_compositor::WlCompositor>,
|
||||
subcompositor: Attached<wl_subcompositor::WlSubcompositor>,
|
||||
inner: Rc<RefCell<Inner>>,
|
||||
pool: AutoMemPool,
|
||||
active: WindowState,
|
||||
hidden: bool,
|
||||
pointers: Vec<ThemedPointer>,
|
||||
themer: ThemeManager,
|
||||
surface_version: u32,
|
||||
}
|
||||
|
||||
impl Frame for FallbackFrame {
|
||||
type Error = ::std::io::Error;
|
||||
type Config = ();
|
||||
fn init(
|
||||
base_surface: &wl_surface::WlSurface,
|
||||
compositor: &Attached<wl_compositor::WlCompositor>,
|
||||
subcompositor: &Attached<wl_subcompositor::WlSubcompositor>,
|
||||
shm: &Attached<wl_shm::WlShm>,
|
||||
theme_manager: Option<ThemeManager>,
|
||||
implementation: Box<dyn FnMut(FrameRequest, u32, DispatchData)>,
|
||||
) -> Result<FallbackFrame, ::std::io::Error> {
|
||||
let (themer, theme_over_surface) = if let Some(theme_manager) = theme_manager {
|
||||
(theme_manager, false)
|
||||
} else {
|
||||
(ThemeManager::init(ThemeSpec::System, compositor.clone(), shm.clone()), true)
|
||||
};
|
||||
|
||||
let inner = Rc::new(RefCell::new(Inner {
|
||||
parts: vec![],
|
||||
size: (1, 1),
|
||||
resizable: true,
|
||||
implem: implementation,
|
||||
theme_over_surface,
|
||||
maximized: false,
|
||||
fullscreened: false,
|
||||
}));
|
||||
|
||||
let pool = AutoMemPool::new(shm.clone())?;
|
||||
|
||||
Ok(FallbackFrame {
|
||||
base_surface: base_surface.clone(),
|
||||
compositor: compositor.clone(),
|
||||
subcompositor: subcompositor.clone(),
|
||||
inner,
|
||||
pool,
|
||||
active: WindowState::Inactive,
|
||||
hidden: true,
|
||||
pointers: Vec::new(),
|
||||
themer,
|
||||
surface_version: compositor.as_ref().version(),
|
||||
})
|
||||
}
|
||||
|
||||
fn new_seat(&mut self, seat: &Attached<wl_seat::WlSeat>) {
|
||||
use self::wl_pointer::Event;
|
||||
let inner = self.inner.clone();
|
||||
let pointer = self.themer.theme_pointer_with_impl(
|
||||
seat,
|
||||
move |event, pointer: ThemedPointer, ddata: DispatchData| {
|
||||
let data: &RefCell<PointerUserData> = pointer.as_ref().user_data().get().unwrap();
|
||||
let mut data = data.borrow_mut();
|
||||
let mut inner = inner.borrow_mut();
|
||||
match event {
|
||||
Event::Enter { serial, surface, surface_x, surface_y } => {
|
||||
data.location = precise_location(
|
||||
inner.find_surface(&surface),
|
||||
inner.size.0,
|
||||
surface_x,
|
||||
surface_y,
|
||||
);
|
||||
data.position = (surface_x, surface_y);
|
||||
change_pointer(&pointer, &inner, data.location, Some(serial))
|
||||
}
|
||||
Event::Leave { serial, .. } => {
|
||||
data.location = Location::None;
|
||||
change_pointer(&pointer, &inner, data.location, Some(serial));
|
||||
(inner.implem)(FrameRequest::Refresh, 0, ddata);
|
||||
}
|
||||
Event::Motion { surface_x, surface_y, .. } => {
|
||||
data.position = (surface_x, surface_y);
|
||||
let newpos =
|
||||
precise_location(data.location, inner.size.0, surface_x, surface_y);
|
||||
if newpos != data.location {
|
||||
match (newpos, data.location) {
|
||||
(Location::Button(_), _) | (_, Location::Button(_)) => {
|
||||
// pointer movement involves a button, request refresh
|
||||
(inner.implem)(FrameRequest::Refresh, 0, ddata);
|
||||
}
|
||||
_ => (),
|
||||
}
|
||||
// we changed of part of the decoration, pointer image
|
||||
// may need to be changed
|
||||
data.location = newpos;
|
||||
change_pointer(&pointer, &inner, data.location, None)
|
||||
}
|
||||
}
|
||||
Event::Button { serial, button, state, .. } => {
|
||||
if state == wl_pointer::ButtonState::Pressed {
|
||||
let request = match button {
|
||||
// Left mouse button.
|
||||
0x110 => request_for_location_on_lmb(
|
||||
&data,
|
||||
inner.maximized,
|
||||
inner.resizable,
|
||||
),
|
||||
// Right mouse button.
|
||||
0x111 => request_for_location_on_rmb(&data),
|
||||
_ => None,
|
||||
};
|
||||
|
||||
if let Some(request) = request {
|
||||
(inner.implem)(request, serial, ddata);
|
||||
}
|
||||
}
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
},
|
||||
);
|
||||
pointer.as_ref().user_data().set(|| {
|
||||
RefCell::new(PointerUserData {
|
||||
location: Location::None,
|
||||
position: (0.0, 0.0),
|
||||
seat: seat.detach(),
|
||||
})
|
||||
});
|
||||
self.pointers.push(pointer);
|
||||
}
|
||||
|
||||
fn remove_seat(&mut self, seat: &wl_seat::WlSeat) {
|
||||
self.pointers.retain(|pointer| {
|
||||
let user_data = pointer.as_ref().user_data().get::<RefCell<PointerUserData>>().unwrap();
|
||||
let guard = user_data.borrow_mut();
|
||||
if &guard.seat == seat {
|
||||
pointer.release();
|
||||
false
|
||||
} else {
|
||||
true
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
fn set_states(&mut self, states: &[State]) -> bool {
|
||||
let mut inner = self.inner.borrow_mut();
|
||||
let mut need_redraw = false;
|
||||
|
||||
// Process active.
|
||||
let new_active = if states.contains(&State::Activated) {
|
||||
WindowState::Active
|
||||
} else {
|
||||
WindowState::Inactive
|
||||
};
|
||||
need_redraw |= new_active != self.active;
|
||||
self.active = new_active;
|
||||
|
||||
// Process maximized.
|
||||
let new_maximized = states.contains(&State::Maximized);
|
||||
need_redraw |= new_maximized != inner.maximized;
|
||||
inner.maximized = new_maximized;
|
||||
|
||||
// Process fullscreened.
|
||||
let new_fullscreened = states.contains(&State::Fullscreen);
|
||||
need_redraw |= new_fullscreened != inner.fullscreened;
|
||||
inner.fullscreened = new_fullscreened;
|
||||
|
||||
need_redraw
|
||||
}
|
||||
|
||||
fn set_hidden(&mut self, hidden: bool) {
|
||||
self.hidden = hidden;
|
||||
let mut inner = self.inner.borrow_mut();
|
||||
if !self.hidden {
|
||||
if inner.parts.is_empty() {
|
||||
inner.parts = vec![
|
||||
Part::new(
|
||||
&self.base_surface,
|
||||
&self.compositor,
|
||||
&self.subcompositor,
|
||||
Some(Rc::clone(&self.inner)),
|
||||
),
|
||||
Part::new(&self.base_surface, &self.compositor, &self.subcompositor, None),
|
||||
Part::new(&self.base_surface, &self.compositor, &self.subcompositor, None),
|
||||
Part::new(&self.base_surface, &self.compositor, &self.subcompositor, None),
|
||||
Part::new(&self.base_surface, &self.compositor, &self.subcompositor, None),
|
||||
];
|
||||
}
|
||||
} else {
|
||||
inner.parts.clear();
|
||||
}
|
||||
}
|
||||
|
||||
fn set_resizable(&mut self, resizable: bool) {
|
||||
self.inner.borrow_mut().resizable = resizable;
|
||||
}
|
||||
|
||||
fn resize(&mut self, newsize: (u32, u32)) {
|
||||
self.inner.borrow_mut().size = newsize;
|
||||
}
|
||||
|
||||
fn redraw(&mut self) {
|
||||
let inner = self.inner.borrow_mut();
|
||||
|
||||
// Don't draw borders if the frame explicitly hidden or fullscreened.
|
||||
if self.hidden || inner.fullscreened {
|
||||
// Don't draw the borders.
|
||||
for p in inner.parts.iter() {
|
||||
p.surface.attach(None, 0, 0);
|
||||
p.surface.commit();
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
// `parts` can't be empty here, since the initial state for `self.hidden` is true, and
|
||||
// they will be created once `self.hidden` will become `false`.
|
||||
let parts = &inner.parts;
|
||||
|
||||
let scales: Vec<u32> = parts
|
||||
.iter()
|
||||
.map(|part| crate::surface::get_surface_scale_factor(&part.surface) as u32)
|
||||
.collect();
|
||||
|
||||
let (width, height) = inner.size;
|
||||
|
||||
// Use header scale for all the thing.
|
||||
let header_scale = scales[HEAD];
|
||||
|
||||
let scaled_header_height = HEADER_SIZE * header_scale;
|
||||
let scaled_header_width = width * header_scale;
|
||||
|
||||
{
|
||||
// Create the buffers and draw
|
||||
let color = if self.active == WindowState::Active {
|
||||
PRIMARY_COLOR_ACTIVE.to_ne_bytes()
|
||||
} else {
|
||||
PRIMARY_COLOR_INACTIVE.to_ne_bytes()
|
||||
};
|
||||
|
||||
// -> head-subsurface
|
||||
if let Ok((canvas, buffer)) = self.pool.buffer(
|
||||
scaled_header_width as i32,
|
||||
scaled_header_height as i32,
|
||||
4 * scaled_header_width as i32,
|
||||
wl_shm::Format::Argb8888,
|
||||
) {
|
||||
for pixel in canvas.chunks_exact_mut(4) {
|
||||
pixel[0] = color[0];
|
||||
pixel[1] = color[1];
|
||||
pixel[2] = color[2];
|
||||
pixel[3] = color[3];
|
||||
}
|
||||
|
||||
draw_buttons(
|
||||
canvas,
|
||||
width,
|
||||
header_scale,
|
||||
inner.resizable,
|
||||
self.active,
|
||||
&self
|
||||
.pointers
|
||||
.iter()
|
||||
.flat_map(|p| {
|
||||
if p.as_ref().is_alive() {
|
||||
let data: &RefCell<PointerUserData> =
|
||||
p.as_ref().user_data().get().unwrap();
|
||||
Some(data.borrow().location)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
})
|
||||
.collect::<Vec<Location>>(),
|
||||
);
|
||||
|
||||
parts[HEAD].subsurface.set_position(0, -(HEADER_SIZE as i32));
|
||||
parts[HEAD].surface.attach(Some(&buffer), 0, 0);
|
||||
if self.surface_version >= 4 {
|
||||
parts[HEAD].surface.damage_buffer(
|
||||
0,
|
||||
0,
|
||||
scaled_header_width as i32,
|
||||
scaled_header_height as i32,
|
||||
);
|
||||
} else {
|
||||
// surface is old and does not support damage_buffer, so we damage
|
||||
// in surface coordinates and hope it is not rescaled
|
||||
parts[HEAD].surface.damage(0, 0, width as i32, HEADER_SIZE as i32);
|
||||
}
|
||||
parts[HEAD].surface.commit();
|
||||
}
|
||||
|
||||
// -> top-subsurface
|
||||
if let Ok((canvas, buffer)) = self.pool.buffer(
|
||||
((width + 2 * BORDER_SIZE) * scales[TOP]) as i32,
|
||||
(BORDER_SIZE * scales[TOP]) as i32,
|
||||
(4 * scales[TOP] * (width + 2 * BORDER_SIZE)) as i32,
|
||||
wl_shm::Format::Argb8888,
|
||||
) {
|
||||
for pixel in canvas.chunks_exact_mut(4) {
|
||||
pixel[0] = color[0];
|
||||
pixel[1] = color[1];
|
||||
pixel[2] = color[2];
|
||||
pixel[3] = color[3];
|
||||
}
|
||||
parts[TOP].subsurface.set_position(
|
||||
-(BORDER_SIZE as i32),
|
||||
-(HEADER_SIZE as i32 + BORDER_SIZE as i32),
|
||||
);
|
||||
parts[TOP].surface.attach(Some(&buffer), 0, 0);
|
||||
if self.surface_version >= 4 {
|
||||
parts[TOP].surface.damage_buffer(
|
||||
0,
|
||||
0,
|
||||
((width + 2 * BORDER_SIZE) * scales[TOP]) as i32,
|
||||
(BORDER_SIZE * scales[TOP]) as i32,
|
||||
);
|
||||
} else {
|
||||
// surface is old and does not support damage_buffer, so we damage
|
||||
// in surface coordinates and hope it is not rescaled
|
||||
parts[TOP].surface.damage(
|
||||
0,
|
||||
0,
|
||||
(width + 2 * BORDER_SIZE) as i32,
|
||||
BORDER_SIZE as i32,
|
||||
);
|
||||
}
|
||||
parts[TOP].surface.commit();
|
||||
}
|
||||
|
||||
// -> bottom-subsurface
|
||||
if let Ok((canvas, buffer)) = self.pool.buffer(
|
||||
((width + 2 * BORDER_SIZE) * scales[BOTTOM]) as i32,
|
||||
(BORDER_SIZE * scales[BOTTOM]) as i32,
|
||||
(4 * scales[BOTTOM] * (width + 2 * BORDER_SIZE)) as i32,
|
||||
wl_shm::Format::Argb8888,
|
||||
) {
|
||||
for pixel in canvas.chunks_exact_mut(4) {
|
||||
pixel[0] = color[0];
|
||||
pixel[1] = color[1];
|
||||
pixel[2] = color[2];
|
||||
pixel[3] = color[3];
|
||||
}
|
||||
parts[BOTTOM].subsurface.set_position(-(BORDER_SIZE as i32), height as i32);
|
||||
parts[BOTTOM].surface.attach(Some(&buffer), 0, 0);
|
||||
if self.surface_version >= 4 {
|
||||
parts[BOTTOM].surface.damage_buffer(
|
||||
0,
|
||||
0,
|
||||
((width + 2 * BORDER_SIZE) * scales[BOTTOM]) as i32,
|
||||
(BORDER_SIZE * scales[BOTTOM]) as i32,
|
||||
);
|
||||
} else {
|
||||
// surface is old and does not support damage_buffer, so we damage
|
||||
// in surface coordinates and hope it is not rescaled
|
||||
parts[BOTTOM].surface.damage(
|
||||
0,
|
||||
0,
|
||||
(width + 2 * BORDER_SIZE) as i32,
|
||||
BORDER_SIZE as i32,
|
||||
);
|
||||
}
|
||||
parts[BOTTOM].surface.commit();
|
||||
}
|
||||
|
||||
// -> left-subsurface
|
||||
if let Ok((canvas, buffer)) = self.pool.buffer(
|
||||
(BORDER_SIZE * scales[LEFT]) as i32,
|
||||
((height + HEADER_SIZE) * scales[LEFT]) as i32,
|
||||
4 * (BORDER_SIZE * scales[LEFT]) as i32,
|
||||
wl_shm::Format::Argb8888,
|
||||
) {
|
||||
for pixel in canvas.chunks_exact_mut(4) {
|
||||
pixel[0] = color[0];
|
||||
pixel[1] = color[1];
|
||||
pixel[2] = color[2];
|
||||
pixel[3] = color[3];
|
||||
}
|
||||
parts[LEFT].subsurface.set_position(-(BORDER_SIZE as i32), -(HEADER_SIZE as i32));
|
||||
parts[LEFT].surface.attach(Some(&buffer), 0, 0);
|
||||
if self.surface_version >= 4 {
|
||||
parts[LEFT].surface.damage_buffer(
|
||||
0,
|
||||
0,
|
||||
(BORDER_SIZE * scales[LEFT]) as i32,
|
||||
((height + HEADER_SIZE) * scales[LEFT]) as i32,
|
||||
);
|
||||
} else {
|
||||
// surface is old and does not support damage_buffer, so we damage
|
||||
// in surface coordinates and hope it is not rescaled
|
||||
parts[LEFT].surface.damage(
|
||||
0,
|
||||
0,
|
||||
BORDER_SIZE as i32,
|
||||
(height + HEADER_SIZE) as i32,
|
||||
);
|
||||
}
|
||||
parts[LEFT].surface.commit();
|
||||
}
|
||||
|
||||
// -> right-subsurface
|
||||
if let Ok((canvas, buffer)) = self.pool.buffer(
|
||||
(BORDER_SIZE * scales[RIGHT]) as i32,
|
||||
((height + HEADER_SIZE) * scales[RIGHT]) as i32,
|
||||
4 * (BORDER_SIZE * scales[RIGHT]) as i32,
|
||||
wl_shm::Format::Argb8888,
|
||||
) {
|
||||
for pixel in canvas.chunks_exact_mut(4) {
|
||||
pixel[0] = color[0];
|
||||
pixel[1] = color[1];
|
||||
pixel[2] = color[2];
|
||||
pixel[3] = color[3];
|
||||
}
|
||||
parts[RIGHT].subsurface.set_position(width as i32, -(HEADER_SIZE as i32));
|
||||
parts[RIGHT].surface.attach(Some(&buffer), 0, 0);
|
||||
if self.surface_version >= 4 {
|
||||
parts[RIGHT].surface.damage_buffer(
|
||||
0,
|
||||
0,
|
||||
(BORDER_SIZE * scales[RIGHT]) as i32,
|
||||
((height + HEADER_SIZE) * scales[RIGHT]) as i32,
|
||||
);
|
||||
} else {
|
||||
// surface is old and does not support damage_buffer, so we damage
|
||||
// in surface coordinates and hope it is not rescaled
|
||||
parts[RIGHT].surface.damage(
|
||||
0,
|
||||
0,
|
||||
BORDER_SIZE as i32,
|
||||
(height + HEADER_SIZE) as i32,
|
||||
);
|
||||
}
|
||||
parts[RIGHT].surface.commit();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn subtract_borders(&self, width: i32, height: i32) -> (i32, i32) {
|
||||
if self.hidden || self.inner.borrow().fullscreened {
|
||||
(width, height)
|
||||
} else {
|
||||
(width - 2 * BORDER_SIZE as i32, height - HEADER_SIZE as i32 - 2 * BORDER_SIZE as i32)
|
||||
}
|
||||
}
|
||||
|
||||
fn add_borders(&self, width: i32, height: i32) -> (i32, i32) {
|
||||
if self.hidden || self.inner.borrow().fullscreened {
|
||||
(width, height)
|
||||
} else {
|
||||
(width + 2 * BORDER_SIZE as i32, height + HEADER_SIZE as i32 + 2 * BORDER_SIZE as i32)
|
||||
}
|
||||
}
|
||||
|
||||
fn location(&self) -> (i32, i32) {
|
||||
if self.hidden || self.inner.borrow().fullscreened {
|
||||
(0, 0)
|
||||
} else {
|
||||
(-(BORDER_SIZE as i32), -(HEADER_SIZE as i32 + BORDER_SIZE as i32))
|
||||
}
|
||||
}
|
||||
|
||||
fn set_config(&mut self, _config: ()) {}
|
||||
|
||||
fn set_title(&mut self, _title: String) {}
|
||||
}
|
||||
|
||||
impl Drop for FallbackFrame {
|
||||
fn drop(&mut self) {
|
||||
for ptr in self.pointers.drain(..) {
|
||||
if ptr.as_ref().version() >= 3 {
|
||||
ptr.release();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn change_pointer(pointer: &ThemedPointer, inner: &Inner, location: Location, serial: Option<u32>) {
|
||||
// Prevent theming of the surface if it was requested.
|
||||
if !inner.theme_over_surface && location == Location::None {
|
||||
return;
|
||||
}
|
||||
|
||||
let name = match location {
|
||||
// If we can't resize a frame we shouldn't show resize cursors.
|
||||
_ if !inner.resizable => "left_ptr",
|
||||
Location::Top => "top_side",
|
||||
Location::TopRight => "top_right_corner",
|
||||
Location::Right => "right_side",
|
||||
Location::BottomRight => "bottom_right_corner",
|
||||
Location::Bottom => "bottom_side",
|
||||
Location::BottomLeft => "bottom_left_corner",
|
||||
Location::Left => "left_side",
|
||||
Location::TopLeft => "top_left_corner",
|
||||
_ => "left_ptr",
|
||||
};
|
||||
|
||||
if pointer.set_cursor(name, serial).is_err() {
|
||||
error!("Failed to set cursor");
|
||||
}
|
||||
}
|
||||
|
||||
fn request_for_location_on_lmb(
|
||||
pointer_data: &PointerUserData,
|
||||
maximized: bool,
|
||||
resizable: bool,
|
||||
) -> Option<FrameRequest> {
|
||||
use wayland_protocols::xdg_shell::client::xdg_toplevel::ResizeEdge;
|
||||
match pointer_data.location {
|
||||
Location::Top if resizable => {
|
||||
Some(FrameRequest::Resize(pointer_data.seat.clone(), ResizeEdge::Top))
|
||||
}
|
||||
Location::TopLeft if resizable => {
|
||||
Some(FrameRequest::Resize(pointer_data.seat.clone(), ResizeEdge::TopLeft))
|
||||
}
|
||||
Location::Left if resizable => {
|
||||
Some(FrameRequest::Resize(pointer_data.seat.clone(), ResizeEdge::Left))
|
||||
}
|
||||
Location::BottomLeft if resizable => {
|
||||
Some(FrameRequest::Resize(pointer_data.seat.clone(), ResizeEdge::BottomLeft))
|
||||
}
|
||||
Location::Bottom if resizable => {
|
||||
Some(FrameRequest::Resize(pointer_data.seat.clone(), ResizeEdge::Bottom))
|
||||
}
|
||||
Location::BottomRight if resizable => {
|
||||
Some(FrameRequest::Resize(pointer_data.seat.clone(), ResizeEdge::BottomRight))
|
||||
}
|
||||
Location::Right if resizable => {
|
||||
Some(FrameRequest::Resize(pointer_data.seat.clone(), ResizeEdge::Right))
|
||||
}
|
||||
Location::TopRight if resizable => {
|
||||
Some(FrameRequest::Resize(pointer_data.seat.clone(), ResizeEdge::TopRight))
|
||||
}
|
||||
Location::Head => Some(FrameRequest::Move(pointer_data.seat.clone())),
|
||||
Location::Button(UIButton::Close) => Some(FrameRequest::Close),
|
||||
Location::Button(UIButton::Maximize) => {
|
||||
if maximized {
|
||||
Some(FrameRequest::UnMaximize)
|
||||
} else {
|
||||
Some(FrameRequest::Maximize)
|
||||
}
|
||||
}
|
||||
Location::Button(UIButton::Minimize) => Some(FrameRequest::Minimize),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
fn request_for_location_on_rmb(pointer_data: &PointerUserData) -> Option<FrameRequest> {
|
||||
match pointer_data.location {
|
||||
Location::Head | Location::Button(_) => Some(FrameRequest::ShowMenu(
|
||||
pointer_data.seat.clone(),
|
||||
pointer_data.position.0 as i32,
|
||||
// We must offset it by header size for precise position.
|
||||
pointer_data.position.1 as i32 - HEADER_SIZE as i32,
|
||||
)),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
fn draw_buttons(
|
||||
canvas: &mut [u8],
|
||||
width: u32,
|
||||
scale: u32,
|
||||
maximizable: bool,
|
||||
state: WindowState,
|
||||
mouses: &[Location],
|
||||
) {
|
||||
let scale = scale as usize;
|
||||
|
||||
if width >= HEADER_SIZE {
|
||||
// Draw the close button
|
||||
let btn_state = if mouses.iter().any(|&l| l == Location::Button(UIButton::Close)) {
|
||||
ButtonState::Hovered
|
||||
} else {
|
||||
ButtonState::Idle
|
||||
};
|
||||
|
||||
if state == WindowState::Active && btn_state == ButtonState::Hovered {
|
||||
draw_button(canvas, 0, scale, width as usize, BTN_HOVER_BG.to_ne_bytes());
|
||||
}
|
||||
draw_icon(canvas, width as usize, 0, scale, BTN_ICON_COLOR.to_ne_bytes(), Icon::Close);
|
||||
}
|
||||
|
||||
if width as usize >= 2 * HEADER_SIZE as usize {
|
||||
let btn_state = if !maximizable {
|
||||
ButtonState::Disabled
|
||||
} else if mouses.iter().any(|&l| l == Location::Button(UIButton::Maximize)) {
|
||||
ButtonState::Hovered
|
||||
} else {
|
||||
ButtonState::Idle
|
||||
};
|
||||
|
||||
if state == WindowState::Active && btn_state == ButtonState::Hovered {
|
||||
draw_button(
|
||||
canvas,
|
||||
HEADER_SIZE as usize,
|
||||
scale,
|
||||
width as usize,
|
||||
BTN_HOVER_BG.to_ne_bytes(),
|
||||
);
|
||||
}
|
||||
draw_icon(
|
||||
canvas,
|
||||
width as usize,
|
||||
HEADER_SIZE as usize,
|
||||
scale,
|
||||
BTN_ICON_COLOR.to_ne_bytes(),
|
||||
Icon::Maximize,
|
||||
);
|
||||
}
|
||||
|
||||
if width as usize >= 3 * HEADER_SIZE as usize {
|
||||
let btn_state = if mouses.iter().any(|&l| l == Location::Button(UIButton::Minimize)) {
|
||||
ButtonState::Hovered
|
||||
} else {
|
||||
ButtonState::Idle
|
||||
};
|
||||
|
||||
if state == WindowState::Active && btn_state == ButtonState::Hovered {
|
||||
draw_button(
|
||||
canvas,
|
||||
2 * HEADER_SIZE as usize,
|
||||
scale,
|
||||
width as usize,
|
||||
BTN_HOVER_BG.to_ne_bytes(),
|
||||
);
|
||||
}
|
||||
draw_icon(
|
||||
canvas,
|
||||
width as usize,
|
||||
2 * HEADER_SIZE as usize,
|
||||
scale,
|
||||
BTN_ICON_COLOR.to_ne_bytes(),
|
||||
Icon::Minimize,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
enum Icon {
|
||||
Close,
|
||||
Maximize,
|
||||
Minimize,
|
||||
}
|
||||
|
||||
fn draw_button(canvas: &mut [u8], x_offset: usize, scale: usize, width: usize, btn_color: [u8; 4]) {
|
||||
let h = HEADER_SIZE as usize;
|
||||
let x_start = width - h - x_offset;
|
||||
// main square
|
||||
for y in 0..h * scale {
|
||||
let canvas =
|
||||
&mut canvas[(x_start + y * width) * 4 * scale..(x_start + y * width + h) * scale * 4];
|
||||
for pixel in canvas.chunks_exact_mut(4) {
|
||||
pixel[0] = btn_color[0];
|
||||
pixel[1] = btn_color[1];
|
||||
pixel[2] = btn_color[2];
|
||||
pixel[3] = btn_color[3];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn draw_icon(
|
||||
canvas: &mut [u8],
|
||||
width: usize,
|
||||
x_offset: usize,
|
||||
scale: usize,
|
||||
icon_color: [u8; 4],
|
||||
icon: Icon,
|
||||
) {
|
||||
let h = HEADER_SIZE as usize;
|
||||
let sh = scale * h;
|
||||
let x_start = width - h - x_offset;
|
||||
|
||||
match icon {
|
||||
Icon::Close => {
|
||||
// Draw black rectangle
|
||||
for y in sh / 4..3 * sh / 4 {
|
||||
let line = &mut canvas[(x_start + y * width + h / 4) * 4 * scale
|
||||
..(x_start + y * width + 3 * h / 4) * 4 * scale];
|
||||
for pixel in line.chunks_exact_mut(4) {
|
||||
pixel[0] = icon_color[0];
|
||||
pixel[1] = icon_color[1];
|
||||
pixel[2] = icon_color[2];
|
||||
pixel[3] = icon_color[3];
|
||||
}
|
||||
}
|
||||
}
|
||||
Icon::Maximize => {
|
||||
// Draw an empty rectangle
|
||||
for y in 2 * sh / 8..3 * sh / 8 {
|
||||
let line = &mut canvas[(x_start + y * width + h / 4) * 4 * scale
|
||||
..(x_start + y * width + 3 * h / 4) * 4 * scale];
|
||||
for pixel in line.chunks_exact_mut(4) {
|
||||
pixel[0] = icon_color[0];
|
||||
pixel[1] = icon_color[1];
|
||||
pixel[2] = icon_color[2];
|
||||
pixel[3] = icon_color[3];
|
||||
}
|
||||
}
|
||||
for y in 3 * sh / 8..5 * sh / 8 {
|
||||
let line = &mut canvas[(x_start + y * width + 2 * h / 8) * 4 * scale
|
||||
..(x_start + y * width + 3 * h / 8) * 4 * scale];
|
||||
for pixel in line.chunks_exact_mut(4) {
|
||||
pixel[0] = icon_color[0];
|
||||
pixel[1] = icon_color[1];
|
||||
pixel[2] = icon_color[2];
|
||||
pixel[3] = icon_color[3];
|
||||
}
|
||||
let line = &mut canvas[(x_start + y * width + 5 * h / 8) * 4 * scale
|
||||
..(x_start + y * width + 6 * h / 8) * 4 * scale];
|
||||
for pixel in line.chunks_exact_mut(4) {
|
||||
pixel[0] = icon_color[0];
|
||||
pixel[1] = icon_color[1];
|
||||
pixel[2] = icon_color[2];
|
||||
pixel[3] = icon_color[3];
|
||||
}
|
||||
}
|
||||
for y in 5 * sh / 8..6 * sh / 8 {
|
||||
let line = &mut canvas[(x_start + y * width + h / 4) * 4 * scale
|
||||
..(x_start + y * width + 3 * h / 4) * 4 * scale];
|
||||
for pixel in line.chunks_exact_mut(4) {
|
||||
pixel[0] = icon_color[0];
|
||||
pixel[1] = icon_color[1];
|
||||
pixel[2] = icon_color[2];
|
||||
pixel[3] = icon_color[3];
|
||||
}
|
||||
}
|
||||
}
|
||||
Icon::Minimize => {
|
||||
// Draw an underline
|
||||
for y in 5 * sh / 8..3 * sh / 4 {
|
||||
let line = &mut canvas[(x_start + y * width + h / 4) * 4 * scale
|
||||
..(x_start + y * width + 3 * h / 4) * 4 * scale];
|
||||
for pixel in line.chunks_exact_mut(4) {
|
||||
pixel[0] = icon_color[0];
|
||||
pixel[1] = icon_color[1];
|
||||
pixel[2] = icon_color[2];
|
||||
pixel[3] = icon_color[3];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
857
third-party/vendor/smithay-client-toolkit/src/window/mod.rs
vendored
Normal file
857
third-party/vendor/smithay-client-toolkit/src/window/mod.rs
vendored
Normal file
|
|
@ -0,0 +1,857 @@
|
|||
//! Window abstraction
|
||||
use std::cell::RefCell;
|
||||
use std::fmt;
|
||||
use std::rc::Rc;
|
||||
use std::sync::Arc;
|
||||
|
||||
use wayland_client::protocol::{
|
||||
wl_compositor, wl_output, wl_seat, wl_shm, wl_subcompositor, wl_surface,
|
||||
};
|
||||
use wayland_client::{Attached, DispatchData};
|
||||
|
||||
use wayland_protocols::xdg_shell::client::xdg_toplevel::ResizeEdge;
|
||||
pub use wayland_protocols::xdg_shell::client::xdg_toplevel::State;
|
||||
|
||||
use wayland_protocols::unstable::xdg_decoration::v1::client::{
|
||||
zxdg_decoration_manager_v1::ZxdgDecorationManagerV1,
|
||||
zxdg_toplevel_decoration_v1::{self, ZxdgToplevelDecorationV1},
|
||||
};
|
||||
|
||||
use crate::{
|
||||
environment::{Environment, GlobalHandler, MultiGlobalHandler},
|
||||
seat::pointer::ThemeManager,
|
||||
shell,
|
||||
};
|
||||
|
||||
mod fallback_frame;
|
||||
pub use self::fallback_frame::FallbackFrame;
|
||||
|
||||
// Defines the minimum window size. Minimum width is set to 2 pixels to circumvent
|
||||
// a bug in mutter - https://gitlab.gnome.org/GNOME/mutter/issues/259
|
||||
const MIN_WINDOW_SIZE: (u32, u32) = (2, 1);
|
||||
|
||||
/// Represents the status of a button
|
||||
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
|
||||
pub enum ButtonState {
|
||||
/// Button is being hovered over by pointer
|
||||
Hovered,
|
||||
/// Button is not being hovered over by pointer
|
||||
Idle,
|
||||
/// Button is disabled
|
||||
Disabled,
|
||||
}
|
||||
|
||||
/// Represents the status of a window
|
||||
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
|
||||
pub enum WindowState {
|
||||
/// The window is active, in the foreground
|
||||
Active,
|
||||
/// The window is inactive, in the background
|
||||
Inactive,
|
||||
}
|
||||
|
||||
impl From<bool> for WindowState {
|
||||
fn from(b: bool) -> WindowState {
|
||||
if b {
|
||||
WindowState::Active
|
||||
} else {
|
||||
WindowState::Inactive
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl From<WindowState> for bool {
|
||||
fn from(s: WindowState) -> bool {
|
||||
match s {
|
||||
WindowState::Active => true,
|
||||
WindowState::Inactive => false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Possible events generated by a window that you need to handle
|
||||
#[derive(Clone, Debug)]
|
||||
pub enum Event {
|
||||
/// The state of your window has been changed
|
||||
Configure {
|
||||
/// Optional new size for your *inner* surface
|
||||
///
|
||||
/// This is the new size of the contents of your window
|
||||
/// as suggested by the server. You can ignore it and choose
|
||||
/// a new size if you want better control on the possible
|
||||
/// sizes of your window.
|
||||
///
|
||||
/// The size is expressed in logical pixels, you need to multiply it by
|
||||
/// your buffer scale to get the actual number of pixels to draw.
|
||||
///
|
||||
/// In all cases, these events can be generated in large batches
|
||||
/// during an interactive resize, and you should buffer them before
|
||||
/// processing them. You only need to handle the last one of a batch.
|
||||
new_size: Option<(u32, u32)>,
|
||||
/// New combination of states of your window
|
||||
///
|
||||
/// Typically tells you if your surface is active/inactive, maximized,
|
||||
/// etc...
|
||||
states: Vec<State>,
|
||||
},
|
||||
/// A close request has been received
|
||||
///
|
||||
/// Most likely the user has clicked on the close button of the decorations
|
||||
/// or something equivalent
|
||||
Close,
|
||||
/// The decorations need to be refreshed
|
||||
Refresh,
|
||||
}
|
||||
|
||||
/// Possible decoration modes for a Window
|
||||
///
|
||||
/// This represents what your application requests from the server.
|
||||
///
|
||||
/// In any case, the compositor may override your requests. In that case SCTK
|
||||
/// will follow its decision.
|
||||
///
|
||||
/// If you don't care about it, you should use `FollowServer` (which is the
|
||||
/// SCTK default). It'd be the most ergonomic for your users.
|
||||
#[derive(Debug, PartialEq, Eq)]
|
||||
pub enum Decorations {
|
||||
/// Request server-side decorations
|
||||
ServerSide,
|
||||
/// Force using the client-side `Frame`
|
||||
ClientSide,
|
||||
/// Follow the preference of the compositor
|
||||
FollowServer,
|
||||
/// Don't decorate the Window
|
||||
None,
|
||||
}
|
||||
|
||||
struct WindowInner<F> {
|
||||
frame: Rc<RefCell<F>>,
|
||||
shell_surface: Arc<Box<dyn shell::ShellSurface>>,
|
||||
user_impl: Box<dyn FnMut(Event, DispatchData)>,
|
||||
min_size: (u32, u32),
|
||||
max_size: Option<(u32, u32)>,
|
||||
current_size: (u32, u32),
|
||||
old_size: Option<(u32, u32)>,
|
||||
decorated: bool,
|
||||
}
|
||||
|
||||
impl<F> fmt::Debug for WindowInner<F>
|
||||
where
|
||||
F: fmt::Debug,
|
||||
{
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
f.debug_struct("WindowInner")
|
||||
.field("frame", &self.frame)
|
||||
.field("shell_surface", &self.shell_surface)
|
||||
.field("user_impl", &"Fn() -> { ... }")
|
||||
.field("min_size", &self.min_size)
|
||||
.field("max_size", &self.max_size)
|
||||
.field("current_size", &self.current_size)
|
||||
.field("old_size", &self.old_size)
|
||||
.field("decorated", &self.decorated)
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
|
||||
/// A window
|
||||
///
|
||||
/// This wrapper handles for you the decoration of your window
|
||||
/// and the interaction with the server regarding the shell protocol.
|
||||
///
|
||||
/// You are still entirely responsible for drawing the contents of your
|
||||
/// window.
|
||||
///
|
||||
/// Note also that as the dimensions of wayland surfaces is defined by
|
||||
/// their attached buffer, you need to keep the decorations in sync with
|
||||
/// your contents via the `resize(..)` method.
|
||||
///
|
||||
/// Different kind of decorations can be used by customizing the type
|
||||
/// parameter. A few are provided in this crate if the `frames` cargo feature
|
||||
/// is enabled, but any type implementing the `Frame` trait can do.
|
||||
pub struct Window<F: Frame> {
|
||||
frame: Rc<RefCell<F>>,
|
||||
surface: wl_surface::WlSurface,
|
||||
decoration: Option<ZxdgToplevelDecorationV1>,
|
||||
shell_surface: Arc<Box<dyn shell::ShellSurface>>,
|
||||
inner: Rc<RefCell<Option<WindowInner<F>>>>,
|
||||
_seat_listener: crate::seat::SeatListener,
|
||||
}
|
||||
|
||||
impl<F: Frame + 'static> Window<F> {
|
||||
/// Create a new window wrapping a given wayland surface as its main content and
|
||||
/// following the compositor's preference regarding server-side decorations
|
||||
///
|
||||
/// It can fail if the initialization of the frame fails (for example if the
|
||||
/// frame class fails to initialize its SHM).
|
||||
///
|
||||
/// Providing non `None` value for `theme_manager` should prevent theming pointer
|
||||
/// over the `surface`.
|
||||
fn init_with_decorations<Impl, E>(
|
||||
env: &crate::environment::Environment<E>,
|
||||
surface: wl_surface::WlSurface,
|
||||
theme_manager: Option<ThemeManager>,
|
||||
initial_dims: (u32, u32),
|
||||
implementation: Impl,
|
||||
) -> Result<Window<F>, F::Error>
|
||||
where
|
||||
Impl: FnMut(Event, DispatchData) + 'static,
|
||||
E: GlobalHandler<wl_compositor::WlCompositor>
|
||||
+ GlobalHandler<wl_subcompositor::WlSubcompositor>
|
||||
+ GlobalHandler<wl_shm::WlShm>
|
||||
+ crate::shell::ShellHandling
|
||||
+ MultiGlobalHandler<wl_seat::WlSeat>
|
||||
+ GlobalHandler<ZxdgDecorationManagerV1>
|
||||
+ crate::seat::SeatHandling,
|
||||
{
|
||||
let compositor = env.require_global::<wl_compositor::WlCompositor>();
|
||||
let subcompositor = env.require_global::<wl_subcompositor::WlSubcompositor>();
|
||||
let shm = env.require_global::<wl_shm::WlShm>();
|
||||
let shell = env
|
||||
.get_shell()
|
||||
.expect("[SCTK] Cannot create a window if the compositor advertized no shell.");
|
||||
|
||||
let inner = Rc::new(RefCell::new(None::<WindowInner<F>>));
|
||||
let frame_inner = inner.clone();
|
||||
let shell_inner = inner.clone();
|
||||
let mut frame = F::init(
|
||||
&surface,
|
||||
&compositor,
|
||||
&subcompositor,
|
||||
&shm,
|
||||
theme_manager,
|
||||
Box::new(move |req, serial, ddata: DispatchData| {
|
||||
if let Some(ref mut inner) = *shell_inner.borrow_mut() {
|
||||
match req {
|
||||
FrameRequest::Minimize => inner.shell_surface.set_minimized(),
|
||||
FrameRequest::Maximize => inner.shell_surface.set_maximized(),
|
||||
FrameRequest::UnMaximize => inner.shell_surface.unset_maximized(),
|
||||
FrameRequest::Move(seat) => inner.shell_surface.move_(&seat, serial),
|
||||
FrameRequest::Resize(seat, edges) => {
|
||||
inner.shell_surface.resize(&seat, serial, edges)
|
||||
}
|
||||
FrameRequest::ShowMenu(seat, x, y) => {
|
||||
inner.shell_surface.show_window_menu(&seat, serial, x, y)
|
||||
}
|
||||
FrameRequest::Close => (inner.user_impl)(Event::Close, ddata),
|
||||
FrameRequest::Refresh => (inner.user_impl)(Event::Refresh, ddata),
|
||||
}
|
||||
}
|
||||
}) as Box<_>,
|
||||
)?;
|
||||
|
||||
let decoration_mgr = env.get_global::<ZxdgDecorationManagerV1>();
|
||||
if decoration_mgr.is_none() {
|
||||
// We don't have ServerSide decorations, so we'll be using CSD, and so should
|
||||
// mark frame as not hidden.
|
||||
frame.set_hidden(false);
|
||||
}
|
||||
|
||||
frame.resize(initial_dims);
|
||||
let frame = Rc::new(RefCell::new(frame));
|
||||
let shell_surface = Arc::new(shell::create_shell_surface(
|
||||
&shell,
|
||||
&surface,
|
||||
move |event, mut ddata: DispatchData| {
|
||||
let mut frame_inner = frame_inner.borrow_mut();
|
||||
let mut inner = match frame_inner.as_mut() {
|
||||
Some(inner) => inner,
|
||||
None => return,
|
||||
};
|
||||
|
||||
match event {
|
||||
shell::Event::Configure { states, mut new_size } => {
|
||||
let mut frame = inner.frame.borrow_mut();
|
||||
|
||||
// Populate frame changes. We should do it before performing new_size
|
||||
// recalculation, since we should account for a fullscreen state.
|
||||
let need_refresh = frame.set_states(&states);
|
||||
|
||||
// Clamp size.
|
||||
new_size = new_size.map(|(w, h)| {
|
||||
use std::cmp::{max, min};
|
||||
let (mut w, mut h) = frame.subtract_borders(w as i32, h as i32);
|
||||
let (minw, minh) = inner.min_size;
|
||||
w = max(w, minw as i32);
|
||||
h = max(h, minh as i32);
|
||||
if let Some((maxw, maxh)) = inner.max_size {
|
||||
w = min(w, maxw as i32);
|
||||
h = min(h, maxh as i32);
|
||||
}
|
||||
(max(w, 1) as u32, max(h, 1) as u32)
|
||||
});
|
||||
|
||||
// Check whether we should save old size for later restoration.
|
||||
let should_stash_size = states
|
||||
.iter()
|
||||
.find(|s| {
|
||||
matches!(
|
||||
*s,
|
||||
State::Maximized
|
||||
| State::Fullscreen
|
||||
| State::TiledTop
|
||||
| State::TiledRight
|
||||
| State::TiledBottom
|
||||
| State::TiledLeft
|
||||
)
|
||||
})
|
||||
.map(|_| true)
|
||||
.unwrap_or(false);
|
||||
|
||||
if should_stash_size {
|
||||
if inner.old_size.is_none() {
|
||||
// We are getting maximized/fullscreened, store the size for
|
||||
// restoration.
|
||||
inner.old_size = Some(inner.current_size);
|
||||
}
|
||||
} else if new_size.is_none() {
|
||||
// We are getting de-maximized/de-fullscreened/un-tiled, restore the
|
||||
// size, if we were not previously maximized/fullscreened, old_size is
|
||||
// None and this does nothing.
|
||||
new_size = inner.old_size.take();
|
||||
} else {
|
||||
// We are neither maximized nor fullscreened, but are given a size,
|
||||
// respect it and forget about the old size.
|
||||
inner.old_size = None;
|
||||
}
|
||||
|
||||
if need_refresh {
|
||||
(inner.user_impl)(Event::Refresh, ddata.reborrow());
|
||||
}
|
||||
(inner.user_impl)(Event::Configure { states, new_size }, ddata);
|
||||
}
|
||||
shell::Event::Close => {
|
||||
(inner.user_impl)(Event::Close, ddata);
|
||||
}
|
||||
}
|
||||
},
|
||||
));
|
||||
|
||||
// setup size and geometry
|
||||
{
|
||||
let frame = frame.borrow_mut();
|
||||
let (minw, minh) =
|
||||
frame.add_borders(MIN_WINDOW_SIZE.0 as i32, MIN_WINDOW_SIZE.1 as i32);
|
||||
shell_surface.set_min_size(Some((minw, minh)));
|
||||
let (w, h) = frame.add_borders(initial_dims.0 as i32, initial_dims.1 as i32);
|
||||
let (x, y) = frame.location();
|
||||
shell_surface.set_geometry(x, y, w, h);
|
||||
}
|
||||
|
||||
// initial seat setup
|
||||
let mut seats = Vec::<wl_seat::WlSeat>::new();
|
||||
for seat in env.get_all_seats() {
|
||||
crate::seat::with_seat_data(&seat, |seat_data| {
|
||||
if seat_data.has_pointer && !seat_data.defunct {
|
||||
seats.push(seat.detach());
|
||||
frame.borrow_mut().new_seat(&seat);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// setup seat_listener
|
||||
let seat_frame = frame.clone();
|
||||
let seat_listener = env.listen_for_seats(move |seat, seat_data, _| {
|
||||
let is_known = seats.contains(&seat);
|
||||
if !is_known && seat_data.has_pointer && !seat_data.defunct {
|
||||
seat_frame.borrow_mut().new_seat(&seat);
|
||||
seats.push(seat.detach());
|
||||
} else if is_known && ((!seat_data.has_pointer) || seat_data.defunct) {
|
||||
seat_frame.borrow_mut().remove_seat(&seat);
|
||||
seats.retain(|s| s != &*seat);
|
||||
}
|
||||
});
|
||||
|
||||
*inner.borrow_mut() = Some(WindowInner {
|
||||
frame: frame.clone(),
|
||||
shell_surface: shell_surface.clone(),
|
||||
user_impl: Box::new(implementation) as Box<_>,
|
||||
min_size: (MIN_WINDOW_SIZE.0, MIN_WINDOW_SIZE.1),
|
||||
max_size: None,
|
||||
current_size: initial_dims,
|
||||
old_size: None,
|
||||
decorated: true,
|
||||
});
|
||||
|
||||
// Setup window decorations if applicable.
|
||||
let decoration = Self::setup_decorations_handler(
|
||||
&decoration_mgr,
|
||||
&shell_surface,
|
||||
frame.clone(),
|
||||
inner.clone(),
|
||||
);
|
||||
|
||||
let window = Window {
|
||||
frame,
|
||||
shell_surface,
|
||||
decoration,
|
||||
surface,
|
||||
inner,
|
||||
_seat_listener: seat_listener,
|
||||
};
|
||||
|
||||
Ok(window)
|
||||
}
|
||||
|
||||
/// Setup handling for zxdg_toplevel_decoration_v1 in case protocol is available.
|
||||
fn setup_decorations_handler(
|
||||
decoration_mgr: &Option<Attached<ZxdgDecorationManagerV1>>,
|
||||
shell_surface: &Arc<Box<dyn shell::ShellSurface>>,
|
||||
decoration_frame: Rc<RefCell<F>>,
|
||||
decoration_inner: Rc<RefCell<Option<WindowInner<F>>>>,
|
||||
) -> Option<ZxdgToplevelDecorationV1> {
|
||||
let (toplevel, mgr) = match (shell_surface.get_xdg(), decoration_mgr) {
|
||||
(Some(toplevel), Some(ref mgr)) => (toplevel, mgr),
|
||||
_ => {
|
||||
return None;
|
||||
}
|
||||
};
|
||||
|
||||
let decoration = mgr.get_toplevel_decoration(toplevel);
|
||||
|
||||
decoration.quick_assign(move |_, event, _| {
|
||||
use self::zxdg_toplevel_decoration_v1::{Event, Mode};
|
||||
let mode = if let Event::Configure { mode } = event { mode } else { unreachable!() };
|
||||
|
||||
match mode {
|
||||
Mode::ServerSide => {
|
||||
decoration_frame.borrow_mut().set_hidden(true);
|
||||
}
|
||||
Mode::ClientSide => {
|
||||
let want_decorate = decoration_inner
|
||||
.borrow_mut()
|
||||
.as_ref()
|
||||
.map(|inner| inner.decorated)
|
||||
.unwrap_or(false);
|
||||
decoration_frame.borrow_mut().set_hidden(!want_decorate);
|
||||
}
|
||||
_ => unreachable!(),
|
||||
}
|
||||
});
|
||||
|
||||
Some(decoration.detach())
|
||||
}
|
||||
|
||||
/// Access the surface wrapped in this Window
|
||||
pub fn surface(&self) -> &wl_surface::WlSurface {
|
||||
&self.surface
|
||||
}
|
||||
|
||||
/// Refreshes the frame
|
||||
///
|
||||
/// Redraws the frame to match its requested state (dimensions, presence/
|
||||
/// absence of decorations, ...)
|
||||
///
|
||||
/// You need to call this method after every change to the dimensions or state
|
||||
/// of the decorations of your window, otherwise the drawn decorations may go
|
||||
/// out of sync with the state of your content.
|
||||
///
|
||||
/// Your implementation will also receive `Refresh` events when the frame requests
|
||||
/// to be redrawn (to provide some frame animations for example).
|
||||
pub fn refresh(&mut self) {
|
||||
self.frame.borrow_mut().redraw();
|
||||
}
|
||||
|
||||
/// Set a short title for the window.
|
||||
///
|
||||
/// This string may be used to identify the surface in a task bar, window list, or other
|
||||
/// user interface elements provided by the compositor.
|
||||
///
|
||||
/// You need to call `refresh()` afterwards for this to properly
|
||||
/// take effect.
|
||||
pub fn set_title(&self, mut title: String) {
|
||||
// Truncate the title to at most 1024 bytes, so that it does not blow up the protocol
|
||||
// messages
|
||||
if title.len() > 1024 {
|
||||
let mut new_len = 1024;
|
||||
while !title.is_char_boundary(new_len) {
|
||||
new_len -= 1;
|
||||
}
|
||||
title.truncate(new_len);
|
||||
}
|
||||
self.frame.borrow_mut().set_title(title.clone());
|
||||
self.shell_surface.set_title(title);
|
||||
}
|
||||
|
||||
/// Set an app id for the surface.
|
||||
///
|
||||
/// The surface class identifies the general class of applications to which the surface
|
||||
/// belongs.
|
||||
///
|
||||
/// Several wayland compositors will try to find a `.desktop` file matching this name
|
||||
/// to find metadata about your apps.
|
||||
pub fn set_app_id(&self, app_id: String) {
|
||||
self.shell_surface.set_app_id(app_id);
|
||||
}
|
||||
|
||||
/// Set whether the window should be decorated or not.
|
||||
///
|
||||
/// If `zxdg_toplevel_decoration_v1` object is presented and alive, requesting `None`
|
||||
/// decorations will result in setting `ClientSide` decorations with hidden frame, and if
|
||||
/// `ClientSide` decorations were requested, it'll result in destroying
|
||||
/// `zxdg_toplevel_decoration_v1` object, meaning that you won't be able to get `ServerSide`
|
||||
/// decorations back.
|
||||
///
|
||||
/// In case `zxdg_toplevel_decoration_v1` is not available or the corresponding object is not
|
||||
/// alive anymore, `decorate` with `ServerSide` or `FollowServer` values will always result in
|
||||
/// `ClientSide` decorations being used.
|
||||
///
|
||||
/// You need to call `refresh()` afterwards for this to properly
|
||||
/// take effect.
|
||||
pub fn set_decorate(&mut self, decorate: Decorations) {
|
||||
use self::zxdg_toplevel_decoration_v1::Mode;
|
||||
|
||||
// Update inner.decorated state.
|
||||
if let Some(inner) = self.inner.borrow_mut().as_mut() {
|
||||
if Decorations::None == decorate {
|
||||
inner.decorated = false;
|
||||
} else {
|
||||
inner.decorated = true;
|
||||
}
|
||||
}
|
||||
|
||||
match self.decoration.as_ref() {
|
||||
// Server side decorations are there.
|
||||
Some(decoration) => {
|
||||
match decorate {
|
||||
Decorations::ClientSide => {
|
||||
// The user explicitly requested `ClientSide` decorations, we should destroy
|
||||
// the server side decorations if some are presented.
|
||||
decoration.destroy();
|
||||
self.decoration = None;
|
||||
self.frame.borrow_mut().set_hidden(false);
|
||||
}
|
||||
Decorations::ServerSide => {
|
||||
decoration.set_mode(Mode::ServerSide);
|
||||
}
|
||||
Decorations::FollowServer => {
|
||||
decoration.unset_mode();
|
||||
}
|
||||
Decorations::None => {
|
||||
// The user explicitly requested `None` decorations, however
|
||||
// since we can't destroy and recreate decoration object on the fly switch
|
||||
// them to `ClientSide` with the hidden frame. The server is free to ignore
|
||||
// us with such request, but not that we can do much about it.
|
||||
decoration.set_mode(Mode::ClientSide);
|
||||
self.frame.borrow_mut().set_hidden(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
// Server side decorations are not presented or were destroyed.
|
||||
None => {
|
||||
match decorate {
|
||||
// We map `ServerSide` and `FollowServer` decorations to `ClientSide`, since
|
||||
// server side decorations are no longer available.
|
||||
Decorations::ClientSide
|
||||
| Decorations::ServerSide
|
||||
| Decorations::FollowServer => {
|
||||
self.frame.borrow_mut().set_hidden(false);
|
||||
}
|
||||
Decorations::None => {
|
||||
self.frame.borrow_mut().set_hidden(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Set whether the window should be resizeable by the user
|
||||
///
|
||||
/// This is not an hard blocking, as the compositor can always
|
||||
/// resize you forcibly if it wants. However it signals it that
|
||||
/// you don't want this window to be resized.
|
||||
///
|
||||
/// Additionally, the decorations will stop suggesting the user
|
||||
/// to resize by dragging the borders if you set the window as
|
||||
/// non-resizable.
|
||||
///
|
||||
/// When re-activating resizability, any previously set min/max
|
||||
/// sizes are restored.
|
||||
pub fn set_resizable(&self, resizable: bool) {
|
||||
let mut frame = self.frame.borrow_mut();
|
||||
frame.set_resizable(resizable);
|
||||
let mut inner = self.inner.borrow_mut();
|
||||
if let Some(ref mut inner) = *inner {
|
||||
if resizable {
|
||||
// restore the min/max sizes
|
||||
self.shell_surface.set_min_size(
|
||||
Some(inner.min_size).map(|(w, h)| frame.add_borders(w as i32, h as i32)),
|
||||
);
|
||||
self.shell_surface.set_max_size(
|
||||
inner.max_size.map(|(w, h)| frame.add_borders(w as i32, h as i32)),
|
||||
);
|
||||
} else {
|
||||
// Lock the min/max sizes to current size.
|
||||
let (w, h) = inner.current_size;
|
||||
self.shell_surface.set_min_size(Some(frame.add_borders(w as i32, h as i32)));
|
||||
self.shell_surface.set_max_size(Some(frame.add_borders(w as i32, h as i32)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Resize the decorations
|
||||
///
|
||||
/// You should call this whenever you change the size of the contents
|
||||
/// of your window, with the new _inner size_ of your window.
|
||||
///
|
||||
/// This size is expressed in logical pixels, like the one received
|
||||
/// in [`Event::Configure`](enum.Event.html).
|
||||
///
|
||||
/// You need to call `refresh()` afterwards for this to properly
|
||||
/// take effect.
|
||||
pub fn resize(&mut self, w: u32, h: u32) {
|
||||
use std::cmp::max;
|
||||
let w = max(w, 1);
|
||||
let h = max(h, 1);
|
||||
if let Some(ref mut inner) = *self.inner.borrow_mut() {
|
||||
inner.current_size = (w, h);
|
||||
}
|
||||
let mut frame = self.frame.borrow_mut();
|
||||
frame.resize((w, h));
|
||||
let (w, h) = frame.add_borders(w as i32, h as i32);
|
||||
let (x, y) = frame.location();
|
||||
self.shell_surface.set_geometry(x, y, w, h);
|
||||
}
|
||||
|
||||
/// Request the window to be maximized
|
||||
pub fn set_maximized(&self) {
|
||||
self.shell_surface.set_maximized();
|
||||
}
|
||||
|
||||
/// Request the window to be un-maximized
|
||||
pub fn unset_maximized(&self) {
|
||||
self.shell_surface.unset_maximized();
|
||||
}
|
||||
|
||||
/// Request the window to be minimized
|
||||
pub fn set_minimized(&self) {
|
||||
self.shell_surface.set_minimized();
|
||||
}
|
||||
|
||||
/// Request the window to be set fullscreen
|
||||
///
|
||||
/// Note: The decorations hiding behavior is `Frame` dependant.
|
||||
/// To check whether you need to hide them consult your frame documentation.
|
||||
pub fn set_fullscreen(&self, output: Option<&wl_output::WlOutput>) {
|
||||
self.shell_surface.set_fullscreen(output);
|
||||
}
|
||||
|
||||
/// Request the window to quit fullscreen mode
|
||||
pub fn unset_fullscreen(&self) {
|
||||
self.shell_surface.unset_fullscreen();
|
||||
}
|
||||
|
||||
/// Sets the minimum possible size for this window
|
||||
///
|
||||
/// Provide either a tuple `Some((width, height))` or `None` to unset the
|
||||
/// minimum size.
|
||||
///
|
||||
/// Setting either value in the tuple to `0` means that this axis should not
|
||||
/// be limited.
|
||||
///
|
||||
/// The provided size is the interior size, not counting decorations.
|
||||
///
|
||||
/// This size is expressed in logical pixels, like the one received
|
||||
/// in [`Event::Configure`](enum.Event.html).
|
||||
pub fn set_min_size(&mut self, size: Option<(u32, u32)>) {
|
||||
let (w, h) = size.unwrap_or(MIN_WINDOW_SIZE);
|
||||
let (w, h) = self.frame.borrow_mut().add_borders(w as i32, h as i32);
|
||||
self.shell_surface.set_min_size(Some((w, h)));
|
||||
if let Some(ref mut inner) = *self.inner.borrow_mut() {
|
||||
inner.min_size = size.unwrap_or(MIN_WINDOW_SIZE)
|
||||
}
|
||||
}
|
||||
|
||||
/// Sets the maximum possible size for this window
|
||||
///
|
||||
/// Provide either a tuple `Some((width, height))` or `None` to unset the
|
||||
/// maximum size.
|
||||
///
|
||||
/// Setting either value in the tuple to `0` means that this axis should not
|
||||
/// be limited.
|
||||
///
|
||||
/// The provided size is the interior size, not counting decorations.
|
||||
///
|
||||
/// This size is expressed in logical pixels, like the one received
|
||||
/// in [`Event::Configure`](enum.Event.html).
|
||||
pub fn set_max_size(&mut self, size: Option<(u32, u32)>) {
|
||||
let max_size = size.map(|(w, h)| self.frame.borrow_mut().add_borders(w as i32, h as i32));
|
||||
self.shell_surface.set_max_size(max_size);
|
||||
if let Some(ref mut inner) = *self.inner.borrow_mut() {
|
||||
inner.max_size = size.map(|(w, h)| (w as u32, h as u32));
|
||||
}
|
||||
}
|
||||
|
||||
/// Sets the frame configuration for the window
|
||||
///
|
||||
/// This allows to configure the frame at runtime if it supports
|
||||
/// it. See the documentation of your `Frame` implementation for
|
||||
/// details about what configuration it supports.
|
||||
pub fn set_frame_config(&mut self, config: F::Config) {
|
||||
self.frame.borrow_mut().set_config(config)
|
||||
}
|
||||
|
||||
/// Start an interactive, user-driven move of the surface
|
||||
///
|
||||
/// This request must be used in response to some sort of user action
|
||||
/// like a button press, key press, or touch down event. The passed
|
||||
/// serial is used to determine the type of interactive move (touch,
|
||||
/// pointer, etc).
|
||||
///
|
||||
/// The server may ignore move requests depending on the state of
|
||||
/// the surface (e.g. fullscreen or maximized), or if the passed serial
|
||||
/// is no longer valid.
|
||||
pub fn start_interactive_move(&self, seat: &wl_seat::WlSeat, serial: u32) {
|
||||
self.shell_surface.move_(seat, serial);
|
||||
}
|
||||
}
|
||||
|
||||
impl<F: Frame> Drop for Window<F> {
|
||||
fn drop(&mut self) {
|
||||
self.inner.borrow_mut().take();
|
||||
|
||||
// Destroy decorations manager, so the inner frame could be dropped.
|
||||
if let Some(decoration) = self.decoration.take() {
|
||||
decoration.destroy();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<F: Frame> fmt::Debug for Window<F>
|
||||
where
|
||||
F: fmt::Debug,
|
||||
{
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
f.debug_struct("Window")
|
||||
.field("frame", &self.frame)
|
||||
.field("surface", &self.surface)
|
||||
.field("decoration", &self.decoration)
|
||||
.field("shell_surface", &self.shell_surface)
|
||||
.field("inner", &self.inner)
|
||||
.field("_seat_listener", &self._seat_listener)
|
||||
.finish()
|
||||
}
|
||||
}
|
||||
|
||||
/// Request generated by a Frame
|
||||
///
|
||||
/// These requests are generated by a Frame and the Window will
|
||||
/// forward them appropriately to the server.
|
||||
#[derive(Debug)]
|
||||
pub enum FrameRequest {
|
||||
/// The window should be minimized
|
||||
Minimize,
|
||||
/// The window should be maximized
|
||||
Maximize,
|
||||
/// The window should be unmaximized
|
||||
UnMaximize,
|
||||
/// The window should be closed
|
||||
Close,
|
||||
/// An interactive move should be started
|
||||
Move(wl_seat::WlSeat),
|
||||
/// An interactive resize should be started
|
||||
Resize(wl_seat::WlSeat, ResizeEdge),
|
||||
/// Show window menu.
|
||||
ShowMenu(wl_seat::WlSeat, i32, i32),
|
||||
/// The frame requests to be refreshed
|
||||
Refresh,
|
||||
}
|
||||
|
||||
/// Interface for defining the drawing of decorations
|
||||
///
|
||||
/// A type implementing this trait can be used to define custom
|
||||
/// decorations additionnaly to the ones provided by this crate
|
||||
/// and be used with `Window`.
|
||||
pub trait Frame: Sized {
|
||||
/// Type of errors that may occur when attempting to create a frame
|
||||
type Error;
|
||||
/// Configuration for this frame
|
||||
type Config;
|
||||
/// Initialize the Frame.
|
||||
///
|
||||
/// Providing non `None` to `theme_manager` should prevent `Frame` to theme pointer
|
||||
/// over `base_surface` surface.
|
||||
fn init(
|
||||
base_surface: &wl_surface::WlSurface,
|
||||
compositor: &Attached<wl_compositor::WlCompositor>,
|
||||
subcompositor: &Attached<wl_subcompositor::WlSubcompositor>,
|
||||
shm: &Attached<wl_shm::WlShm>,
|
||||
theme_manager: Option<ThemeManager>,
|
||||
callback: Box<dyn FnMut(FrameRequest, u32, DispatchData)>,
|
||||
) -> Result<Self, Self::Error>;
|
||||
/// Set the Window XDG states for the frame
|
||||
///
|
||||
/// This notably includes information about whether the window is
|
||||
/// maximized, active, or tiled, and can affect the way decorations
|
||||
/// are drawn.
|
||||
///
|
||||
/// Calling this should *not* trigger a redraw, but return `true` if
|
||||
/// a redraw is needed.
|
||||
fn set_states(&mut self, states: &[State]) -> bool;
|
||||
/// Hide or show the decorations
|
||||
///
|
||||
/// Calling this should *not* trigger a redraw
|
||||
fn set_hidden(&mut self, hidden: bool);
|
||||
/// Set whether interactive resize hints should be displayed
|
||||
/// and reacted to
|
||||
fn set_resizable(&mut self, resizable: bool);
|
||||
/// Notify that a new wl_seat should be handled
|
||||
///
|
||||
/// This seat is guaranteed to have pointer capability
|
||||
fn new_seat(&mut self, seat: &Attached<wl_seat::WlSeat>);
|
||||
/// Notify that this seat has lost the pointer capability or
|
||||
/// has been lost
|
||||
fn remove_seat(&mut self, seat: &wl_seat::WlSeat);
|
||||
/// Change the size of the decorations
|
||||
///
|
||||
/// Calling this should *not* trigger a redraw
|
||||
fn resize(&mut self, newsize: (u32, u32));
|
||||
/// Redraw the decorations
|
||||
fn redraw(&mut self);
|
||||
/// Subtracts the border dimensions from the given dimensions.
|
||||
fn subtract_borders(&self, width: i32, height: i32) -> (i32, i32);
|
||||
/// Adds the border dimensions to the given dimensions.
|
||||
fn add_borders(&self, width: i32, height: i32) -> (i32, i32);
|
||||
/// Returns the coordinates of the top-left corner of the borders relative to the content
|
||||
///
|
||||
/// Values should thus be negative
|
||||
fn location(&self) -> (i32, i32) {
|
||||
(0, 0)
|
||||
}
|
||||
/// Sets the configuration for the frame
|
||||
fn set_config(&mut self, config: Self::Config);
|
||||
|
||||
/// Sets the frames title
|
||||
fn set_title(&mut self, title: String);
|
||||
}
|
||||
|
||||
impl<E> Environment<E>
|
||||
where
|
||||
E: GlobalHandler<wl_compositor::WlCompositor>
|
||||
+ GlobalHandler<wl_subcompositor::WlSubcompositor>
|
||||
+ GlobalHandler<wl_shm::WlShm>
|
||||
+ crate::shell::ShellHandling
|
||||
+ MultiGlobalHandler<wl_seat::WlSeat>
|
||||
+ GlobalHandler<ZxdgDecorationManagerV1>
|
||||
+ crate::seat::SeatHandling,
|
||||
{
|
||||
/// Create a new window wrapping given surface
|
||||
///
|
||||
/// This window handles decorations for you, this includes
|
||||
/// drawing them if the compositor doe snot support them, resizing interactions
|
||||
/// and moving the window. It also provides close/maximize/minimize buttons.
|
||||
///
|
||||
/// Many interactions still require your input, and are given to you via the
|
||||
/// callback you need to provide.
|
||||
pub fn create_window<F: Frame + 'static, CB>(
|
||||
&self,
|
||||
surface: wl_surface::WlSurface,
|
||||
theme_manager: Option<ThemeManager>,
|
||||
initial_dims: (u32, u32),
|
||||
callback: CB,
|
||||
) -> Result<Window<F>, F::Error>
|
||||
where
|
||||
CB: FnMut(Event, DispatchData) + 'static,
|
||||
{
|
||||
Window::<F>::init_with_decorations(self, surface, theme_manager, initial_dims, callback)
|
||||
}
|
||||
}
|
||||
18
third-party/vendor/smithay-client-toolkit/travis_install_wayland.sh
vendored
Executable file
18
third-party/vendor/smithay-client-toolkit/travis_install_wayland.sh
vendored
Executable file
|
|
@ -0,0 +1,18 @@
|
|||
#!/bin/sh
|
||||
|
||||
# download and compile the wayland libs for given version, they will be installed in ~/install
|
||||
|
||||
wayland_version=$1
|
||||
|
||||
mkdir ~/temp/ ~/install
|
||||
|
||||
# download and extract
|
||||
cd ~/temp/
|
||||
wget https://github.com/wayland-project/wayland/archive/${wayland_version}.tar.gz -O wayland.tar.gz
|
||||
tar xf wayland.tar.gz
|
||||
cd wayland-${wayland_version}
|
||||
|
||||
# compile and install
|
||||
./autogen.sh --prefix=$HOME/install --disable-documentation --disable-dtd-validation --disable-dependency-tracking
|
||||
make
|
||||
make install
|
||||
24
third-party/vendor/smithay-client-toolkit/update_keysyms.sh
vendored
Executable file
24
third-party/vendor/smithay-client-toolkit/update_keysyms.sh
vendored
Executable file
|
|
@ -0,0 +1,24 @@
|
|||
#!/bin/bash
|
||||
|
||||
X11_INCLUDEDIR="/usr/include/X11"
|
||||
KEYSYMDEFS="${X11_INCLUDEDIR}/keysymdef.h
|
||||
${X11_INCLUDEDIR}/XF86keysym.h
|
||||
${X11_INCLUDEDIR}/Sunkeysym.h
|
||||
${X11_INCLUDEDIR}/DECkeysym.h
|
||||
${X11_INCLUDEDIR}/HPkeysym.h"
|
||||
TARGET_FILE=src/seat/keyboard/keysyms.rs
|
||||
|
||||
echo "//" > $TARGET_FILE
|
||||
echo "// This file was auto-generated using the update-keysyms.sh script." >> $TARGET_FILE
|
||||
echo "//" >> $TARGET_FILE
|
||||
echo "" >> $TARGET_FILE
|
||||
echo "#![allow(missing_docs, non_upper_case_globals, unused_parens, clippy::all)]" >> $TARGET_FILE
|
||||
echo "#![cfg_attr(rustfmt, rustfmt_skip)]" >> $TARGET_FILE
|
||||
echo "" >> $TARGET_FILE
|
||||
|
||||
cat $KEYSYMDEFS | sed -e '/XK_Ydiaeresis\s*0x100000ee/d' \
|
||||
-e '/#define _/d' \
|
||||
-e 's/#define\s*\(\w*\)XK_/#define XKB_KEY_\1/' \
|
||||
-e '/\(#ifdef\|#ifndef\|#endif\)/d' \
|
||||
-e 's/#define/pub const/g' \
|
||||
-e 's/0x\([0-9a-fA-F]*\)/:u32 = 0x\1;/g' >> $TARGET_FILE
|
||||
Loading…
Add table
Add a link
Reference in a new issue