mirror of
https://github.com/zed-industries/zed.git
synced 2025-02-03 17:44:30 +00:00
x11: create window and route events
This commit is contained in:
parent
cefc98258f
commit
aed363d3c7
5 changed files with 194 additions and 52 deletions
|
@ -1,5 +1,6 @@
|
||||||
mod blade_atlas;
|
mod blade_atlas;
|
||||||
mod blade_belt;
|
mod blade_belt;
|
||||||
|
mod blade_renderer;
|
||||||
mod dispatcher;
|
mod dispatcher;
|
||||||
mod display;
|
mod display;
|
||||||
mod platform;
|
mod platform;
|
||||||
|
@ -14,3 +15,4 @@ pub(crate) use text_system::*;
|
||||||
pub(crate) use window::*;
|
pub(crate) use window::*;
|
||||||
|
|
||||||
use blade_belt::*;
|
use blade_belt::*;
|
||||||
|
use blade_renderer::*;
|
||||||
|
|
|
@ -22,6 +22,22 @@ struct BladeAtlasState {
|
||||||
tiles_by_key: FxHashMap<AtlasKey, AtlasTile>,
|
tiles_by_key: FxHashMap<AtlasKey, AtlasTile>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl BladeAtlasState {
|
||||||
|
fn destroy(&mut self) {
|
||||||
|
for texture in self.monochrome_textures.drain(..) {
|
||||||
|
self.gpu.destroy_texture(texture.raw);
|
||||||
|
}
|
||||||
|
for texture in self.polychrome_textures.drain(..) {
|
||||||
|
self.gpu.destroy_texture(texture.raw);
|
||||||
|
}
|
||||||
|
for texture in self.path_textures.drain(..) {
|
||||||
|
self.gpu.destroy_texture(texture.raw);
|
||||||
|
}
|
||||||
|
self.gpu.destroy_command_encoder(&mut self.gpu_encoder);
|
||||||
|
self.upload_belt.destroy(&self.gpu);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl BladeAtlas {
|
impl BladeAtlas {
|
||||||
pub(crate) fn new(gpu: &Arc<blade::Context>) -> Self {
|
pub(crate) fn new(gpu: &Arc<blade::Context>) -> Self {
|
||||||
BladeAtlas(Mutex::new(BladeAtlasState {
|
BladeAtlas(Mutex::new(BladeAtlasState {
|
||||||
|
@ -41,6 +57,10 @@ impl BladeAtlas {
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub(crate) fn destroy(&self) {
|
||||||
|
self.0.lock().destroy();
|
||||||
|
}
|
||||||
|
|
||||||
pub(crate) fn clear_textures(&self, texture_kind: AtlasTextureKind) {
|
pub(crate) fn clear_textures(&self, texture_kind: AtlasTextureKind) {
|
||||||
let mut lock = self.0.lock();
|
let mut lock = self.0.lock();
|
||||||
let textures = match texture_kind {
|
let textures = match texture_kind {
|
||||||
|
|
11
crates/gpui/src/platform/linux/blade_renderer.rs
Normal file
11
crates/gpui/src/platform/linux/blade_renderer.rs
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
use std::sync::Arc;
|
||||||
|
|
||||||
|
pub struct BladeRenderer {
|
||||||
|
gpu: Arc<blade::Context>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl BladeRenderer {
|
||||||
|
pub fn new(gpu: Arc<blade::Context>) -> Self {
|
||||||
|
Self { gpu }
|
||||||
|
}
|
||||||
|
}
|
|
@ -2,11 +2,13 @@
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
Action, AnyWindowHandle, BackgroundExecutor, ClipboardItem, CursorStyle, DisplayId,
|
Action, AnyWindowHandle, BackgroundExecutor, ClipboardItem, CursorStyle, DisplayId,
|
||||||
ForegroundExecutor, Keymap, LinuxDispatcher, LinuxDisplay, LinuxTextSystem, LinuxWindow, Menu,
|
ForegroundExecutor, Keymap, LinuxDispatcher, LinuxDisplay, LinuxTextSystem, LinuxWindow,
|
||||||
PathPromptOptions, Platform, PlatformDisplay, PlatformInput, PlatformTextSystem,
|
LinuxWindowState, LinuxWindowStatePtr, Menu, PathPromptOptions, Platform, PlatformDisplay,
|
||||||
PlatformWindow, Result, SemanticVersion, Task, WindowOptions,
|
PlatformInput, PlatformTextSystem, PlatformWindow, Result, SemanticVersion, Task,
|
||||||
|
WindowOptions,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
use collections::{HashMap, HashSet};
|
||||||
use futures::channel::oneshot;
|
use futures::channel::oneshot;
|
||||||
use parking_lot::Mutex;
|
use parking_lot::Mutex;
|
||||||
|
|
||||||
|
@ -17,16 +19,48 @@ use std::{
|
||||||
time::Duration,
|
time::Duration,
|
||||||
};
|
};
|
||||||
use time::UtcOffset;
|
use time::UtcOffset;
|
||||||
use x11rb::{connection::Connection as _, rust_connection::RustConnection};
|
use x11rb::{
|
||||||
|
connection::Connection as _,
|
||||||
|
protocol::{
|
||||||
|
xproto::{Atom, ConnectionExt as _},
|
||||||
|
Event,
|
||||||
|
},
|
||||||
|
rust_connection::RustConnection,
|
||||||
|
};
|
||||||
|
|
||||||
pub(crate) struct LinuxPlatform(Mutex<LinuxPlatformState>);
|
pub(crate) struct LinuxPlatform(Mutex<LinuxPlatformState>);
|
||||||
|
|
||||||
|
pub(crate) struct WmAtoms {
|
||||||
|
pub protocols: Atom,
|
||||||
|
pub delete_window: Atom,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl WmAtoms {
|
||||||
|
fn new(x11_connection: &RustConnection) -> Self {
|
||||||
|
Self {
|
||||||
|
protocols: x11_connection
|
||||||
|
.intern_atom(false, b"WM_PROTOCOLS")
|
||||||
|
.unwrap()
|
||||||
|
.reply()
|
||||||
|
.unwrap()
|
||||||
|
.atom,
|
||||||
|
delete_window: x11_connection
|
||||||
|
.intern_atom(false, b"WM_DELETE_WINDOW")
|
||||||
|
.unwrap()
|
||||||
|
.reply()
|
||||||
|
.unwrap()
|
||||||
|
.atom,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
pub(crate) struct LinuxPlatformState {
|
pub(crate) struct LinuxPlatformState {
|
||||||
x11_connection: RustConnection,
|
x11_connection: RustConnection,
|
||||||
x11_root_index: usize,
|
x11_root_index: usize,
|
||||||
gpu: Arc<blade::Context>,
|
atoms: WmAtoms,
|
||||||
background_executor: BackgroundExecutor,
|
background_executor: BackgroundExecutor,
|
||||||
foreground_executor: ForegroundExecutor,
|
foreground_executor: ForegroundExecutor,
|
||||||
|
windows: HashMap<u32, LinuxWindowStatePtr>,
|
||||||
text_system: Arc<LinuxTextSystem>,
|
text_system: Arc<LinuxTextSystem>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -39,24 +73,17 @@ impl Default for LinuxPlatform {
|
||||||
impl LinuxPlatform {
|
impl LinuxPlatform {
|
||||||
pub(crate) fn new() -> Self {
|
pub(crate) fn new() -> Self {
|
||||||
let (x11_connection, x11_root_index) = x11rb::connect(None).unwrap();
|
let (x11_connection, x11_root_index) = x11rb::connect(None).unwrap();
|
||||||
|
let atoms = WmAtoms::new(&x11_connection);
|
||||||
|
|
||||||
let dispatcher = Arc::new(LinuxDispatcher::new());
|
let dispatcher = Arc::new(LinuxDispatcher::new());
|
||||||
let gpu = Arc::new(
|
|
||||||
unsafe {
|
|
||||||
blade::Context::init(blade::ContextDesc {
|
|
||||||
validation: cfg!(debug_assertions),
|
|
||||||
capture: false,
|
|
||||||
})
|
|
||||||
}
|
|
||||||
.unwrap(),
|
|
||||||
);
|
|
||||||
|
|
||||||
Self(Mutex::new(LinuxPlatformState {
|
Self(Mutex::new(LinuxPlatformState {
|
||||||
x11_connection,
|
x11_connection,
|
||||||
x11_root_index,
|
x11_root_index,
|
||||||
gpu,
|
atoms,
|
||||||
background_executor: BackgroundExecutor::new(dispatcher.clone()),
|
background_executor: BackgroundExecutor::new(dispatcher.clone()),
|
||||||
foreground_executor: ForegroundExecutor::new(dispatcher),
|
foreground_executor: ForegroundExecutor::new(dispatcher),
|
||||||
|
windows: HashMap::default(),
|
||||||
text_system: Arc::new(LinuxTextSystem::new()),
|
text_system: Arc::new(LinuxTextSystem::new()),
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
|
@ -76,7 +103,58 @@ impl Platform for LinuxPlatform {
|
||||||
}
|
}
|
||||||
|
|
||||||
fn run(&self, on_finish_launching: Box<dyn FnOnce()>) {
|
fn run(&self, on_finish_launching: Box<dyn FnOnce()>) {
|
||||||
on_finish_launching()
|
on_finish_launching();
|
||||||
|
|
||||||
|
let mut need_repaint = HashSet::<u32>::default();
|
||||||
|
|
||||||
|
while !self.0.lock().windows.is_empty() {
|
||||||
|
let event = self.0.lock().x11_connection.wait_for_event().unwrap();
|
||||||
|
let mut event_option = Some(event);
|
||||||
|
while let Some(event) = event_option {
|
||||||
|
match event {
|
||||||
|
Event::Expose(event) => {
|
||||||
|
if event.count == 0 {
|
||||||
|
need_repaint.insert(event.window);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Event::ConfigureNotify(event) => {
|
||||||
|
let lock = self.0.lock();
|
||||||
|
let mut window = lock.windows[&event.window].lock();
|
||||||
|
window.resize(event.width, event.height);
|
||||||
|
}
|
||||||
|
Event::MotionNotify(_event) => {
|
||||||
|
//mouse_position = (event.event_x, event.event_y);
|
||||||
|
//need_repaint.insert(event.window);
|
||||||
|
}
|
||||||
|
Event::MapNotify(_) => {}
|
||||||
|
Event::ClientMessage(event) => {
|
||||||
|
let mut lock = self.0.lock();
|
||||||
|
let data = event.data.as_data32();
|
||||||
|
if data[0] == lock.atoms.delete_window {
|
||||||
|
{
|
||||||
|
let mut window = lock.windows[&event.window].lock();
|
||||||
|
window.destroy();
|
||||||
|
}
|
||||||
|
lock.windows.remove(&event.window);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Event::Error(error) => {
|
||||||
|
log::error!("X11 error {:?}", error);
|
||||||
|
}
|
||||||
|
_ => {}
|
||||||
|
}
|
||||||
|
|
||||||
|
let lock = self.0.lock();
|
||||||
|
event_option = lock.x11_connection.poll_for_event().unwrap();
|
||||||
|
}
|
||||||
|
|
||||||
|
for x11_window in need_repaint.drain() {
|
||||||
|
let lock = self.0.lock();
|
||||||
|
let mut window = lock.windows[&x11_window].lock();
|
||||||
|
window.paint();
|
||||||
|
lock.x11_connection.flush().unwrap();
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn quit(&self) {}
|
fn quit(&self) {}
|
||||||
|
@ -118,14 +196,19 @@ impl Platform for LinuxPlatform {
|
||||||
handle: AnyWindowHandle,
|
handle: AnyWindowHandle,
|
||||||
options: WindowOptions,
|
options: WindowOptions,
|
||||||
) -> Box<dyn PlatformWindow> {
|
) -> Box<dyn PlatformWindow> {
|
||||||
let lock = self.0.lock();
|
let mut lock = self.0.lock();
|
||||||
Box::new(LinuxWindow::new(
|
let win_id = lock.x11_connection.generate_id().unwrap();
|
||||||
|
|
||||||
|
let window_ptr = LinuxWindowState::new_ptr(
|
||||||
options,
|
options,
|
||||||
handle,
|
handle,
|
||||||
&lock.x11_connection,
|
&lock.x11_connection,
|
||||||
lock.x11_root_index,
|
lock.x11_root_index,
|
||||||
&lock.gpu,
|
win_id,
|
||||||
))
|
&lock.atoms,
|
||||||
|
);
|
||||||
|
lock.windows.insert(win_id, window_ptr.clone());
|
||||||
|
Box::new(LinuxWindow(window_ptr))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn set_display_link_output_callback(
|
fn set_display_link_output_callback(
|
||||||
|
|
|
@ -1,8 +1,9 @@
|
||||||
|
use super::BladeRenderer;
|
||||||
use crate::{
|
use crate::{
|
||||||
px, AnyWindowHandle, AtlasKey, AtlasTextureId, AtlasTile, BladeAtlas, Bounds, KeyDownEvent,
|
px, AnyWindowHandle, AtlasKey, AtlasTextureId, AtlasTile, BladeAtlas, Bounds, KeyDownEvent,
|
||||||
Keystroke, LinuxDisplay, Pixels, PlatformAtlas, PlatformDisplay, PlatformInput,
|
Keystroke, LinuxDisplay, Pixels, PlatformAtlas, PlatformDisplay, PlatformInput,
|
||||||
PlatformInputHandler, PlatformWindow, Point, Size, TileId, WindowAppearance, WindowBounds,
|
PlatformInputHandler, PlatformWindow, Point, Size, TileId, WindowAppearance, WindowBounds,
|
||||||
WindowOptions,
|
WindowOptions, WmAtoms,
|
||||||
};
|
};
|
||||||
use collections::HashMap;
|
use collections::HashMap;
|
||||||
use parking_lot::Mutex;
|
use parking_lot::Mutex;
|
||||||
|
@ -21,45 +22,37 @@ use x11rb::{
|
||||||
|
|
||||||
pub(crate) struct LinuxWindowState {
|
pub(crate) struct LinuxWindowState {
|
||||||
display: Rc<dyn PlatformDisplay>,
|
display: Rc<dyn PlatformDisplay>,
|
||||||
win_id: u32,
|
x11_window: u32,
|
||||||
|
window_bounds: WindowBounds,
|
||||||
|
content_size: Size<Pixels>,
|
||||||
sprite_atlas: Arc<BladeAtlas>,
|
sprite_atlas: Arc<BladeAtlas>,
|
||||||
|
renderer: BladeRenderer,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub(crate) type LinuxWindowStatePtr = Arc<Mutex<LinuxWindowState>>;
|
||||||
#[derive(Clone)]
|
#[derive(Clone)]
|
||||||
pub(crate) struct LinuxWindow(pub(crate) Arc<Mutex<LinuxWindowState>>);
|
pub(crate) struct LinuxWindow(pub(crate) LinuxWindowStatePtr);
|
||||||
|
|
||||||
impl LinuxWindow {
|
impl LinuxWindowState {
|
||||||
pub fn new(
|
pub fn new_ptr(
|
||||||
options: WindowOptions,
|
options: WindowOptions,
|
||||||
handle: AnyWindowHandle,
|
handle: AnyWindowHandle,
|
||||||
x11_connection: &RustConnection,
|
x11_connection: &RustConnection,
|
||||||
x11_main_screen_index: usize,
|
x11_main_screen_index: usize,
|
||||||
gpu: &Arc<blade::Context>,
|
x11_window: u32,
|
||||||
) -> Self {
|
atoms: &WmAtoms,
|
||||||
|
) -> LinuxWindowStatePtr {
|
||||||
let x11_screen_index = options
|
let x11_screen_index = options
|
||||||
.display_id
|
.display_id
|
||||||
.map_or(x11_main_screen_index, |did| did.0 as usize);
|
.map_or(x11_main_screen_index, |did| did.0 as usize);
|
||||||
let screen = &x11_connection.setup().roots[x11_screen_index];
|
let screen = &x11_connection.setup().roots[x11_screen_index];
|
||||||
|
|
||||||
let win_id = x11_connection.generate_id().unwrap();
|
|
||||||
let win_aux = CreateWindowAux::new()
|
let win_aux = CreateWindowAux::new()
|
||||||
.event_mask(
|
.event_mask(
|
||||||
EventMask::EXPOSURE | EventMask::STRUCTURE_NOTIFY | EventMask::POINTER_MOTION,
|
EventMask::EXPOSURE | EventMask::STRUCTURE_NOTIFY | EventMask::POINTER_MOTION,
|
||||||
)
|
)
|
||||||
.background_pixel(screen.white_pixel);
|
.background_pixel(screen.white_pixel);
|
||||||
|
|
||||||
let wm_protocols = x11_connection
|
|
||||||
.intern_atom(false, b"WM_PROTOCOLS")
|
|
||||||
.unwrap()
|
|
||||||
.reply()
|
|
||||||
.unwrap()
|
|
||||||
.atom;
|
|
||||||
let wm_delete_window = x11_connection
|
|
||||||
.intern_atom(false, b"WM_DELETE_WINDOW")
|
|
||||||
.unwrap()
|
|
||||||
.reply()
|
|
||||||
.unwrap()
|
|
||||||
.atom;
|
|
||||||
let (bound_x, bound_y, bound_width, bound_height) = match options.bounds {
|
let (bound_x, bound_y, bound_width, bound_height) = match options.bounds {
|
||||||
WindowBounds::Fullscreen | WindowBounds::Maximized => {
|
WindowBounds::Fullscreen | WindowBounds::Maximized => {
|
||||||
(0, 0, screen.width_in_pixels, screen.height_in_pixels)
|
(0, 0, screen.width_in_pixels, screen.height_in_pixels)
|
||||||
|
@ -75,7 +68,7 @@ impl LinuxWindow {
|
||||||
x11_connection
|
x11_connection
|
||||||
.create_window(
|
.create_window(
|
||||||
x11rb::COPY_DEPTH_FROM_PARENT,
|
x11rb::COPY_DEPTH_FROM_PARENT,
|
||||||
win_id,
|
x11_window,
|
||||||
screen.root,
|
screen.root,
|
||||||
bound_x,
|
bound_x,
|
||||||
bound_y,
|
bound_y,
|
||||||
|
@ -93,7 +86,7 @@ impl LinuxWindow {
|
||||||
x11_connection
|
x11_connection
|
||||||
.change_property8(
|
.change_property8(
|
||||||
PropMode::REPLACE,
|
PropMode::REPLACE,
|
||||||
win_id,
|
x11_window,
|
||||||
AtomEnum::WM_NAME,
|
AtomEnum::WM_NAME,
|
||||||
AtomEnum::STRING,
|
AtomEnum::STRING,
|
||||||
title.as_bytes(),
|
title.as_bytes(),
|
||||||
|
@ -104,30 +97,63 @@ impl LinuxWindow {
|
||||||
x11_connection
|
x11_connection
|
||||||
.change_property32(
|
.change_property32(
|
||||||
PropMode::REPLACE,
|
PropMode::REPLACE,
|
||||||
win_id,
|
x11_window,
|
||||||
wm_protocols,
|
atoms.protocols,
|
||||||
AtomEnum::ATOM,
|
AtomEnum::ATOM,
|
||||||
&[wm_delete_window],
|
&[atoms.delete_window],
|
||||||
)
|
)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
x11_connection.map_window(win_id).unwrap();
|
x11_connection.map_window(x11_window).unwrap();
|
||||||
|
x11_connection.flush().unwrap();
|
||||||
|
|
||||||
Self(Arc::new(Mutex::new(LinuxWindowState {
|
let gpu = Arc::new(
|
||||||
|
unsafe {
|
||||||
|
blade::Context::init(blade::ContextDesc {
|
||||||
|
validation: cfg!(debug_assertions),
|
||||||
|
capture: false,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
.unwrap(),
|
||||||
|
);
|
||||||
|
|
||||||
|
Arc::new(Mutex::new(Self {
|
||||||
display: Rc::new(LinuxDisplay::new(x11_connection, x11_screen_index)),
|
display: Rc::new(LinuxDisplay::new(x11_connection, x11_screen_index)),
|
||||||
win_id,
|
x11_window,
|
||||||
sprite_atlas: Arc::new(BladeAtlas::new(gpu)),
|
window_bounds: options.bounds,
|
||||||
})))
|
content_size: Size {
|
||||||
|
width: Pixels(bound_width as f32),
|
||||||
|
height: Pixels(bound_height as f32),
|
||||||
|
},
|
||||||
|
sprite_atlas: Arc::new(BladeAtlas::new(&gpu)),
|
||||||
|
renderer: BladeRenderer::new(gpu),
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn resize(&mut self, width: u16, height: u16) {
|
||||||
|
self.content_size = Size {
|
||||||
|
width: Pixels(width as f32),
|
||||||
|
height: Pixels(height as f32),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn destroy(&mut self) {
|
||||||
|
self.sprite_atlas.destroy();
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn paint(&mut self) {
|
||||||
|
//TODO
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl PlatformWindow for LinuxWindow {
|
impl PlatformWindow for LinuxWindow {
|
||||||
fn bounds(&self) -> WindowBounds {
|
fn bounds(&self) -> WindowBounds {
|
||||||
unimplemented!()
|
//TODO: update when window moves
|
||||||
|
self.0.lock().window_bounds
|
||||||
}
|
}
|
||||||
|
|
||||||
fn content_size(&self) -> Size<Pixels> {
|
fn content_size(&self) -> Size<Pixels> {
|
||||||
unimplemented!()
|
self.0.lock().content_size
|
||||||
}
|
}
|
||||||
|
|
||||||
fn scale_factor(&self) -> f32 {
|
fn scale_factor(&self) -> f32 {
|
||||||
|
|
Loading…
Reference in a new issue