Integrate native Rime and stabilize input, output, and rendering

- Add optional modifier-only Super binding whose tap is cancelled by
  other input, focus changes, grabs, locks, and session teardown.
- Ship the native ferese-rime user service, Rime defaults, installer
  inventory, package dependencies, session environment, and docs.
- Vendor Smithay 8a27023 with the SHM texture-cache format fix and add
  GPU regressions for format changes and resize snapshots.
- Release reused CRTC owners before staging output modes, then restore
  changed outputs before former owners when a transaction fails.
- Wait for resize-capture GPU completion, expand filtered surface
  damage, and preserve IME popup origin under fractional scaling.
- Mark weekends like holidays in both calendar layouts.
This commit is contained in:
aria committed 2026-10-30 03:13:43 +09:00
1 parent a2be421974
commit 993bc0f88f
391 files changed
+130908 -76

No files matched your search

+501
View File
@@ -0,0 +1,501 @@
use std::{fmt, fs::File, os::unix::io::OwnedFd, path::PathBuf};
use clap::{Args, Parser, Subcommand, ValueEnum};
use smithay::{
backend::{
allocator::{
Allocator, Fourcc, Modifier,
dmabuf::{AnyError, Dmabuf, DmabufAllocator},
dumb::DumbAllocator,
gbm::{GbmAllocator, GbmBufferFlags, GbmDevice},
vulkan::{ImageUsageFlags, VulkanAllocator},
},
drm::{DrmDeviceFd, DrmNode},
egl::{EGLContext, EGLDevice, EGLDisplay},
renderer::{
Bind, Color32F, ExportMem, Frame, ImportDma, Offscreen, Renderer,
gles::{GlesRenderbuffer, GlesRenderer},
},
vulkan::{Instance, PhysicalDevice, version::Version},
},
utils::{DeviceFd, Rectangle, Transform},
};
use tracing::info;
#[derive(Parser, Debug)]
#[command(author, version, about, long_about = None)]
struct Cli {
#[command(subcommand)]
subcommand: Subcommands,
}
#[derive(Subcommand, Debug)]
enum Subcommands {
/// Intersect formats to find usable combinations
#[command(arg_required_else_help = true)]
Formats {
/// Devices that should be able to render a format
#[arg(short, long)]
render: Vec<String>,
/// Devices that should be able to sample from a format
#[arg(short, long)]
sample: Vec<String>,
},
/// Test exporting and importing buffers with various apis and devices
Test(TestArgs),
}
#[derive(Args, Debug)]
struct TestArgs {
/// Allocator used for creating the buffer
#[arg(short, long)]
allocator: AllocatorType,
/// Usage flags for allocating the image, when using the vulkan api
#[arg(short, long, value_parser = usage_flags_from_string, required_if_eq("allocator", "vulkan"))]
usage_flags: Option<ImageUsageFlags>,
/// Exporting device node
#[arg(short, long, required_if_eq_any([("allocator", "dumb"), ("allocator", "gbm"), ("allocator", "vulkan")]))]
export: Option<String>,
/// Importing device node (may be equal to export)
#[arg(short, long)]
import: String,
/// Renderer used to draw into buffer before exporting
#[arg(short = 'r', long)]
export_renderer: Option<RendererType>,
#[arg(short = 't', long)]
import_renderer: Option<RendererType>,
#[arg(short, long, requires("export_renderer"), requires("import_renderer"))]
dump: Option<String>,
#[arg(long, default_value_t = 256)]
width: u32,
#[arg(long, default_value_t = 256)]
height: u32,
#[arg(short, long, default_value = "Abgr8888", value_parser = fourcc_from_string)]
fourcc: Fourcc,
#[arg(short, long, default_value = "Linear", value_parser = modifier_from_string)]
modifier: Modifier,
}
#[derive(ValueEnum, Debug, Clone, Copy)]
pub enum AllocatorType {
#[value(name = "dumb")]
DumbBuffer,
#[value(name = "gbm")]
Gbm,
#[value(name = "vulkan")]
Vulkan,
}
#[derive(ValueEnum, Debug, Clone, Copy)]
pub enum RendererType {
#[value(name = "gles")]
Gles,
}
impl fmt::Display for RendererType {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str("Gles")
}
}
fn main() {
let args = Cli::parse();
if let Ok(env_filter) = tracing_subscriber::EnvFilter::try_from_default_env() {
tracing_subscriber::fmt().with_env_filter(env_filter).init();
} else {
tracing_subscriber::fmt().init();
}
match args.subcommand {
Subcommands::Formats { render, sample } => format_test(render, sample),
Subcommands::Test(args) => buffer_test(args),
}
}
fn format_test(render: Vec<String>, sample: Vec<String>) {
for format in render
.iter()
.map(|path| {
let path = PathBuf::from(&path);
let device = EGLDevice::enumerate()
.expect("Failed to enumerate EGL devices")
.find(|device| {
device.drm_device_path().ok().as_ref() == Some(&path)
|| device.render_device_path().ok().as_ref() == Some(&path)
})
.expect("Unable to find egl device");
let display = unsafe { EGLDisplay::new(device).expect("Failed to create EGL display") };
display.dmabuf_render_formats().clone()
})
.chain(sample.iter().map(|path| {
let path = PathBuf::from(&path);
let device = EGLDevice::enumerate()
.expect("Failed to enumerate EGL devices")
.find(|device| {
device.drm_device_path().ok().as_ref() == Some(&path)
|| device.render_device_path().ok().as_ref() == Some(&path)
})
.expect("Unable to find egl device");
let display = unsafe { EGLDisplay::new(device).expect("Failed to create EGL display") };
display.dmabuf_texture_formats().clone()
}))
.fold(None, |set, formats| match set {
None => Some(formats),
Some(set) => Some(set.intersection(&formats).copied().collect()),
})
.unwrap_or_default()
{
println!(
"Format: Fourcc {} / Modifier {}",
format.code,
Into::<u64>::into(format.modifier)
)
}
}
fn open_device(path: &str) -> DrmDeviceFd {
let file = File::options()
.read(true)
.write(true)
.open(path)
.expect("Failed to open device node");
DrmDeviceFd::new(DeviceFd::from(Into::<OwnedFd>::into(file)))
}
fn buffer_test(args: TestArgs) {
// 1. create allocator
let path = args
.export
.expect("Dumb buffer allocator requires an export device");
info!("Export device: {}", path);
let mut allocator = match args.allocator {
AllocatorType::DumbBuffer => {
let fd = open_device(&path);
let dumb_allocator = DumbAllocator::new(fd);
Box::new(DmabufAllocator(dumb_allocator)) as Box<dyn Allocator<Buffer = Dmabuf, Error = AnyError>>
}
AllocatorType::Gbm => {
let fd = open_device(&path);
let gbm = GbmDevice::new(fd).expect("Failed to init gbm device");
let gbm_allocator = GbmAllocator::new(gbm, GbmBufferFlags::RENDERING);
Box::new(DmabufAllocator(gbm_allocator)) as Box<_>
}
AllocatorType::Vulkan => {
let node = DrmNode::from_path(&path).expect("Failed to find drm node");
let instance =
Instance::new(Version::VERSION_1_2, None).expect("Unable to create vulkan instance");
let physical_device = PhysicalDevice::enumerate(&instance)
.expect("Failed to enumerate physical devices")
.filter(|phd| phd.has_device_extension(c"VK_EXT_physical_device_drm"))
.find(|phd| {
phd.primary_node().unwrap() == Some(node) || phd.render_node().unwrap() == Some(node)
})
.expect("Unable to find physical device");
Box::new(DmabufAllocator(
VulkanAllocator::new(&physical_device, args.usage_flags.unwrap())
.expect("Failed to create vulkan allocator"),
)) as Box<_>
}
};
// 2. alloc buffer
let mut buffer = allocator
.create_buffer(args.width, args.height, args.fourcc, &[args.modifier])
.expect("Failed to allocate buffer");
// 3. render into buffer on src device
match args.export_renderer {
None => {}
Some(RendererType::Gles) => {
let path = PathBuf::from(&path);
let device = EGLDevice::enumerate()
.expect("Failed to enumerate EGL devices")
.find(|device| {
device.drm_device_path().ok().as_ref() == Some(&path)
|| device.render_device_path().ok().as_ref() == Some(&path)
})
.expect("Unable to find egl device");
let display = unsafe { EGLDisplay::new(device).expect("Failed to create EGL display") };
let context = EGLContext::new(&display).expect("Failed to create EGL context");
let mut renderer = unsafe { GlesRenderer::new(context).expect("Failed to init GL ES renderer") };
render_into(&mut renderer, &mut buffer, args.width as i32, args.height as i32);
}
}
// 4. import / render buffer on dst device
let path = PathBuf::from(args.import);
info!("Import device: {}", path.display());
match args.import_renderer {
None => {
let device = EGLDevice::enumerate()
.expect("Failed to enumerate EGL devices")
.find(|device| {
device.drm_device_path().ok().as_ref() == Some(&path)
|| device.render_device_path().ok().as_ref() == Some(&path)
})
.expect("Unable to find egl device");
let display = unsafe { EGLDisplay::new(device).expect("Failed to create EGL display") };
display
.create_image_from_dmabuf(&buffer)
.expect("Failed to import dmabuf");
}
Some(RendererType::Gles) => {
let device = EGLDevice::enumerate()
.expect("Failed to enumerate EGL devices")
.find(|device| {
device.drm_device_path().ok().as_ref() == Some(&path)
|| device.render_device_path().ok().as_ref() == Some(&path)
})
.expect("Unable to find egl device");
let display = unsafe { EGLDisplay::new(device).expect("Failed to create EGL display") };
let context = EGLContext::new(&display).expect("Failed to create EGL context");
let mut renderer = unsafe { GlesRenderer::new(context).expect("Failed to init GL ES renderer") };
render_from::<_, GlesRenderbuffer>(
&mut renderer,
buffer,
args.width as i32,
args.height as i32,
args.dump,
);
}
}
}
fn render_into<R, T>(renderer: &mut R, buffer: &mut T, w: i32, h: i32)
where
R: Renderer + Bind<T>,
{
// Bind it as a framebuffer
let mut framebuffer = renderer.bind(buffer).expect("Failed to bind dmabuf");
let mut frame = renderer
.render(&mut framebuffer, (w, h).into(), Transform::Normal)
.expect("Failed to create render frame");
frame
.clear(
Color32F::new(1.0, 0.0, 0.0, 1.0),
&[Rectangle::from_size((w / 2, h / 2).into())],
)
.expect("Render error");
frame
.clear(
Color32F::new(0.0, 1.0, 0.0, 1.0),
&[Rectangle::new((w / 2, 0).into(), (w / 2, h / 2).into())],
)
.expect("Render error");
frame
.clear(
Color32F::new(0.0, 0.0, 1.0, 1.0),
&[Rectangle::new((0, h / 2).into(), (w / 2, h / 2).into())],
)
.expect("Render error");
frame
.clear(
Color32F::new(1.0, 1.0, 0.0, 1.0),
&[Rectangle::new((w / 2, h / 2).into(), (w / 2, h / 2).into())],
)
.expect("Render error");
frame
.finish()
.expect("Failed to finish render frame")
.wait()
.expect("Synchronization error");
}
fn render_from<R, T>(renderer: &mut R, buffer: Dmabuf, w: i32, h: i32, dump: Option<String>)
where
R: Renderer + ImportDma + ExportMem + Offscreen<T>,
{
let texture = renderer
.import_dmabuf(&buffer, None)
.expect("Failed to import dmabuf");
let mut offscreen = Offscreen::<T>::create_buffer(renderer, Fourcc::Abgr8888, (w, h).into())
.expect("Failed to create offscreen buffer");
let mut framebuffer = renderer
.bind(&mut offscreen)
.expect("Failed to bind offscreen buffer");
let mut frame = renderer
.render(&mut framebuffer, (w, h).into(), Transform::Normal)
.expect("Failed to create render frame");
frame
.render_texture_at(
&texture,
(0, 0).into(),
1,
1.,
Transform::Normal,
&[Rectangle::from_size((w, h).into())],
&[],
1.0,
)
.expect("Failed to sample dmabuf");
frame
.finish()
.expect("Failed to finish render frame")
.wait()
.expect("Synchronization error");
if let Some(path) = dump {
let mapping = renderer
.copy_framebuffer(
&framebuffer,
Rectangle::from_size((w, h).into()),
Fourcc::Abgr8888,
)
.expect("Failed to map framebuffer");
let copy = renderer.map_texture(&mapping).expect("Failed to read mapping");
image::save_buffer(path, copy, w as u32, h as u32, image::ColorType::Rgba8)
.expect("Failed to save image");
}
}
fn fourcc_from_string(name: &str) -> Result<Fourcc, &'static str> {
Ok(match name {
"Abgr1555" => Fourcc::Abgr1555,
"Abgr16161616f" => Fourcc::Abgr16161616f,
"Abgr2101010" => Fourcc::Abgr2101010,
"Abgr4444" => Fourcc::Abgr4444,
"Abgr8888" => Fourcc::Abgr8888,
"Argb1555" => Fourcc::Argb1555,
"Argb16161616f" => Fourcc::Argb16161616f,
"Argb2101010" => Fourcc::Argb2101010,
"Argb4444" => Fourcc::Argb4444,
"Argb8888" => Fourcc::Argb8888,
"Axbxgxrx106106106106" => Fourcc::Axbxgxrx106106106106,
"Ayuv" => Fourcc::Ayuv,
"Bgr233" => Fourcc::Bgr233,
"Bgr565" => Fourcc::Bgr565,
"Bgr565_a8" => Fourcc::Bgr565_a8,
"Bgr888" => Fourcc::Bgr888,
"Bgr888_a8" => Fourcc::Bgr888_a8,
"Bgra1010102" => Fourcc::Bgra1010102,
"Bgra4444" => Fourcc::Bgra4444,
"Bgra5551" => Fourcc::Bgra5551,
"Bgra8888" => Fourcc::Bgra8888,
"Bgrx1010102" => Fourcc::Bgrx1010102,
"Bgrx4444" => Fourcc::Bgrx4444,
"Bgrx5551" => Fourcc::Bgrx5551,
"Bgrx8888" => Fourcc::Bgrx8888,
"Bgrx8888_a8" => Fourcc::Bgrx8888_a8,
"Big_endian" => Fourcc::Big_endian,
"C8" => Fourcc::C8,
"Gr1616" => Fourcc::Gr1616,
"Gr88" => Fourcc::Gr88,
"Nv12" => Fourcc::Nv12,
"Nv15" => Fourcc::Nv15,
"Nv16" => Fourcc::Nv16,
"Nv21" => Fourcc::Nv21,
"Nv24" => Fourcc::Nv24,
"Nv42" => Fourcc::Nv42,
"Nv61" => Fourcc::Nv61,
"P010" => Fourcc::P010,
"P012" => Fourcc::P012,
"P016" => Fourcc::P016,
"P210" => Fourcc::P210,
"Q401" => Fourcc::Q401,
"Q410" => Fourcc::Q410,
"R16" => Fourcc::R16,
"R8" => Fourcc::R8,
"Rg1616" => Fourcc::Rg1616,
"Rg88" => Fourcc::Rg88,
"Rgb332" => Fourcc::Rgb332,
"Rgb565" => Fourcc::Rgb565,
"Rgb565_a8" => Fourcc::Rgb565_a8,
"Rgb888" => Fourcc::Rgb888,
"Rgb888_a8" => Fourcc::Rgb888_a8,
"Rgba1010102" => Fourcc::Rgba1010102,
"Rgba4444" => Fourcc::Rgba4444,
"Rgba5551" => Fourcc::Rgba5551,
"Rgba8888" => Fourcc::Rgba8888,
"Rgbx1010102" => Fourcc::Rgbx1010102,
"Rgbx4444" => Fourcc::Rgbx4444,
"Rgbx5551" => Fourcc::Rgbx5551,
"Rgbx8888" => Fourcc::Rgbx8888,
"Rgbx8888_a8" => Fourcc::Rgbx8888_a8,
"Uyvy" => Fourcc::Uyvy,
"Vuy101010" => Fourcc::Vuy101010,
"Vuy888" => Fourcc::Vuy888,
"Vyuy" => Fourcc::Vyuy,
"X0l0" => Fourcc::X0l0,
"X0l2" => Fourcc::X0l2,
"Xbgr1555" => Fourcc::Xbgr1555,
"Xbgr16161616f" => Fourcc::Xbgr16161616f,
"Xbgr2101010" => Fourcc::Xbgr2101010,
"Xbgr4444" => Fourcc::Xbgr4444,
"Xbgr8888" => Fourcc::Xbgr8888,
"Xbgr8888_a8" => Fourcc::Xbgr8888_a8,
"Xrgb1555" => Fourcc::Xrgb1555,
"Xrgb16161616f" => Fourcc::Xrgb16161616f,
"Xrgb2101010" => Fourcc::Xrgb2101010,
"Xrgb4444" => Fourcc::Xrgb4444,
"Xrgb8888" => Fourcc::Xrgb8888,
"Xrgb8888_a8" => Fourcc::Xrgb8888_a8,
"Xvyu12_16161616" => Fourcc::Xvyu12_16161616,
"Xvyu16161616" => Fourcc::Xvyu16161616,
"Xvyu2101010" => Fourcc::Xvyu2101010,
"Xyuv8888" => Fourcc::Xyuv8888,
"Y0l0" => Fourcc::Y0l0,
"Y0l2" => Fourcc::Y0l2,
"Y210" => Fourcc::Y210,
"Y212" => Fourcc::Y212,
"Y216" => Fourcc::Y216,
"Y410" => Fourcc::Y410,
"Y412" => Fourcc::Y412,
"Y416" => Fourcc::Y416,
"Yuv410" => Fourcc::Yuv410,
"Yuv411" => Fourcc::Yuv411,
"Yuv420" => Fourcc::Yuv420,
"Yuv420_10bit" => Fourcc::Yuv420_10bit,
"Yuv420_8bit" => Fourcc::Yuv420_8bit,
"Yuv422" => Fourcc::Yuv422,
"Yuv444" => Fourcc::Yuv444,
"Yuyv" => Fourcc::Yuyv,
"Yvu410" => Fourcc::Yvu410,
"Yvu411" => Fourcc::Yvu411,
"Yvu420" => Fourcc::Yvu420,
"Yvu422" => Fourcc::Yvu422,
"Yvu444" => Fourcc::Yvu444,
"Yvyu" => Fourcc::Yvyu,
_ => {
return Err("Unknown pixel format");
}
})
}
fn modifier_from_string(modifier: &str) -> Result<Modifier, std::num::ParseIntError> {
Ok(match modifier {
"Invalid" => Modifier::Invalid,
"Linear" => Modifier::Linear,
x => Modifier::from(str::parse::<u64>(x)?),
})
}
fn usage_flags_from_string(flags: &str) -> Result<ImageUsageFlags, String> {
let flags = flags
.split('+')
.map(|f| {
Ok(match f.to_lowercase().trim() {
"all" => ImageUsageFlags::all(),
"empty" | "none" => ImageUsageFlags::empty(),
"color_attachment" => ImageUsageFlags::COLOR_ATTACHMENT,
"sampled" => ImageUsageFlags::SAMPLED,
"transfer_src" => ImageUsageFlags::TRANSFER_SRC,
"transfer_dst" => ImageUsageFlags::TRANSFER_DST,
x => {
return Err(format!("Unknown Usage Flag: {}", x));
}
})
})
.collect::<Result<Vec<ImageUsageFlags>, String>>()?;
Ok(flags
.into_iter()
.fold(ImageUsageFlags::empty(), |akk, val| akk | val))
}
+78
View File
@@ -0,0 +1,78 @@
use std::sync::Arc;
use smithay::delegate_dispatch2;
use smithay::reexports::wayland_server::Display;
use smithay::wayland::compositor::{CompositorClientState, CompositorHandler, CompositorState};
use wayland_server::backend::{ClientData, ClientId, DisconnectReason};
use wayland_server::protocol::wl_surface::WlSurface;
use wayland_server::{Client, ListeningSocket};
struct App {
compositor_state: CompositorState,
}
impl CompositorHandler for App {
fn compositor_state(&mut self) -> &mut CompositorState {
&mut self.compositor_state
}
fn client_compositor_state<'a>(&self, client: &'a Client) -> &'a CompositorClientState {
&client.get_data::<ClientState>().unwrap().compositor_state
}
fn commit(&mut self, surface: &WlSurface) {
dbg!("Commit", surface);
}
}
fn main() -> Result<(), Box<dyn std::error::Error>> {
let mut display: Display<App> = Display::new()?;
let dh = display.handle();
let compositor_state = CompositorState::new::<App>(&dh);
let mut state = App { compositor_state };
let listener = ListeningSocket::bind("wayland-5").unwrap();
let mut clients = Vec::new();
loop {
if let Some(stream) = listener.accept().unwrap() {
println!("Got a client: {:?}", stream);
let client = display
.handle()
.insert_client(stream, Arc::new(ClientState::default()))
.unwrap();
clients.push(client);
}
display.dispatch_clients(&mut state)?;
display.flush_clients()?;
}
}
#[derive(Default)]
struct ClientState {
compositor_state: CompositorClientState,
}
impl ClientData for ClientState {
fn initialized(&self, _client_id: ClientId) {
println!("initialized");
}
fn disconnected(&self, _client_id: ClientId, _reason: DisconnectReason) {
println!("disconnected");
}
}
impl AsMut<CompositorState> for App {
fn as_mut(&mut self) -> &mut CompositorState {
&mut self.compositor_state
}
}
delegate_dispatch2!(App);
+295
View File
@@ -0,0 +1,295 @@
use std::sync::Arc;
use ::winit::event_loop::pump_events::PumpStatus;
use smithay::{
backend::{
input::{InputEvent, InputTime, KeyboardKeyEvent},
renderer::{
Color32F, Frame, Renderer,
element::{
Kind,
surface::{WaylandSurfaceRenderElement, render_elements_from_surface_tree},
},
gles::GlesRenderer,
utils::{draw_render_elements, on_commit_buffer_handler},
},
winit::{self, WinitEvent},
},
input::{Seat, SeatHandler, SeatState, keyboard::FilterResult},
reexports::wayland_server::{Display, protocol::wl_seat},
utils::{Rectangle, Serial, Transform},
wayland::{
buffer::BufferHandler,
compositor::{
CompositorClientState, CompositorHandler, CompositorState, SurfaceAttributes, TraversalAction,
with_surface_tree_downward,
},
pointer_constraints::PointerConstraintsHandler,
selection::{
SelectionHandler,
data_device::{DataDeviceHandler, DataDeviceState, WaylandDndGrabHandler},
},
shell::xdg::{PopupSurface, PositionerState, ToplevelSurface, XdgShellHandler, XdgShellState},
shm::{ShmHandler, ShmState},
},
};
use wayland_protocols::xdg::shell::server::xdg_toplevel;
use wayland_server::{
Client, ListeningSocket,
backend::{ClientData, ClientId, DisconnectReason},
protocol::{
wl_buffer,
wl_surface::{self, WlSurface},
},
};
impl BufferHandler for App {
fn buffer_destroyed(&mut self, _buffer: &wl_buffer::WlBuffer) {}
}
impl XdgShellHandler for App {
fn xdg_shell_state(&mut self) -> &mut XdgShellState {
&mut self.xdg_shell_state
}
fn new_toplevel(&mut self, surface: ToplevelSurface) {
surface.with_pending_state(|state| {
state.states.set(xdg_toplevel::State::Activated);
});
surface.send_configure();
}
fn new_popup(&mut self, _surface: PopupSurface, _positioner: PositionerState) {
// Handle popup creation here
}
fn grab(&mut self, _surface: PopupSurface, _seat: wl_seat::WlSeat, _serial: Serial) {
// Handle popup grab here
}
fn reposition_request(&mut self, _surface: PopupSurface, _positioner: PositionerState, _token: u32) {
// Handle popup reposition here
}
}
impl SelectionHandler for App {
type SelectionUserData = ();
}
impl DataDeviceHandler for App {
fn data_device_state(&mut self) -> &mut DataDeviceState {
&mut self.data_device_state
}
}
impl WaylandDndGrabHandler for App {}
impl CompositorHandler for App {
fn compositor_state(&mut self) -> &mut CompositorState {
&mut self.compositor_state
}
fn client_compositor_state<'a>(&self, client: &'a Client) -> &'a CompositorClientState {
&client.get_data::<ClientState>().unwrap().compositor_state
}
fn commit(&mut self, surface: &WlSurface) {
on_commit_buffer_handler::<Self>(surface);
}
}
impl ShmHandler for App {
fn shm_state(&self) -> &ShmState {
&self.shm_state
}
}
impl SeatHandler for App {
type KeyboardFocus = WlSurface;
type PointerFocus = WlSurface;
type TouchFocus = WlSurface;
fn seat_state(&mut self) -> &mut SeatState<Self> {
&mut self.seat_state
}
fn focus_changed(&mut self, _seat: &Seat<Self>, _focused: Option<&WlSurface>) {}
fn cursor_image(&mut self, _seat: &Seat<Self>, _image: smithay::input::pointer::CursorImageStatus) {}
}
impl PointerConstraintsHandler for App {}
struct App {
compositor_state: CompositorState,
xdg_shell_state: XdgShellState,
shm_state: ShmState,
seat_state: SeatState<Self>,
data_device_state: DataDeviceState,
seat: Seat<Self>,
}
fn main() -> Result<(), Box<dyn std::error::Error>> {
if let Ok(env_filter) = tracing_subscriber::EnvFilter::try_from_default_env() {
tracing_subscriber::fmt().with_env_filter(env_filter).init();
} else {
tracing_subscriber::fmt().init();
}
run_winit()
}
pub fn run_winit() -> Result<(), Box<dyn std::error::Error>> {
let mut display: Display<App> = Display::new()?;
let dh = display.handle();
let compositor_state = CompositorState::new::<App>(&dh);
let shm_state = ShmState::new::<App>(&dh, vec![]);
let mut seat_state = SeatState::new();
let seat = seat_state.new_wl_seat(&dh, "winit");
let mut state = {
App {
compositor_state,
xdg_shell_state: XdgShellState::new::<App>(&dh),
shm_state,
seat_state,
data_device_state: DataDeviceState::new::<App>(&dh),
seat,
}
};
let listener = ListeningSocket::bind("wayland-5").unwrap();
let mut clients = Vec::new();
let (mut backend, mut winit) = winit::init::<GlesRenderer>()?;
let start_time = std::time::Instant::now();
let keyboard = state.seat.add_keyboard(Default::default(), 200, 200).unwrap();
unsafe { std::env::set_var("WAYLAND_DISPLAY", "wayland-5") };
std::process::Command::new("weston-terminal").spawn().ok();
loop {
let status = winit.dispatch_new_events(|event| match event {
WinitEvent::Resized { .. } => {}
WinitEvent::Input(event) => match event {
InputEvent::Keyboard { event } => {
keyboard.input::<(), _>(
&mut state,
event.key_code(),
event.state(),
0.into(),
InputTime::now(),
|_, _, _| {
//
FilterResult::Forward
},
);
}
InputEvent::PointerMotionAbsolute { .. } => {
if let Some(surface) = state.xdg_shell_state.toplevel_surfaces().iter().next().cloned() {
let surface = surface.wl_surface().clone();
keyboard.set_focus(&mut state, Some(surface), 0.into());
};
}
_ => {}
},
_ => (),
});
match status {
PumpStatus::Continue => (),
PumpStatus::Exit(_) => return Ok(()),
};
let size = backend.window_size();
let damage = Rectangle::from_size(size);
{
let (renderer, mut framebuffer) = backend.bind().unwrap();
let elements = state
.xdg_shell_state
.toplevel_surfaces()
.iter()
.flat_map(|surface| {
render_elements_from_surface_tree(
renderer,
surface.wl_surface(),
(0, 0),
1.0,
1.0,
Kind::Unspecified,
)
})
.collect::<Vec<WaylandSurfaceRenderElement<GlesRenderer>>>();
let mut frame = renderer
.render(&mut framebuffer, size, Transform::Flipped180)
.unwrap();
frame.clear(Color32F::new(0.1, 0.0, 0.0, 1.0), &[damage]).unwrap();
draw_render_elements(&mut frame, 1.0, &elements, &[damage]).unwrap();
// We rely on the nested compositor to do the sync for us
let _ = frame.finish().unwrap();
for surface in state.xdg_shell_state.toplevel_surfaces() {
send_frames_surface_tree(surface.wl_surface(), start_time.elapsed().as_millis() as u32);
}
if let Some(stream) = listener.accept()? {
println!("Got a client: {:?}", stream);
let client = display
.handle()
.insert_client(stream, Arc::new(ClientState::default()))
.unwrap();
clients.push(client);
}
display.dispatch_clients(&mut state)?;
display.flush_clients()?;
}
// It is important that all events on the display have been dispatched and flushed to clients before
// swapping buffers because this operation may block.
backend.submit(Some(&[damage])).unwrap();
}
}
pub fn send_frames_surface_tree(surface: &wl_surface::WlSurface, time: u32) {
with_surface_tree_downward(
surface,
(),
|_, _, &()| TraversalAction::DoChildren(()),
|_surf, states, &()| {
// the surface may not have any user_data if it is a subsurface and has not
// yet been committed
for callback in states
.cached_state
.get::<SurfaceAttributes>()
.current()
.frame_callbacks
.drain(..)
{
callback.done(time);
}
},
|_, _, &()| true,
);
}
#[derive(Default)]
struct ClientState {
compositor_state: CompositorClientState,
}
impl ClientData for ClientState {
fn initialized(&self, _client_id: ClientId) {
println!("initialized");
}
fn disconnected(&self, _client_id: ClientId, _reason: DisconnectReason) {
println!("disconnected");
}
}
smithay::delegate_dispatch2!(App);
+110
View File
@@ -0,0 +1,110 @@
use std::sync::Arc;
use smithay::backend::input::InputTime;
use smithay::input::{Seat, SeatHandler, SeatState, keyboard::FilterResult};
use smithay::reexports::wayland_server::{
Display, ListeningSocket,
backend::{ClientData, ClientId, DisconnectReason},
protocol::wl_surface::WlSurface,
};
use smithay::wayland::compositor::{CompositorClientState, CompositorHandler, CompositorState};
use smithay::wayland::pointer_constraints::PointerConstraintsHandler;
struct App {
compositor_state: CompositorState,
seat_state: SeatState<Self>,
seat: Seat<Self>,
}
impl SeatHandler for App {
type KeyboardFocus = WlSurface;
type PointerFocus = WlSurface;
type TouchFocus = WlSurface;
fn seat_state(&mut self) -> &mut SeatState<Self> {
&mut self.seat_state
}
fn focus_changed(&mut self, _seat: &Seat<Self>, _focused: Option<&WlSurface>) {}
fn cursor_image(&mut self, _seat: &Seat<Self>, _image: smithay::input::pointer::CursorImageStatus) {}
}
impl PointerConstraintsHandler for App {}
fn main() -> Result<(), Box<dyn std::error::Error>> {
let mut display: Display<App> = Display::new()?;
let dh = display.handle();
let compositor_state = CompositorState::new::<App>(&dh);
let mut seat_state = SeatState::new();
let seat = seat_state.new_wl_seat(&dh, "Example");
let mut state = App {
compositor_state,
seat_state,
seat,
};
let keyboard = state.seat.add_keyboard(Default::default(), 25, 600)?;
let listener = ListeningSocket::bind("wayland-5").unwrap();
let mut clients = Vec::new();
loop {
if let Some(stream) = listener.accept().unwrap() {
println!("Got a client: {:?}", stream);
let client = display
.handle()
.insert_client(stream, Arc::new(ClientState(CompositorClientState::default())))
.unwrap();
clients.push(client);
}
keyboard.input(
&mut state,
smithay::backend::input::Keycode::from(9u32),
smithay::backend::input::KeyState::Pressed,
0.into(),
InputTime::now(),
|_, _, _| {
if false {
FilterResult::Intercept(0)
} else {
FilterResult::Forward
}
},
);
keyboard.set_focus(&mut state, Option::<WlSurface>::None, 0.into());
display.dispatch_clients(&mut state)?;
display.flush_clients()?;
}
}
struct ClientState(CompositorClientState);
impl ClientData for ClientState {
fn initialized(&self, _client_id: ClientId) {
println!("initialized");
}
fn disconnected(&self, _client_id: ClientId, _reason: DisconnectReason) {
println!("disconnected");
}
}
impl CompositorHandler for App {
fn compositor_state(&mut self) -> &mut CompositorState {
&mut self.compositor_state
}
fn client_compositor_state<'a>(&self, client: &'a wayland_server::Client) -> &'a CompositorClientState {
&client.get_data::<ClientState>().unwrap().0
}
fn commit(&mut self, _surface: &WlSurface) {}
}
smithay::delegate_dispatch2!(App);
+61
View File
@@ -0,0 +1,61 @@
use drm_fourcc::{DrmFourcc, DrmModifier};
use smithay::backend::{
allocator::{
Allocator, Buffer,
dmabuf::AsDmabuf,
vulkan::{ImageUsageFlags, VulkanAllocator},
},
vulkan::{Instance, PhysicalDevice, version::Version},
};
fn main() {
if let Ok(env_filter) = tracing_subscriber::EnvFilter::try_from_default_env() {
tracing_subscriber::fmt().with_env_filter(env_filter).init();
} else {
tracing_subscriber::fmt().init();
}
println!(
"Available instance extensions: {:?}",
Instance::enumerate_extensions().unwrap().collect::<Vec<_>>()
);
println!();
let instance = Instance::new(Version::VERSION_1_3, None).unwrap();
for (idx, phy) in PhysicalDevice::enumerate(&instance).unwrap().enumerate() {
println!(
"Device #{}: {} v{}, {:?}",
idx,
phy.name(),
phy.api_version(),
phy.driver()
);
}
let physical_device = PhysicalDevice::enumerate(&instance)
.unwrap()
.next()
.expect("No physical devices");
// The allocator should create buffers that are suitable as render targets.
let mut allocator = VulkanAllocator::new(&physical_device, ImageUsageFlags::COLOR_ATTACHMENT).unwrap();
let image = allocator
.create_buffer(100, 200, DrmFourcc::Argb8888, &[DrmModifier::Linear])
.expect("create");
assert_eq!(image.width(), 100);
assert_eq!(image.height(), 200);
let image_dmabuf = image.export().expect("Export dmabuf");
drop(image);
let _image2 = allocator
.create_buffer(200, 200, DrmFourcc::Argb8888, &[DrmModifier::Linear])
.expect("create");
drop(allocator);
drop(image_dmabuf);
}