T51: round 2 fix: on X11 the room above runs to the top of the RandR monitor holding most of the pane, as Windows and macOS pick a window's display, where the monitor under her bottom left corner put her over the gap beside a staggered monitor
This commit is contained in:
2 files changed
+44
-19
No files matched your search
@@ -109,4 +109,4 @@ ELYTerminal. A terminal on GPUI and Ely components. `PRD.md` says what it does;
|
||||
- 2026-10-18: herdr's shortcuts, which a user found dead, as kitty and Ghostty send them. herdr 0.9.5 asks for kitty's disambiguate, events and alternates on macOS and Linux, and takes `cmd+shift+ctrl+[` as `[` with Shift, Ctrl and Super, `CSI 91;14u`, the symbol as its alternate, `CSI 91:123;14u`, as Ghostty sends it; gpui named the key `{` and cleared Shift, so ELYTerminal sent `CSI 123;13u`, which herdr matches only to `cmd+ctrl+{`. Under kitty's flags a key Shift made a symbol of now goes as its own character with Shift, the symbol its alternate; legacy keys keep fixterms' symbol without Shift, as Ghostty's do. On macOS the key's own character comes from `UCKeyTranslate` on the event's key code, in the layout gpui reads: Command's while Command is down or while the plain one types no ASCII; AppKit's `charactersByApplyingModifiers:` ignores the layout for events built in-process. On Windows herdr asks for no kitty flags and reads the console's key records, and ConPTY asks every terminal for win32-input-mode, `CSI ? 9001 h`; so, as Windows Terminal does, each key that types no text goes as `CSI Vk ; Sc ; Uc ; Kd ; Cs ; Rc _`: the virtual key from gpui's name and the layout (`VkKeyScanW`), its scan code, what Windows types for it (`ToUnicode`, a dead key left as it is), down or up, and the modifier keys, locks and enhanced block from `GetKeyState`. kitty's flags go first, as in Windows Terminal; text stays text, so a repeat or a release goes as a record where its press did, a repeat that types after its modifiers came up too, and a release goes so only there; a full reset ends the mode, as Windows Terminal's does. A key that is down is known by its code, AppKit's key code on macOS and the virtual key on Windows, since gpui names a release by the modifiers left down: a release after Shift came up still goes out. Linux has only gpui's name. A console record holds no Windows key, so no Windows terminal passes herdr a `cmd` binding, and a keypad key goes as its main key, since gpui names it so. On Linux gpui keeps neither the key code nor its xkb state, and names a non-ASCII key by its place on a US keyboard, so a key's own character cannot be found: Shift with a symbol and Ctrl, Alt or Super goes as the symbol without Shift, which herdr matches to a binding on the symbol, `ctrl+{`.
|
||||
- 2026-10-18: inside herdr, herdr opens its panes' links, as the owner's Cmd-press and Ctrl-press ask. herdr 0.9.5 wraps a long URL at its pane's edge with no wrap flag and writes its panes' OSC 8 links to the terminal without their targets, so ELYTerminal's own finder opened a cut URL or nothing; herdr itself opens the link under a Ctrl-press, whole from its own pane, a wrapped URL or an OSC 8 target, and underlines it while Ctrl is held. So while herdr's client leads the session and tracks the mouse, every link press goes to herdr as the mouse report it is, and on macOS herdr hears Cmd as Ctrl, the wheel aside. herdr's rules come with it, by its design: while it takes the mouse it keeps its panes' OSC 8 targets from the terminal, so that it alone answers Ctrl-press, and it opens only `http` and `https` links; a `file://` link inside herdr stays shut, since the text the terminal sees may be the label of another link. Shift with the press, which takes the mouse back from a program, is the way past herdr its docs name: the terminal then opens what it sees, a `file://` link too. With herdr's mouse capture off, herdr writes its links' targets out and the terminal opens them itself. Elsewhere ELYTerminal opens the link itself, as before.
|
||||
- 2026-10-18: Ely peeks over the main window's top edge, which the owner asked for after the 0.1.3 film, her head and fingers drawn from the icon's own paths and cut at the edge she holds, in a transparent window of her own, 112 by 107 points with 16 above her for her hops, 140 in from the pane's left edge, less on a pane too narrow for that, so she holds its edge at any width, and the main window seems to hide the rest of her. Her window never takes the keys from the terminal: on macOS her panel becomes key only if needed, which gpui's view never asks; on Windows her window answers a press's activation with none, every other message passed on to gpui's own procedure; on X11 and Wayland a press moves no focus. gpui moves no window and places its popups only on Wayland, so each platform keeps her there its own way: on macOS gpui's popup panel is made the main window's child, at its level and in its Spaces, without a shadow, so AppKit moves it and hides it with the window; on Windows gpui's popup is owned by the main window and loses the topmost place gpui gives it, so it stays just above the main window and hides with it; gpui makes it an overlapped window, which Windows gives a caption and a frame, and opens it as large as the display when the middle of the box asked for lies off every display, as hers does over a window near the screen's top: in 0.1.4 a user found her at the bottom of a framed window the display's size, which took the clicks meant for the windows behind; so it becomes a bare popup, without caption, frame, corners or shadow, and the glue posts her window her bottom left corner on the pane's top edge as the main window moves, since Windows sends a new size at once and gpui hears it only outside its own updates, as gpui defers its own resize; her window sizes her at the DPI it has when the place lands, so her height is her own DPI's and her bottom the pane's top, whatever the two windows' DPI, and a place posted before her DPI changed sizes her at the new one; at a new DPI gpui takes the rect Windows suggests, and once the move that brought it is done she takes her box at that DPI on the corner her last place gave her, while that DPI is still hers and the box lies mostly on a display of it, as Windows reckons a window's display, else Windows' rect stands; so a keep changes no DPI and posts no other, where between two displays a corner kept always, or a keep whose DPI had passed while places piled up, set her DPI back, and back again; on X11 gpui's popup, which no window manager places, is moved onto the pane, stacked just above the window manager's frame as the window moves or comes forward, and unmapped while it hides; on Wayland she is gpui's anchored popup, an xdg_popup on the pane's top edge, which the compositor keeps there; gpui moves a popup's anchor never, so a new width that moves her place opens her again. She opens with the sessions' mood and moves all the time, as the films move her, which the owner asked for after 0.1.4, where she stood still but for a press. She is rigged from the icon's paths as the films rig her, in layers of one color, each an SVG mask gpui rasterizes once at its size and moves with a matrix of its own after its pixels are placed, so she moves between pixels; her head bobs with her breath while her neck, collar and hands stay on the edge, her side locks sway, her ahoge breathes, and she blinks on her own, at times twice. Her mood sets her act: calm, she glances left, then right, her head after her eyes, and dozes after 5 minutes, her eyes falling shut, her head and ahoge drooping, zs floating up; a program that works runs her eyes along the terminal's lines while her fingers tap by turns; one that waits on you widens her eyes, opens her mouth, rocks a mark beside her and hops her twice every 2.6 s; an error raises her brows and shakes her, more softly every 4.5 s after, while a drop runs down; a finish cheers her for 4 s with smiling eyes, a hop and sparkles; a press winks at you while a heart rises. Each act begins with her eyes opening and her mouth popping, and a change of act, a change of mood though the act stays, as inside a wink, and a press each ease her from the pose she was in, in 0.3 s, her brows with her, the marks she leaves fading. She draws at every frame the display gives her window while it shows, and gpui gives a hidden window none; her time is folded into each move's round in f64, so weeks of one mood keep her steps fine. Settings › Terminal › Ely turns her off; she is on by default.
|
||||
- 2026-10-19: Ely stands inside the main window, her bottom on its pane's bottom edge, as far in from the left, while her box would not fit above it, which the owner asked for after 0.1.4: on a window at the screen's top or full screen her titled macOS panel, which AppKit keeps under the menu bar, stuck between the menu bar and the terminal, and on Windows and X11 she stood off the screen. The rule lives in `pet.rs`: she stands on the top edge while the room above the pane, up to the top of the work area of the main window's display, holds her box, 123.4 points; each platform measures the room and places her: macOS from the main window's `frame` and its `screen`'s `visibleFrame`, which leaves out the menu bar, both in AppKit's points; Windows from the client rect and the work area (`rcWork`) of the monitor `MonitorFromWindow` names, at the main window's scale, since above it or inside it she stands on that monitor; X11 from the window's place on the root, up to the top of the RandR monitor under her, or of `_NET_WORKAREA`, its first desktop's, where that lies lower: the work area spans every monitor, so on a lower monitor its top is another's. A Wayland client knows no place of its own, so there she stands on the bottom edge while the window is full screen, maximized or tiled at its top, which gpui reports only for a window it draws itself: under server decorations, as Sway's tiling draws them, she stays on top. gpui moves a popup's anchor never, hears a change of state alone as no change of bounds, and gives an unseen popup no frames, so twice a second she checks where the window's size and state put her anchor, counted from the pane as gpui counts it, and opens there again when it moved. Each place logs the edge she moves to. On WSLg, X11 and Weston's Wayland both put her inside a window as tall as the screen or maximized, and over one with room; headless Sway and a two-monitor Xorg did too.
|
||||
- 2026-10-19: Ely stands inside the main window, her bottom on its pane's bottom edge, as far in from the left, while her box would not fit above it, which the owner asked for after 0.1.4: on a window at the screen's top or full screen her titled macOS panel, which AppKit keeps under the menu bar, stuck between the menu bar and the terminal, and on Windows and X11 she stood off the screen. The rule lives in `pet.rs`: she stands on the top edge while the room above the pane, up to the top of the work area of the main window's display, holds her box, 123.4 points; each platform measures the room and places her: macOS from the main window's `frame` and its `screen`'s `visibleFrame`, which leaves out the menu bar, both in AppKit's points; Windows from the client rect and the work area (`rcWork`) of the monitor `MonitorFromWindow` names, at the main window's scale, since above it or inside it she stands on that monitor; X11 from the pane's place on the root, up to the top of the RandR monitor holding most of it, as Windows and macOS pick a window's display, or of `_NET_WORKAREA`, its first desktop's, where that lies lower: the work area spans every monitor, so on a lower monitor its top is another's. A Wayland client knows no place of its own, so there she stands on the bottom edge while the window is full screen, maximized or tiled at its top, which gpui reports only for a window it draws itself: under server decorations, as Sway's tiling draws them, she stays on top. gpui moves a popup's anchor never, hears a change of state alone as no change of bounds, and gives an unseen popup no frames, so twice a second she checks where the window's size and state put her anchor, counted from the pane as gpui counts it, and opens there again when it moved. Each place logs the edge she moves to. On WSLg, X11 and Weston's Wayland both put her inside a window as tall as the screen or maximized, and over one with room; headless Sway and a two-monitor Xorg did too.
|
||||
+43
-18
@@ -1,7 +1,7 @@
|
||||
//! Ely's window on X11: gpui's popup, which no window manager places, put on an edge of the main window's pane and just above it.
|
||||
|
||||
use anyhow::{Context as _, bail};
|
||||
use gpui::{Bounds, Pixels, Window};
|
||||
use gpui::{Bounds, Pixels, Window, px};
|
||||
use raw_window_handle::{HasWindowHandle, RawWindowHandle};
|
||||
use x11rb::{
|
||||
protocol::{
|
||||
@@ -49,7 +49,7 @@ impl Glue {
|
||||
Ok(glue)
|
||||
}
|
||||
|
||||
/// Stands her on the main window's pane, on its top edge while her box fits under the top of the monitor under her, or of the work area where that lies lower, else on its bottom edge, as far in as its width allows, stacked just above the main window's frame, and gives the edge.
|
||||
/// Stands her on the main window's pane, on its top edge while her box fits under the top of the monitor holding most of the pane, or of the work area where that lies lower, else on its bottom edge, as far in as its width allows, stacked just above the main window's frame, and gives the edge.
|
||||
pub fn place(&self, main: &Window) -> anyhow::Result<Edge> {
|
||||
self.put(main.scale_factor(), pane(main))
|
||||
}
|
||||
@@ -62,17 +62,22 @@ impl Glue {
|
||||
.reply()
|
||||
.context("pet: the main window's place")?;
|
||||
let down = |y: Pixels| i32::from(at.dst_y) + (f32::from(y) * scale).round() as i32;
|
||||
let x = f32::from(pane.origin.x) + left(pane.size.width);
|
||||
let x = i32::from(at.dst_x) + (x * scale).round() as i32;
|
||||
let top = down(pane.origin.y);
|
||||
let usable = usable(&self.monitors(root)?, self.work_top(root)?, (x, top));
|
||||
let across = |x: Pixels| i32::from(at.dst_x) + (f32::from(x) * scale).round() as i32;
|
||||
let (west, top) = (across(pane.origin.x), down(pane.origin.y));
|
||||
let rect = [
|
||||
west,
|
||||
top,
|
||||
across(pane.right()) - west,
|
||||
down(pane.bottom()) - top,
|
||||
];
|
||||
let usable = usable(&self.monitors(root)?, self.work_top(root)?, rect);
|
||||
let edge = edge(usable.map_or(0.0, |usable| (top - usable) as f32 / scale));
|
||||
let bottom = down(match edge {
|
||||
Edge::Top => pane.origin.y,
|
||||
Edge::Bottom => pane.bottom(),
|
||||
});
|
||||
let aux = ConfigureWindowAux::new()
|
||||
.x(x)
|
||||
.x(across(pane.origin.x + px(left(pane.size.width))))
|
||||
.y(bottom - ((HEAD + ROOM) * scale).round() as i32)
|
||||
.sibling(frame)
|
||||
.stack_mode(StackMode::ABOVE);
|
||||
@@ -132,11 +137,18 @@ impl Glue {
|
||||
}
|
||||
}
|
||||
|
||||
/// The top of the room over `(x, y)`: the top of the monitor it lies on, or the work area's top `area` where that lies lower; none off every monitor.
|
||||
fn usable(monitors: &[[i32; 4]], area: Option<i32>, (x, y): (i32, i32)) -> Option<i32> {
|
||||
let [_, top, _, _] = monitors.iter().find(|[left, top, width, height]| {
|
||||
(*left..left + width).contains(&x) && (*top..top + height).contains(&y)
|
||||
})?;
|
||||
/// The top of the room over a pane at `[x, y, width, height]`: the top of the monitor holding most of it, as Windows and macOS pick a window's display, or the work area's top `area` where that lies lower; none off every monitor.
|
||||
fn usable(monitors: &[[i32; 4]], area: Option<i32>, pane: [i32; 4]) -> Option<i32> {
|
||||
let shared = |monitor: &&[i32; 4]| {
|
||||
let overlap = |i: usize| {
|
||||
let end = (monitor[i] + monitor[i + 2]).min(pane[i] + pane[i + 2]);
|
||||
i64::from((end - monitor[i].max(pane[i])).max(0))
|
||||
};
|
||||
overlap(0) * overlap(1)
|
||||
};
|
||||
let [_, top, _, _] = (monitors.iter())
|
||||
.filter(|monitor| shared(monitor) > 0)
|
||||
.max_by_key(shared)?;
|
||||
Some(area.map_or(*top, |area| area.max(*top)))
|
||||
}
|
||||
|
||||
@@ -165,25 +177,38 @@ mod tests {
|
||||
use crate::testx11::Xvfb;
|
||||
|
||||
#[test]
|
||||
fn the_room_above_runs_to_the_top_of_the_monitor_under_her_or_of_the_work_area() {
|
||||
fn the_room_above_runs_to_the_top_of_the_monitor_holding_most_of_the_pane_or_of_the_work_area()
|
||||
{
|
||||
let stacked = [[0, 0, 1280, 800], [0, 800, 1280, 800]];
|
||||
let low = [100, 800, 900, 600];
|
||||
assert_eq!(
|
||||
usable(&stacked, None, (240, 800)),
|
||||
usable(&stacked, None, low),
|
||||
Some(800),
|
||||
"the lower monitor's top"
|
||||
"the lower one's top"
|
||||
);
|
||||
assert_eq!(
|
||||
usable(&stacked, Some(0), (240, 800)),
|
||||
usable(&stacked, Some(0), low),
|
||||
Some(800),
|
||||
"a work area over both monitors leaves the lower one's top"
|
||||
);
|
||||
assert_eq!(
|
||||
usable(&stacked, Some(32), (240, 300)),
|
||||
usable(&stacked, Some(32), [100, 300, 900, 400]),
|
||||
Some(32),
|
||||
"a panel atop the upper one"
|
||||
);
|
||||
assert_eq!(
|
||||
usable(&stacked, None, (2000, 300)),
|
||||
usable(&stacked, None, [100, 500, 900, 400]),
|
||||
Some(0),
|
||||
"across both, the upper one holds most"
|
||||
);
|
||||
let staggered = [[0, 0, 1280, 1024], [1280, 400, 1280, 800]];
|
||||
assert_eq!(
|
||||
usable(&staggered, Some(0), [1100, 400, 1000, 600]),
|
||||
Some(400),
|
||||
"the right one holds most, though she stands over the left"
|
||||
);
|
||||
assert_eq!(
|
||||
usable(&stacked, None, [2000, 300, 900, 400]),
|
||||
None,
|
||||
"off every monitor"
|
||||
);
|
||||
|
||||
Reference in new issue
Block a user