Skip to content

Commit

Permalink
fix(deps): Use winit 0.28.6 (Wayland support) + new keyboard API and …
Browse files Browse the repository at this point in the history
…glutin 0.30.7 (#1789)

The exact version of winit is f7a400ddf6a7822a1ac0cf80ca805dcf3298872e and includes the big keyboard API refactoring, a very big effort by the winit team - rust-windowing/winit#2662. Additionally the actual Winit version is several versions newer than the previous one (0.24.0), so a lot of other things have also been fixed.

The biggest impact fix for Neovide is working Wayland support #1356
The winit update also fixes unhanded key up events #1631

There are probably also a lot of still unconfirmed fixes, and maybe also some new regression as with all big updates.

---------

Co-authored-by: Fred Sundvik <fsundvik@gmail.com>
Co-authored-by: rkuklik <90713105+rkuklik@users.noreply.github.com>
Co-authored-by: MultisampledNight <contact@multisamplednight.com>
Co-authored-by: Serhii Tereshchenko <serg.partizan@gmail.com>
  • Loading branch information
5 people committed Jun 14, 2023
1 parent b2d66d2 commit f412399
Show file tree
Hide file tree
Showing 8 changed files with 723 additions and 694 deletions.
1,169 changes: 554 additions & 615 deletions Cargo.lock

Large diffs are not rendered by default.

9 changes: 7 additions & 2 deletions Cargo.toml
Expand Up @@ -29,7 +29,8 @@ euclid = "0.22.7"
flexi_logger = { version = "0.22.3", default-features = false }
futures = "0.3.21"
gl = "0.14.0"
glutin = { git = "https://github.com/neovide/glutin", branch = "new-keyboard-all", features = ["serde"] }
glutin = "0.30.7"
glutin-winit = "0.3.0"
image = { version = "0.24.1", default-features = false, features = ["ico"] }
itertools = "0.10.5"
lazy_static = "1.4.0"
Expand All @@ -40,6 +41,7 @@ nvim-rs = { version = "0.5.0", features = ["use_tokio"] }
parking_lot = "0.12.0"
pin-project = "1.0.10"
rand = "0.8.5"
raw-window-handle = "0.5.0"
rmpv = "1.0.0"
serde = { version = "1.0.136", features = ["derive"] }
serde_json = "1.0.79"
Expand All @@ -51,7 +53,7 @@ toml = "0.7.3"
tracy-client-sys = { version = "0.19.0", optional = true }
unicode-segmentation = "1.9.0"
which = "4.2.5"
winit = { git = "https://github.com/neovide/winit", branch = "new-keyboard-all" }
winit = { version = "=0.28.6", features = ["serde"] }
xdg = "2.4.1"

[dev-dependencies]
Expand Down Expand Up @@ -101,3 +103,6 @@ long_description = """
This is a simple graphical user interface for Neovim. Where possible there are some graphical improvements, but it should act functionally like the terminal UI.
"""
osx_minimum_system_version = "10.11"

[patch.crates-io]
winit = { git = "https://github.com/rust-windowing/winit", branch= "master", version = "0.28.6" }
131 changes: 102 additions & 29 deletions src/renderer/opengl.rs
@@ -1,40 +1,113 @@
use std::ffi::{c_void, CStr};
use std::num::NonZeroU32;

use crate::cmd_line::CmdLineSettings;

use glutin::{ContextBuilder, GlProfile, PossiblyCurrent, WindowedContext};
use gl::MAX_RENDERBUFFER_SIZE;
use glutin::surface::SwapInterval;
use glutin::{
config::{Config, ConfigTemplateBuilder},
context::{ContextAttributesBuilder, GlProfile, PossiblyCurrentContext},
display::GetGlDisplay,
prelude::*,
surface::{Surface, SurfaceAttributesBuilder, WindowSurface},
};
use glutin_winit::DisplayBuilder;
use raw_window_handle::HasRawWindowHandle;
use winit::dpi::PhysicalSize;
use winit::{
event_loop::EventLoop,
window::{Window, WindowBuilder},
};

pub struct Context {
surface: Surface<WindowSurface>,
context: PossiblyCurrentContext,
window: Window,
config: Config,
}

use winit::{event_loop::EventLoop, window::WindowBuilder};
pub fn clamp_render_buffer_size(size: PhysicalSize<u32>) -> PhysicalSize<u32> {
PhysicalSize::new(
size.width.clamp(1, MAX_RENDERBUFFER_SIZE),
size.height.clamp(1, MAX_RENDERBUFFER_SIZE),
)
}

impl Context {
pub fn window(&self) -> &Window {
&self.window
}
pub fn resize(&self, width: NonZeroU32, height: NonZeroU32) {
GlSurface::resize(&self.surface, &self.context, width, height)
}
pub fn swap_buffers(&self) -> glutin::error::Result<()> {
GlSurface::swap_buffers(&self.surface, &self.context)
}
pub fn get_proc_address(&self, addr: &CStr) -> *const c_void {
GlDisplay::get_proc_address(&self.surface.display(), addr)
}
pub fn get_config(&self) -> &Config {
&self.config
}

pub fn get_render_target_size(&self) -> PhysicalSize<u32> {
clamp_render_buffer_size(self.window.inner_size())
}
}

pub type Context = WindowedContext<glutin::PossiblyCurrent>;
fn gen_config(mut config_iterator: Box<dyn Iterator<Item = Config> + '_>) -> Config {
config_iterator.next().unwrap()
}

pub fn build_context<TE>(
cmd_line_settings: &CmdLineSettings,
winit_window_builder: WindowBuilder,
event_loop: &EventLoop<TE>,
) -> WindowedContext<PossiblyCurrent> {
let builder = ContextBuilder::new()
.with_pixel_format(24, 8)
.with_stencil_buffer(8)
.with_gl_profile(GlProfile::Core)
.with_srgb(cmd_line_settings.srgb)
.with_vsync(cmd_line_settings.vsync);

let ctx = match builder
.clone()
.build_windowed(winit_window_builder.clone(), event_loop)
{
Ok(ctx) => ctx,
Err(err) => {
// haven't found any sane way to actually match on the pattern rabbithole CreationError
// provides, so here goes nothing
if err.to_string().contains("vsync") {
builder
.with_vsync(false)
.build_windowed(winit_window_builder, event_loop)
.unwrap()
} else {
panic!("{}", err);
}
}
) -> Context {
let template_builder = ConfigTemplateBuilder::new()
.with_stencil_size(8)
.with_transparency(true);
let (window, config) = DisplayBuilder::new()
.with_window_builder(Some(winit_window_builder))
.build(event_loop, template_builder, gen_config)
.expect("Failed to create Window");
let window = window.expect("Could not create Window");

let gl_display = config.display();
let raw_window_handle = window.raw_window_handle();

let size = clamp_render_buffer_size(window.inner_size());

let surface_attributes = SurfaceAttributesBuilder::<WindowSurface>::new()
.with_srgb(Some(cmd_line_settings.srgb))
.build(
raw_window_handle,
NonZeroU32::new(size.width).unwrap(),
NonZeroU32::new(size.height).unwrap(),
);
let surface = unsafe { gl_display.create_window_surface(&config, &surface_attributes) }
.expect("Failed to create Windows Surface");

let context_attributes = ContextAttributesBuilder::new()
.with_profile(GlProfile::Core)
.build(Some(raw_window_handle));
let context = unsafe { gl_display.create_context(&config, &context_attributes) }
.expect("Failed to create OpenGL context")
.make_current(&surface)
.unwrap();

// NOTE: We don't care if these fails, the driver can override the SwapInterval in any case, so it needs to work in all cases
let _ = if cmd_line_settings.vsync {
surface.set_swap_interval(&context, SwapInterval::Wait(NonZeroU32::new(1).unwrap()))
} else {
surface.set_swap_interval(&context, SwapInterval::DontWait)
};
unsafe { ctx.make_current().unwrap() }

Context {
surface,
context,
window,
config,
}
}
8 changes: 6 additions & 2 deletions src/renderer/rendered_window.rs
Expand Up @@ -15,6 +15,9 @@ use crate::{
redraw_scheduler::REDRAW_SCHEDULER,
renderer::{animation_utils::*, GridRenderer, RendererSettings},
};
use winit::dpi::PhysicalSize;

use super::opengl::clamp_render_buffer_size;

#[derive(Clone, Debug)]
pub struct LineFragment {
Expand Down Expand Up @@ -58,12 +61,13 @@ pub struct WindowPadding {
pub bottom: u32,
}

fn build_window_surface(parent_canvas: &mut Canvas, pixel_size: (i32, i32)) -> Surface {
fn build_window_surface(parent_canvas: &mut Canvas, pixel_size: PhysicalSize<u32>) -> Surface {
let pixel_size = clamp_render_buffer_size(pixel_size);
let mut context = parent_canvas.recording_context().unwrap();
let budgeted = Budgeted::Yes;
let parent_image_info = parent_canvas.image_info();
let image_info = ImageInfo::new(
pixel_size,
(pixel_size.width as i32, pixel_size.height as i32),
parent_image_info.color_type(),
parent_image_info.alpha_type(),
parent_image_info.color_space(),
Expand Down
9 changes: 2 additions & 7 deletions src/window/draw_background.rs
Expand Up @@ -15,13 +15,8 @@ pub fn draw_background(window: &Window) {
autoreleasepool(|| unsafe {
let [red, green, blue, alpha] = color.to_array();
let ns_window: id = window.ns_window() as id;
let ns_background = NSColor::colorWithSRGBRed_green_blue_alpha_(
nil,
red.into(),
green.into(),
blue.into(),
alpha.into(),
);
let ns_background =
NSColor::colorWithSRGBRed_green_blue_alpha_(nil, red, green, blue, alpha);
ns_window.setBackgroundColor_(ns_background);
ns_window.setTitlebarAppearsTransparent_(YES);
});
Expand Down
40 changes: 23 additions & 17 deletions src/window/keyboard_manager.rs
Expand Up @@ -5,7 +5,7 @@ use crate::{
window::KeyboardSettings,
};
use winit::{
event::{ElementState, Event, KeyEvent, WindowEvent},
event::{ElementState, Event, Ime, KeyEvent, WindowEvent},
keyboard::{Key, Key::Dead},
platform::modifier_supplement::KeyEventExtModifierSupplement,
};
Expand Down Expand Up @@ -60,7 +60,7 @@ impl KeyboardManager {
.push(InputEvent::KeyEvent(key_event.clone()));
}
Event::WindowEvent {
event: WindowEvent::ReceivedImeText(string),
event: WindowEvent::Ime(Ime::Commit(string)),
..
} => {
self.queued_input_events
Expand All @@ -72,10 +72,11 @@ impl KeyboardManager {
} => {
// Record the modifier states so that we can properly add them to the keybinding
// text
self.shift = modifiers.shift_key();
self.ctrl = modifiers.control_key();
self.alt = modifiers.alt_key();
self.logo = modifiers.super_key();
let state = modifiers.state();
self.shift = state.shift_key();
self.ctrl = state.control_key();
self.alt = state.alt_key();
self.logo = state.super_key();
}
Event::MainEventsCleared => {
// If the window wasn't just focused.
Expand Down Expand Up @@ -130,7 +131,7 @@ impl KeyboardManager {
fn maybe_get_keybinding(&self, key_event: &KeyEvent) -> Option<String> {
// Determine if this key event represents a key which won't ever
// present text.
if let Some(key_text) = is_control_key(key_event.logical_key) {
if let Some(key_text) = is_control_key(&key_event.logical_key) {
if self.prev_dead_key.is_some() {
//recover dead key to normal character
let real_char = String::from(self.prev_dead_key.unwrap());
Expand All @@ -140,12 +141,12 @@ impl KeyboardManager {
}
} else {
let key_text = if self.prev_dead_key.is_none() {
key_event.text
key_event.text.as_ref().map(|text| text.as_str())
} else {
key_event.text_with_all_modifiers()
};
if let Some(original_key_text) = key_text {
let mut key_text = original_key_text;
let mut key_text = original_key_text.to_string();
if self.alt {
if let Some(modify) = key_event_text(key_event) {
key_text = modify;
Expand All @@ -154,10 +155,10 @@ impl KeyboardManager {
// This is not a control key, so we rely upon winit to determine if
// this is a deadkey or not.
let keybinding_string =
if let Some((escaped_text, use_shift)) = is_special(key_text) {
if let Some((escaped_text, use_shift)) = is_special(&key_text) {
self.format_special_key(use_shift, escaped_text)
} else {
self.format_normal_key(key_text)
self.format_normal_key(&key_text)
};

Some(keybinding_string)
Expand Down Expand Up @@ -209,17 +210,22 @@ fn use_alt(alt: bool) -> bool {
}

#[cfg(not(target_os = "macos"))]
fn key_event_text(key_event: &KeyEvent) -> Option<&str> {
key_event.key_without_modifiers().to_text()
fn key_event_text(key_event: &KeyEvent) -> Option<String> {
key_event
.key_without_modifiers()
.to_text()
.map(|text| text.to_string())
}

#[cfg(target_os = "macos")]
fn key_event_text(key_event: &KeyEvent) -> Option<&str> {
fn key_event_text(key_event: &KeyEvent) -> Option<String> {
let settings = SETTINGS.get::<KeyboardSettings>();
if settings.macos_alt_is_meta {
key_event.text
key_event.text.as_ref().map(|text| text.to_string())
} else {
key_event.text_with_all_modifiers()
key_event
.text_with_all_modifiers()
.map(|text| text.to_string())
}
}

Expand All @@ -231,7 +237,7 @@ fn or_empty(condition: bool, text: &str) -> &str {
}
}

fn is_control_key(key: Key<'static>) -> Option<&str> {
fn is_control_key(key: &Key) -> Option<&'static str> {
match key {
Key::Backspace => Some("BS"),
Key::Escape => Some("Esc"),
Expand Down
22 changes: 15 additions & 7 deletions src/window/mod.rs
Expand Up @@ -6,6 +6,8 @@ mod settings;
#[cfg(target_os = "macos")]
mod draw_background;

#[cfg(target_os = "linux")]
use std::env;
use std::time::{Duration, Instant};

use log::trace;
Expand All @@ -24,7 +26,9 @@ use winit::platform::macos::WindowBuilderExtMacOS;
use draw_background::draw_background;

#[cfg(target_os = "linux")]
use winit::platform::unix::WindowBuilderExtUnix;
use winit::platform::wayland::WindowBuilderExtWayland;
#[cfg(target_os = "linux")]
use winit::platform::x11::WindowBuilderExtX11;

use image::{load_from_memory, GenericImageView, Pixel};
use keyboard_manager::KeyboardManager;
Expand Down Expand Up @@ -392,12 +396,16 @@ pub fn create_window() {
}

#[cfg(target_os = "linux")]
let winit_window_builder = winit_window_builder
.with_app_id(cmd_line_settings.wayland_app_id.clone())
.with_class(
cmd_line_settings.x11_wm_class_instance.clone(),
cmd_line_settings.x11_wm_class.clone(),
);
let winit_window_builder = {
if env::var("WAYLAND_DISPLAY").is_ok() {
let app_id = &cmd_line_settings.wayland_app_id;
WindowBuilderExtWayland::with_name(winit_window_builder, "neovide", app_id.clone())
} else {
let class = &cmd_line_settings.x11_wm_class;
let instance = &cmd_line_settings.x11_wm_class_instance;
WindowBuilderExtX11::with_name(winit_window_builder, class, instance)
}
};

#[cfg(target_os = "macos")]
let winit_window_builder = winit_window_builder.with_accepts_first_mouse(false);
Expand Down

1 comment on commit f412399

@ttytm
Copy link
Contributor

@ttytm ttytm commented on f412399 Jun 14, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🚀🎉

Please sign in to comment.