Skip to content
This repository has been archived by the owner on Jul 15, 2024. It is now read-only.

Migrate away from individual handlers for each Window #176

Draft
wants to merge 10 commits into
base: main
Choose a base branch
from
5 changes: 4 additions & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,9 @@ categories = [
exclude = ["/.github/"]
publish = false # Until it's ready

[workspace]
members = ["v2"]

[package.metadata.docs.rs]
all-features = true
rustdoc-args = ["--cfg", "docsrs"]
Expand All @@ -24,7 +27,7 @@ default-target = "x86_64-pc-windows-msvc"
cargo-args = ["-Zunstable-options", "-Zrustdoc-scrape-examples"]

[features]
default = ["x11"]
default = ["x11", "wayland"]
x11 = ["ashpd", "bindgen", "futures", "nix", "pkg-config", "x11rb"]
wayland = [
# Required for XKBCommon
Expand Down
68 changes: 68 additions & 0 deletions v2/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
[package]
name = "v2"
version = "0.1.0"
edition = "2021"

# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html

[features]
default = ["wayland"]
wayland = [
# Required for XKBCommon
"pkg-config",
"bindgen",
"nix",
"smithay-client-toolkit",
"wayland-backend",
]


[dependencies]
kurbo = "0.10.4"
# glazier = { path = "../" }
cfg-if = "1.0.0"

tracing = { version = "0.1.22", features = ["log"] }
raw-window-handle = { version = "0.5.0", default_features = false }

keyboard-types = { version = "0.7", default_features = false }
instant = { version = "0.1.6", features = ["wasm-bindgen"] }
thiserror = "1.0.51"

[dev-dependencies]
static_assertions = "1.1.0"

[target.'cfg(any(target_os = "freebsd", target_os="linux", target_os="openbsd"))'.dependencies]
ashpd = { version = "0.6.7", optional = true }
futures = { version = "0.3.24", optional = true, features = ["executor"] }

nix = { version = "0.27.0", optional = true }

x11rb = { version = "0.13", features = [
"allow-unsafe-code",
"present",
"render",
"randr",
"xfixes",
"xkb",
"resource_manager",
"cursor",
"xinput",
], optional = true }

rand = { version = "0.8.0", optional = true }
log = { version = "0.4.14", optional = true }

smithay-client-toolkit = { version = "0.18.0", optional = true, default-features = false, features = [
# Don't use the built-in xkb handling
"calloop",
] }
# Wayland dependencies
# Needed for supporting RawWindowHandle
wayland-backend = { version = "0.3.2", default_features = false, features = [
"client_system",
], optional = true }

[target.'cfg(any(target_os = "freebsd", target_os="linux", target_os="openbsd"))'.build-dependencies]
bindgen = { version = "0.66", optional = true }
pkg-config = { version = "0.3.25", optional = true }
68 changes: 68 additions & 0 deletions v2/build.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
#[cfg(not(all(
any(feature = "x11", feature = "wayland"),
any(target_os = "freebsd", target_os = "linux", target_os = "openbsd")
)))]
fn main() {}

#[cfg(all(
any(feature = "x11", feature = "wayland"),
any(target_os = "freebsd", target_os = "linux", target_os = "openbsd")
))]
fn main() {
use pkg_config::probe_library;
use std::env;
use std::path::PathBuf;

let xkbcommon = probe_library("xkbcommon").unwrap();

#[cfg(feature = "x11")]
probe_library("xkbcommon-x11").unwrap();

let mut header = "\
#include <xkbcommon/xkbcommon-compose.h>
#include <xkbcommon/xkbcommon-names.h>
#include <xkbcommon/xkbcommon.h>"
.to_string();

if cfg!(feature = "x11") {
header += "
#include <xkbcommon/xkbcommon-x11.h>";
}

let bindings = bindgen::Builder::default()
// The input header we would like to generate
// bindings for.
.header_contents("wrapper.h", &header)
.clang_args(
xkbcommon
.include_paths
.iter()
.filter_map(|path| path.to_str().map(|s| format!("-I{s}"))),
)
// Tell cargo to invalidate the built crate whenever any of the
// included header files changed.
.parse_callbacks(Box::new(bindgen::CargoCallbacks))
.prepend_enum_name(false)
.size_t_is_usize(true)
.allowlist_function("xkb_.*")
.allowlist_type("xkb_.*")
.allowlist_var("XKB_.*")
.allowlist_type("xcb_connection_t")
// this needs var args
.blocklist_function("xkb_context_set_log_fn")
// we use FILE from libc
.blocklist_type("FILE")
.blocklist_type("va_list")
.default_enum_style(bindgen::EnumVariation::NewType {
is_bitfield: true,
is_global: false,
})
.generate()
.expect("Unable to generate bindings");

// Write the bindings to the $OUT_DIR/xkbcommon.rs file.
let out_path = PathBuf::from(env::var("OUT_DIR").unwrap());
bindings
.write_to_file(out_path.join("xkbcommon_sys.rs"))
.expect("Couldn't write bindings!");
}
4 changes: 4 additions & 0 deletions v2/src/backend/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
mod new_wayland;
mod shared;

pub(crate) use new_wayland::*;
60 changes: 60 additions & 0 deletions v2/src/backend/new_wayland/error.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
// Copyright 2020 The Druid Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

//! Wayland errors

use std::fmt;

