Skip to content

Commit 4d539f6

Browse files
committed
wip
1 parent a190acb commit 4d539f6

5 files changed

Lines changed: 117 additions & 24 deletions

File tree

Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ dpi = "0.1.2"
4040
tracing = { version = "0.1", optional = true }
4141

4242
[target.'cfg(target_os="linux")'.dependencies]
43-
x11rb = { version = "0.13.2", features = ["cursor", "resource_manager", "allow-unsafe-code", "dl-libxcb"], default-features = false }
43+
x11rb = { version = "0.13.2", features = ["cursor", "present", "dri3", "resource_manager", "allow-unsafe-code", "dl-libxcb"], default-features = false }
4444
xkbcommon-dl = { version = "0.4.2", features = ["x11"] }
4545
x11-dl = { version = "2.21.0" }
4646
calloop = "0.14.4"

examples/test-frame-pacing/src/main.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -132,6 +132,7 @@ impl WindowHandler for FramePacingTest {
132132
}
133133

134134
fn on_event(&self, event: Event) -> EventStatus {
135+
dbg!(&event);
135136
if let Event::Keyboard(KeyboardEvent { key, state: KeyState::Down, .. }) = event {
136137
match key {
137138
Key::Named(NamedKey::ArrowLeft) => {
@@ -154,7 +155,7 @@ fn main() -> Result<(), baseview::Error> {
154155
let window_open_options = WindowSettings::new()
155156
.with_title("Femtovg on Baseview")
156157
.with_size(LogicalSize::new(512, 512))
157-
.with_gl_config(GlConfig { alpha_bits: 8, ..GlConfig::default() });
158+
.with_gl_config(GlConfig { alpha_bits: 8, vsync: true, ..GlConfig::default() });
158159

159160
Window::create(window_open_options, FramePacingTest::new)?.run_until_closed()?;
160161
Ok(())

src/platform/x11/event_loop.rs

Lines changed: 42 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,8 @@ pub(crate) struct EventLoop {
6262

6363
response_sender: mpsc::Sender<WindowThreadResponseMessage>,
6464
main_thread: Option<MainThreadCaller>,
65+
66+
last_rendered_msc: Option<u64>,
6567
}
6668

6769
const FRAME_INTERVAL: Duration = Duration::from_millis(15);
@@ -75,9 +77,9 @@ impl EventLoop {
7577
) -> Result<Self, Error> {
7678
let loop_handle = inner.handle();
7779

78-
loop_handle
79-
.insert_source(Timer::from_duration(FRAME_INTERVAL), |i, _, e| e.handle_frame(i))
80-
.map_err(|e| e.error)?;
80+
/*loop_handle
81+
.insert_source(Timer::from_duration(FRAME_INTERVAL), |i, _, e| e.handle_frame(i))
82+
.map_err(|e| e.error)?;*/
8183

8284
loop_handle
8385
.insert_source(
@@ -101,6 +103,7 @@ impl EventLoop {
101103

102104
window,
103105
response_sender,
106+
last_rendered_msc: None,
104107
})
105108
}
106109

@@ -111,7 +114,18 @@ impl EventLoop {
111114
// when they've all been coalesced.
112115
self.new_physical_size = None;
113116

117+
let mut current_sequence_number = None;
118+
114119
while let Some(event) = self.window.connection.conn.poll_for_event()? {
120+
let sequence = event.wire_sequence_number();
121+
122+
if sequence == current_sequence_number {
123+
continue;
124+
}
125+
126+
current_sequence_number = sequence;
127+
128+
//dbg!(event.wire_sequence_number());
115129
self.handle_xcb_event(event)?;
116130
}
117131

@@ -207,7 +221,10 @@ impl EventLoop {
207221
Ok(())
208222
}
209223
WindowThreadRequest::Show => {
224+
self.window.xcb_window.present_select_input()?.unwrap(); // TODO: unwrap: fallback to timer
210225
self.window.xcb_window.map_window()?.check()?;
226+
self.window.xcb_window.present_notify(0)?.check().unwrap(); // TODO: unwrap
227+
self.window.connection.conn.flush()?;
211228
Ok(())
212229
}
213230
WindowThreadRequest::Hide => {
@@ -330,6 +347,7 @@ impl EventLoop {
330347
}
331348

332349
XEvent::ConfigureNotify(event) => {
350+
//dbg!(event.width, event.height);
333351
let new_physical_size = PhysicalSize::new(event.width, event.height);
334352

335353
if self.new_physical_size.is_some() || new_physical_size != self.window.get_size() {
@@ -406,8 +424,9 @@ impl EventLoop {
406424
self.handle_event(ev);
407425
}
408426

409-
XEvent::FocusIn(_) => {
427+
XEvent::FocusIn(e) => {
410428
self.window.is_focused.set(true);
429+
dbg!(e.sequence);
411430
self.handle_event(Event::Window(WindowEvent::Focused));
412431
}
413432

@@ -416,6 +435,25 @@ impl EventLoop {
416435
self.handle_event(Event::Window(WindowEvent::Unfocused));
417436
}
418437

438+
XEvent::PresentCompleteNotify(e) => {
439+
//dbg!(e.msc);
440+
if Some(e.msc) != self.last_rendered_msc {
441+
//return Ok(());
442+
443+
//dbg!(e.msc, self.last_rendered_msc);
444+
445+
if let Err(e) = self.handler.on_frame() {
446+
self.run_error = Some(e.into());
447+
self.stop_now();
448+
} else {
449+
self.last_rendered_msc = Some(e.msc);
450+
//dbg!("Present_notify");
451+
}
452+
}
453+
self.window.xcb_window.present_notify(e.msc.wrapping_add(1))?; // TODO: unwrap
454+
self.window.connection.conn.flush()?;
455+
}
456+
419457
_ => {}
420458
}
421459

src/platform/x11/xcb_connection.rs

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
11
use std::cell::RefCell;
22
use std::collections::hash_map::{Entry, HashMap};
33
use std::sync::Arc;
4-
use x11rb::connection::Connection;
4+
use x11rb::connection::{Connection, RequestConnection};
55
use x11rb::cursor::Handle as CursorHandle;
6+
use x11rb::protocol::present;
67
use x11rb::protocol::xproto::{self, Cursor, Screen};
78
use x11rb::resource_manager;
89

@@ -50,6 +51,8 @@ pub struct X11Connection {
5051
pub(crate) resources: resource_manager::Database,
5152
pub(crate) cursor_handle: CursorHandle,
5253
pub(crate) cursor_cache: RefCell<HashMap<MouseCursor, u32>>,
54+
55+
pub(crate) present_supported: bool,
5356
}
5457

5558
impl X11Connection {
@@ -62,12 +65,15 @@ impl X11Connection {
6265
let resources = resource_manager::new_from_default(xcb_conn)?;
6366
let cursor_handle = CursorHandle::new(xcb_conn, screen as usize, &resources)?.reply()?;
6467

68+
let present_supported = conn.extension_information(present::X11_EXTENSION_NAME)?.is_some();
69+
6570
Ok(Self {
6671
conn: Arc::new(conn),
6772
atoms,
6873
resources,
6974
cursor_handle,
7075
cursor_cache: RefCell::new(HashMap::new()),
76+
present_supported,
7177
})
7278
}
7379

src/platform/x11/xcb_window.rs

Lines changed: 65 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@ use x11rb::connection::Connection;
88
use x11rb::cookie::VoidCookie;
99
use x11rb::errors::{ConnectionError, ReplyOrIdError};
1010
use x11rb::properties::WmSizeHints;
11+
use x11rb::protocol::present;
12+
use x11rb::protocol::present::ConnectionExt;
1113
use x11rb::protocol::xproto::{
1214
AtomEnum, ConfigureWindowAux, ConnectionExt as _, CreateWindowAux, EventMask, PropMode,
1315
WindowClass,
@@ -18,6 +20,7 @@ use x11rb::xcb_ffi::XCBConnection;
1820
pub struct XcbWindow {
1921
connection: Rc<X11Connection>,
2022
window_id: NonZeroU32,
23+
present_notify_event_id: Option<NonZeroU32>,
2124
}
2225

2326
impl XcbWindow {
@@ -59,15 +62,39 @@ impl XcbWindow {
5962
.border_pixel(0),
6063
)?;
6164

62-
Ok(Self { window_id, connection })
65+
let present_notify_event_id = if !connection.present_supported {
66+
None
67+
} else {
68+
let Some(event_id) = NonZero::new(connection.conn.generate_id()?) else {
69+
unreachable!();
70+
};
71+
72+
Some(event_id)
73+
};
74+
75+
Ok(Self { window_id, connection, present_notify_event_id })
76+
}
77+
78+
pub fn present_select_input(
79+
&self,
80+
) -> Result<Option<VoidCookie<'_, XCBConnection>>, ConnectionError> {
81+
let Some(event_id) = self.present_notify_event_id else {
82+
return Ok(None);
83+
};
84+
85+
Ok(Some(self.connection.conn.present_select_input(
86+
event_id.get(),
87+
self.window_id.get(),
88+
present::EventMask::COMPLETE_NOTIFY,
89+
)?))
6390
}
6491

65-
pub fn map_window(&self) -> Result<VoidCookie<'_, XCBConnection>, ReplyOrIdError> {
66-
Ok(self.connection.conn.map_window(self.window_id.get())?)
92+
pub fn map_window(&self) -> Result<VoidCookie<'_, XCBConnection>, ConnectionError> {
93+
self.connection.conn.map_window(self.window_id.get())
6794
}
6895

69-
pub fn unmap_window(&self) -> Result<VoidCookie<'_, XCBConnection>, ReplyOrIdError> {
70-
Ok(self.connection.conn.unmap_window(self.window_id.get())?)
96+
pub fn unmap_window(&self) -> Result<VoidCookie<'_, XCBConnection>, ConnectionError> {
97+
self.connection.conn.unmap_window(self.window_id.get())
7198
}
7299

73100
pub fn resize(
@@ -90,41 +117,51 @@ impl XcbWindow {
90117
)
91118
}
92119

93-
pub fn set_title(&self, title: &str) -> Result<VoidCookie<'_, XCBConnection>, ReplyOrIdError> {
94-
Ok(self.connection.conn.change_property8(
120+
pub fn set_title(&self, title: &str) -> Result<VoidCookie<'_, XCBConnection>, ConnectionError> {
121+
self.connection.conn.change_property8(
95122
PropMode::REPLACE,
96123
self.window_id.get(),
97124
AtomEnum::WM_NAME,
98125
AtomEnum::STRING,
99126
title.as_bytes(),
100-
)?)
127+
)
101128
}
102129

103-
pub fn enable_wm_protocols(&self) -> Result<VoidCookie<'_, XCBConnection>, ReplyOrIdError> {
104-
Ok(self.connection.conn.change_property32(
130+
pub fn enable_wm_protocols(&self) -> Result<VoidCookie<'_, XCBConnection>, ConnectionError> {
131+
self.connection.conn.change_property32(
105132
PropMode::REPLACE,
106133
self.window_id.get(),
107134
self.connection.atoms.WM_PROTOCOLS,
108135
AtomEnum::ATOM,
109136
&[self.connection.atoms.WM_DELETE_WINDOW],
110-
)?)
137+
)
111138
}
112139

113-
pub fn enable_dnd_protocols(&self) -> Result<VoidCookie<'_, XCBConnection>, ReplyOrIdError> {
114-
Ok(self.connection.conn.change_property32(
140+
pub fn enable_dnd_protocols(&self) -> Result<VoidCookie<'_, XCBConnection>, ConnectionError> {
141+
self.connection.conn.change_property32(
115142
PropMode::REPLACE,
116143
self.window_id.get(),
117144
self.connection.atoms.XdndAware,
118145
AtomEnum::ATOM,
119146
&[5u32], // Latest version; hasn't changed since 2002
120-
)?)
147+
)
121148
}
122149

123150
pub fn set_size_hints(
124151
&self, size_hints: WmSizeHints,
125-
) -> Result<VoidCookie<'_, XCBConnection>, ReplyOrIdError> {
126-
Ok(size_hints
127-
.set_normal_hints(&self.connection.conn as &XCBConnection, self.window_id.get())?)
152+
) -> Result<VoidCookie<'_, XCBConnection>, ConnectionError> {
153+
size_hints.set_normal_hints(&self.connection.conn as &XCBConnection, self.window_id.get())
154+
}
155+
156+
pub fn present_supported(&self) -> bool {
157+
self.present_notify_event_id.is_some()
158+
}
159+
160+
pub fn present_notify(
161+
&self, target_msc: u64,
162+
) -> Result<VoidCookie<'_, XCBConnection>, ConnectionError> {
163+
//dbg!(target_msc);
164+
self.connection.conn.present_notify_msc(self.window_id.get(), 0, target_msc, 1, 0)
128165
}
129166

130167
#[inline]
@@ -135,6 +172,17 @@ impl XcbWindow {
135172

136173
impl Drop for XcbWindow {
137174
fn drop(&mut self) {
175+
if let Some(event_id) = self.present_notify_event_id {
176+
match self.connection.conn.present_select_input(
177+
event_id.get(),
178+
self.window_id.get(),
179+
present::EventMask::NO_EVENT,
180+
) {
181+
Err(e) => crate::warn!("Failed to send request to switch XPresent off: {}", e),
182+
Ok(cookie) => cookie.check_warn(),
183+
}
184+
}
185+
138186
match self.connection.conn.destroy_window(self.window_id.get()) {
139187
Err(e) => crate::warn!("Failed to send request to destroy X window: {}", e),
140188
Ok(cookie) => cookie.check_warn(),

0 commit comments

Comments
 (0)