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"
humantime = "2.1.0"
dirs = "4.0.0"
backtrace = { version = "0.3.64", optional = true }
backtrace = "0.3.64"
[build-dependencies]
repc = "0.1.1"
@ -49,4 +48,4 @@ bstr = { version = "0.2.17", default-features = false, features = ["std"] }
#opt-level = 3
[features]
rc_tracking = ["backtrace"]
rc_tracking = []

View file

@ -9,12 +9,19 @@ Do not expect any kind of structured commit history.
## 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.
* **input.so**: For processing input events.
* **EGL.so**, **GLESv2.so**: For OpenGL rendering.
* **gbm.so**: For graphics buffer allocation.
* **xkbcommon.so**: For keymap handling.
* **udev.so**: For device enumeration and hotplug support.
* **cairo.so**, **pangocairo-1.0.so**, **gobject-2.0.so**, **pango-1.0.so**: For text rendering.
* **libinput.so**: For processing input events.
* **libEGL.so**, **libGLESv2.so**: For OpenGL rendering.
* **libgbm.so**: For graphics buffer allocation.
* **libxkbcommon.so**: For keymap handling.
* **libudev.so**: For device enumeration and hotplug support.
* **libcairo.so**, **libpangocairo-1.0.so**, **libgobject-2.0.so**, **libpango-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::{bincode_ops, logging, Config, ConfigEntry, ConfigEntryGen, VERSION};
use crate::drm::Connector;
use crate::input::acceleration::AccelProfile;
use crate::input::capability::Capability;
use crate::input::InputDevice;
@ -24,6 +25,8 @@ pub(crate) struct Client {
response: RefCell<Vec<Response>>,
on_new_seat: RefCell<Option<Rc<dyn Fn(Seat)>>>,
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>>>,
}
@ -103,6 +106,8 @@ pub unsafe extern "C" fn init(
response: Default::default(),
on_new_seat: Default::default(),
on_new_input_device: Default::default(),
on_connector_connected: Default::default(),
on_new_connector: Default::default(),
bufs: Default::default(),
});
let init = slice::from_raw_parts(init, size);
@ -471,6 +476,20 @@ impl Client {
}
}
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::capability::Capability;
use crate::input::InputDevice;
@ -14,6 +15,18 @@ pub enum ServerMessage {
Response {
response: Response,
},
ConnectorConnect {
device: Connector,
},
ConnectorDisconnect {
device: Connector,
},
NewConnector {
device: Connector,
},
DelConnector {
device: Connector,
},
NewInputDevice {
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;
#[doc(hidden)]
pub mod _private;
pub mod drm;
pub mod embedded;
pub mod input;
pub mod keyboard;

View file

@ -1,8 +1,9 @@
use crate::drm::drm::ConnectorType;
use crate::fixed::Fixed;
use std::fmt::Debug;
use std::fmt::{Debug, Display, Formatter};
use std::rc::Rc;
linear_ids!(OutputIds, OutputId);
linear_ids!(ConnectorIds, ConnectorId);
linear_ids!(InputDeviceIds, InputDeviceId);
pub trait Backend {
@ -13,17 +14,42 @@ pub trait Backend {
pub struct Mode {
pub width: 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 {
fn id(&self) -> OutputId;
fn id(&self) -> ConnectorId;
fn kernel_id(&self) -> ConnectorKernelId;
fn event(&self) -> Option<ConnectorEvent>;
fn on_change(&self, cb: Rc<dyn Fn()>);
}
#[derive(Debug)]
pub enum ConnectorEvent {
Connected(MonitorInfo),
Disconnected,
Removed,
ModeChanged(Mode),
}
@ -79,7 +105,7 @@ pub enum ScrollAxis {
#[derive(Debug)]
pub enum InputEvent {
Key(u32, KeyState),
OutputPosition(OutputId, Fixed, Fixed),
ConnectorPosition(ConnectorId, Fixed, Fixed),
#[allow(dead_code)]
Motion(Fixed, Fixed),
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;
pub struct DummyBackend {}
@ -10,14 +11,21 @@ impl Backend for DummyBackend {
}
pub struct DummyOutput {
pub id: OutputId,
pub id: ConnectorId,
}
impl Connector for DummyOutput {
fn id(&self) -> OutputId {
fn id(&self) -> ConnectorId {
self.id
}
fn kernel_id(&self) -> ConnectorKernelId {
ConnectorKernelId {
ty: ConnectorType::Unknown(0),
id: 0,
}
}
fn event(&self) -> Option<ConnectorEvent> {
None
}

View file

@ -1,5 +1,5 @@
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::drm::drm::{
drm_mode_modeinfo, Change, ConnectorStatus, ConnectorType, DrmBlob, DrmConnector, DrmCrtc,
@ -25,6 +25,7 @@ use std::ffi::CString;
use std::fmt::{Debug, Formatter};
use std::rc::Rc;
use uapi::c;
use crate::edid::Descriptor;
pub struct PendingDrmDevice {
pub id: DrmId,
@ -72,12 +73,16 @@ pub struct MetalConnector {
pub id: DrmConnector,
pub master: Rc<DrmMaster>,
pub output_id: OutputId,
pub connector_id: ConnectorId,
pub crtcs: AHashMap<DrmCrtc, Rc<MetalCrtc>>,
pub modes: Vec<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 buffers: CloneCell<Option<Rc<[RenderBuffer; 2]>>>,
@ -114,8 +119,15 @@ impl Debug for OnChange {
}
impl Connector for MetalConnector {
fn id(&self) -> OutputId {
self.output_id
fn id(&self) -> ConnectorId {
self.connector_id
}
fn kernel_id(&self) -> ConnectorKernelId {
ConnectorKernelId {
ty: self.connector_type,
id: self.connector_type_id,
}
}
fn event(&self) -> Option<ConnectorEvent> {
@ -212,28 +224,73 @@ fn create_connector(
}
}
let props = collect_properties(&dev.master, connector)?;
let mode = info.modes.first().cloned().map(Rc::new);
let events = SyncQueue::default();
if let Some(mode) = &mode {
events.push(ConnectorEvent::ModeChanged(Mode {
width: mode.hdisplay as _,
height: mode.vdisplay as _,
refresh_rate: mode.refresh_rate(),
}));
let connection = ConnectorStatus::from(info.connection);
let connector_type = ConnectorType::from(info.connector_type);
let mut name = String::new();
let mut manufacturer = String::new();
let mut serial_number = String::new();
'fetch_edid: {
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 {
id: connector,
master: dev.master.clone(),
output_id: state.output_ids.next(),
connector_id: state.connector_ids.next(),
crtcs,
mode: CloneCell::new(mode),
events,
mode: CloneCell::new(info.modes.first().cloned().map(Rc::new)),
monitor_manufacturer: manufacturer,
monitor_name: name,
monitor_serial_number: serial_number,
events: Default::default(),
modes: info.modes,
buffers: Default::default(),
next_buffer: Default::default(),
connector_type: info.connector_type.into(),
connector_type,
connector_type_id: info.connector_type_id,
connection: info.connection.into(),
connection,
mm_width: info.mm_width,
mm_height: info.mm_height,
subpixel: info.subpixel,
@ -488,7 +545,27 @@ impl MetalBackend {
self.state
.backend_events
.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);
}
}
@ -901,7 +978,7 @@ impl MetalBackend {
_ => return,
};
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
.egl
.render(&*node, &self.state, Some(node.global.pos.get()));

View file

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

View file

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

View file

@ -1,6 +1,6 @@
mod handler;
use crate::backend::InputDeviceId;
use crate::backend::{ConnectorId, InputDeviceId};
use crate::config::handler::ConfigProxyHandler;
use crate::ifs::wl_seat::SeatId;
use crate::state::State;
@ -8,6 +8,7 @@ use crate::utils::numcell::NumCell;
use crate::utils::ptr_ext::PtrExt;
use jay_config::_private::ipc::{InitMessage, ServerMessage, V1InitMessage};
use jay_config::_private::{bincode_ops, ConfigEntry, VERSION};
use jay_config::drm::Connector;
use jay_config::input::InputDevice;
use jay_config::keyboard::ModifiedKeySym;
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) {
self.handler.send(&ServerMessage::NewInputDevice {
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_ATOMIC_NONBLOCK, DRM_MODE_PAGE_FLIP_EVENT,
};
use crate::backend;
#[derive(Debug, Error)]
pub enum DrmError {
@ -597,13 +598,21 @@ impl DrmModeInfo {
}
}
pub fn refresh_rate(&self) -> u32 {
let clock_mhz = self.clock as u64 * 1_000_000;
pub fn to_backend(&self) -> backend::Mode {
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 vtotal = self.vtotal as u64;
(((clock_mhz / htotal) + (vtotal / 2)) / vtotal) as u32
(((clock_millihz / htotal) + (vtotal / 2)) / vtotal) as u32
// simplifies to
// clock_mhz / (htotal * vtotal) + 1/2
// clock_millihz / (htotal * vtotal) + 1/2
// why round up (+1/2) instead of down?
}
}
@ -725,6 +734,7 @@ pub enum ConnectorType {
WRITEBACK,
SPI,
USB,
EmbeddedWindow,
}
impl Display for ConnectorType {
@ -751,6 +761,7 @@ impl Display for ConnectorType {
Self::WRITEBACK => "Writeback",
Self::SPI => "SPI",
Self::USB => "USB",
Self::EmbeddedWindow => "EmbeddedWindow",
};
f.write_str(s)
}
@ -808,6 +819,7 @@ impl Into<u32> for ConnectorType {
Self::WRITEBACK => sys::DRM_MODE_CONNECTOR_WRITEBACK,
Self::SPI => sys::DRM_MODE_CONNECTOR_SPI,
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::Connector;
use crate::client::{Client, ClientError, ClientId};
use crate::globals::{Global, GlobalName};
use crate::ifs::zxdg_output_v1::ZxdgOutputV1;
use crate::leaks::Tracker;
use crate::object::Object;
use crate::rect::Rect;
use crate::state::ConnectorData;
use crate::tree::OutputNode;
use crate::utils::buffd::MsgParser;
use crate::utils::buffd::MsgParserError;
@ -53,21 +53,38 @@ const MODE_PREFERRED: u32 = 2;
pub struct WlOutputGlobal {
name: GlobalName,
pub connector: Rc<dyn Connector>,
pub connector: Rc<ConnectorData>,
pub pos: Cell<Rect>,
pub manufacturer: String,
pub display: String,
pub mode: Cell<backend::Mode>,
pub node: CloneCell<Option<Rc<OutputNode>>>,
pub width_mm: i32,
pub height_mm: i32,
pub bindings: RefCell<AHashMap<ClientId, AHashMap<WlOutputId, Rc<WlOutput>>>>,
}
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 {
name,
connector: connector.clone(),
pos: Cell::new(Rect::new_empty(x1, 0)),
mode: Default::default(),
pos: Cell::new(Rect::new_sized(x1, 0, mode.width, mode.height).unwrap()),
manufacturer: manufacturer.to_string(),
display: product.to_string(),
mode: Cell::new(*mode),
node: Default::default(),
width_mm,
height_mm,
bindings: Default::default(),
}
}
@ -163,24 +180,25 @@ impl WlOutput {
self_id: self.id,
x: pos.x1(),
y: pos.y1(),
physical_width: pos.width(),
physical_height: pos.height(),
physical_width: self.global.width_mm,
physical_height: self.global.height_mm,
subpixel: SP_UNKNOWN,
make: "jay",
model: "jay",
make: &self.global.manufacturer,
model: &self.global.display,
transform: TF_NORMAL,
};
self.client.event(event);
}
fn send_mode(&self) {
let mode = self.global.mode.get();
let pos = self.global.pos.get();
let event = Mode {
self_id: self.id,
flags: MODE_CURRENT,
width: pos.width(),
height: pos.height(),
refresh: 60_000_000,
width: mode.width,
height: mode.height,
refresh: mode.refresh_rate_millihz as _,
};
self.client.event(event);
}

View file

@ -1,6 +1,7 @@
use crate::client::{Client, ClientError};
use crate::leaks::Tracker;
use crate::object::Object;
use crate::rect::{Rect, Region, RegionBuilder};
use crate::utils::buffd::MsgParser;
use crate::utils::buffd::MsgParserError;
use crate::wire::wl_region::*;
@ -8,7 +9,6 @@ use crate::wire::WlRegionId;
use std::cell::RefCell;
use std::rc::Rc;
use thiserror::Error;
use crate::rect::{Rect, Region, RegionBuilder};
pub struct WlRegion {
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::fixed::Fixed;
use crate::ifs::ipc;
@ -113,19 +113,28 @@ impl WlSeatGlobal {
pub fn event(self: &Rc<Self>, event: InputEvent) {
match event {
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::Button(b, s) => self.pointer_owner.button(self, b, s),
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) {
let output = match self.state.outputs.get(&output) {
fn connector_position_event(
self: &Rc<Self>,
connector: ConnectorId,
mut x: Fixed,
mut y: Fixed,
) {
let output = match self.state.connectors.get(&connector) {
Some(o) => o,
_ => 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());
y += Fixed::from_int(pos.y1());
self.set_new_position(x, y);

View file

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

View file

@ -1,6 +1,7 @@
use crate::utils::errorfmt::ErrorFmt;
use crate::utils::oserror::OsError;
use crate::utils::ptr_ext::MutPtrExt;
use backtrace::Backtrace;
use bstr::{BStr, BString, ByteSlice};
use log::{Level, Log, Metadata, Record};
use std::cell::UnsafeCell;
@ -29,8 +30,7 @@ impl Logger {
Ok(fd) => fd,
Err(e) => {
let e = OsError::from(e);
eprintln!("Error: Could not dup stderr: {}", ErrorFmt(e));
std::process::exit(1);
fatal!("Error: Could not dup stderr: {}", ErrorFmt(e));
}
};
Self::install(level, b"", file)
@ -55,13 +55,31 @@ impl Logger {
Err(Errno(c::EEXIST)) => {}
Err(e) => {
let e: OsError = e.into();
eprintln!("Error: Could not create log file: {}", ErrorFmt(e));
std::process::exit(1);
fatal!("Error: Could not create log file: {}", ErrorFmt(e));
}
}
}
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)
}

View file

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

View file

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

View file

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

View file

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

View file

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

View file

@ -1,7 +1,6 @@
use crate::backend::{BackendEvent, Connector};
use crate::backend::{BackendEvent};
use crate::state::State;
use crate::tasks::input_device;
use crate::tasks::output::OutputHandler;
use crate::tasks::{connector, input_device};
use std::rc::Rc;
pub struct BackendEventHandler {
@ -18,18 +17,8 @@ impl BackendEventHandler {
fn handle_event(&mut self, event: BackendEvent) {
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),
}
}
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::cursor::KnownCursor;
use crate::fixed::Fixed;
@ -356,7 +356,7 @@ tree_id!(ToplevelNodeId);
pub struct DisplayNode {
pub id: NodeId,
pub outputs: CopyHashMap<OutputId, Rc<OutputNode>>,
pub outputs: CopyHashMap<ConnectorId, Rc<OutputNode>>,
pub stacked: LinkedList<Rc<dyn Node>>,
pub xstacked: LinkedList<Rc<Xwindow>>,
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>) {
self.workspace.set(Some(ws.clone()));
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>> {
unsafe { self.endpoint(self.root.data.as_ref().prev.get()) }
}