use smithay_client_toolkit::reexports::{
calloop,
client::{globals::BindError, ConnectError},
};

// TODO: Work out error handling
#[derive(Debug)]
pub enum Error {
Connect(ConnectError),
Bind(BindError),
Calloop(calloop::Error),
}

impl fmt::Display for Error {
fn fmt(&self, f: &mut fmt::Formatter) -> Result<(), fmt::Error> {
match self {
Error::Connect(e) => write!(f, "could not connect to the wayland server: {e:}"),
Error::Bind(e) => write!(f, "could not bind a wayland global: {e:}"),
Error::Calloop(e) => write!(f, "calloop failed: {e:}"),
}
}
}

impl std::error::Error for Error {}

impl From<ConnectError> for Error {
fn from(value: ConnectError) -> Self {
Self::Connect(value)
}
}

impl From<BindError> for Error {
fn from(value: BindError) -> Self {
Self::Bind(value)
}
}

impl From<calloop::Error> for Error {
fn from(value: calloop::Error) -> Self {
Self::Calloop(value)
}
}
137 changes: 137 additions & 0 deletions v2/src/backend/new_wayland/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,137 @@
// Copyright 2019 The Druid Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

//! wayland platform support

use std::{any::TypeId, fmt::Debug, marker::PhantomData};

use smithay_client_toolkit::{
delegate_registry,
reexports::{
calloop::{self, LoopHandle, LoopSignal},
client::{Proxy, QueueHandle},
protocols::wp::text_input::zv3::client::zwp_text_input_manager_v3::ZwpTextInputManagerV3,
},
registry::{ProvidesRegistryState, RegistryState},
registry_handlers,
};
use thiserror::Error;

use self::{outputs::Outputs, windows::Windowing};
use super::shared::xkb::Context;

use crate::{handler::PlatformHandler, window::IdleToken, Glazier};

pub(crate) mod error;
mod outputs;
mod run_loop;
mod windows;

#[derive(Error, Debug)]
pub enum BackendWindowCreationError {}

pub(crate) use run_loop::{launch, LoopHandle as LoopHandle2};

pub(crate) type GlazierImpl<'a> = &'a mut WaylandState;

/// The main state type of the event loop. Implements dispatching for all supported
/// wayland events
struct WaylandPlatform {
// Drop the handler as early as possible, in case there are any Wgpu surfaces owned by it
pub(self) handler: Box<dyn PlatformHandler>,
pub(self) state: WaylandState,
}

pub(crate) struct WaylandState {
// Meta
/// The type of the user's [PlatformHandler]. Used to allow
/// [Glazier::handle] to have eager error handling
pub(self) handler_type: TypeId,

// Event loop management
/// The queue used to communicate with the platform
pub(self) wayland_queue: QueueHandle<WaylandPlatform>,
/// Used to stop the event loop
pub(self) loop_signal: LoopSignal,
/// Used to add new items into the loop. Primarily used for timers and keyboard repeats
pub(self) loop_handle: LoopHandle<'static, WaylandPlatform>,

// Callbacks and other delayed actions
/// The actions which the application has requested to occur on the next opportunity
pub(self) idle_actions: Vec<IdleAction>,
/// Actions which the application has requested to happen, but which require access to the handler
// pub(self) actions: VecDeque<ActiveAction>,
/// The sender used to access the event loop from other threads
pub(self) loop_sender: calloop::channel::Sender<LoopCallback>,

// Subsytem state
/// Monitors, not currently used
pub(self) monitors: Outputs,

// State of the windowing subsystem
pub(self) windows: Windowing,

// Input. Wayland splits input into seats, and doesn't provide much
// help in implementing cases where there are multiple of these
/// The sctk manager for seats
// pub(self) seats: SeatState,
/// The data
// pub(self) input_states: Vec<SeatInfo>,
/// Global used for IME. Optional because the compositor might not implement text input
pub(self) text_input: Option<ZwpTextInputManagerV3>,
/// The xkb context object
pub(self) xkb_context: Context,
// Other wayland state
pub(self) registry_state: RegistryState,
}

delegate_registry!(WaylandPlatform);

impl ProvidesRegistryState for WaylandPlatform {
fn registry(&mut self) -> &mut RegistryState {
&mut self.state.registry_state
}
registry_handlers![Outputs];
}

// We *could* implement `Deref<Target=WaylandState>` for `WaylandPlatform`, but
// that causes borrow checking issues, because the borrow checker doesn't know
// that the derefs don't make unrelated fields alias in a horrible but safe way.
// To enable greater consistency, we therefore force using `plat.state`

impl WaylandPlatform {
fn with_glz<R>(&mut self, f: impl FnOnce(&mut dyn PlatformHandler, Glazier) -> R) -> R {
f(&mut *self.handler, Glazier(&mut self.state, PhantomData))
// TODO: Is now the time to drain `self.actions`?
}
}

enum IdleAction {
Callback(LoopCallback),
Token(IdleToken),
}

type LoopCallback = Box<dyn FnOnce(&mut WaylandPlatform) + Send>;

fn on_unknown_event<P: Proxy>(proxy: &P, event: P::Event)
where
P::Event: Debug,
{
let name = P::interface().name;
tracing::warn!(
proxy = ?proxy,
event = ?event,
issues_url = "https://github.com/linebender/glazier/issues",
"Got an unknown event for interface {name}, got event: {event:?}. Please report this to Glazier on GitHub");
}
Loading
Loading