1
0
Fork 0
forked from wry/wry

autocommit 2022-04-05 18:28:42 CEST

This commit is contained in:
Julian Orth 2022-04-05 18:28:42 +02:00
parent 1f05ea431e
commit a3e9f21fc5
29 changed files with 568 additions and 225 deletions

View file

@ -37,8 +37,7 @@ clap = { version = "3.1.6", features = ["derive", "wrap_help"] }
clap_complete = "3.1.1" clap_complete = "3.1.1"
humantime = "2.1.0" humantime = "2.1.0"
dirs = "4.0.0" dirs = "4.0.0"
backtrace = "0.3.64"
backtrace = { version = "0.3.64", optional = true }
[build-dependencies] [build-dependencies]
repc = "0.1.1" repc = "0.1.1"
@ -49,4 +48,4 @@ bstr = { version = "0.2.17", default-features = false, features = ["std"] }
#opt-level = 3 #opt-level = 3
[features] [features]
rc_tracking = ["backtrace"] rc_tracking = []

View file

@ -9,12 +9,19 @@ Do not expect any kind of structured commit history.
## Dependencies ## Dependencies
While Jay is written almost completely in rust, it has some native dependencies: While Jay is written almost completely in rust, it depends on the following libraries:
* **pixman-1.so**: For damage tracking. * **libinput.so**: For processing input events.
* **input.so**: For processing input events. * **libEGL.so**, **libGLESv2.so**: For OpenGL rendering.
* **EGL.so**, **GLESv2.so**: For OpenGL rendering. * **libgbm.so**: For graphics buffer allocation.
* **gbm.so**: For graphics buffer allocation. * **libxkbcommon.so**: For keymap handling.
* **xkbcommon.so**: For keymap handling. * **libudev.so**: For device enumeration and hotplug support.
* **udev.so**: For device enumeration and hotplug support. * **libcairo.so**, **libpangocairo-1.0.so**, **libgobject-2.0.so**, **libpango-1.0.so**: For text rendering.
* **cairo.so**, **pangocairo-1.0.so**, **gobject-2.0.so**, **pango-1.0.so**: For text rendering.
Furthermore, Jay depends on the following runtime services:
* **An up-to-date linux kernel**
* **XWayland**: For XWayland support.
* **Pipewire**: For screen-recording.
* **A running X server**: For the X backend. (Only required if you want to run Jay as an X client.)
* **Logind**: For the metal backend. (Only required if you want to run Jay from a TTY.)

View file

@ -2,6 +2,7 @@
use crate::_private::ipc::{ClientMessage, InitMessage, Response, ServerMessage}; use crate::_private::ipc::{ClientMessage, InitMessage, Response, ServerMessage};
use crate::_private::{bincode_ops, logging, Config, ConfigEntry, ConfigEntryGen, VERSION}; use crate::_private::{bincode_ops, logging, Config, ConfigEntry, ConfigEntryGen, VERSION};
use crate::drm::Connector;
use crate::input::acceleration::AccelProfile; use crate::input::acceleration::AccelProfile;
use crate::input::capability::Capability; use crate::input::capability::Capability;
use crate::input::InputDevice; use crate::input::InputDevice;
@ -24,6 +25,8 @@ pub(crate) struct Client {
response: RefCell<Vec<Response>>, response: RefCell<Vec<Response>>,
on_new_seat: RefCell<Option<Rc<dyn Fn(Seat)>>>, on_new_seat: RefCell<Option<Rc<dyn Fn(Seat)>>>,
on_new_input_device: RefCell<Option<Rc<dyn Fn(InputDevice)>>>, on_new_input_device: RefCell<Option<Rc<dyn Fn(InputDevice)>>>,
on_connector_connected: RefCell<Option<Rc<dyn Fn(Connector)>>>,
on_new_connector: RefCell<Option<Rc<dyn Fn(Connector)>>>,
bufs: RefCell<Vec<Vec<u8>>>, bufs: RefCell<Vec<Vec<u8>>>,
} }
@ -103,6 +106,8 @@ pub unsafe extern "C" fn init(
response: Default::default(), response: Default::default(),
on_new_seat: Default::default(), on_new_seat: Default::default(),
on_new_input_device: Default::default(), on_new_input_device: Default::default(),
on_connector_connected: Default::default(),
on_new_connector: Default::default(),
bufs: Default::default(), bufs: Default::default(),
}); });
let init = slice::from_raw_parts(init, size); let init = slice::from_raw_parts(init, size);
@ -471,6 +476,20 @@ impl Client {
} }
} }
ServerMessage::DelInputDevice { .. } => {} ServerMessage::DelInputDevice { .. } => {}
ServerMessage::ConnectorConnect { device } => {
let handler = self.on_connector_connected.borrow_mut().clone();
if let Some(handler) = handler {
handler(device);
}
}
ServerMessage::ConnectorDisconnect { .. } => {}
ServerMessage::NewConnector { device } => {
let handler = self.on_new_connector.borrow_mut().clone();
if let Some(handler) = handler {
handler(device);
}
}
ServerMessage::DelConnector { .. } => {}
} }
} }

View file

