autocommit 2022-04-05 18:28:42 CEST
This commit is contained in:
parent
1f05ea431e
commit
a3e9f21fc5
29 changed files with 568 additions and 225 deletions
|
|
@ -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 = []
|
||||
|
|
|
|||
23
README.md
23
README.md
|
|
@ -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.)
|
||||
|
|
|
|||
|
|
@ -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 { .. } => {}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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
4
jay-config/src/drm.rs
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
use bincode::{Decode, Encode};
|
||||
|
||||
#[derive(Encode, Decode, Copy, Clone, Debug, Hash, Eq, PartialEq)]
|
||||
pub struct Connector(pub u64);
|
||||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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),
|
||||
|
|
|
|||
|
|
@ -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
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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()));
|
||||
|
|
|
|||
|
|
@ -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()
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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(),
|
||||
|
|
|
|||
|
|
@ -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 _),
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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());
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -2,7 +2,6 @@
|
|||
c_variadic,
|
||||
thread_local,
|
||||
label_break_value,
|
||||
try_blocks,
|
||||
generic_associated_types,
|
||||
extern_types
|
||||
)]
|
||||
|
|
|
|||
|
|
@ -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 }
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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) {
|
||||
|
|
|
|||
19
src/state.rs
19
src/state.rs
|
|
@ -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) {
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
mod backend;
|
||||
mod connector;
|
||||
mod input_device;
|
||||
mod output;
|
||||
mod slow_clients;
|
||||
mod start_backend;
|
||||
|
||||
|
|
|
|||
|
|
@ -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
128
src/tasks/connector.rs
Normal 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);
|
||||
}
|
||||
}
|
||||
|
|
@ -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());
|
||||
}
|
||||
}
|
||||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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());
|
||||
|
|
|
|||
|
|
@ -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()) }
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue