logiguard fork: GPUI xdg-activation keyboard-focus serial fix
Some checks failed
Update All Top Ranking Issues / update_top_ranking_issues (push) Has been cancelled
Triage Project Sync (#84) / Sync triage project (push) Has been cancelled
release_nightly / notify_on_failure (push) Has been cancelled
release_nightly / check_style (push) Has been cancelled
release_nightly / run_tests_windows (push) Has been cancelled
release_nightly / clippy_windows (push) Has been cancelled
release_nightly / bundle_linux_aarch64 (push) Has been cancelled
release_nightly / bundle_linux_x86_64 (push) Has been cancelled
release_nightly / bundle_mac_aarch64 (push) Has been cancelled
release_nightly / bundle_mac_x86_64 (push) Has been cancelled
release_nightly / bundle_windows_aarch64 (push) Has been cancelled
release_nightly / bundle_windows_x86_64 (push) Has been cancelled
release_nightly / build_nix_linux_x86_64 (push) Has been cancelled
release_nightly / build_nix_mac_aarch64 (push) Has been cancelled
release_nightly / update_nightly_tag (push) Has been cancelled
Hotfix Review Monitor / check-hotfix-reviews (push) Has been cancelled
Stale PR Review Reminder / check-stale-prs (push) Has been cancelled
Update Weekly Top Ranking Issues / update_top_ranking_issues (push) Has been cancelled
Bump collab-staging Tag / update-collab-staging-tag (push) Has been cancelled
compliance_check / scheduled_compliance_check (push) Has been cancelled

Single-commit orphan branch: full zed-industries/zed @ 8c74db0 source tree
with a 3-file patch applied (no upstream history).

Patch (crates/gpui_linux/src/linux/wayland/):
  - serial.rs: add SerialKind::KeyboardEnter
  - client.rs: store wl_keyboard.enter serial; add latest_serial_of()
  - window.rs: activate() uses keyboard-enter serial (Mutter focus gate)

Mutter honors window activation only when the token carries the keyboard-
focus serial from wl_keyboard.enter; GPUI used a stale mouse-press serial.
See docs/tray-window-focus-wayland.md in logiguard.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
Mohamad Khani
2026-07-14 02:22:17 +03:30
commit b72a46db68
3984 changed files with 1583326 additions and 0 deletions

View File

@@ -0,0 +1,23 @@
[package]
name = "line_ending_selector"
version = "0.1.0"
edition.workspace = true
publish.workspace = true
license = "GPL-3.0-or-later"
[lints]
workspace = true
[lib]
path = "src/line_ending_selector.rs"
doctest = false
[dependencies]
editor.workspace = true
gpui.workspace = true
language.workspace = true
picker.workspace = true
project.workspace = true
ui.workspace = true
util.workspace = true
workspace.workspace = true

View File

@@ -0,0 +1 @@
../../LICENSE-GPL

View File

@@ -0,0 +1,79 @@
use editor::Editor;
use gpui::{App, Entity, Subscription, WeakEntity};
use language::LineEnding;
use ui::{Tooltip, prelude::*};
use workspace::{
HideStatusItem, StatusBarSettings, StatusItemView, item::ItemHandle, item::Settings,
};
use crate::{LineEndingSelector, Toggle};
#[derive(Default)]
pub struct LineEndingIndicator {
line_ending: Option<LineEnding>,
active_editor: Option<WeakEntity<Editor>>,
_observe_active_editor: Option<Subscription>,
}
impl LineEndingIndicator {
fn update(&mut self, editor: Entity<Editor>, _: &mut Window, cx: &mut Context<Self>) {
self.line_ending = None;
self.active_editor = None;
if let Some(buffer) = editor.read(cx).active_buffer(cx) {
let line_ending = buffer.read(cx).line_ending();
self.line_ending = Some(line_ending);
self.active_editor = Some(editor.downgrade());
}
cx.notify();
}
}
impl Render for LineEndingIndicator {
fn render(&mut self, _window: &mut Window, cx: &mut Context<Self>) -> impl IntoElement {
if !StatusBarSettings::get_global(cx).line_endings_button {
return div();
}
div().when_some(self.line_ending.as_ref(), |el, line_ending| {
el.child(
Button::new("change-line-ending", line_ending.label())
.label_size(LabelSize::Small)
.on_click(cx.listener(|this, _, window, cx| {
if let Some(editor) = this.active_editor.as_ref() {
LineEndingSelector::toggle(editor, window, cx);
}
}))
.tooltip(|_window, cx| Tooltip::for_action("Select Line Ending", &Toggle, cx)),
)
})
}
}
impl StatusItemView for LineEndingIndicator {
fn set_active_pane_item(
&mut self,
active_pane_item: Option<&dyn ItemHandle>,
window: &mut Window,
cx: &mut Context<Self>,
) {
if let Some(editor) = active_pane_item.and_then(|item| item.downcast::<Editor>()) {
self._observe_active_editor = Some(cx.observe_in(&editor, window, Self::update));
self.update(editor, window, cx);
} else {
self.line_ending = None;
self._observe_active_editor = None;
}
cx.notify();
}
fn hide_setting(&self, _: &App) -> Option<HideStatusItem> {
Some(HideStatusItem::new(|settings| {
settings
.status_bar
.get_or_insert_default()
.line_endings_button = Some(false);
}))
}
}

View File

@@ -0,0 +1,192 @@
mod line_ending_indicator;
use editor::Editor;
use gpui::{DismissEvent, Entity, EventEmitter, FocusHandle, Focusable, Task, WeakEntity, actions};
use language::{Buffer, LineEnding};
pub use line_ending_indicator::LineEndingIndicator;
use picker::{Picker, PickerDelegate};
use project::Project;
use std::sync::Arc;
use ui::{ListItem, ListItemSpacing, prelude::*};
use util::ResultExt;
use workspace::ModalView;
actions!(
line_ending_selector,
[
/// Toggles the line ending selector modal.
Toggle
]
);
pub fn init(cx: &mut App) {
cx.observe_new(LineEndingSelector::register).detach();
}
pub struct LineEndingSelector {
picker: Entity<Picker<LineEndingSelectorDelegate>>,
}
impl LineEndingSelector {
fn register(editor: &mut Editor, _window: Option<&mut Window>, cx: &mut Context<Editor>) {
let editor_handle = cx.weak_entity();
editor
.register_action(move |_: &Toggle, window, cx| {
Self::toggle(&editor_handle, window, cx);
})
.detach();
}
fn toggle(editor: &WeakEntity<Editor>, window: &mut Window, cx: &mut App) {
let Some((workspace, buffer)) = editor
.update(cx, |editor, cx| {
Some((editor.workspace()?, editor.active_buffer(cx)?))
})
.ok()
.flatten()
else {
return;
};
workspace.update(cx, |workspace, cx| {
let project = workspace.project().clone();
workspace.toggle_modal(window, cx, move |window, cx| {
LineEndingSelector::new(buffer, project, window, cx)
});
})
}
fn new(
buffer: Entity<Buffer>,
project: Entity<Project>,
window: &mut Window,
cx: &mut Context<Self>,
) -> Self {
let line_ending = buffer.read(cx).line_ending();
let delegate =
LineEndingSelectorDelegate::new(cx.entity().downgrade(), buffer, project, line_ending);
let picker = cx.new(|cx| Picker::nonsearchable_uniform_list(delegate, window, cx));
Self { picker }
}
}
impl Render for LineEndingSelector {
fn render(&mut self, _window: &mut Window, _cx: &mut Context<Self>) -> impl IntoElement {
v_flex().w(rems(34.)).child(self.picker.clone())
}
}
impl Focusable for LineEndingSelector {
fn focus_handle(&self, cx: &App) -> FocusHandle {
self.picker.focus_handle(cx)
}
}
impl EventEmitter<DismissEvent> for LineEndingSelector {}
impl ModalView for LineEndingSelector {}
struct LineEndingSelectorDelegate {
line_ending_selector: WeakEntity<LineEndingSelector>,
buffer: Entity<Buffer>,
project: Entity<Project>,
line_ending: LineEnding,
matches: Vec<LineEnding>,
selected_index: usize,
}
impl LineEndingSelectorDelegate {
fn new(
line_ending_selector: WeakEntity<LineEndingSelector>,
buffer: Entity<Buffer>,
project: Entity<Project>,
line_ending: LineEnding,
) -> Self {
Self {
line_ending_selector,
buffer,
project,
line_ending,
matches: vec![LineEnding::Unix, LineEnding::Windows],
selected_index: 0,
}
}
}
impl PickerDelegate for LineEndingSelectorDelegate {
type ListItem = ListItem;
fn placeholder_text(&self, _window: &mut Window, _cx: &mut App) -> Arc<str> {
"Select a line ending…".into()
}
fn match_count(&self) -> usize {
self.matches.len()
}
fn confirm(&mut self, _: bool, window: &mut Window, cx: &mut Context<Picker<Self>>) {
if let Some(line_ending) = self.matches.get(self.selected_index) {
self.buffer.update(cx, |this, cx| {
this.set_line_ending(*line_ending, cx);
});
let buffer = self.buffer.clone();
let project = self.project.clone();
cx.defer(move |cx| {
project.update(cx, |this, cx| {
this.save_buffer(buffer, cx).detach();
});
});
}
self.dismissed(window, cx);
}
fn dismissed(&mut self, _: &mut Window, cx: &mut Context<Picker<Self>>) {
self.line_ending_selector
.update(cx, |_, cx| cx.emit(DismissEvent))
.log_err();
}
fn selected_index(&self) -> usize {
self.selected_index
}
fn set_selected_index(
&mut self,
ix: usize,
_window: &mut Window,
_: &mut Context<Picker<Self>>,
) {
self.selected_index = ix;
}
fn update_matches(
&mut self,
_query: String,
_window: &mut Window,
_cx: &mut Context<Picker<Self>>,
) -> gpui::Task<()> {
return Task::ready(());
}
fn render_match(
&self,
ix: usize,
selected: bool,
_: &mut Window,
_: &mut Context<Picker<Self>>,
) -> Option<Self::ListItem> {
let line_ending = self.matches.get(ix)?;
let label = line_ending.label();
let mut list_item = ListItem::new(ix)
.inset(true)
.spacing(ListItemSpacing::Sparse)
.toggle_state(selected)
.child(Label::new(label));
if &self.line_ending == line_ending {
list_item = list_item.end_slot(Icon::new(IconName::Check).color(Color::Muted));
}
Some(list_item)
}
}