@ -1,3 +1,4 @@
use crate::drm::Connector;
use crate::input::acceleration::AccelProfile; use crate::input::acceleration::AccelProfile;
use crate::input::capability::Capability; use crate::input::capability::Capability;
use crate::input::InputDevice; use crate::input::InputDevice;
@ -14,6 +15,18 @@ pub enum ServerMessage {
Response { Response {
response: Response, response: Response,
}, },
ConnectorConnect {
device: Connector,
},
ConnectorDisconnect {
device: Connector,
},
NewConnector {
device: Connector,
},
DelConnector {
device: Connector,
},
NewInputDevice { NewInputDevice {
device: InputDevice, device: InputDevice,
}, },

4
jay-config/src/drm.rs Normal file
View file

@ -0,0 +1,4 @@
use bincode::{Decode, Encode};
#[derive(Encode, Decode, Copy, Clone, Debug, Hash, Eq, PartialEq)]
pub struct Connector(pub u64);

View file

@ -8,6 +8,7 @@ use std::collections::HashMap;
mod macros; mod macros;
#[doc(hidden)] #[doc(hidden)]
pub mod _private; pub mod _private;
pub mod drm;
pub mod embedded; pub mod embedded;
pub mod input; pub mod input;
pub mod keyboard; pub mod keyboard;

View file

@ -1,8 +1,9 @@
use crate::drm::drm::ConnectorType;
use crate::fixed::Fixed; use crate::fixed::Fixed;
use std::fmt::Debug; use std::fmt::{Debug, Display, Formatter};
use std::rc::Rc; use std::rc::Rc;
linear_ids!(OutputIds, OutputId); linear_ids!(ConnectorIds, ConnectorId);
linear_ids!(InputDeviceIds, InputDeviceId); linear_ids!(InputDeviceIds, InputDeviceId);
pub trait Backend { pub trait Backend {
@ -13,17 +14,42 @@ pub trait Backend {
pub struct Mode { pub struct Mode {
pub width: i32, pub width: i32,
pub height: i32, pub height: i32,
pub refresh_rate: u32, pub refresh_rate_millihz: u32,
}
#[derive(Clone, Debug)]
pub struct MonitorInfo {
pub modes: Vec<Mode>,
pub manufacturer: String,
pub product: String,
pub serial_number: String,
pub initial_mode: Mode,
pub width_mm: i32,
pub height_mm: i32,
}
pub struct ConnectorKernelId {
pub ty: ConnectorType,
pub id: u32,
}
impl Display for ConnectorKernelId {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
write!(f, "{}-{}", self.ty, self.id)
}
} }
pub trait Connector { pub trait Connector {
fn id(&self) -> OutputId; fn id(&self) -> ConnectorId;
fn kernel_id(&self) -> ConnectorKernelId;
fn event(&self) -> Option<ConnectorEvent>; fn event(&self) -> Option<ConnectorEvent>;
fn on_change(&self, cb: Rc<dyn Fn()>); fn on_change(&self, cb: Rc<dyn Fn()>);
} }
#[derive(Debug)] #[derive(Debug)]
pub enum ConnectorEvent { pub enum ConnectorEvent {
Connected(MonitorInfo),
Disconnected,
Removed, Removed,
ModeChanged(Mode), ModeChanged(Mode),
} }
@ -79,7 +105,7 @@ pub enum ScrollAxis {
#[derive(Debug)] #[derive(Debug)]
pub enum InputEvent { pub enum InputEvent {
Key(u32, KeyState), Key(u32, KeyState),
OutputPosition(OutputId, Fixed, Fixed), ConnectorPosition(ConnectorId, Fixed, Fixed),
#[allow(dead_code)] #[allow(dead_code)]
Motion(Fixed, Fixed), Motion(Fixed, Fixed),
Button(u32, KeyState), Button(u32, KeyState),

View file

@ -1,4 +1,5 @@
use crate::backend::{Backend, Connector, ConnectorEvent, OutputId}; use crate::backend::{Backend, Connector, ConnectorEvent, ConnectorId, ConnectorKernelId};
use crate::drm::drm::ConnectorType;
use std::rc::Rc; use std::rc::Rc;
pub struct DummyBackend {} pub struct DummyBackend {}
@ -10,14 +11,21 @@ impl Backend for DummyBackend {
} }
pub struct DummyOutput { pub struct DummyOutput {
pub id: OutputId, pub id: ConnectorId,
} }
impl Connector for DummyOutput { impl Connector for DummyOutput {
fn id(&self) -> OutputId { fn id(&self) -> ConnectorId {
self.id self.id
} }
fn kernel_id(&self) -> ConnectorKernelId {
ConnectorKernelId {
ty: ConnectorType::Unknown(0),
id: 0,
}
}
fn event(&self) -> Option<ConnectorEvent> { fn event(&self) -> Option<ConnectorEvent> {
None None
} }

View file

@ -1,5 +1,5 @@
use crate::async_engine::{AsyncFd, SpawnedFuture}; use crate::async_engine::{AsyncFd, SpawnedFuture};
use crate::backend::{BackendEvent, Connector, ConnectorEvent, Mode, OutputId}; use crate::backend::{BackendEvent, Connector, ConnectorEvent, ConnectorId, ConnectorKernelId, MonitorInfo};
use crate::backends::metal::{DrmId, MetalBackend, MetalError}; use crate::backends::metal::{DrmId, MetalBackend, MetalError};
use crate::drm::drm::{ use crate::drm::drm::{
drm_mode_modeinfo, Change, ConnectorStatus, ConnectorType, DrmBlob, DrmConnector, DrmCrtc, drm_mode_modeinfo, Change, ConnectorStatus, ConnectorType, DrmBlob, DrmConnector, DrmCrtc,
@ -25,6 +25,7 @@ use std::ffi::CString;
use std::fmt::{Debug, Formatter}; use std::fmt::{Debug, Formatter};
use std::rc::Rc; use std::rc::Rc;
use uapi::c; use uapi::c;
use crate::edid::Descriptor;
pub struct PendingDrmDevice { pub struct PendingDrmDevice {
pub id: DrmId, pub id: DrmId,
@ -72,12 +73,16 @@ pub struct MetalConnector {
pub id: DrmConnector, pub id: DrmConnector,
pub master: Rc<DrmMaster>, pub master: Rc<DrmMaster>,
pub output_id: OutputId, pub connector_id: ConnectorId,
pub crtcs: AHashMap<DrmCrtc, Rc<MetalCrtc>>, pub crtcs: AHashMap<DrmCrtc, Rc<MetalCrtc>>,
pub modes: Vec<DrmModeInfo>, pub modes: Vec<DrmModeInfo>,
pub mode: CloneCell<Option<Rc<DrmModeInfo>>>, pub mode: CloneCell<Option<Rc<DrmModeInfo>>>,
pub monitor_manufacturer: String,
pub monitor_name: String,
pub monitor_serial_number: String,
pub events: SyncQueue<ConnectorEvent>, pub events: SyncQueue<ConnectorEvent>,
pub buffers: CloneCell<Option<Rc<[RenderBuffer; 2]>>>, pub buffers: CloneCell<Option<Rc<[RenderBuffer; 2]>>>,
@ -114,8 +119,15 @@ impl Debug for OnChange {
} }
impl Connector for MetalConnector { impl Connector for MetalConnector {
fn id(&self) -> OutputId { fn id(&self) -> ConnectorId {
self.output_id self.connector_id
}
fn kernel_id(&self) -> ConnectorKernelId {
ConnectorKernelId {
ty: self.connector_type,
id: self.connector_type_id,
}
} }
fn event(&self) -> Option<ConnectorEvent> { fn event(&self) -> Option<ConnectorEvent> {
@ -212,28 +224,73 @@ fn create_connector(
} }
} }
let props = collect_properties(&dev.master, connector)?; let props = collect_properties(&dev.master, connector)?;
let mode = info.modes.first().cloned().map(Rc::new); let connection = ConnectorStatus::from(info.connection);
let events = SyncQueue::default(); let connector_type = ConnectorType::from(info.connector_type);
if let Some(mode) = &mode { let mut name = String::new();
events.push(ConnectorEvent::ModeChanged(Mode { let mut manufacturer = String::new();
width: mode.hdisplay as _, let mut serial_number = String::new();
height: mode.vdisplay as _, 'fetch_edid: {
refresh_rate: mode.refresh_rate(), if connection != ConnectorStatus::Connected {
})); break 'fetch_edid;
}
let edid = match props.get("EDID") {
Ok(e) => e,
_ => {
log::warn!("Connector {}-{} is connected but has no EDID blob", connector_type, info.connector_type_id);
break 'fetch_edid;
}
};
let blob = match dev.master.getblob_vec::<u8>(DrmBlob(edid.value.get() as _)) {
Ok(b) => b,
Err(e) => {
log::error!("Could not fetch edid property of connector {}-{}: {}", connector_type, info.connector_type_id, ErrorFmt(e));
break 'fetch_edid;
}
};
let edid = match crate::edid::parse(&blob) {
Ok(e) => e,
Err(e) => {
log::error!("Could not parse edid property of connector {}-{}: {}", connector_type, info.connector_type_id, ErrorFmt(e));
break 'fetch_edid;
}
};
manufacturer = edid.base_block.id_manufacturer_name.to_string();
for descriptor in &edid.base_block.descriptors {
if let Some(d) = descriptor {
match d {
Descriptor::DisplayProductSerialNumber(s) => {
serial_number = s.to_string();
}
Descriptor::DisplayProductName(s) => {
name = s.to_string();
}
_ => { },
}
}
}
if name.is_empty() {
log::warn!("The display attached to connector {}-{} does not have a product name descriptor", connector_type, info.connector_type_id);
}
if serial_number.is_empty() {
serial_number = edid.base_block.id_serial_number.to_string();
}
} }
Ok(MetalConnector { Ok(MetalConnector {
id: connector, id: connector,
master: dev.master.clone(), master: dev.master.clone(),
output_id: state.output_ids.next(), connector_id: state.connector_ids.next(),
crtcs, crtcs,
mode: CloneCell::new(mode), mode: CloneCell::new(info.modes.first().cloned().map(Rc::new)),
events, monitor_manufacturer: manufacturer,
monitor_name: name,
monitor_serial_number: serial_number,
events: Default::default(),
modes: info.modes, modes: info.modes,
buffers: Default::default(), buffers: Default::default(),
next_buffer: Default::default(), next_buffer: Default::default(),
connector_type: info.connector_type.into(), connector_type,
connector_type_id: info.connector_type_id, connector_type_id: info.connector_type_id,
connection: info.connection.into(), connection,
mm_width: info.mm_width, mm_width: info.mm_width,
mm_height: info.mm_height, mm_height: info.mm_height,
subpixel: info.subpixel, subpixel: info.subpixel,
@ -488,7 +545,27 @@ impl MetalBackend {
self.state self.state
.backend_events .backend_events
.push(BackendEvent::NewConnector(connector.clone())); .push(BackendEvent::NewConnector(connector.clone()));
if connector.primary_plane.get().is_some() { if connector.connection == ConnectorStatus::Connected {
if connector.primary_plane.get().is_none() {
log::error!("Connector {}-{} is connected but does not have a primary plane", connector.connector_type, connector.connector_type_id);
continue;
}
let mut prev_mode = None;
let mut modes = vec!();
for mode in connector.modes.iter().map(|m| m.to_backend()) {
if prev_mode.replace(mode) != Some(mode) {
modes.push(mode);
}
}
connector.events.push(ConnectorEvent::Connected(MonitorInfo {
modes,
manufacturer: connector.monitor_manufacturer.clone(),
product: connector.monitor_name.clone(),
serial_number: connector.monitor_serial_number.clone(),
initial_mode: connector.mode.get().unwrap().to_backend(),
width_mm: connector.mm_width as _,
height_mm: connector.mm_height as _,
}));
self.start_connector(connector); self.start_connector(connector);
} }
} }
@ -901,7 +978,7 @@ impl MetalBackend {
_ => return, _ => return,
}; };
let buffer = &buffers[connector.next_buffer.fetch_add(1) % buffers.len()]; let buffer = &buffers[connector.next_buffer.fetch_add(1) % buffers.len()];
if let Some(node) = self.state.root.outputs.get(&connector.output_id) { if let Some(node) = self.state.root.outputs.get(&connector.connector_id) {
buffer buffer
.egl .egl
.render(&*node, &self.state, Some(node.global.pos.get())); .render(&*node, &self.state, Some(node.global.pos.get()));

View file

@ -1,9 +1,6 @@
use crate::async_engine::{Phase, SpawnedFuture}; use crate::async_engine::{Phase, SpawnedFuture};
use crate::backend::{ use crate::backend::{Backend, BackendEvent, Connector, ConnectorEvent, ConnectorId, ConnectorKernelId, InputDevice, InputDeviceAccelProfile, InputDeviceCapability, InputDeviceId, InputEvent, KeyState, Mode, MonitorInfo, ScrollAxis};
Backend, BackendEvent, Connector, ConnectorEvent, InputDevice, InputDeviceAccelProfile, use crate::drm::drm::{ConnectorType, Drm, DrmError};
InputDeviceCapability, InputDeviceId, InputEvent, KeyState, Mode, OutputId, ScrollAxis,
};
use crate::drm::drm::{Drm, DrmError};
use crate::drm::gbm::{GbmDevice, GbmError, GBM_BO_USE_RENDERING}; use crate::drm::gbm::{GbmDevice, GbmError, GBM_BO_USE_RENDERING};
use crate::drm::{ModifiedFormat, INVALID_MODIFIER}; use crate::drm::{ModifiedFormat, INVALID_MODIFIER};
use crate::fixed::Fixed; use crate::fixed::Fixed;
@ -394,7 +391,7 @@ impl XBackendData {
}; };
let images = self.create_images(window_id, WIDTH, HEIGHT).await?; let images = self.create_images(window_id, WIDTH, HEIGHT).await?;
let output = Rc::new(XOutput { let output = Rc::new(XOutput {
id: self.state.output_ids.next(), id: self.state.connector_ids.next(),
_backend: self.clone(), _backend: self.clone(),
window: window_id, window: window_id,
events: Default::default(), events: Default::default(),
@ -481,6 +478,20 @@ impl XBackendData {
self.state self.state
.backend_events .backend_events
.push(BackendEvent::NewConnector(output.clone())); .push(BackendEvent::NewConnector(output.clone()));
output.events.push(ConnectorEvent::Connected(MonitorInfo {
modes: vec![],
manufacturer: "X.Org Foundation".to_string(),
product: format!("X-Window-{}", window_id),
serial_number: window_id.to_string(),
initial_mode: Mode {
width: WIDTH,
height: HEIGHT,
refresh_rate_millihz: 60_000, // TODO
},
width_mm: WIDTH,
height_mm: HEIGHT,
}));
output.changed();
self.present(&output).await; self.present(&output).await;
Ok(()) Ok(())
} }
@ -759,7 +770,7 @@ impl XBackendData {
self.outputs.get(&event.event), self.outputs.get(&event.event),
self.mouse_seats.get(&event.deviceid), self.mouse_seats.get(&event.deviceid),
) { ) {
seat.mouse_event(InputEvent::OutputPosition( seat.mouse_event(InputEvent::ConnectorPosition(
win.id, win.id,
Fixed::from_1616(event.event_x), Fixed::from_1616(event.event_x),
Fixed::from_1616(event.event_y), Fixed::from_1616(event.event_y),
@ -777,7 +788,7 @@ impl XBackendData {
(Some(a), Some(b)) => (a, b), (Some(a), Some(b)) => (a, b),
_ => return Ok(()), _ => return Ok(()),
}; };
seat.mouse_event(InputEvent::OutputPosition( seat.mouse_event(InputEvent::ConnectorPosition(
win.id, win.id,
Fixed::from_1616(event.event_x), Fixed::from_1616(event.event_x),
Fixed::from_1616(event.event_y), Fixed::from_1616(event.event_y),
@ -792,6 +803,7 @@ impl XBackendData {
Some(o) => o, Some(o) => o,
_ => return Ok(()), _ => return Ok(()),
}; };
output.events.push(ConnectorEvent::Disconnected);
output.events.push(ConnectorEvent::Removed); output.events.push(ConnectorEvent::Removed);
output.changed(); output.changed();
Ok(()) Ok(())
@ -820,7 +832,7 @@ impl XBackendData {
output.events.push(ConnectorEvent::ModeChanged(Mode { output.events.push(ConnectorEvent::ModeChanged(Mode {
width, width,
height, height,
refresh_rate: 60, // TODO refresh_rate_millihz: 60, // TODO
})); }));
output.changed(); output.changed();
} }
@ -829,7 +841,7 @@ impl XBackendData {
} }
struct XOutput { struct XOutput {
id: OutputId, id: ConnectorId,
_backend: Rc<XBackendData>, _backend: Rc<XBackendData>,
window: u32, window: u32,
events: SyncQueue<ConnectorEvent>, events: SyncQueue<ConnectorEvent>,
@ -859,10 +871,17 @@ impl XOutput {
} }
impl Connector for XOutput { impl Connector for XOutput {
fn id(&self) -> OutputId { fn id(&self) -> ConnectorId {
self.id self.id
} }
fn kernel_id(&self) -> ConnectorKernelId {
ConnectorKernelId {
ty: ConnectorType::EmbeddedWindow,
id: self.id.raw(),
}
}
fn event(&self) -> Option<ConnectorEvent> { fn event(&self) -> Option<ConnectorEvent> {
self.events.pop() self.events.pop()
} }

View file

@ -7,14 +7,13 @@ use crate::clientmem::ClientMemError;
use crate::config::ConfigProxy; use crate::config::ConfigProxy;
use crate::dbus::Dbus; use crate::dbus::Dbus;
use crate::event_loop::{EventLoop, EventLoopError}; use crate::event_loop::{EventLoop, EventLoopError};
use crate::forker::ForkerError;
use crate::globals::Globals; use crate::globals::Globals;
use crate::ifs::wl_output::WlOutputGlobal; use crate::ifs::wl_output::WlOutputGlobal;
use crate::ifs::wl_surface::NoneSurfaceExt; use crate::ifs::wl_surface::NoneSurfaceExt;
use crate::logger::Logger; use crate::logger::Logger;
use crate::render::RenderError; use crate::render::RenderError;
use crate::sighand::SighandError; use crate::sighand::SighandError;
use crate::state::State; use crate::state::{ConnectorData, State};
use crate::tree::{ use crate::tree::{
container_layout, container_render_data, float_layout, float_titles, DisplayNode, NodeIds, container_layout, container_render_data, float_layout, float_titles, DisplayNode, NodeIds,
OutputNode, WorkspaceNode, OutputNode, WorkspaceNode,
@ -22,12 +21,11 @@ use crate::tree::{
use crate::utils::clonecell::CloneCell; use crate::utils::clonecell::CloneCell;
use crate::utils::errorfmt::ErrorFmt; use crate::utils::errorfmt::ErrorFmt;
use crate::utils::fdcloser::FdCloser; use crate::utils::fdcloser::FdCloser;
use crate::utils::numcell::NumCell;
use crate::utils::queue::AsyncQueue; use crate::utils::queue::AsyncQueue;
use crate::utils::run_toplevel::RunToplevel; use crate::utils::run_toplevel::RunToplevel;
use crate::wheel::{Wheel, WheelError}; use crate::wheel::{Wheel, WheelError};
use crate::xkbcommon::XkbContext; use crate::xkbcommon::XkbContext;
use crate::{clientmem, forker, leaks, render, sighand, tasks, xwayland}; use crate::{backend, clientmem, forker, leaks, render, sighand, tasks, xwayland};
use forker::ForkerProxy; use forker::ForkerProxy;
use std::cell::Cell; use std::cell::Cell;
use std::ops::Deref; use std::ops::Deref;
@ -36,8 +34,12 @@ use std::sync::Arc;
use thiserror::Error; use thiserror::Error;
pub fn start_compositor(global: GlobalArgs, args: RunArgs) { pub fn start_compositor(global: GlobalArgs, args: RunArgs) {
let forker = match ForkerProxy::create() {
Ok(f) => Rc::new(f),
Err(e) => fatal!("Could not create a forker process: {}", ErrorFmt(e)),
};
let logger = Logger::install_compositor(global.log_level.into()); let logger = Logger::install_compositor(global.log_level.into());
if let Err(e) = main_(logger.clone(), &args) { if let Err(e) = main_(forker, logger.clone(), &args) {
let e = ErrorFmt(e); let e = ErrorFmt(e);
log::error!("A fatal error occurred: {}", e); log::error!("A fatal error occurred: {}", e);
eprintln!("A fatal error occurred: {}", e); eprintln!("A fatal error occurred: {}", e);
@ -62,12 +64,9 @@ enum MainError {
AsyncError(#[from] AsyncError), AsyncError(#[from] AsyncError),
#[error("The render backend caused an error")] #[error("The render backend caused an error")]
RenderError(#[from] RenderError), RenderError(#[from] RenderError),
#[error("The ol' forker caused an error")]
ForkerError(#[from] ForkerError),
} }
fn main_(logger: Arc<Logger>, _args: &RunArgs) -> Result<(), MainError> { fn main_(forker: Rc<ForkerProxy>, logger: Arc<Logger>, _args: &RunArgs) -> Result<(), MainError> {
let forker = Rc::new(ForkerProxy::create()?);
leaks::init(); leaks::init();
render::init()?; render::init()?;
clientmem::init()?; clientmem::init()?;
@ -90,17 +89,14 @@ fn main_(logger: Arc<Logger>, _args: &RunArgs) -> Result<(), MainError> {
cursors: Default::default(), cursors: Default::default(),
wheel, wheel,
clients: Clients::new(), clients: Clients::new(),
next_name: NumCell::new(1),
globals: Globals::new(), globals: Globals::new(),
output_ids: Default::default(), connector_ids: Default::default(),
root: Rc::new(DisplayNode::new(node_ids.next())), root: Rc::new(DisplayNode::new(node_ids.next())),
workspaces: Default::default(), workspaces: Default::default(),
dummy_output: Default::default(), dummy_output: Default::default(),
node_ids, node_ids,
backend_events: AsyncQueue::new(), backend_events: AsyncQueue::new(),
output_handlers: Default::default(),
seat_ids: Default::default(), seat_ids: Default::default(),
outputs: Default::default(),
seat_queue: Default::default(), seat_queue: Default::default(),
slow_clients: AsyncQueue::new(), slow_clients: AsyncQueue::new(),
none_surface_ext: Rc::new(NoneSurfaceExt), none_surface_ext: Rc::new(NoneSurfaceExt),
@ -116,16 +112,29 @@ fn main_(logger: Arc<Logger>, _args: &RunArgs) -> Result<(), MainError> {
dbus: Dbus::new(&engine, &run_toplevel), dbus: Dbus::new(&engine, &run_toplevel),
fdcloser: FdCloser::new(), fdcloser: FdCloser::new(),
logger, logger,
connectors: Default::default(),
}); });
{ {
let dummy_output = Rc::new(OutputNode { let dummy_output = Rc::new(OutputNode {
id: state.node_ids.next(), id: state.node_ids.next(),
global: Rc::new(WlOutputGlobal::new( global: Rc::new(WlOutputGlobal::new(
state.globals.name(), state.globals.name(),
Rc::new(DummyOutput { &Rc::new(ConnectorData {
id: state.output_ids.next(), connector: Rc::new(DummyOutput { id: state.connector_ids.next() }),
monitor_info: Default::default(),
handler: Cell::new(None),
node: Default::default()
}), }),
0, 0,
&backend::Mode {
width: 0,
height: 0,
refresh_rate_millihz: 0,
},
"none",
"none",
0,
0,
)), )),
workspaces: Default::default(), workspaces: Default::default(),
workspace: Default::default(), workspace: Default::default(),

View file

@ -1,6 +1,6 @@
mod handler; mod handler;
use crate::backend::InputDeviceId; use crate::backend::{ConnectorId, InputDeviceId};
use crate::config::handler::ConfigProxyHandler; use crate::config::handler::ConfigProxyHandler;
use crate::ifs::wl_seat::SeatId; use crate::ifs::wl_seat::SeatId;
use crate::state::State; use crate::state::State;
@ -8,6 +8,7 @@ use crate::utils::numcell::NumCell;
use crate::utils::ptr_ext::PtrExt; use crate::utils::ptr_ext::PtrExt;
use jay_config::_private::ipc::{InitMessage, ServerMessage, V1InitMessage}; use jay_config::_private::ipc::{InitMessage, ServerMessage, V1InitMessage};
use jay_config::_private::{bincode_ops, ConfigEntry, VERSION}; use jay_config::_private::{bincode_ops, ConfigEntry, VERSION};
use jay_config::drm::Connector;
use jay_config::input::InputDevice; use jay_config::input::InputDevice;
use jay_config::keyboard::ModifiedKeySym; use jay_config::keyboard::ModifiedKeySym;
use jay_config::Seat; use jay_config::Seat;
@ -38,6 +39,30 @@ impl ConfigProxy {
}); });
} }
pub fn new_connector(&self, connector: ConnectorId) {
self.handler.send(&ServerMessage::NewConnector {
device: Connector(connector.raw() as _),
});
}
pub fn del_connector(&self, connector: ConnectorId) {
self.handler.send(&ServerMessage::DelConnector {
device: Connector(connector.raw() as _),
});
}
pub fn connector_connected(&self, connector: ConnectorId) {
self.handler.send(&ServerMessage::ConnectorConnect {
device: Connector(connector.raw() as _),
});
}
pub fn connector_disconnected(&self, connector: ConnectorId) {
self.handler.send(&ServerMessage::ConnectorDisconnect {
device: Connector(connector.raw() as _),
});
}
pub fn new_input_device(&self, dev: InputDeviceId) { pub fn new_input_device(&self, dev: InputDeviceId) {
self.handler.send(&ServerMessage::NewInputDevice { self.handler.send(&ServerMessage::NewInputDevice {
device: InputDevice(dev.raw() as _), device: InputDevice(dev.raw() as _),

View file

@ -33,6 +33,7 @@ pub use sys::{
drm_mode_modeinfo, DRM_CLIENT_CAP_ATOMIC, DRM_MODE_ATOMIC_ALLOW_MODESET, drm_mode_modeinfo, DRM_CLIENT_CAP_ATOMIC, DRM_MODE_ATOMIC_ALLOW_MODESET,
DRM_MODE_ATOMIC_NONBLOCK, DRM_MODE_PAGE_FLIP_EVENT, DRM_MODE_ATOMIC_NONBLOCK, DRM_MODE_PAGE_FLIP_EVENT,
}; };
use crate::backend;
#[derive(Debug, Error)] #[derive(Debug, Error)]
pub enum DrmError { pub enum DrmError {
@ -597,13 +598,21 @@ impl DrmModeInfo {
} }
} }
pub fn refresh_rate(&self) -> u32 { pub fn to_backend(&self) -> backend::Mode {
let clock_mhz = self.clock as u64 * 1_000_000; backend::Mode {
width: self.hdisplay as _,
height: self.vdisplay as _,
refresh_rate_millihz: self.refresh_rate_millihz(),
}
}
pub fn refresh_rate_millihz(&self) -> u32 {
let clock_millihz = self.clock as u64 * 1_000_000;
let htotal = self.htotal as u64; let htotal = self.htotal as u64;
let vtotal = self.vtotal as u64; let vtotal = self.vtotal as u64;
(((clock_mhz / htotal) + (vtotal / 2)) / vtotal) as u32 (((clock_millihz / htotal) + (vtotal / 2)) / vtotal) as u32
// simplifies to // simplifies to
// clock_mhz / (htotal * vtotal) + 1/2 // clock_millihz / (htotal * vtotal) + 1/2
// why round up (+1/2) instead of down? // why round up (+1/2) instead of down?
} }
} }
@ -725,6 +734,7 @@ pub enum ConnectorType {
WRITEBACK, WRITEBACK,
SPI, SPI,
USB, USB,
EmbeddedWindow,
} }
impl Display for ConnectorType { impl Display for ConnectorType {
@ -751,6 +761,7 @@ impl Display for ConnectorType {
Self::WRITEBACK => "Writeback", Self::WRITEBACK => "Writeback",
Self::SPI => "SPI", Self::SPI => "SPI",
Self::USB => "USB", Self::USB => "USB",
Self::EmbeddedWindow => "EmbeddedWindow",
}; };
f.write_str(s) f.write_str(s)
} }
@ -808,6 +819,7 @@ impl Into<u32> for ConnectorType {
Self::WRITEBACK => sys::DRM_MODE_CONNECTOR_WRITEBACK, Self::WRITEBACK => sys::DRM_MODE_CONNECTOR_WRITEBACK,
Self::SPI => sys::DRM_MODE_CONNECTOR_SPI, Self::SPI => sys::DRM_MODE_CONNECTOR_SPI,
Self::USB => sys::DRM_MODE_CONNECTOR_USB, Self::USB => sys::DRM_MODE_CONNECTOR_USB,
Self::EmbeddedWindow => sys::DRM_MODE_CONNECTOR_Unknown,
} }
} }
} }

View file

@ -1,11 +1,11 @@
use crate::backend; use crate::backend;
use crate::backend::Connector;
use crate::client::{Client, ClientError, ClientId}; use crate::client::{Client, ClientError, ClientId};
use crate::globals::{Global, GlobalName}; use crate::globals::{Global, GlobalName};
use crate::ifs::zxdg_output_v1::ZxdgOutputV1; use crate::ifs::zxdg_output_v1::ZxdgOutputV1;
use crate::leaks::Tracker; use crate::leaks::Tracker;
use crate::object::Object; use crate::object::Object;
use crate::rect::Rect; use crate::rect::Rect;
use crate::state::ConnectorData;
use crate::tree::OutputNode; use crate::tree::OutputNode;
use crate::utils::buffd::MsgParser; use crate::utils::buffd::MsgParser;
use crate::utils::buffd::MsgParserError; use crate::utils::buffd::MsgParserError;
@ -53,21 +53,38 @@ const MODE_PREFERRED: u32 = 2;
pub struct WlOutputGlobal { pub struct WlOutputGlobal {
name: GlobalName, name: GlobalName,
pub connector: Rc<dyn Connector>, pub connector: Rc<ConnectorData>,
pub pos: Cell<Rect>, pub pos: Cell<Rect>,
pub manufacturer: String,
pub display: String,
pub mode: Cell<backend::Mode>, pub mode: Cell<backend::Mode>,
pub node: CloneCell<Option<Rc<OutputNode>>>, pub node: CloneCell<Option<Rc<OutputNode>>>,
pub width_mm: i32,
pub height_mm: i32,
pub bindings: RefCell<AHashMap<ClientId, AHashMap<WlOutputId, Rc<WlOutput>>>>, pub bindings: RefCell<AHashMap<ClientId, AHashMap<WlOutputId, Rc<WlOutput>>>>,
} }
impl WlOutputGlobal { impl WlOutputGlobal {
pub fn new(name: GlobalName, connector: Rc<dyn Connector>, x1: i32) -> Self { pub fn new(
name: GlobalName,
connector: &Rc<ConnectorData>,
x1: i32,
mode: &backend::Mode,
manufacturer: &str,
product: &str,
width_mm: i32,
height_mm: i32,
) -> Self {
Self { Self {
name, name,
connector: connector.clone(), connector: connector.clone(),
pos: Cell::new(Rect::new_empty(x1, 0)), pos: Cell::new(Rect::new_sized(x1, 0, mode.width, mode.height).unwrap()),
mode: Default::default(), manufacturer: manufacturer.to_string(),
display: product.to_string(),
mode: Cell::new(*mode),
node: Default::default(), node: Default::default(),
width_mm,
height_mm,
bindings: Default::default(), bindings: Default::default(),
} }
} }
@ -163,24 +180,25 @@ impl WlOutput {
self_id: self.id, self_id: self.id,
x: pos.x1(), x: pos.x1(),
y: pos.y1(), y: pos.y1(),
physical_width: pos.width(), physical_width: self.global.width_mm,
physical_height: pos.height(), physical_height: self.global.height_mm,
subpixel: SP_UNKNOWN, subpixel: SP_UNKNOWN,
make: "jay", make: &self.global.manufacturer,
model: "jay", model: &self.global.display,
transform: TF_NORMAL, transform: TF_NORMAL,
}; };
self.client.event(event); self.client.event(event);
} }
fn send_mode(&self) { fn send_mode(&self) {
let mode = self.global.mode.get();
let pos = self.global.pos.get(); let pos = self.global.pos.get();
let event = Mode { let event = Mode {
self_id: self.id, self_id: self.id,
flags: MODE_CURRENT, flags: MODE_CURRENT,
width: pos.width(), width: mode.width,
height: pos.height(), height: mode.height,
refresh: 60_000_000, refresh: mode.refresh_rate_millihz as _,
}; };
self.client.event(event); self.client.event(event);
} }

View file

@ -1,6 +1,7 @@
use crate::client::{Client, ClientError}; use crate::client::{Client, ClientError};
use crate::leaks::Tracker; use crate::leaks::Tracker;
use crate::object::Object; use crate::object::Object;
use crate::rect::{Rect, Region, RegionBuilder};
use crate::utils::buffd::MsgParser; use crate::utils::buffd::MsgParser;
use crate::utils::buffd::MsgParserError; use crate::utils::buffd::MsgParserError;
use crate::wire::wl_region::*; use crate::wire::wl_region::*;
@ -8,7 +9,6 @@ use crate::wire::WlRegionId;
use std::cell::RefCell; use std::cell::RefCell;
use std::rc::Rc; use std::rc::Rc;
use thiserror::Error; use thiserror::Error;
use crate::rect::{Rect, Region, RegionBuilder};
pub struct WlRegion { pub struct WlRegion {
id: WlRegionId, id: WlRegionId,

View file

@ -1,4 +1,4 @@
use crate::backend::{InputEvent, KeyState, OutputId, ScrollAxis}; use crate::backend::{ConnectorId, InputEvent, KeyState, ScrollAxis};
use crate::client::{Client, ClientId}; use crate::client::{Client, ClientId};
use crate::fixed::Fixed; use crate::fixed::Fixed;
use crate::ifs::ipc; use crate::ifs::ipc;
@ -113,19 +113,28 @@ impl WlSeatGlobal {
pub fn event(self: &Rc<Self>, event: InputEvent) { pub fn event(self: &Rc<Self>, event: InputEvent) {
match event { match event {
InputEvent::Key(k, s) => self.key_event(k, s), InputEvent::Key(k, s) => self.key_event(k, s),
InputEvent::OutputPosition(o, x, y) => self.output_position_event(o, x, y), InputEvent::ConnectorPosition(o, x, y) => self.connector_position_event(o, x, y),
InputEvent::Motion(dx, dy) => self.motion_event(dx, dy), InputEvent::Motion(dx, dy) => self.motion_event(dx, dy),
InputEvent::Button(b, s) => self.pointer_owner.button(self, b, s), InputEvent::Button(b, s) => self.pointer_owner.button(self, b, s),
InputEvent::Scroll(d, a) => self.pointer_owner.scroll(self, d, a), InputEvent::Scroll(d, a) => self.pointer_owner.scroll(self, d, a),
} }
} }
fn output_position_event(self: &Rc<Self>, output: OutputId, mut x: Fixed, mut y: Fixed) { fn connector_position_event(
let output = match self.state.outputs.get(&output) { self: &Rc<Self>,
connector: ConnectorId,
mut x: Fixed,
mut y: Fixed,
) {
let output = match self.state.connectors.get(&connector) {
Some(o) => o, Some(o) => o,
_ => return, _ => return,
}; };
let pos = output.position(); let node = match output.node.get() {
Some(n) => n,
_ => return,
};
let pos = node.global.pos.get();
x += Fixed::from_int(pos.x1()); x += Fixed::from_int(pos.x1());
y += Fixed::from_int(pos.y1()); y += Fixed::from_int(pos.y1());
self.set_new_position(x, y); self.set_new_position(x, y);

View file

@ -75,11 +75,13 @@ impl ZwlrLayerShellV1 {
break 'get_output output; break 'get_output output;
} }
} }
let outputs = self.client.state.outputs.lock(); let outputs = self.client.state.connectors.lock();
match outputs.values().next() { for output in outputs.values() {
Some(ou) => ou.node.get().unwrap(), if let Some(node) = output.node.get() {
_ => return Err(GetLayerSurfaceError::NoOutputs), break 'get_output node;
}
} }
return Err(GetLayerSurfaceError::NoOutputs);
} }
}; };
log::info!("output = {:?}", output.global.pos.get()); log::info!("output = {:?}", output.global.pos.get());

View file

@ -1,6 +1,7 @@
use crate::utils::errorfmt::ErrorFmt; use crate::utils::errorfmt::ErrorFmt;
use crate::utils::oserror::OsError; use crate::utils::oserror::OsError;
use crate::utils::ptr_ext::MutPtrExt; use crate::utils::ptr_ext::MutPtrExt;
use backtrace::Backtrace;
use bstr::{BStr, BString, ByteSlice}; use bstr::{BStr, BString, ByteSlice};
use log::{Level, Log, Metadata, Record}; use log::{Level, Log, Metadata, Record};
use std::cell::UnsafeCell; use std::cell::UnsafeCell;
@ -29,8 +30,7 @@ impl Logger {
Ok(fd) => fd, Ok(fd) => fd,
Err(e) => { Err(e) => {
let e = OsError::from(e); let e = OsError::from(e);
eprintln!("Error: Could not dup stderr: {}", ErrorFmt(e)); fatal!("Error: Could not dup stderr: {}", ErrorFmt(e));
std::process::exit(1);
} }
}; };
Self::install(level, b"", file) Self::install(level, b"", file)
@ -55,13 +55,31 @@ impl Logger {
Err(Errno(c::EEXIST)) => {} Err(Errno(c::EEXIST)) => {}
Err(e) => { Err(e) => {
let e: OsError = e.into(); let e: OsError = e.into();
eprintln!("Error: Could not create log file: {}", ErrorFmt(e)); fatal!("Error: Could not create log file: {}", ErrorFmt(e));
std::process::exit(1);
} }
} }
} }
unreachable!(); unreachable!();
}; };
std::panic::set_hook(Box::new(|p| {
if let Some(loc) = p.location() {
log::error!(
"Panic at {} line {} column {}",
loc.file(),
loc.line(),
loc.column()
);
} else {
log::error!("Panic at unknown location");
}
if let Some(msg) = p.payload().downcast_ref::<&str>() {
log::error!("Message: {}", msg);
}
if let Some(msg) = p.payload().downcast_ref::<String>() {
log::error!("Message: {}", msg);
}
log::error!("Backtrace:\n{:?}", Backtrace::new());
}));
Self::install(level, path.as_bytes(), file) Self::install(level, path.as_bytes(), file)
} }

View file

@ -2,7 +2,6 @@
c_variadic, c_variadic,
thread_local, thread_local,
label_break_value, label_break_value,
try_blocks,
generic_associated_types, generic_associated_types,
extern_types extern_types
)] )]

View file

@ -17,7 +17,7 @@ pub struct Rect {
type Container = SmallVec<[Rect; 1]>; type Container = SmallVec<[Rect; 1]>;
#[derive(Default, Clone)] #[derive(Clone)]
pub struct Region { pub struct Region {
rects: Container, rects: Container,
extents: Rect, extents: Rect,
@ -75,6 +75,7 @@ impl Rect {
Some(Self { x1, y1, x2, y2 }) Some(Self { x1, y1, x2, y2 })
} }
#[allow(dead_code)]
fn new_unchecked(x1: i32, y1: i32, x2: i32, y2: i32) -> Self { fn new_unchecked(x1: i32, y1: i32, x2: i32, y2: i32) -> Self {
Self { x1, y1, x2, y2 } Self { x1, y1, x2, y2 }
} }

View file

@ -1,12 +1,21 @@
use crate::rect::{Container, Rect, Region}; use crate::rect::{Container, Rect, Region};
use crate::utils::windows::WindowsExt; use crate::utils::windows::WindowsExt;
use once_cell::unsync::Lazy;
use smallvec::SmallVec; use smallvec::SmallVec;
use std::cmp::Ordering; use std::cmp::Ordering;
use std::collections::{BinaryHeap}; use std::collections::BinaryHeap;
use std::mem; use std::mem;
use std::ops::Deref; use std::ops::Deref;
use std::rc::Rc; use std::rc::Rc;
#[thread_local]
static EMPTY: Lazy<Rc<Region>> = Lazy::new(|| {
Rc::new(Region {
rects: Default::default(),
extents: Default::default(),
})
});
impl Region { impl Region {
pub fn new(rect: Rect) -> Rc<Self> { pub fn new(rect: Rect) -> Rc<Self> {
let mut rects = SmallVec::new(); let mut rects = SmallVec::new();
@ -17,9 +26,13 @@ impl Region {
}) })
} }
pub fn empty() -> Rc<Self> {
EMPTY.clone()
}
pub fn from_rects(rects: &[Rect]) -> Rc<Self> { pub fn from_rects(rects: &[Rect]) -> Rc<Self> {
if rects.is_empty() { if rects.is_empty() {
return Rc::new(Self::default()); return Self::empty();
} }
if rects.len() == 1 { if rects.len() == 1 {
return Self::new(rects[0]); return Self::new(rects[0]);
@ -531,13 +544,22 @@ impl Default for BuilderOp {
} }
} }
#[derive(Default)]
pub struct RegionBuilder { pub struct RegionBuilder {
base: Rc<Region>, base: Rc<Region>,
op: BuilderOp, op: BuilderOp,
pending: Vec<Rect>, pending: Vec<Rect>,
} }
impl Default for RegionBuilder {
fn default() -> Self {
Self {
base: Region::empty(),
op: Default::default(),
pending: Default::default(),
}
}
}
impl RegionBuilder { impl RegionBuilder {
pub fn add(&mut self, rect: Rect) { pub fn add(&mut self, rect: Rect) {
self.set_op(BuilderOp::Add); self.set_op(BuilderOp::Add);
@ -556,7 +578,7 @@ impl RegionBuilder {
pub fn clear(&mut self) { pub fn clear(&mut self) {
self.pending.clear(); self.pending.clear();
self.base = Rc::new(Region::default()); self.base = Region::empty();
} }
fn set_op(&mut self, op: BuilderOp) { fn set_op(&mut self, op: BuilderOp) {

View file

@ -1,7 +1,5 @@
use crate::async_engine::{AsyncEngine, SpawnedFuture}; use crate::async_engine::{AsyncEngine, SpawnedFuture};
use crate::backend::{ use crate::backend::{Backend, BackendEvent, Connector, ConnectorId, ConnectorIds, InputDevice, InputDeviceId, InputDeviceIds, MonitorInfo};
Backend, BackendEvent, InputDevice, InputDeviceId, InputDeviceIds, OutputId, OutputIds,
};
use crate::client::{Client, Clients}; use crate::client::{Client, Clients};
use crate::config::ConfigProxy; use crate::config::ConfigProxy;
use crate::cursor::ServerCursors; use crate::cursor::ServerCursors;
@ -9,7 +7,6 @@ use crate::dbus::Dbus;
use crate::event_loop::EventLoop; use crate::event_loop::EventLoop;
use crate::forker::ForkerProxy; use crate::forker::ForkerProxy;
use crate::globals::{Globals, GlobalsError, WaylandGlobal}; use crate::globals::{Globals, GlobalsError, WaylandGlobal};
use crate::ifs::wl_output::WlOutputGlobal;
use crate::ifs::wl_seat::{SeatIds, WlSeatGlobal}; use crate::ifs::wl_seat::{SeatIds, WlSeatGlobal};
use crate::ifs::wl_surface::NoneSurfaceExt; use crate::ifs::wl_surface::NoneSurfaceExt;
use crate::logger::Logger; use crate::logger::Logger;
@ -25,7 +22,6 @@ use crate::utils::copyhashmap::CopyHashMap;
use crate::utils::errorfmt::ErrorFmt; use crate::utils::errorfmt::ErrorFmt;
use crate::utils::fdcloser::FdCloser; use crate::utils::fdcloser::FdCloser;
use crate::utils::linkedlist::LinkedList; use crate::utils::linkedlist::LinkedList;
use crate::utils::numcell::NumCell;
use crate::utils::queue::AsyncQueue; use crate::utils::queue::AsyncQueue;
use crate::wheel::Wheel; use crate::wheel::Wheel;
use crate::xkbcommon::{XkbContext, XkbKeymap}; use crate::xkbcommon::{XkbContext, XkbKeymap};
@ -45,9 +41,8 @@ pub struct State {
pub cursors: CloneCell<Option<Rc<ServerCursors>>>, pub cursors: CloneCell<Option<Rc<ServerCursors>>>,
pub wheel: Rc<Wheel>, pub wheel: Rc<Wheel>,
pub clients: Clients, pub clients: Clients,
pub next_name: NumCell<u32>,
pub globals: Globals, pub globals: Globals,
pub output_ids: OutputIds, pub connector_ids: ConnectorIds,
pub seat_ids: SeatIds, pub seat_ids: SeatIds,
pub input_device_ids: InputDeviceIds, pub input_device_ids: InputDeviceIds,
pub node_ids: NodeIds, pub node_ids: NodeIds,
@ -55,9 +50,7 @@ pub struct State {
pub workspaces: CopyHashMap<String, Rc<WorkspaceNode>>, pub workspaces: CopyHashMap<String, Rc<WorkspaceNode>>,
pub dummy_output: CloneCell<Option<Rc<OutputNode>>>, pub dummy_output: CloneCell<Option<Rc<OutputNode>>>,
pub backend_events: AsyncQueue<BackendEvent>, pub backend_events: AsyncQueue<BackendEvent>,
pub output_handlers: RefCell<AHashMap<OutputId, SpawnedFuture<()>>>,
pub input_device_handlers: RefCell<AHashMap<InputDeviceId, InputDeviceData>>, pub input_device_handlers: RefCell<AHashMap<InputDeviceId, InputDeviceData>>,
pub outputs: CopyHashMap<OutputId, Rc<WlOutputGlobal>>,
pub seat_queue: LinkedList<Rc<WlSeatGlobal>>, pub seat_queue: LinkedList<Rc<WlSeatGlobal>>,
pub slow_clients: AsyncQueue<Rc<Client>>, pub slow_clients: AsyncQueue<Rc<Client>>,
pub none_surface_ext: Rc<NoneSurfaceExt>, pub none_surface_ext: Rc<NoneSurfaceExt>,
@ -71,6 +64,7 @@ pub struct State {
pub dbus: Dbus, pub dbus: Dbus,
pub fdcloser: Arc<FdCloser>, pub fdcloser: Arc<FdCloser>,
pub logger: Arc<Logger>, pub logger: Arc<Logger>,
pub connectors: CopyHashMap<ConnectorId, Rc<ConnectorData>>,
} }
pub struct InputDeviceData { pub struct InputDeviceData {
@ -84,6 +78,13 @@ pub struct DeviceHandlerData {
pub device: Rc<dyn InputDevice>, pub device: Rc<dyn InputDevice>,
} }
pub struct ConnectorData {
pub connector: Rc<dyn Connector>,
pub monitor_info: CloneCell<Option<Rc<MonitorInfo>>>,
pub handler: Cell<Option<SpawnedFuture<()>>>,
pub node: CloneCell<Option<Rc<OutputNode>>>,
}
impl State { impl State {
pub fn set_render_ctx(&self, ctx: &Rc<RenderContext>) { pub fn set_render_ctx(&self, ctx: &Rc<RenderContext>) {
let cursors = match ServerCursors::load(ctx) { let cursors = match ServerCursors::load(ctx) {

View file

@ -1,6 +1,6 @@
mod backend; mod backend;
mod connector;
mod input_device; mod input_device;
mod output;
mod slow_clients; mod slow_clients;
mod start_backend; mod start_backend;

View file

@ -1,7 +1,6 @@
use crate::backend::{BackendEvent, Connector}; use crate::backend::{BackendEvent};
use crate::state::State; use crate::state::State;
use crate::tasks::input_device; use crate::tasks::{connector, input_device};
use crate::tasks::output::OutputHandler;
use std::rc::Rc; use std::rc::Rc;
pub struct BackendEventHandler { pub struct BackendEventHandler {
@ -18,18 +17,8 @@ impl BackendEventHandler {
fn handle_event(&mut self, event: BackendEvent) { fn handle_event(&mut self, event: BackendEvent) {
match event { match event {
BackendEvent::NewConnector(output) => self.handle_new_output(output), BackendEvent::NewConnector(connector) => connector::handle(&self.state, &connector),
BackendEvent::NewInputDevice(s) => input_device::handle(&self.state, s), BackendEvent::NewInputDevice(s) => input_device::handle(&self.state, s),
} }
} }
fn handle_new_output(&mut self, output: Rc<dyn Connector>) {
let id = output.id();
let oh = OutputHandler {
state: self.state.clone(),
output,
};
let future = self.state.eng.spawn(oh.handle());
self.state.output_handlers.borrow_mut().insert(id, future);
}
} }

128
src/tasks/connector.rs Normal file
View file

@ -0,0 +1,128 @@
use crate::backend::{Connector, ConnectorEvent, ConnectorId, MonitorInfo};
use crate::ifs::wl_output::WlOutputGlobal;
use crate::rect::Rect;
use crate::state::{ConnectorData, State};
use crate::tree::{OutputNode, OutputRenderData};
use crate::utils::asyncevent::AsyncEvent;
use crate::utils::clonecell::CloneCell;
use std::cell::RefCell;
use std::rc::Rc;
pub fn handle(state: &Rc<State>, connector: &Rc<dyn Connector>) {
let id = connector.id();
let data = Rc::new(ConnectorData {
connector: connector.clone(),
monitor_info: Default::default(),
handler: Default::default(),
node: Default::default(),
});
let oh = ConnectorHandler {
id,
state: state.clone(),
data: data.clone(),
};
let future = state.eng.spawn(oh.handle());
data.handler.set(Some(future));
state.connectors.set(id, data);
}
struct ConnectorHandler {
id: ConnectorId,
state: Rc<State>,
data: Rc<ConnectorData>,
}
impl ConnectorHandler {
async fn handle(self) {
let ae = Rc::new(AsyncEvent::default());
{
let ae = ae.clone();
self.data.connector.on_change(Rc::new(move || ae.trigger()));
}
if let Some(config) = self.state.config.get() {
config.new_connector(self.id);
}
'outer: loop {
while let Some(event) = self.data.connector.event() {
match event {
ConnectorEvent::Removed => break 'outer,
ConnectorEvent::Connected(mi) => self.handle_connected(&ae, mi).await,
_ => unreachable!(),
}
}
ae.triggered().await;
}
if let Some(config) = self.state.config.get() {
config.del_connector(self.id);
}
self.data.handler.set(None);
self.state.connectors.remove(&self.id);
}
async fn handle_connected(&self, ae: &Rc<AsyncEvent>, info: MonitorInfo) {
log::info!("Connector {} connected: {:#?}", self.data.connector.kernel_id(), info);
self.data.monitor_info.set(Some(Rc::new(info.clone())));
let name = self.state.globals.name();
let x1 = self
.state
.root
.outputs
.lock()
.values()
.map(|o| o.global.pos.get().x2())
.max()
.unwrap_or(0);
let global = Rc::new(WlOutputGlobal::new(
name,
&self.data,
x1,
&info.initial_mode,
&info.manufacturer,
&info.product,
info.width_mm,
info.height_mm,
));
let on = Rc::new(OutputNode {
id: self.state.node_ids.next(),
workspaces: Default::default(),
workspace: CloneCell::new(None),
seat_state: Default::default(),
global: global.clone(),
layers: Default::default(),
render_data: RefCell::new(OutputRenderData {
active_workspace: Rect::new_empty(0, 0),
inactive_workspaces: Default::default(),
titles: Default::default(),
}),
state: self.state.clone(),
is_dummy: false,
});
self.data.node.set(Some(on.clone()));
global.node.set(Some(on.clone()));
if let Some(config) = self.state.config.get() {
config.connector_connected(self.id);
}
on.ensure_workspace();
self.state.root.outputs.set(self.id, on.clone());
self.state.add_global(&global);
'outer: loop {
while let Some(event) = self.data.connector.event() {
match event {
ConnectorEvent::Disconnected => break 'outer,
ConnectorEvent::ModeChanged(mode) => {
on.update_mode(mode);
}
_ => unreachable!(),
}
}
ae.triggered().await;
}
if let Some(config) = self.state.config.get() {
config.connector_disconnected(self.id);
}
self.data.node.take();
global.node.set(None);
let _ = self.state.remove_global(&*global);
self.state.root.outputs.remove(&self.id);
}
}

View file

@ -1,98 +0,0 @@
use crate::backend::{Connector, ConnectorEvent};
use crate::ifs::wl_output::WlOutputGlobal;
use crate::rect::Rect;
use crate::state::State;
use crate::tree::{OutputNode, OutputRenderData, WorkspaceNode};
use crate::utils::asyncevent::AsyncEvent;
use crate::utils::clonecell::CloneCell;
use std::cell::RefCell;
use std::rc::Rc;
pub struct OutputHandler {
pub state: Rc<State>,
pub output: Rc<dyn Connector>,
}
impl OutputHandler {
pub async fn handle(self) {
let ae = Rc::new(AsyncEvent::default());
{
let ae = ae.clone();
self.output.on_change(Rc::new(move || ae.trigger()));
}
let name = self.state.globals.name();
let x1 = self
.state
.root
.outputs
.lock()
.values()
.map(|o| o.global.pos.get().x2())
.max()
.unwrap_or(0);
let global = Rc::new(WlOutputGlobal::new(name, self.output.clone(), x1));
let on = Rc::new(OutputNode {
id: self.state.node_ids.next(),
workspaces: Default::default(),
workspace: CloneCell::new(None),
seat_state: Default::default(),
global: global.clone(),
layers: Default::default(),
render_data: RefCell::new(OutputRenderData {
active_workspace: Rect::new_empty(0, 0),
inactive_workspaces: Default::default(),
titles: Default::default(),
}),
state: self.state.clone(),
is_dummy: false,
});
global.node.set(Some(on.clone()));
let name = 'name: {
for i in 1.. {
let name = i.to_string();
if !self.state.workspaces.contains(&name) {
break 'name name;
}
}
unreachable!();
};
let workspace = Rc::new(WorkspaceNode {
id: self.state.node_ids.next(),
output: CloneCell::new(on.clone()),
position: Default::default(),
container: Default::default(),
stacked: Default::default(),
seat_state: Default::default(),
name: name.clone(),
output_link: Default::default(),
});
self.state.workspaces.set(name, workspace.clone());
workspace
.output_link
.set(Some(on.workspaces.add_last(workspace.clone())));
on.show_workspace(&workspace);
on.update_render_data();
self.state.root.outputs.set(self.output.id(), on.clone());
self.state.add_global(&global);
self.state.outputs.set(self.output.id(), global.clone());
'outer: loop {
while let Some(event) = self.output.event() {
match event {
ConnectorEvent::Removed => break 'outer,
ConnectorEvent::ModeChanged(mode) => {
on.update_mode(mode);
}
}
}
ae.triggered().await;
}
global.node.set(None);
self.state.outputs.remove(&self.output.id());
let _ = self.state.remove_global(&*global);
self.state
.output_handlers
.borrow_mut()
.remove(&self.output.id());
self.state.root.outputs.remove(&self.output.id());
}
}

View file

@ -1,4 +1,4 @@
use crate::backend::{KeyState, OutputId, ScrollAxis}; use crate::backend::{ConnectorId, KeyState, ScrollAxis};
use crate::client::{Client, ClientId}; use crate::client::{Client, ClientId};
use crate::cursor::KnownCursor; use crate::cursor::KnownCursor;
use crate::fixed::Fixed; use crate::fixed::Fixed;
@ -356,7 +356,7 @@ tree_id!(ToplevelNodeId);
pub struct DisplayNode { pub struct DisplayNode {
pub id: NodeId, pub id: NodeId,
pub outputs: CopyHashMap<OutputId, Rc<OutputNode>>, pub outputs: CopyHashMap<ConnectorId, Rc<OutputNode>>,
pub stacked: LinkedList<Rc<dyn Node>>, pub stacked: LinkedList<Rc<dyn Node>>,
pub xstacked: LinkedList<Rc<Xwindow>>, pub xstacked: LinkedList<Rc<Xwindow>>,
pub seat_state: NodeSeatState, pub seat_state: NodeSeatState,

View file

@ -78,6 +78,37 @@ impl OutputNode {
} }
} }
pub fn ensure_workspace(self: &Rc<Self>) {
if !self.workspaces.is_empty() {
return;
}
let name = 'name: {
for i in 1.. {
let name = i.to_string();
if !self.state.workspaces.contains(&name) {
break 'name name;
}
}
unreachable!();
};
let workspace = Rc::new(WorkspaceNode {
id: self.state.node_ids.next(),
output: CloneCell::new(self.clone()),
position: Default::default(),
container: Default::default(),
stacked: Default::default(),
seat_state: Default::default(),
name: name.clone(),
output_link: Default::default(),
});
self.state.workspaces.set(name, workspace.clone());
workspace
.output_link
.set(Some(self.workspaces.add_last(workspace.clone())));
self.show_workspace(&workspace);
self.update_render_data();
}
pub fn show_workspace(&self, ws: &Rc<WorkspaceNode>) { pub fn show_workspace(&self, ws: &Rc<WorkspaceNode>) {
self.workspace.set(Some(ws.clone())); self.workspace.set(Some(ws.clone()));
ws.clone().change_extents(&self.workspace_rect()); ws.clone().change_extents(&self.workspace_rect());

View file

@ -52,6 +52,10 @@ impl<T> LinkedList<T> {
} }
} }
pub fn is_empty(&self) -> bool {
self.last().is_none()
}
pub fn last(&self) -> Option<NodeRef<T>> { pub fn last(&self) -> Option<NodeRef<T>> {
unsafe { self.endpoint(self.root.data.as_ref().prev.get()) } unsafe { self.endpoint(self.root.data.as_ref().prev.get()) }
} }