T48: Ely on Windows in a box her own size, as a user found her broken in 0.1.4: gpui opened her popup as large as the display when the middle of the box asked for lay off it, as an overlapped window Windows framed, so she sat at its bottom over the terminal and it took the clicks; her window becomes a bare popup, and the glue posts it her size, from her own DPI, with her place on the pane's top edge, since gpui hears a new size only outside its own updates
This commit is contained in:
5 files changed
+193
-32
No files matched your search
@@ -107,4 +107,4 @@ ELYTerminal. A terminal on GPUI and Ely components. `PRD.md` says what it does;
|
|||||||
- 2026-10-17: a session posts system notifications through gpui's, which the owner asked for. At start `set_app_identity` names the app: on Windows that sets the process's AppUserModelID, which the taskbar groups its windows by too, so a pin made before may show apart, and the first notification writes the app's name under `HKCU\Software\Classes\AppUserModelId`, as gpui needs for an app without a package; on macOS gpui posts only from the app bundle, asks for leave at the first, and logs once, under the `gpui_macos` target, that it posts nothing outside one; on Linux it posts over D-Bus through notify-rust. Each terminal view posts its own, in the order its output asks, so OSC 99's answers keep their place before a DA1 sent after them: when its row's mark turns to waiting, done or error, at each turn, as the row's waiting mark is set, the hover's line for the record its body; when a command C started ends, `Command finished`, or `Command exited with N`, so a prompt drawn again, as macOS's bash 3.2 draws one with its D at a resize, posts nothing, and bash 3.2, which sends no C, posts no command's end, as Ghostty's integration, which skips it, posts none; and when a program asks through OSC 9, 777 or 99, its title and body the body, each line shown as text. It posts only while its window is not active or it is not the session shown, which the workspace tells it, one every 3 seconds at most, a later one logged and dropped; the title is the program's title, else the app's name, as the row names the session, which the tap reads in the output's order, so a title set before a notification in the same output names it. The session's own share a tag, so a new one takes the old one's place where the system allows, as an OSC 99 id does; OSC 9, 777 and OSC 99 without an id each take their own; a session keeps its last 64. A press goes to the window whose session posted it: it shows the window and the session, unless OSC 99 said `a=-focus`, and tells a program that asked `a=report`, `OSC 99 ; i=ID ;`; a press whose record went, a twin Linux stacked and pressed before or one past the 64 kept, still shows its session and tells no program. macOS brings the app forward at any press, `a=-focus` or not, and on Wayland gpui asks for the window with a serial the compositor likely refuses, so KWin and Mutter mark it as asking for attention instead. gpui hears of no close on any system, so `c=1` hears `p=close;untracked` once posted, and alive lists the ids posted and not yet pressed, closed or expired. On macOS and Windows a close takes the notification back, and `w` over 0 takes it back once it runs out, its session gone or not, unless a later post holds its tag; gpui on Linux closes and replaces none, so there a close and `w` pass by, logged, a closed one stays alive, and an update posts beside the old one. `o` passes by: each notification posts only while unseen, which is kitty's invisible, so the query answers `a=focus,report:o=invisible:p=title,body,close,?,alive:w=1`, on Linux `a=focus,report:o=invisible:p=title,body,?,alive`, never with the `c` that close events would need. Windows' toasts are silent, as macOS's are; on Linux the notification daemon decides, and reads the body as markup, so its `&`, `<` and `>` go as entities.
|
- 2026-10-17: a session posts system notifications through gpui's, which the owner asked for. At start `set_app_identity` names the app: on Windows that sets the process's AppUserModelID, which the taskbar groups its windows by too, so a pin made before may show apart, and the first notification writes the app's name under `HKCU\Software\Classes\AppUserModelId`, as gpui needs for an app without a package; on macOS gpui posts only from the app bundle, asks for leave at the first, and logs once, under the `gpui_macos` target, that it posts nothing outside one; on Linux it posts over D-Bus through notify-rust. Each terminal view posts its own, in the order its output asks, so OSC 99's answers keep their place before a DA1 sent after them: when its row's mark turns to waiting, done or error, at each turn, as the row's waiting mark is set, the hover's line for the record its body; when a command C started ends, `Command finished`, or `Command exited with N`, so a prompt drawn again, as macOS's bash 3.2 draws one with its D at a resize, posts nothing, and bash 3.2, which sends no C, posts no command's end, as Ghostty's integration, which skips it, posts none; and when a program asks through OSC 9, 777 or 99, its title and body the body, each line shown as text. It posts only while its window is not active or it is not the session shown, which the workspace tells it, one every 3 seconds at most, a later one logged and dropped; the title is the program's title, else the app's name, as the row names the session, which the tap reads in the output's order, so a title set before a notification in the same output names it. The session's own share a tag, so a new one takes the old one's place where the system allows, as an OSC 99 id does; OSC 9, 777 and OSC 99 without an id each take their own; a session keeps its last 64. A press goes to the window whose session posted it: it shows the window and the session, unless OSC 99 said `a=-focus`, and tells a program that asked `a=report`, `OSC 99 ; i=ID ;`; a press whose record went, a twin Linux stacked and pressed before or one past the 64 kept, still shows its session and tells no program. macOS brings the app forward at any press, `a=-focus` or not, and on Wayland gpui asks for the window with a serial the compositor likely refuses, so KWin and Mutter mark it as asking for attention instead. gpui hears of no close on any system, so `c=1` hears `p=close;untracked` once posted, and alive lists the ids posted and not yet pressed, closed or expired. On macOS and Windows a close takes the notification back, and `w` over 0 takes it back once it runs out, its session gone or not, unless a later post holds its tag; gpui on Linux closes and replaces none, so there a close and `w` pass by, logged, a closed one stays alive, and an update posts beside the old one. `o` passes by: each notification posts only while unseen, which is kitty's invisible, so the query answers `a=focus,report:o=invisible:p=title,body,close,?,alive:w=1`, on Linux `a=focus,report:o=invisible:p=title,body,?,alive`, never with the `c` that close events would need. Windows' toasts are silent, as macOS's are; on Linux the notification daemon decides, and reads the body as markup, so its `&`, `<` and `>` go as entities.
|
||||||
- 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: 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: 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, and is moved as the main window moves; 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 her face follows the most pressing of their marks: a program that waits surprises her, with a mark and two hops; an error worries her, with a shake; a finish cheers her for 4 s, with a hop; work keeps her eyes on the terminal; calm, she blinks and glances, and dozes after 5 minutes; a press winks at you. Each face is an SVG drawn once, at three times her size so Retina draws her sharp, and she draws again only when her face changes. Settings › Terminal › Ely turns her off; she is on by default.
|
- 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 sets its size, from its own DPI, with its place on the pane's top edge as the main window moves, posting both to her window, since Windows sends a new size at once and gpui hears it only outside its own updates, as gpui defers its own resize; 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 her face follows the most pressing of their marks: a program that waits surprises her, with a mark and two hops; an error worries her, with a shake; a finish cheers her for 4 s, with a hop; work keeps her eyes on the terminal; calm, she blinks and glances, and dozes after 5 minutes; a press winks at you. Each face is an SVG drawn once, at three times her size so Retina draws her sharp, and she draws again only when her face changes. Settings › Terminal › Ely turns her off; she is on by default.
|
||||||
+1
-1
@@ -61,7 +61,7 @@ image = { version = "0.25", default-features = false, features = ["png"] }
|
|||||||
[target.'cfg(windows)'.dependencies]
|
[target.'cfg(windows)'.dependencies]
|
||||||
raw-window-handle = "0.6"
|
raw-window-handle = "0.6"
|
||||||
sysinfo = { version = "0.39", default-features = false, features = ["system"] }
|
sysinfo = { version = "0.39", default-features = false, features = ["system"] }
|
||||||
windows-sys = { version = "0.61", features = ["Win32_Foundation", "Win32_Storage_FileSystem", "Win32_System_Console", "Win32_System_Diagnostics_ToolHelp", "Win32_System_Pipes", "Win32_UI_Input_KeyboardAndMouse", "Win32_UI_WindowsAndMessaging"] }
|
windows-sys = { version = "0.61", features = ["Win32_Foundation", "Win32_Storage_FileSystem", "Win32_System_Console", "Win32_System_Diagnostics_ToolHelp", "Win32_System_Pipes", "Win32_UI_HiDpi", "Win32_UI_Input_KeyboardAndMouse", "Win32_UI_WindowsAndMessaging"] }
|
||||||
|
|
||||||
[target.'cfg(windows)'.dev-dependencies]
|
[target.'cfg(windows)'.dev-dependencies]
|
||||||
windows-sys = { version = "0.61", features = ["Win32_Security", "Win32_System_IO"] }
|
windows-sys = { version = "0.61", features = ["Win32_Security", "Win32_System_IO"] }
|
||||||
|
|||||||
@@ -120,3 +120,12 @@ Progress truth. Top to bottom, one at a time. A task is ticked only after its co
|
|||||||
## 25. Release
|
## 25. Release
|
||||||
|
|
||||||
- [x] T47 Release v0.1.4 with T44 to T46: version 0.1.4; the macOS disk image, the Windows zip and the Linux tarball, each from its script; the notes list what changed.
|
- [x] T47 Release v0.1.4 with T44 to T46: version 0.1.4; the macOS disk image, the Windows zip and the Linux tarball, each from its script; the notes list what changed.
|
||||||
|
|
||||||
|
## 26. Ely on Windows
|
||||||
|
|
||||||
|
- [ ] T48 Ely on Windows stands on the main window's top edge in a box her own size, which a user found broken in 0.1.4: gpui opened her window as large as the display when her box's middle lay off it, so she sat at its bottom and the rest of it took the clicks meant for the windows behind; her window is a bare popup, without the caption, border, corners and shadow gpui's popup carries, and the glue sets her size from her own DPI as well as her place on the pane's top edge.
|
||||||
|
- [ ] T49 Ely moves all the time, as she does in the films, which the owner asked for after 0.1.4, where she stood still and only a press made her wink: rigged from the icon's paths as the films rig her, she bobs, her side locks sway and her ahoge breathes, she blinks and glances on her own, and each mood moves her through that rig, smoothly.
|
||||||
|
|
||||||
|
## 27. Release
|
||||||
|
|
||||||
|
- [ ] T50 v0.1.4 rebuilt with T48 and T49, which the owner asked for in place of a v0.1.5: the version stays 0.1.4; the macOS disk image, the Windows zip and the Linux tarball, each from its script, replace the old ones on the release; the tag moves to their commit; the notes give the new SHA-256 and what changed.
|
||||||
+181
-29
@@ -1,24 +1,36 @@
|
|||||||
//! Ely's window on Windows: owned by the main window, so it stays above it and hides with it, and moved as it moves.
|
//! Ely's window on Windows: a bare popup owned by the main window, so it stays above it and hides with it, sized and moved as it moves.
|
||||||
|
|
||||||
use std::sync::atomic::{AtomicIsize, Ordering};
|
use std::sync::atomic::{AtomicIsize, Ordering};
|
||||||
|
|
||||||
use anyhow::bail;
|
use anyhow::bail;
|
||||||
use gpui::{Window, px};
|
use gpui::{Bounds, Pixels, Window};
|
||||||
use raw_window_handle::{HasWindowHandle, RawWindowHandle};
|
use raw_window_handle::{HasWindowHandle, RawWindowHandle};
|
||||||
use windows_sys::Win32::{
|
use windows_sys::Win32::{
|
||||||
Foundation::{HWND, LPARAM, LRESULT, RECT, WPARAM},
|
Foundation::{HWND, LPARAM, LRESULT, WPARAM},
|
||||||
UI::WindowsAndMessaging::{
|
UI::{
|
||||||
CallWindowProcW, GWLP_HWNDPARENT, GWLP_WNDPROC, GetWindowRect, HWND_NOTOPMOST,
|
HiDpi::GetDpiForWindow,
|
||||||
MA_NOACTIVATE, SWP_NOACTIVATE, SWP_NOMOVE, SWP_NOSIZE, SWP_NOZORDER, SetWindowLongPtrW,
|
WindowsAndMessaging::{
|
||||||
SetWindowPos, WM_MOUSEACTIVATE, WNDPROC,
|
CallWindowProcW, GWL_STYLE, GWLP_HWNDPARENT, GWLP_WNDPROC, GetWindowInfo,
|
||||||
|
GetWindowLongPtrW, HWND_NOTOPMOST, MA_NOACTIVATE, PostMessageW, SWP_FRAMECHANGED,
|
||||||
|
SWP_NOACTIVATE, SWP_NOMOVE, SWP_NOSIZE, SWP_NOZORDER, SetWindowLongPtrW, SetWindowPos,
|
||||||
|
USER_DEFAULT_SCREEN_DPI, WINDOWINFO, WM_APP, WM_MOUSEACTIVATE, WNDPROC, WS_CAPTION,
|
||||||
|
WS_POPUP,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
use super::{HEAD, ROOM, left};
|
use super::{HEAD, ROOM, WIDTH, left, pane};
|
||||||
|
|
||||||
/// gpui's window procedure, which hers passes every message on to but a press's activation.
|
/// gpui's window procedure, which hers passes every message on to but a press's activation and her place.
|
||||||
static GPUI: AtomicIsize = AtomicIsize::new(0);
|
static GPUI: AtomicIsize = AtomicIsize::new(0);
|
||||||
|
|
||||||
|
/// Her place, posted to her window: Windows sends a new size at once, which gpui hears only outside its own updates.
|
||||||
|
const PLACE: u32 = WM_APP;
|
||||||
|
const _: () = assert!(
|
||||||
|
size_of::<WPARAM>() == 8,
|
||||||
|
"a place packs two i32 into a word"
|
||||||
|
);
|
||||||
|
|
||||||
/// Her window and the main one.
|
/// Her window and the main one.
|
||||||
pub struct Glue {
|
pub struct Glue {
|
||||||
pet: HWND,
|
pet: HWND,
|
||||||
@@ -26,18 +38,24 @@ pub struct Glue {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl Glue {
|
impl Glue {
|
||||||
/// Gives her window the main window as owner, takes away the topmost place gpui gives a popup, and keeps a press from activating it, so the keys stay with the terminal.
|
|
||||||
pub fn new(pet: &Window, main: &Window) -> anyhow::Result<Self> {
|
pub fn new(pet: &Window, main: &Window) -> anyhow::Result<Self> {
|
||||||
let (pet, main) = (native(pet)?, native(main)?);
|
Self::hold(native(pet)?, native(main)?)
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Gives her window the main window as owner, a bare popup's frame, none of the topmost place gpui gives a popup, and keeps a press from activating it, so the keys stay with the terminal.
|
||||||
|
fn hold(pet: HWND, main: HWND) -> anyhow::Result<Self> {
|
||||||
unsafe {
|
unsafe {
|
||||||
SetWindowLongPtrW(pet, GWLP_HWNDPARENT, main as isize);
|
SetWindowLongPtrW(pet, GWLP_HWNDPARENT, main as isize);
|
||||||
|
// gpui's popup is overlapped, which Windows gives a caption and a frame.
|
||||||
|
let style = (GetWindowLongPtrW(pet, GWL_STYLE) as u32 & !WS_CAPTION) | WS_POPUP;
|
||||||
|
SetWindowLongPtrW(pet, GWL_STYLE, style as isize);
|
||||||
let gpui = SetWindowLongPtrW(pet, GWLP_WNDPROC, inactive as *const () as isize);
|
let gpui = SetWindowLongPtrW(pet, GWLP_WNDPROC, inactive as *const () as isize);
|
||||||
anyhow::ensure!(gpui != 0, "pet: her window's procedure does not read");
|
anyhow::ensure!(gpui != 0, "pet: her window's procedure does not read");
|
||||||
GPUI.store(gpui, Ordering::Relaxed);
|
GPUI.store(gpui, Ordering::Relaxed);
|
||||||
let flags = SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE;
|
let flags = SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE | SWP_FRAMECHANGED;
|
||||||
anyhow::ensure!(
|
anyhow::ensure!(
|
||||||
SetWindowPos(pet, HWND_NOTOPMOST, 0, 0, 0, 0, flags) != 0,
|
SetWindowPos(pet, HWND_NOTOPMOST, 0, 0, 0, 0, flags) != 0,
|
||||||
"pet: her window keeps the topmost place"
|
"pet: her window keeps the topmost place or its frame"
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
Ok(Self { pet, main })
|
Ok(Self { pet, main })
|
||||||
@@ -45,22 +63,36 @@ impl Glue {
|
|||||||
|
|
||||||
/// Stands her on the main window's top edge, as far in as its width allows.
|
/// Stands her on the main window's top edge, as far in as its width allows.
|
||||||
pub fn place(&self, main: &Window) -> anyhow::Result<()> {
|
pub fn place(&self, main: &Window) -> anyhow::Result<()> {
|
||||||
let scale = main.scale_factor();
|
self.put(main.scale_factor(), pane(main))
|
||||||
let mut frame = RECT::default();
|
}
|
||||||
unsafe {
|
|
||||||
anyhow::ensure!(
|
/// Posts her box, sized at her own DPI as gpui did not when it opened her off the display, standing at `scale` over `pane`, in the main window's points.
|
||||||
GetWindowRect(self.main, &mut frame) != 0,
|
fn put(&self, scale: f32, pane: Bounds<Pixels>) -> anyhow::Result<()> {
|
||||||
"pet: the main window's place does not read"
|
let mut main = WINDOWINFO {
|
||||||
);
|
cbSize: size_of::<WINDOWINFO>() as u32,
|
||||||
let width = px((frame.right - frame.left) as f32 / scale);
|
..Default::default()
|
||||||
let x = frame.left + (left(width) * scale).round() as i32;
|
};
|
||||||
let y = frame.top - ((HEAD + ROOM) * scale).round() as i32;
|
let (dpi, read) = unsafe {
|
||||||
let flags = SWP_NOSIZE | SWP_NOZORDER | SWP_NOACTIVATE;
|
(
|
||||||
anyhow::ensure!(
|
GetDpiForWindow(self.pet),
|
||||||
SetWindowPos(self.pet, std::ptr::null_mut(), x, y, 0, 0, flags) != 0,
|
GetWindowInfo(self.main, &mut main),
|
||||||
"pet: her window does not move"
|
)
|
||||||
);
|
};
|
||||||
}
|
anyhow::ensure!(dpi != 0, "pet: her window's DPI does not read");
|
||||||
|
anyhow::ensure!(read != 0, "pet: the main window's place does not read");
|
||||||
|
let own = dpi as f32 / USER_DEFAULT_SCREEN_DPI as f32;
|
||||||
|
let (x, y) = (f32::from(pane.origin.x), f32::from(pane.origin.y));
|
||||||
|
let at = (
|
||||||
|
main.rcClient.left + ((x + left(pane.size.width)) * scale).round() as i32,
|
||||||
|
main.rcClient.top + ((y - HEAD - ROOM) * scale).round() as i32,
|
||||||
|
);
|
||||||
|
let size = (
|
||||||
|
(WIDTH * own).round() as i32,
|
||||||
|
((HEAD + ROOM) * own).round() as i32,
|
||||||
|
);
|
||||||
|
let posted = unsafe { PostMessageW(self.pet, PLACE, pack(at), pack(size) as LPARAM) };
|
||||||
|
anyhow::ensure!(posted != 0, "pet: her place does not post");
|
||||||
|
log::debug!("pet: Ely is to stand at {at:?}, {size:?} pixels");
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -75,10 +107,28 @@ unsafe extern "system" fn inactive(
|
|||||||
if message == WM_MOUSEACTIVATE {
|
if message == WM_MOUSEACTIVATE {
|
||||||
return MA_NOACTIVATE as LRESULT;
|
return MA_NOACTIVATE as LRESULT;
|
||||||
}
|
}
|
||||||
|
if message == PLACE {
|
||||||
|
let ((x, y), (width, height)) = (unpack(wparam), unpack(lparam as WPARAM));
|
||||||
|
let flags = SWP_NOZORDER | SWP_NOACTIVATE;
|
||||||
|
let moved =
|
||||||
|
unsafe { SetWindowPos(window, std::ptr::null_mut(), x, y, width, height, flags) };
|
||||||
|
if moved == 0 {
|
||||||
|
log::error!("pet: her window does not move to {x},{y}");
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
let gpui: WNDPROC = unsafe { std::mem::transmute(GPUI.load(Ordering::Relaxed)) };
|
let gpui: WNDPROC = unsafe { std::mem::transmute(GPUI.load(Ordering::Relaxed)) };
|
||||||
unsafe { CallWindowProcW(gpui, window, message, wparam, lparam) }
|
unsafe { CallWindowProcW(gpui, window, message, wparam, lparam) }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn pack((low, high): (i32, i32)) -> WPARAM {
|
||||||
|
low as u32 as WPARAM | (high as u32 as WPARAM) << 32
|
||||||
|
}
|
||||||
|
|
||||||
|
fn unpack(word: WPARAM) -> (i32, i32) {
|
||||||
|
(word as u32 as i32, (word >> 32) as u32 as i32)
|
||||||
|
}
|
||||||
|
|
||||||
/// The HWND gpui made for `window`.
|
/// The HWND gpui made for `window`.
|
||||||
fn native(window: &Window) -> anyhow::Result<HWND> {
|
fn native(window: &Window) -> anyhow::Result<HWND> {
|
||||||
let handle = HasWindowHandle::window_handle(window)
|
let handle = HasWindowHandle::window_handle(window)
|
||||||
@@ -88,3 +138,105 @@ fn native(window: &Window) -> anyhow::Result<HWND> {
|
|||||||
};
|
};
|
||||||
Ok(win32.hwnd.get() as HWND)
|
Ok(win32.hwnd.get() as HWND)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
mod tests {
|
||||||
|
use gpui::{point, px, size};
|
||||||
|
use windows_sys::{
|
||||||
|
Win32::UI::WindowsAndMessaging::{
|
||||||
|
CW_USEDEFAULT, CreateWindowExW, DestroyWindow, DispatchMessageW, MSG, PM_REMOVE,
|
||||||
|
PeekMessageW, WS_EX_TOOLWINDOW, WS_EX_TOPMOST, WS_OVERLAPPEDWINDOW,
|
||||||
|
},
|
||||||
|
w,
|
||||||
|
};
|
||||||
|
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
/// A hidden window of `style`, at the size Windows picks, as gpui asks for its windows.
|
||||||
|
fn window(style: u32, extended: u32) -> HWND {
|
||||||
|
let window = unsafe {
|
||||||
|
CreateWindowExW(
|
||||||
|
extended,
|
||||||
|
w!("STATIC"),
|
||||||
|
w!(""),
|
||||||
|
style,
|
||||||
|
CW_USEDEFAULT,
|
||||||
|
CW_USEDEFAULT,
|
||||||
|
CW_USEDEFAULT,
|
||||||
|
CW_USEDEFAULT,
|
||||||
|
std::ptr::null_mut(),
|
||||||
|
std::ptr::null_mut(),
|
||||||
|
std::ptr::null_mut(),
|
||||||
|
std::ptr::null(),
|
||||||
|
)
|
||||||
|
};
|
||||||
|
assert!(!window.is_null(), "a test window");
|
||||||
|
window
|
||||||
|
}
|
||||||
|
|
||||||
|
fn info(window: HWND) -> WINDOWINFO {
|
||||||
|
let mut info = WINDOWINFO {
|
||||||
|
cbSize: size_of::<WINDOWINFO>() as u32,
|
||||||
|
..Default::default()
|
||||||
|
};
|
||||||
|
assert_ne!(unsafe { GetWindowInfo(window, &mut info) }, 0, "its info");
|
||||||
|
info
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn the_large_framed_popup_gpui_makes_stands_bare_in_her_box_on_the_panes_top_edge() {
|
||||||
|
let main = window(WS_OVERLAPPEDWINDOW, 0);
|
||||||
|
// gpui's popup: style 0, an overlapped window, which Windows gives a caption.
|
||||||
|
let pet = window(0, WS_EX_TOOLWINDOW | WS_EX_TOPMOST);
|
||||||
|
assert_eq!(
|
||||||
|
info(pet).dwStyle & WS_CAPTION,
|
||||||
|
WS_CAPTION,
|
||||||
|
"framed as gpui makes it"
|
||||||
|
);
|
||||||
|
let glue = Glue::hold(pet, main).expect("held");
|
||||||
|
let pane = Bounds::new(point(px(0.), px(0.)), size(px(380.), px(280.)));
|
||||||
|
glue.put(1.0, pane).expect("placed");
|
||||||
|
let made = info(pet).rcWindow;
|
||||||
|
let mut message = MSG::default();
|
||||||
|
while unsafe { PeekMessageW(&mut message, pet, 0, 0, PM_REMOVE) } != 0 {
|
||||||
|
unsafe { DispatchMessageW(&message) };
|
||||||
|
}
|
||||||
|
let (her, under) = (info(pet), info(main));
|
||||||
|
let own = unsafe { GetDpiForWindow(pet) } as f32 / 96.0;
|
||||||
|
unsafe {
|
||||||
|
DestroyWindow(pet);
|
||||||
|
DestroyWindow(main);
|
||||||
|
}
|
||||||
|
let (outer, inner) = (her.rcWindow, her.rcClient);
|
||||||
|
let frame = (
|
||||||
|
inner.left - outer.left,
|
||||||
|
inner.top - outer.top,
|
||||||
|
outer.right - inner.right,
|
||||||
|
outer.bottom - inner.bottom,
|
||||||
|
);
|
||||||
|
let drawn = (outer.right - outer.left, outer.bottom - outer.top);
|
||||||
|
let own_box = (
|
||||||
|
(WIDTH * own).round() as i32,
|
||||||
|
((HEAD + ROOM) * own).round() as i32,
|
||||||
|
);
|
||||||
|
let edge = (
|
||||||
|
under.rcClient.left + left(px(380.)).round() as i32,
|
||||||
|
under.rcClient.top + (-HEAD - ROOM).round() as i32,
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
(
|
||||||
|
her.dwStyle & WS_CAPTION,
|
||||||
|
frame,
|
||||||
|
drawn,
|
||||||
|
(outer.left, outer.top)
|
||||||
|
),
|
||||||
|
(0, (0, 0, 0, 0), own_box, edge),
|
||||||
|
"bare, in her own box, on the pane's top edge"
|
||||||
|
);
|
||||||
|
assert_ne!(
|
||||||
|
(made.right - made.left, made.bottom - made.top),
|
||||||
|
own_box,
|
||||||
|
"no size until her window takes the posted place"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
+1
-1
@@ -84,7 +84,7 @@ What no check reached: no real KWin drew the blur; of Wayland desktops, only Hyp
|
|||||||
| The pointer over the terminal is the arrow | T26 | the terminal sets no pointer, so gpui's arrow shows; on X11 with the pointer over the grid, gpui asked for no I-beam ("text"), which it had asked for, and failed to load under Xvfb, before | Pass |
|
| The pointer over the terminal is the arrow | T26 | the terminal sets no pointer, so gpui's arrow shows; on X11 with the pointer over the grid, gpui asked for no I-beam ("text"), which it had asked for, and failed to load under Xvfb, before | Pass |
|
||||||
| The title bar is transparent | T11, T19 | T19 review | Pass |
|
| The title bar is transparent | T11, T19 | T19 review | Pass |
|
||||||
| Every title bar control takes its press, Ely's window buttons on Windows and Linux too | T26 | Ely `cdc5ce8`: `window_controls_occlude_what_lies_under` (each style; before, the box under Windows' caption buttons took the press), `a_wobbling_press_on_a_slot_moves_no_window` (before, the window moved), `a_wobbling_press_on_a_column_title_moves_the_window`; on the owner's PC, window messages asked the app for Windows' hit test along the title bar: v0.1.0 answered caption from end to end, T26's build answers client over the toggles and +, caption over the titles, min, max and close over Ely's buttons, and a press on close closed the app; the owner, given v0.1.1's build and these controls to click, reported it works on Windows | Pass |
|
| Every title bar control takes its press, Ely's window buttons on Windows and Linux too | T26 | Ely `cdc5ce8`: `window_controls_occlude_what_lies_under` (each style; before, the box under Windows' caption buttons took the press), `a_wobbling_press_on_a_slot_moves_no_window` (before, the window moved), `a_wobbling_press_on_a_column_title_moves_the_window`; on the owner's PC, window messages asked the app for Windows' hit test along the title bar: v0.1.0 answered caption from end to end, T26's build answers client over the toggles and +, caption over the titles, min, max and close over Ely's buttons, and a press on close closed the app; the owner, given v0.1.1's build and these controls to click, reported it works on Windows | Pass |
|
||||||
| Ely peeks over the window's top edge from behind it; her face follows the sessions; a press pets her | T46 | `pet::look` tests: the most pressing mark sets her mood, calm blinks, glances and dozes, a finish cheers for 4 s, waiting and errors hold; `pet::faces` tests: every face parses at three times her box and shows its marks; `pet::x11` test under Xvfb: her window stands on the pane, just above the window manager's frame, and unmaps and maps with the main window; on macOS `shot` found her the main window's child, 112 by 124 points on its top edge 140 in, a capture of the screen showed her over the edge, and OSC 7501 reports gave her surprise, worry, eyes down and a cheer; a press on her window left the main window key and every key typed before and after it reached the program; switched off and on during an error she came back worried; on a 100-point window she stood at its left edge, and moved, resized and minimized she followed it; Windows type-checks; Wayland's popup is gpui's own | Pass |
|
| Ely peeks over the window's top edge from behind it; her face follows the sessions; a press pets her | T46, T48 | `pet::look` tests: the most pressing mark sets her mood, calm blinks, glances and dozes, a finish cheers for 4 s, waiting and errors hold; `pet::faces` tests: every face parses at three times her box and shows its marks; `pet::x11` test under Xvfb: her window stands on the pane, just above the window manager's frame, and unmaps and maps with the main window; on macOS `shot` found her the main window's child, 112 by 124 points on its top edge 140 in, a capture of the screen showed her over the edge, and OSC 7501 reports gave her surprise, worry, eyes down and a cheer; a press on her window left the main window key and every key typed before and after it reached the program; switched off and on during an error she came back worried; on a 100-point window she stood at its left edge, and moved, resized and minimized she followed it; on Windows the `pet::windows` test, red before T48, finds the framed popup Windows makes, 1898 by 1481, bare in her own box, 168 by 185 at 150%, on the pane's top edge, and no size until her window takes the place posted to it; on a Windows 11 VM at 150%, with the main window 49 pixels from the screen's top, 0.1.4 opened her 2326 by 1654 with a caption, drawn at its bottom over the terminal, and a press inside the terminal went to her; T48's build opened her 168 by 185 without one, on the edge, the press went to the terminal, one beside her box to the program behind, and one on her to her; with the window mid-screen she showed whole on its edge; Wayland's popup is gpui's own | Pass |
|
||||||
|
|
||||||
## 7. Sessions panel
|
## 7. Sessions panel
|
||||||
|
|
||||||
|
|||||||
Reference in new issue
Block a user