feat: pop-os megasquash
x11: Workaround nvidia driver lacking DRI
feat(mouse area): add double click
mouse area: add double click
compositor: Add code to extract adapter from x11
refactor: Extract ids_from_dev from wayland specific code
wayland: Don't crash if libwayland isn't available
feat(sctk): support for overflow widget
sctk: Fixes for cursor icon
* With multiple windows, `SetCursor` is only sent for the focused
window. Fixing a flicker between icons when two windows are using
different cursors.
* If there is a drag surface, let that surface set the cursor. And not
any other.
* Set cursor on `enter`, and when switching between CSDs and app area.
Fixes https://github.com/pop-os/libcosmic/issues/533.
improv(sctk): per-surface cursor position tracking
feat(sctk): support ShowWindowMenu
Make text wrap configurable
fix(core): state order and handling of new trees
fix: settings.decorations enables SSD
refactor(sctk): convert window actions
fix: enable the tokio feature for accesskit_unix
fix: only try to connect to clipboard if on linux
iced_wgpu: don't query Wayland on macos
Update `window_clipboard`
sctk: Unmap subsurfaces instead of immediately destroying them
Destroying a surface is immediate, rather than synchronized with
commits.
This fixes a flickering behavior with drag and drop in
cosmic-workspaces.
sctk: Add alpha setting to `Subsurface` widget
sctk: Update `sctk`, `wayland-protocols`
Update for cosmic-text undefined buffer size
Adapt to cosmic-text undefined width change
fix: unset VK_LOADER_DRIVERS_DISABLE after enumeration
Allows applications to be launched on the NVIDIA GPU with Vulkan support
Adapt to new cosmic-text
wgpu: Fix wayland device id conversion
wgpu: Fix querying adapter, even if we already have one
wgpu: fix nvidia gpu powering up in hybrid setups
cargo fmt
fix: refactor dnd impl to support responsive widget
fix: update read and write methods so they don't recurse
fix(core): replace debug_assert in diff
fix: avoid with_borrow_mut
fix: better handling of state tree
This persists widget state associated with widgets assigned custom IDs even when the tree structure changes, but resets state if the custom ID is not found.
fix: emit Event::Resized to fix nav bar in cosmic-settings
fix(image): guess the image format before decoding
iced_wgpu: Query wayland for the device to use, if possible
sctk: Support `start_drag` with drags started from touch events
sctk: Add touch support
fix: update widnow-clipboard tag
fix: clean up dnd surfaces when a window is removed
Adjust to line ending needing to be specified as part of cosmic_text::BufferLine
sctk: Add support for drag-and-drop surface offsets
This adds an offset `Vector` as an argument to `on_drag`, and allows
passing an offset to `start_drag`.
Some applications using drag and drop want the top left corner of the
drag surface (as happens without an offset). But others want the drag
surface to be offset based on where the cursor is on the widget when
starting the drag. This can just be `-1 * offset`, but may be scaled if
the drag surface is a different size from the original widget.
fix(sctk): nested popup parent
feat(mouseare): mouse enter and exit
fix(tiny_skia): damage
fix(scrollable): filter scroll events in the wrong direction
sctk: Use empty input region for subsurfaces
This seems to work, and is a better way to deal with subsurface input if
there aren't any problems. This way, input events simply go to the
parent surface, so we don't have to deal with various edge cases related
to that. (Though for compositor-side issues, we still need to fix those
for other clients.)
This helps with an issue with drag-and-drop and subsurfaces on Smithay,
and a different issue on Kwin (in KDE 5.27, at least).
Send `DataSource` events to all surfaces
Previously these events are directed to the first surface, then removed
from `sctk_events`. Which is definitely not right.
slider & toggler roundness
Update window_clipboard to pop-dnd-4
fix(tiny-skia): non-simple border scaling
the issue can be seen with sharp corners when using the screenshot portal with scaling
Add read_primary/write_primary
chore: update tag
fix: translate offer positions in scrollable
fix(winit multi-window): handle exit_on_close request
fix(scrollable): pass child layout when calculating drag destinations
fix(container): id and set_id should use content
Clean up after lock surfaces are destroyed
Call unlock on session lock
chore: update tag
fixes for dnd
sctk: Fix handling of DnD with subsurfaces (#122)
Map subsurface to parent and add offset.
refactor: remove Sync bound for Message
fix: pass correct state and layout for container widgets
fix: docs
feat: update advertised drag destinations after rebuilding an interface
fix: color format & multi-window
fix: doc
feat: winit dnd
fix: ambiguous import
chore: reexport mime from window_clipboard
chore: use tag
clippy
feat: add actions and commands for new clipboard methods
cleanup docs
feat: custom mime types for Clipboard
sctk: Fix handling of layer surface `pointer_interactivity` (#115)
A null `region` represents an infinite region (the default). To set an
empty region, we need to create a `wl_region`.
fix(tiny_skia): disable shadows due to rendering glitch
fix(winit): add static lifetimes to multi-window application update
fix(winit): add static lifetimes to application update
Use `TypeId` to identify `subscription::Map`
(cherry picked from commit f39a5fd895)
fix(sctk): destroy drag icon and send event after cancel action
fix: clipboard cleanup
fix(sctk): clipboard dummy impl typo
refactor(sctk): optional clipboard
fix(sctk): broadcast events after update
when broadcasting events for no specific surface, it should be done after update so that the runtime subscription is current
fix(multi_window): enable drag resize
sctk: Map subsurface pointer events to parent surface, with offset
sctk_subsurface: Use two surfaces, handle button presses
Useful for testing pointer input to subsurfaces.
sctk: Add `subsurface_ids` mapping subsurface to parent and offset
sctk_subsurface_gst: NV12 surface suppport; disabled
Whether or not this works seems to depend on driver, or gstreamer
version...
Handle frame callbacks for subsurfaces, and `commit` parent surface
If the main surface is occluded completely by opaque subsurfaces, it may
not receive `frame` events. So we need to request frame events for all
subsurfaces as well.
Additionally, with "synchronized" subsurfaces, we need to `commit` the
parent surface for subsurface changes to take effect.
Fixes issues with subsurfaces updating slowly, or only when mouse moved
under some circumstances.
examples/sctk_subsurface_gst: Cache `BufferSource` in `BufferRef` qdata
Similar to `waylandsink`. Allows us to avoid creating a buffer source
(and ultimately `wl_buffer`) for every buffer swap.
sctk/subsurface: Cache `wl_buffer`s
Creating a new `wl_buffer` each frame seems to perform poorly. We can
instead keep a cache of `wl_buffer`s we have created from a
`BufferSource`.
sctk/subsurface: Avoid unnecessary subsurface commits if unchanged
feat(slider): add breakpoints
fix: autosize surface layout
Autosized surfaces perform the layout step to get the size and then again when building the interface, but sometimes the calculated size is not enough space when used as a bound, so we need to add a tiny amount to the calculated size. This also makes the event loop timeout duration configurable. Viewport physical size is calculated directly from the logical size now as well in iced-sctk to avoid inconsistencies that resulted from recalculating the logical size after using it to calculate the physical size.
fix(sctk): send close event instead of close requested when a window is closed
sctk: add command to set maximize state
Add `show_window_menu` action
Winit currently supports this only on Windows and Wayland.
This requests that a context menu is shown at the cursor position,
like the menu normally triggered by right clicking the title bar. This
is important for implementing client side decorations with Iced widgets.
Remove unnecessary redraw request
This was particularly visible on Redox where there is no vsync, but also
causes unnecessary redraws on Linux
chore: update accesskit
Disable broken rustdoc links
sctk: Add `Subsurface` widget (#79)
This adds a widget that attaches an shm or dma buffer to a subsurface,
scaled with `wp_viewporter`.
By exposing this as a widget, rather than as a type of window, it can be
positioned and scaled like any other iced widget. It provides an API
that's similar to an iced image.
The initial version of this just took a `wl_buffer`. But this makes
buffer re-use problematic. In particular, the docs for `wl_surface::attach`
note that `wl_buffer::release` events become unreliable if a buffer is
attached to multiple surfaces. And indicates that a client should create
multiple `wl_buffer` instances, or use `wp_linux_buffer_release`.
So we store information about the buffer, and create `wl_buffer`s as
needed. `SubsurfaceBuffer::new` also returns a future that's signaled
when all references are destroyed, both `wl_buffer`s and any instance of
the `SubsurfaceBuffer` that might still be used in the `view`.
So this seems like the best solution for now, within the
model-view-update architecture.
This has two examples: `sctk_subsurface`, showing a single-color shm
buffer, and `sctk_subsurface_gst`, which plays an h264 video to a
subsurface with vaapi decoding.
chore: use pop-os fork of winit
chore: unpin cosmic-text
Update wgpu to a commit that fixes use on Nvidia drivers
This can be tested with something like
`VK_ICD_FILENAMES=/usr/share/vulkan/icd.d/nvidia_icd.json cargo run -p tour
--features iced/wgpu`.
On Nvidia I'm seeing a flood of `Suboptimal present of frame` warnings.
So some improvement may still be needed here. But if it doesn't regress
behavior on other hardware, that seems like an improvement over
freezing.
fix(winit): pass text with modifiers in event
chore: update cosmic-text and glyphon
fix: distinguish between the key character and the utf8 of a key event
feat(wgpu): use alpha modes for compositing if available
chore: use updated softbuffer
fix: typo
fix: downgrade resvg
fix: core/serde
chore: remove default features
typo: add rev to glyphon
Update to cosmic-text refactor
Fix docs error
Add function to fill a Raw
Fixes for last commit
fix: broadcast surface events
dnd_listener: Fix behavior when there are multiple listeners (#87)
A `dnd_listener` widget shouldn't handle a DnD event when the dnd drag
isn't within the widget's bounds. So add a few more checks for this.
Enter/leave events generated by `DndOfferEvent::Motion` also don't
behave as one might expect, since the enter may occur before the leave
depending on the order it calls `on_event` on the widget. Not sure how
to address that, but cosmic-workspaces can just ignore the leave events
for now.
Otherwise, this seems to be working fine, after these changes.
chore: fix sctk multi-window dependency
cleanup: formatting and clippy
fix(example): sctk_drag id
fix: translate the wayland event position for content inside a scrollable
fix: set web-sys to =0.3.64
fix: clip mask checks
chore: use advanced text shaping for pick list
fix: dnd widget layout
fix: ambiguous palette import
chore: remove artifacts job
fix: CI tests
fix: add back the window id to the frames subscription
fix: tooltip children and diff
refactor: udpate gradient angles for slider
reexport limits
fix: editor and sctk_todos examples
cleanup: clippy
cleanup git workflows
chore: cleanup iced_widget
refactor
Update mod.rs
chore: update softbuffer
Hack to remove image blur
iced_core: feature for serde serialization of KeyCode
fix(wgpu): handle border_radius property with image raster
feat: add border radius to image rendering
feat: Add side mouse button events
cleanup: clippy fixes and formatting
Part of this is a refactor of the ID
cleanup: clippy and fmt
fix: test workflow
fix: add note in CHANGELOG
fix: clippy
refactor: restore default style of slider
feat: allow setting the width and height of a rule
fix: slider gradient angle
feat: gradient backgground for the slider rail
feat(mouse-area): added on_drag method
fix(widget): container inherited wrong icon color from renderer
fix(button): inherit icon color if set to none
feat(renderer): define default icon color
By default, this is the same as the text color for best visibility.
feat(winit): client-side resize drag support
feat(winit): client-side resize drag support
Make vertical scroll properties optional
fix: quad rendering including border only inside of the bounds
Move `Screenshot` inside `window` module
Added offscreen rendering support for wgpu & tiny-skia exposed with the window::screenshot command.
Provide access to font from each crate
Use nested for lazy widgets
Use layout with children for nesting
Introduce internal `overlay::Nested` for `UserInterface`
fix: reset button state if the cursor leaves
runtime: Handle widget operations in `program::State` helper (#46)
chore: default line height, text size, and shaping for cosmic
feat: sctk shell
fix: quad rendering including border only inside of the bounds
fix: better slider drawing (it allows just the border part of the handle quad outside of the layout bouds, which isn't great, but is ok for our purposes due to being transparent)
cleanup: fix & format
fix: use iced_core::Font
cleanup
fix: allow leaving out winit & iced-sctk
fix: settings
fix: slider draw improvements
fix: websocket example
fix: modal example
fix: scrollable example
fix: toast example
fix: avoid panicking in iced_sctk with lazy widgets in auto-size surfaces
fix: todos panic
fix: only diff auto-sized surfaces in iced_sctk build_user_interface & improve sctk examples
wip (iced-sctk): window resize with icons
feat (iced-sctk): support for setting cursor
refactor: default decorations to client
fix: set window geometry after receiving configure
fix: size limits with no max bound must be cut off
fix: send size update when autosized surface resizes
fix: use ceil size for positioner
cleanup: remove dbg statement
fix: remove a destroyed surface from compositor surfaces
fix errors after rebase and wip scaling support
fix: handling of scale factor in set_logical_size
fix (sctk_drag example): add .into for border radius
fix: fractional scaling
sctk: Fire RedrawRequests
wip: animations via frame event
fix / refactor iced-sctk redraw & frame event handling
cleanup: note about frame request in iced-sctk
fix: send resize when necessary for layer surface and popups too
fix: always request redraw for a new surface
fix: scaling and autosize surface improvements
refactor: sctk_lazy keyboard interactivity
feat(sctk): configurable natural_scroll property
feat: send state and capabilities events when there are changes
fix: redraw when an update is needed and clean up the logic
Update sctk to latest commit
Fix compilation of sctk drag example
fix(sctk): update interface before checking if it has a redraw request
refactor: after autosize surface resize wait to redraw until the resize has been applied
refactor: better handling of autosize surfaces
chore: update sctk
chore: update sctk
fixes sctk_drag example
fix: default to ControlFlow::Wait for applications with no surface
this seems to help CPU usage for app library and launcher
default to 250ms timeout in the event loop
Update sctk
sctk: Implement xdg-activation support
fix: don't require Flags to be clone for settings on wayland
chore: error if neither winit or wayland feature is set
chore: Allow compiling without windowing system (#65)
fix(iced-sctk): handle exit_on_close_request
fix: make sure that each widget operation operates on every interface
This should be ok even for widget actions like focus next because there can only ever be a single focused widget
cargo fmt
cleanup: dbg statement
fix(iced-sctk): replace panic with handling for remaining enum variants
refactor: use iced clipboard for interacting with the selection
refactor: allow passing an activation token when creating a window
sctk: Add support for `ext-session-lock` protocol
fix(sctk): build and use tree for layout of autosize surfaces
Update winit to latest commit used by upstream iced
fix(sctk): send key characters
fix(sctk): check if key is a named key first
refactor(sctk): keep compositor surface in state
feat: accessibility with some widget impls
feat: stable ids
a11y: Don't unconditionally pull winit (#43)
Update conversion.rs
integration fixes
integration
integration
integration
integration
s
integration
some integration work
more integration
Update Cargo.toml
Update mod.rs
Update multi_window.rs
s
more integration
more integration (ryanabx wip #100000)
more integration!!
integration 2
integration
more integration (rbx)
s
integration
integration work
integration
Update Cargo.toml
integration
simple integration things
int
integration to 175
integration(170)
Co-Authored-By: Ashley Wulber <48420062+wash2@users.noreply.github.com>
Co-Authored-By: Victoria Brekenfeld <4404502+Drakulix@users.noreply.github.com>
Co-Authored-By: Eduardo Flores <edfloreshz@gmail.com>
Co-Authored-By: Michael Murphy <michael@mmurphy.dev>
Co-Authored-By: wiiznokes <78230769+wiiznokes@users.noreply.github.com>
Co-Authored-By: Jeremy Soller <jeremy@system76.com>
Co-Authored-By: Ryan Brue <56272643+ryanabx@users.noreply.github.com>
fix(column): handle keys len change
fix: iced-sctk a11y
wip: winit single window updates
tokio feature hangs even without a11y feature
fix: multiwindow a11y fixes
fix: component
update winit
wip: sctk integration to winit shell
refactor: remove accesskit_unix
fix: svg
fix: remove wayland default feature
feat: derive Hash for image Handle
fix: cleanup 0.13 rebase errors
fix: remove path dependencies
conversion for Radius
conversion for Padding
setter for Svg border radius
re-export Limits
fix: connect clipboard if disconnected on layer surface or popup creation
fix: connect clipboard if disconnected on session lock surface creation
fix: update size of layer surface after configure
fix: insert user interfaces for popup and lock surfaces on creation
fix: svg scaling
feat: popups on winit windows
fix: default text shaping to advanced
fix: fallback to renderer icon style if svg is symbolic
fixes
fix: sctk frame handling
feat: autosize handling
fix: better autosize handling
fix: avoid duplicate window events from sctk
fix: better handling of popups
fix: refactor redraw handling for sctk
fix: include id in frames
fix: image
fix: scrollable delta direction
sctk: unregister clipboard when surface is done
set min / max size when size is requested
fix: popups
filter pointer events
feat: add Hide variant to mouse Interaction
dnd fixes
fix: use physical width for DnD surface
fix: tiny-skia svg quality
refactor: peek_dnd try to parse data
cleanup text conversion
cleanup
svg scaling fixes
background color fix
Introduce consecutive_click_distance like other toolkits do such as gtk,qt,imgui.
This commit is contained in:
parent
595af03a9f
commit
08fe1f3aa5
233 changed files with 24391 additions and 1911 deletions
188
winit/src/platform_specific/mod.rs
Normal file
188
winit/src/platform_specific/mod.rs
Normal file
|
|
@ -0,0 +1,188 @@
|
|||
//! Wayland specific shell
|
||||
//!
|
||||
|
||||
use std::collections::HashMap;
|
||||
|
||||
use iced_graphics::{Compositor, compositor};
|
||||
use iced_runtime::{core::window, platform_specific, user_interface};
|
||||
#[cfg(all(feature = "wayland", target_os = "linux"))]
|
||||
use sctk::reexports::client::Connection;
|
||||
|
||||
#[cfg(all(feature = "wayland", target_os = "linux"))]
|
||||
pub mod wayland;
|
||||
|
||||
#[cfg(all(feature = "wayland", target_os = "linux"))]
|
||||
pub use wayland::*;
|
||||
#[cfg(all(feature = "wayland", target_os = "linux"))]
|
||||
use wayland_backend::client::Backend;
|
||||
|
||||
use crate::{Program, WindowManager};
|
||||
|
||||
pub type UserInterfaces<'a, P> = HashMap<
|
||||
window::Id,
|
||||
user_interface::UserInterface<
|
||||
'a,
|
||||
<P as Program>::Message,
|
||||
<P as Program>::Theme,
|
||||
<P as Program>::Renderer,
|
||||
>,
|
||||
rustc_hash::FxBuildHasher,
|
||||
>;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum Event {
|
||||
#[cfg(all(feature = "wayland", target_os = "linux"))]
|
||||
Wayland(sctk_event::SctkEvent),
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
|
||||
pub enum SurfaceIdWrapper {
|
||||
LayerSurface(window::Id),
|
||||
Window(window::Id),
|
||||
Popup(window::Id),
|
||||
SessionLock(window::Id),
|
||||
}
|
||||
impl SurfaceIdWrapper {
|
||||
pub fn inner(&self) -> window::Id {
|
||||
match self {
|
||||
SurfaceIdWrapper::LayerSurface(id) => *id,
|
||||
SurfaceIdWrapper::Window(id) => *id,
|
||||
SurfaceIdWrapper::Popup(id) => *id,
|
||||
SurfaceIdWrapper::SessionLock(id) => *id,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Default)]
|
||||
pub struct PlatformSpecific {
|
||||
#[cfg(all(feature = "wayland", target_os = "linux"))]
|
||||
wayland: WaylandSpecific,
|
||||
}
|
||||
|
||||
impl PlatformSpecific {
|
||||
pub(crate) fn send_action(
|
||||
&mut self,
|
||||
action: iced_runtime::platform_specific::Action,
|
||||
) {
|
||||
match action {
|
||||
#[cfg(all(feature = "wayland", target_os = "linux"))]
|
||||
platform_specific::Action::Wayland(a) => {
|
||||
self.send_wayland(wayland::Action::Action(a));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn send_ready(&mut self) {
|
||||
#[cfg(all(feature = "wayland", target_os = "linux"))]
|
||||
{
|
||||
self.send_wayland(wayland::Action::Ready);
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn update_subsurfaces(
|
||||
&mut self,
|
||||
id: window::Id,
|
||||
window: &dyn winit::window::Window,
|
||||
) {
|
||||
#[cfg(all(feature = "wayland", target_os = "linux"))]
|
||||
{
|
||||
use sctk::reexports::client::{
|
||||
Proxy, protocol::wl_surface::WlSurface,
|
||||
};
|
||||
use wayland_backend::client::ObjectId;
|
||||
|
||||
let Ok(backend) = window.rwh_06_display_handle().display_handle()
|
||||
else {
|
||||
log::error!("No display handle");
|
||||
return;
|
||||
};
|
||||
|
||||
let conn = match backend.as_raw() {
|
||||
raw_window_handle::RawDisplayHandle::Wayland(
|
||||
wayland_display_handle,
|
||||
) => {
|
||||
let backend = unsafe {
|
||||
Backend::from_foreign_display(
|
||||
wayland_display_handle.display.as_ptr().cast(),
|
||||
)
|
||||
};
|
||||
Connection::from_backend(backend)
|
||||
}
|
||||
_ => {
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
let Ok(raw) = window.rwh_06_window_handle().window_handle() else {
|
||||
log::error!("Invalid window handle {id:?}");
|
||||
return;
|
||||
};
|
||||
let wl_surface = match raw.as_raw() {
|
||||
raw_window_handle::RawWindowHandle::Wayland(
|
||||
wayland_window_handle,
|
||||
) => {
|
||||
let res = unsafe {
|
||||
ObjectId::from_ptr(
|
||||
WlSurface::interface(),
|
||||
wayland_window_handle.surface.as_ptr().cast(),
|
||||
)
|
||||
};
|
||||
let Ok(id) = res else {
|
||||
log::error!(
|
||||
"Could not create WlSurface Id from window"
|
||||
);
|
||||
return;
|
||||
};
|
||||
let Ok(surface) = WlSurface::from_id(&conn, id) else {
|
||||
log::error!("Could not create WlSurface from Id");
|
||||
return;
|
||||
};
|
||||
surface
|
||||
}
|
||||
|
||||
_ => {
|
||||
log::error!("Unexpected window handle type");
|
||||
return;
|
||||
}
|
||||
};
|
||||
self.wayland.update_subsurfaces(id, &wl_surface);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn handle_event<'a, P>(
|
||||
e: Event,
|
||||
events: &mut Vec<(Option<window::Id>, iced_runtime::core::Event)>,
|
||||
platform_specific: &mut PlatformSpecific,
|
||||
program: &'a crate::program::Instance<P>,
|
||||
compositor: &mut <<P as Program>::Renderer as compositor::Default>::Compositor,
|
||||
window_manager: &mut WindowManager<
|
||||
P,
|
||||
<<P as Program>::Renderer as compositor::Default>::Compositor,
|
||||
>,
|
||||
user_interfaces: &mut UserInterfaces<'a, P>,
|
||||
clipboard: &mut crate::Clipboard,
|
||||
#[cfg(feature = "a11y")] adapters: &mut std::collections::HashMap<
|
||||
window::Id,
|
||||
(u64, iced_accessibility::accesskit_winit::Adapter),
|
||||
>,
|
||||
) where
|
||||
P: Program,
|
||||
{
|
||||
match e {
|
||||
#[cfg(all(feature = "wayland", target_os = "linux"))]
|
||||
Event::Wayland(e) => {
|
||||
platform_specific.wayland.handle_event(
|
||||
e,
|
||||
events,
|
||||
program,
|
||||
compositor,
|
||||
window_manager,
|
||||
user_interfaces,
|
||||
clipboard,
|
||||
#[cfg(feature = "a11y")]
|
||||
adapters,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
31
winit/src/platform_specific/wayland/commands/activation.rs
Normal file
31
winit/src/platform_specific/wayland/commands/activation.rs
Normal file
|
|
@ -0,0 +1,31 @@
|
|||
use crate::core::window::Id as SurfaceId;
|
||||
use iced_runtime::{
|
||||
self,
|
||||
platform_specific::{self, wayland},
|
||||
task, Action, Task,
|
||||
};
|
||||
|
||||
pub fn request_token(
|
||||
app_id: Option<String>,
|
||||
window: Option<SurfaceId>,
|
||||
) -> Task<Option<String>> {
|
||||
task::oneshot(|channel| {
|
||||
Action::PlatformSpecific(platform_specific::Action::Wayland(
|
||||
wayland::Action::Activation(
|
||||
wayland::activation::Action::RequestToken {
|
||||
app_id,
|
||||
window,
|
||||
channel,
|
||||
},
|
||||
),
|
||||
))
|
||||
})
|
||||
}
|
||||
|
||||
pub fn activate<Message>(window: SurfaceId, token: String) -> Task<Message> {
|
||||
task::effect(Action::PlatformSpecific(
|
||||
platform_specific::Action::Wayland(wayland::Action::Activation(
|
||||
wayland::activation::Action::Activate { window, token },
|
||||
)),
|
||||
))
|
||||
}
|
||||
116
winit/src/platform_specific/wayland/commands/layer_surface.rs
Normal file
116
winit/src/platform_specific/wayland/commands/layer_surface.rs
Normal file
|
|
@ -0,0 +1,116 @@
|
|||
//! Interact with the window of your application.
|
||||
|
||||
use crate::core::window::Id as SurfaceId;
|
||||
use iced_runtime::{
|
||||
self,
|
||||
platform_specific::{
|
||||
self,
|
||||
wayland::{
|
||||
self,
|
||||
layer_surface::{IcedMargin, SctkLayerSurfaceSettings},
|
||||
},
|
||||
},
|
||||
task, Action, Task,
|
||||
};
|
||||
|
||||
pub use sctk::shell::wlr_layer::{Anchor, KeyboardInteractivity, Layer};
|
||||
|
||||
// TODO ASHLEY: maybe implement as builder that outputs a batched commands
|
||||
/// <https://wayland.app/protocols/wlr-layer-shell-unstable-v1#zwlr_layer_shell_v1:request:get_layer_surface>
|
||||
pub fn get_layer_surface<Message>(
|
||||
builder: SctkLayerSurfaceSettings,
|
||||
) -> Task<Message> {
|
||||
task::effect(Action::PlatformSpecific(
|
||||
platform_specific::Action::Wayland(wayland::Action::LayerSurface(
|
||||
wayland::layer_surface::Action::LayerSurface { builder },
|
||||
)),
|
||||
))
|
||||
}
|
||||
|
||||
/// <https://wayland.app/protocols/wlr-layer-shell-unstable-v1#zwlr_layer_surface_v1:request:destroy>
|
||||
pub fn destroy_layer_surface<Message>(id: SurfaceId) -> Task<Message> {
|
||||
task::effect(Action::PlatformSpecific(
|
||||
platform_specific::Action::Wayland(wayland::Action::LayerSurface(
|
||||
wayland::layer_surface::Action::Destroy(id),
|
||||
)),
|
||||
))
|
||||
}
|
||||
|
||||
/// <https://wayland.app/protocols/wlr-layer-shell-unstable-v1#zwlr_layer_surface_v1:request:set_size>
|
||||
pub fn set_size<Message>(
|
||||
id: SurfaceId,
|
||||
width: Option<u32>,
|
||||
height: Option<u32>,
|
||||
) -> Task<Message> {
|
||||
task::effect(Action::PlatformSpecific(
|
||||
platform_specific::Action::Wayland(wayland::Action::LayerSurface(
|
||||
wayland::layer_surface::Action::Size { id, width, height },
|
||||
)),
|
||||
))
|
||||
}
|
||||
/// <https://wayland.app/protocols/wlr-layer-shell-unstable-v1#zwlr_layer_surface_v1:request:set_anchor>
|
||||
pub fn set_anchor<Message>(id: SurfaceId, anchor: Anchor) -> Task<Message> {
|
||||
task::effect(Action::PlatformSpecific(
|
||||
platform_specific::Action::Wayland(wayland::Action::LayerSurface(
|
||||
wayland::layer_surface::Action::Anchor { id, anchor },
|
||||
)),
|
||||
))
|
||||
}
|
||||
/// <https://wayland.app/protocols/wlr-layer-shell-unstable-v1#zwlr_layer_surface_v1:request:set_exclusive_zone>
|
||||
pub fn set_exclusive_zone<Message>(id: SurfaceId, zone: i32) -> Task<Message> {
|
||||
task::effect(Action::PlatformSpecific(
|
||||
platform_specific::Action::Wayland(wayland::Action::LayerSurface(
|
||||
wayland::layer_surface::Action::ExclusiveZone {
|
||||
id,
|
||||
exclusive_zone: zone,
|
||||
},
|
||||
)),
|
||||
))
|
||||
}
|
||||
|
||||
/// <https://wayland.app/protocols/wlr-layer-shell-unstable-v1#zwlr_layer_surface_v1:request:set_margin>
|
||||
pub fn set_margin<Message>(
|
||||
id: SurfaceId,
|
||||
top: i32,
|
||||
right: i32,
|
||||
bottom: i32,
|
||||
left: i32,
|
||||
) -> Task<Message> {
|
||||
task::effect(Action::PlatformSpecific(
|
||||
platform_specific::Action::Wayland(wayland::Action::LayerSurface(
|
||||
wayland::layer_surface::Action::Margin {
|
||||
id,
|
||||
margin: IcedMargin {
|
||||
top,
|
||||
right,
|
||||
bottom,
|
||||
left,
|
||||
},
|
||||
},
|
||||
)),
|
||||
))
|
||||
}
|
||||
|
||||
/// <https://wayland.app/protocols/wlr-layer-shell-unstable-v1#zwlr_layer_surface_v1:request:set_keyboard_interactivity>
|
||||
pub fn set_keyboard_interactivity<Message>(
|
||||
id: SurfaceId,
|
||||
keyboard_interactivity: KeyboardInteractivity,
|
||||
) -> Task<Message> {
|
||||
task::effect(Action::PlatformSpecific(
|
||||
platform_specific::Action::Wayland(wayland::Action::LayerSurface(
|
||||
wayland::layer_surface::Action::KeyboardInteractivity {
|
||||
id,
|
||||
keyboard_interactivity,
|
||||
},
|
||||
)),
|
||||
))
|
||||
}
|
||||
|
||||
/// <https://wayland.app/protocols/wlr-layer-shell-unstable-v1#zwlr_layer_surface_v1:request:set_layer>
|
||||
pub fn set_layer<Message>(id: SurfaceId, layer: Layer) -> Task<Message> {
|
||||
task::effect(Action::PlatformSpecific(
|
||||
platform_specific::Action::Wayland(wayland::Action::LayerSurface(
|
||||
wayland::layer_surface::Action::Layer { id, layer },
|
||||
)),
|
||||
))
|
||||
}
|
||||
6
winit/src/platform_specific/wayland/commands/mod.rs
Normal file
6
winit/src/platform_specific/wayland/commands/mod.rs
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
//! Interact with the wayland objects of your application.
|
||||
|
||||
pub mod activation;
|
||||
pub mod layer_surface;
|
||||
pub mod popup;
|
||||
pub mod session_lock;
|
||||
42
winit/src/platform_specific/wayland/commands/popup.rs
Normal file
42
winit/src/platform_specific/wayland/commands/popup.rs
Normal file
|
|
@ -0,0 +1,42 @@
|
|||
//! Interact with the popups of your application.
|
||||
use crate::core::window::Id as SurfaceId;
|
||||
use iced_runtime::{
|
||||
self,
|
||||
platform_specific::{
|
||||
self,
|
||||
wayland::{self, popup::SctkPopupSettings},
|
||||
},
|
||||
task, Action, Task,
|
||||
};
|
||||
|
||||
/// <https://wayland.app/protocols/wlr-layer-shell-unstable-v1#zwlr_layer_surface_v1:request:get_popup>
|
||||
/// <https://wayland.app/protocols/xdg-shell#xdg_surface:request:get_popup>
|
||||
pub fn get_popup<Message>(popup: SctkPopupSettings) -> Task<Message> {
|
||||
task::effect(Action::PlatformSpecific(
|
||||
platform_specific::Action::Wayland(wayland::Action::Popup(
|
||||
wayland::popup::Action::Popup { popup },
|
||||
)),
|
||||
))
|
||||
}
|
||||
|
||||
/// <https://wayland.app/protocols/xdg-shell#xdg_popup:request:reposition>
|
||||
pub fn set_size<Message>(
|
||||
id: SurfaceId,
|
||||
width: u32,
|
||||
height: u32,
|
||||
) -> Task<Message> {
|
||||
task::effect(Action::PlatformSpecific(
|
||||
platform_specific::Action::Wayland(wayland::Action::Popup(
|
||||
wayland::popup::Action::Size { id, width, height },
|
||||
)),
|
||||
))
|
||||
}
|
||||
|
||||
/// <https://wayland.app/protocols/xdg-shell#xdg_popup:request:destroy>
|
||||
pub fn destroy_popup<Message>(id: SurfaceId) -> Task<Message> {
|
||||
task::effect(Action::PlatformSpecific(
|
||||
platform_specific::Action::Wayland(wayland::Action::Popup(
|
||||
wayland::popup::Action::Destroy { id },
|
||||
)),
|
||||
))
|
||||
}
|
||||
42
winit/src/platform_specific/wayland/commands/session_lock.rs
Normal file
42
winit/src/platform_specific/wayland/commands/session_lock.rs
Normal file
|
|
@ -0,0 +1,42 @@
|
|||
use crate::core::window::Id as SurfaceId;
|
||||
use iced_runtime::{
|
||||
self,
|
||||
platform_specific::{self, wayland},
|
||||
task, Action, Task,
|
||||
};
|
||||
use sctk::reexports::client::protocol::wl_output::WlOutput;
|
||||
|
||||
pub fn lock<Message>() -> Task<Message> {
|
||||
task::effect(Action::PlatformSpecific(
|
||||
platform_specific::Action::Wayland(wayland::Action::SessionLock(
|
||||
wayland::session_lock::Action::Lock,
|
||||
)),
|
||||
))
|
||||
}
|
||||
|
||||
pub fn unlock<Message>() -> Task<Message> {
|
||||
task::effect(Action::PlatformSpecific(
|
||||
platform_specific::Action::Wayland(wayland::Action::SessionLock(
|
||||
wayland::session_lock::Action::Unlock,
|
||||
)),
|
||||
))
|
||||
}
|
||||
|
||||
pub fn get_lock_surface<Message>(
|
||||
id: SurfaceId,
|
||||
output: WlOutput,
|
||||
) -> Task<Message> {
|
||||
task::effect(Action::PlatformSpecific(
|
||||
platform_specific::Action::Wayland(wayland::Action::SessionLock(
|
||||
wayland::session_lock::Action::LockSurface { id, output },
|
||||
)),
|
||||
))
|
||||
}
|
||||
|
||||
pub fn destroy_lock_surface<Message>(id: SurfaceId) -> Task<Message> {
|
||||
task::effect(Action::PlatformSpecific(
|
||||
platform_specific::Action::Wayland(wayland::Action::SessionLock(
|
||||
wayland::session_lock::Action::DestroyLockSurface { id },
|
||||
)),
|
||||
))
|
||||
}
|
||||
120
winit/src/platform_specific/wayland/conversion.rs
Normal file
120
winit/src/platform_specific/wayland/conversion.rs
Normal file
|
|
@ -0,0 +1,120 @@
|
|||
use iced_runtime::core::{
|
||||
keyboard,
|
||||
mouse::{self, ScrollDelta},
|
||||
};
|
||||
use sctk::{
|
||||
reexports::client::protocol::wl_pointer::AxisSource,
|
||||
seat::{
|
||||
keyboard::Modifiers,
|
||||
pointer::{
|
||||
AxisScroll, BTN_EXTRA, BTN_LEFT, BTN_MIDDLE, BTN_RIGHT,
|
||||
BTN_SIDE,
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
/// An error that occurred while running an application.
|
||||
#[derive(Debug, thiserror::Error)]
|
||||
#[error("the futures executor could not be created")]
|
||||
pub struct KeyCodeError(u32);
|
||||
|
||||
pub fn pointer_button_to_native(button: u32) -> Option<mouse::Button> {
|
||||
if button == BTN_LEFT {
|
||||
Some(mouse::Button::Left)
|
||||
} else if button == BTN_RIGHT {
|
||||
Some(mouse::Button::Right)
|
||||
} else if button == BTN_MIDDLE {
|
||||
Some(mouse::Button::Middle)
|
||||
} else if button == BTN_SIDE {
|
||||
Some(mouse::Button::Back)
|
||||
} else if button == BTN_EXTRA {
|
||||
Some(mouse::Button::Forward)
|
||||
} else {
|
||||
button.try_into().ok().map(mouse::Button::Other)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn pointer_axis_to_native(
|
||||
source: Option<AxisSource>,
|
||||
horizontal: AxisScroll,
|
||||
vertical: AxisScroll,
|
||||
) -> Option<ScrollDelta> {
|
||||
source.map(|source| match source {
|
||||
AxisSource::Wheel | AxisSource::WheelTilt => ScrollDelta::Lines {
|
||||
x: -1. * horizontal.discrete as f32,
|
||||
y: -1. * vertical.discrete as f32,
|
||||
},
|
||||
_ => ScrollDelta::Pixels {
|
||||
x: -1. * horizontal.absolute as f32,
|
||||
y: -1. * vertical.absolute as f32,
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
pub fn modifiers_to_native(mods: Modifiers) -> keyboard::Modifiers {
|
||||
let mut native_mods = keyboard::Modifiers::empty();
|
||||
if mods.alt {
|
||||
native_mods = native_mods.union(keyboard::Modifiers::ALT);
|
||||
}
|
||||
if mods.ctrl {
|
||||
native_mods = native_mods.union(keyboard::Modifiers::CTRL);
|
||||
}
|
||||
if mods.logo {
|
||||
native_mods = native_mods.union(keyboard::Modifiers::LOGO);
|
||||
}
|
||||
if mods.shift {
|
||||
native_mods = native_mods.union(keyboard::Modifiers::SHIFT);
|
||||
}
|
||||
// TODO Ashley: missing modifiers as platform specific additions?
|
||||
// if mods.caps_lock {
|
||||
// native_mods = native_mods.union(keyboard::Modifier);
|
||||
// }
|
||||
// if mods.num_lock {
|
||||
// native_mods = native_mods.union(keyboard::Modifiers::);
|
||||
// }
|
||||
native_mods
|
||||
}
|
||||
|
||||
// pub fn keysym_to_vkey(keysym: RawKeysym) -> Option<KeyCode> {
|
||||
// key_conversion.get(&keysym).cloned()
|
||||
// }
|
||||
|
||||
// pub(crate) fn cursor_icon(cursor: winit::window::CursorIcon) -> CursorIcon {
|
||||
// match cursor {
|
||||
// CursorIcon::Default => todo!(),
|
||||
// CursorIcon::ContextMenu => todo!(),
|
||||
// CursorIcon::Help => todo!(),
|
||||
// CursorIcon::Pointer => todo!(),
|
||||
// CursorIcon::Progress => todo!(),
|
||||
// CursorIcon::Wait => todo!(),
|
||||
// CursorIcon::Cell => todo!(),
|
||||
// CursorIcon::Crosshair => todo!(),
|
||||
// CursorIcon::Text => todo!(),
|
||||
// CursorIcon::VerticalText => todo!(),
|
||||
// CursorIcon::Alias => todo!(),
|
||||
// CursorIcon::Copy => todo!(),
|
||||
// CursorIcon::Move => todo!(),
|
||||
// CursorIcon::NoDrop => todo!(),
|
||||
// CursorIcon::NotAllowed => todo!(),
|
||||
// CursorIcon::Grab => todo!(),
|
||||
// CursorIcon::Grabbing => todo!(),
|
||||
// CursorIcon::EResize => todo!(),
|
||||
// CursorIcon::NResize => todo!(),
|
||||
// CursorIcon::NeResize => todo!(),
|
||||
// CursorIcon::NwResize => todo!(),
|
||||
// CursorIcon::SResize => todo!(),
|
||||
// CursorIcon::SeResize => todo!(),
|
||||
// CursorIcon::SwResize => todo!(),
|
||||
// CursorIcon::WResize => todo!(),
|
||||
// CursorIcon::EwResize => todo!(),
|
||||
// CursorIcon::NsResize => todo!(),
|
||||
// CursorIcon::NeswResize => todo!(),
|
||||
// CursorIcon::NwseResize => todo!(),
|
||||
// CursorIcon::ColResize => todo!(),
|
||||
// CursorIcon::RowResize => todo!(),
|
||||
// CursorIcon::AllScroll => todo!(),
|
||||
// CursorIcon::ZoomIn => todo!(),
|
||||
// CursorIcon::ZoomOut => todo!(),
|
||||
// _ => todo!(),
|
||||
// }
|
||||
// }
|
||||
|
|
@ -0,0 +1,56 @@
|
|||
/// Set by the user callback given to the [`EventLoop::run`] method.
|
||||
///
|
||||
/// Indicates the desired behavior of the event loop after [`Event::RedrawEventsCleared`] is emitted.
|
||||
///
|
||||
/// Defaults to [`Poll`].
|
||||
///
|
||||
/// ## Persistency
|
||||
///
|
||||
/// Almost every change is persistent between multiple calls to the event loop closure within a
|
||||
/// given run loop. The only exception to this is [`ExitWithCode`] which, once set, cannot be unset.
|
||||
/// Changes are **not** persistent between multiple calls to `run_return` - issuing a new call will
|
||||
/// reset the control flow to [`Poll`].
|
||||
///
|
||||
/// [`ExitWithCode`]: Self::ExitWithCode
|
||||
/// [`Poll`]: Self::Poll
|
||||
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
|
||||
pub enum ControlFlow {
|
||||
/// When the current loop iteration finishes, immediately begin a new iteration regardless of
|
||||
/// whether or not new events are available to process.
|
||||
///
|
||||
/// ## Platform-specific
|
||||
///
|
||||
/// - **Web:** Events are queued and usually sent when `requestAnimationFrame` fires but sometimes
|
||||
/// the events in the queue may be sent before the next `requestAnimationFrame` callback, for
|
||||
/// example when the scaling of the page has changed. This should be treated as an implementation
|
||||
/// detail which should not be relied on.
|
||||
Poll,
|
||||
/// When the current loop iteration finishes, suspend the thread until another event arrives.
|
||||
Wait,
|
||||
/// When the current loop iteration finishes, suspend the thread until either another event
|
||||
/// arrives or the given time is reached.
|
||||
///
|
||||
/// Useful for implementing efficient timers. Applications which want to render at the display's
|
||||
/// native refresh rate should instead use [`Poll`] and the VSync functionality of a graphics API
|
||||
/// to reduce odds of missed frames.
|
||||
///
|
||||
/// [`Poll`]: Self::Poll
|
||||
WaitUntil(std::time::Instant),
|
||||
/// Send a [`LoopDestroyed`] event and stop the event loop. This variant is *sticky* - once set,
|
||||
/// `control_flow` cannot be changed from `ExitWithCode`, and any future attempts to do so will
|
||||
/// result in the `control_flow` parameter being reset to `ExitWithCode`.
|
||||
///
|
||||
/// The contained number will be used as exit code. The [`Exit`] constant is a shortcut for this
|
||||
/// with exit code 0.
|
||||
///
|
||||
/// ## Platform-specific
|
||||
///
|
||||
/// - **Android / iOS / WASM:** The supplied exit code is unused.
|
||||
/// - **Unix:** On most Unix-like platforms, only the 8 least significant bits will be used,
|
||||
/// which can cause surprises with negative exit values (`-42` would end up as `214`). See
|
||||
/// [`std::process::exit`].
|
||||
///
|
||||
/// [`LoopDestroyed`]: Event::LoopDestroyed
|
||||
/// [`Exit`]: ControlFlow::Exit
|
||||
ExitWithCode(i32),
|
||||
}
|
||||
387
winit/src/platform_specific/wayland/event_loop/mod.rs
Normal file
387
winit/src/platform_specific/wayland/event_loop/mod.rs
Normal file
|
|
@ -0,0 +1,387 @@
|
|||
pub mod control_flow;
|
||||
pub mod proxy;
|
||||
pub mod state;
|
||||
|
||||
#[cfg(feature = "a11y")]
|
||||
use crate::platform_specific::SurfaceIdWrapper;
|
||||
use crate::{
|
||||
Control,
|
||||
futures::futures::channel::mpsc,
|
||||
platform_specific::wayland::{
|
||||
handlers::{
|
||||
wp_fractional_scaling::FractionalScalingManager,
|
||||
wp_viewporter::ViewporterState,
|
||||
},
|
||||
sctk_event::SctkEvent,
|
||||
},
|
||||
subsurface_widget::SubsurfaceState,
|
||||
};
|
||||
|
||||
use raw_window_handle::HasDisplayHandle;
|
||||
use sctk::reexports::{
|
||||
calloop_wayland_source::WaylandSource, client::protocol::wl_subcompositor,
|
||||
};
|
||||
use sctk::{
|
||||
activation::ActivationState,
|
||||
compositor::CompositorState,
|
||||
globals::GlobalData,
|
||||
output::OutputState,
|
||||
reexports::{
|
||||
calloop::{self, EventLoop},
|
||||
client::{
|
||||
ConnectError, Connection, Proxy, globals::registry_queue_init,
|
||||
},
|
||||
},
|
||||
registry::RegistryState,
|
||||
seat::SeatState,
|
||||
session_lock::SessionLockState,
|
||||
shell::{WaylandSurface, wlr_layer::LayerShell, xdg::XdgShell},
|
||||
shm::Shm,
|
||||
};
|
||||
use state::{FrameStatus, SctkWindow};
|
||||
#[cfg(feature = "a11y")]
|
||||
use std::sync::{Arc, Mutex};
|
||||
use std::{
|
||||
collections::{HashMap, HashSet},
|
||||
fmt::Debug,
|
||||
};
|
||||
use tracing::error;
|
||||
use wayland_backend::client::Backend;
|
||||
use winit::event_loop::OwnedDisplayHandle;
|
||||
|
||||
use self::state::SctkState;
|
||||
|
||||
#[derive(Debug, Default, Clone, Copy)]
|
||||
pub struct Features {
|
||||
// TODO
|
||||
}
|
||||
|
||||
pub struct SctkEventLoop {
|
||||
pub(crate) event_loop: EventLoop<'static, SctkState>,
|
||||
pub(crate) _features: Features,
|
||||
pub(crate) state: SctkState,
|
||||
}
|
||||
|
||||
impl SctkEventLoop {
|
||||
pub(crate) fn new(
|
||||
winit_event_sender: mpsc::UnboundedSender<Control>,
|
||||
proxy: winit::event_loop::EventLoopProxy,
|
||||
display: OwnedDisplayHandle,
|
||||
) -> Result<
|
||||
calloop::channel::Sender<super::Action>,
|
||||
Box<dyn std::any::Any + std::marker::Send>,
|
||||
> {
|
||||
let Ok(dh) = display.display_handle() else {
|
||||
log::error!("Failed to get display handle");
|
||||
return Err(Box::new(()));
|
||||
};
|
||||
let raw_window_handle::RawDisplayHandle::Wayland(wayland_dh) =
|
||||
dh.as_raw()
|
||||
else {
|
||||
panic!("Invalid wayland display handle.");
|
||||
};
|
||||
|
||||
let backend = unsafe {
|
||||
Backend::from_foreign_display(wayland_dh.display.as_ptr().cast())
|
||||
};
|
||||
let connection = Connection::from_backend(backend);
|
||||
|
||||
let (action_tx, action_rx) = calloop::channel::channel();
|
||||
let res = std::thread::spawn(move || {
|
||||
let _display = connection.display();
|
||||
let (globals, event_queue) =
|
||||
registry_queue_init(&connection).unwrap();
|
||||
let event_loop =
|
||||
calloop::EventLoop::<SctkState>::try_new().unwrap();
|
||||
let loop_handle = event_loop.handle();
|
||||
|
||||
let qh = event_queue.handle();
|
||||
let registry_state = RegistryState::new(&globals);
|
||||
|
||||
_ = loop_handle
|
||||
.insert_source(action_rx, |event, _, state| {
|
||||
match event {
|
||||
calloop::channel::Event::Msg(e) => match e {
|
||||
crate::platform_specific::Action::Action(a) => {
|
||||
if let Err(err) = state.handle_action(a) {
|
||||
log::warn!("{err:?}");
|
||||
}
|
||||
}
|
||||
crate::platform_specific::Action::TrackWindow(
|
||||
window,
|
||||
id,
|
||||
) => {
|
||||
state.windows.push(SctkWindow { window, id });
|
||||
}
|
||||
crate::platform_specific::Action::RemoveWindow(id) => {
|
||||
// TODO clean up popups matching the window.
|
||||
state.windows.retain(|window| id != window.id);
|
||||
}
|
||||
crate::platform_specific::Action::SetCursor(icon) => {
|
||||
if let Some(seat) = state.seats.get_mut(0) {
|
||||
seat.icon = Some(icon);
|
||||
seat.set_cursor(&state.connection, icon);
|
||||
}
|
||||
}
|
||||
crate::platform_specific::Action::RequestRedraw(id) => {
|
||||
let e = state.frame_status.entry(id).or_insert(FrameStatus::RequestedRedraw);
|
||||
if matches!(e, FrameStatus::Received) {
|
||||
*e = FrameStatus::Ready;
|
||||
}
|
||||
}
|
||||
crate::platform_specific::Action::PrePresentNotify(
|
||||
_,
|
||||
) => {
|
||||
// TODO
|
||||
}
|
||||
crate::platform_specific::Action::Ready => {
|
||||
state.ready = true;
|
||||
}
|
||||
crate::platform_specific::Action::Dropped(id) => {
|
||||
_ = state.destroyed.remove(&id.inner());
|
||||
}
|
||||
},
|
||||
calloop::channel::Event::Closed => {
|
||||
log::info!("Calloop channel closed.");
|
||||
}
|
||||
}
|
||||
})
|
||||
.unwrap();
|
||||
let wayland_source =
|
||||
WaylandSource::new(connection.clone(), event_queue);
|
||||
|
||||
let wayland_dispatcher = calloop::Dispatcher::new(
|
||||
wayland_source,
|
||||
|_, queue, winit_state| queue.dispatch_pending(winit_state),
|
||||
);
|
||||
|
||||
let _wayland_source_dispatcher = event_loop
|
||||
.handle()
|
||||
.register_dispatcher(wayland_dispatcher.clone())
|
||||
.unwrap();
|
||||
|
||||
let (viewporter_state, fractional_scaling_manager) =
|
||||
match FractionalScalingManager::new(&globals, &qh) {
|
||||
Ok(m) => {
|
||||
let viewporter_state =
|
||||
match ViewporterState::new(&globals, &qh) {
|
||||
Ok(s) => Some(s),
|
||||
Err(e) => {
|
||||
error!(
|
||||
"Failed to initialize viewporter: {}",
|
||||
e
|
||||
);
|
||||
None
|
||||
}
|
||||
};
|
||||
(viewporter_state, Some(m))
|
||||
}
|
||||
Err(e) => {
|
||||
error!(
|
||||
"Failed to initialize fractional scaling manager: {}",
|
||||
e
|
||||
);
|
||||
(None, None)
|
||||
}
|
||||
};
|
||||
|
||||
let mut state = Self {
|
||||
event_loop,
|
||||
state: SctkState {
|
||||
connection,
|
||||
registry_state,
|
||||
seat_state: SeatState::new(&globals, &qh),
|
||||
output_state: OutputState::new(&globals, &qh),
|
||||
compositor_state: CompositorState::bind(&globals, &qh)
|
||||
.expect("wl_compositor is not available"),
|
||||
shm_state: Shm::bind(&globals, &qh)
|
||||
.expect("wl_shm is not available"),
|
||||
xdg_shell_state: XdgShell::bind(&globals, &qh)
|
||||
.expect("xdg shell is not available"),
|
||||
layer_shell: LayerShell::bind(&globals, &qh).ok(),
|
||||
activation_state: ActivationState::bind(&globals, &qh).ok(),
|
||||
session_lock_state: SessionLockState::new(&globals, &qh),
|
||||
session_lock: None,
|
||||
|
||||
queue_handle: qh,
|
||||
loop_handle,
|
||||
|
||||
_cursor_surface: None,
|
||||
_multipool: None,
|
||||
outputs: Vec::new(),
|
||||
seats: Vec::new(),
|
||||
windows: Vec::new(),
|
||||
layer_surfaces: Vec::new(),
|
||||
popups: Vec::new(),
|
||||
lock_surfaces: Vec::new(),
|
||||
_kbd_focus: None,
|
||||
touch_points: HashMap::new(),
|
||||
sctk_events: Vec::new(),
|
||||
frame_status: HashMap::new(),
|
||||
fractional_scaling_manager,
|
||||
viewporter_state,
|
||||
compositor_updates: Default::default(),
|
||||
events_sender: winit_event_sender,
|
||||
proxy,
|
||||
id_map: Default::default(),
|
||||
to_commit: HashMap::new(),
|
||||
ready: true,
|
||||
destroyed: HashSet::new(),
|
||||
pending_popup: Default::default(),
|
||||
activation_token_ctr: 0,
|
||||
token_senders: HashMap::new(),
|
||||
},
|
||||
_features: Default::default(),
|
||||
};
|
||||
let wl_compositor = state
|
||||
.state
|
||||
.registry_state
|
||||
.bind_one(&state.state.queue_handle, 1..=6, GlobalData)
|
||||
.unwrap();
|
||||
let wl_subcompositor = state.state.registry_state.bind_one(
|
||||
&state.state.queue_handle,
|
||||
1..=1,
|
||||
GlobalData,
|
||||
);
|
||||
let wp_viewporter = state.state.registry_state.bind_one(
|
||||
&state.state.queue_handle,
|
||||
1..=1,
|
||||
GlobalData,
|
||||
);
|
||||
let wl_shm = state
|
||||
.state
|
||||
.registry_state
|
||||
.bind_one(&state.state.queue_handle, 1..=1, GlobalData)
|
||||
.unwrap();
|
||||
let wp_dmabuf = state
|
||||
.state
|
||||
.registry_state
|
||||
.bind_one(&state.state.queue_handle, 2..=4, GlobalData)
|
||||
.ok();
|
||||
let wp_alpha_modifier = state
|
||||
.state
|
||||
.registry_state
|
||||
.bind_one(&state.state.queue_handle, 1..=1, ())
|
||||
.ok();
|
||||
|
||||
if let (Ok(wl_subcompositor), Ok(wp_viewporter)) =
|
||||
(wl_subcompositor, wp_viewporter)
|
||||
{
|
||||
state::send_event(
|
||||
&state.state.events_sender,
|
||||
&state.state.proxy,
|
||||
SctkEvent::Subcompositor(SubsurfaceState {
|
||||
wl_compositor,
|
||||
wl_subcompositor,
|
||||
wp_viewporter,
|
||||
wl_shm,
|
||||
wp_dmabuf,
|
||||
wp_alpha_modifier,
|
||||
qh: state.state.queue_handle.clone(),
|
||||
buffers: HashMap::new(),
|
||||
unmapped_subsurfaces: Vec::new(),
|
||||
}),
|
||||
);
|
||||
} else {
|
||||
log::warn!("Subsurfaces not supported.")
|
||||
}
|
||||
|
||||
log::info!("SCTK setup complete.");
|
||||
loop {
|
||||
match state
|
||||
.state
|
||||
.events_sender
|
||||
.unbounded_send(Control::AboutToWait)
|
||||
{
|
||||
Ok(_) => {}
|
||||
Err(err) => {
|
||||
log::error!(
|
||||
"SCTK failed to send Control::AboutToWait. {err:?}"
|
||||
);
|
||||
if state.state.events_sender.is_closed() {
|
||||
return Ok(());
|
||||
}
|
||||
}
|
||||
}
|
||||
if !state.state.ready {
|
||||
continue;
|
||||
}
|
||||
|
||||
if let Err(err) =
|
||||
state.event_loop.dispatch(None, &mut state.state)
|
||||
{
|
||||
log::error!("SCTK dispatch error: {err}");
|
||||
}
|
||||
let had_events = !state.state.sctk_events.is_empty();
|
||||
let mut wake_up = had_events;
|
||||
|
||||
for s in state
|
||||
.state
|
||||
.layer_surfaces
|
||||
.iter()
|
||||
.map(|s| s.surface.wl_surface())
|
||||
.chain(
|
||||
state.state.popups.iter().map(|s| s.popup.wl_surface()),
|
||||
)
|
||||
.chain(
|
||||
state
|
||||
.state
|
||||
.lock_surfaces
|
||||
.iter()
|
||||
.map(|s| s.session_lock_surface.wl_surface()),
|
||||
)
|
||||
{
|
||||
let id = s.id();
|
||||
if state
|
||||
.state
|
||||
.frame_status
|
||||
.get(&id)
|
||||
.map(|v| !matches!(v, state::FrameStatus::Ready))
|
||||
.unwrap_or(true)
|
||||
|| !state.state.id_map.contains_key(&id)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
wake_up = true;
|
||||
|
||||
_ = s.frame(&state.state.queue_handle, s.clone());
|
||||
_ = state.state.frame_status.remove(&id);
|
||||
_ = state.state.events_sender.unbounded_send(
|
||||
Control::Winit(
|
||||
winit::window::WindowId::from_raw(
|
||||
id.as_ptr() as usize
|
||||
),
|
||||
winit::event::WindowEvent::RedrawRequested,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
for e in state.state.sctk_events.drain(..) {
|
||||
if let SctkEvent::Winit(id, e) = e {
|
||||
_ = state
|
||||
.state
|
||||
.events_sender
|
||||
.unbounded_send(Control::Winit(id, e));
|
||||
} else {
|
||||
_ = state.state.events_sender.unbounded_send(
|
||||
Control::PlatformSpecific(
|
||||
crate::platform_specific::Event::Wayland(e),
|
||||
),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
if wake_up {
|
||||
state.state.proxy.wake_up();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
if res.is_finished() {
|
||||
log::warn!("SCTK thread finished.");
|
||||
res.join().map(|_: Result<(), ConnectError>| action_tx)
|
||||
} else {
|
||||
Ok(action_tx)
|
||||
}
|
||||
}
|
||||
}
|
||||
66
winit/src/platform_specific/wayland/event_loop/proxy.rs
Normal file
66
winit/src/platform_specific/wayland/event_loop/proxy.rs
Normal file
|
|
@ -0,0 +1,66 @@
|
|||
use iced_futures::futures::{
|
||||
channel::mpsc,
|
||||
task::{Context, Poll},
|
||||
Sink,
|
||||
};
|
||||
use sctk::reexports::calloop;
|
||||
use std::pin::Pin;
|
||||
|
||||
/// An event loop proxy that implements `Sink`.
|
||||
#[derive(Debug)]
|
||||
pub struct Proxy<Message: 'static> {
|
||||
raw: calloop::channel::Sender<Message>,
|
||||
}
|
||||
|
||||
impl<Message: 'static> Clone for Proxy<Message> {
|
||||
fn clone(&self) -> Self {
|
||||
Self {
|
||||
raw: self.raw.clone(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<Message: 'static> Proxy<Message> {
|
||||
/// Creates a new [`Proxy`] from an `EventLoopProxy`.
|
||||
pub fn new(raw: calloop::channel::Sender<Message>) -> Self {
|
||||
Self { raw }
|
||||
}
|
||||
/// send an event
|
||||
pub fn send_event(&self, message: Message) {
|
||||
let _ = self.raw.send(message);
|
||||
}
|
||||
}
|
||||
|
||||
impl<Message: 'static> Sink<Message> for Proxy<Message> {
|
||||
type Error = mpsc::SendError;
|
||||
|
||||
fn poll_ready(
|
||||
self: Pin<&mut Self>,
|
||||
_cx: &mut Context<'_>,
|
||||
) -> Poll<Result<(), Self::Error>> {
|
||||
Poll::Ready(Ok(()))
|
||||
}
|
||||
|
||||
fn start_send(
|
||||
self: Pin<&mut Self>,
|
||||
message: Message,
|
||||
) -> Result<(), Self::Error> {
|
||||
let _ = self.raw.send(message);
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn poll_flush(
|
||||
self: Pin<&mut Self>,
|
||||
_cx: &mut Context<'_>,
|
||||
) -> Poll<Result<(), Self::Error>> {
|
||||
Poll::Ready(Ok(()))
|
||||
}
|
||||
|
||||
fn poll_close(
|
||||
self: Pin<&mut Self>,
|
||||
_cx: &mut Context<'_>,
|
||||
) -> Poll<Result<(), Self::Error>> {
|
||||
Poll::Ready(Ok(()))
|
||||
}
|
||||
}
|
||||
1133
winit/src/platform_specific/wayland/event_loop/state.rs
Normal file
1133
winit/src/platform_specific/wayland/event_loop/state.rs
Normal file
File diff suppressed because it is too large
Load diff
47
winit/src/platform_specific/wayland/handlers/activation.rs
Normal file
47
winit/src/platform_specific/wayland/handlers/activation.rs
Normal file
|
|
@ -0,0 +1,47 @@
|
|||
use iced_futures::futures::channel::oneshot::Sender;
|
||||
use sctk::{
|
||||
activation::{ActivationHandler, RequestData, RequestDataExt},
|
||||
delegate_activation,
|
||||
reexports::client::protocol::{wl_seat::WlSeat, wl_surface::WlSurface},
|
||||
};
|
||||
|
||||
use crate::platform_specific::wayland::event_loop::state::SctkState;
|
||||
|
||||
pub struct IcedRequestData {
|
||||
id: u32,
|
||||
data: RequestData,
|
||||
}
|
||||
|
||||
impl IcedRequestData {
|
||||
pub fn new(data: RequestData, id: u32) -> IcedRequestData {
|
||||
IcedRequestData { data, id }
|
||||
}
|
||||
}
|
||||
|
||||
impl RequestDataExt for IcedRequestData {
|
||||
fn app_id(&self) -> Option<&str> {
|
||||
self.data.app_id()
|
||||
}
|
||||
|
||||
fn seat_and_serial(&self) -> Option<(&WlSeat, u32)> {
|
||||
self.data.seat_and_serial()
|
||||
}
|
||||
|
||||
fn surface(&self) -> Option<&WlSurface> {
|
||||
self.data.surface()
|
||||
}
|
||||
}
|
||||
|
||||
impl ActivationHandler for SctkState {
|
||||
type RequestData = IcedRequestData;
|
||||
|
||||
fn new_token(&mut self, token: String, data: &Self::RequestData) {
|
||||
if let Some(tx) = self.token_senders.remove(&data.id) {
|
||||
_ = tx.send(Some(token));
|
||||
} else {
|
||||
log::error!("Missing activation request Id.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
delegate_activation!(SctkState, IcedRequestData);
|
||||
67
winit/src/platform_specific/wayland/handlers/compositor.rs
Normal file
67
winit/src/platform_specific/wayland/handlers/compositor.rs
Normal file
|
|
@ -0,0 +1,67 @@
|
|||
// SPDX-License-Identifier: MPL-2.0-only
|
||||
use sctk::{
|
||||
compositor::CompositorHandler,
|
||||
delegate_compositor,
|
||||
reexports::client::{
|
||||
protocol::{wl_output, wl_surface},
|
||||
Connection, Proxy, QueueHandle,
|
||||
},
|
||||
};
|
||||
|
||||
use crate::{
|
||||
event_loop::state::receive_frame,
|
||||
platform_specific::wayland::event_loop::state::SctkState,
|
||||
};
|
||||
|
||||
impl CompositorHandler for SctkState {
|
||||
fn scale_factor_changed(
|
||||
&mut self,
|
||||
_conn: &Connection,
|
||||
_qh: &QueueHandle<Self>,
|
||||
surface: &wl_surface::WlSurface,
|
||||
new_factor: i32,
|
||||
) {
|
||||
self.scale_factor_changed(surface, new_factor as f64, true);
|
||||
}
|
||||
|
||||
fn frame(
|
||||
&mut self,
|
||||
_conn: &Connection,
|
||||
_qh: &QueueHandle<Self>,
|
||||
surface: &wl_surface::WlSurface,
|
||||
_time: u32,
|
||||
) {
|
||||
_ = receive_frame(&mut self.frame_status, surface);
|
||||
}
|
||||
|
||||
fn transform_changed(
|
||||
&mut self,
|
||||
_conn: &Connection,
|
||||
_qh: &QueueHandle<Self>,
|
||||
_surface: &wl_surface::WlSurface,
|
||||
_new_transform: wl_output::Transform,
|
||||
) {
|
||||
// TODO
|
||||
// this is not required
|
||||
}
|
||||
|
||||
fn surface_enter(
|
||||
&mut self,
|
||||
_: &Connection,
|
||||
_: &QueueHandle<Self>,
|
||||
_: &wl_surface::WlSurface,
|
||||
_: &wl_output::WlOutput,
|
||||
) {
|
||||
}
|
||||
|
||||
fn surface_leave(
|
||||
&mut self,
|
||||
_: &Connection,
|
||||
_: &QueueHandle<Self>,
|
||||
_: &wl_surface::WlSurface,
|
||||
_: &wl_output::WlOutput,
|
||||
) {
|
||||
}
|
||||
}
|
||||
|
||||
delegate_compositor!(SctkState);
|
||||
37
winit/src/platform_specific/wayland/handlers/mod.rs
Normal file
37
winit/src/platform_specific/wayland/handlers/mod.rs
Normal file
|
|
@ -0,0 +1,37 @@
|
|||
// handlers
|
||||
pub mod activation;
|
||||
pub mod compositor;
|
||||
pub mod output;
|
||||
pub mod seat;
|
||||
pub mod session_lock;
|
||||
pub mod shell;
|
||||
pub mod subcompositor;
|
||||
pub mod wp_fractional_scaling;
|
||||
pub mod wp_viewporter;
|
||||
|
||||
use sctk::{
|
||||
delegate_registry, delegate_shm,
|
||||
output::OutputState,
|
||||
registry::{ProvidesRegistryState, RegistryState},
|
||||
registry_handlers,
|
||||
seat::SeatState,
|
||||
shm::{Shm, ShmHandler},
|
||||
};
|
||||
|
||||
use crate::platform_specific::wayland::event_loop::state::SctkState;
|
||||
|
||||
impl ShmHandler for SctkState {
|
||||
fn shm_state(&mut self) -> &mut Shm {
|
||||
&mut self.shm_state
|
||||
}
|
||||
}
|
||||
|
||||
impl ProvidesRegistryState for SctkState {
|
||||
fn registry(&mut self) -> &mut RegistryState {
|
||||
&mut self.registry_state
|
||||
}
|
||||
registry_handlers![OutputState, SeatState,];
|
||||
}
|
||||
|
||||
delegate_shm!(SctkState);
|
||||
delegate_registry!(SctkState);
|
||||
49
winit/src/platform_specific/wayland/handlers/output.rs
Normal file
49
winit/src/platform_specific/wayland/handlers/output.rs
Normal file
|
|
@ -0,0 +1,49 @@
|
|||
use crate::platform_specific::wayland::{
|
||||
event_loop::state::SctkState, sctk_event::SctkEvent,
|
||||
};
|
||||
use sctk::{delegate_output, output::OutputHandler};
|
||||
|
||||
impl OutputHandler for SctkState {
|
||||
fn output_state(&mut self) -> &mut sctk::output::OutputState {
|
||||
&mut self.output_state
|
||||
}
|
||||
|
||||
fn new_output(
|
||||
&mut self,
|
||||
_conn: &sctk::reexports::client::Connection,
|
||||
_qh: &sctk::reexports::client::QueueHandle<Self>,
|
||||
output: sctk::reexports::client::protocol::wl_output::WlOutput,
|
||||
) {
|
||||
self.sctk_events.push(SctkEvent::NewOutput {
|
||||
id: output.clone(),
|
||||
info: self.output_state.info(&output),
|
||||
});
|
||||
self.outputs.push(output);
|
||||
}
|
||||
|
||||
fn update_output(
|
||||
&mut self,
|
||||
_conn: &sctk::reexports::client::Connection,
|
||||
_qh: &sctk::reexports::client::QueueHandle<Self>,
|
||||
output: sctk::reexports::client::protocol::wl_output::WlOutput,
|
||||
) {
|
||||
if let Some(info) = self.output_state.info(&output) {
|
||||
self.sctk_events.push(SctkEvent::UpdateOutput {
|
||||
id: output.clone(),
|
||||
info,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
fn output_destroyed(
|
||||
&mut self,
|
||||
_conn: &sctk::reexports::client::Connection,
|
||||
_qh: &sctk::reexports::client::QueueHandle<Self>,
|
||||
output: sctk::reexports::client::protocol::wl_output::WlOutput,
|
||||
) {
|
||||
self.sctk_events.push(SctkEvent::RemovedOutput(output));
|
||||
// TODO clean up any layer surfaces on this output?
|
||||
}
|
||||
}
|
||||
|
||||
delegate_output!(SctkState);
|
||||
257
winit/src/platform_specific/wayland/handlers/seat/keyboard.rs
Normal file
257
winit/src/platform_specific/wayland/handlers/seat/keyboard.rs
Normal file
|
|
@ -0,0 +1,257 @@
|
|||
use crate::platform_specific::wayland::{
|
||||
event_loop::state::SctkState,
|
||||
sctk_event::{KeyboardEventVariant, SctkEvent},
|
||||
};
|
||||
use sctk::reexports::client::Proxy;
|
||||
use sctk::{
|
||||
delegate_keyboard,
|
||||
seat::keyboard::{KeyboardHandler, Keysym},
|
||||
};
|
||||
|
||||
impl KeyboardHandler for SctkState {
|
||||
fn enter(
|
||||
&mut self,
|
||||
_conn: &sctk::reexports::client::Connection,
|
||||
_qh: &sctk::reexports::client::QueueHandle<Self>,
|
||||
keyboard: &sctk::reexports::client::protocol::wl_keyboard::WlKeyboard,
|
||||
surface: &sctk::reexports::client::protocol::wl_surface::WlSurface,
|
||||
_serial: u32,
|
||||
_raw: &[u32],
|
||||
_keysyms: &[Keysym],
|
||||
) {
|
||||
self.request_redraw(surface);
|
||||
let (i, mut is_active, _seat) = {
|
||||
let (i, is_active, my_seat) =
|
||||
match self.seats.iter_mut().enumerate().find_map(|(i, s)| {
|
||||
if s.kbd.as_ref() == Some(keyboard) {
|
||||
Some((i, s))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}) {
|
||||
Some((i, s)) => (i, i == 0, s),
|
||||
None => return,
|
||||
};
|
||||
_ = my_seat.kbd_focus.replace(surface.clone());
|
||||
|
||||
let seat = my_seat.seat.clone();
|
||||
(i, is_active, seat)
|
||||
};
|
||||
|
||||
// TODO Ashley: thoroughly test this
|
||||
// swap the active seat to be the current seat if the current "active" seat is not focused on the application anyway
|
||||
if !is_active && self.seats[0].kbd_focus.is_none() {
|
||||
is_active = true;
|
||||
self.seats.swap(0, i);
|
||||
}
|
||||
|
||||
if is_active {
|
||||
let id = winit::window::WindowId::from_raw(
|
||||
surface.id().as_ptr() as usize
|
||||
);
|
||||
if self.windows.iter().any(|w| w.window.id() == id) {
|
||||
return;
|
||||
}
|
||||
self.sctk_events.push(SctkEvent::Winit(
|
||||
id,
|
||||
winit::event::WindowEvent::Focused(true),
|
||||
));
|
||||
// self.sctk_events.push(SctkEvent::KeyboardEvent {
|
||||
// variant: KeyboardEventVariant::Enter(surface.clone()),
|
||||
// kbd_id: keyboard.clone(),
|
||||
// seat_id: seat,
|
||||
// })
|
||||
}
|
||||
}
|
||||
|
||||
fn leave(
|
||||
&mut self,
|
||||
_conn: &sctk::reexports::client::Connection,
|
||||
_qh: &sctk::reexports::client::QueueHandle<Self>,
|
||||
keyboard: &sctk::reexports::client::protocol::wl_keyboard::WlKeyboard,
|
||||
surface: &sctk::reexports::client::protocol::wl_surface::WlSurface,
|
||||
_serial: u32,
|
||||
) {
|
||||
self.request_redraw(surface);
|
||||
let (is_active, seat, kbd) = {
|
||||
let (is_active, my_seat) =
|
||||
match self.seats.iter_mut().enumerate().find_map(|(i, s)| {
|
||||
if s.kbd.as_ref() == Some(keyboard) {
|
||||
Some((i, s))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}) {
|
||||
Some((i, s)) => (i == 0, s),
|
||||
None => return,
|
||||
};
|
||||
let seat = my_seat.seat.clone();
|
||||
let kbd = keyboard.clone();
|
||||
_ = my_seat.kbd_focus.take();
|
||||
(is_active, seat, kbd)
|
||||
};
|
||||
|
||||
if is_active {
|
||||
self.sctk_events.push(SctkEvent::KeyboardEvent {
|
||||
variant: KeyboardEventVariant::Leave(surface.clone()),
|
||||
kbd_id: kbd,
|
||||
seat_id: seat,
|
||||
surface: surface.clone(),
|
||||
});
|
||||
// if there is another seat with a keyboard focused on a surface make that the new active seat
|
||||
if let Some(i) =
|
||||
self.seats.iter().position(|s| s.kbd_focus.is_some())
|
||||
{
|
||||
self.seats.swap(0, i);
|
||||
let s = &self.seats[0];
|
||||
let id = winit::window::WindowId::from_raw(
|
||||
surface.id().as_ptr() as usize,
|
||||
);
|
||||
if self.windows.iter().any(|w| w.window.id() == id) {
|
||||
return;
|
||||
}
|
||||
self.sctk_events.push(SctkEvent::Winit(
|
||||
id,
|
||||
winit::event::WindowEvent::Focused(true),
|
||||
));
|
||||
self.sctk_events.push(SctkEvent::KeyboardEvent {
|
||||
variant: KeyboardEventVariant::Enter(
|
||||
s.kbd_focus.clone().unwrap(),
|
||||
),
|
||||
kbd_id: s.kbd.clone().unwrap(),
|
||||
seat_id: s.seat.clone(),
|
||||
surface: surface.clone(),
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn press_key(
|
||||
&mut self,
|
||||
_conn: &sctk::reexports::client::Connection,
|
||||
_qh: &sctk::reexports::client::QueueHandle<Self>,
|
||||
keyboard: &sctk::reexports::client::protocol::wl_keyboard::WlKeyboard,
|
||||
serial: u32,
|
||||
event: sctk::seat::keyboard::KeyEvent,
|
||||
) {
|
||||
let (is_active, my_seat) =
|
||||
match self.seats.iter_mut().enumerate().find_map(|(i, s)| {
|
||||
if s.kbd.as_ref() == Some(keyboard) {
|
||||
Some((i, s))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}) {
|
||||
Some((i, s)) => (i == 0, s),
|
||||
None => return,
|
||||
};
|
||||
let seat_id = my_seat.seat.clone();
|
||||
let kbd_id = keyboard.clone();
|
||||
_ = my_seat.last_kbd_press.replace((event.clone(), serial));
|
||||
if is_active {
|
||||
// FIXME can't create winit key events because of private field
|
||||
// if let Some(id) = id {
|
||||
// let physical_key = raw_keycode_to_physicalkey(event.raw_code);
|
||||
// let (logical_key, location) =
|
||||
// keysym_to_vkey_location(event.keysym);
|
||||
// self.sctk_events.push(SctkEvent::Winit(
|
||||
// id,
|
||||
// winit::event::WindowEvent::KeyboardInput {
|
||||
// device_id: Default::default(),
|
||||
// event: winit::event::KeyEvent {
|
||||
// physical_key,
|
||||
// logical_key,
|
||||
// text: event.utf8.map(|s| s.into()),
|
||||
// location,
|
||||
// state: winit::event::ElementState::Pressed,
|
||||
// repeat: false, // TODO we don't have this info...
|
||||
// },
|
||||
// is_synthetic: false,
|
||||
// },
|
||||
// ))
|
||||
// }
|
||||
if let Some(surface) = my_seat.kbd_focus.clone() {
|
||||
self.request_redraw(&surface);
|
||||
self.sctk_events.push(SctkEvent::KeyboardEvent {
|
||||
variant: KeyboardEventVariant::Press(event),
|
||||
kbd_id,
|
||||
seat_id,
|
||||
surface,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn release_key(
|
||||
&mut self,
|
||||
_conn: &sctk::reexports::client::Connection,
|
||||
_qh: &sctk::reexports::client::QueueHandle<Self>,
|
||||
keyboard: &sctk::reexports::client::protocol::wl_keyboard::WlKeyboard,
|
||||
_serial: u32,
|
||||
event: sctk::seat::keyboard::KeyEvent,
|
||||
) {
|
||||
let (is_active, my_seat) =
|
||||
match self.seats.iter_mut().enumerate().find_map(|(i, s)| {
|
||||
if s.kbd.as_ref() == Some(keyboard) {
|
||||
Some((i, s))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}) {
|
||||
Some((i, s)) => (i == 0, s),
|
||||
None => return,
|
||||
};
|
||||
let seat_id = my_seat.seat.clone();
|
||||
let kbd_id = keyboard.clone();
|
||||
|
||||
if is_active {
|
||||
if let Some(surface) = my_seat.kbd_focus.clone() {
|
||||
self.request_redraw(&surface);
|
||||
self.sctk_events.push(SctkEvent::KeyboardEvent {
|
||||
variant: KeyboardEventVariant::Release(event),
|
||||
kbd_id,
|
||||
seat_id,
|
||||
surface,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn update_modifiers(
|
||||
&mut self,
|
||||
_conn: &sctk::reexports::client::Connection,
|
||||
_qh: &sctk::reexports::client::QueueHandle<Self>,
|
||||
keyboard: &sctk::reexports::client::protocol::wl_keyboard::WlKeyboard,
|
||||
_serial: u32,
|
||||
modifiers: sctk::seat::keyboard::Modifiers,
|
||||
layout: u32,
|
||||
) {
|
||||
let (is_active, my_seat) =
|
||||
match self.seats.iter_mut().enumerate().find_map(|(i, s)| {
|
||||
if s.kbd.as_ref() == Some(keyboard) {
|
||||
Some((i, s))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}) {
|
||||
Some((i, s)) => (i == 0, s),
|
||||
None => return,
|
||||
};
|
||||
let seat_id = my_seat.seat.clone();
|
||||
let kbd_id = keyboard.clone();
|
||||
|
||||
if is_active {
|
||||
if let Some(surface) = my_seat.kbd_focus.clone() {
|
||||
self.request_redraw(&surface);
|
||||
self.sctk_events.push(SctkEvent::KeyboardEvent {
|
||||
variant: KeyboardEventVariant::Modifiers(modifiers),
|
||||
kbd_id,
|
||||
seat_id,
|
||||
surface,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
delegate_keyboard!(SctkState);
|
||||
5
winit/src/platform_specific/wayland/handlers/seat/mod.rs
Normal file
5
winit/src/platform_specific/wayland/handlers/seat/mod.rs
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
// TODO support multi-seat handling
|
||||
pub mod keyboard;
|
||||
pub mod pointer;
|
||||
pub mod seat;
|
||||
pub mod touch;
|
||||
204
winit/src/platform_specific/wayland/handlers/seat/pointer.rs
Normal file
204
winit/src/platform_specific/wayland/handlers/seat/pointer.rs
Normal file
|
|
@ -0,0 +1,204 @@
|
|||
use crate::{
|
||||
event_loop::state::FrameStatus,
|
||||
platform_specific::wayland::{
|
||||
event_loop::state::SctkState, sctk_event::SctkEvent,
|
||||
},
|
||||
};
|
||||
use sctk::{
|
||||
delegate_pointer,
|
||||
reexports::client::Proxy,
|
||||
seat::pointer::{
|
||||
CursorIcon, PointerEvent, PointerEventKind, PointerHandler,
|
||||
},
|
||||
};
|
||||
use winit::{
|
||||
dpi::PhysicalPosition,
|
||||
event::{
|
||||
ButtonSource, MouseButton, MouseScrollDelta, PointerKind,
|
||||
PointerSource, TouchPhase, WindowEvent,
|
||||
},
|
||||
};
|
||||
|
||||
impl PointerHandler for SctkState {
|
||||
fn pointer_frame(
|
||||
&mut self,
|
||||
conn: &sctk::reexports::client::Connection,
|
||||
_qh: &sctk::reexports::client::QueueHandle<Self>,
|
||||
pointer: &sctk::reexports::client::protocol::wl_pointer::WlPointer,
|
||||
events: &[sctk::seat::pointer::PointerEvent],
|
||||
) {
|
||||
let (is_active, my_seat) =
|
||||
match self.seats.iter_mut().enumerate().find_map(|(i, s)| {
|
||||
if s.ptr.as_ref().map(|p| p.pointer()) == Some(pointer) {
|
||||
Some((i, s))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}) {
|
||||
Some((i, s)) => (i == 0, s),
|
||||
None => return,
|
||||
};
|
||||
|
||||
// track events, but only forward for the active seat
|
||||
for e in events {
|
||||
if my_seat.active_icon != my_seat.icon {
|
||||
// Restore cursor that was set by appliction, or default
|
||||
my_seat.set_cursor(
|
||||
conn,
|
||||
my_seat.icon.unwrap_or(CursorIcon::Default),
|
||||
);
|
||||
}
|
||||
|
||||
if is_active {
|
||||
let id = winit::window::WindowId::from_raw(
|
||||
e.surface.id().as_ptr() as usize,
|
||||
);
|
||||
if self.windows.iter().any(|w| w.window.id() == id) {
|
||||
continue;
|
||||
}
|
||||
let entry = self
|
||||
.frame_status
|
||||
.entry(e.surface.id())
|
||||
.or_insert(FrameStatus::RequestedRedraw);
|
||||
if matches!(entry, FrameStatus::Received) {
|
||||
*entry = FrameStatus::Ready;
|
||||
}
|
||||
if let PointerEventKind::Motion { time } = &e.kind {
|
||||
self.sctk_events.push(SctkEvent::PointerEvent {
|
||||
variant: PointerEvent {
|
||||
surface: e.surface.clone(),
|
||||
position: e.position,
|
||||
kind: PointerEventKind::Motion { time: *time },
|
||||
},
|
||||
ptr_id: pointer.clone(),
|
||||
seat_id: my_seat.seat.clone(),
|
||||
});
|
||||
} else {
|
||||
self.sctk_events.push(SctkEvent::Winit(
|
||||
id,
|
||||
match e.kind {
|
||||
PointerEventKind::Enter { serial } => {
|
||||
WindowEvent::PointerEntered {
|
||||
device_id: Default::default(),
|
||||
position: e.position.into(),
|
||||
primary: is_active,
|
||||
kind: PointerKind::Mouse,
|
||||
}
|
||||
}
|
||||
PointerEventKind::Leave { serial } => {
|
||||
WindowEvent::PointerLeft {
|
||||
device_id: Default::default(),
|
||||
position: Some(e.position.into()),
|
||||
primary: is_active,
|
||||
kind: PointerKind::Mouse,
|
||||
}
|
||||
}
|
||||
PointerEventKind::Motion { time } => {
|
||||
WindowEvent::PointerMoved {
|
||||
device_id: Default::default(),
|
||||
position: e.position.into(),
|
||||
primary: is_active,
|
||||
source: PointerSource::Mouse,
|
||||
}
|
||||
}
|
||||
PointerEventKind::Press {
|
||||
time,
|
||||
button,
|
||||
serial,
|
||||
} => WindowEvent::PointerButton {
|
||||
device_id: Default::default(),
|
||||
state: winit::event::ElementState::Pressed,
|
||||
button: ButtonSource::Mouse(
|
||||
wayland_button_to_winit(button),
|
||||
),
|
||||
position: e.position.into(),
|
||||
primary: is_active,
|
||||
},
|
||||
PointerEventKind::Release {
|
||||
time,
|
||||
button,
|
||||
serial,
|
||||
} => WindowEvent::PointerButton {
|
||||
device_id: Default::default(),
|
||||
state: winit::event::ElementState::Released,
|
||||
button: ButtonSource::Mouse(
|
||||
wayland_button_to_winit(button),
|
||||
),
|
||||
position: e.position.into(),
|
||||
primary: is_active,
|
||||
},
|
||||
PointerEventKind::Axis {
|
||||
time,
|
||||
horizontal,
|
||||
vertical,
|
||||
source,
|
||||
} => WindowEvent::MouseWheel {
|
||||
device_id: Default::default(),
|
||||
delta: if horizontal.discrete > 0 {
|
||||
MouseScrollDelta::LineDelta(
|
||||
horizontal.discrete as f32,
|
||||
vertical.discrete as f32,
|
||||
)
|
||||
} else {
|
||||
MouseScrollDelta::PixelDelta(
|
||||
PhysicalPosition::new(
|
||||
horizontal.absolute,
|
||||
vertical.absolute,
|
||||
),
|
||||
)
|
||||
},
|
||||
phase: if horizontal.stop {
|
||||
TouchPhase::Ended
|
||||
} else {
|
||||
TouchPhase::Moved
|
||||
},
|
||||
},
|
||||
},
|
||||
));
|
||||
}
|
||||
}
|
||||
match e.kind {
|
||||
PointerEventKind::Enter { .. } => {
|
||||
_ = my_seat.ptr_focus.replace(e.surface.clone());
|
||||
}
|
||||
PointerEventKind::Leave { .. } => {
|
||||
_ = my_seat.ptr_focus.take();
|
||||
_ = my_seat.active_icon = None;
|
||||
}
|
||||
PointerEventKind::Press {
|
||||
time,
|
||||
button,
|
||||
serial,
|
||||
} => {
|
||||
_ = my_seat.last_ptr_press.replace((time, button, serial));
|
||||
}
|
||||
// TODO revisit events that ought to be handled and change internal state
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Convert the Wayland button into winit.
|
||||
fn wayland_button_to_winit(button: u32) -> MouseButton {
|
||||
// These values are coming from <linux/input-event-codes.h>.
|
||||
const BTN_LEFT: u32 = 0x110;
|
||||
const BTN_RIGHT: u32 = 0x111;
|
||||
const BTN_MIDDLE: u32 = 0x112;
|
||||
const BTN_SIDE: u32 = 0x113;
|
||||
const BTN_EXTRA: u32 = 0x114;
|
||||
const BTN_FORWARD: u32 = 0x115;
|
||||
const BTN_BACK: u32 = 0x116;
|
||||
|
||||
match button {
|
||||
BTN_LEFT => MouseButton::Left,
|
||||
BTN_RIGHT => MouseButton::Right,
|
||||
BTN_MIDDLE => MouseButton::Middle,
|
||||
BTN_BACK | BTN_SIDE => MouseButton::Back,
|
||||
BTN_FORWARD | BTN_EXTRA => MouseButton::Forward,
|
||||
button => MouseButton::try_from_u8(button as u8)
|
||||
.unwrap_or(MouseButton::Button32), // TODO why was Other variant removed?
|
||||
}
|
||||
}
|
||||
|
||||
delegate_pointer!(SctkState);
|
||||
214
winit/src/platform_specific/wayland/handlers/seat/seat.rs
Normal file
214
winit/src/platform_specific/wayland/handlers/seat/seat.rs
Normal file
|
|
@ -0,0 +1,214 @@
|
|||
use crate::platform_specific::wayland::{
|
||||
event_loop::{state::SctkSeat, state::SctkState},
|
||||
sctk_event::{KeyboardEventVariant, SctkEvent, SeatEventVariant},
|
||||
};
|
||||
use iced_runtime::keyboard::Modifiers;
|
||||
use sctk::{
|
||||
delegate_seat,
|
||||
reexports::client::{protocol::wl_keyboard::WlKeyboard, Proxy},
|
||||
seat::{pointer::ThemeSpec, SeatHandler},
|
||||
};
|
||||
|
||||
impl SeatHandler for SctkState {
|
||||
fn seat_state(&mut self) -> &mut sctk::seat::SeatState {
|
||||
&mut self.seat_state
|
||||
}
|
||||
|
||||
fn new_seat(
|
||||
&mut self,
|
||||
_conn: &sctk::reexports::client::Connection,
|
||||
qh: &sctk::reexports::client::QueueHandle<Self>,
|
||||
seat: sctk::reexports::client::protocol::wl_seat::WlSeat,
|
||||
) {
|
||||
self.sctk_events.push(SctkEvent::SeatEvent {
|
||||
variant: SeatEventVariant::New,
|
||||
id: seat.clone(),
|
||||
});
|
||||
|
||||
self.seats.push(SctkSeat {
|
||||
seat,
|
||||
kbd: None,
|
||||
ptr: None,
|
||||
touch: None,
|
||||
_modifiers: Modifiers::default(),
|
||||
kbd_focus: None,
|
||||
ptr_focus: None,
|
||||
last_ptr_press: None,
|
||||
last_kbd_press: None,
|
||||
last_touch_down: None,
|
||||
icon: None,
|
||||
active_icon: None,
|
||||
});
|
||||
}
|
||||
|
||||
fn new_capability(
|
||||
&mut self,
|
||||
_conn: &sctk::reexports::client::Connection,
|
||||
qh: &sctk::reexports::client::QueueHandle<Self>,
|
||||
seat: sctk::reexports::client::protocol::wl_seat::WlSeat,
|
||||
capability: sctk::seat::Capability,
|
||||
) {
|
||||
let my_seat = match self.seats.iter_mut().find(|s| s.seat == seat) {
|
||||
Some(s) => s,
|
||||
None => {
|
||||
self.seats.push(SctkSeat {
|
||||
seat: seat.clone(),
|
||||
kbd: None,
|
||||
ptr: None,
|
||||
touch: None,
|
||||
|
||||
_modifiers: Modifiers::default(),
|
||||
kbd_focus: None,
|
||||
ptr_focus: None,
|
||||
last_ptr_press: None,
|
||||
last_kbd_press: None,
|
||||
last_touch_down: None,
|
||||
icon: None,
|
||||
active_icon: None,
|
||||
});
|
||||
self.seats.last_mut().unwrap()
|
||||
}
|
||||
};
|
||||
// TODO data device
|
||||
match capability {
|
||||
sctk::seat::Capability::Keyboard => {
|
||||
let seat_clone = seat.clone();
|
||||
let seat_clone_2 = seat.clone();
|
||||
if let Ok(kbd) = self.seat_state.get_keyboard_with_repeat(
|
||||
qh,
|
||||
&seat,
|
||||
None,
|
||||
self.loop_handle.clone(),
|
||||
Box::new(move |state, kbd: &WlKeyboard, e| {
|
||||
let Some(my_seat) = state
|
||||
.seats
|
||||
.iter_mut()
|
||||
.find(|s| s.seat == seat_clone_2)
|
||||
else {
|
||||
return;
|
||||
};
|
||||
if let Some(surface) = my_seat.kbd_focus.clone() {
|
||||
state.sctk_events.push(SctkEvent::KeyboardEvent {
|
||||
variant: KeyboardEventVariant::Repeat(e),
|
||||
kbd_id: kbd.clone(),
|
||||
seat_id: seat_clone.clone(),
|
||||
surface,
|
||||
});
|
||||
}
|
||||
}),
|
||||
) {
|
||||
self.sctk_events.push(SctkEvent::SeatEvent {
|
||||
variant: SeatEventVariant::NewCapability(
|
||||
capability,
|
||||
kbd.id(),
|
||||
),
|
||||
id: seat.clone(),
|
||||
});
|
||||
_ = my_seat.kbd.replace(kbd);
|
||||
}
|
||||
}
|
||||
sctk::seat::Capability::Pointer => {
|
||||
let surface = self.compositor_state.create_surface(qh);
|
||||
|
||||
if let Ok(ptr) = self.seat_state.get_pointer_with_theme(
|
||||
qh,
|
||||
&seat,
|
||||
self.shm_state.wl_shm(),
|
||||
surface,
|
||||
ThemeSpec::default(),
|
||||
) {
|
||||
self.sctk_events.push(SctkEvent::SeatEvent {
|
||||
variant: SeatEventVariant::NewCapability(
|
||||
capability,
|
||||
ptr.pointer().id(),
|
||||
),
|
||||
id: seat.clone(),
|
||||
});
|
||||
_ = my_seat.ptr.replace(ptr);
|
||||
}
|
||||
}
|
||||
sctk::seat::Capability::Touch => {
|
||||
if let Some(touch) = self.seat_state.get_touch(qh, &seat).ok() {
|
||||
self.sctk_events.push(SctkEvent::SeatEvent {
|
||||
variant: SeatEventVariant::NewCapability(
|
||||
capability,
|
||||
touch.id(),
|
||||
),
|
||||
id: seat.clone(),
|
||||
});
|
||||
_ = my_seat.touch.replace(touch);
|
||||
}
|
||||
}
|
||||
_ => unimplemented!(),
|
||||
}
|
||||
}
|
||||
|
||||
fn remove_capability(
|
||||
&mut self,
|
||||
_conn: &sctk::reexports::client::Connection,
|
||||
_qh: &sctk::reexports::client::QueueHandle<Self>,
|
||||
seat: sctk::reexports::client::protocol::wl_seat::WlSeat,
|
||||
capability: sctk::seat::Capability,
|
||||
) {
|
||||
let my_seat = match self.seats.iter_mut().find(|s| s.seat == seat) {
|
||||
Some(s) => s,
|
||||
None => return,
|
||||
};
|
||||
|
||||
// TODO data device
|
||||
match capability {
|
||||
// TODO use repeating kbd?
|
||||
sctk::seat::Capability::Keyboard => {
|
||||
if let Some(kbd) = my_seat.kbd.take() {
|
||||
self.sctk_events.push(SctkEvent::SeatEvent {
|
||||
variant: SeatEventVariant::RemoveCapability(
|
||||
capability,
|
||||
kbd.id(),
|
||||
),
|
||||
id: seat.clone(),
|
||||
});
|
||||
}
|
||||
}
|
||||
sctk::seat::Capability::Pointer => {
|
||||
if let Some(ptr) = my_seat.ptr.take() {
|
||||
self.sctk_events.push(SctkEvent::SeatEvent {
|
||||
variant: SeatEventVariant::RemoveCapability(
|
||||
capability,
|
||||
ptr.pointer().id(),
|
||||
),
|
||||
id: seat.clone(),
|
||||
});
|
||||
}
|
||||
}
|
||||
sctk::seat::Capability::Touch => {
|
||||
if let Some(touch) = my_seat.touch.take() {
|
||||
self.sctk_events.push(SctkEvent::SeatEvent {
|
||||
variant: SeatEventVariant::RemoveCapability(
|
||||
capability,
|
||||
touch.id(),
|
||||
),
|
||||
id: seat.clone(),
|
||||
});
|
||||
}
|
||||
}
|
||||
_ => unimplemented!(),
|
||||
}
|
||||
}
|
||||
|
||||
fn remove_seat(
|
||||
&mut self,
|
||||
_conn: &sctk::reexports::client::Connection,
|
||||
_qh: &sctk::reexports::client::QueueHandle<Self>,
|
||||
seat: sctk::reexports::client::protocol::wl_seat::WlSeat,
|
||||
) {
|
||||
self.sctk_events.push(SctkEvent::SeatEvent {
|
||||
variant: SeatEventVariant::Remove,
|
||||
id: seat.clone(),
|
||||
});
|
||||
if let Some(i) = self.seats.iter().position(|s| s.seat == seat) {
|
||||
_ = self.seats.remove(i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
delegate_seat!(SctkState);
|
||||
157
winit/src/platform_specific/wayland/handlers/seat/touch.rs
Normal file
157
winit/src/platform_specific/wayland/handlers/seat/touch.rs
Normal file
|
|
@ -0,0 +1,157 @@
|
|||
// TODO handle multiple seats?
|
||||
|
||||
use crate::{
|
||||
event_loop::state::FrameStatus,
|
||||
platform_specific::wayland::{
|
||||
event_loop::state::SctkState, sctk_event::SctkEvent,
|
||||
},
|
||||
};
|
||||
use iced_runtime::core::{touch, Point};
|
||||
use sctk::{
|
||||
delegate_touch,
|
||||
reexports::client::{
|
||||
protocol::{wl_surface::WlSurface, wl_touch::WlTouch},
|
||||
Connection, Proxy, QueueHandle,
|
||||
},
|
||||
seat::touch::TouchHandler,
|
||||
};
|
||||
|
||||
impl TouchHandler for SctkState {
|
||||
fn down(
|
||||
&mut self,
|
||||
_: &Connection,
|
||||
_: &QueueHandle<Self>,
|
||||
touch: &WlTouch,
|
||||
serial: u32,
|
||||
time: u32,
|
||||
surface: WlSurface,
|
||||
id: i32,
|
||||
position: (f64, f64),
|
||||
) {
|
||||
self.request_redraw(&surface);
|
||||
let Some(my_seat) = self
|
||||
.seats
|
||||
.iter_mut()
|
||||
.find(|s| s.touch.as_ref() == Some(touch))
|
||||
else {
|
||||
return;
|
||||
};
|
||||
|
||||
_ = my_seat.last_touch_down.replace((time, id, serial));
|
||||
|
||||
let id = touch::Finger(id as u64);
|
||||
let position = Point::new(position.0 as f32, position.1 as f32);
|
||||
_ = self.touch_points.insert(id, (surface.clone(), position));
|
||||
self.sctk_events.push(SctkEvent::TouchEvent {
|
||||
variant: touch::Event::FingerPressed { id, position },
|
||||
touch_id: touch.clone(),
|
||||
seat_id: my_seat.seat.clone(),
|
||||
surface,
|
||||
});
|
||||
}
|
||||
|
||||
fn up(
|
||||
&mut self,
|
||||
_: &Connection,
|
||||
_: &QueueHandle<Self>,
|
||||
touch: &WlTouch,
|
||||
_serial: u32,
|
||||
_time: u32,
|
||||
id: i32,
|
||||
) {
|
||||
let Some(my_seat) =
|
||||
self.seats.iter().find(|s| s.touch.as_ref() == Some(touch))
|
||||
else {
|
||||
return;
|
||||
};
|
||||
|
||||
let id = touch::Finger(id as u64);
|
||||
if let Some((surface, position)) = self.touch_points.get(&id).cloned() {
|
||||
self.sctk_events.push(SctkEvent::TouchEvent {
|
||||
variant: touch::Event::FingerLifted { id, position },
|
||||
touch_id: touch.clone(),
|
||||
seat_id: my_seat.seat.clone(),
|
||||
surface,
|
||||
});
|
||||
}
|
||||
}
|
||||
fn motion(
|
||||
&mut self,
|
||||
_: &Connection,
|
||||
_: &QueueHandle<Self>,
|
||||
touch: &WlTouch,
|
||||
_time: u32,
|
||||
id: i32,
|
||||
position: (f64, f64),
|
||||
) {
|
||||
let Some(my_seat) =
|
||||
self.seats.iter().find(|s| s.touch.as_ref() == Some(touch))
|
||||
else {
|
||||
return;
|
||||
};
|
||||
|
||||
let id = touch::Finger(id as u64);
|
||||
let position = Point::new(position.0 as f32, position.1 as f32);
|
||||
if let Some((surface, position_ref)) = self.touch_points.get_mut(&id) {
|
||||
let entry = self
|
||||
.frame_status
|
||||
.entry(surface.id())
|
||||
.or_insert(FrameStatus::RequestedRedraw);
|
||||
if matches!(entry, FrameStatus::Received) {
|
||||
*entry = FrameStatus::Ready;
|
||||
}
|
||||
*position_ref = position;
|
||||
self.sctk_events.push(SctkEvent::TouchEvent {
|
||||
variant: touch::Event::FingerMoved { id, position },
|
||||
touch_id: touch.clone(),
|
||||
seat_id: my_seat.seat.clone(),
|
||||
surface: surface.clone(),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
fn shape(
|
||||
&mut self,
|
||||
_: &Connection,
|
||||
_: &QueueHandle<Self>,
|
||||
_: &WlTouch,
|
||||
_: i32,
|
||||
_: f64,
|
||||
_: f64,
|
||||
) {
|
||||
}
|
||||
|
||||
fn orientation(
|
||||
&mut self,
|
||||
_: &Connection,
|
||||
_: &QueueHandle<Self>,
|
||||
_: &WlTouch,
|
||||
_: i32,
|
||||
_: f64,
|
||||
) {
|
||||
}
|
||||
|
||||
fn cancel(
|
||||
&mut self,
|
||||
_: &Connection,
|
||||
_: &QueueHandle<Self>,
|
||||
touch: &WlTouch,
|
||||
) {
|
||||
let Some(my_seat) =
|
||||
self.seats.iter().find(|s| s.touch.as_ref() == Some(touch))
|
||||
else {
|
||||
return;
|
||||
};
|
||||
|
||||
for (id, (surface, position)) in self.touch_points.drain() {
|
||||
self.sctk_events.push(SctkEvent::TouchEvent {
|
||||
variant: touch::Event::FingerLost { id, position },
|
||||
touch_id: touch.clone(),
|
||||
seat_id: my_seat.seat.clone(),
|
||||
surface,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
delegate_touch!(SctkState);
|
||||
58
winit/src/platform_specific/wayland/handlers/session_lock.rs
Normal file
58
winit/src/platform_specific/wayland/handlers/session_lock.rs
Normal file
|
|
@ -0,0 +1,58 @@
|
|||
use crate::platform_specific::wayland::{
|
||||
handlers::SctkState, sctk_event::SctkEvent,
|
||||
};
|
||||
use sctk::{
|
||||
delegate_session_lock,
|
||||
reexports::client::{Connection, QueueHandle},
|
||||
session_lock::{
|
||||
SessionLock, SessionLockHandler, SessionLockSurface,
|
||||
SessionLockSurfaceConfigure,
|
||||
},
|
||||
};
|
||||
|
||||
impl SessionLockHandler for SctkState {
|
||||
fn locked(
|
||||
&mut self,
|
||||
_conn: &Connection,
|
||||
_qh: &QueueHandle<Self>,
|
||||
_session_lock: SessionLock,
|
||||
) {
|
||||
self.sctk_events.push(SctkEvent::SessionLocked);
|
||||
}
|
||||
|
||||
fn finished(
|
||||
&mut self,
|
||||
_conn: &Connection,
|
||||
_qh: &QueueHandle<Self>,
|
||||
_session_lock: SessionLock,
|
||||
) {
|
||||
self.sctk_events.push(SctkEvent::SessionLockFinished);
|
||||
}
|
||||
|
||||
fn configure(
|
||||
&mut self,
|
||||
_conn: &Connection,
|
||||
_qh: &QueueHandle<Self>,
|
||||
session_lock_surface: SessionLockSurface,
|
||||
configure: SessionLockSurfaceConfigure,
|
||||
_serial: u32,
|
||||
) {
|
||||
let lock_surface = match self.lock_surfaces.iter_mut().find(|s| {
|
||||
s.session_lock_surface.wl_surface()
|
||||
== session_lock_surface.wl_surface()
|
||||
}) {
|
||||
Some(l) => l,
|
||||
None => return,
|
||||
};
|
||||
let first = lock_surface.last_configure.is_none();
|
||||
_ = lock_surface.last_configure.replace(configure.clone());
|
||||
self.sctk_events
|
||||
.push(SctkEvent::SessionLockSurfaceConfigure {
|
||||
surface: session_lock_surface.wl_surface().clone(),
|
||||
configure,
|
||||
first,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
delegate_session_lock!(SctkState);
|
||||
118
winit/src/platform_specific/wayland/handlers/shell/layer.rs
Normal file
118
winit/src/platform_specific/wayland/handlers/shell/layer.rs
Normal file
|
|
@ -0,0 +1,118 @@
|
|||
use crate::platform_specific::wayland::{
|
||||
event_loop::state::SctkState,
|
||||
sctk_event::{LayerSurfaceEventVariant, SctkEvent},
|
||||
};
|
||||
use sctk::{
|
||||
delegate_layer,
|
||||
reexports::client::Proxy,
|
||||
shell::{
|
||||
wlr_layer::{Anchor, KeyboardInteractivity, LayerShellHandler},
|
||||
WaylandSurface,
|
||||
},
|
||||
};
|
||||
use std::fmt::Debug;
|
||||
use winit::dpi::LogicalSize;
|
||||
|
||||
impl LayerShellHandler for SctkState {
|
||||
fn closed(
|
||||
&mut self,
|
||||
_conn: &sctk::reexports::client::Connection,
|
||||
_qh: &sctk::reexports::client::QueueHandle<Self>,
|
||||
layer: &sctk::shell::wlr_layer::LayerSurface,
|
||||
) {
|
||||
let layer = match self.layer_surfaces.iter().position(|s| {
|
||||
s.surface.wl_surface().id() == layer.wl_surface().id()
|
||||
}) {
|
||||
Some(w) => self.layer_surfaces.remove(w),
|
||||
None => return,
|
||||
};
|
||||
|
||||
self.sctk_events.push(SctkEvent::LayerSurfaceEvent {
|
||||
variant: LayerSurfaceEventVariant::Done,
|
||||
id: layer.surface.wl_surface().clone(),
|
||||
})
|
||||
// TODO popup cleanup
|
||||
}
|
||||
|
||||
fn configure(
|
||||
&mut self,
|
||||
_conn: &sctk::reexports::client::Connection,
|
||||
_qh: &sctk::reexports::client::QueueHandle<Self>,
|
||||
layer: &sctk::shell::wlr_layer::LayerSurface,
|
||||
mut configure: sctk::shell::wlr_layer::LayerSurfaceConfigure,
|
||||
_serial: u32,
|
||||
) {
|
||||
self.request_redraw(layer.wl_surface());
|
||||
let layer =
|
||||
match self.layer_surfaces.iter_mut().find(|s| {
|
||||
s.surface.wl_surface().id() == layer.wl_surface().id()
|
||||
}) {
|
||||
Some(l) => l,
|
||||
None => return,
|
||||
};
|
||||
let common = layer.common.lock().unwrap();
|
||||
let requested_size = common.requested_size;
|
||||
drop(common);
|
||||
configure.new_size.0 = if let Some(w) = requested_size.0 {
|
||||
w
|
||||
} else {
|
||||
configure.new_size.0.max(1)
|
||||
};
|
||||
configure.new_size.1 = if let Some(h) = requested_size.1 {
|
||||
h
|
||||
} else {
|
||||
configure.new_size.1.max(1)
|
||||
};
|
||||
|
||||
layer.update_viewport(configure.new_size.0, configure.new_size.1);
|
||||
let first = layer.last_configure.is_none();
|
||||
_ = layer.last_configure.replace(configure.clone());
|
||||
let mut common = layer.common.lock().unwrap();
|
||||
common.size =
|
||||
LogicalSize::new(configure.new_size.0, configure.new_size.1);
|
||||
self.sctk_events.push(SctkEvent::LayerSurfaceEvent {
|
||||
variant: LayerSurfaceEventVariant::Configure(
|
||||
configure,
|
||||
layer.surface.wl_surface().clone(),
|
||||
first,
|
||||
),
|
||||
id: layer.surface.wl_surface().clone(),
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
delegate_layer!(SctkState);
|
||||
|
||||
#[allow(dead_code)]
|
||||
/// A request to SCTK window from Winit window.
|
||||
#[derive(Debug, Clone)]
|
||||
pub enum LayerSurfaceRequest {
|
||||
/// Set fullscreen.
|
||||
///
|
||||
/// Passing `None` will set it on the current monitor.
|
||||
Size(LogicalSize<u32>),
|
||||
|
||||
/// Unset fullscreen.
|
||||
UnsetFullscreen,
|
||||
|
||||
/// Show cursor for the certain window or not.
|
||||
ShowCursor(bool),
|
||||
|
||||
/// Set anchor
|
||||
Anchor(Anchor),
|
||||
|
||||
/// Set margin
|
||||
ExclusiveZone(i32),
|
||||
|
||||
/// Set margin
|
||||
Margin(u32),
|
||||
|
||||
/// Passthrough mouse input to underlying windows.
|
||||
KeyboardInteractivity(KeyboardInteractivity),
|
||||
|
||||
/// Redraw was requested.
|
||||
Redraw,
|
||||
|
||||
/// Window should be closed.
|
||||
Close,
|
||||
}
|
||||
|
|
@ -0,0 +1,3 @@
|
|||
pub mod layer;
|
||||
pub mod xdg_popup;
|
||||
pub mod xdg_window;
|
||||
|
|
@ -0,0 +1,92 @@
|
|||
use crate::platform_specific::wayland::{
|
||||
event_loop::state::{self, PopupParent, SctkState},
|
||||
sctk_event::{PopupEventVariant, SctkEvent},
|
||||
};
|
||||
use sctk::{
|
||||
delegate_xdg_popup, reexports::client::Proxy,
|
||||
shell::xdg::popup::PopupHandler,
|
||||
};
|
||||
|
||||
impl PopupHandler for SctkState {
|
||||
fn configure(
|
||||
&mut self,
|
||||
_conn: &sctk::reexports::client::Connection,
|
||||
_qh: &sctk::reexports::client::QueueHandle<Self>,
|
||||
popup: &sctk::shell::xdg::popup::Popup,
|
||||
configure: sctk::shell::xdg::popup::PopupConfigure,
|
||||
) {
|
||||
self.request_redraw(popup.wl_surface());
|
||||
let sctk_popup = match self.popups.iter_mut().find(|s| {
|
||||
s.popup.wl_surface().clone() == popup.wl_surface().clone()
|
||||
}) {
|
||||
Some(p) => p,
|
||||
None => return,
|
||||
};
|
||||
let first = sctk_popup.last_configure.is_none();
|
||||
_ = sctk_popup.last_configure.replace(configure.clone());
|
||||
|
||||
self.sctk_events.push(SctkEvent::PopupEvent {
|
||||
variant: PopupEventVariant::Configure(
|
||||
configure,
|
||||
popup.wl_surface().clone(),
|
||||
first,
|
||||
),
|
||||
id: popup.wl_surface().clone(),
|
||||
toplevel_id: sctk_popup.data.toplevel.clone(),
|
||||
parent_id: match &sctk_popup.data.parent {
|
||||
PopupParent::LayerSurface(s) => s.clone(),
|
||||
PopupParent::Window(s) => s.clone(),
|
||||
PopupParent::Popup(s) => s.clone(),
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
fn done(
|
||||
&mut self,
|
||||
_conn: &sctk::reexports::client::Connection,
|
||||
_qh: &sctk::reexports::client::QueueHandle<Self>,
|
||||
popup: &sctk::shell::xdg::popup::Popup,
|
||||
) {
|
||||
let sctk_popup = match self.popups.iter().position(|s| {
|
||||
s.popup.wl_surface().clone() == popup.wl_surface().clone()
|
||||
}) {
|
||||
Some(p) => self.popups.remove(p),
|
||||
None => return,
|
||||
};
|
||||
let mut to_destroy = vec![sctk_popup];
|
||||
while let Some(popup_to_destroy) = to_destroy.last() {
|
||||
match popup_to_destroy.data.parent.clone() {
|
||||
state::PopupParent::LayerSurface(_)
|
||||
| state::PopupParent::Window(_) => {
|
||||
break;
|
||||
}
|
||||
state::PopupParent::Popup(popup_to_destroy_first) => {
|
||||
let popup_to_destroy_first = self
|
||||
.popups
|
||||
.iter()
|
||||
.position(|p| {
|
||||
p.popup.wl_surface() == &popup_to_destroy_first
|
||||
})
|
||||
.unwrap();
|
||||
let popup_to_destroy_first =
|
||||
self.popups.remove(popup_to_destroy_first);
|
||||
to_destroy.push(popup_to_destroy_first);
|
||||
}
|
||||
}
|
||||
}
|
||||
for popup in to_destroy.into_iter().rev() {
|
||||
if let Some(id) = self.id_map.remove(&popup.popup.wl_surface().id())
|
||||
{
|
||||
_ = self.destroyed.insert(id);
|
||||
}
|
||||
|
||||
self.sctk_events.push(SctkEvent::PopupEvent {
|
||||
variant: PopupEventVariant::Done,
|
||||
toplevel_id: popup.data.toplevel.clone(),
|
||||
parent_id: popup.data.parent.wl_surface().clone(),
|
||||
id: popup.popup.wl_surface().clone(),
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
delegate_xdg_popup!(SctkState);
|
||||
|
|
@ -0,0 +1,27 @@
|
|||
use crate::platform_specific::wayland::event_loop::state::SctkState;
|
||||
use sctk::{
|
||||
delegate_xdg_shell, delegate_xdg_window, shell::xdg::window::WindowHandler,
|
||||
};
|
||||
|
||||
impl WindowHandler for SctkState {
|
||||
fn request_close(
|
||||
&mut self,
|
||||
_conn: &sctk::reexports::client::Connection,
|
||||
_qh: &sctk::reexports::client::QueueHandle<Self>,
|
||||
_window: &sctk::shell::xdg::window::Window,
|
||||
) {
|
||||
}
|
||||
|
||||
fn configure(
|
||||
&mut self,
|
||||
_conn: &sctk::reexports::client::Connection,
|
||||
_qh: &sctk::reexports::client::QueueHandle<Self>,
|
||||
_window: &sctk::shell::xdg::window::Window,
|
||||
_configure: sctk::shell::xdg::window::WindowConfigure,
|
||||
_serial: u32,
|
||||
) {
|
||||
}
|
||||
}
|
||||
|
||||
delegate_xdg_window!(SctkState);
|
||||
delegate_xdg_shell!(SctkState);
|
||||
|
|
@ -0,0 +1,4 @@
|
|||
use crate::platform_specific::wayland::handlers::SctkState;
|
||||
use sctk::delegate_subcompositor;
|
||||
|
||||
delegate_subcompositor!(SctkState);
|
||||
|
|
@ -0,0 +1,91 @@
|
|||
// From: https://github.com/rust-windowing/winit/blob/master/src/platform_impl/linux/wayland/types/wp_fractional_scaling.rs
|
||||
//! Handling of the fractional scaling.
|
||||
|
||||
|
||||
use sctk::reexports::client::globals::{BindError, GlobalList};
|
||||
use sctk::reexports::client::protocol::wl_surface::WlSurface;
|
||||
use sctk::reexports::client::Dispatch;
|
||||
use sctk::reexports::client::{delegate_dispatch, Connection, Proxy, QueueHandle};
|
||||
use sctk::reexports::protocols::wp::fractional_scale::v1::client::wp_fractional_scale_manager_v1::WpFractionalScaleManagerV1;
|
||||
use sctk::reexports::protocols::wp::fractional_scale::v1::client::wp_fractional_scale_v1::Event as FractionalScalingEvent;
|
||||
use sctk::reexports::protocols::wp::fractional_scale::v1::client::wp_fractional_scale_v1::WpFractionalScaleV1;
|
||||
|
||||
use sctk::globals::GlobalData;
|
||||
|
||||
use crate::platform_specific::wayland::event_loop::state::SctkState;
|
||||
|
||||
/// The scaling factor denominator.
|
||||
const SCALE_DENOMINATOR: f64 = 120.;
|
||||
|
||||
/// Fractional scaling manager.
|
||||
#[derive(Debug)]
|
||||
pub struct FractionalScalingManager {
|
||||
manager: WpFractionalScaleManagerV1,
|
||||
}
|
||||
|
||||
pub struct FractionalScaling {
|
||||
/// The surface used for scaling.
|
||||
surface: WlSurface,
|
||||
}
|
||||
|
||||
impl FractionalScalingManager {
|
||||
/// Create new viewporter.
|
||||
pub fn new(
|
||||
globals: &GlobalList,
|
||||
queue_handle: &QueueHandle<SctkState>,
|
||||
) -> Result<Self, BindError> {
|
||||
let manager = globals.bind(queue_handle, 1..=1, GlobalData)?;
|
||||
Ok(Self { manager })
|
||||
}
|
||||
|
||||
pub fn fractional_scaling(
|
||||
&self,
|
||||
surface: &WlSurface,
|
||||
queue_handle: &QueueHandle<SctkState>,
|
||||
) -> WpFractionalScaleV1 {
|
||||
let data = FractionalScaling {
|
||||
surface: surface.clone(),
|
||||
};
|
||||
self.manager
|
||||
.get_fractional_scale(surface, queue_handle, data)
|
||||
}
|
||||
}
|
||||
|
||||
impl Dispatch<WpFractionalScaleManagerV1, GlobalData, SctkState>
|
||||
for FractionalScalingManager
|
||||
{
|
||||
fn event(
|
||||
_: &mut SctkState,
|
||||
_: &WpFractionalScaleManagerV1,
|
||||
_: <WpFractionalScaleManagerV1 as Proxy>::Event,
|
||||
_: &GlobalData,
|
||||
_: &Connection,
|
||||
_: &QueueHandle<SctkState>,
|
||||
) {
|
||||
// No events.
|
||||
}
|
||||
}
|
||||
|
||||
impl Dispatch<WpFractionalScaleV1, FractionalScaling, SctkState>
|
||||
for FractionalScalingManager
|
||||
{
|
||||
fn event(
|
||||
state: &mut SctkState,
|
||||
_: &WpFractionalScaleV1,
|
||||
event: <WpFractionalScaleV1 as Proxy>::Event,
|
||||
data: &FractionalScaling,
|
||||
_: &Connection,
|
||||
_: &QueueHandle<SctkState>,
|
||||
) {
|
||||
if let FractionalScalingEvent::PreferredScale { scale } = event {
|
||||
state.scale_factor_changed(
|
||||
&data.surface,
|
||||
scale as f64 / SCALE_DENOMINATOR,
|
||||
false,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
delegate_dispatch!(SctkState: [WpFractionalScaleManagerV1: GlobalData] => FractionalScalingManager);
|
||||
delegate_dispatch!(SctkState: [WpFractionalScaleV1: FractionalScaling] => FractionalScalingManager);
|
||||
|
|
@ -0,0 +1,71 @@
|
|||
//! Handling of the wp-viewporter.
|
||||
|
||||
|
||||
use sctk::reexports::client::globals::{BindError, GlobalList};
|
||||
use sctk::reexports::client::protocol::wl_surface::WlSurface;
|
||||
use sctk::reexports::client::Dispatch;
|
||||
use sctk::reexports::client::{
|
||||
delegate_dispatch, Connection, Proxy, QueueHandle,
|
||||
};
|
||||
use sctk::reexports::protocols::wp::viewporter::client::wp_viewport::WpViewport;
|
||||
use sctk::reexports::protocols::wp::viewporter::client::wp_viewporter::WpViewporter;
|
||||
|
||||
use sctk::globals::GlobalData;
|
||||
|
||||
use crate::platform_specific::wayland::event_loop::state::SctkState;
|
||||
|
||||
/// Viewporter.
|
||||
#[derive(Debug)]
|
||||
pub struct ViewporterState {
|
||||
viewporter: WpViewporter,
|
||||
}
|
||||
|
||||
impl ViewporterState {
|
||||
/// Create new viewporter.
|
||||
pub fn new(
|
||||
globals: &GlobalList,
|
||||
queue_handle: &QueueHandle<SctkState>,
|
||||
) -> Result<Self, BindError> {
|
||||
let viewporter = globals.bind(queue_handle, 1..=1, GlobalData)?;
|
||||
Ok(Self { viewporter })
|
||||
}
|
||||
|
||||
/// Get the viewport for the given object.
|
||||
pub fn get_viewport(
|
||||
&self,
|
||||
surface: &WlSurface,
|
||||
queue_handle: &QueueHandle<SctkState>,
|
||||
) -> WpViewport {
|
||||
self.viewporter
|
||||
.get_viewport(surface, queue_handle, GlobalData)
|
||||
}
|
||||
}
|
||||
|
||||
impl Dispatch<WpViewporter, GlobalData, SctkState> for ViewporterState {
|
||||
fn event(
|
||||
_: &mut SctkState,
|
||||
_: &WpViewporter,
|
||||
_: <WpViewporter as Proxy>::Event,
|
||||
_: &GlobalData,
|
||||
_: &Connection,
|
||||
_: &QueueHandle<SctkState>,
|
||||
) {
|
||||
// No events.
|
||||
}
|
||||
}
|
||||
|
||||
impl Dispatch<WpViewport, GlobalData, SctkState> for ViewporterState {
|
||||
fn event(
|
||||
_: &mut SctkState,
|
||||
_: &WpViewport,
|
||||
_: <WpViewport as Proxy>::Event,
|
||||
_: &GlobalData,
|
||||
_: &Connection,
|
||||
_: &QueueHandle<SctkState>,
|
||||
) {
|
||||
// No events.
|
||||
}
|
||||
}
|
||||
|
||||
delegate_dispatch!(SctkState: [WpViewporter: GlobalData] => ViewporterState);
|
||||
delegate_dispatch!(SctkState: [WpViewport: GlobalData] => ViewporterState);
|
||||
895
winit/src/platform_specific/wayland/keymap.rs
Normal file
895
winit/src/platform_specific/wayland/keymap.rs
Normal file
|
|
@ -0,0 +1,895 @@
|
|||
// Borrowed from winit
|
||||
use winit::keyboard::{KeyCode, NativeKeyCode, PhysicalKey};
|
||||
/// Map the raw X11-style keycode to the `KeyCode` enum.
|
||||
///
|
||||
/// X11-style keycodes are offset by 8 from the keycodes the Linux kernel uses.
|
||||
pub fn raw_keycode_to_physicalkey(keycode: u32) -> PhysicalKey {
|
||||
scancode_to_physicalkey(keycode.saturating_sub(8))
|
||||
}
|
||||
|
||||
/// Map the linux scancode to Keycode.
|
||||
///
|
||||
/// Both X11 and Wayland use keys with `+ 8` offset to linux scancode.
|
||||
pub fn scancode_to_physicalkey(scancode: u32) -> PhysicalKey {
|
||||
// The keycode values are taken from linux/include/uapi/linux/input-event-codes.h, as
|
||||
// libxkbcommon's documentation seems to suggest that the keycode values we're interested in
|
||||
// are defined by the Linux kernel. If Winit programs end up being run on other Unix-likes,
|
||||
// I can only hope they agree on what the keycodes mean.
|
||||
//
|
||||
// Some of the keycodes are likely superfluous for our purposes, and some are ones which are
|
||||
// difficult to test the correctness of, or discover the purpose of. Because of this, they've
|
||||
// either been commented out here, or not included at all.
|
||||
PhysicalKey::Code(match scancode {
|
||||
0 => return PhysicalKey::Unidentified(NativeKeyCode::Xkb(0)),
|
||||
1 => KeyCode::Escape,
|
||||
2 => KeyCode::Digit1,
|
||||
3 => KeyCode::Digit2,
|
||||
4 => KeyCode::Digit3,
|
||||
5 => KeyCode::Digit4,
|
||||
6 => KeyCode::Digit5,
|
||||
7 => KeyCode::Digit6,
|
||||
8 => KeyCode::Digit7,
|
||||
9 => KeyCode::Digit8,
|
||||
10 => KeyCode::Digit9,
|
||||
11 => KeyCode::Digit0,
|
||||
12 => KeyCode::Minus,
|
||||
13 => KeyCode::Equal,
|
||||
14 => KeyCode::Backspace,
|
||||
15 => KeyCode::Tab,
|
||||
16 => KeyCode::KeyQ,
|
||||
17 => KeyCode::KeyW,
|
||||
18 => KeyCode::KeyE,
|
||||
19 => KeyCode::KeyR,
|
||||
20 => KeyCode::KeyT,
|
||||
21 => KeyCode::KeyY,
|
||||
22 => KeyCode::KeyU,
|
||||
23 => KeyCode::KeyI,
|
||||
24 => KeyCode::KeyO,
|
||||
25 => KeyCode::KeyP,
|
||||
26 => KeyCode::BracketLeft,
|
||||
27 => KeyCode::BracketRight,
|
||||
28 => KeyCode::Enter,
|
||||
29 => KeyCode::ControlLeft,
|
||||
30 => KeyCode::KeyA,
|
||||
31 => KeyCode::KeyS,
|
||||
32 => KeyCode::KeyD,
|
||||
33 => KeyCode::KeyF,
|
||||
34 => KeyCode::KeyG,
|
||||
35 => KeyCode::KeyH,
|
||||
36 => KeyCode::KeyJ,
|
||||
37 => KeyCode::KeyK,
|
||||
38 => KeyCode::KeyL,
|
||||
39 => KeyCode::Semicolon,
|
||||
40 => KeyCode::Quote,
|
||||
41 => KeyCode::Backquote,
|
||||
42 => KeyCode::ShiftLeft,
|
||||
43 => KeyCode::Backslash,
|
||||
44 => KeyCode::KeyZ,
|
||||
45 => KeyCode::KeyX,
|
||||
46 => KeyCode::KeyC,
|
||||
47 => KeyCode::KeyV,
|
||||
48 => KeyCode::KeyB,
|
||||
49 => KeyCode::KeyN,
|
||||
50 => KeyCode::KeyM,
|
||||
51 => KeyCode::Comma,
|
||||
52 => KeyCode::Period,
|
||||
53 => KeyCode::Slash,
|
||||
54 => KeyCode::ShiftRight,
|
||||
55 => KeyCode::NumpadMultiply,
|
||||
56 => KeyCode::AltLeft,
|
||||
57 => KeyCode::Space,
|
||||
58 => KeyCode::CapsLock,
|
||||
59 => KeyCode::F1,
|
||||
60 => KeyCode::F2,
|
||||
61 => KeyCode::F3,
|
||||
62 => KeyCode::F4,
|
||||
63 => KeyCode::F5,
|
||||
64 => KeyCode::F6,
|
||||
65 => KeyCode::F7,
|
||||
66 => KeyCode::F8,
|
||||
67 => KeyCode::F9,
|
||||
68 => KeyCode::F10,
|
||||
69 => KeyCode::NumLock,
|
||||
70 => KeyCode::ScrollLock,
|
||||
71 => KeyCode::Numpad7,
|
||||
72 => KeyCode::Numpad8,
|
||||
73 => KeyCode::Numpad9,
|
||||
74 => KeyCode::NumpadSubtract,
|
||||
75 => KeyCode::Numpad4,
|
||||
76 => KeyCode::Numpad5,
|
||||
77 => KeyCode::Numpad6,
|
||||
78 => KeyCode::NumpadAdd,
|
||||
79 => KeyCode::Numpad1,
|
||||
80 => KeyCode::Numpad2,
|
||||
81 => KeyCode::Numpad3,
|
||||
82 => KeyCode::Numpad0,
|
||||
83 => KeyCode::NumpadDecimal,
|
||||
85 => KeyCode::Lang5,
|
||||
86 => KeyCode::IntlBackslash,
|
||||
87 => KeyCode::F11,
|
||||
88 => KeyCode::F12,
|
||||
89 => KeyCode::IntlRo,
|
||||
90 => KeyCode::Lang3,
|
||||
91 => KeyCode::Lang4,
|
||||
92 => KeyCode::Convert,
|
||||
93 => KeyCode::KanaMode,
|
||||
94 => KeyCode::NonConvert,
|
||||
// 95 => KeyCode::KPJPCOMMA,
|
||||
96 => KeyCode::NumpadEnter,
|
||||
97 => KeyCode::ControlRight,
|
||||
98 => KeyCode::NumpadDivide,
|
||||
99 => KeyCode::PrintScreen,
|
||||
100 => KeyCode::AltRight,
|
||||
// 101 => KeyCode::LINEFEED,
|
||||
102 => KeyCode::Home,
|
||||
103 => KeyCode::ArrowUp,
|
||||
104 => KeyCode::PageUp,
|
||||
105 => KeyCode::ArrowLeft,
|
||||
106 => KeyCode::ArrowRight,
|
||||
107 => KeyCode::End,
|
||||
108 => KeyCode::ArrowDown,
|
||||
109 => KeyCode::PageDown,
|
||||
110 => KeyCode::Insert,
|
||||
111 => KeyCode::Delete,
|
||||
// 112 => KeyCode::MACRO,
|
||||
113 => KeyCode::AudioVolumeMute,
|
||||
114 => KeyCode::AudioVolumeDown,
|
||||
115 => KeyCode::AudioVolumeUp,
|
||||
// 116 => KeyCode::POWER,
|
||||
117 => KeyCode::NumpadEqual,
|
||||
// 118 => KeyCode::KPPLUSMINUS,
|
||||
119 => KeyCode::Pause,
|
||||
// 120 => KeyCode::SCALE,
|
||||
121 => KeyCode::NumpadComma,
|
||||
122 => KeyCode::Lang1,
|
||||
123 => KeyCode::Lang2,
|
||||
124 => KeyCode::IntlYen,
|
||||
125 => KeyCode::MetaLeft,
|
||||
126 => KeyCode::MetaRight,
|
||||
127 => KeyCode::ContextMenu,
|
||||
// 128 => KeyCode::STOP,
|
||||
// 129 => KeyCode::AGAIN,
|
||||
// 130 => KeyCode::PROPS,
|
||||
// 131 => KeyCode::UNDO,
|
||||
// 132 => KeyCode::FRONT,
|
||||
// 133 => KeyCode::COPY,
|
||||
// 134 => KeyCode::OPEN,
|
||||
// 135 => KeyCode::PASTE,
|
||||
// 136 => KeyCode::FIND,
|
||||
// 137 => KeyCode::CUT,
|
||||
// 138 => KeyCode::HELP,
|
||||
// 139 => KeyCode::MENU,
|
||||
// 140 => KeyCode::CALC,
|
||||
// 141 => KeyCode::SETUP,
|
||||
// 142 => KeyCode::SLEEP,
|
||||
// 143 => KeyCode::WAKEUP,
|
||||
// 144 => KeyCode::FILE,
|
||||
// 145 => KeyCode::SENDFILE,
|
||||
// 146 => KeyCode::DELETEFILE,
|
||||
// 147 => KeyCode::XFER,
|
||||
// 148 => KeyCode::PROG1,
|
||||
// 149 => KeyCode::PROG2,
|
||||
// 150 => KeyCode::WWW,
|
||||
// 151 => KeyCode::MSDOS,
|
||||
// 152 => KeyCode::COFFEE,
|
||||
// 153 => KeyCode::ROTATE_DISPLAY,
|
||||
// 154 => KeyCode::CYCLEWINDOWS,
|
||||
// 155 => KeyCode::MAIL,
|
||||
// 156 => KeyCode::BOOKMARKS,
|
||||
// 157 => KeyCode::COMPUTER,
|
||||
// 158 => KeyCode::BACK,
|
||||
// 159 => KeyCode::FORWARD,
|
||||
// 160 => KeyCode::CLOSECD,
|
||||
// 161 => KeyCode::EJECTCD,
|
||||
// 162 => KeyCode::EJECTCLOSECD,
|
||||
163 => KeyCode::MediaTrackNext,
|
||||
164 => KeyCode::MediaPlayPause,
|
||||
165 => KeyCode::MediaTrackPrevious,
|
||||
166 => KeyCode::MediaStop,
|
||||
// 167 => KeyCode::RECORD,
|
||||
// 168 => KeyCode::REWIND,
|
||||
// 169 => KeyCode::PHONE,
|
||||
// 170 => KeyCode::ISO,
|
||||
// 171 => KeyCode::CONFIG,
|
||||
// 172 => KeyCode::HOMEPAGE,
|
||||
// 173 => KeyCode::REFRESH,
|
||||
// 174 => KeyCode::EXIT,
|
||||
// 175 => KeyCode::MOVE,
|
||||
// 176 => KeyCode::EDIT,
|
||||
// 177 => KeyCode::SCROLLUP,
|
||||
// 178 => KeyCode::SCROLLDOWN,
|
||||
// 179 => KeyCode::KPLEFTPAREN,
|
||||
// 180 => KeyCode::KPRIGHTPAREN,
|
||||
// 181 => KeyCode::NEW,
|
||||
// 182 => KeyCode::REDO,
|
||||
183 => KeyCode::F13,
|
||||
184 => KeyCode::F14,
|
||||
185 => KeyCode::F15,
|
||||
186 => KeyCode::F16,
|
||||
187 => KeyCode::F17,
|
||||
188 => KeyCode::F18,
|
||||
189 => KeyCode::F19,
|
||||
190 => KeyCode::F20,
|
||||
191 => KeyCode::F21,
|
||||
192 => KeyCode::F22,
|
||||
193 => KeyCode::F23,
|
||||
194 => KeyCode::F24,
|
||||
// 200 => KeyCode::PLAYCD,
|
||||
// 201 => KeyCode::PAUSECD,
|
||||
// 202 => KeyCode::PROG3,
|
||||
// 203 => KeyCode::PROG4,
|
||||
// 204 => KeyCode::DASHBOARD,
|
||||
// 205 => KeyCode::SUSPEND,
|
||||
// 206 => KeyCode::CLOSE,
|
||||
// 207 => KeyCode::PLAY,
|
||||
// 208 => KeyCode::FASTFORWARD,
|
||||
// 209 => KeyCode::BASSBOOST,
|
||||
// 210 => KeyCode::PRINT,
|
||||
// 211 => KeyCode::HP,
|
||||
// 212 => KeyCode::CAMERA,
|
||||
// 213 => KeyCode::SOUND,
|
||||
// 214 => KeyCode::QUESTION,
|
||||
// 215 => KeyCode::EMAIL,
|
||||
// 216 => KeyCode::CHAT,
|
||||
// 217 => KeyCode::SEARCH,
|
||||
// 218 => KeyCode::CONNECT,
|
||||
// 219 => KeyCode::FINANCE,
|
||||
// 220 => KeyCode::SPORT,
|
||||
// 221 => KeyCode::SHOP,
|
||||
// 222 => KeyCode::ALTERASE,
|
||||
// 223 => KeyCode::CANCEL,
|
||||
// 224 => KeyCode::BRIGHTNESSDOW,
|
||||
// 225 => KeyCode::BRIGHTNESSU,
|
||||
// 226 => KeyCode::MEDIA,
|
||||
// 227 => KeyCode::SWITCHVIDEOMODE,
|
||||
// 228 => KeyCode::KBDILLUMTOGGLE,
|
||||
// 229 => KeyCode::KBDILLUMDOWN,
|
||||
// 230 => KeyCode::KBDILLUMUP,
|
||||
// 231 => KeyCode::SEND,
|
||||
// 232 => KeyCode::REPLY,
|
||||
// 233 => KeyCode::FORWARDMAIL,
|
||||
// 234 => KeyCode::SAVE,
|
||||
// 235 => KeyCode::DOCUMENTS,
|
||||
// 236 => KeyCode::BATTERY,
|
||||
// 237 => KeyCode::BLUETOOTH,
|
||||
// 238 => KeyCode::WLAN,
|
||||
// 239 => KeyCode::UWB,
|
||||
240 => return PhysicalKey::Unidentified(NativeKeyCode::Unidentified),
|
||||
// 241 => KeyCode::VIDEO_NEXT,
|
||||
// 242 => KeyCode::VIDEO_PREV,
|
||||
// 243 => KeyCode::BRIGHTNESS_CYCLE,
|
||||
// 244 => KeyCode::BRIGHTNESS_AUTO,
|
||||
// 245 => KeyCode::DISPLAY_OFF,
|
||||
// 246 => KeyCode::WWAN,
|
||||
// 247 => KeyCode::RFKILL,
|
||||
// 248 => KeyCode::KEY_MICMUTE,
|
||||
_ => return PhysicalKey::Unidentified(NativeKeyCode::Xkb(scancode)),
|
||||
})
|
||||
}
|
||||
|
||||
pub fn physicalkey_to_scancode(key: PhysicalKey) -> Option<u32> {
|
||||
let code = match key {
|
||||
PhysicalKey::Code(code) => code,
|
||||
PhysicalKey::Unidentified(code) => {
|
||||
return match code {
|
||||
NativeKeyCode::Unidentified => Some(240),
|
||||
NativeKeyCode::Xkb(raw) => Some(raw),
|
||||
_ => None,
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
match code {
|
||||
KeyCode::Escape => Some(1),
|
||||
KeyCode::Digit1 => Some(2),
|
||||
KeyCode::Digit2 => Some(3),
|
||||
KeyCode::Digit3 => Some(4),
|
||||
KeyCode::Digit4 => Some(5),
|
||||
KeyCode::Digit5 => Some(6),
|
||||
KeyCode::Digit6 => Some(7),
|
||||
KeyCode::Digit7 => Some(8),
|
||||
KeyCode::Digit8 => Some(9),
|
||||
KeyCode::Digit9 => Some(10),
|
||||
KeyCode::Digit0 => Some(11),
|
||||
KeyCode::Minus => Some(12),
|
||||
KeyCode::Equal => Some(13),
|
||||
KeyCode::Backspace => Some(14),
|
||||
KeyCode::Tab => Some(15),
|
||||
KeyCode::KeyQ => Some(16),
|
||||
KeyCode::KeyW => Some(17),
|
||||
KeyCode::KeyE => Some(18),
|
||||
KeyCode::KeyR => Some(19),
|
||||
KeyCode::KeyT => Some(20),
|
||||
KeyCode::KeyY => Some(21),
|
||||
KeyCode::KeyU => Some(22),
|
||||
KeyCode::KeyI => Some(23),
|
||||
KeyCode::KeyO => Some(24),
|
||||
KeyCode::KeyP => Some(25),
|
||||
KeyCode::BracketLeft => Some(26),
|
||||
KeyCode::BracketRight => Some(27),
|
||||
KeyCode::Enter => Some(28),
|
||||
KeyCode::ControlLeft => Some(29),
|
||||
KeyCode::KeyA => Some(30),
|
||||
KeyCode::KeyS => Some(31),
|
||||
KeyCode::KeyD => Some(32),
|
||||
KeyCode::KeyF => Some(33),
|
||||
KeyCode::KeyG => Some(34),
|
||||
KeyCode::KeyH => Some(35),
|
||||
KeyCode::KeyJ => Some(36),
|
||||
KeyCode::KeyK => Some(37),
|
||||
KeyCode::KeyL => Some(38),
|
||||
KeyCode::Semicolon => Some(39),
|
||||
KeyCode::Quote => Some(40),
|
||||
KeyCode::Backquote => Some(41),
|
||||
KeyCode::ShiftLeft => Some(42),
|
||||
KeyCode::Backslash => Some(43),
|
||||
KeyCode::KeyZ => Some(44),
|
||||
KeyCode::KeyX => Some(45),
|
||||
KeyCode::KeyC => Some(46),
|
||||
KeyCode::KeyV => Some(47),
|
||||
KeyCode::KeyB => Some(48),
|
||||
KeyCode::KeyN => Some(49),
|
||||
KeyCode::KeyM => Some(50),
|
||||
KeyCode::Comma => Some(51),
|
||||
KeyCode::Period => Some(52),
|
||||
KeyCode::Slash => Some(53),
|
||||
KeyCode::ShiftRight => Some(54),
|
||||
KeyCode::NumpadMultiply => Some(55),
|
||||
KeyCode::AltLeft => Some(56),
|
||||
KeyCode::Space => Some(57),
|
||||
KeyCode::CapsLock => Some(58),
|
||||
KeyCode::F1 => Some(59),
|
||||
KeyCode::F2 => Some(60),
|
||||
KeyCode::F3 => Some(61),
|
||||
KeyCode::F4 => Some(62),
|
||||
KeyCode::F5 => Some(63),
|
||||
KeyCode::F6 => Some(64),
|
||||
KeyCode::F7 => Some(65),
|
||||
KeyCode::F8 => Some(66),
|
||||
KeyCode::F9 => Some(67),
|
||||
KeyCode::F10 => Some(68),
|
||||
KeyCode::NumLock => Some(69),
|
||||
KeyCode::ScrollLock => Some(70),
|
||||
KeyCode::Numpad7 => Some(71),
|
||||
KeyCode::Numpad8 => Some(72),
|
||||
KeyCode::Numpad9 => Some(73),
|
||||
KeyCode::NumpadSubtract => Some(74),
|
||||
KeyCode::Numpad4 => Some(75),
|
||||
KeyCode::Numpad5 => Some(76),
|
||||
KeyCode::Numpad6 => Some(77),
|
||||
KeyCode::NumpadAdd => Some(78),
|
||||
KeyCode::Numpad1 => Some(79),
|
||||
KeyCode::Numpad2 => Some(80),
|
||||
KeyCode::Numpad3 => Some(81),
|
||||
KeyCode::Numpad0 => Some(82),
|
||||
KeyCode::NumpadDecimal => Some(83),
|
||||
KeyCode::Lang5 => Some(85),
|
||||
KeyCode::IntlBackslash => Some(86),
|
||||
KeyCode::F11 => Some(87),
|
||||
KeyCode::F12 => Some(88),
|
||||
KeyCode::IntlRo => Some(89),
|
||||
KeyCode::Lang3 => Some(90),
|
||||
KeyCode::Lang4 => Some(91),
|
||||
KeyCode::Convert => Some(92),
|
||||
KeyCode::KanaMode => Some(93),
|
||||
KeyCode::NonConvert => Some(94),
|
||||
KeyCode::NumpadEnter => Some(96),
|
||||
KeyCode::ControlRight => Some(97),
|
||||
KeyCode::NumpadDivide => Some(98),
|
||||
KeyCode::PrintScreen => Some(99),
|
||||
KeyCode::AltRight => Some(100),
|
||||
KeyCode::Home => Some(102),
|
||||
KeyCode::ArrowUp => Some(103),
|
||||
KeyCode::PageUp => Some(104),
|
||||
KeyCode::ArrowLeft => Some(105),
|
||||
KeyCode::ArrowRight => Some(106),
|
||||
KeyCode::End => Some(107),
|
||||
KeyCode::ArrowDown => Some(108),
|
||||
KeyCode::PageDown => Some(109),
|
||||
KeyCode::Insert => Some(110),
|
||||
KeyCode::Delete => Some(111),
|
||||
KeyCode::AudioVolumeMute => Some(113),
|
||||
KeyCode::AudioVolumeDown => Some(114),
|
||||
KeyCode::AudioVolumeUp => Some(115),
|
||||
KeyCode::NumpadEqual => Some(117),
|
||||
KeyCode::Pause => Some(119),
|
||||
KeyCode::NumpadComma => Some(121),
|
||||
KeyCode::Lang1 => Some(122),
|
||||
KeyCode::Lang2 => Some(123),
|
||||
KeyCode::IntlYen => Some(124),
|
||||
KeyCode::MetaLeft => Some(125),
|
||||
KeyCode::MetaRight => Some(126),
|
||||
KeyCode::ContextMenu => Some(127),
|
||||
KeyCode::MediaTrackNext => Some(163),
|
||||
KeyCode::MediaPlayPause => Some(164),
|
||||
KeyCode::MediaTrackPrevious => Some(165),
|
||||
KeyCode::MediaStop => Some(166),
|
||||
KeyCode::F13 => Some(183),
|
||||
KeyCode::F14 => Some(184),
|
||||
KeyCode::F15 => Some(185),
|
||||
KeyCode::F16 => Some(186),
|
||||
KeyCode::F17 => Some(187),
|
||||
KeyCode::F18 => Some(188),
|
||||
KeyCode::F19 => Some(189),
|
||||
KeyCode::F20 => Some(190),
|
||||
KeyCode::F21 => Some(191),
|
||||
KeyCode::F22 => Some(192),
|
||||
KeyCode::F23 => Some(193),
|
||||
KeyCode::F24 => Some(194),
|
||||
_ => None,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn keysym_to_key(keysym: u32) -> Key {
|
||||
use xkbcommon_dl::keysyms;
|
||||
Key::Named(match keysym {
|
||||
// TTY function keys
|
||||
keysyms::BackSpace => Named::Backspace,
|
||||
keysyms::Tab => Named::Tab,
|
||||
// keysyms::Linefeed => Named::Linefeed,
|
||||
keysyms::Clear => Named::Clear,
|
||||
keysyms::Return => Named::Enter,
|
||||
keysyms::Pause => Named::Pause,
|
||||
keysyms::Scroll_Lock => Named::ScrollLock,
|
||||
keysyms::Sys_Req => Named::PrintScreen,
|
||||
keysyms::Escape => Named::Escape,
|
||||
keysyms::Delete => Named::Delete,
|
||||
|
||||
// IME keys
|
||||
keysyms::Multi_key => Named::Compose,
|
||||
keysyms::Codeinput => Named::CodeInput,
|
||||
keysyms::SingleCandidate => Named::SingleCandidate,
|
||||
keysyms::MultipleCandidate => Named::AllCandidates,
|
||||
keysyms::PreviousCandidate => Named::PreviousCandidate,
|
||||
|
||||
// Japanese key
|
||||
keysyms::Kanji => Named::KanjiMode,
|
||||
keysyms::Muhenkan => Named::NonConvert,
|
||||
keysyms::Henkan_Mode => Named::Convert,
|
||||
keysyms::Romaji => Named::Romaji,
|
||||
keysyms::Hiragana => Named::Hiragana,
|
||||
keysyms::Hiragana_Katakana => Named::HiraganaKatakana,
|
||||
keysyms::Zenkaku => Named::Zenkaku,
|
||||
keysyms::Hankaku => Named::Hankaku,
|
||||
keysyms::Zenkaku_Hankaku => Named::ZenkakuHankaku,
|
||||
// keysyms::Touroku => Named::Touroku,
|
||||
// keysyms::Massyo => Named::Massyo,
|
||||
keysyms::Kana_Lock => Named::KanaMode,
|
||||
keysyms::Kana_Shift => Named::KanaMode,
|
||||
keysyms::Eisu_Shift => Named::Alphanumeric,
|
||||
keysyms::Eisu_toggle => Named::Alphanumeric,
|
||||
// NOTE: The next three items are aliases for values we've already mapped.
|
||||
// keysyms::Kanji_Bangou => Named::CodeInput,
|
||||
// keysyms::Zen_Koho => Named::AllCandidates,
|
||||
// keysyms::Mae_Koho => Named::PreviousCandidate,
|
||||
|
||||
// Cursor control & motion
|
||||
keysyms::Home => Named::Home,
|
||||
keysyms::Left => Named::ArrowLeft,
|
||||
keysyms::Up => Named::ArrowUp,
|
||||
keysyms::Right => Named::ArrowRight,
|
||||
keysyms::Down => Named::ArrowDown,
|
||||
// keysyms::Prior => Named::PageUp,
|
||||
keysyms::Page_Up => Named::PageUp,
|
||||
// keysyms::Next => Named::PageDown,
|
||||
keysyms::Page_Down => Named::PageDown,
|
||||
keysyms::End => Named::End,
|
||||
// keysyms::Begin => Named::Begin,
|
||||
|
||||
// Misc. functions
|
||||
keysyms::Select => Named::Select,
|
||||
keysyms::Print => Named::PrintScreen,
|
||||
keysyms::Execute => Named::Execute,
|
||||
keysyms::Insert => Named::Insert,
|
||||
keysyms::Undo => Named::Undo,
|
||||
keysyms::Redo => Named::Redo,
|
||||
keysyms::Menu => Named::ContextMenu,
|
||||
keysyms::Find => Named::Find,
|
||||
keysyms::Cancel => Named::Cancel,
|
||||
keysyms::Help => Named::Help,
|
||||
keysyms::Break => Named::Pause,
|
||||
keysyms::Mode_switch => Named::ModeChange,
|
||||
// keysyms::script_switch => Named::ModeChange,
|
||||
keysyms::Num_Lock => Named::NumLock,
|
||||
|
||||
// Keypad keys
|
||||
// keysyms::KP_Space => return Key::Character(" "),
|
||||
keysyms::KP_Tab => Named::Tab,
|
||||
keysyms::KP_Enter => Named::Enter,
|
||||
keysyms::KP_F1 => Named::F1,
|
||||
keysyms::KP_F2 => Named::F2,
|
||||
keysyms::KP_F3 => Named::F3,
|
||||
keysyms::KP_F4 => Named::F4,
|
||||
keysyms::KP_Home => Named::Home,
|
||||
keysyms::KP_Left => Named::ArrowLeft,
|
||||
keysyms::KP_Up => Named::ArrowUp,
|
||||
keysyms::KP_Right => Named::ArrowRight,
|
||||
keysyms::KP_Down => Named::ArrowDown,
|
||||
// keysyms::KP_Prior => Named::PageUp,
|
||||
keysyms::KP_Page_Up => Named::PageUp,
|
||||
// keysyms::KP_Next => Named::PageDown,
|
||||
keysyms::KP_Page_Down => Named::PageDown,
|
||||
keysyms::KP_End => Named::End,
|
||||
// This is the key labeled "5" on the numpad when NumLock is off.
|
||||
// keysyms::KP_Begin => Named::Begin,
|
||||
keysyms::KP_Insert => Named::Insert,
|
||||
keysyms::KP_Delete => Named::Delete,
|
||||
// keysyms::KP_Equal => Named::Equal,
|
||||
// keysyms::KP_Multiply => Named::Multiply,
|
||||
// keysyms::KP_Add => Named::Add,
|
||||
// keysyms::KP_Separator => Named::Separator,
|
||||
// keysyms::KP_Subtract => Named::Subtract,
|
||||
// keysyms::KP_Decimal => Named::Decimal,
|
||||
// keysyms::KP_Divide => Named::Divide,
|
||||
|
||||
// keysyms::KP_0 => return Key::Character("0"),
|
||||
// keysyms::KP_1 => return Key::Character("1"),
|
||||
// keysyms::KP_2 => return Key::Character("2"),
|
||||
// keysyms::KP_3 => return Key::Character("3"),
|
||||
// keysyms::KP_4 => return Key::Character("4"),
|
||||
// keysyms::KP_5 => return Key::Character("5"),
|
||||
// keysyms::KP_6 => return Key::Character("6"),
|
||||
// keysyms::KP_7 => return Key::Character("7"),
|
||||
// keysyms::KP_8 => return Key::Character("8"),
|
||||
// keysyms::KP_9 => return Key::Character("9"),
|
||||
|
||||
// Function keys
|
||||
keysyms::F1 => Named::F1,
|
||||
keysyms::F2 => Named::F2,
|
||||
keysyms::F3 => Named::F3,
|
||||
keysyms::F4 => Named::F4,
|
||||
keysyms::F5 => Named::F5,
|
||||
keysyms::F6 => Named::F6,
|
||||
keysyms::F7 => Named::F7,
|
||||
keysyms::F8 => Named::F8,
|
||||
keysyms::F9 => Named::F9,
|
||||
keysyms::F10 => Named::F10,
|
||||
keysyms::F11 => Named::F11,
|
||||
keysyms::F12 => Named::F12,
|
||||
keysyms::F13 => Named::F13,
|
||||
keysyms::F14 => Named::F14,
|
||||
keysyms::F15 => Named::F15,
|
||||
keysyms::F16 => Named::F16,
|
||||
keysyms::F17 => Named::F17,
|
||||
keysyms::F18 => Named::F18,
|
||||
keysyms::F19 => Named::F19,
|
||||
keysyms::F20 => Named::F20,
|
||||
keysyms::F21 => Named::F21,
|
||||
keysyms::F22 => Named::F22,
|
||||
keysyms::F23 => Named::F23,
|
||||
keysyms::F24 => Named::F24,
|
||||
keysyms::F25 => Named::F25,
|
||||
keysyms::F26 => Named::F26,
|
||||
keysyms::F27 => Named::F27,
|
||||
keysyms::F28 => Named::F28,
|
||||
keysyms::F29 => Named::F29,
|
||||
keysyms::F30 => Named::F30,
|
||||
keysyms::F31 => Named::F31,
|
||||
keysyms::F32 => Named::F32,
|
||||
keysyms::F33 => Named::F33,
|
||||
keysyms::F34 => Named::F34,
|
||||
keysyms::F35 => Named::F35,
|
||||
|
||||
// Modifiers
|
||||
keysyms::Shift_L => Named::Shift,
|
||||
keysyms::Shift_R => Named::Shift,
|
||||
keysyms::Control_L => Named::Control,
|
||||
keysyms::Control_R => Named::Control,
|
||||
keysyms::Caps_Lock => Named::CapsLock,
|
||||
// keysyms::Shift_Lock => Named::ShiftLock,
|
||||
|
||||
// keysyms::Meta_L => Named::Meta,
|
||||
// keysyms::Meta_R => Named::Meta,
|
||||
keysyms::Alt_L => Named::Alt,
|
||||
keysyms::Alt_R => Named::Alt,
|
||||
keysyms::Super_L => Named::Super,
|
||||
keysyms::Super_R => Named::Super,
|
||||
keysyms::Hyper_L => Named::Hyper,
|
||||
keysyms::Hyper_R => Named::Hyper,
|
||||
|
||||
// XKB function and modifier keys
|
||||
// keysyms::ISO_Lock => Named::IsoLock,
|
||||
// keysyms::ISO_Level2_Latch => Named::IsoLevel2Latch,
|
||||
keysyms::ISO_Level3_Shift => Named::AltGraph,
|
||||
keysyms::ISO_Level3_Latch => Named::AltGraph,
|
||||
keysyms::ISO_Level3_Lock => Named::AltGraph,
|
||||
// keysyms::ISO_Level5_Shift => Named::IsoLevel5Shift,
|
||||
// keysyms::ISO_Level5_Latch => Named::IsoLevel5Latch,
|
||||
// keysyms::ISO_Level5_Lock => Named::IsoLevel5Lock,
|
||||
// keysyms::ISO_Group_Shift => Named::IsoGroupShift,
|
||||
// keysyms::ISO_Group_Latch => Named::IsoGroupLatch,
|
||||
// keysyms::ISO_Group_Lock => Named::IsoGroupLock,
|
||||
keysyms::ISO_Next_Group => Named::GroupNext,
|
||||
// keysyms::ISO_Next_Group_Lock => Named::GroupNextLock,
|
||||
keysyms::ISO_Prev_Group => Named::GroupPrevious,
|
||||
// keysyms::ISO_Prev_Group_Lock => Named::GroupPreviousLock,
|
||||
keysyms::ISO_First_Group => Named::GroupFirst,
|
||||
// keysyms::ISO_First_Group_Lock => Named::GroupFirstLock,
|
||||
keysyms::ISO_Last_Group => Named::GroupLast,
|
||||
// keysyms::ISO_Last_Group_Lock => Named::GroupLastLock,
|
||||
//
|
||||
keysyms::ISO_Left_Tab => Named::Tab,
|
||||
// keysyms::ISO_Move_Line_Up => Named::IsoMoveLineUp,
|
||||
// keysyms::ISO_Move_Line_Down => Named::IsoMoveLineDown,
|
||||
// keysyms::ISO_Partial_Line_Up => Named::IsoPartialLineUp,
|
||||
// keysyms::ISO_Partial_Line_Down => Named::IsoPartialLineDown,
|
||||
// keysyms::ISO_Partial_Space_Left => Named::IsoPartialSpaceLeft,
|
||||
// keysyms::ISO_Partial_Space_Right => Named::IsoPartialSpaceRight,
|
||||
// keysyms::ISO_Set_Margin_Left => Named::IsoSetMarginLeft,
|
||||
// keysyms::ISO_Set_Margin_Right => Named::IsoSetMarginRight,
|
||||
// keysyms::ISO_Release_Margin_Left => Named::IsoReleaseMarginLeft,
|
||||
// keysyms::ISO_Release_Margin_Right => Named::IsoReleaseMarginRight,
|
||||
// keysyms::ISO_Release_Both_Margins => Named::IsoReleaseBothMargins,
|
||||
// keysyms::ISO_Fast_Cursor_Left => Named::IsoFastCursorLeft,
|
||||
// keysyms::ISO_Fast_Cursor_Right => Named::IsoFastCursorRight,
|
||||
// keysyms::ISO_Fast_Cursor_Up => Named::IsoFastCursorUp,
|
||||
// keysyms::ISO_Fast_Cursor_Down => Named::IsoFastCursorDown,
|
||||
// keysyms::ISO_Continuous_Underline => Named::IsoContinuousUnderline,
|
||||
// keysyms::ISO_Discontinuous_Underline => Named::IsoDiscontinuousUnderline,
|
||||
// keysyms::ISO_Emphasize => Named::IsoEmphasize,
|
||||
// keysyms::ISO_Center_Object => Named::IsoCenterObject,
|
||||
keysyms::ISO_Enter => Named::Enter,
|
||||
|
||||
// dead_grave..dead_currency
|
||||
|
||||
// dead_lowline..dead_longsolidusoverlay
|
||||
|
||||
// dead_a..dead_capital_schwa
|
||||
|
||||
// dead_greek
|
||||
|
||||
// First_Virtual_Screen..Terminate_Server
|
||||
|
||||
// AccessX_Enable..AudibleBell_Enable
|
||||
|
||||
// Pointer_Left..Pointer_Drag5
|
||||
|
||||
// Pointer_EnableKeys..Pointer_DfltBtnPrev
|
||||
|
||||
// ch..C_H
|
||||
|
||||
// 3270 terminal keys
|
||||
// keysyms::3270_Duplicate => Named::Duplicate,
|
||||
// keysyms::3270_FieldMark => Named::FieldMark,
|
||||
// keysyms::3270_Right2 => Named::Right2,
|
||||
// keysyms::3270_Left2 => Named::Left2,
|
||||
// keysyms::3270_BackTab => Named::BackTab,
|
||||
keysyms::_3270_EraseEOF => Named::EraseEof,
|
||||
// keysyms::3270_EraseInput => Named::EraseInput,
|
||||
// keysyms::3270_Reset => Named::Reset,
|
||||
// keysyms::3270_Quit => Named::Quit,
|
||||
// keysyms::3270_PA1 => Named::Pa1,
|
||||
// keysyms::3270_PA2 => Named::Pa2,
|
||||
// keysyms::3270_PA3 => Named::Pa3,
|
||||
// keysyms::3270_Test => Named::Test,
|
||||
keysyms::_3270_Attn => Named::Attn,
|
||||
// keysyms::3270_CursorBlink => Named::CursorBlink,
|
||||
// keysyms::3270_AltCursor => Named::AltCursor,
|
||||
// keysyms::3270_KeyClick => Named::KeyClick,
|
||||
// keysyms::3270_Jump => Named::Jump,
|
||||
// keysyms::3270_Ident => Named::Ident,
|
||||
// keysyms::3270_Rule => Named::Rule,
|
||||
// keysyms::3270_Copy => Named::Copy,
|
||||
keysyms::_3270_Play => Named::Play,
|
||||
// keysyms::3270_Setup => Named::Setup,
|
||||
// keysyms::3270_Record => Named::Record,
|
||||
// keysyms::3270_ChangeScreen => Named::ChangeScreen,
|
||||
// keysyms::3270_DeleteWord => Named::DeleteWord,
|
||||
keysyms::_3270_ExSelect => Named::ExSel,
|
||||
keysyms::_3270_CursorSelect => Named::CrSel,
|
||||
keysyms::_3270_PrintScreen => Named::PrintScreen,
|
||||
keysyms::_3270_Enter => Named::Enter,
|
||||
|
||||
keysyms::space => Named::Space,
|
||||
// exclam..Sinh_kunddaliya
|
||||
|
||||
// XFree86
|
||||
// keysyms::XF86_ModeLock => Named::ModeLock,
|
||||
|
||||
// XFree86 - Backlight controls
|
||||
keysyms::XF86_MonBrightnessUp => Named::BrightnessUp,
|
||||
keysyms::XF86_MonBrightnessDown => Named::BrightnessDown,
|
||||
// keysyms::XF86_KbdLightOnOff => Named::LightOnOff,
|
||||
// keysyms::XF86_KbdBrightnessUp => Named::KeyboardBrightnessUp,
|
||||
// keysyms::XF86_KbdBrightnessDown => Named::KeyboardBrightnessDown,
|
||||
|
||||
// XFree86 - "Internet"
|
||||
keysyms::XF86_Standby => Named::Standby,
|
||||
keysyms::XF86_AudioLowerVolume => Named::AudioVolumeDown,
|
||||
keysyms::XF86_AudioRaiseVolume => Named::AudioVolumeUp,
|
||||
keysyms::XF86_AudioPlay => Named::MediaPlay,
|
||||
keysyms::XF86_AudioStop => Named::MediaStop,
|
||||
keysyms::XF86_AudioPrev => Named::MediaTrackPrevious,
|
||||
keysyms::XF86_AudioNext => Named::MediaTrackNext,
|
||||
keysyms::XF86_HomePage => Named::BrowserHome,
|
||||
keysyms::XF86_Mail => Named::LaunchMail,
|
||||
// keysyms::XF86_Start => Named::Start,
|
||||
keysyms::XF86_Search => Named::BrowserSearch,
|
||||
keysyms::XF86_AudioRecord => Named::MediaRecord,
|
||||
|
||||
// XFree86 - PDA
|
||||
keysyms::XF86_Calculator => Named::LaunchApplication2,
|
||||
// keysyms::XF86_Memo => Named::Memo,
|
||||
// keysyms::XF86_ToDoList => Named::ToDoList,
|
||||
keysyms::XF86_Calendar => Named::LaunchCalendar,
|
||||
keysyms::XF86_PowerDown => Named::Power,
|
||||
// keysyms::XF86_ContrastAdjust => Named::AdjustContrast,
|
||||
// keysyms::XF86_RockerUp => Named::RockerUp,
|
||||
// keysyms::XF86_RockerDown => Named::RockerDown,
|
||||
// keysyms::XF86_RockerEnter => Named::RockerEnter,
|
||||
|
||||
// XFree86 - More "Internet"
|
||||
keysyms::XF86_Back => Named::BrowserBack,
|
||||
keysyms::XF86_Forward => Named::BrowserForward,
|
||||
// keysyms::XF86_Stop => Named::Stop,
|
||||
keysyms::XF86_Refresh => Named::BrowserRefresh,
|
||||
keysyms::XF86_PowerOff => Named::Power,
|
||||
keysyms::XF86_WakeUp => Named::WakeUp,
|
||||
keysyms::XF86_Eject => Named::Eject,
|
||||
keysyms::XF86_ScreenSaver => Named::LaunchScreenSaver,
|
||||
keysyms::XF86_WWW => Named::LaunchWebBrowser,
|
||||
keysyms::XF86_Sleep => Named::Standby,
|
||||
keysyms::XF86_Favorites => Named::BrowserFavorites,
|
||||
keysyms::XF86_AudioPause => Named::MediaPause,
|
||||
// keysyms::XF86_AudioMedia => Named::AudioMedia,
|
||||
keysyms::XF86_MyComputer => Named::LaunchApplication1,
|
||||
// keysyms::XF86_VendorHome => Named::VendorHome,
|
||||
// keysyms::XF86_LightBulb => Named::LightBulb,
|
||||
// keysyms::XF86_Shop => Named::BrowserShop,
|
||||
// keysyms::XF86_History => Named::BrowserHistory,
|
||||
// keysyms::XF86_OpenURL => Named::OpenUrl,
|
||||
// keysyms::XF86_AddFavorite => Named::AddFavorite,
|
||||
// keysyms::XF86_HotLinks => Named::HotLinks,
|
||||
// keysyms::XF86_BrightnessAdjust => Named::BrightnessAdjust,
|
||||
// keysyms::XF86_Finance => Named::BrowserFinance,
|
||||
// keysyms::XF86_Community => Named::BrowserCommunity,
|
||||
keysyms::XF86_AudioRewind => Named::MediaRewind,
|
||||
// keysyms::XF86_BackForward => Key::???,
|
||||
// XF86_Launch0..XF86_LaunchF
|
||||
|
||||
// XF86_ApplicationLeft..XF86_CD
|
||||
keysyms::XF86_Calculater => Named::LaunchApplication2, // Nice typo, libxkbcommon :)
|
||||
// XF86_Clear
|
||||
keysyms::XF86_Close => Named::Close,
|
||||
keysyms::XF86_Copy => Named::Copy,
|
||||
keysyms::XF86_Cut => Named::Cut,
|
||||
// XF86_Display..XF86_Documents
|
||||
keysyms::XF86_Excel => Named::LaunchSpreadsheet,
|
||||
// XF86_Explorer..XF86iTouch
|
||||
keysyms::XF86_LogOff => Named::LogOff,
|
||||
// XF86_Market..XF86_MenuPB
|
||||
keysyms::XF86_MySites => Named::BrowserFavorites,
|
||||
keysyms::XF86_New => Named::New,
|
||||
// XF86_News..XF86_OfficeHome
|
||||
keysyms::XF86_Open => Named::Open,
|
||||
// XF86_Option
|
||||
keysyms::XF86_Paste => Named::Paste,
|
||||
keysyms::XF86_Phone => Named::LaunchPhone,
|
||||
// XF86_Q
|
||||
keysyms::XF86_Reply => Named::MailReply,
|
||||
keysyms::XF86_Reload => Named::BrowserRefresh,
|
||||
// XF86_RotateWindows..XF86_RotationKB
|
||||
keysyms::XF86_Save => Named::Save,
|
||||
// XF86_ScrollUp..XF86_ScrollClick
|
||||
keysyms::XF86_Send => Named::MailSend,
|
||||
keysyms::XF86_Spell => Named::SpellCheck,
|
||||
keysyms::XF86_SplitScreen => Named::SplitScreenToggle,
|
||||
// XF86_Support..XF86_User2KB
|
||||
keysyms::XF86_Video => Named::LaunchMediaPlayer,
|
||||
// XF86_WheelButton
|
||||
keysyms::XF86_Word => Named::LaunchWordProcessor,
|
||||
// XF86_Xfer
|
||||
keysyms::XF86_ZoomIn => Named::ZoomIn,
|
||||
keysyms::XF86_ZoomOut => Named::ZoomOut,
|
||||
|
||||
// XF86_Away..XF86_Messenger
|
||||
keysyms::XF86_WebCam => Named::LaunchWebCam,
|
||||
keysyms::XF86_MailForward => Named::MailForward,
|
||||
// XF86_Pictures
|
||||
keysyms::XF86_Music => Named::LaunchMusicPlayer,
|
||||
|
||||
// XF86_Battery..XF86_UWB
|
||||
//
|
||||
keysyms::XF86_AudioForward => Named::MediaFastForward,
|
||||
// XF86_AudioRepeat
|
||||
keysyms::XF86_AudioRandomPlay => Named::RandomToggle,
|
||||
keysyms::XF86_Subtitle => Named::Subtitle,
|
||||
keysyms::XF86_AudioCycleTrack => Named::MediaAudioTrack,
|
||||
// XF86_CycleAngle..XF86_Blue
|
||||
//
|
||||
keysyms::XF86_Suspend => Named::Standby,
|
||||
keysyms::XF86_Hibernate => Named::Hibernate,
|
||||
// XF86_TouchpadToggle..XF86_TouchpadOff
|
||||
//
|
||||
keysyms::XF86_AudioMute => Named::AudioVolumeMute,
|
||||
|
||||
// XF86_Switch_VT_1..XF86_Switch_VT_12
|
||||
|
||||
// XF86_Ungrab..XF86_ClearGrab
|
||||
keysyms::XF86_Next_VMode => Named::VideoModeNext,
|
||||
// keysyms::XF86_Prev_VMode => Named::VideoModePrevious,
|
||||
// XF86_LogWindowTree..XF86_LogGrabInfo
|
||||
|
||||
// SunFA_Grave..SunFA_Cedilla
|
||||
|
||||
// keysyms::SunF36 => Named::F36 | Named::F11,
|
||||
// keysyms::SunF37 => Named::F37 | Named::F12,
|
||||
|
||||
// keysyms::SunSys_Req => Named::PrintScreen,
|
||||
// The next couple of xkb (until SunStop) are already handled.
|
||||
// SunPrint_Screen..SunPageDown
|
||||
|
||||
// SunUndo..SunFront
|
||||
keysyms::SUN_Copy => Named::Copy,
|
||||
keysyms::SUN_Open => Named::Open,
|
||||
keysyms::SUN_Paste => Named::Paste,
|
||||
keysyms::SUN_Cut => Named::Cut,
|
||||
|
||||
// SunPowerSwitch
|
||||
keysyms::SUN_AudioLowerVolume => Named::AudioVolumeDown,
|
||||
keysyms::SUN_AudioMute => Named::AudioVolumeMute,
|
||||
keysyms::SUN_AudioRaiseVolume => Named::AudioVolumeUp,
|
||||
// SUN_VideoDegauss
|
||||
keysyms::SUN_VideoLowerBrightness => Named::BrightnessDown,
|
||||
keysyms::SUN_VideoRaiseBrightness => Named::BrightnessUp,
|
||||
// SunPowerSwitchShift
|
||||
//
|
||||
_ => return Key::Unidentified,
|
||||
})
|
||||
}
|
||||
|
||||
use iced_runtime::keyboard::{Key, Location, key::Named};
|
||||
|
||||
pub fn keysym_location(keysym: u32) -> Location {
|
||||
use xkbcommon_dl::keysyms;
|
||||
match keysym {
|
||||
xkeysym::key::Shift_L
|
||||
| keysyms::Control_L
|
||||
| keysyms::Meta_L
|
||||
| keysyms::Alt_L
|
||||
| keysyms::Super_L
|
||||
| keysyms::Hyper_L => Location::Left,
|
||||
keysyms::Shift_R
|
||||
| keysyms::Control_R
|
||||
| keysyms::Meta_R
|
||||
| keysyms::Alt_R
|
||||
| keysyms::Super_R
|
||||
| keysyms::Hyper_R => Location::Right,
|
||||
keysyms::KP_0
|
||||
| keysyms::KP_1
|
||||
| keysyms::KP_2
|
||||
| keysyms::KP_3
|
||||
| keysyms::KP_4
|
||||
| keysyms::KP_5
|
||||
| keysyms::KP_6
|
||||
| keysyms::KP_7
|
||||
| keysyms::KP_8
|
||||
| keysyms::KP_9
|
||||
| keysyms::KP_Space
|
||||
| keysyms::KP_Tab
|
||||
| keysyms::KP_Enter
|
||||
| keysyms::KP_F1
|
||||
| keysyms::KP_F2
|
||||
| keysyms::KP_F3
|
||||
| keysyms::KP_F4
|
||||
| keysyms::KP_Home
|
||||
| keysyms::KP_Left
|
||||
| keysyms::KP_Up
|
||||
| keysyms::KP_Right
|
||||
| keysyms::KP_Down
|
||||
| keysyms::KP_Page_Up
|
||||
| keysyms::KP_Page_Down
|
||||
| keysyms::KP_End
|
||||
| keysyms::KP_Begin
|
||||
| keysyms::KP_Insert
|
||||
| keysyms::KP_Delete
|
||||
| keysyms::KP_Equal
|
||||
| keysyms::KP_Multiply
|
||||
| keysyms::KP_Add
|
||||
| keysyms::KP_Separator
|
||||
| keysyms::KP_Subtract
|
||||
| keysyms::KP_Decimal
|
||||
| keysyms::KP_Divide => Location::Numpad,
|
||||
_ => Location::Standard,
|
||||
}
|
||||
}
|
||||
210
winit/src/platform_specific/wayland/mod.rs
Normal file
210
winit/src/platform_specific/wayland/mod.rs
Normal file
|
|
@ -0,0 +1,210 @@
|
|||
pub mod commands;
|
||||
pub mod conversion;
|
||||
pub(crate) mod event_loop;
|
||||
pub(crate) mod handlers;
|
||||
pub mod keymap;
|
||||
pub mod sctk_event;
|
||||
pub mod subsurface_widget;
|
||||
pub mod winit_window;
|
||||
|
||||
use super::{PlatformSpecific, SurfaceIdWrapper};
|
||||
use crate::{Control, Program, WindowManager};
|
||||
|
||||
use crate::platform_specific::UserInterfaces;
|
||||
use cursor_icon::CursorIcon;
|
||||
use iced_futures::futures::channel::mpsc;
|
||||
use iced_graphics::{Compositor, compositor};
|
||||
use iced_runtime::core::window;
|
||||
use sctk::reexports::calloop;
|
||||
use sctk::reexports::client::protocol::wl_surface::WlSurface;
|
||||
use sctk::seat::keyboard::Modifiers;
|
||||
use sctk_event::SctkEvent;
|
||||
use std::{collections::HashMap, sync::Arc};
|
||||
use subsurface_widget::{SubsurfaceInstance, SubsurfaceState};
|
||||
use wayland_backend::client::ObjectId;
|
||||
use winit::event_loop::OwnedDisplayHandle;
|
||||
|
||||
pub(crate) enum Action {
|
||||
Action(iced_runtime::platform_specific::wayland::Action),
|
||||
SetCursor(CursorIcon),
|
||||
RequestRedraw(ObjectId),
|
||||
PrePresentNotify(ObjectId),
|
||||
TrackWindow(Arc<dyn winit::window::Window>, window::Id),
|
||||
RemoveWindow(window::Id),
|
||||
Dropped(SurfaceIdWrapper),
|
||||
Ready,
|
||||
}
|
||||
|
||||
impl std::fmt::Debug for Action {
|
||||
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
match self {
|
||||
Self::Action(arg0) => f.debug_tuple("Action").field(arg0).finish(),
|
||||
Self::SetCursor(arg0) => {
|
||||
f.debug_tuple("SetCursor").field(arg0).finish()
|
||||
}
|
||||
Self::RequestRedraw(arg0) => {
|
||||
f.debug_tuple("RequestRedraw").field(arg0).finish()
|
||||
}
|
||||
Self::PrePresentNotify(arg0) => {
|
||||
f.debug_tuple("PrePresentNotify").field(arg0).finish()
|
||||
}
|
||||
Self::TrackWindow(_arg0, arg1) => {
|
||||
f.debug_tuple("TrackWindow").field(arg1).finish()
|
||||
}
|
||||
Self::RemoveWindow(arg0) => {
|
||||
f.debug_tuple("RemoveWindow").field(arg0).finish()
|
||||
}
|
||||
Self::Ready => write!(f, "Ready"),
|
||||
Self::Dropped(_surface_id_wrapper) => write!(f, "Dropped"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Default)]
|
||||
pub(crate) struct WaylandSpecific {
|
||||
winit_event_sender: Option<mpsc::UnboundedSender<Control>>,
|
||||
proxy: Option<winit::event_loop::EventLoopProxy>,
|
||||
sender: Option<calloop::channel::Sender<Action>>,
|
||||
display_handle: Option<OwnedDisplayHandle>,
|
||||
modifiers: Modifiers,
|
||||
surface_ids: HashMap<ObjectId, SurfaceIdWrapper>,
|
||||
destroyed_surface_ids: HashMap<ObjectId, SurfaceIdWrapper>,
|
||||
subsurface_ids: HashMap<ObjectId, (i32, i32, window::Id)>,
|
||||
subsurface_state: Option<SubsurfaceState>,
|
||||
surface_subsurfaces: HashMap<window::Id, Vec<SubsurfaceInstance>>,
|
||||
}
|
||||
|
||||
impl PlatformSpecific {
|
||||
pub(crate) fn with_wayland(
|
||||
mut self,
|
||||
tx: mpsc::UnboundedSender<Control>,
|
||||
raw: winit::event_loop::EventLoopProxy,
|
||||
display: OwnedDisplayHandle,
|
||||
) -> Self {
|
||||
self.wayland.winit_event_sender = Some(tx);
|
||||
self.wayland.display_handle = Some(display);
|
||||
self.wayland.proxy = Some(raw);
|
||||
// TODO remove this
|
||||
self.wayland.sender =
|
||||
crate::platform_specific::event_loop::SctkEventLoop::new(
|
||||
self.wayland.winit_event_sender.clone().unwrap(),
|
||||
self.wayland.proxy.clone().unwrap(),
|
||||
self.wayland.display_handle.clone().unwrap(),
|
||||
)
|
||||
.ok();
|
||||
self
|
||||
}
|
||||
|
||||
pub(crate) fn send_wayland(&mut self, action: Action) {
|
||||
if self.wayland.sender.is_none()
|
||||
&& self.wayland.winit_event_sender.is_some()
|
||||
&& self.wayland.display_handle.is_some()
|
||||
&& self.wayland.proxy.is_some()
|
||||
{
|
||||
self.wayland.sender =
|
||||
crate::platform_specific::event_loop::SctkEventLoop::new(
|
||||
self.wayland.winit_event_sender.clone().unwrap(),
|
||||
self.wayland.proxy.clone().unwrap(),
|
||||
self.wayland.display_handle.clone().unwrap(),
|
||||
)
|
||||
.ok();
|
||||
}
|
||||
|
||||
if let Some(tx) = self.wayland.sender.as_ref() {
|
||||
_ = tx.send(action);
|
||||
} else {
|
||||
log::error!("Failed to process wayland Action.");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl WaylandSpecific {
|
||||
pub(crate) fn handle_event<'a, P>(
|
||||
&mut self,
|
||||
e: SctkEvent,
|
||||
events: &mut Vec<(Option<window::Id>, iced_runtime::core::Event)>,
|
||||
program: &'a crate::program::Instance<P>,
|
||||
compositor: &mut <<P as Program>::Renderer as compositor::Default>::Compositor,
|
||||
window_manager: &mut WindowManager<
|
||||
P,
|
||||
<<P as Program>::Renderer as compositor::Default>::Compositor,
|
||||
>,
|
||||
user_interfaces: &mut UserInterfaces<'a, P>,
|
||||
clipboard: &mut crate::Clipboard,
|
||||
#[cfg(feature = "a11y")] adapters: &mut HashMap<
|
||||
window::Id,
|
||||
(u64, iced_accessibility::accesskit_winit::Adapter),
|
||||
>,
|
||||
) where
|
||||
P: Program,
|
||||
{
|
||||
let Self {
|
||||
winit_event_sender,
|
||||
proxy,
|
||||
sender,
|
||||
display_handle,
|
||||
surface_ids,
|
||||
destroyed_surface_ids,
|
||||
subsurface_ids,
|
||||
modifiers,
|
||||
subsurface_state,
|
||||
surface_subsurfaces,
|
||||
} = self;
|
||||
|
||||
match e {
|
||||
sctk_event => {
|
||||
let Some(sender) = sender.as_ref() else {
|
||||
log::error!("Missing calloop sender");
|
||||
return Default::default();
|
||||
};
|
||||
let Some(event_sender) = winit_event_sender.as_ref() else {
|
||||
log::error!("Missing control sender");
|
||||
return Default::default();
|
||||
};
|
||||
let Some(proxy) = proxy.as_ref() else {
|
||||
log::error!("Missing event loop proxy");
|
||||
return Default::default();
|
||||
};
|
||||
|
||||
sctk_event.process(
|
||||
modifiers,
|
||||
program,
|
||||
compositor,
|
||||
window_manager,
|
||||
surface_ids,
|
||||
subsurface_ids,
|
||||
sender,
|
||||
event_sender,
|
||||
proxy,
|
||||
user_interfaces,
|
||||
events,
|
||||
clipboard,
|
||||
subsurface_state,
|
||||
#[cfg(feature = "a11y")]
|
||||
adapters,
|
||||
);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
pub(crate) fn update_subsurfaces(
|
||||
&mut self,
|
||||
id: window::Id,
|
||||
wl_surface: &WlSurface,
|
||||
) {
|
||||
let subsurfaces = crate::subsurface_widget::take_subsurfaces();
|
||||
let mut entry = self.surface_subsurfaces.entry(id);
|
||||
let surface_subsurfaces = entry.or_default();
|
||||
let Some(subsurface_state) = self.subsurface_state.as_mut() else {
|
||||
return;
|
||||
};
|
||||
|
||||
subsurface_state.update_subsurfaces(
|
||||
id,
|
||||
&mut self.subsurface_ids,
|
||||
wl_surface,
|
||||
surface_subsurfaces,
|
||||
&subsurfaces,
|
||||
);
|
||||
}
|
||||
}
|
||||
1212
winit/src/platform_specific/wayland/sctk_event.rs
Executable file
1212
winit/src/platform_specific/wayland/sctk_event.rs
Executable file
File diff suppressed because it is too large
Load diff
701
winit/src/platform_specific/wayland/subsurface_widget.rs
Normal file
701
winit/src/platform_specific/wayland/subsurface_widget.rs
Normal file
|
|
@ -0,0 +1,701 @@
|
|||
// TODO z-order option?
|
||||
|
||||
use crate::core::{
|
||||
ContentFit, Element, Length, Rectangle, Size,
|
||||
layout::{self, Layout},
|
||||
mouse, renderer,
|
||||
widget::{self, Widget},
|
||||
};
|
||||
use std::{
|
||||
cell::RefCell,
|
||||
collections::HashMap,
|
||||
fmt::Debug,
|
||||
future::Future,
|
||||
hash::{Hash, Hasher},
|
||||
mem,
|
||||
os::unix::io::{AsFd, OwnedFd},
|
||||
pin::Pin,
|
||||
ptr,
|
||||
sync::{Arc, Mutex, Weak},
|
||||
task,
|
||||
};
|
||||
|
||||
use crate::futures::futures::channel::oneshot;
|
||||
use iced_futures::core::window;
|
||||
use sctk::{
|
||||
compositor::SurfaceData,
|
||||
globals::GlobalData,
|
||||
reexports::client::{
|
||||
Connection, Dispatch, Proxy, QueueHandle, delegate_noop,
|
||||
protocol::{
|
||||
wl_buffer::{self, WlBuffer},
|
||||
wl_compositor::WlCompositor,
|
||||
wl_shm::{self, WlShm},
|
||||
wl_shm_pool::{self, WlShmPool},
|
||||
wl_subcompositor::WlSubcompositor,
|
||||
wl_subsurface::WlSubsurface,
|
||||
wl_surface::WlSurface,
|
||||
},
|
||||
},
|
||||
};
|
||||
use wayland_backend::client::ObjectId;
|
||||
use wayland_protocols::wp::{
|
||||
alpha_modifier::v1::client::{
|
||||
wp_alpha_modifier_surface_v1::WpAlphaModifierSurfaceV1,
|
||||
wp_alpha_modifier_v1::WpAlphaModifierV1,
|
||||
},
|
||||
linux_dmabuf::zv1::client::{
|
||||
zwp_linux_buffer_params_v1::{self, ZwpLinuxBufferParamsV1},
|
||||
zwp_linux_dmabuf_v1::{self, ZwpLinuxDmabufV1},
|
||||
},
|
||||
viewporter::client::{
|
||||
wp_viewport::WpViewport, wp_viewporter::WpViewporter,
|
||||
},
|
||||
};
|
||||
|
||||
use crate::platform_specific::{
|
||||
SurfaceIdWrapper, event_loop::state::SctkState,
|
||||
};
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Plane {
|
||||
pub fd: OwnedFd,
|
||||
pub plane_idx: u32,
|
||||
pub offset: u32,
|
||||
pub stride: u32,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Dmabuf {
|
||||
pub width: i32,
|
||||
pub height: i32,
|
||||
pub planes: Vec<Plane>,
|
||||
pub format: u32,
|
||||
pub modifier: u64,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct Shmbuf {
|
||||
pub fd: OwnedFd,
|
||||
pub offset: i32,
|
||||
pub width: i32,
|
||||
pub height: i32,
|
||||
pub stride: i32,
|
||||
pub format: wl_shm::Format,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub enum BufferSource {
|
||||
Shm(Shmbuf),
|
||||
Dma(Dmabuf),
|
||||
}
|
||||
|
||||
impl From<Shmbuf> for BufferSource {
|
||||
fn from(buf: Shmbuf) -> Self {
|
||||
Self::Shm(buf)
|
||||
}
|
||||
}
|
||||
|
||||
impl From<Dmabuf> for BufferSource {
|
||||
fn from(buf: Dmabuf) -> Self {
|
||||
Self::Dma(buf)
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
struct SubsurfaceBufferInner {
|
||||
source: Arc<BufferSource>,
|
||||
_sender: oneshot::Sender<()>,
|
||||
}
|
||||
|
||||
/// Refcounted type containing a `BufferSource` with a sender that is signaled
|
||||
/// all references are dropped and `wl_buffer`s created from the source are
|
||||
/// released.
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct SubsurfaceBuffer(Arc<SubsurfaceBufferInner>);
|
||||
|
||||
pub struct BufferData {
|
||||
source: WeakBufferSource,
|
||||
// This reference is held until the surface `release`s the buffer
|
||||
subsurface_buffer: Mutex<Option<SubsurfaceBuffer>>,
|
||||
}
|
||||
|
||||
impl BufferData {
|
||||
fn for_buffer(buffer: &WlBuffer) -> Option<&Self> {
|
||||
buffer.data::<BufferData>()
|
||||
}
|
||||
}
|
||||
|
||||
/// Future signalled when subsurface buffer is released
|
||||
pub struct SubsurfaceBufferRelease(oneshot::Receiver<()>);
|
||||
|
||||
impl SubsurfaceBufferRelease {
|
||||
/// Non-blocking check if buffer is released yet, without awaiting
|
||||
pub fn released(&mut self) -> bool {
|
||||
self.0.try_recv() == Ok(None)
|
||||
}
|
||||
}
|
||||
|
||||
impl Future for SubsurfaceBufferRelease {
|
||||
type Output = ();
|
||||
|
||||
fn poll(
|
||||
mut self: Pin<&mut Self>,
|
||||
cx: &mut task::Context<'_>,
|
||||
) -> task::Poll<()> {
|
||||
Pin::new(&mut self.0).poll(cx).map(|_| ())
|
||||
}
|
||||
}
|
||||
|
||||
impl SubsurfaceBuffer {
|
||||
pub fn new(source: Arc<BufferSource>) -> (Self, SubsurfaceBufferRelease) {
|
||||
let (_sender, receiver) = oneshot::channel();
|
||||
let subsurface_buffer =
|
||||
SubsurfaceBuffer(Arc::new(SubsurfaceBufferInner {
|
||||
source,
|
||||
_sender,
|
||||
}));
|
||||
(subsurface_buffer, SubsurfaceBufferRelease(receiver))
|
||||
}
|
||||
|
||||
// Behavior of `wl_buffer::released` is undefined if attached to multiple surfaces. To allow
|
||||
// things like that, create a new `wl_buffer` each time.
|
||||
fn create_buffer(
|
||||
&self,
|
||||
shm: &WlShm,
|
||||
dmabuf: Option<&ZwpLinuxDmabufV1>,
|
||||
qh: &QueueHandle<SctkState>,
|
||||
) -> Option<WlBuffer> {
|
||||
// create reference to source, that is dropped on release
|
||||
match self.0.source.as_ref() {
|
||||
BufferSource::Shm(buf) => {
|
||||
let pool = shm.create_pool(
|
||||
buf.fd.as_fd(),
|
||||
buf.offset + buf.height * buf.stride,
|
||||
qh,
|
||||
GlobalData,
|
||||
);
|
||||
let buffer = pool.create_buffer(
|
||||
buf.offset,
|
||||
buf.width,
|
||||
buf.height,
|
||||
buf.stride,
|
||||
buf.format,
|
||||
qh,
|
||||
BufferData {
|
||||
source: WeakBufferSource(Arc::downgrade(
|
||||
&self.0.source,
|
||||
)),
|
||||
subsurface_buffer: Mutex::new(Some(self.clone())),
|
||||
},
|
||||
);
|
||||
pool.destroy();
|
||||
Some(buffer)
|
||||
}
|
||||
BufferSource::Dma(buf) => {
|
||||
if let Some(dmabuf) = dmabuf {
|
||||
let params = dmabuf.create_params(qh, GlobalData);
|
||||
for plane in &buf.planes {
|
||||
let modifier_hi = (buf.modifier >> 32) as u32;
|
||||
let modifier_lo = (buf.modifier & 0xffffffff) as u32;
|
||||
params.add(
|
||||
plane.fd.as_fd(),
|
||||
plane.plane_idx,
|
||||
plane.offset,
|
||||
plane.stride,
|
||||
modifier_hi,
|
||||
modifier_lo,
|
||||
);
|
||||
}
|
||||
// Will cause protocol error if format is not supported
|
||||
Some(params.create_immed(
|
||||
buf.width,
|
||||
buf.height,
|
||||
buf.format,
|
||||
zwp_linux_buffer_params_v1::Flags::empty(),
|
||||
qh,
|
||||
BufferData {
|
||||
source: WeakBufferSource(Arc::downgrade(
|
||||
&self.0.source,
|
||||
)),
|
||||
subsurface_buffer: Mutex::new(Some(self.clone())),
|
||||
},
|
||||
))
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl PartialEq for SubsurfaceBuffer {
|
||||
fn eq(&self, rhs: &Self) -> bool {
|
||||
Arc::ptr_eq(&self.0, &rhs.0)
|
||||
}
|
||||
}
|
||||
|
||||
impl Dispatch<WlShmPool, GlobalData> for SctkState {
|
||||
fn event(
|
||||
_: &mut SctkState,
|
||||
_: &WlShmPool,
|
||||
_: wl_shm_pool::Event,
|
||||
_: &GlobalData,
|
||||
_: &Connection,
|
||||
_: &QueueHandle<SctkState>,
|
||||
) {
|
||||
unreachable!()
|
||||
}
|
||||
}
|
||||
|
||||
impl Dispatch<ZwpLinuxDmabufV1, GlobalData> for SctkState {
|
||||
fn event(
|
||||
_: &mut SctkState,
|
||||
_: &ZwpLinuxDmabufV1,
|
||||
_: zwp_linux_dmabuf_v1::Event,
|
||||
_: &GlobalData,
|
||||
_: &Connection,
|
||||
_: &QueueHandle<SctkState>,
|
||||
) {
|
||||
}
|
||||
}
|
||||
|
||||
impl Dispatch<ZwpLinuxBufferParamsV1, GlobalData> for SctkState {
|
||||
fn event(
|
||||
_: &mut SctkState,
|
||||
_: &ZwpLinuxBufferParamsV1,
|
||||
_: zwp_linux_buffer_params_v1::Event,
|
||||
_: &GlobalData,
|
||||
_: &Connection,
|
||||
_: &QueueHandle<SctkState>,
|
||||
) {
|
||||
}
|
||||
}
|
||||
|
||||
impl Dispatch<WlBuffer, BufferData> for SctkState {
|
||||
fn event(
|
||||
_: &mut SctkState,
|
||||
_: &WlBuffer,
|
||||
event: wl_buffer::Event,
|
||||
data: &BufferData,
|
||||
_: &Connection,
|
||||
_: &QueueHandle<SctkState>,
|
||||
) {
|
||||
match event {
|
||||
wl_buffer::Event::Release => {
|
||||
// Release reference to `SubsurfaceBuffer`
|
||||
_ = data.subsurface_buffer.lock().unwrap().take();
|
||||
}
|
||||
_ => unreachable!(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[doc(hidden)]
|
||||
#[derive(Clone, Debug)]
|
||||
pub(crate) struct WeakBufferSource(Weak<BufferSource>);
|
||||
|
||||
impl PartialEq for WeakBufferSource {
|
||||
fn eq(&self, rhs: &Self) -> bool {
|
||||
Weak::ptr_eq(&self.0, &rhs.0)
|
||||
}
|
||||
}
|
||||
|
||||
impl Eq for WeakBufferSource {}
|
||||
|
||||
impl Hash for WeakBufferSource {
|
||||
fn hash<H: Hasher>(&self, state: &mut H) {
|
||||
ptr::hash::<BufferSource, _>(self.0.as_ptr(), state)
|
||||
}
|
||||
}
|
||||
|
||||
// create wl_buffer from BufferSource (avoid create_immed?)
|
||||
// release
|
||||
#[derive(Debug, Clone)]
|
||||
#[doc(hidden)]
|
||||
pub struct SubsurfaceState {
|
||||
pub wl_compositor: WlCompositor,
|
||||
pub wl_subcompositor: WlSubcompositor,
|
||||
pub wp_viewporter: WpViewporter,
|
||||
pub wl_shm: WlShm,
|
||||
pub wp_dmabuf: Option<ZwpLinuxDmabufV1>,
|
||||
pub wp_alpha_modifier: Option<WpAlphaModifierV1>,
|
||||
pub qh: QueueHandle<SctkState>,
|
||||
pub(crate) buffers: HashMap<WeakBufferSource, Vec<WlBuffer>>,
|
||||
pub unmapped_subsurfaces: Vec<SubsurfaceInstance>,
|
||||
}
|
||||
|
||||
impl SubsurfaceState {
|
||||
fn create_subsurface(&self, parent: &WlSurface) -> SubsurfaceInstance {
|
||||
let wl_surface = self
|
||||
.wl_compositor
|
||||
.create_surface(&self.qh, SurfaceData::new(None, 1));
|
||||
|
||||
// Use empty input region so parent surface gets pointer events
|
||||
let region = self.wl_compositor.create_region(&self.qh, ());
|
||||
wl_surface.set_input_region(Some(®ion));
|
||||
region.destroy();
|
||||
|
||||
let wl_subsurface = self.wl_subcompositor.get_subsurface(
|
||||
&wl_surface,
|
||||
parent,
|
||||
&self.qh,
|
||||
(),
|
||||
);
|
||||
|
||||
let wp_viewport = self.wp_viewporter.get_viewport(
|
||||
&wl_surface,
|
||||
&self.qh,
|
||||
sctk::globals::GlobalData,
|
||||
);
|
||||
|
||||
let wp_alpha_modifier_surface =
|
||||
self.wp_alpha_modifier.as_ref().map(|wp_alpha_modifier| {
|
||||
wp_alpha_modifier.get_surface(&wl_surface, &self.qh, ())
|
||||
});
|
||||
|
||||
SubsurfaceInstance {
|
||||
wl_surface,
|
||||
wl_subsurface,
|
||||
wp_viewport,
|
||||
wp_alpha_modifier_surface,
|
||||
wl_buffer: None,
|
||||
bounds: None,
|
||||
}
|
||||
}
|
||||
|
||||
// Update `subsurfaces` from `view_subsurfaces`
|
||||
pub(crate) fn update_subsurfaces(
|
||||
&mut self,
|
||||
parent_id: window::Id,
|
||||
subsurface_ids: &mut HashMap<ObjectId, (i32, i32, window::Id)>,
|
||||
parent: &WlSurface,
|
||||
subsurfaces: &mut Vec<SubsurfaceInstance>,
|
||||
view_subsurfaces: &[SubsurfaceInfo],
|
||||
) {
|
||||
// Subsurfaces aren't destroyed immediately to sync removal with parent
|
||||
// surface commit. Since `destroy` is immediate.
|
||||
//
|
||||
// They should be safe to destroy by the next time `update_subsurfaces`
|
||||
// is run.
|
||||
self.unmapped_subsurfaces.clear();
|
||||
|
||||
// Remove cached `wl_buffers` for any `BufferSource`s that no longer exist.
|
||||
self.buffers.retain(|k, v| {
|
||||
let retain = k.0.strong_count() > 0;
|
||||
if !retain {
|
||||
v.iter().for_each(|b| b.destroy());
|
||||
}
|
||||
retain
|
||||
});
|
||||
|
||||
// If view requested fewer subsurfaces than there currently are,
|
||||
// unmap excess.
|
||||
while view_subsurfaces.len() < subsurfaces.len() {
|
||||
let subsurface = subsurfaces.pop().unwrap();
|
||||
subsurface.unmap();
|
||||
self.unmapped_subsurfaces.push(subsurface);
|
||||
}
|
||||
// Create new subsurfaces if there aren't enough.
|
||||
while subsurfaces.len() < view_subsurfaces.len() {
|
||||
subsurfaces.push(self.create_subsurface(parent));
|
||||
}
|
||||
// Attach buffers to subsurfaces, set viewports, and commit.
|
||||
for (subsurface_data, subsurface) in
|
||||
view_subsurfaces.iter().zip(subsurfaces.iter_mut())
|
||||
{
|
||||
subsurface.attach_and_commit(
|
||||
parent_id,
|
||||
subsurface_ids,
|
||||
subsurface_data,
|
||||
self,
|
||||
);
|
||||
}
|
||||
|
||||
if let Some(backend) = parent.backend().upgrade() {
|
||||
subsurface_ids.retain(|k, _| backend.info(k.clone()).is_ok());
|
||||
}
|
||||
}
|
||||
|
||||
// Cache `wl_buffer` for use when `BufferSource` is used in future
|
||||
// (Avoid creating wl_buffer each buffer swap)
|
||||
fn insert_cached_wl_buffer(&mut self, buffer: WlBuffer) {
|
||||
let source = BufferData::for_buffer(&buffer).unwrap().source.clone();
|
||||
self.buffers.entry(source).or_default().push(buffer);
|
||||
}
|
||||
|
||||
// Gets a cached `wl_buffer` for the `SubsurfaceBuffer`, if any. And stores `SubsurfaceBuffer`
|
||||
// reference to be releated on `wl_buffer` release.
|
||||
//
|
||||
// If `wl_buffer` isn't released, it is destroyed instead.
|
||||
fn get_cached_wl_buffer(
|
||||
&mut self,
|
||||
subsurface_buffer: &SubsurfaceBuffer,
|
||||
) -> Option<WlBuffer> {
|
||||
let buffers = self.buffers.get_mut(&WeakBufferSource(
|
||||
Arc::downgrade(&subsurface_buffer.0.source),
|
||||
))?;
|
||||
while let Some(buffer) = buffers.pop() {
|
||||
let mut subsurface_buffer_ref = buffer
|
||||
.data::<BufferData>()
|
||||
.unwrap()
|
||||
.subsurface_buffer
|
||||
.lock()
|
||||
.unwrap();
|
||||
if subsurface_buffer_ref.is_none() {
|
||||
*subsurface_buffer_ref = Some(subsurface_buffer.clone());
|
||||
drop(subsurface_buffer_ref);
|
||||
return Some(buffer);
|
||||
} else {
|
||||
buffer.destroy();
|
||||
}
|
||||
}
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for SubsurfaceState {
|
||||
fn drop(&mut self) {
|
||||
self.buffers
|
||||
.values()
|
||||
.flatten()
|
||||
.for_each(|buffer| buffer.destroy());
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug)]
|
||||
pub(crate) struct SubsurfaceInstance {
|
||||
pub(crate) wl_surface: WlSurface,
|
||||
wl_subsurface: WlSubsurface,
|
||||
wp_viewport: WpViewport,
|
||||
wp_alpha_modifier_surface: Option<WpAlphaModifierSurfaceV1>,
|
||||
wl_buffer: Option<WlBuffer>,
|
||||
bounds: Option<Rectangle<f32>>,
|
||||
}
|
||||
|
||||
impl SubsurfaceInstance {
|
||||
// TODO correct damage? no damage/commit if unchanged?
|
||||
fn attach_and_commit(
|
||||
&mut self,
|
||||
parent_id: window::Id,
|
||||
subsurface_ids: &mut HashMap<ObjectId, (i32, i32, window::Id)>,
|
||||
info: &SubsurfaceInfo,
|
||||
state: &mut SubsurfaceState,
|
||||
) {
|
||||
let buffer_changed;
|
||||
|
||||
let old_buffer = self.wl_buffer.take();
|
||||
let old_buffer_data =
|
||||
old_buffer.as_ref().and_then(|b| BufferData::for_buffer(&b));
|
||||
let buffer = if old_buffer_data.is_some_and(|b| {
|
||||
b.subsurface_buffer.lock().unwrap().as_ref() == Some(&info.buffer)
|
||||
}) {
|
||||
// Same "BufferSource" is already attached to this subsurface. Don't create new `wl_buffer`.
|
||||
buffer_changed = false;
|
||||
old_buffer.unwrap()
|
||||
} else {
|
||||
if let Some(old_buffer) = old_buffer {
|
||||
state.insert_cached_wl_buffer(old_buffer);
|
||||
}
|
||||
|
||||
buffer_changed = true;
|
||||
|
||||
if let Some(buffer) = state.get_cached_wl_buffer(&info.buffer) {
|
||||
buffer
|
||||
} else if let Some(buffer) = info.buffer.create_buffer(
|
||||
&state.wl_shm,
|
||||
state.wp_dmabuf.as_ref(),
|
||||
&state.qh,
|
||||
) {
|
||||
buffer
|
||||
} else {
|
||||
// TODO log error
|
||||
self.wl_surface.attach(None, 0, 0);
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
// XXX scale factor?
|
||||
let bounds_changed = self.bounds != Some(info.bounds);
|
||||
// wlroots seems to have issues changing buffer without running this
|
||||
if bounds_changed || buffer_changed {
|
||||
self.wl_subsurface
|
||||
.set_position(info.bounds.x as i32, info.bounds.y as i32);
|
||||
self.wp_viewport.set_destination(
|
||||
info.bounds.width as i32,
|
||||
info.bounds.height as i32,
|
||||
);
|
||||
}
|
||||
if buffer_changed {
|
||||
self.wl_surface.attach(Some(&buffer), 0, 0);
|
||||
self.wl_surface.damage(0, 0, i32::MAX, i32::MAX);
|
||||
}
|
||||
if buffer_changed || bounds_changed {
|
||||
_ = self.wl_surface.frame(&state.qh, self.wl_surface.clone());
|
||||
self.wl_surface.commit();
|
||||
}
|
||||
|
||||
if let Some(wp_alpha_modifier_surface) = &self.wp_alpha_modifier_surface
|
||||
{
|
||||
let alpha = (info.alpha.clamp(0.0, 1.0) * u32::MAX as f32) as u32;
|
||||
wp_alpha_modifier_surface.set_multiplier(alpha);
|
||||
}
|
||||
|
||||
_ = subsurface_ids.insert(
|
||||
self.wl_surface.id(),
|
||||
(info.bounds.x as i32, info.bounds.y as i32, parent_id),
|
||||
);
|
||||
|
||||
self.wl_buffer = Some(buffer);
|
||||
self.bounds = Some(info.bounds);
|
||||
}
|
||||
|
||||
pub fn unmap(&self) {
|
||||
self.wl_surface.attach(None, 0, 0);
|
||||
self.wl_surface.commit();
|
||||
}
|
||||
}
|
||||
|
||||
impl Drop for SubsurfaceInstance {
|
||||
fn drop(&mut self) {
|
||||
self.wp_viewport.destroy();
|
||||
self.wl_subsurface.destroy();
|
||||
self.wl_surface.destroy();
|
||||
if let Some(wl_buffer) = self.wl_buffer.as_ref() {
|
||||
wl_buffer.destroy();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) struct SubsurfaceInfo {
|
||||
pub buffer: SubsurfaceBuffer,
|
||||
pub bounds: Rectangle<f32>,
|
||||
pub alpha: f32,
|
||||
}
|
||||
|
||||
thread_local! {
|
||||
static SUBSURFACES: RefCell<Vec<SubsurfaceInfo>> = RefCell::new(Vec::new());
|
||||
}
|
||||
|
||||
pub(crate) fn take_subsurfaces() -> Vec<SubsurfaceInfo> {
|
||||
SUBSURFACES.with(|subsurfaces| mem::take(&mut *subsurfaces.borrow_mut()))
|
||||
}
|
||||
|
||||
#[must_use]
|
||||
pub struct Subsurface<'a> {
|
||||
buffer_size: Size<f32>,
|
||||
buffer: &'a SubsurfaceBuffer,
|
||||
width: Length,
|
||||
height: Length,
|
||||
content_fit: ContentFit,
|
||||
alpha: f32,
|
||||
}
|
||||
|
||||
impl<'a, Message, Theme, Renderer> Widget<Message, Theme, Renderer>
|
||||
for Subsurface<'a>
|
||||
where
|
||||
Renderer: renderer::Renderer,
|
||||
{
|
||||
fn size(&self) -> Size<Length> {
|
||||
Size::new(self.width, self.height)
|
||||
}
|
||||
|
||||
// Based on image widget
|
||||
fn layout(
|
||||
&mut self,
|
||||
_tree: &mut widget::Tree,
|
||||
_renderer: &Renderer,
|
||||
limits: &layout::Limits,
|
||||
) -> layout::Node {
|
||||
let raw_size =
|
||||
limits.resolve(self.width, self.height, self.buffer_size);
|
||||
|
||||
let full_size = self.content_fit.fit(self.buffer_size, raw_size);
|
||||
|
||||
let final_size = Size {
|
||||
width: match self.width {
|
||||
Length::Shrink => f32::min(raw_size.width, full_size.width),
|
||||
_ => raw_size.width,
|
||||
},
|
||||
height: match self.height {
|
||||
Length::Shrink => f32::min(raw_size.height, full_size.height),
|
||||
_ => raw_size.height,
|
||||
},
|
||||
};
|
||||
|
||||
layout::Node::new(final_size)
|
||||
}
|
||||
|
||||
fn draw(
|
||||
&self,
|
||||
_state: &widget::Tree,
|
||||
_renderer: &mut Renderer,
|
||||
_theme: &Theme,
|
||||
_style: &renderer::Style,
|
||||
layout: Layout<'_>,
|
||||
_cursor: mouse::Cursor,
|
||||
_viewport: &Rectangle,
|
||||
) {
|
||||
// Instead of using renderer, we need to add surface to a list that is
|
||||
// read by the iced-sctk shell.
|
||||
SUBSURFACES.with(|subsurfaces| {
|
||||
subsurfaces.borrow_mut().push(SubsurfaceInfo {
|
||||
buffer: self.buffer.clone(),
|
||||
bounds: layout.bounds(),
|
||||
alpha: self.alpha,
|
||||
})
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a> Subsurface<'a> {
|
||||
pub fn new(
|
||||
buffer_width: u32,
|
||||
buffer_height: u32,
|
||||
buffer: &'a SubsurfaceBuffer,
|
||||
) -> Self {
|
||||
Self {
|
||||
buffer_size: Size::new(buffer_width as f32, buffer_height as f32),
|
||||
buffer,
|
||||
// Matches defaults of image widget
|
||||
width: Length::Shrink,
|
||||
height: Length::Shrink,
|
||||
content_fit: ContentFit::Contain,
|
||||
alpha: 1.,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn width(mut self, width: Length) -> Self {
|
||||
self.width = width;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn height(mut self, height: Length) -> Self {
|
||||
self.height = height;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn content_fit(mut self, content_fit: ContentFit) -> Self {
|
||||
self.content_fit = content_fit;
|
||||
self
|
||||
}
|
||||
|
||||
pub fn alpha(mut self, alpha: f32) -> Self {
|
||||
self.alpha = alpha;
|
||||
self
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, Message, Theme, Renderer> From<Subsurface<'a>>
|
||||
for Element<'a, Message, Theme, Renderer>
|
||||
where
|
||||
Message: Clone + 'a,
|
||||
Renderer: renderer::Renderer,
|
||||
{
|
||||
fn from(subsurface: Subsurface<'a>) -> Self {
|
||||
Self::new(subsurface)
|
||||
}
|
||||
}
|
||||
|
||||
delegate_noop!(SctkState: ignore WpAlphaModifierV1);
|
||||
delegate_noop!(SctkState: ignore WpAlphaModifierSurfaceV1);
|
||||
453
winit/src/platform_specific/wayland/winit_window.rs
Normal file
453
winit/src/platform_specific/wayland/winit_window.rs
Normal file
|
|
@ -0,0 +1,453 @@
|
|||
use crate::platform_specific::wayland::Action;
|
||||
use raw_window_handle::HandleError;
|
||||
use sctk::reexports::{
|
||||
calloop::channel,
|
||||
client::{
|
||||
Proxy, QueueHandle,
|
||||
protocol::{wl_display::WlDisplay, wl_surface::WlSurface},
|
||||
},
|
||||
};
|
||||
use std::sync::{Arc, Mutex};
|
||||
use winit::{
|
||||
dpi::{LogicalSize, PhysicalPosition},
|
||||
error::{NotSupportedError, RequestError},
|
||||
window::WindowButtons,
|
||||
};
|
||||
|
||||
use crate::platform_specific::SurfaceIdWrapper;
|
||||
|
||||
use super::event_loop::state::{
|
||||
Common, CommonSurface, SctkLayerSurface, SctkLockSurface, SctkPopup,
|
||||
SctkState, TOKEN_CTR,
|
||||
};
|
||||
|
||||
#[derive(Debug)]
|
||||
pub(crate) enum Surface {
|
||||
Popup(SctkPopup),
|
||||
Layer(SctkLayerSurface),
|
||||
Lock(SctkLockSurface),
|
||||
}
|
||||
|
||||
impl Surface {}
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct SctkWinitWindow {
|
||||
tx: channel::Sender<Action>,
|
||||
id: SurfaceIdWrapper,
|
||||
surface: CommonSurface,
|
||||
common: Arc<Mutex<Common>>,
|
||||
display: WlDisplay,
|
||||
pub(crate) queue_handle: QueueHandle<SctkState>,
|
||||
wait_redraw: bool,
|
||||
}
|
||||
|
||||
impl Drop for SctkWinitWindow {
|
||||
fn drop(&mut self) {
|
||||
self.tx.send(Action::Dropped(self.id)).unwrap();
|
||||
}
|
||||
}
|
||||
|
||||
impl SctkWinitWindow {
|
||||
pub(crate) fn new(
|
||||
tx: channel::Sender<Action>,
|
||||
common: Arc<Mutex<Common>>,
|
||||
id: SurfaceIdWrapper,
|
||||
surface: CommonSurface,
|
||||
display: WlDisplay,
|
||||
queue_handle: QueueHandle<SctkState>,
|
||||
) -> Arc<dyn winit::window::Window> {
|
||||
Arc::new(Self {
|
||||
tx,
|
||||
common,
|
||||
id,
|
||||
surface,
|
||||
display,
|
||||
queue_handle,
|
||||
wait_redraw: false,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
impl winit::window::Window for SctkWinitWindow {
|
||||
fn id(&self) -> winit::window::WindowId {
|
||||
winit::window::WindowId::from_raw(
|
||||
self.surface.wl_surface().id().as_ptr() as usize,
|
||||
)
|
||||
}
|
||||
|
||||
fn scale_factor(&self) -> f64 {
|
||||
let guard = self.common.lock().unwrap();
|
||||
guard.fractional_scale.unwrap_or(1.)
|
||||
}
|
||||
|
||||
fn request_redraw(&self) {
|
||||
let surface = self.surface.wl_surface();
|
||||
_ = self.tx.send(Action::RequestRedraw(surface.id()));
|
||||
}
|
||||
|
||||
fn pre_present_notify(&self) {
|
||||
let surface = self.surface.wl_surface();
|
||||
_ = surface.frame(&self.queue_handle, surface.clone());
|
||||
_ = self
|
||||
.tx
|
||||
.send(Action::PrePresentNotify(self.surface.wl_surface().id()));
|
||||
}
|
||||
|
||||
fn set_cursor(&self, cursor: winit_core::cursor::Cursor) {
|
||||
match cursor {
|
||||
winit_core::cursor::Cursor::Icon(icon) => {
|
||||
_ = self.tx.send(Action::SetCursor(icon));
|
||||
}
|
||||
winit_core::cursor::Cursor::Custom(_) => {
|
||||
// TODO
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn set_cursor_visible(&self, visible: bool) {
|
||||
// TODO
|
||||
}
|
||||
|
||||
fn surface_size(&self) -> winit::dpi::PhysicalSize<u32> {
|
||||
let guard = self.common.lock().unwrap();
|
||||
let size = guard.size;
|
||||
size.to_physical(guard.fractional_scale.unwrap_or(1.))
|
||||
}
|
||||
|
||||
fn request_surface_size(
|
||||
&self,
|
||||
size: winit::dpi::Size,
|
||||
) -> Option<winit::dpi::PhysicalSize<u32>> {
|
||||
let mut guard = self.common.lock().unwrap();
|
||||
self.request_redraw();
|
||||
let size: LogicalSize<u32> =
|
||||
size.to_logical(guard.fractional_scale.unwrap_or(1.));
|
||||
match &self.surface {
|
||||
CommonSurface::Popup(popup, positioner) => {
|
||||
if size.width == 0 || size.height == 0 {
|
||||
return None;
|
||||
}
|
||||
guard.size = size;
|
||||
guard.requested_size.0 = Some(guard.size.width);
|
||||
guard.requested_size.1 = Some(guard.size.height);
|
||||
positioner.set_size(
|
||||
guard.size.width as i32,
|
||||
guard.size.height as i32,
|
||||
);
|
||||
popup.xdg_surface().set_window_geometry(
|
||||
0,
|
||||
0,
|
||||
guard.size.width as i32,
|
||||
guard.size.height as i32,
|
||||
);
|
||||
popup.xdg_popup().reposition(
|
||||
positioner,
|
||||
TOKEN_CTR
|
||||
.fetch_add(1, std::sync::atomic::Ordering::Relaxed),
|
||||
);
|
||||
if let Some(viewport) = guard.wp_viewport.as_ref() {
|
||||
// Set inner size without the borders.
|
||||
viewport.set_destination(
|
||||
guard.size.width as i32,
|
||||
guard.size.height as i32,
|
||||
);
|
||||
}
|
||||
}
|
||||
CommonSurface::Layer(layer_surface) => {
|
||||
guard.requested_size = (
|
||||
(size.width > 0).then_some(size.width),
|
||||
(size.height > 0).then_some(size.height),
|
||||
);
|
||||
if size.width > 0 {
|
||||
guard.size.width = size.width;
|
||||
}
|
||||
if size.height > 0 {
|
||||
guard.size.height = size.height;
|
||||
}
|
||||
layer_surface.set_size(size.width, size.height);
|
||||
if let Some(viewport) = guard.wp_viewport.as_ref() {
|
||||
// Set inner size without the borders.
|
||||
viewport.set_destination(
|
||||
guard.size.width as i32,
|
||||
guard.size.height as i32,
|
||||
);
|
||||
}
|
||||
}
|
||||
CommonSurface::Lock(_) => {}
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
fn reset_dead_keys(&self) {
|
||||
// TODO refer to winit for implementation
|
||||
}
|
||||
|
||||
fn set_outer_position(&self, position: winit::dpi::Position) {}
|
||||
|
||||
fn outer_size(&self) -> winit::dpi::PhysicalSize<u32> {
|
||||
// XXX not applicable to wrapped surfaces
|
||||
Default::default()
|
||||
}
|
||||
|
||||
fn set_min_surface_size(&self, min_size: Option<winit::dpi::Size>) {
|
||||
// XXX not applicable to wrapped surfaces
|
||||
}
|
||||
|
||||
fn set_max_surface_size(&self, max_size: Option<winit::dpi::Size>) {
|
||||
// XXX not applicable to wrapped surfaces
|
||||
}
|
||||
|
||||
fn set_surface_resize_increments(
|
||||
&self,
|
||||
increments: Option<winit::dpi::Size>,
|
||||
) {
|
||||
log::warn!(
|
||||
"`set_surface_resize_increments` is not implemented for Wayland"
|
||||
)
|
||||
}
|
||||
|
||||
fn set_title(&self, title: &str) {
|
||||
// XXX not applicable to wrapped surfaces
|
||||
}
|
||||
|
||||
fn set_transparent(&self, transparent: bool) {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn rwh_06_display_handle(
|
||||
&self,
|
||||
) -> &dyn raw_window_handle::HasDisplayHandle {
|
||||
self
|
||||
}
|
||||
|
||||
fn rwh_06_window_handle(&self) -> &dyn raw_window_handle::HasWindowHandle {
|
||||
self
|
||||
}
|
||||
|
||||
fn current_monitor(&self) -> Option<winit::monitor::MonitorHandle> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn available_monitors(
|
||||
&self,
|
||||
) -> Box<dyn Iterator<Item = winit::monitor::MonitorHandle>> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn has_focus(&self) -> bool {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn set_ime_cursor_area(
|
||||
&self,
|
||||
position: winit::dpi::Position,
|
||||
size: winit::dpi::Size,
|
||||
) {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn set_ime_allowed(&self, allowed: bool) {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn set_ime_purpose(&self, purpose: winit::window::ImePurpose) {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn set_blur(&self, blur: bool) {
|
||||
// TODO
|
||||
}
|
||||
|
||||
fn set_visible(&self, visible: bool) {}
|
||||
|
||||
fn is_visible(&self) -> Option<bool> {
|
||||
None
|
||||
}
|
||||
|
||||
fn set_resizable(&self, resizable: bool) {}
|
||||
|
||||
fn is_resizable(&self) -> bool {
|
||||
false
|
||||
}
|
||||
|
||||
fn set_enabled_buttons(&self, buttons: winit::window::WindowButtons) {
|
||||
// TODO v5 of xdg_shell.
|
||||
}
|
||||
|
||||
fn enabled_buttons(&self) -> winit::window::WindowButtons {
|
||||
WindowButtons::all()
|
||||
}
|
||||
|
||||
fn set_minimized(&self, minimized: bool) {
|
||||
// XXX not applicable to the wrapped surfaces
|
||||
}
|
||||
|
||||
fn is_minimized(&self) -> Option<bool> {
|
||||
// XXX clients don't know whether they are minimized or not.
|
||||
None
|
||||
}
|
||||
|
||||
fn set_maximized(&self, maximized: bool) {
|
||||
// XXX can't minimize the wrapped surfaces
|
||||
}
|
||||
|
||||
fn is_maximized(&self) -> bool {
|
||||
// XXX can't maximize the wrapped surfaces
|
||||
false
|
||||
}
|
||||
|
||||
fn set_fullscreen(
|
||||
&self,
|
||||
fullscreen: Option<winit_core::monitor::Fullscreen>,
|
||||
) {
|
||||
// XXX can't fullscreen the wrapped surfaces
|
||||
}
|
||||
|
||||
fn fullscreen(&self) -> Option<winit_core::monitor::Fullscreen> {
|
||||
// XXX can't fullscreen the wrapped surfaces
|
||||
None
|
||||
}
|
||||
|
||||
fn set_decorations(&self, decorations: bool) {
|
||||
// XXX no decorations supported for the wrapped surfaces
|
||||
}
|
||||
|
||||
fn is_decorated(&self) -> bool {
|
||||
false
|
||||
}
|
||||
|
||||
fn set_window_level(&self, level: winit::window::WindowLevel) {}
|
||||
|
||||
fn set_window_icon(&self, window_icon: Option<winit_core::icon::Icon>) {}
|
||||
|
||||
fn focus_window(&self) {}
|
||||
|
||||
fn request_user_attention(
|
||||
&self,
|
||||
request_type: Option<winit::window::UserAttentionType>,
|
||||
) {
|
||||
// XXX can't request attention on wrapped surfaces
|
||||
}
|
||||
|
||||
fn set_theme(&self, theme: Option<winit::window::Theme>) {}
|
||||
|
||||
fn theme(&self) -> Option<winit::window::Theme> {
|
||||
None
|
||||
}
|
||||
|
||||
fn set_content_protected(&self, protected: bool) {}
|
||||
|
||||
fn title(&self) -> String {
|
||||
String::new()
|
||||
}
|
||||
|
||||
fn show_window_menu(&self, _position: winit::dpi::Position) {
|
||||
// XXX can't show window menu on wrapped surfaces
|
||||
}
|
||||
|
||||
fn primary_monitor(&self) -> Option<winit::monitor::MonitorHandle> {
|
||||
None
|
||||
}
|
||||
|
||||
fn surface_resize_increments(
|
||||
&self,
|
||||
) -> Option<winit::dpi::PhysicalSize<u32>> {
|
||||
None
|
||||
}
|
||||
|
||||
fn drag_window(&self) -> Result<(), winit::error::RequestError> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn drag_resize_window(
|
||||
&self,
|
||||
_direction: winit::window::ResizeDirection,
|
||||
) -> Result<(), winit::error::RequestError> {
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn set_cursor_hittest(
|
||||
&self,
|
||||
_hittest: bool,
|
||||
) -> Result<(), winit::error::RequestError> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn surface_position(&self) -> winit::dpi::PhysicalPosition<i32> {
|
||||
PhysicalPosition::new(0, 0)
|
||||
}
|
||||
|
||||
fn outer_position(
|
||||
&self,
|
||||
) -> Result<winit::dpi::PhysicalPosition<i32>, winit::error::RequestError>
|
||||
{
|
||||
Err(RequestError::NotSupported(NotSupportedError::new(
|
||||
"Not supported on wayland.",
|
||||
)))
|
||||
}
|
||||
|
||||
fn set_cursor_position(
|
||||
&self,
|
||||
position: winit::dpi::Position,
|
||||
) -> Result<(), winit::error::RequestError> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn set_cursor_grab(
|
||||
&self,
|
||||
mode: winit::window::CursorGrabMode,
|
||||
) -> Result<(), winit::error::RequestError> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn safe_area(&self) -> winit::dpi::PhysicalInsets<u32> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn request_ime_update(
|
||||
&self,
|
||||
request: winit::window::ImeRequest,
|
||||
) -> Result<(), winit::window::ImeRequestError> {
|
||||
todo!()
|
||||
}
|
||||
|
||||
fn ime_capabilities(&self) -> Option<winit::window::ImeCapabilities> {
|
||||
todo!()
|
||||
}
|
||||
}
|
||||
|
||||
impl raw_window_handle::HasWindowHandle for SctkWinitWindow {
|
||||
fn window_handle(
|
||||
&self,
|
||||
) -> Result<
|
||||
raw_window_handle::WindowHandle<'_>,
|
||||
raw_window_handle::HandleError,
|
||||
> {
|
||||
let raw = raw_window_handle::WaylandWindowHandle::new({
|
||||
let ptr = self.surface.wl_surface().id().as_ptr();
|
||||
let Some(ptr) = std::ptr::NonNull::new(ptr as *mut _) else {
|
||||
return Err(HandleError::Unavailable);
|
||||
};
|
||||
ptr
|
||||
});
|
||||
|
||||
unsafe { Ok(raw_window_handle::WindowHandle::borrow_raw(raw.into())) }
|
||||
}
|
||||
}
|
||||
|
||||
impl raw_window_handle::HasDisplayHandle for SctkWinitWindow {
|
||||
fn display_handle(
|
||||
&self,
|
||||
) -> Result<
|
||||
raw_window_handle::DisplayHandle<'_>,
|
||||
raw_window_handle::HandleError,
|
||||
> {
|
||||
let raw = raw_window_handle::WaylandDisplayHandle::new({
|
||||
let ptr = self.display.id().as_ptr();
|
||||
std::ptr::NonNull::new(ptr as *mut _)
|
||||
.expect("wl_proxy should never be null")
|
||||
});
|
||||
|
||||
unsafe { Ok(raw_window_handle::DisplayHandle::borrow_raw(raw.into())) }
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue