mirror of
https://codeberg.org/dnkl/foot.git
synced 2026-04-25 06:46:36 -04:00
Merge branch 'master' into crosshair-v4
This commit is contained in:
commit
8f0a87b1b7
60 changed files with 1744 additions and 944 deletions
140
CHANGELOG.md
140
CHANGELOG.md
|
|
@ -51,13 +51,43 @@
|
|||
|
||||
## Unreleased
|
||||
### Added
|
||||
|
||||
* Unicode input mode now accepts input from the numpad as well,
|
||||
numlock is ignored.
|
||||
* A new `resize-by-cells` option, enabled by default, allows the size
|
||||
of floating windows to be constrained to multiples of the cell size.
|
||||
* Support for custom (i.e. other than ctrl/shift/alt/super) modifiers
|
||||
in key bindings ([#1348][1348]).
|
||||
* `pipe-command-output` key binding.
|
||||
* Support for OSC-176, _"Set App-ID"_
|
||||
(https://gist.github.com/delthas/d451e2cc1573bb2364839849c7117239).
|
||||
|
||||
[1348]: https://codeberg.org/dnkl/foot/issues/1348
|
||||
|
||||
|
||||
### Changed
|
||||
|
||||
* config: ARGB color values now default to opaque, rather than
|
||||
transparent, when the alpha component has been left out
|
||||
([#1526][1526]).
|
||||
* The `foot` process now changes CWD to `/` after spawning the shell
|
||||
process. This ensures the terminal itself does not "lock" a
|
||||
directory; for example, preventing a mount point from being
|
||||
unmounted ([#1528][1528]).
|
||||
* Kitty keyboard protocol: updated behavior of modifiers bits during
|
||||
modifier key events, to match the (new [#6913][kitty-6913]) behavior
|
||||
in kitty >= 0.32.0 ([#1561][1561]).
|
||||
* When changing font sizes or display scales in floating windows, the
|
||||
window will be resized as needed to preserve the same grid size.
|
||||
* `smm` now disables private mode 1036 (_"send ESC when Meta modifies
|
||||
a key"_), and enables private mode 1034 (_"8-bit Meta mode"_). `rmm`
|
||||
does the opposite ([#1584][1584]).
|
||||
|
||||
[1526]: https://codeberg.org/dnkl/foot/issues/1526
|
||||
[1528]: https://codeberg.org/dnkl/foot/issues/1528
|
||||
[1561]: https://codeberg.org/dnkl/foot/issues/1561
|
||||
[kitty-6913]: https://github.com/kovidgoyal/kitty/issues/6913
|
||||
[1584]: https://codeberg.org/dnkl/foot/issues/1584
|
||||
|
||||
|
||||
### Deprecated
|
||||
|
|
@ -67,8 +97,20 @@
|
|||
* config: improved validation of color values.
|
||||
* config: double close of file descriptor, resulting in a chain of
|
||||
errors ultimately leading to a startup failure ([#1531][1531]).
|
||||
* Crash when using a desktop scaling factor > 1, on compositors that
|
||||
implements neither the `fractional-scale-v1`, nor the
|
||||
`cursor-shape-v1` Wayland protocols ([#1573][1573]).
|
||||
* Crash in `--server` mode when one or more environment variables are
|
||||
set in `[environment]`.
|
||||
* Environment variables normally set by foot lost with `footclient
|
||||
-E,--client-environment` ([#1568][1568]).
|
||||
* XDG toplevel protocol violation, by trying to set a title that
|
||||
contains an invalid UTF-8 sequence ([#1552][1552]).
|
||||
|
||||
[1531]: https://codeberg.org/dnkl/foot/issues/1531
|
||||
[1573]: https://codeberg.org/dnkl/foot/issues/1573
|
||||
[1568]: https://codeberg.org/dnkl/foot/issues/1568
|
||||
[1552]: https://codeberg.org/dnkl/foot/issues/1552
|
||||
|
||||
|
||||
### Security
|
||||
|
|
@ -345,7 +387,7 @@
|
|||
* Kitty keyboard protocol: F3 is now encoded as `CSI 13~` instead of
|
||||
`CSI R`. The kitty keyboard protocol originally allowed F3 to be
|
||||
encoded as `CSI R`, but this was removed from the specification
|
||||
since `CSI R` conflicts with the _”Cursor Position Report”_.
|
||||
since `CSI R` conflicts with the _"Cursor Position Report"_.
|
||||
* `[main].utempter` renamed to `[main].utmp-helper`. The old option
|
||||
name is still recognized, but will log a deprecation warning.
|
||||
* Meson option `default-utempter-path` renamed to
|
||||
|
|
@ -353,9 +395,9 @@
|
|||
* Opaque sixels now retain the background opacity (when current
|
||||
background color is the **default** background color)
|
||||
([#1360][1360]).
|
||||
* Text cursor’s vertical position after emitting a sixel, when sixel
|
||||
* Text cursor's vertical position after emitting a sixel, when sixel
|
||||
scrolling is **enabled** (the default) has been updated to match
|
||||
XTerm’s, and the VT382’s behavior: the cursor is positioned **on**
|
||||
XTerm's, and the VT382's behavior: the cursor is positioned **on**
|
||||
the last sixel row, rather than _after_ it. This allows printing
|
||||
sixels on the last row without scrolling up, but also means
|
||||
applications may have to explicitly emit a newline to ensure the
|
||||
|
|
@ -451,8 +493,8 @@
|
|||
([#1188][1188]).
|
||||
* Bracketed paste terminfo entries (`BD`, `BE`, `PE` and `PS`, added
|
||||
to ncurses in 2022-12-24). Vim makes use of these.
|
||||
* “Report version” terminfo entries (`XR`/`xr`).
|
||||
* “Report DA2” terminfo entries (`RV`/`rv`).
|
||||
* "Report version" terminfo entries (`XR`/`xr`).
|
||||
* "Report DA2" terminfo entries (`RV`/`rv`).
|
||||
* `XF` terminfo capability (focus in/out events available).
|
||||
* `$TERM_PROGRAM` and `$TERM_PROGRAM_VERSION` environment variables
|
||||
unset in the slave process.
|
||||
|
|
@ -505,12 +547,12 @@
|
|||
* Crash when interactively resizing the window with a very large
|
||||
scrollback.
|
||||
* Crash when a sixel image exceeds the current sixel max height.
|
||||
* Crash after reverse-scrolling (`CSI Ps T`) in the ‘normal’
|
||||
* Crash after reverse-scrolling (`CSI Ps T`) in the 'normal'
|
||||
(non-alternate) screen ([#1190][1190]).
|
||||
* Background transparency being applied to the text "behind" the
|
||||
cursor. Only applies to block cursor using inversed fg/bg
|
||||
colors. ([#1205][1205]).
|
||||
* Crash when monitor’s physical size is "too small" ([#1209][1209]).
|
||||
* Crash when monitor's physical size is "too small" ([#1209][1209]).
|
||||
* Line-height adjustment when incrementing/decrementing the font size
|
||||
with a user-set line-height ([#1218][1218]).
|
||||
* Scaling factor not being correctly applied when converting pt-or-px
|
||||
|
|
@ -581,7 +623,7 @@
|
|||
* Crash on buggy compositors (GNOME) that sometimes send pointer-enter
|
||||
events with a NULL surface. Foot now ignores these events, and the
|
||||
subsequent motion and leave events.
|
||||
* Regression: “random” selected empty cells being highlighted as
|
||||
* Regression: "random" selected empty cells being highlighted as
|
||||
selected when they should not.
|
||||
* Crash when either resizing the terminal window, or scrolling in the
|
||||
scrollback history ([#1074][1074])
|
||||
|
|
@ -621,7 +663,7 @@
|
|||
|
||||
### Changed
|
||||
|
||||
* Use `$HOME` instead of `getpwuid()` to retrieve the user’s home
|
||||
* Use `$HOME` instead of `getpwuid()` to retrieve the user's home
|
||||
directory when searching for `foot.ini`.
|
||||
* HT, VT and FF are no longer stripped when pasting in non-bracketed
|
||||
mode ([#1084][1084]).
|
||||
|
|
@ -685,7 +727,7 @@
|
|||
### Added
|
||||
|
||||
* Workaround for Sway bug [#6960][sway-6960]: scrollback search and
|
||||
the OSC-555 (“flash”) escape sequence leaves dimmed (search) and
|
||||
the OSC-555 ("flash") escape sequence leaves dimmed (search) and
|
||||
yellow (flash) artifacts ([#1046][1046]).
|
||||
* `Control+Shift+v` and `XF86Paste` have been added to the default set
|
||||
of key bindings that paste from the clipboard into the scrollback
|
||||
|
|
@ -698,7 +740,7 @@
|
|||
|
||||
### Changed
|
||||
|
||||
* Scrollback search’s `extend-to-word-boundary` no longer stops at
|
||||
* Scrollback search's `extend-to-word-boundary` no longer stops at
|
||||
space-to-word boundaries, making selection extension feel more
|
||||
natural.
|
||||
|
||||
|
|
@ -739,7 +781,7 @@
|
|||
([#950][950]).
|
||||
* footclient: `-E,--client-environment` command line option. When
|
||||
used, the child process in the new terminal instance inherits the
|
||||
environment from the footclient process instead of the server’s
|
||||
environment from the footclient process instead of the server's
|
||||
([#1004][1004]).
|
||||
* `[csd].hide-when-maximized=yes|no` option ([#1019][1019]).
|
||||
* Scrollback search mode now highlights all matches.
|
||||
|
|
@ -795,7 +837,7 @@
|
|||
|
||||
* Build: missing `wayland_client` dependency in `test-config`
|
||||
([#918][918]).
|
||||
* “(null)” being logged as font-name (for some fonts) when warning
|
||||
* "(null)" being logged as font-name (for some fonts) when warning
|
||||
about a non-monospaced primary font.
|
||||
* Rare crash when the window is resized while a mouse selection is
|
||||
ongoing ([#922][922]).
|
||||
|
|
@ -817,7 +859,7 @@
|
|||
([#1009][1009]).
|
||||
* Window geometry when CSDs are enabled and CSD border width set to a
|
||||
non-zero value. This fixes window snapping in e.g. GNOME.
|
||||
* Window size “jumping” when starting an interactive resize when CSDs
|
||||
* Window size "jumping" when starting an interactive resize when CSDs
|
||||
are enabled, and CSD border width set to a non-zero value.
|
||||
* Key binding overrides on the command line having no effect with
|
||||
`footclient` instances ([#931][931]).
|
||||
|
|
@ -883,15 +925,15 @@
|
|||
* PaperColorDark and PaperColorLight themes renamed to
|
||||
paper-color-dark and paper-color-light, for consistency with other
|
||||
theme names.
|
||||
* `[scrollback].multiplier` is now applied in “alternate scroll” mode,
|
||||
* `[scrollback].multiplier` is now applied in "alternate scroll" mode,
|
||||
where scroll events are translated to fake arrow key presses on the
|
||||
alt screen ([#859](https://codeberg.org/dnkl/foot/issues/859)).
|
||||
* The width of the block cursor’s outline in an unfocused window is
|
||||
now scaled by the output scaling factor (“desktop
|
||||
scaling”). Previously, it was always 1px.
|
||||
* Foot will now try to change the locale to either “C.UTF-8” or
|
||||
“en_US.UTF-8” if started with a non-UTF8 locale. If this fails, foot
|
||||
will start, but only to display a window with an error (user’s shell
|
||||
* The width of the block cursor's outline in an unfocused window is
|
||||
now scaled by the output scaling factor ("desktop
|
||||
scaling"). Previously, it was always 1px.
|
||||
* Foot will now try to change the locale to either "C.UTF-8" or
|
||||
"en_US.UTF-8" if started with a non-UTF8 locale. If this fails, foot
|
||||
will start, but only to display a window with an error (user's shell
|
||||
is not executed).
|
||||
* `gettimeofday()` has been replaced with `clock_gettime()`, due to it being
|
||||
marked as obsolete by POSIX.
|
||||
|
|
@ -919,7 +961,7 @@
|
|||
|
||||
### Fixed
|
||||
|
||||
* Font size adjustment (“zooming”) when font is configured with a
|
||||
* Font size adjustment ("zooming") when font is configured with a
|
||||
**pixelsize**, and `dpi-aware=no`
|
||||
([#842](https://codeberg.org/dnkl/foot/issues/842)).
|
||||
* Key presses triggering keyboard layout switches also emitting CSI
|
||||
|
|
@ -993,7 +1035,7 @@
|
|||
* Initial support for the [Kitty keyboard protocol](https://sw.kovidgoyal.net/kitty/keyboard-protocol/).
|
||||
Modes supported:
|
||||
- [Disambiguate escape codes](https://sw.kovidgoyal.net/kitty/keyboard-protocol/#disambiguate) (mode `0b1`)
|
||||
* “Window menu” (compositor provided) on right clicks on the CSD title
|
||||
* "Window menu" (compositor provided) on right clicks on the CSD title
|
||||
bar.
|
||||
|
||||
|
||||
|
|
@ -1030,7 +1072,7 @@
|
|||
|
||||
### Fixed
|
||||
|
||||
* Regression: `letter-spacing` resulting in a “not a valid option”
|
||||
* Regression: `letter-spacing` resulting in a "not a valid option"
|
||||
error ([#795](https://codeberg.org/dnkl/foot/issues/795)).
|
||||
* Regression: bad section name in configuration error messages.
|
||||
* Regression: `pipe-*` key bindings not being parsed correctly,
|
||||
|
|
@ -1065,7 +1107,7 @@
|
|||
* `[csd].border-width` and `[csd].border-color`, allowing you to
|
||||
configure the width and color of the CSD border.
|
||||
* Support for `XTMODKEYS` with `Pp=4` and `Pv=2` (_modifyOtherKeys=2_).
|
||||
* `[colors].dim0-7` options, allowing you to configure custom “dim”
|
||||
* `[colors].dim0-7` options, allowing you to configure custom "dim"
|
||||
colors ([#776](https://codeberg.org/dnkl/foot/issues/776)).
|
||||
|
||||
|
||||
|
|
@ -1081,9 +1123,9 @@
|
|||
due to the compositor not implementing a recent enough version of
|
||||
the `wl_seat` interface ([#779](https://codeberg.org/dnkl/foot/issues/779)).
|
||||
* Boolean options in `foot.ini` are now limited to
|
||||
“yes|true|on|1|no|false|off|0”, Previously, anything that did not
|
||||
match “yes|true|on”, or a number greater than 0, was treated as
|
||||
“false”.
|
||||
"yes|true|on|1|no|false|off|0", Previously, anything that did not
|
||||
match "yes|true|on", or a number greater than 0, was treated as
|
||||
"false".
|
||||
* `[scrollback].multiplier` is no longer applied when the alternate
|
||||
screen is in use ([#787](https://codeberg.org/dnkl/foot/issues/787)).
|
||||
|
||||
|
|
@ -1098,7 +1140,7 @@
|
|||
|
||||
### Fixed
|
||||
|
||||
* ‘Sticky’ modifiers in input handling; when determining modifier
|
||||
* 'Sticky' modifiers in input handling; when determining modifier
|
||||
state, foot was looking at **depressed** modifiers, not
|
||||
**effective** modifiers, like it should.
|
||||
* Fix crashes after enabling CSD at runtime when `csd.size` is 0.
|
||||
|
|
@ -1106,7 +1148,7 @@
|
|||
([#752](https://codeberg.org/dnkl/foot/issues/752)).
|
||||
* Clipboard occasionally ceasing to work, until window has been
|
||||
re-focused ([#753](https://codeberg.org/dnkl/foot/issues/753)).
|
||||
* Don’t propagate window title updates to the Wayland compositor
|
||||
* Don't propagate window title updates to the Wayland compositor
|
||||
unless the new title is different from the old title.
|
||||
|
||||
|
||||
|
|
@ -1126,7 +1168,7 @@
|
|||
### Changed
|
||||
|
||||
* PGO helper scripts no longer set `LC_CTYPE=en_US.UTF-8`. But, note
|
||||
that “full” PGO builds still **require** a UTF-8 locale; you need
|
||||
that "full" PGO builds still **require** a UTF-8 locale; you need
|
||||
to set one manually in your build script
|
||||
([#728](https://codeberg.org/dnkl/foot/issues/728)).
|
||||
|
||||
|
|
@ -1154,11 +1196,11 @@
|
|||
definitions when `-Dterminfo=enabled`.
|
||||
* `-Dcustom-terminfo-install-location` no longer accepts `no` as a
|
||||
special value, to disable exporting `TERMINFO`. To achieve the same
|
||||
result, simply don’t set it at all. If it _is_ set, `TERMINFO` is
|
||||
result, simply don't set it at all. If it _is_ set, `TERMINFO` is
|
||||
still exported, like before.
|
||||
* The default install location for the terminfo definitions have been
|
||||
changed back to `${datadir}/terminfo`.
|
||||
* `dpi-aware=auto`: fonts are now scaled using the monitor’s DPI only
|
||||
* `dpi-aware=auto`: fonts are now scaled using the monitor's DPI only
|
||||
when **all** monitors have a scaling factor of one
|
||||
([#714](https://codeberg.org/dnkl/foot/issues/714)).
|
||||
* fcft >= 3.0.0 in now required.
|
||||
|
|
@ -1207,12 +1249,12 @@
|
|||
terminating the client application) from 4 to 60 seconds.
|
||||
* When terminating the client application, foot now sends `SIGTERM` immediately
|
||||
after closing the PTY, instead of waiting 2 seconds.
|
||||
* Foot now sends `SIGTERM`/`SIGKILL` to the client application’s process group,
|
||||
instead of just to the client application’s process.
|
||||
* Foot now sends `SIGTERM`/`SIGKILL` to the client application's process group,
|
||||
instead of just to the client application's process.
|
||||
* `kmous` terminfo capability from `\E[M` to `\E[<`.
|
||||
* pt-or-px values (`letter-spacing`, etc) and the line thickness
|
||||
(`tweak.box-drawing-base-thickness`) in box drawing characters are
|
||||
now translated to pixel values using the monitor’s scaling factor
|
||||
now translated to pixel values using the monitor's scaling factor
|
||||
when `dpi-aware=no`, or `dpi-aware=auto` and the scaling factor is
|
||||
larger than 1 ([#680](https://codeberg.org/dnkl/foot/issues/680)).
|
||||
* Spawning a new terminal with a working directory that does not exist
|
||||
|
|
@ -1222,7 +1264,7 @@
|
|||
### Removed
|
||||
|
||||
* `km`/`smm`/`rmm` from terminfo; foot prefixes Alt-key combinations
|
||||
with `ESC`, and not by setting the 8:th “meta” bit, regardless of
|
||||
with `ESC`, and not by setting the 8:th "meta" bit, regardless of
|
||||
`smm`/`rmm`. While this _can_ be disabled by, resetting private mode
|
||||
1036, the terminfo should reflect the **default** behavior
|
||||
([#670](https://codeberg.org/dnkl/foot/issues/670)).
|
||||
|
|
@ -1389,10 +1431,10 @@ For full support, the following is required:
|
|||
|
||||
If `tweak.grapheme-shaping` has **not** been enabled, foot will
|
||||
neither use libutf8proc to do grapheme cluster segmentation, nor will
|
||||
it use fcft’s grapheme shaping capabilities to shape combining
|
||||
it use fcft's grapheme shaping capabilities to shape combining
|
||||
characters.
|
||||
|
||||
This feature is _experimental_ mostly due to the “wcwidth” problem;
|
||||
This feature is _experimental_ mostly due to the "wcwidth" problem;
|
||||
how many cells should foot allocate for a grapheme cluster? While the
|
||||
answer may seem simple, the problem is that, whatever the answer is,
|
||||
the client application **must** come up with the **same**
|
||||
|
|
@ -1470,9 +1512,9 @@ supported.
|
|||
* Point values in `line-height`, `letter-spacing`,
|
||||
`horizontal-letter-offset` and `vertical-letter-offset` are now
|
||||
rounded, not truncated, when translated to pixel values.
|
||||
* Foot’s exit code is now -26/230 when foot itself failed to launch
|
||||
* Foot's exit code is now -26/230 when foot itself failed to launch
|
||||
(due to invalid command line options, client application/shell not
|
||||
found etc). Footclient’s exit code is -36/220 when it itself fails
|
||||
found etc). Footclient's exit code is -36/220 when it itself fails
|
||||
to launch (e.g. bad command line option) and -26/230 when the foot
|
||||
server failed to instantiate a new window
|
||||
([#466](https://codeberg.org/dnkl/foot/issues/466)).
|
||||
|
|
@ -1525,7 +1567,7 @@ supported.
|
|||
resulting in PGO build failures.
|
||||
* Wrong colors in the 256-color cube
|
||||
([#479](https://codeberg.org/dnkl/foot/issues/479)).
|
||||
* Memory leak triggered by “opening” an OSC-8 URI and then resetting
|
||||
* Memory leak triggered by "opening" an OSC-8 URI and then resetting
|
||||
the terminal without closing the URI
|
||||
([#495](https://codeberg.org/dnkl/foot/issues/495)).
|
||||
* Assertion when emitting a sixel occupying the entire scrollback
|
||||
|
|
@ -1534,7 +1576,7 @@ supported.
|
|||
invisible) for certain combinations of fonts and font sizes
|
||||
([#503](https://codeberg.org/dnkl/foot/issues/503)).
|
||||
* Sixels with transparent bottom border being resized below the size
|
||||
specified in _”Set Raster Attributes”_.
|
||||
specified in _"Set Raster Attributes"_.
|
||||
* Fonts sometimes not being reloaded with the correct scaling factor
|
||||
when `dpi-aware=no`, or `dpi-aware=auto` with monitor(s) with a
|
||||
scaling factor > 1 ([#509](https://codeberg.org/dnkl/foot/issues/509)).
|
||||
|
|
@ -1730,7 +1772,7 @@ supported.
|
|||
background color for empty pixels instead of the default background
|
||||
color ([#391](https://codeberg.org/dnkl/foot/issues/391)).
|
||||
* Sixel decoding optimized; up to 100% faster in some cases.
|
||||
* Reported sixel “max geometry” from current window size, to the
|
||||
* Reported sixel "max geometry" from current window size, to the
|
||||
configured maximum size (defaulting to 10000x10000).
|
||||
|
||||
|
||||
|
|
@ -1829,7 +1871,7 @@ supported.
|
|||
* Pasting URIs from the clipboard when the source has not
|
||||
newline-terminated the last URI
|
||||
([#291](https://codeberg.org/dnkl/foot/issues/291)).
|
||||
* Sixel “current geometry” query response not being bounded by the
|
||||
* Sixel "current geometry" query response not being bounded by the
|
||||
current window dimensions (fixes `lsix` output)
|
||||
* Crash on keyboard input when repeat rate was zero (i.e. no repeat).
|
||||
* Wrong button encoding of mouse buttons 6 and 7 in mouse events.
|
||||
|
|
@ -1906,7 +1948,7 @@ means foot can be PGO:d in e.g. sandboxed build scripts. See
|
|||
and `CSI ? 737769 l` disables it. This can be used to
|
||||
e.g. enable/disable IME when entering/leaving insert mode in vim.
|
||||
* `dpi-aware` option to `foot.ini`. The default, `auto`, sizes fonts
|
||||
using the monitor’s DPI when output scaling has been
|
||||
using the monitor's DPI when output scaling has been
|
||||
**disabled**. If output scaling has been **enabled**, fonts are
|
||||
sized using the scaling factor. DPI-only font sizing can be forced
|
||||
by setting `dpi-aware=yes`. Setting `dpi-aware=no` forces font
|
||||
|
|
@ -1986,7 +2028,7 @@ means foot can be PGO:d in e.g. sandboxed build scripts. See
|
|||
`\E[38:2...m`) can now be used _without_ the color space ID
|
||||
parameter.
|
||||
* SGR 21 no longer disables **bold**. According to ECMA-48, SGR 21 is
|
||||
_”double underline_”. Foot does not (yet) implement that, but that’s
|
||||
_"double underline_". Foot does not (yet) implement that, but that's
|
||||
no reason to implement a non-standard behavior.
|
||||
* `DECRQM` now returns actual state of the requested mode, instead of
|
||||
always returning `2`.
|
||||
|
|
@ -2034,7 +2076,7 @@ means foot can be PGO:d in e.g. sandboxed build scripts. See
|
|||
([#194](https://codeberg.org/dnkl/foot/issues/194)).
|
||||
* Single-width characters with double-width glyphs are now allowed to
|
||||
overflow into neighboring cells by default. Set
|
||||
**tweak.allow-overflowing-double-width-glyphs** to ‘no’ to disable
|
||||
**tweak.allow-overflowing-double-width-glyphs** to 'no' to disable
|
||||
this.
|
||||
|
||||
### Fixed
|
||||
|
|
@ -2238,7 +2280,7 @@ means foot can be PGO:d in e.g. sandboxed build scripts. See
|
|||
binding has consumed it.
|
||||
* Input events from getting mixed with paste data
|
||||
([#101](https://codeberg.org/dnkl/foot/issues/101)).
|
||||
* Missing DPI values for “some” monitors on Gnome
|
||||
* Missing DPI values for "some" monitors on Gnome
|
||||
([#118](https://codeberg.org/dnkl/foot/issues/118)).
|
||||
* Handling of multi-column composed characters while reflowing.
|
||||
* Escape sequences sent for key combinations with `Return`, that did
|
||||
|
|
|
|||
32
INSTALL.md
32
INSTALL.md
|
|
@ -94,24 +94,24 @@ A note on terminfo; the terminfo database exposes terminal
|
|||
capabilities to the applications running inside the terminal. As such,
|
||||
it is important that the terminfo used reflects the actual
|
||||
terminal. Using the `xterm-256color` terminfo will, in many cases,
|
||||
work, but I still recommend using foot’s own terminfo. There are two
|
||||
work, but I still recommend using foot's own terminfo. There are two
|
||||
reasons for this:
|
||||
|
||||
* foot’s terminfo contains a couple of non-standard capabilities,
|
||||
* foot's terminfo contains a couple of non-standard capabilities,
|
||||
used by e.g. tmux.
|
||||
* New capabilities added to the `xterm-256color` terminfo could
|
||||
potentially break foot.
|
||||
* There may be future additions or changes to foot’s terminfo.
|
||||
* There may be future additions or changes to foot's terminfo.
|
||||
|
||||
As of ncurses 2021-07-31, ncurses includes a version of foot’s
|
||||
As of ncurses 2021-07-31, ncurses includes a version of foot's
|
||||
terminfo. **The recommendation is to use those**, and only install the
|
||||
terminfo definitions from this git repo if the system’s ncurses
|
||||
terminfo definitions from this git repo if the system's ncurses
|
||||
predates 2021-07-31.
|
||||
|
||||
But, note that the foot terminfo definitions in ncurses’ lack the
|
||||
But, note that the foot terminfo definitions in ncurses' lack the
|
||||
non-standard capabilities. This mostly affects tmux; without them,
|
||||
`terminal-overrides` must be configured to enable truecolor
|
||||
support. For this reason, it _is_ possible to install “our” terminfo
|
||||
support. For this reason, it _is_ possible to install "our" terminfo
|
||||
definitions as well, either in a non-default location, or under a
|
||||
different name.
|
||||
|
||||
|
|
@ -124,10 +124,10 @@ details.
|
|||
Installing them under a different name generally works well, but will
|
||||
break applications that check if `$TERM == foot`.
|
||||
|
||||
Hence the recommendation to simply use ncurses’ terminfo definitions
|
||||
Hence the recommendation to simply use ncurses' terminfo definitions
|
||||
if available.
|
||||
|
||||
If packaging “our” terminfo definitions, I recommend doing that as a
|
||||
If packaging "our" terminfo definitions, I recommend doing that as a
|
||||
separate package, to allow them to be installed on remote systems
|
||||
without having to install foot itself.
|
||||
|
||||
|
|
@ -176,9 +176,9 @@ meson ... -Ddefault-terminfo=foot -Dterminfo-base-name=foot-extra
|
|||
```
|
||||
(or just leave out `-Ddefault-terminfo`, since it defaults to `foot` anyway).
|
||||
|
||||
Finally, `-Dcustom-terminfo-install-location` enables foot’s terminfo
|
||||
to co-exist with ncurses’ version, without changing the terminfo
|
||||
names. The idea is that you install foot’s terminfo to a non-standard
|
||||
Finally, `-Dcustom-terminfo-install-location` enables foot's terminfo
|
||||
to co-exist with ncurses' version, without changing the terminfo
|
||||
names. The idea is that you install foot's terminfo to a non-standard
|
||||
location, for example `/usr/share/foot/terminfo`. Use
|
||||
`-Dcustom-terminfo-install-location` to tell foot where the terminfo
|
||||
is. Foot will set the environment variable `TERMINFO` to this value
|
||||
|
|
@ -194,7 +194,7 @@ in the meson build. It does **not** change the default value of
|
|||
`TERM`, and it does **not** disable `TERMINFO`, if
|
||||
`-Dcustom-terminfo-install-location` has been set. Use this if
|
||||
packaging the terminfo definitions in a separate package (and the
|
||||
build script isn’t shared with the ‘foot’ package).
|
||||
build script isn't shared with the 'foot' package).
|
||||
|
||||
Example:
|
||||
|
||||
|
|
@ -269,7 +269,7 @@ reason there are a number of helper scripts available.
|
|||
scripts in the `pgo` directory to do a complete PGO build. This script
|
||||
is intended to be used when doing manual builds.
|
||||
|
||||
Note that all “full” PGO builds (which `auto` will prefer, if
|
||||
Note that all "full" PGO builds (which `auto` will prefer, if
|
||||
possible) **require** `LC_CTYPE` to be set to an UTF-8 locale. This is
|
||||
**not** done automatically.
|
||||
|
||||
|
|
@ -370,7 +370,7 @@ fail.
|
|||
|
||||
The snippet above then creates an (empty) temporary file. Then, it
|
||||
runs a script that generates random escape sequences (if you cat
|
||||
`${tmp_file}` in a terminal, you’ll see random colored characters all
|
||||
`${tmp_file}` in a terminal, you'll see random colored characters all
|
||||
over the screen). Finally, we feed the randomly generated escape
|
||||
sequences to the PGO helper. This is what generates the profiling data
|
||||
used in the next step.
|
||||
|
|
@ -450,7 +450,7 @@ sed 's/@default_terminfo@/foot/g' foot.info | \
|
|||
tic -o <output-directory> -x -e foot,foot-direct -
|
||||
```
|
||||
|
||||
Where _”output-directory”_ **must** match the value passed to
|
||||
Where _"output-directory"_ **must** match the value passed to
|
||||
`-Dcustom-terminfo-install-location` in the foot build. If
|
||||
`-Dcustom-terminfo-install-location` has not been set, `-o
|
||||
<output-directory>` can simply be omitted.
|
||||
|
|
|
|||
66
README.md
66
README.md
|
|
@ -28,6 +28,7 @@ The fast, lightweight and minimalistic Wayland terminal emulator.
|
|||
1. [Shell integration](#shell-integration)
|
||||
1. [Current working directory](#current-working-directory)
|
||||
1. [Jumping between prompts](#jumping-between-prompts)
|
||||
1. [Piping last command's output](#piping-last-command-s-output)
|
||||
1. [Alt/meta](#alt-meta)
|
||||
1. [Backspace](#backspace)
|
||||
1. [Keypad](#keypad)
|
||||
|
|
@ -302,10 +303,10 @@ Foot supports URL detection. But, unlike many other terminal
|
|||
emulators, where URLs are highlighted when they are hovered and opened
|
||||
by clicking on them, foot uses a keyboard driven approach.
|
||||
|
||||
Pressing <kbd>ctrl</kbd>+<kbd>shift</kbd>+<kbd>o</kbd> enters _“URL
|
||||
mode”_, where all currently visible URLs are underlined, and is
|
||||
associated with a _“jump-label”_. The jump-label indicates the _key
|
||||
sequence_ (e.g. **”AF”**) to use to activate the URL.
|
||||
Pressing <kbd>ctrl</kbd>+<kbd>shift</kbd>+<kbd>o</kbd> enters _"URL
|
||||
mode"_, where all currently visible URLs are underlined, and is
|
||||
associated with a _"jump-label"_. The jump-label indicates the _key
|
||||
sequence_ (e.g. **"AF"**) to use to activate the URL.
|
||||
|
||||
The key binding can, of course, be customized, like all other key
|
||||
bindings in foot. See `show-urls-launch` and `show-urls-copy` in the
|
||||
|
|
@ -328,7 +329,7 @@ the jump label key sequences can be configured.
|
|||
|
||||
New foot terminal instances (bound to
|
||||
<kbd>ctrl</kbd>+<kbd>shift</kbd>+<kbd>n</kbd> by default) will open in
|
||||
the current working directory, **if** the shell in the “parent”
|
||||
the current working directory, **if** the shell in the "parent"
|
||||
terminal reports directory changes.
|
||||
|
||||
This is done with the OSC-7 escape sequence. Most shells can be
|
||||
|
|
@ -359,6 +360,42 @@ See the
|
|||
[wiki](https://codeberg.org/dnkl/foot/wiki#user-content-jumping-between-prompts)
|
||||
for details, and examples for other shells.
|
||||
|
||||
### Piping last command's output
|
||||
|
||||
The key binding `pipe-command-output` can pipe the last command's
|
||||
output to an application of your choice (similar to the other `pipe-*`
|
||||
key bindings):
|
||||
|
||||
```ini
|
||||
[key-bindings]
|
||||
pipe-command-output=[sh -c "f=$(mktemp); cat - > $f; footclient emacsclient -nw $f; rm $f"] Control+Shift+g
|
||||
```
|
||||
|
||||
When pressing <kbd>ctrl</kbd>+<kbd>shift</kbd>+<kbd>g</kbd>, the last
|
||||
command's output is written to a temporary file, then an emacsclient
|
||||
is started in a new footclient instance. The temporary file is removed
|
||||
after the footclient instance has closed.
|
||||
|
||||
For this to work, the shell must emit an OSC-133;C (`\E]133;C\E\\`)
|
||||
sequence before command output starts, and an OSC-133;D
|
||||
(`\E]133;D\E\\`) when the command output ends.
|
||||
|
||||
In fish, one way to do this is to add `preexec` and `postexec` hooks:
|
||||
|
||||
```fish
|
||||
function foot_cmd_start --on-event fish_preexec
|
||||
echo -en "\e]133;C\e\\"
|
||||
end
|
||||
|
||||
function foot_cmd_end --on-event fish_postexec
|
||||
echo -en "\e]133;D\e\\"
|
||||
end
|
||||
```
|
||||
|
||||
See the
|
||||
[wiki](https://codeberg.org/dnkl/foot/wiki#user-content-piping-last-command-s-output)
|
||||
for details, and examples for other shells
|
||||
|
||||
|
||||
## Alt/meta
|
||||
|
||||
|
|
@ -435,19 +472,19 @@ multiplied.
|
|||
For this reason, and because of the new _fractional scaling_ protocol
|
||||
(see below for details), and because this is how Wayland applications
|
||||
are expected to behave, foot >= 1.15 will default to scaling fonts
|
||||
using the compositor’s scaling factor, and **not** the monitor
|
||||
using the compositor's scaling factor, and **not** the monitor
|
||||
DPI.
|
||||
|
||||
This means the (assuming the monitors are at the same viewing
|
||||
distance) the font size will appear to change when you move the foot
|
||||
window across different monitors, **unless** you have configured the
|
||||
monitors’ scaling factors correctly in the compositor.
|
||||
monitors' scaling factors correctly in the compositor.
|
||||
|
||||
This can be changed by setting the `dpi-aware` option to `yes` in
|
||||
`foot.ini`. When enabled, fonts will **not** be sized using the
|
||||
scaling factor, but will instead be sized using the monitor’s
|
||||
scaling factor, but will instead be sized using the monitor's
|
||||
DPI. When the foot window is moved across monitors, the font size is
|
||||
updated for the current monitor’s DPI.
|
||||
updated for the current monitor's DPI.
|
||||
|
||||
This means that, assuming the monitors are **at the same viewing
|
||||
distance**, the font size will appear to be the same, at all times.
|
||||
|
|
@ -499,6 +536,7 @@ with the terminal emulator itself. Foot implements the following OSCs:
|
|||
* `OSC 117` - reset highlight background color
|
||||
* `OSC 119` - reset highlight foreground color
|
||||
* `OSC 133` - [shell integration](#shell-integration)
|
||||
* `OSC 176` - set app ID
|
||||
* `OSC 555` - flash screen (**foot specific**)
|
||||
* `OSC 777` - desktop notification (only the `;notify` sub-command of
|
||||
OSC 777 is supported.)
|
||||
|
|
@ -537,7 +575,7 @@ emulator actually responded to.
|
|||
|
||||
Starting with version 1.7.0, foot also implements `XTVERSION`, to
|
||||
which it will reply with `\EP>|foot(version)\E\\`. Version is
|
||||
e.g. “1.8.2” for a regular release, or “1.8.2-36-g7db8e06f” for a git
|
||||
e.g. "1.8.2" for a regular release, or "1.8.2-36-g7db8e06f" for a git
|
||||
build.
|
||||
|
||||
|
||||
|
|
@ -550,9 +588,9 @@ It allows querying the terminal for terminfo
|
|||
capabilities. Applications using this feature do not need to use the
|
||||
classic, file-based, terminfo definition. For example, if all
|
||||
applications used this feature, you would no longer have to install
|
||||
foot’s terminfo on remote hosts you SSH into.
|
||||
foot's terminfo on remote hosts you SSH into.
|
||||
|
||||
XTerm’s implementation (as of XTerm-370) only supports querying key
|
||||
XTerm's implementation (as of XTerm-370) only supports querying key
|
||||
(as in keyboard keys) capabilities, and three custom capabilities:
|
||||
|
||||
* `TN` - terminal name
|
||||
|
|
@ -564,7 +602,7 @@ Kitty has extended this, and also supports querying all integer and
|
|||
string capabilities.
|
||||
|
||||
Foot supports this, and extends it even further, to also include
|
||||
boolean capabilities. This means foot’s entire terminfo can be queried
|
||||
boolean capabilities. This means foot's entire terminfo can be queried
|
||||
via `XTGETTCAP`.
|
||||
|
||||
Note that both Kitty and foot handles **responses** to
|
||||
|
|
@ -576,7 +614,7 @@ capability/value pairs. There are a couple of issues with this:
|
|||
* The success/fail flag in the beginning of the response is always `1`
|
||||
(success), unless the very **first** queried capability is invalid.
|
||||
* XTerm will not respond **at all** to an invalid capability, unless
|
||||
it’s the first one in the `XTGETTCAP` query.
|
||||
it's the first one in the `XTGETTCAP` query.
|
||||
* XTerm will end the response at the first invalid capability.
|
||||
|
||||
In other words, if you send a large multi-capability query, you will
|
||||
|
|
|
|||
|
|
@ -2098,7 +2098,7 @@ draw_braille(struct buf *buf, char32_t wc)
|
|||
if (x_px_left >= 1) { x_spacing++; x_px_left--; }
|
||||
if (y_px_left >= 3) { y_spacing++; y_px_left -= 3; }
|
||||
|
||||
/* Fourth, margins (“spacing”, but on the sides) */
|
||||
/* Fourth, margins ("spacing", but on the sides) */
|
||||
if (x_px_left >= 2) { x_margin++; x_px_left -= 2; }
|
||||
if (y_px_left >= 2) { y_margin++; y_px_left -= 2; }
|
||||
|
||||
|
|
|
|||
4
char32.c
4
char32.c
|
|
@ -129,11 +129,11 @@ UNITTEST
|
|||
|
||||
UNITTEST
|
||||
{
|
||||
char32_t *c = c32dup(U"foobar");
|
||||
char32_t *c = xc32dup(U"foobar");
|
||||
xassert(c32cmp(c, U"foobar") == 0);
|
||||
free(c);
|
||||
|
||||
c = c32dup(U"");
|
||||
c = xc32dup(U"");
|
||||
xassert(c32cmp(c, U"") == 0);
|
||||
free(c);
|
||||
}
|
||||
|
|
|
|||
8
client.c
8
client.c
|
|
@ -315,11 +315,11 @@ main(int argc, char *const *argv)
|
|||
}
|
||||
|
||||
case 'l':
|
||||
if (optarg == NULL || strcmp(optarg, "auto") == 0)
|
||||
if (optarg == NULL || streq(optarg, "auto"))
|
||||
log_colorize = LOG_COLORIZE_AUTO;
|
||||
else if (strcmp(optarg, "never") == 0)
|
||||
else if (streq(optarg, "never"))
|
||||
log_colorize = LOG_COLORIZE_NEVER;
|
||||
else if (strcmp(optarg, "always") == 0)
|
||||
else if (streq(optarg, "always"))
|
||||
log_colorize = LOG_COLORIZE_ALWAYS;
|
||||
else {
|
||||
fprintf(stderr, "%s: argument must be one of 'never', 'always' or 'auto'\n", optarg);
|
||||
|
|
@ -419,7 +419,7 @@ main(int argc, char *const *argv)
|
|||
|
||||
if (resolved_path_cwd != NULL &&
|
||||
resolved_path_pwd != NULL &&
|
||||
strcmp(resolved_path_cwd, resolved_path_pwd) == 0)
|
||||
streq(resolved_path_cwd, resolved_path_pwd))
|
||||
{
|
||||
/*
|
||||
* The resolved path of $PWD matches the resolved path of
|
||||
|
|
|
|||
|
|
@ -23,7 +23,7 @@ cmd_scrollback_up(struct terminal *term, int rows)
|
|||
const int grid_rows = grid->num_rows;
|
||||
|
||||
/* The view row number in scrollback relative coordinates. This is
|
||||
* the maximum number of rows we’re allowed to scroll */
|
||||
* the maximum number of rows we're allowed to scroll */
|
||||
int sb_start = grid_sb_start_ignore_uninitialized(grid, term->rows);
|
||||
int view_sb_rel =
|
||||
grid_row_abs_to_sb_precalc_sb_start(grid, sb_start, view);
|
||||
|
|
|
|||
18
config.h
18
config.h
|
|
@ -44,12 +44,14 @@ struct config_font {
|
|||
};
|
||||
DEFINE_LIST(struct config_font);
|
||||
|
||||
#if 0
|
||||
struct config_key_modifiers {
|
||||
bool shift;
|
||||
bool alt;
|
||||
bool ctrl;
|
||||
bool super;
|
||||
};
|
||||
#endif
|
||||
|
||||
struct argv {
|
||||
char **args;
|
||||
|
|
@ -80,9 +82,12 @@ enum key_binding_type {
|
|||
MOUSE_BINDING,
|
||||
};
|
||||
|
||||
typedef tll(char *) config_modifier_list_t;
|
||||
|
||||
struct config_key_binding {
|
||||
int action; /* One of the varios bind_action_* enums from wayland.h */
|
||||
struct config_key_modifiers modifiers;
|
||||
//struct config_key_modifiers modifiers;
|
||||
config_modifier_list_t modifiers;
|
||||
union {
|
||||
/* Key bindings */
|
||||
struct {
|
||||
|
|
@ -134,6 +139,9 @@ struct config {
|
|||
unsigned pad_x;
|
||||
unsigned pad_y;
|
||||
bool center;
|
||||
|
||||
bool resize_by_cells;
|
||||
|
||||
uint16_t resize_delay_ms;
|
||||
|
||||
struct {
|
||||
|
|
@ -268,7 +276,8 @@ struct config {
|
|||
struct {
|
||||
bool hide_when_typing;
|
||||
bool alternate_scroll_mode;
|
||||
struct config_key_modifiers selection_override_modifiers;
|
||||
//struct config_key_modifiers selection_override_modifiers;
|
||||
config_modifier_list_t selection_override_modifiers;
|
||||
} mouse;
|
||||
|
||||
struct {
|
||||
|
|
@ -310,7 +319,7 @@ struct config {
|
|||
uint32_t buttons;
|
||||
uint32_t minimize;
|
||||
uint32_t maximize;
|
||||
uint32_t quit; /* ‘close’ collides with #define in epoll-shim */
|
||||
uint32_t quit; /* 'close' collides with #define in epoll-shim */
|
||||
uint32_t border;
|
||||
} color;
|
||||
|
||||
|
|
@ -380,10 +389,11 @@ struct config *config_clone(const struct config *old);
|
|||
bool config_font_parse(const char *pattern, struct config_font *font);
|
||||
void config_font_list_destroy(struct config_font_list *font_list);
|
||||
|
||||
#if 0
|
||||
struct seat;
|
||||
xkb_mod_mask_t
|
||||
conf_modifiers_to_mask(
|
||||
const struct seat *seat, const struct config_key_modifiers *modifiers);
|
||||
|
||||
#endif
|
||||
bool check_if_font_is_monospaced(
|
||||
const char *pattern, user_notifications_t *notifications);
|
||||
|
|
|
|||
59
csi.c
59
csi.c
|
|
@ -1081,44 +1081,29 @@ csi_dispatch(struct terminal *term, uint8_t final)
|
|||
break;
|
||||
|
||||
case 'h':
|
||||
/* Set mode */
|
||||
switch (vt_param_get(term, 0, 0)) {
|
||||
case 2: /* Keyboard Action Mode - AM */
|
||||
LOG_WARN("unimplemented: keyboard action mode (AM)");
|
||||
break;
|
||||
|
||||
case 4: /* Insert Mode - IRM */
|
||||
term->insert_mode = true;
|
||||
case 'l': {
|
||||
/* Set/Reset Mode (SM/RM) */
|
||||
int param = vt_param_get(term, 0, 0);
|
||||
bool sm = final == 'h';
|
||||
if (param == 4) {
|
||||
/* Insertion Replacement Mode (IRM) */
|
||||
term->insert_mode = sm;
|
||||
term_update_ascii_printer(term);
|
||||
break;
|
||||
}
|
||||
|
||||
case 12: /* Send/receive Mode - SRM */
|
||||
LOG_WARN("unimplemented: send/receive mode (SRM)");
|
||||
break;
|
||||
|
||||
case 20: /* Automatic Newline Mode - LNM */
|
||||
/* TODO: would be easy to implemented; when active
|
||||
* term_linefeed() would _also_ do a
|
||||
* term_carriage_return() */
|
||||
LOG_WARN("unimplemented: automatic newline mode (LNM)");
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
||||
case 'l':
|
||||
/* Reset mode */
|
||||
switch (vt_param_get(term, 0, 0)) {
|
||||
case 4: /* Insert Mode - IRM */
|
||||
term->insert_mode = false;
|
||||
term_update_ascii_printer(term);
|
||||
break;
|
||||
|
||||
case 2: /* Keyboard Action Mode - AM */
|
||||
case 12: /* Send/receive Mode - SRM */
|
||||
case 20: /* Automatic Newline Mode - LNM */
|
||||
break;
|
||||
/*
|
||||
* ECMA-48 defines modes 1-22, all of which were optional
|
||||
* (§7.1; "may have one state only") and are considered
|
||||
* deprecated (§7.1) in the latest (5th) edition. xterm only
|
||||
* documents modes 2, 4, 12 and 20, the last of which was
|
||||
* outright removed (§8.3.106) in 5th edition ECMA-48.
|
||||
*/
|
||||
if (sm) {
|
||||
LOG_WARN("SM with unimplemented mode: %d", param);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
case 'r': {
|
||||
int start = vt_param_get(term, 0, 1);
|
||||
|
|
@ -1551,8 +1536,8 @@ csi_dispatch(struct terminal *term, uint8_t final)
|
|||
break;
|
||||
|
||||
case 4: /* modifyOtherKeys */
|
||||
/* We don’t support fully disabling modifyOtherKeys,
|
||||
* but simply revert back to mode ‘1’ */
|
||||
/* We don't support fully disabling modifyOtherKeys,
|
||||
* but simply revert back to mode '1' */
|
||||
term->modify_other_keys_2 = false;
|
||||
LOG_DBG("modifyOtherKeys=1");
|
||||
break;
|
||||
|
|
@ -1630,7 +1615,7 @@ csi_dispatch(struct terminal *term, uint8_t final)
|
|||
break;
|
||||
}
|
||||
}
|
||||
break; /* private[0] == ‘<’ */
|
||||
break; /* private[0] == '<' */
|
||||
}
|
||||
|
||||
case ' ': {
|
||||
|
|
@ -1777,7 +1762,7 @@ csi_dispatch(struct terminal *term, uint8_t final)
|
|||
break;
|
||||
}
|
||||
|
||||
break; /* private[0] == ‘?’ && private[1] == ‘$’ */
|
||||
break; /* private[0] == '?' && private[1] == '$' */
|
||||
|
||||
default:
|
||||
UNHANDLED();
|
||||
|
|
|
|||
|
|
@ -101,7 +101,7 @@ cursor_string_to_server_shape(const char *xcursor)
|
|||
|
||||
for (size_t i = 0; i < ALEN(table); i++) {
|
||||
for (size_t j = 0; j < ALEN(table[i]); j++) {
|
||||
if (table[i][j] != NULL && strcmp(xcursor, table[i][j]) == 0) {
|
||||
if (table[i][j] != NULL && streq(xcursor, table[i][j])) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
8
dcs.c
8
dcs.c
|
|
@ -138,12 +138,12 @@ xtgettcap_reply(struct terminal *term, const char *hex_cap_name, size_t len)
|
|||
/*
|
||||
* Reply format:
|
||||
* \EP 1 + r cap=value \E\\
|
||||
* Where ‘cap’ and ‘value are hex encoded ascii strings
|
||||
* Where 'cap' and 'value are hex encoded ascii strings
|
||||
*/
|
||||
char *reply = xmalloc(
|
||||
5 + /* DCS 1 + r (\EP1+r) */
|
||||
len + /* capability name, hex encoded */
|
||||
1 + /* ‘=’ */
|
||||
1 + /* '=' */
|
||||
strlen(value) * 2 + /* capability value, hex encoded */
|
||||
2 + /* ST (\E\\) */
|
||||
1);
|
||||
|
|
@ -253,8 +253,8 @@ decrqss_unhook(struct terminal *term)
|
|||
/*
|
||||
* A note on the Ps parameter in the reply: many DEC manual
|
||||
* instances (e.g. https://vt100.net/docs/vt510-rm/DECRPSS) claim
|
||||
* that 0 means “request is valid”, and 1 means “request is
|
||||
* invalid”.
|
||||
* that 0 means "request is valid", and 1 means "request is
|
||||
* invalid".
|
||||
*
|
||||
* However, this appears to be a typo; actual hardware inverts the
|
||||
* response (as does XTerm and mlterm):
|
||||
|
|
|
|||
|
|
@ -687,6 +687,17 @@ All _OSC_ sequences begin with *\\E]*, sometimes abbreviated _OSC_.
|
|||
| \\E] 133 ; A \\E\\
|
||||
: FinalTerm
|
||||
: Mark start of shell prompt
|
||||
| \\E] 133 ; C \\E\\
|
||||
: FinalTerm
|
||||
: Mark start of command output
|
||||
| \\E] 133 ; D \\E\\
|
||||
: FinalTerm
|
||||
: Mark end of command output
|
||||
| \\E] 176 ; _app-id_ \\E\\
|
||||
: foot
|
||||
: Set app ID. _app-id_ is optional; if assigned,
|
||||
the terminal window App ID will be set to the value.
|
||||
An empty App ID resets the value to the default.
|
||||
| \\E] 555 \\E\\
|
||||
: foot
|
||||
: Flash the entire terminal (foot extension)
|
||||
|
|
|
|||
|
|
@ -313,10 +313,10 @@ Foot supports URL detection. But, unlike many other terminal
|
|||
emulators, where URLs are highlighted when they are hovered and opened
|
||||
by clicking on them, foot uses a keyboard driven approach.
|
||||
|
||||
Pressing *ctrl*+*shift*+*o* enters _“Open URL mode”_, where all currently
|
||||
Pressing *ctrl*+*shift*+*o* enters _"Open URL mode"_, where all currently
|
||||
visible URLs are underlined, and is associated with a
|
||||
_“jump-label”_. The jump-label indicates the _key sequence_
|
||||
(e.g. *”AF”*) to use to activate the URL.
|
||||
_"jump-label"_. The jump-label indicates the _key sequence_
|
||||
(e.g. *"AF"*) to use to activate the URL.
|
||||
|
||||
The key binding can, of course, be customized, like all other key
|
||||
bindings in foot. See *show-urls-launch* and *show-urls-copy* in
|
||||
|
|
@ -398,7 +398,7 @@ For more information, see *foot.ini*(5).
|
|||
|
||||
New foot terminal instances (bound to *ctrl*+*shift*+*n* by default)
|
||||
will open in the current working directory, if the shell in the
|
||||
“parent” terminal reports directory changes.
|
||||
"parent" terminal reports directory changes.
|
||||
|
||||
This is done with the OSC-7 escape sequence. Most shells can be
|
||||
scripted to do this, if they do not support it natively. See the wiki
|
||||
|
|
@ -424,6 +424,38 @@ See the wiki
|
|||
(https://codeberg.org/dnkl/foot/wiki#user-content-jumping-between-prompts)
|
||||
for details, and examples for other shells.
|
||||
|
||||
## Piping last command's output
|
||||
|
||||
The key binding *pipe-command-output* can pipe the last command's
|
||||
output to an application of your choice (similar to the other
|
||||
*pipe-\** key bindings):
|
||||
|
||||
*\[key-bindings\]++
|
||||
pipe-command-output=[sh -c "f=$(mktemp); cat - > $f; footclient emacsclient -nw $f; rm $f"] Control+Shift+g*
|
||||
|
||||
When pressing *ctrl*+*shift*+*g*, the last command's output is written
|
||||
to a temporary file, then an emacsclient is started in a new
|
||||
footclient instance. The temporary file is removed after the
|
||||
footclient instance has closed.
|
||||
|
||||
For this to work, the shell must emit an OSC-133;C (*\\E]133;C\\E\\\\*)
|
||||
sequence before command output starts, and an OSC-133;D
|
||||
(*\\E]133;D\\E\\\\*) when the command output ends.
|
||||
|
||||
In fish, one way to do this is to add _preexec_ and _postexec_ hooks:
|
||||
|
||||
*function foot_cmd_start --on-event fish_preexec
|
||||
echo -en "\\e]133;C\\e\\\\"
|
||||
end*
|
||||
|
||||
*function foot_cmd_end --on-event fish_postexec
|
||||
echo -en "\\e]133;D\\e\\\\"
|
||||
end*
|
||||
|
||||
See the wiki
|
||||
(https://codeberg.org/dnkl/foot/wiki#user-content-piping-last-commands-output)
|
||||
for details, and examples for other shells
|
||||
|
||||
# TERMINFO
|
||||
|
||||
Client applications use the terminfo identifier specified by the
|
||||
|
|
@ -464,10 +496,10 @@ also implemented (and extended, to some degree) by Kitty.
|
|||
|
||||
It allows querying the terminal for terminfo classic, file-based,
|
||||
terminfo definition. For example, if all applications used this
|
||||
feature, you would no longer have to install foot’s terminfo on remote
|
||||
feature, you would no longer have to install foot's terminfo on remote
|
||||
hosts you SSH into.
|
||||
|
||||
XTerm’s implementation (as of XTerm-370) only supports querying key
|
||||
XTerm's implementation (as of XTerm-370) only supports querying key
|
||||
(as in keyboard keys) capabilities, and three custom capabilities:
|
||||
|
||||
- TN - terminal name
|
||||
|
|
@ -479,7 +511,7 @@ Kitty has extended this, and also supports querying all integer and
|
|||
string capabilities.
|
||||
|
||||
Foot supports this, and extends it even further, to also include
|
||||
boolean capabilities. This means foot’s entire terminfo can be queried
|
||||
boolean capabilities. This means foot's entire terminfo can be queried
|
||||
via *XTGETTCAP*.
|
||||
|
||||
Note that both Kitty and foot handles responses to multi-capability
|
||||
|
|
@ -490,7 +522,7 @@ capability/value pairs. There are a couple of issues with this:
|
|||
|
||||
- The success/fail flag in the beginning of the response is always 1
|
||||
(success), unless the very first queried capability is invalid.
|
||||
- XTerm will not respond at all to an invalid capability, unless it’s
|
||||
- XTerm will not respond at all to an invalid capability, unless it's
|
||||
the first one in the XTGETTCAP query.
|
||||
- XTerm will end the response at the first invalid capability.
|
||||
|
||||
|
|
@ -554,16 +586,31 @@ In all other cases, the exit code is that of the client application
|
|||
set according to either the *--term* command-line option or the
|
||||
*term* config option in *foot.ini*(5).
|
||||
|
||||
*PWD*
|
||||
Current working directory (at the time of launching foot)
|
||||
|
||||
*COLORTERM*
|
||||
This variable is set to *truecolor*, to indicate to client
|
||||
applications that 24-bit RGB colors are supported.
|
||||
|
||||
*PWD*
|
||||
Current working directory (at the time of launching foot)
|
||||
|
||||
*SHELL*
|
||||
Set to the launched shell, if the shell is valid (it is listed in
|
||||
*/etc/shells*).
|
||||
|
||||
In addition to the variables listed above, custom environment
|
||||
variables may be defined in *foot.ini*(5).
|
||||
|
||||
## Variables *unset* in the child process
|
||||
|
||||
*TERM_PROGRAM*
|
||||
*TERM_PROGRAM_VERSION*
|
||||
These environment variables are set by certain other terminal
|
||||
emulators. We unset them, to prevent applications from
|
||||
misdetecting foot.
|
||||
|
||||
In addition to the variables listed above, custom environment
|
||||
variables to unset may be defined in *foot.ini*(5).
|
||||
|
||||
# BUGS
|
||||
|
||||
Please report bugs to https://codeberg.org/dnkl/foot/issues
|
||||
|
|
|
|||
|
|
@ -252,6 +252,21 @@ empty string to be set, but it must be quoted: *KEY=""*)
|
|||
|
||||
Default: _100_.
|
||||
|
||||
*resize-by-cells*
|
||||
Boolean.
|
||||
|
||||
When set to *yes*, the window size will be constrained to multiples
|
||||
of the cell size (plus any configured padding). When set to *no*,
|
||||
the window size will be unconstrained, and padding may be adjusted
|
||||
as necessary to accommodate window sizes that are not multiples of
|
||||
the cell size.
|
||||
|
||||
This option only applies to floating windows. Sizes of maxmized, tiled
|
||||
or fullscreen windows will not be constrained to multiples of the cell
|
||||
size.
|
||||
|
||||
Default: _yes_
|
||||
|
||||
*initial-window-size-pixels*
|
||||
Initial window width and height in _pixels_ (subject to output
|
||||
scaling), in the form _WIDTHxHEIGHT_. The height _includes_ the
|
||||
|
|
@ -836,11 +851,12 @@ e.g. *search-start=none*.
|
|||
*fullscreen*
|
||||
Toggles the fullscreen state. Default: _none_.
|
||||
|
||||
*pipe-visible*, *pipe-scrollback*, *pipe-selected*
|
||||
Pipes the currently visible text, the entire scrollback, or the
|
||||
currently selected text to an external tool. The syntax for this
|
||||
option is a bit special; the first part of the value is the
|
||||
command to execute enclosed in "[]", followed by the binding(s).
|
||||
*pipe-visible*, *pipe-scrollback*, *pipe-selected*, *pipe-command-output*
|
||||
Pipes the currently visible text, the entire scrollback, the
|
||||
currently selected text, or the last command's output to an
|
||||
external tool. The syntax for this option is a bit special; the
|
||||
first part of the value is the command to execute enclosed in
|
||||
"[]", followed by the binding(s).
|
||||
|
||||
You can configure multiple pipes as long as the command strings
|
||||
are different and the key bindings are unique.
|
||||
|
|
@ -848,9 +864,14 @@ e.g. *search-start=none*.
|
|||
Note that the command is *not* automatically run inside a shell;
|
||||
use *sh -c "command line"* if you need that.
|
||||
|
||||
Example:
|
||||
*pipe-visible=[sh -c "xurls | uniq | tac | fuzzel | xargs -r
|
||||
firefox"] Control+Print*
|
||||
Example #1:
|
||||
# Extract currently visible URLs, let user choose one (via
|
||||
fuzzel), then launch firefox with the selected URL++
|
||||
*pipe-visible=[sh -c "xurls | uniq | tac | fuzzel | xargs -r firefox"] Control+Print*
|
||||
|
||||
Example #2:
|
||||
# Open scrollback contents in Emacs running in a new foot instance++
|
||||
*pipe-scrollback=[sh -c "f=$(mktemp) && cat - > $f && foot emacsclient -t $f; rm $f"] Control+Shift+Print*
|
||||
|
||||
Default: _none_
|
||||
|
||||
|
|
|
|||
|
|
@ -73,6 +73,11 @@ terminal has terminated.
|
|||
The child process in the new terminal instance will use
|
||||
footclient's environment, instead of the server's.
|
||||
|
||||
Environment variables listed in the *Variables set in the child
|
||||
process* section will be overwritten by the foot server. For
|
||||
example, the new terminal will use *TERM* from the configuration,
|
||||
not footclient's environment.
|
||||
|
||||
*-d*,*--log-level*={*info*,*warning*,*error*,*none*}
|
||||
Log level, used both for log output on stderr as well as
|
||||
syslog. Default: _warning_.
|
||||
|
|
@ -163,9 +168,27 @@ fallback to the less specific path, with the following priority:
|
|||
This variable is set to *truecolor*, to indicate to client
|
||||
applications that 24-bit RGB colors are supported.
|
||||
|
||||
*PWD*
|
||||
Current working directory (at the time of launching foot)
|
||||
|
||||
*SHELL*
|
||||
Set to the launched shell, if the shell is valid (it is listed in
|
||||
*/etc/shells*).
|
||||
|
||||
In addition to the variables listed above, custom environment
|
||||
variables may be defined in *foot.ini*(5).
|
||||
|
||||
## Variables *unset* in the child process
|
||||
|
||||
*TERM_PROGRAM*
|
||||
*TERM_PROGRAM_VERSION*
|
||||
These environment variables are set by certain other terminal
|
||||
emulators. We unset them, to prevent applications from
|
||||
misdetecting foot.
|
||||
|
||||
In addition to the variables listed above, custom environment
|
||||
variables to unset may be defined in *foot.ini*(5).
|
||||
|
||||
# SEE ALSO
|
||||
|
||||
*foot*(1)
|
||||
|
|
|
|||
|
|
@ -40,7 +40,7 @@
|
|||
RV=\E[>c,
|
||||
Se=\E[ q,
|
||||
Ss=\E[%p1%d q,
|
||||
Sync=\E[?2026%?%p1%{1}%-%tl%eh,
|
||||
Sync=\E[?2026%?%p1%{1}%-%tl%eh%;,
|
||||
TS=\E]2;,
|
||||
XM=\E[?1006;1004;1000%?%p1%{1}%=%th%el%;,
|
||||
XR=\E[>0q,
|
||||
|
|
@ -241,6 +241,7 @@
|
|||
rmcup=\E[?1049l\E[23;0;0t,
|
||||
rmir=\E[4l,
|
||||
rmkx=\E[?1l\E>,
|
||||
rmm=\E[?1036h\E[?1034l,
|
||||
rmso=\E[27m,
|
||||
rmul=\E[24m,
|
||||
rmxx=\E[29m,
|
||||
|
|
@ -258,6 +259,7 @@
|
|||
smcup=\E[?1049h\E[22;0;0t,
|
||||
smir=\E[4h,
|
||||
smkx=\E[?1h\E=,
|
||||
smm=\E[?1036l\E[?1034h,
|
||||
smso=\E[7m,
|
||||
smul=\E[4m,
|
||||
smxx=\E[9m,
|
||||
|
|
|
|||
1
foot.ini
1
foot.ini
|
|
@ -160,6 +160,7 @@
|
|||
# pipe-visible=[sh -c "xurls | fuzzel | xargs -r firefox"] none
|
||||
# pipe-scrollback=[sh -c "xurls | fuzzel | xargs -r firefox"] none
|
||||
# pipe-selected=[xargs -r firefox] none
|
||||
# pipe-command-output=[wl-copy] none # Copy last command's output to the clipboard
|
||||
# show-urls-launch=Control+Shift+o
|
||||
# show-urls-copy=none
|
||||
# show-urls-persistent=none
|
||||
|
|
|
|||
102
grid.c
102
grid.c
|
|
@ -1,5 +1,6 @@
|
|||
#include "grid.h"
|
||||
|
||||
#include <limits.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
|
|
@ -16,7 +17,7 @@
|
|||
#define TIME_REFLOW 0
|
||||
|
||||
/*
|
||||
* “sb” (scrollback relative) coordinates
|
||||
* "sb" (scrollback relative) coordinates
|
||||
*
|
||||
* The scrollback relative row number 0 is the *first*, and *oldest*
|
||||
* row in the scrollback history (and thus the *first* row to be
|
||||
|
|
@ -231,7 +232,7 @@ grid_snapshot(const struct grid *grid)
|
|||
clone_row->cells = xmalloc(grid->num_cols * sizeof(clone_row->cells[0]));
|
||||
clone_row->linebreak = row->linebreak;
|
||||
clone_row->dirty = row->dirty;
|
||||
clone_row->prompt_marker = row->prompt_marker;
|
||||
clone_row->shell_integration = row->shell_integration;
|
||||
|
||||
for (int c = 0; c < grid->num_cols; c++)
|
||||
clone_row->cells[c] = row->cells[c];
|
||||
|
|
@ -366,7 +367,9 @@ grid_row_alloc(int cols, bool initialize)
|
|||
row->dirty = false;
|
||||
row->linebreak = false;
|
||||
row->extra = NULL;
|
||||
row->prompt_marker = false;
|
||||
row->shell_integration.prompt_marker = false;
|
||||
row->shell_integration.cmd_start = -1;
|
||||
row->shell_integration.cmd_end = -1;
|
||||
|
||||
if (initialize) {
|
||||
row->cells = xcalloc(cols, sizeof(row->cells[0]));
|
||||
|
|
@ -425,7 +428,9 @@ grid_resize_without_reflow(
|
|||
|
||||
new_row->dirty = old_row->dirty;
|
||||
new_row->linebreak = false;
|
||||
new_row->prompt_marker = old_row->prompt_marker;
|
||||
new_row->shell_integration.prompt_marker = old_row->shell_integration.prompt_marker;
|
||||
new_row->shell_integration.cmd_start = min(old_row->shell_integration.cmd_start, new_cols - 1);
|
||||
new_row->shell_integration.cmd_end = min(old_row->shell_integration.cmd_end, new_cols - 1);
|
||||
|
||||
if (new_cols > old_cols) {
|
||||
/* Clear "new" columns */
|
||||
|
|
@ -587,7 +592,9 @@ _line_wrap(struct grid *old_grid, struct row **new_grid, struct row *row,
|
|||
/* Scrollback is full, need to reuse a row */
|
||||
grid_row_reset_extra(new_row);
|
||||
new_row->linebreak = false;
|
||||
new_row->prompt_marker = false;
|
||||
new_row->shell_integration.prompt_marker = false;
|
||||
new_row->shell_integration.cmd_start = -1;
|
||||
new_row->shell_integration.cmd_end = -1;
|
||||
|
||||
tll_foreach(old_grid->sixel_images, it) {
|
||||
if (it->item.pos.row == *row_idx) {
|
||||
|
|
@ -599,7 +606,7 @@ _line_wrap(struct grid *old_grid, struct row **new_grid, struct row *row,
|
|||
/*
|
||||
* TODO: detect if the reused row is covered by the
|
||||
* selection. Of so, cancel the selection. The problem: we
|
||||
* don’t know if we’ve translated the selection coordinates
|
||||
* don't know if we've translated the selection coordinates
|
||||
* yet.
|
||||
*/
|
||||
}
|
||||
|
|
@ -609,7 +616,7 @@ _line_wrap(struct grid *old_grid, struct row **new_grid, struct row *row,
|
|||
return new_row;
|
||||
|
||||
/*
|
||||
* URI ranges are per row. Thus, we need to ‘close’ the still-open
|
||||
* URI ranges are per row. Thus, we need to 'close' the still-open
|
||||
* ranges on the previous row, and re-open them on the
|
||||
* next/current row.
|
||||
*/
|
||||
|
|
@ -789,7 +796,7 @@ grid_resize_and_reflow(
|
|||
}
|
||||
|
||||
if (!old_row->linebreak && col_count > 0) {
|
||||
/* Don’t truncate logical lines */
|
||||
/* Don't truncate logical lines */
|
||||
col_count = old_cols;
|
||||
}
|
||||
|
||||
|
|
@ -831,35 +838,26 @@ grid_resize_and_reflow(
|
|||
int end;
|
||||
bool tp_break = false;
|
||||
bool uri_break = false;
|
||||
bool ftcs_break = false;
|
||||
|
||||
/*
|
||||
* Set end-coordinate for this chunk, by finding the next
|
||||
* point-of-interest on this row.
|
||||
*
|
||||
* If there are no more tracking points, or URI ranges,
|
||||
* the end-coordinate will be at the end of the row,
|
||||
*/
|
||||
if (range != range_terminator) {
|
||||
int uri_col = (range->start >= start ? range->start : range->end) + 1;
|
||||
/* Figure out where to end this chunk */
|
||||
{
|
||||
const int uri_col = range != range_terminator
|
||||
? ((range->start >= start ? range->start : range->end) + 1)
|
||||
: INT_MAX;
|
||||
const int tp_col = tp != NULL ? tp->col + 1 : INT_MAX;
|
||||
const int ftcs_col = old_row->shell_integration.cmd_start >= start
|
||||
? old_row->shell_integration.cmd_start + 1
|
||||
: old_row->shell_integration.cmd_end >= start
|
||||
? old_row->shell_integration.cmd_end + 1
|
||||
: INT_MAX;
|
||||
|
||||
if (tp != NULL) {
|
||||
int tp_col = tp->col + 1;
|
||||
end = min(tp_col, uri_col);
|
||||
end = min(col_count, min(min(tp_col, uri_col), ftcs_col));
|
||||
|
||||
tp_break = end == tp_col;
|
||||
uri_break = end == uri_col;
|
||||
LOG_DBG("tp+uri break at %d (%d, %d)", end, tp_col, uri_col);
|
||||
} else {
|
||||
end = uri_col;
|
||||
uri_break = true;
|
||||
LOG_DBG("uri break at %d", end);
|
||||
}
|
||||
} else if (tp != NULL) {
|
||||
end = tp->col + 1;
|
||||
tp_break = true;
|
||||
LOG_DBG("TP break at %d", end);
|
||||
} else
|
||||
end = col_count;
|
||||
uri_break = end == uri_col;
|
||||
tp_break = end == tp_col;
|
||||
ftcs_break = end == ftcs_col;
|
||||
}
|
||||
|
||||
int cols = end - start;
|
||||
xassert(cols > 0);
|
||||
|
|
@ -887,8 +885,8 @@ grid_resize_and_reflow(
|
|||
xassert(amount > 0);
|
||||
|
||||
/*
|
||||
* If we’re going to reach the end of the new row, we
|
||||
* need to make sure we don’t end in the middle of a
|
||||
* If we're going to reach the end of the new row, we
|
||||
* need to make sure we don't end in the middle of a
|
||||
* multi-column character.
|
||||
*/
|
||||
int spacers = 0;
|
||||
|
|
@ -897,7 +895,7 @@ grid_resize_and_reflow(
|
|||
* While the cell *after* the last cell is a CELL_SPACER
|
||||
*
|
||||
* This means we have a multi-column character
|
||||
* that doesn’t fit on the current row. We need to
|
||||
* that doesn't fit on the current row. We need to
|
||||
* push it to the next row, and insert CELL_SPACER
|
||||
* cells as padding.
|
||||
*/
|
||||
|
|
@ -920,7 +918,7 @@ grid_resize_and_reflow(
|
|||
xassert(from + amount <= old_cols);
|
||||
|
||||
if (from == 0)
|
||||
new_row->prompt_marker = old_row->prompt_marker;
|
||||
new_row->shell_integration.prompt_marker = old_row->shell_integration.prompt_marker;
|
||||
|
||||
memcpy(
|
||||
&new_row->cells[new_col_idx], &old_row->cells[from],
|
||||
|
|
@ -979,6 +977,16 @@ grid_resize_and_reflow(
|
|||
}
|
||||
}
|
||||
|
||||
if (ftcs_break) {
|
||||
xassert(old_row->shell_integration.cmd_start == start + cols - 1 ||
|
||||
old_row->shell_integration.cmd_end == start + cols - 1);
|
||||
|
||||
if (old_row->shell_integration.cmd_start == start + cols - 1)
|
||||
new_row->shell_integration.cmd_start = new_col_idx - 1;
|
||||
if (old_row->shell_integration.cmd_end == start + cols - 1)
|
||||
new_row->shell_integration.cmd_end = new_col_idx - 1;
|
||||
}
|
||||
|
||||
left -= cols;
|
||||
start += cols;
|
||||
}
|
||||
|
|
@ -996,9 +1004,9 @@ grid_resize_and_reflow(
|
|||
{
|
||||
/*
|
||||
* line_wrap() "closes" still-open URIs. Since this is
|
||||
* the *last* row, and since we’re line-breaking due
|
||||
* the *last* row, and since we're line-breaking due
|
||||
* to a hard line-break (rather than running out of
|
||||
* cells in the "new_row"), there shouldn’t be an open
|
||||
* cells in the "new_row"), there shouldn't be an open
|
||||
* URI (it would have been closed when we reached the
|
||||
* end of the URI while reflowing the last "old"
|
||||
* row).
|
||||
|
|
@ -1025,7 +1033,7 @@ grid_resize_and_reflow(
|
|||
xassert(old_rows == 0 || *next_tp == &terminator);
|
||||
|
||||
#if defined(_DEBUG)
|
||||
/* Verify all URI ranges have been “closed” */
|
||||
/* Verify all URI ranges have been "closed" */
|
||||
for (int r = 0; r < new_rows; r++) {
|
||||
const struct row *row = new_grid[r];
|
||||
|
||||
|
|
@ -1069,7 +1077,7 @@ grid_resize_and_reflow(
|
|||
grid->num_cols = new_cols;
|
||||
|
||||
/*
|
||||
* Set new viewport, making sure it’s not too far down.
|
||||
* Set new viewport, making sure it's not too far down.
|
||||
*
|
||||
* This is done by using scrollback-start relative cooardinates,
|
||||
* and bounding the new viewport to (grid_rows - screen_rows).
|
||||
|
|
@ -1135,7 +1143,7 @@ grid_row_uri_range_put(struct row *row, int col, const char *uri, uint64_t id)
|
|||
const bool matching_id = r->id == id;
|
||||
|
||||
if (matching_id && r->end + 1 == col) {
|
||||
/* Extend existing URI’s tail */
|
||||
/* Extend existing URI's tail */
|
||||
r->end++;
|
||||
goto out;
|
||||
}
|
||||
|
|
@ -1174,7 +1182,7 @@ grid_row_uri_range_put(struct row *row, int col, const char *uri, uint64_t id)
|
|||
uri_range_insert(extra, i + 1, col + 1, r->end, r->id, r->uri);
|
||||
|
||||
/* The insertion may xrealloc() the vector, making our
|
||||
* ‘old’ pointer invalid */
|
||||
* 'old' pointer invalid */
|
||||
r = &extra->uri_ranges.v[i];
|
||||
r->end = col - 1;
|
||||
xassert(r->start <= r->end);
|
||||
|
|
@ -1311,7 +1319,7 @@ grid_row_uri_range_erase(struct row *row, int start, int end)
|
|||
extra, i + 1, end + 1, old->end, old->id, old->uri);
|
||||
|
||||
/* The insertion may xrealloc() the vector, making our
|
||||
* ‘old’ pointer invalid */
|
||||
* 'old' pointer invalid */
|
||||
old = &extra->uri_ranges.v[i];
|
||||
old->end = start - 1;
|
||||
return; /* There can be no more URIs affected by the erase range */
|
||||
|
|
@ -1394,11 +1402,11 @@ UNITTEST
|
|||
* The insertion logic typically triggers an xrealloc(), which, in
|
||||
* some cases, *moves* the entire URI vector to a new base
|
||||
* address. grid_row_uri_range_erase() did not account for this,
|
||||
* and tried to update the ‘end’ member in the URI range we just
|
||||
* and tried to update the 'end' member in the URI range we just
|
||||
* split. This causes foot to crash when the xrealloc() has moved
|
||||
* the URI range vector.
|
||||
*
|
||||
* (note: we’re only verifying we don’t crash here, hence the lack
|
||||
* (note: we're only verifying we don't crash here, hence the lack
|
||||
* of assertions).
|
||||
*/
|
||||
free(row_data.uri_ranges.v);
|
||||
|
|
|
|||
219
input.c
219
input.c
|
|
@ -263,7 +263,8 @@ execute_binding(struct seat *seat, struct terminal *term,
|
|||
break;
|
||||
/* FALLTHROUGH */
|
||||
case BIND_ACTION_PIPE_VIEW:
|
||||
case BIND_ACTION_PIPE_SELECTED: {
|
||||
case BIND_ACTION_PIPE_SELECTED:
|
||||
case BIND_ACTION_PIPE_COMMAND_OUTPUT: {
|
||||
if (binding->aux->type != BINDING_AUX_PIPE)
|
||||
return true;
|
||||
|
||||
|
|
@ -305,6 +306,10 @@ execute_binding(struct seat *seat, struct terminal *term,
|
|||
len = text != NULL ? strlen(text) : 0;
|
||||
break;
|
||||
|
||||
case BIND_ACTION_PIPE_COMMAND_OUTPUT:
|
||||
success = term_command_output_to_text(term, &text, &len);
|
||||
break;
|
||||
|
||||
default:
|
||||
BUG("Unhandled action type");
|
||||
success = false;
|
||||
|
|
@ -413,7 +418,7 @@ execute_binding(struct seat *seat, struct terminal *term,
|
|||
const struct row *row = grid->rows[r_abs];
|
||||
xassert(row != NULL);
|
||||
|
||||
if (!row->prompt_marker)
|
||||
if (!row->shell_integration.prompt_marker)
|
||||
continue;
|
||||
|
||||
grid->view = r_abs;
|
||||
|
|
@ -445,9 +450,9 @@ execute_binding(struct seat *seat, struct terminal *term,
|
|||
const struct row *row = grid->rows[r_abs];
|
||||
xassert(row != NULL);
|
||||
|
||||
if (!row->prompt_marker) {
|
||||
if (!row->shell_integration.prompt_marker) {
|
||||
if (r_abs == grid->offset + term->rows - 1) {
|
||||
/* We’ve reached the bottom of the scrollback */
|
||||
/* We've reached the bottom of the scrollback */
|
||||
break;
|
||||
}
|
||||
continue;
|
||||
|
|
@ -615,17 +620,19 @@ keyboard_keymap(void *data, struct wl_keyboard *wl_keyboard,
|
|||
seat->kbd.mod_caps = xkb_keymap_mod_get_index(seat->kbd.xkb_keymap, XKB_MOD_NAME_CAPS);
|
||||
seat->kbd.mod_num = xkb_keymap_mod_get_index(seat->kbd.xkb_keymap, XKB_MOD_NAME_NUM);
|
||||
|
||||
seat->kbd.bind_significant = 0;
|
||||
/* Significant modifiers in the legacy keyboard protocol */
|
||||
seat->kbd.legacy_significant = 0;
|
||||
if (seat->kbd.mod_shift != XKB_MOD_INVALID)
|
||||
seat->kbd.bind_significant |= 1 << seat->kbd.mod_shift;
|
||||
seat->kbd.legacy_significant |= 1 << seat->kbd.mod_shift;
|
||||
if (seat->kbd.mod_alt != XKB_MOD_INVALID)
|
||||
seat->kbd.bind_significant |= 1 << seat->kbd.mod_alt;
|
||||
seat->kbd.legacy_significant |= 1 << seat->kbd.mod_alt;
|
||||
if (seat->kbd.mod_ctrl != XKB_MOD_INVALID)
|
||||
seat->kbd.bind_significant |= 1 << seat->kbd.mod_ctrl;
|
||||
seat->kbd.legacy_significant |= 1 << seat->kbd.mod_ctrl;
|
||||
if (seat->kbd.mod_super != XKB_MOD_INVALID)
|
||||
seat->kbd.bind_significant |= 1 << seat->kbd.mod_super;
|
||||
seat->kbd.legacy_significant |= 1 << seat->kbd.mod_super;
|
||||
|
||||
seat->kbd.kitty_significant = seat->kbd.bind_significant;
|
||||
/* Significant modifiers in the kitty keyboard protocol */
|
||||
seat->kbd.kitty_significant = seat->kbd.legacy_significant;
|
||||
if (seat->kbd.mod_caps != XKB_MOD_INVALID)
|
||||
seat->kbd.kitty_significant |= 1 << seat->kbd.mod_caps;
|
||||
if (seat->kbd.mod_num != XKB_MOD_INVALID)
|
||||
|
|
@ -890,7 +897,7 @@ UNITTEST
|
|||
|
||||
const struct key_data *info = keymap_lookup(&term, XKB_KEY_ISO_Left_Tab, MOD_SHIFT | MOD_CTRL);
|
||||
xassert(info != NULL);
|
||||
xassert(strcmp(info->seq, "\033[27;6;9~") == 0);
|
||||
xassert(streq(info->seq, "\033[27;6;9~"));
|
||||
}
|
||||
|
||||
UNITTEST
|
||||
|
|
@ -901,18 +908,19 @@ UNITTEST
|
|||
|
||||
const struct key_data *info = keymap_lookup(&term, XKB_KEY_Return, MOD_ALT);
|
||||
xassert(info != NULL);
|
||||
xassert(strcmp(info->seq, "\033\r") == 0);
|
||||
xassert(streq(info->seq, "\033\r"));
|
||||
|
||||
term.modify_other_keys_2 = true;
|
||||
info = keymap_lookup(&term, XKB_KEY_Return, MOD_ALT);
|
||||
xassert(info != NULL);
|
||||
xassert(strcmp(info->seq, "\033[27;3;13~") == 0);
|
||||
xassert(streq(info->seq, "\033[27;3;13~"));
|
||||
}
|
||||
|
||||
void
|
||||
get_current_modifiers(const struct seat *seat,
|
||||
xkb_mod_mask_t *effective,
|
||||
xkb_mod_mask_t *consumed, uint32_t key)
|
||||
xkb_mod_mask_t *consumed, uint32_t key,
|
||||
bool filter_locked)
|
||||
{
|
||||
if (unlikely(seat->kbd.xkb_state == NULL)) {
|
||||
if (effective != NULL)
|
||||
|
|
@ -922,24 +930,27 @@ get_current_modifiers(const struct seat *seat,
|
|||
}
|
||||
|
||||
else {
|
||||
const xkb_mod_mask_t locked =
|
||||
xkb_state_serialize_mods(seat->kbd.xkb_state, XKB_STATE_MODS_LOCKED);
|
||||
|
||||
if (effective != NULL) {
|
||||
*effective = xkb_state_serialize_mods(
|
||||
seat->kbd.xkb_state, XKB_STATE_MODS_EFFECTIVE);
|
||||
|
||||
if (filter_locked)
|
||||
*effective &= ~locked;
|
||||
}
|
||||
|
||||
if (consumed != NULL) {
|
||||
*consumed = xkb_state_key_get_consumed_mods2(
|
||||
seat->kbd.xkb_state, key, XKB_CONSUMED_MODE_XKB);
|
||||
|
||||
if (filter_locked)
|
||||
*consumed &= ~locked;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static xkb_mod_mask_t
|
||||
get_locked_modifiers(const struct seat *seat)
|
||||
{
|
||||
return xkb_state_serialize_mods(seat->kbd.xkb_state, XKB_STATE_MODS_LOCKED);
|
||||
}
|
||||
|
||||
struct kbd_ctx {
|
||||
xkb_layout_index_t layout;
|
||||
xkb_keycode_t key;
|
||||
|
|
@ -1004,24 +1015,24 @@ legacy_kbd_protocol(struct seat *seat, struct terminal *term,
|
|||
|
||||
if (term->modify_other_keys_2) {
|
||||
/*
|
||||
* Try to mimic XTerm’s behavior, when holding shift:
|
||||
* Try to mimic XTerm's behavior, when holding shift:
|
||||
*
|
||||
* - if other modifiers are pressed (e.g. Alt), emit a CSI escape
|
||||
* - upper-case symbols A-Z are encoded as an CSI escape
|
||||
* - other upper-case symbols (e.g ‘Ö’) or emitted as is
|
||||
* - other upper-case symbols (e.g 'Ö') or emitted as is
|
||||
* - non-upper cased symbols are _mostly_ emitted as is (foot
|
||||
* always emits as is)
|
||||
*
|
||||
* Examples (assuming Swedish layout):
|
||||
* - Shift-a (‘A’) emits a CSI
|
||||
* - Shift-, (‘;’) emits ‘;’
|
||||
* - Shift-a ('A') emits a CSI
|
||||
* - Shift-, (';') emits ';'
|
||||
* - Shift-Alt-, (Alt-;) emits a CSI
|
||||
* - Shift-ö (‘Ö’) emits ‘Ö’
|
||||
* - Shift-ö ('Ö') emits 'Ö'
|
||||
*/
|
||||
|
||||
/* Any modifiers, besides shift active? */
|
||||
const xkb_mod_mask_t shift_mask = 1 << seat->kbd.mod_shift;
|
||||
if ((ctx->mods & ~shift_mask & seat->kbd.bind_significant) != 0)
|
||||
if ((ctx->mods & ~shift_mask & seat->kbd.legacy_significant) != 0)
|
||||
modify_other_keys2_in_effect = true;
|
||||
|
||||
else {
|
||||
|
|
@ -1029,9 +1040,9 @@ legacy_kbd_protocol(struct seat *seat, struct terminal *term,
|
|||
seat->kbd.xkb_state, ctx->key);
|
||||
|
||||
/*
|
||||
* Get pressed key’s base symbol.
|
||||
* - for ‘A’ (shift-a), that’s ‘a’
|
||||
* - for ‘;’ (shift-,), that’s ‘,’
|
||||
* Get pressed key's base symbol.
|
||||
* - for 'A' (shift-a), that's 'a'
|
||||
* - for ';' (shift-,), that's ','
|
||||
*/
|
||||
const xkb_keysym_t *base_syms = NULL;
|
||||
size_t base_count = xkb_keymap_key_get_syms_by_level(
|
||||
|
|
@ -1167,23 +1178,83 @@ kitty_kbd_protocol(struct seat *seat, struct terminal *term,
|
|||
if (composed && released)
|
||||
return false;
|
||||
|
||||
/* TODO: should we even bother with this, or just say it’s not supported? */
|
||||
/* TODO: should we even bother with this, or just say it's not supported? */
|
||||
if (!disambiguate && !report_all_as_escapes && pressed)
|
||||
return legacy_kbd_protocol(seat, term, ctx);
|
||||
|
||||
const xkb_mod_mask_t mods = ctx->mods & seat->kbd.kitty_significant;
|
||||
const xkb_mod_mask_t consumed = xkb_state_key_get_consumed_mods2(
|
||||
seat->kbd.xkb_state, ctx->key, XKB_CONSUMED_MODE_GTK) & seat->kbd.kitty_significant;
|
||||
const xkb_mod_mask_t effective = mods & ~consumed;
|
||||
const xkb_mod_mask_t caps_num =
|
||||
(seat->kbd.mod_caps != XKB_MOD_INVALID ? 1 << seat->kbd.mod_caps : 0) |
|
||||
(seat->kbd.mod_num != XKB_MOD_INVALID ? 1 << seat->kbd.mod_num : 0);
|
||||
|
||||
const xkb_keysym_t sym = ctx->sym;
|
||||
const uint32_t *utf32 = ctx->utf32;
|
||||
const uint8_t *const utf8 = ctx->utf8.buf;
|
||||
const size_t count = ctx->utf8.count;
|
||||
|
||||
/* Lookup sym in the pre-defined keysym table */
|
||||
const struct kitty_key_data *info = bsearch(
|
||||
&sym, kitty_keymap, ALEN(kitty_keymap), sizeof(kitty_keymap[0]),
|
||||
&kitty_search);
|
||||
xassert(info == NULL || info->sym == sym);
|
||||
|
||||
xkb_mod_mask_t mods = 0;
|
||||
xkb_mod_mask_t consumed = 0;
|
||||
|
||||
if (info != NULL && info->is_modifier) {
|
||||
/*
|
||||
* Special-case modifier keys.
|
||||
*
|
||||
* Normally, the "current" XKB state reflects the state
|
||||
* *before* the current key event. In other words, the
|
||||
* modifiers for key events that affect the modifier state
|
||||
* (e.g. one of the control keys, or shift keys etc) does
|
||||
* *not* include the key itself.
|
||||
*
|
||||
* Put another way, if you press "control", the modifier set
|
||||
* is empty in the key press event, but contains "ctrl" in the
|
||||
* release event.
|
||||
*
|
||||
* The kitty protocol mandates the modifier list contain the
|
||||
* key itself, in *both* the press and release event.
|
||||
*
|
||||
* We handle this by updating the XKB state to *include* the
|
||||
* current key, retrieve the set of modifiers (including the
|
||||
* set of consumed modifiers), and then revert the XKB update.
|
||||
*/
|
||||
xkb_state_update_key(
|
||||
seat->kbd.xkb_state, ctx->key, pressed ? XKB_KEY_DOWN : XKB_KEY_UP);
|
||||
|
||||
get_current_modifiers(seat, &mods, NULL, ctx->key, false);
|
||||
consumed = xkb_state_key_get_consumed_mods2(
|
||||
seat->kbd.xkb_state, ctx->key, XKB_CONSUMED_MODE_GTK);
|
||||
|
||||
#if 0
|
||||
/*
|
||||
* TODO: according to the XKB docs, state updates should
|
||||
* always be in pairs: each press should be followed by a
|
||||
* release. However, doing this just breaks the xkb state.
|
||||
*
|
||||
* *Not* pairing the above press/release with a corresponding
|
||||
* release/press appears to do exactly what we want.
|
||||
*/
|
||||
xkb_state_update_key(
|
||||
seat->kbd.xkb_state, ctx->key, pressed ? XKB_KEY_UP : XKB_KEY_DOWN);
|
||||
#endif
|
||||
} else {
|
||||
/* Same as ctx->mods, but without locked modifiers being
|
||||
filtered out */
|
||||
get_current_modifiers(seat, &mods, NULL, ctx->key, false);
|
||||
|
||||
/* Re-retrieve the consumed modifiers using the GTK mode, to
|
||||
better match kitty. */
|
||||
consumed = xkb_state_key_get_consumed_mods2(
|
||||
seat->kbd.xkb_state, ctx->key, XKB_CONSUMED_MODE_GTK);
|
||||
}
|
||||
|
||||
mods &= seat->kbd.kitty_significant;
|
||||
consumed &= seat->kbd.kitty_significant;
|
||||
|
||||
const xkb_mod_mask_t effective = mods & ~consumed;
|
||||
const xkb_mod_mask_t caps_num =
|
||||
(seat->kbd.mod_caps != XKB_MOD_INVALID ? 1 << seat->kbd.mod_caps : 0) |
|
||||
(seat->kbd.mod_num != XKB_MOD_INVALID ? 1 << seat->kbd.mod_num : 0);
|
||||
|
||||
bool is_text = count > 0 && utf32 != NULL && (effective & ~caps_num) == 0;
|
||||
for (size_t i = 0; utf32[i] != U'\0'; i++) {
|
||||
if (!iswprint(utf32[i])) {
|
||||
|
|
@ -1195,12 +1266,6 @@ kitty_kbd_protocol(struct seat *seat, struct terminal *term,
|
|||
const bool report_associated_text =
|
||||
(flags & KITTY_KBD_REPORT_ASSOCIATED) && is_text && !released;
|
||||
|
||||
/* Lookup sym in the pre-defined keysym table */
|
||||
const struct kitty_key_data *info = bsearch(
|
||||
&sym, kitty_keymap, ALEN(kitty_keymap), sizeof(kitty_keymap[0]),
|
||||
&kitty_search);
|
||||
xassert(info == NULL || info->sym == sym);
|
||||
|
||||
if (composing) {
|
||||
/* We never emit anything while composing, *except* modifiers
|
||||
* (and only in report-all-keys-as-escape-codes mode) */
|
||||
|
|
@ -1258,32 +1323,32 @@ emit_escapes:
|
|||
*
|
||||
* If the keysym is shifted, use its unshifted codepoint
|
||||
* instead. In other words, ctrl+a and ctrl+shift+a should
|
||||
* both use the same value for ‘key’ (97 - i.a. ‘a’).
|
||||
* both use the same value for 'key' (97 - i.a. 'a').
|
||||
*
|
||||
* However, don’t do this if a non-significant modifier was
|
||||
* However, don't do this if a non-significant modifier was
|
||||
* used to generate the symbol. This is needed since we cannot
|
||||
* encode non-significant modifiers, and thus the “extra”
|
||||
* encode non-significant modifiers, and thus the "extra"
|
||||
* modifier(s) would get lost.
|
||||
*
|
||||
* Example:
|
||||
*
|
||||
* the Swedish layout has ‘2’, QUOTATION MARK (“double
|
||||
* quote”), ‘@’, and ‘²’ on the same key. ‘2’ is the base
|
||||
* the Swedish layout has '2', QUOTATION MARK ("double
|
||||
* quote"), '@', and '²' on the same key. '2' is the base
|
||||
* symbol.
|
||||
*
|
||||
* Shift+2 results in QUOTATION MARK
|
||||
* AltGr+2 results in ‘@’
|
||||
* AltGr+Shift+2 results in ‘²’
|
||||
* AltGr+2 results in '@'
|
||||
* AltGr+Shift+2 results in '²'
|
||||
*
|
||||
* The kitty kbd protocol can’t encode AltGr. So, if we
|
||||
* always used the base symbol (‘2’), Alt+Shift+2 would
|
||||
* The kitty kbd protocol can't encode AltGr. So, if we
|
||||
* always used the base symbol ('2'), Alt+Shift+2 would
|
||||
* result in the same escape sequence as
|
||||
* AltGr+Alt+Shift+2.
|
||||
*
|
||||
* (yes, this matches what kitty does, as of 0.23.1)
|
||||
*/
|
||||
|
||||
/* Get the key’s shift level */
|
||||
/* Get the key's shift level */
|
||||
xkb_level_index_t lvl = xkb_state_key_get_level(
|
||||
seat->kbd.xkb_state, ctx->key, ctx->layout);
|
||||
|
||||
|
|
@ -1295,7 +1360,7 @@ emit_escapes:
|
|||
masks, ALEN(masks));
|
||||
|
||||
/* Check modifier combinations - if a combination has
|
||||
* modifiers not in our set of ‘significant’ modifiers,
|
||||
* modifiers not in our set of 'significant' modifiers,
|
||||
* use key sym as-is */
|
||||
bool use_level0_sym = true;
|
||||
for (size_t i = 0; i < mask_count; i++) {
|
||||
|
|
@ -1342,7 +1407,7 @@ emit_escapes:
|
|||
char event[4];
|
||||
if (report_events /*&& !pressed*/) {
|
||||
/* Note: this deviates slightly from Kitty, which omits the
|
||||
* “:1” subparameter for key press events */
|
||||
* ":1" subparameter for key press events */
|
||||
event[0] = ':';
|
||||
event[1] = '0' + (pressed ? 1 : repeating ? 2 : 3);
|
||||
event[2] = '\0';
|
||||
|
|
@ -1463,13 +1528,7 @@ key_press_release(struct seat *seat, struct terminal *term, uint32_t serial,
|
|||
const bool composed = compose_status == XKB_COMPOSE_COMPOSED;
|
||||
|
||||
xkb_mod_mask_t mods, consumed;
|
||||
get_current_modifiers(seat, &mods, &consumed, key);
|
||||
|
||||
const xkb_mod_mask_t locked = get_locked_modifiers(seat);
|
||||
const xkb_mod_mask_t bind_mods
|
||||
= mods & seat->kbd.bind_significant & ~locked;
|
||||
const xkb_mod_mask_t bind_consumed =
|
||||
consumed & seat->kbd.bind_significant & ~locked;
|
||||
get_current_modifiers(seat, &mods, &consumed, key, true);
|
||||
|
||||
xkb_layout_index_t layout_idx =
|
||||
xkb_state_key_get_layout(seat->kbd.xkb_state, key);
|
||||
|
|
@ -1493,7 +1552,7 @@ key_press_release(struct seat *seat, struct terminal *term, uint32_t serial,
|
|||
start_repeater(seat, key);
|
||||
|
||||
search_input(
|
||||
seat, term, bindings, key, sym, mods, consumed, locked,
|
||||
seat, term, bindings, key, sym, mods, consumed,
|
||||
raw_syms, raw_count, serial);
|
||||
return;
|
||||
}
|
||||
|
|
@ -1503,7 +1562,7 @@ key_press_release(struct seat *seat, struct terminal *term, uint32_t serial,
|
|||
start_repeater(seat, key);
|
||||
|
||||
urls_input(
|
||||
seat, term, bindings, key, sym, mods, consumed, locked,
|
||||
seat, term, bindings, key, sym, mods, consumed,
|
||||
raw_syms, raw_count, serial);
|
||||
return;
|
||||
}
|
||||
|
|
@ -1511,7 +1570,7 @@ key_press_release(struct seat *seat, struct terminal *term, uint32_t serial,
|
|||
|
||||
#if 0
|
||||
for (size_t i = 0; i < 32; i++) {
|
||||
if (mods & (1 << i)) {
|
||||
if (mods & (1u << i)) {
|
||||
LOG_INFO("%s", xkb_keymap_mod_get_name(seat->kbd.xkb_keymap, i));
|
||||
}
|
||||
}
|
||||
|
|
@ -1536,13 +1595,13 @@ key_press_release(struct seat *seat, struct terminal *term, uint32_t serial,
|
|||
|
||||
/* Match translated symbol */
|
||||
if (bind->k.sym == sym &&
|
||||
bind->mods == (bind_mods & ~bind_consumed) &&
|
||||
bind->mods == (mods & ~consumed) &&
|
||||
execute_binding(seat, term, bind, serial, 1))
|
||||
{
|
||||
goto maybe_repeat;
|
||||
}
|
||||
|
||||
if (bind->mods != bind_mods || bind_mods != (mods & ~locked))
|
||||
if (bind->mods != mods)
|
||||
continue;
|
||||
|
||||
/* Match untranslated symbols */
|
||||
|
|
@ -2006,7 +2065,7 @@ wl_pointer_motion(void *data, struct wl_pointer *wl_pointer,
|
|||
* event with a NULL surface - see wl_pointer_enter().
|
||||
*
|
||||
* In this case, we never set seat->mouse_focus (since we
|
||||
* can’t map the enter event to a specific window). */
|
||||
* can't map the enter event to a specific window). */
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -2115,7 +2174,7 @@ wl_pointer_motion(void *data, struct wl_pointer *wl_pointer,
|
|||
|
||||
if (cursor_is_on_new_cell) {
|
||||
/* Prevent multiple/different mouse bindings from
|
||||
* triggering if the mouse has moved “too much” (to
|
||||
* triggering if the mouse has moved "too much" (to
|
||||
* another cell) */
|
||||
seat->mouse.count = 0;
|
||||
}
|
||||
|
|
@ -2135,14 +2194,14 @@ wl_pointer_motion(void *data, struct wl_pointer *wl_pointer,
|
|||
if (!term->is_searching) {
|
||||
if (auto_scroll_direction != SELECTION_SCROLL_NOT) {
|
||||
/*
|
||||
* Start ‘selection auto-scrolling’
|
||||
* Start 'selection auto-scrolling'
|
||||
*
|
||||
* The speed of the scrolling is proportional to the
|
||||
* distance between the mouse and the grid; the
|
||||
* further away the mouse is, the faster we scroll.
|
||||
*
|
||||
* Note that the speed is measured in ‘intervals (in
|
||||
* ns) between each timed scroll of a single line’.
|
||||
* Note that the speed is measured in 'intervals (in
|
||||
* ns) between each timed scroll of a single line'.
|
||||
*
|
||||
* Thus, the further away the mouse is, the smaller
|
||||
* interval value we use.
|
||||
|
|
@ -2223,8 +2282,7 @@ static const struct key_binding *
|
|||
xassert(bindings != NULL);
|
||||
|
||||
xkb_mod_mask_t mods;
|
||||
get_current_modifiers(seat, &mods, NULL, 0);
|
||||
mods &= seat->kbd.bind_significant;
|
||||
get_current_modifiers(seat, &mods, NULL, 0, true);
|
||||
|
||||
/* Ignore selection override modifiers when
|
||||
* matching modifiers */
|
||||
|
|
@ -2277,8 +2335,7 @@ static const struct key_binding *
|
|||
continue;
|
||||
}
|
||||
|
||||
const struct config_key_modifiers no_mods = {0};
|
||||
if (memcmp(&binding->modifiers, &no_mods, sizeof(no_mods)) != 0) {
|
||||
if (tll_length(binding->modifiers) > 0) {
|
||||
/* Binding has modifiers */
|
||||
continue;
|
||||
}
|
||||
|
|
@ -2351,7 +2408,7 @@ wl_pointer_button(void *data, struct wl_pointer *wl_pointer,
|
|||
* clicking twice, waiting for the CSD timer, and finally
|
||||
* clicking once more, results in the following sequence
|
||||
* (keyboard and other irrelevant events filtered out, unless
|
||||
* they’re needed to prove a point):
|
||||
* they're needed to prove a point):
|
||||
*
|
||||
* dbg: input.c:1551: cancelling drag timer, moving window
|
||||
* dbg: input.c:759: keyboard_leave: keyboard=0x607000003580, serial=873, surface=0x6070000036d0
|
||||
|
|
@ -2383,12 +2440,12 @@ wl_pointer_button(void *data, struct wl_pointer *wl_pointer,
|
|||
* - GNOME does *not* send a pointer *enter* event after the drag
|
||||
* has stopped
|
||||
* - The second drag does *not* generate a pointer *leave* event
|
||||
* - The missing leave event means we’re still tracking LMB as
|
||||
* - The missing leave event means we're still tracking LMB as
|
||||
* being held down in our seat struct.
|
||||
* - This leads to an assert (debug builds) when LMB is clicked
|
||||
* again (seat’s button list already contains LMB).
|
||||
* again (seat's button list already contains LMB).
|
||||
*
|
||||
* Note: I’ve also observed variants of the above
|
||||
* Note: I've also observed variants of the above
|
||||
*/
|
||||
tll_foreach(seat->mouse.buttons, it) {
|
||||
if (it->item.button == button) {
|
||||
|
|
|
|||
6
input.h
6
input.h
|
|
@ -11,12 +11,12 @@
|
|||
* Custom defines for mouse wheel left/right buttons.
|
||||
*
|
||||
* Libinput does not define these. On Wayland, all scroll events (both
|
||||
* vertical and horizontal) are reported not as buttons, as ‘axis’
|
||||
* vertical and horizontal) are reported not as buttons, as 'axis'
|
||||
* events.
|
||||
*
|
||||
* Libinput _does_ define BTN_BACK and BTN_FORWARD, which is
|
||||
* what we use for vertical scroll events. But for horizontal scroll
|
||||
* events, there aren’t any pre-defined mouse buttons.
|
||||
* events, there aren't any pre-defined mouse buttons.
|
||||
*
|
||||
* Mouse buttons are in the range 0x110 - 0x11f, with joystick defines
|
||||
* starting at 0x120.
|
||||
|
|
@ -33,6 +33,6 @@ void input_repeat(struct seat *seat, uint32_t key);
|
|||
void get_current_modifiers(const struct seat *seat,
|
||||
xkb_mod_mask_t *effective,
|
||||
xkb_mod_mask_t *consumed,
|
||||
uint32_t key);
|
||||
uint32_t key, bool filter_locked);
|
||||
|
||||
enum cursor_shape xcursor_for_csd_border(struct terminal *term, int x, int y);
|
||||
|
|
|
|||
|
|
@ -243,27 +243,27 @@ maybe_repair_key_combo(const struct seat *seat,
|
|||
* modifier, and replace the shifted symbol with its unshifted
|
||||
* variant.
|
||||
*
|
||||
* For example, the combo is “Control+Shift+U”. In this case,
|
||||
* Shift is the modifier used to “shift” ‘u’ to ‘U’, after which
|
||||
* ‘Shift’ will have been “consumed”. Since we filter out consumed
|
||||
* For example, the combo is "Control+Shift+U". In this case,
|
||||
* Shift is the modifier used to "shift" 'u' to 'U', after which
|
||||
* 'Shift' will have been "consumed". Since we filter out consumed
|
||||
* modifiers when matching key combos, this key combo will never
|
||||
* trigger (we will never be able to match the ‘Shift’ modifier).
|
||||
* trigger (we will never be able to match the 'Shift' modifier).
|
||||
*
|
||||
* There are two correct variants of the above key combo:
|
||||
* - “Control+U” (upper case ‘U’)
|
||||
* - “Control+Shift+u” (lower case ‘u’)
|
||||
* - "Control+U" (upper case 'U')
|
||||
* - "Control+Shift+u" (lower case 'u')
|
||||
*
|
||||
* What we do here is, for each key *code*, check if there are any
|
||||
* (shifted) levels where it produces ‘sym’. If there are, check
|
||||
* (shifted) levels where it produces 'sym'. If there are, check
|
||||
* *which* sets of modifiers are needed to produce it, and compare
|
||||
* with ‘mods’.
|
||||
* with 'mods'.
|
||||
*
|
||||
* If there is at least one common modifier, it means ‘sym’ is a
|
||||
* “shifted” symbol, with the corresponding shifting modifier
|
||||
* If there is at least one common modifier, it means 'sym' is a
|
||||
* "shifted" symbol, with the corresponding shifting modifier
|
||||
* explicitly included in the key combo. I.e. the key combo will
|
||||
* never trigger.
|
||||
*
|
||||
* We then proceed and “repair” the key combo by replacing ‘sym’
|
||||
* We then proceed and "repair" the key combo by replacing 'sym'
|
||||
* with the corresponding unshifted symbol.
|
||||
*
|
||||
* To reduce the noise, we ignore all key codes where the shifted
|
||||
|
|
@ -283,7 +283,7 @@ maybe_repair_key_combo(const struct seat *seat,
|
|||
seat->kbd.xkb_keymap, code, layout_idx, 0, &base_syms);
|
||||
|
||||
if (base_count == 0 || sym == base_syms[0]) {
|
||||
/* No unshifted symbols, or unshifted symbol is same as ‘sym’ */
|
||||
/* No unshifted symbols, or unshifted symbol is same as 'sym' */
|
||||
continue;
|
||||
}
|
||||
|
||||
|
|
@ -313,7 +313,7 @@ maybe_repair_key_combo(const struct seat *seat,
|
|||
seat->kbd.xkb_keymap, code, layout_idx, level_idx,
|
||||
mod_masks, ALEN(mod_masks));
|
||||
|
||||
/* Check if key combo’s modifier set intersects */
|
||||
/* Check if key combo's modifier set intersects */
|
||||
for (size_t j = 0; j < mod_mask_count; j++) {
|
||||
if ((mod_masks[j] & mods) != mod_masks[j])
|
||||
continue;
|
||||
|
|
@ -359,19 +359,19 @@ key_cmp(struct key_binding a, struct key_binding b)
|
|||
* Sort bindings such that bindings with the same symbol are
|
||||
* sorted with the binding having the most modifiers comes first.
|
||||
*
|
||||
* This fixes an issue where the “wrong” key binding are triggered
|
||||
* when used with “consumed” modifiers.
|
||||
* This fixes an issue where the "wrong" key binding are triggered
|
||||
* when used with "consumed" modifiers.
|
||||
*
|
||||
* For example: if Control+BackSpace is bound before
|
||||
* Control+Shift+BackSpace, then the latter binding is never
|
||||
* triggered.
|
||||
*
|
||||
* Why? Because Shift is a consumed modifier. This means
|
||||
* Control+BackSpace is “the same” as Control+Shift+BackSpace.
|
||||
* Control+BackSpace is "the same" as Control+Shift+BackSpace.
|
||||
*
|
||||
* By sorting bindings with more modifiers first, we work around
|
||||
* the problem. But note that it is *just* a workaround, and I’m
|
||||
* not confident there aren’t cases where it doesn’t work.
|
||||
* the problem. But note that it is *just* a workaround, and I'm
|
||||
* not confident there aren't cases where it doesn't work.
|
||||
*
|
||||
* See https://codeberg.org/dnkl/foot/issues/1280
|
||||
*/
|
||||
|
|
@ -404,6 +404,24 @@ sort_binding_list(key_binding_list_t *list)
|
|||
tll_sort(*list, key_cmp);
|
||||
}
|
||||
|
||||
static xkb_mod_mask_t
|
||||
mods_to_mask(const struct seat *seat, const config_modifier_list_t *mods)
|
||||
{
|
||||
xkb_mod_mask_t mask = 0;
|
||||
tll_foreach(*mods, it) {
|
||||
xkb_mod_index_t idx = xkb_keymap_mod_get_index(seat->kbd.xkb_keymap, it->item);
|
||||
|
||||
if (idx == XKB_MOD_INVALID) {
|
||||
LOG_ERR("%s: invalid modifier name", it->item);
|
||||
continue;
|
||||
}
|
||||
|
||||
mask |= 1 << idx;
|
||||
}
|
||||
|
||||
return mask;
|
||||
}
|
||||
|
||||
static void NOINLINE
|
||||
convert_key_binding(struct key_set *set,
|
||||
const struct config_key_binding *conf_binding,
|
||||
|
|
@ -411,7 +429,7 @@ convert_key_binding(struct key_set *set,
|
|||
{
|
||||
const struct seat *seat = set->seat;
|
||||
|
||||
xkb_mod_mask_t mods = conf_modifiers_to_mask(seat, &conf_binding->modifiers);
|
||||
xkb_mod_mask_t mods = mods_to_mask(seat, &conf_binding->modifiers);
|
||||
xkb_keysym_t sym = maybe_repair_key_combo(seat, conf_binding->k.sym, mods);
|
||||
|
||||
struct key_binding binding = {
|
||||
|
|
@ -469,7 +487,7 @@ convert_mouse_binding(struct key_set *set,
|
|||
.type = MOUSE_BINDING,
|
||||
.action = conf_binding->action,
|
||||
.aux = &conf_binding->aux,
|
||||
.mods = conf_modifiers_to_mask(set->seat, &conf_binding->modifiers),
|
||||
.mods = mods_to_mask(set->seat, &conf_binding->modifiers),
|
||||
.m = {
|
||||
.button = conf_binding->m.button,
|
||||
.count = conf_binding->m.count,
|
||||
|
|
@ -509,7 +527,7 @@ load_keymap(struct key_set *set)
|
|||
convert_url_bindings(set);
|
||||
convert_mouse_bindings(set);
|
||||
|
||||
set->public.selection_overrides = conf_modifiers_to_mask(
|
||||
set->public.selection_overrides = mods_to_mask(
|
||||
set->seat, &set->conf->mouse.selection_override_modifiers);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -37,6 +37,7 @@ enum bind_action_normal {
|
|||
BIND_ACTION_PIPE_SCROLLBACK,
|
||||
BIND_ACTION_PIPE_VIEW,
|
||||
BIND_ACTION_PIPE_SELECTED,
|
||||
BIND_ACTION_PIPE_COMMAND_OUTPUT,
|
||||
BIND_ACTION_SHOW_URLS_COPY,
|
||||
BIND_ACTION_SHOW_URLS_LAUNCH,
|
||||
BIND_ACTION_SHOW_URLS_PERSISTENT,
|
||||
|
|
|
|||
|
|
@ -13,7 +13,7 @@ struct kitty_key_data {
|
|||
|
||||
_Static_assert(sizeof(struct kitty_key_data) == 7, "bad size");
|
||||
|
||||
/* Note! *Must* Be kept sorted (on ‘sym’) */
|
||||
/* Note! *Must* Be kept sorted (on 'sym') */
|
||||
static const struct kitty_key_data kitty_keymap[] = {
|
||||
{XKB_KEY_ISO_Level3_Shift, 57453, 'u', true},
|
||||
{XKB_KEY_ISO_Level5_Shift, 57454, 'u', true},
|
||||
|
|
|
|||
2
log.c
2
log.c
|
|
@ -199,7 +199,7 @@ log_level_from_string(const char *str)
|
|||
return -1;
|
||||
|
||||
for (int i = 0, n = map_len(); i < n; i++)
|
||||
if (strcmp(str, log_level_map[i].name) == 0)
|
||||
if (streq(str, log_level_map[i].name))
|
||||
return i;
|
||||
|
||||
return -1;
|
||||
|
|
|
|||
10
main.c
10
main.c
|
|
@ -351,11 +351,11 @@ main(int argc, char *const *argv)
|
|||
}
|
||||
|
||||
case 'l':
|
||||
if (optarg == NULL || strcmp(optarg, "auto") == 0)
|
||||
if (optarg == NULL || streq(optarg, "auto"))
|
||||
log_colorize = LOG_COLORIZE_AUTO;
|
||||
else if (strcmp(optarg, "never") == 0)
|
||||
else if (streq(optarg, "never"))
|
||||
log_colorize = LOG_COLORIZE_NEVER;
|
||||
else if (strcmp(optarg, "always") == 0)
|
||||
else if (streq(optarg, "always"))
|
||||
log_colorize = LOG_COLORIZE_ALWAYS;
|
||||
else {
|
||||
fprintf(stderr, "%s: argument must be one of 'never', 'always' or 'auto'\n", optarg);
|
||||
|
|
@ -427,7 +427,7 @@ main(int argc, char *const *argv)
|
|||
|
||||
/*
|
||||
* Try to force an UTF-8 locale. If we succeed, launch the
|
||||
* user’s shell as usual, but add a user-notification saying
|
||||
* user's shell as usual, but add a user-notification saying
|
||||
* the locale has been changed.
|
||||
*/
|
||||
for (size_t i = 0; i < ALEN(fallback_locales); i++) {
|
||||
|
|
@ -538,7 +538,7 @@ main(int argc, char *const *argv)
|
|||
|
||||
if (resolved_path_cwd != NULL &&
|
||||
resolved_path_pwd != NULL &&
|
||||
strcmp(resolved_path_cwd, resolved_path_pwd) == 0)
|
||||
streq(resolved_path_cwd, resolved_path_pwd))
|
||||
{
|
||||
/*
|
||||
* The resolved path of $PWD matches the resolved path of
|
||||
|
|
|
|||
4
notify.c
4
notify.c
|
|
@ -20,7 +20,7 @@ notify_notify(const struct terminal *term, const char *title, const char *body)
|
|||
LOG_DBG("notify: title=\"%s\", msg=\"%s\"", title, body);
|
||||
|
||||
if (term->conf->notify_focus_inhibit && term->kbd_focus) {
|
||||
/* No notifications while we’re focused */
|
||||
/* No notifications while we're focused */
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
@ -36,7 +36,7 @@ notify_notify(const struct terminal *term, const char *title, const char *body)
|
|||
if (!spawn_expand_template(
|
||||
&term->conf->notify, 4,
|
||||
(const char *[]){"app-id", "window-title", "title", "body"},
|
||||
(const char *[]){term->conf->app_id, term->window_title, title, body},
|
||||
(const char *[]){term->app_id ? term->app_id : term->conf->app_id, term->window_title, title, body},
|
||||
&argc, &argv))
|
||||
{
|
||||
return;
|
||||
|
|
|
|||
|
|
@ -33,10 +33,16 @@
|
|||
</screenshot>
|
||||
</screenshots>
|
||||
<releases>
|
||||
<release version="1.13.1" date="2022-08-31">
|
||||
</release>
|
||||
<release version="1.13.0" date="2022-08-07">
|
||||
</release>
|
||||
<release version="1.16.2" date="2023-10-17"/>
|
||||
<release version="1.16.1" date="2023-10-12"/>
|
||||
<release version="1.16.0" date="2023-10-11"/>
|
||||
<release version="1.15.3" date="2023-08-07"/>
|
||||
<release version="1.15.2" date="2023-07-30"/>
|
||||
<release version="1.15.1" date="2023-07-21"/>
|
||||
<release version="1.15.0" date="2023-07-14"/>
|
||||
<release version="1.14.0" date="2023-04-03"/>
|
||||
<release version="1.13.1" date="2022-08-31"/>
|
||||
<release version="1.13.0" date="2022-08-07"/>
|
||||
</releases>
|
||||
<launchable type="desktop-id">org.codeberg.dnkl.foot.desktop</launchable>
|
||||
<url type="homepage">https://codeberg.org/dnkl/foot</url>
|
||||
|
|
|
|||
38
osc.c
38
osc.c
|
|
@ -353,7 +353,7 @@ parse_rgb(const char *string, uint32_t *color, bool *_have_alpha,
|
|||
return false;
|
||||
}
|
||||
|
||||
/* Verify prefix is “rgb:” or “rgba:” */
|
||||
/* Verify prefix is "rgb:" or "rgba:" */
|
||||
if (have_alpha) {
|
||||
if (strncmp(string, "rgba:", 5) != 0)
|
||||
return false;
|
||||
|
|
@ -426,7 +426,7 @@ osc_set_pwd(struct terminal *term, char *string)
|
|||
return;
|
||||
}
|
||||
|
||||
if (strcmp(scheme, "file") == 0 && hostname_is_localhost(host)) {
|
||||
if (streq(scheme, "file") && hostname_is_localhost(host)) {
|
||||
LOG_DBG("OSC7: pwd: %s", path);
|
||||
free(term->cwd);
|
||||
term->cwd = path;
|
||||
|
|
@ -443,9 +443,9 @@ osc_uri(struct terminal *term, char *string)
|
|||
/*
|
||||
* \E]8;<params>;URI\e\\
|
||||
*
|
||||
* Params are key=value pairs, separated by ‘:’.
|
||||
* Params are key=value pairs, separated by ':'.
|
||||
*
|
||||
* The only defined key (as of 2020-05-31) is ‘id’, which is used
|
||||
* The only defined key (as of 2020-05-31) is 'id', which is used
|
||||
* to group split-up URIs:
|
||||
*
|
||||
* ╔═ file1 ════╗
|
||||
|
|
@ -483,7 +483,7 @@ osc_uri(struct terminal *term, char *string)
|
|||
|
||||
const char *value = operator + 1;
|
||||
|
||||
if (strcmp(key, "id") == 0)
|
||||
if (streq(key, "id"))
|
||||
id = sdbm_hash(value);
|
||||
}
|
||||
|
||||
|
|
@ -893,7 +893,7 @@ osc_dispatch(struct terminal *term)
|
|||
term->grid->cursor.point.row,
|
||||
term->grid->cursor.point.col);
|
||||
|
||||
term->grid->cur_row->prompt_marker = true;
|
||||
term->grid->cur_row->shell_integration.prompt_marker = true;
|
||||
break;
|
||||
|
||||
case 'B':
|
||||
|
|
@ -901,15 +901,37 @@ osc_dispatch(struct terminal *term)
|
|||
break;
|
||||
|
||||
case 'C':
|
||||
LOG_DBG("FTCS_COMMAND_EXECUTED");
|
||||
LOG_DBG("FTCS_COMMAND_EXECUTED: %dx%d",
|
||||
term->grid->cursor.point.row,
|
||||
term->grid->cursor.point.col);
|
||||
term->grid->cur_row->shell_integration.cmd_start = term->grid->cursor.point.col;
|
||||
break;
|
||||
|
||||
case 'D':
|
||||
LOG_DBG("FTCS_COMMAND_FINISHED");
|
||||
LOG_DBG("FTCS_COMMAND_FINISHED: %dx%d",
|
||||
term->grid->cursor.point.row,
|
||||
term->grid->cursor.point.col);
|
||||
term->grid->cur_row->shell_integration.cmd_end = term->grid->cursor.point.col;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
|
||||
case 176:
|
||||
if (string[0] == '?' && string[1] == '\0') {
|
||||
const char *terminator = term->vt.osc.bel ? "\a" : "\033\\";
|
||||
char *reply = xasprintf(
|
||||
"\033]176;%s%s",
|
||||
term->app_id != NULL ? term->app_id : term->conf->app_id,
|
||||
terminator);
|
||||
|
||||
term_to_slave(term, reply, strlen(reply));
|
||||
free(reply);
|
||||
break;
|
||||
}
|
||||
|
||||
term_set_app_id(term, string);
|
||||
break;
|
||||
|
||||
case 555:
|
||||
osc_flash(term);
|
||||
break;
|
||||
|
|
|
|||
|
|
@ -10,5 +10,5 @@ trap "rm -rf '${runtime_dir}'" EXIT INT HUP TERM
|
|||
|
||||
XDG_RUNTIME_DIR="${runtime_dir}" WLR_RENDERER=pixman WLR_BACKENDS=headless cage "${srcdir}"/pgo/full-inner.sh "${srcdir}" "${blddir}"
|
||||
|
||||
# Cage’s exit code doesn’t reflect our script’s exit code
|
||||
# Cage's exit code doesn't reflect our script's exit code
|
||||
[ -f "${blddir}"/pgo-ok ] || exit 1
|
||||
|
|
|
|||
|
|
@ -17,8 +17,8 @@ trap cleanup EXIT INT HUP TERM
|
|||
# Generate a custom config that executes our generate-pgo-data script
|
||||
> "${sway_conf}" echo "exec '${srcdir}'/pgo/full-headless-sway-inner.sh '${srcdir}' '${blddir}'"
|
||||
|
||||
# Run Sway. full-headless-sway-inner.sh ends with a ‘swaymsg exit’
|
||||
# Run Sway. full-headless-sway-inner.sh ends with a 'swaymsg exit'
|
||||
XDG_RUNTIME_DIR="${runtime_dir}" WLR_RENDERER=pixman WLR_BACKENDS=headless sway -c "${sway_conf}"
|
||||
|
||||
# Sway’s exit code doesn’t reflect our script’s exit code
|
||||
# Sway's exit code doesn't reflect our script's exit code
|
||||
[ -f "${blddir}"/pgo-ok ] || exit 1
|
||||
|
|
|
|||
|
|
@ -60,7 +60,8 @@ fdm_event_del(struct fdm *fdm, int fd, int events)
|
|||
}
|
||||
|
||||
bool
|
||||
render_resize_force(struct terminal *term, int width, int height)
|
||||
render_resize(
|
||||
struct terminal *term, int width, int height, uint8_t resize_options)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
|
@ -68,6 +69,7 @@ render_resize_force(struct terminal *term, int width, int height)
|
|||
void render_refresh(struct terminal *term) {}
|
||||
void render_refresh_csd(struct terminal *term) {}
|
||||
void render_refresh_title(struct terminal *term) {}
|
||||
void render_refresh_app_id(struct terminal *term) {}
|
||||
|
||||
bool
|
||||
render_xcursor_is_valid(const struct seat *seat, const char *cursor)
|
||||
|
|
@ -173,7 +175,8 @@ void search_selection_cancelled(struct terminal *term) {}
|
|||
|
||||
void get_current_modifiers(const struct seat *seat,
|
||||
xkb_mod_mask_t *effective,
|
||||
xkb_mod_mask_t *consumed, uint32_t key) {}
|
||||
xkb_mod_mask_t *consumed, uint32_t key,
|
||||
bool filter_locked) {}
|
||||
|
||||
static struct key_binding_set kbd;
|
||||
static bool kbd_initialized = false;
|
||||
|
|
|
|||
|
|
@ -98,8 +98,8 @@ if [ ${do_pgo} = yes ]; then
|
|||
ninja -C "${blddir}"
|
||||
|
||||
# If fcft/tllist are subprojects, we need to ensure their tests
|
||||
# have been executed, or we’ll get “profile count data file not
|
||||
# found” errors.
|
||||
# have been executed, or we'll get "profile count data file not
|
||||
# found" errors.
|
||||
ninja -C "${blddir}" test
|
||||
|
||||
# Run mode-dependent script to generate profiling data
|
||||
|
|
|
|||
224
render.c
224
render.c
|
|
@ -269,12 +269,12 @@ color_dim(const struct terminal *term, uint32_t color)
|
|||
continue;
|
||||
|
||||
if (term->colors.table[0 + i] == color) {
|
||||
/* “Regular” color, return the corresponding “dim” */
|
||||
/* "Regular" color, return the corresponding "dim" */
|
||||
return conf->colors.dim[i];
|
||||
}
|
||||
|
||||
else if (term->colors.table[8 + i] == color) {
|
||||
/* “Bright” color, return the corresponding “regular” */
|
||||
/* "Bright" color, return the corresponding "regular" */
|
||||
return term->colors.table[i];
|
||||
}
|
||||
}
|
||||
|
|
@ -1074,7 +1074,7 @@ grid_render_scroll(struct terminal *term, struct buffer *buf,
|
|||
|
||||
/*
|
||||
* TODO: remove this if re-enabling scroll damage when re-applying
|
||||
* last frame’s damage (see reapply_old_damage()
|
||||
* last frame's damage (see reapply_old_damage()
|
||||
*/
|
||||
pixman_region32_union_rect(
|
||||
&buf->dirty[0], &buf->dirty[0], 0, dst_y, buf->width, height);
|
||||
|
|
@ -1151,7 +1151,7 @@ grid_render_scroll_reverse(struct terminal *term, struct buffer *buf,
|
|||
|
||||
/*
|
||||
* TODO: remove this if re-enabling scroll damage when re-applying
|
||||
* last frame’s damage (see reapply_old_damage()
|
||||
* last frame's damage (see reapply_old_damage()
|
||||
*/
|
||||
pixman_region32_union_rect(
|
||||
&buf->dirty[0], &buf->dirty[0], 0, dst_y, buf->width, height);
|
||||
|
|
@ -1289,7 +1289,7 @@ render_sixel(struct terminal *term, pixman_image_t *pix,
|
|||
* If the last sixel row only partially covers the cell row,
|
||||
* 'erase' the cell by rendering them.
|
||||
*
|
||||
* In all cases, do *not* clear the ‘dirty’ bit on the row, to
|
||||
* In all cases, do *not* clear the 'dirty' bit on the row, to
|
||||
* ensure the regular renderer includes them in the damage
|
||||
* rect.
|
||||
*/
|
||||
|
|
@ -1404,8 +1404,8 @@ render_ime_preedit_for_seat(struct terminal *term, struct seat *seat,
|
|||
{
|
||||
/* Cursor will be drawn *after* the pre-edit string, i.e. in
|
||||
* the cell *after*. This means we need to copy, and dirty,
|
||||
* one extra cell from the original grid, or we’ll leave
|
||||
* trailing “cursors” after us if the user deletes text while
|
||||
* one extra cell from the original grid, or we'll leave
|
||||
* trailing "cursors" after us if the user deletes text while
|
||||
* pre-editing */
|
||||
cells_needed++;
|
||||
}
|
||||
|
|
@ -1461,7 +1461,7 @@ render_ime_preedit_for_seat(struct terminal *term, struct seat *seat,
|
|||
* from grid), and mark all cells as dirty. This ensures they are
|
||||
* re-rendered when the pre-edit text is modified or removed.
|
||||
*/
|
||||
struct cell *real_cells = malloc(cells_used * sizeof(real_cells[0]));
|
||||
struct cell *real_cells = xmalloc(cells_used * sizeof(real_cells[0]));
|
||||
for (int i = 0; i < cells_used; i++) {
|
||||
xassert(col_idx + i < term->cols);
|
||||
real_cells[i] = row->cells[col_idx + i];
|
||||
|
|
@ -1623,23 +1623,23 @@ render_overlay(struct terminal *term)
|
|||
* When possible, we only update the areas that have *changed*
|
||||
* since the last frame. That means:
|
||||
*
|
||||
* - clearing/erasing cells that are now selected, but weren’t
|
||||
* - clearing/erasing cells that are now selected, but weren't
|
||||
* in the last frame
|
||||
* - dimming cells that were selected, but aren’t anymore
|
||||
* - dimming cells that were selected, but aren't anymore
|
||||
*
|
||||
* To do this, we save the last frame’s selected cells as a
|
||||
* To do this, we save the last frame's selected cells as a
|
||||
* pixman region.
|
||||
*
|
||||
* Then, we calculate the corresponding region for this
|
||||
* frame’s selected cells.
|
||||
* frame's selected cells.
|
||||
*
|
||||
* Last frame’s region minus this frame’s region gives us the
|
||||
* Last frame's region minus this frame's region gives us the
|
||||
* region that needs to be *dimmed* in this frame
|
||||
*
|
||||
* This frame’s region minus last frame’s region gives us the
|
||||
* This frame's region minus last frame's region gives us the
|
||||
* region that needs to be *cleared* in this frame.
|
||||
*
|
||||
* Finally, the union of the two “diff” regions above, gives
|
||||
* Finally, the union of the two "diff" regions above, gives
|
||||
* us the total region affected by a change, in either way. We
|
||||
* use this as the bounding box for the
|
||||
* wl_surface_damage_buffer() call.
|
||||
|
|
@ -1652,12 +1652,12 @@ render_overlay(struct terminal *term)
|
|||
buf->age == 0;
|
||||
|
||||
if (!buffer_reuse) {
|
||||
/* Can’t reuse last frame’s damage - set to full window,
|
||||
/* Can't reuse last frame's damage - set to full window,
|
||||
* to ensure *everything* is updated */
|
||||
pixman_region32_init_rect(
|
||||
&old_see_through, 0, 0, buf->width, buf->height);
|
||||
} else {
|
||||
/* Use last frame’s saved region */
|
||||
/* Use last frame's saved region */
|
||||
pixman_region32_init(&old_see_through);
|
||||
pixman_region32_copy(&old_see_through, see_through);
|
||||
}
|
||||
|
|
@ -2148,7 +2148,7 @@ render_csd_border(struct terminal *term, enum csd_surface surf_idx,
|
|||
}
|
||||
|
||||
/*
|
||||
* The “visible” border.
|
||||
* The "visible" border.
|
||||
*/
|
||||
|
||||
float scale = term->scale;
|
||||
|
|
@ -2768,21 +2768,21 @@ reapply_old_damage(struct terminal *term, struct buffer *new, struct buffer *old
|
|||
pixman_region32_init(&dirty);
|
||||
|
||||
/*
|
||||
* Figure out current frame’s damage region
|
||||
* Figure out current frame's damage region
|
||||
*
|
||||
* If current frame doesn’t have any scroll damage, we can simply
|
||||
* subtract this frame’s damage from the last frame’s damage. That
|
||||
* way, we don’t have to copy areas from the old frame that’ll
|
||||
* If current frame doesn't have any scroll damage, we can simply
|
||||
* subtract this frame's damage from the last frame's damage. That
|
||||
* way, we don't have to copy areas from the old frame that'll
|
||||
* just get overwritten by current frame.
|
||||
*
|
||||
* Note that this is row based. A “half damaged” row is not
|
||||
* Note that this is row based. A "half damaged" row is not
|
||||
* excluded. I.e. the entire row will be copied from the old frame
|
||||
* to the new, and then when actually rendering the new frame, the
|
||||
* updated cells will overwrite parts of the copied row.
|
||||
*
|
||||
* Since we’re scanning the entire viewport anyway, we also track
|
||||
* Since we're scanning the entire viewport anyway, we also track
|
||||
* whether *all* cells are to be updated. In this case, just force
|
||||
* a full re-rendering, and don’t copy anything from the old
|
||||
* a full re-rendering, and don't copy anything from the old
|
||||
* frame.
|
||||
*/
|
||||
bool full_repaint_needed = true;
|
||||
|
|
@ -2815,28 +2815,28 @@ reapply_old_damage(struct terminal *term, struct buffer *new, struct buffer *old
|
|||
}
|
||||
|
||||
/*
|
||||
* TODO: re-apply last frame’s scroll damage
|
||||
* TODO: re-apply last frame's scroll damage
|
||||
*
|
||||
* We used to do this, but it turned out to be buggy. If we decide
|
||||
* to re-add it, this is where to do it. Note that we’d also have
|
||||
* to re-add it, this is where to do it. Note that we'd also have
|
||||
* to remove the updates to buf->dirty from grid_render_scroll()
|
||||
* and grid_render_scroll_reverse().
|
||||
*/
|
||||
|
||||
if (tll_length(term->grid->scroll_damage) == 0) {
|
||||
/*
|
||||
* We can only subtract current frame’s damage from the old
|
||||
* frame’s if we don’t have any scroll damage.
|
||||
* We can only subtract current frame's damage from the old
|
||||
* frame's if we don't have any scroll damage.
|
||||
*
|
||||
* If we do have scroll damage, the damage region we
|
||||
* calculated above is not (yet) valid - we need to apply the
|
||||
* current frame’s scroll damage *first*. This is done later,
|
||||
* current frame's scroll damage *first*. This is done later,
|
||||
* when rendering the frame.
|
||||
*/
|
||||
pixman_region32_subtract(&dirty, &old->dirty[0], &dirty);
|
||||
pixman_image_set_clip_region32(new->pix[0], &dirty);
|
||||
} else {
|
||||
/* Copy *all* of last frame’s damaged areas */
|
||||
/* Copy *all* of last frame's damaged areas */
|
||||
pixman_image_set_clip_region32(new->pix[0], &old->dirty[0]);
|
||||
}
|
||||
|
||||
|
|
@ -2895,7 +2895,7 @@ grid_render(struct terminal *term)
|
|||
struct buffer_chain *chain = term->render.chains.grid;
|
||||
struct buffer *buf = shm_get_buffer(chain, term->width, term->height);
|
||||
|
||||
/* Dirty old and current cursor cell, to ensure they’re repainted */
|
||||
/* Dirty old and current cursor cell, to ensure they're repainted */
|
||||
dirty_old_cursor(term);
|
||||
dirty_cursor(term);
|
||||
|
||||
|
|
@ -3014,11 +3014,11 @@ grid_render(struct terminal *term)
|
|||
* they are overflowing.
|
||||
*
|
||||
* As soon as we see a non-overflowing cell we can
|
||||
* stop, since it isn’t affecting the string of
|
||||
* stop, since it isn't affecting the string of
|
||||
* overflowing glyphs that follows it.
|
||||
*
|
||||
* As soon as we see a dirty cell, we can stop, since
|
||||
* that means we’ve already handled it (remember the
|
||||
* that means we've already handled it (remember the
|
||||
* outer loop goes from left to right).
|
||||
*/
|
||||
for (struct cell *c = cell - 1; c >= &row->cells[0]; c--) {
|
||||
|
|
@ -3036,9 +3036,9 @@ grid_render(struct terminal *term)
|
|||
* Note that the first non-overflowing cell must be
|
||||
* re-rendered as well, but any cell *after* that is
|
||||
* unaffected by the string of overflowing glyphs
|
||||
* we’re dealing with right now.
|
||||
* we're dealing with right now.
|
||||
*
|
||||
* For performance, this iterates the *outer* loop’s
|
||||
* For performance, this iterates the *outer* loop's
|
||||
* cell pointer - no point in re-checking all these
|
||||
* glyphs again, in the outer loop.
|
||||
*/
|
||||
|
|
@ -3213,9 +3213,9 @@ render_search_box(struct terminal *term)
|
|||
|
||||
/*
|
||||
* We treat the search box pretty much like a row of cells. That
|
||||
* is, a glyph is either 1 or 2 (or more) “cells” wide.
|
||||
* is, a glyph is either 1 or 2 (or more) "cells" wide.
|
||||
*
|
||||
* The search ‘length’, and ‘cursor’ (position) is in
|
||||
* The search 'length', and 'cursor' (position) is in
|
||||
* *characters*, not cells. This means we need to translate from
|
||||
* character count to cell count when calculating the length of
|
||||
* the search box, where in the search string we should start
|
||||
|
|
@ -3383,7 +3383,7 @@ render_search_box(struct terminal *term)
|
|||
}
|
||||
|
||||
/*
|
||||
* Render the search string, starting at ‘glyph_offset’. Note that
|
||||
* Render the search string, starting at 'glyph_offset'. Note that
|
||||
* glyph_offset is in cells, not characters
|
||||
*/
|
||||
for (size_t i = 0,
|
||||
|
|
@ -3656,7 +3656,7 @@ render_urls(struct terminal *term)
|
|||
int x = col * term->cell_width - 15 * term->cell_width / 10;
|
||||
int y = row * term->cell_height - 5 * term->cell_height / 10;
|
||||
|
||||
/* Don’t position it outside our window */
|
||||
/* Don't position it outside our window */
|
||||
if (x < -term->margins.left)
|
||||
x = -term->margins.left;
|
||||
if (y < -term->margins.top)
|
||||
|
|
@ -3697,12 +3697,12 @@ render_urls(struct terminal *term)
|
|||
label[i] = U' ';
|
||||
|
||||
/*
|
||||
* Don’t extend outside our window
|
||||
* Don't extend outside our window
|
||||
*
|
||||
* Truncate label so that it doesn’t extend outside our
|
||||
* Truncate label so that it doesn't extend outside our
|
||||
* window.
|
||||
*
|
||||
* Do it in a way such that we don’t cut the label in the
|
||||
* Do it in a way such that we don't cut the label in the
|
||||
* middle of a double-width character.
|
||||
*/
|
||||
|
||||
|
|
@ -3881,7 +3881,7 @@ delayed_reflow_of_normal_grid(struct terminal *term)
|
|||
term->selection.coords.end.row >= 0 ? ALEN(tracking_points) : 0,
|
||||
tracking_points);
|
||||
|
||||
/* Replace the current, truncated, “normal” grid with the
|
||||
/* Replace the current, truncated, "normal" grid with the
|
||||
* correctly reflowed one */
|
||||
grid_free(&term->normal);
|
||||
term->normal = *term->interactive_resizing.grid;
|
||||
|
|
@ -3944,7 +3944,7 @@ send_dimensions_to_client(struct terminal *term)
|
|||
win->resize_timeout_fd = -1;
|
||||
}
|
||||
} else {
|
||||
/* Send new dimensions to client “in a while” */
|
||||
/* Send new dimensions to client "in a while" */
|
||||
assert(win->is_resizing && term->conf->resize_delay_ms > 0);
|
||||
|
||||
int fd = win->resize_timeout_fd;
|
||||
|
|
@ -3988,9 +3988,25 @@ send_dimensions_to_client(struct terminal *term)
|
|||
}
|
||||
}
|
||||
|
||||
static void
|
||||
set_size_from_grid(struct terminal *term, int *width, int *height, int cols, int rows)
|
||||
{
|
||||
/* Nominal grid dimensions */
|
||||
*width = cols * term->cell_width;
|
||||
*height = rows * term->cell_height;
|
||||
|
||||
/* Include any configured padding */
|
||||
*width += 2 * term->conf->pad_x * term->scale;
|
||||
*height += 2 * term->conf->pad_y * term->scale;
|
||||
|
||||
/* Round to multiples of scale */
|
||||
*width = round(term->scale * round(*width / term->scale));
|
||||
*height = round(term->scale * round(*height / term->scale));
|
||||
}
|
||||
|
||||
/* Move to terminal.c? */
|
||||
static bool
|
||||
maybe_resize(struct terminal *term, int width, int height, bool force)
|
||||
bool
|
||||
render_resize(struct terminal *term, int width, int height, uint8_t opts)
|
||||
{
|
||||
if (term->shutdown.in_progress)
|
||||
return false;
|
||||
|
|
@ -4001,21 +4017,29 @@ maybe_resize(struct terminal *term, int width, int height, bool force)
|
|||
if (term->cell_width == 0 && term->cell_height == 0)
|
||||
return false;
|
||||
|
||||
const bool is_floating =
|
||||
!term->window->is_maximized &&
|
||||
!term->window->is_fullscreen &&
|
||||
!term->window->is_tiled;
|
||||
|
||||
/* Convert logical size to physical size */
|
||||
const float scale = term->scale;
|
||||
width = round(width * scale);
|
||||
height = round(height * scale);
|
||||
|
||||
/* If the grid should be kept, the size should be overridden */
|
||||
if (is_floating && (opts & RESIZE_KEEP_GRID)) {
|
||||
set_size_from_grid(term, &width, &height, term->cols, term->rows);
|
||||
}
|
||||
|
||||
if (width == 0 && height == 0) {
|
||||
/*
|
||||
* The compositor is letting us choose the size
|
||||
*
|
||||
* If we have a "last" used size - use that. Otherwise, use
|
||||
* the size from the user configuration.
|
||||
*/
|
||||
/* The compositor is letting us choose the size */
|
||||
if (term->stashed_width != 0 && term->stashed_height != 0) {
|
||||
/* If a default size is requested, prefer the "last used" size */
|
||||
width = term->stashed_width;
|
||||
height = term->stashed_height;
|
||||
} else {
|
||||
/* Otherwise, use a user-configured size */
|
||||
switch (term->conf->size.type) {
|
||||
case CONF_SIZE_PX:
|
||||
width = term->conf->size.width;
|
||||
|
|
@ -4035,15 +4059,8 @@ maybe_resize(struct terminal *term, int width, int height, bool force)
|
|||
break;
|
||||
|
||||
case CONF_SIZE_CELLS:
|
||||
width = term->conf->size.width * term->cell_width;
|
||||
height = term->conf->size.height * term->cell_height;
|
||||
|
||||
width += 2 * term->conf->pad_x * scale;
|
||||
height += 2 * term->conf->pad_y * scale;
|
||||
|
||||
/* Ensure width/height is a valid multiple of scale */
|
||||
width = roundf(scale * roundf(width / scale));
|
||||
height = roundf(scale * roundf(height / scale));
|
||||
set_size_from_grid(term, &width, &height,
|
||||
term->conf->size.width, term->conf->size.height);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
|
@ -4066,8 +4083,25 @@ maybe_resize(struct terminal *term, int width, int height, bool force)
|
|||
const int pad_x = min(max_pad_x, scale * term->conf->pad_x);
|
||||
const int pad_y = min(max_pad_y, scale * term->conf->pad_y);
|
||||
|
||||
if (!force && width == term->width && height == term->height && scale == term->scale)
|
||||
if (is_floating &&
|
||||
(opts & RESIZE_BY_CELLS) &&
|
||||
term->conf->resize_by_cells)
|
||||
{
|
||||
/* If resizing in cell increments, restrict the width and height */
|
||||
width = ((width - 2 * pad_x) / term->cell_width) * term->cell_width + 2 * pad_x;
|
||||
width = max(min_width, roundf(scale * roundf(width / scale)));
|
||||
|
||||
height = ((height - 2 * pad_y) / term->cell_height) * term->cell_height + 2 * pad_y;
|
||||
height = max(min_height, roundf(scale * roundf(height / scale)));
|
||||
}
|
||||
|
||||
if (!(opts & RESIZE_FORCE) &&
|
||||
width == term->width &&
|
||||
height == term->height &&
|
||||
scale == term->scale)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
/* Cancel an application initiated "Synchronized Update" */
|
||||
term_disable_app_sync_updates(term);
|
||||
|
|
@ -4125,8 +4159,8 @@ maybe_resize(struct terminal *term, int width, int height, bool force)
|
|||
|
||||
|
||||
/*
|
||||
* Since text reflow is slow, don’t do it *while* resizing. Only
|
||||
* do it when done, or after “pausing” the resize for sufficiently
|
||||
* Since text reflow is slow, don't do it *while* resizing. Only
|
||||
* do it when done, or after "pausing" the resize for sufficiently
|
||||
* long. We reuse the TIOCSWINSZ timer to handle this. See
|
||||
* send_dimensions_to_client() and fdm_tiocswinsz().
|
||||
*
|
||||
|
|
@ -4137,7 +4171,7 @@ maybe_resize(struct terminal *term, int width, int height, bool force)
|
|||
if (term->interactive_resizing.grid == NULL) {
|
||||
term_ptmx_pause(term);
|
||||
|
||||
/* Stash the current ‘normal’ grid, as-is, to be used when
|
||||
/* Stash the current 'normal' grid, as-is, to be used when
|
||||
* doing the final reflow */
|
||||
term->interactive_resizing.old_screen_rows = term->rows;
|
||||
term->interactive_resizing.old_cols = term->cols;
|
||||
|
|
@ -4148,7 +4182,7 @@ maybe_resize(struct terminal *term, int width, int height, bool force)
|
|||
if (term->grid == &term->normal)
|
||||
term->interactive_resizing.selection_coords = term->selection.coords;
|
||||
} else {
|
||||
/* We’ll replace the current temporary grid, with a new
|
||||
/* We'll replace the current temporary grid, with a new
|
||||
* one (again based on the original grid) */
|
||||
grid_free(&term->normal);
|
||||
}
|
||||
|
|
@ -4158,12 +4192,12 @@ maybe_resize(struct terminal *term, int width, int height, bool force)
|
|||
/*
|
||||
* Copy the current viewport (of the original grid) to a new
|
||||
* grid that will be used during the resize. For now, throw
|
||||
* away sixels and OSC-8 URLs. They’ll be "restored" when we
|
||||
* away sixels and OSC-8 URLs. They'll be "restored" when we
|
||||
* do the final reflow.
|
||||
*
|
||||
* Note that OSC-8 URLs are perfectly ok to throw away; they
|
||||
* cannot be interacted with during the resize. And, even if
|
||||
* url.osc8-underline=always, the “underline” attribute is
|
||||
* url.osc8-underline=always, the "underline" attribute is
|
||||
* part of the cell, not the URI struct (and thus our faked
|
||||
* grid will still render OSC-8 links underlined).
|
||||
*
|
||||
|
|
@ -4205,7 +4239,7 @@ maybe_resize(struct terminal *term, int width, int height, bool force)
|
|||
selection_cancel(term);
|
||||
else {
|
||||
/*
|
||||
* Don’t cancel, but make sure there aren’t any ongoing
|
||||
* Don't cancel, but make sure there aren't any ongoing
|
||||
* selections after the resize.
|
||||
*/
|
||||
tll_foreach(term->wl->seats, it) {
|
||||
|
|
@ -4217,7 +4251,7 @@ maybe_resize(struct terminal *term, int width, int height, bool force)
|
|||
/*
|
||||
* TODO: if we remove the selection_finalize() call above (i.e. if
|
||||
* we start allowing selections to be ongoing across resizes), the
|
||||
* selection’s pivot point coordinates *must* be added to the
|
||||
* selection's pivot point coordinates *must* be added to the
|
||||
* tracking points list.
|
||||
*/
|
||||
/* Resize grids */
|
||||
|
|
@ -4237,7 +4271,7 @@ maybe_resize(struct terminal *term, int width, int height, bool force)
|
|||
int old_normal_rows = old_rows;
|
||||
|
||||
if (term->interactive_resizing.grid != NULL) {
|
||||
/* Throw away the current, truncated, “normal” grid, and
|
||||
/* Throw away the current, truncated, "normal" grid, and
|
||||
* use the original grid instead (from before the resize
|
||||
* started) */
|
||||
grid_free(&term->normal);
|
||||
|
|
@ -4301,10 +4335,7 @@ damage_view:
|
|||
/* Signal TIOCSWINSZ */
|
||||
send_dimensions_to_client(term);
|
||||
|
||||
if (!term->window->is_maximized &&
|
||||
!term->window->is_fullscreen &&
|
||||
!term->window->is_tiled)
|
||||
{
|
||||
if (is_floating) {
|
||||
/* Stash current size, to enable us to restore it when we're
|
||||
* being un-maximized/fullscreened/tiled */
|
||||
term->stashed_width = term->width;
|
||||
|
|
@ -4367,18 +4398,6 @@ damage_view:
|
|||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
render_resize(struct terminal *term, int width, int height)
|
||||
{
|
||||
return maybe_resize(term, width, height, false);
|
||||
}
|
||||
|
||||
bool
|
||||
render_resize_force(struct terminal *term, int width, int height)
|
||||
{
|
||||
return maybe_resize(term, width, height, true);
|
||||
}
|
||||
|
||||
static void xcursor_callback(
|
||||
void *data, struct wl_callback *wl_callback, uint32_t callback_data);
|
||||
static const struct wl_callback_listener xcursor_listener = {
|
||||
|
|
@ -4565,9 +4584,6 @@ fdm_hook_refresh_pending_terminals(struct fdm *fdm, void *data)
|
|||
void
|
||||
render_refresh_title(struct terminal *term)
|
||||
{
|
||||
if (term->render.title.is_armed)
|
||||
return;
|
||||
|
||||
struct timespec now;
|
||||
if (clock_gettime(CLOCK_MONOTONIC, &now) < 0)
|
||||
return;
|
||||
|
|
@ -4589,6 +4605,28 @@ render_refresh_title(struct terminal *term)
|
|||
render_refresh_csd(term);
|
||||
}
|
||||
|
||||
void
|
||||
render_refresh_app_id(struct terminal *term)
|
||||
{
|
||||
struct timespec now;
|
||||
if (clock_gettime(CLOCK_MONOTONIC, &now) < 0)
|
||||
return;
|
||||
|
||||
struct timespec diff;
|
||||
timespec_sub(&now, &term->render.app_id.last_update, &diff);
|
||||
|
||||
if (diff.tv_sec == 0 && diff.tv_nsec < 8333 * 1000) {
|
||||
const struct itimerspec timeout = {
|
||||
.it_value = {.tv_nsec = 8333 * 1000 - diff.tv_nsec},
|
||||
};
|
||||
|
||||
timerfd_settime(term->render.app_id.timer_fd, 0, &timeout, NULL);
|
||||
} else {
|
||||
term->render.app_id.last_update = now;
|
||||
xdg_toplevel_set_app_id(term->window->xdg_toplevel, term->app_id ? term->app_id : term->conf->app_id);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
render_refresh(struct terminal *term)
|
||||
{
|
||||
|
|
@ -4635,8 +4673,8 @@ render_xcursor_set(struct seat *seat, struct terminal *term,
|
|||
|
||||
if (seat->pointer.shape == shape &&
|
||||
!(shape == CURSOR_SHAPE_CUSTOM &&
|
||||
strcmp(seat->pointer.last_custom_xcursor,
|
||||
term->mouse_user_cursor) != 0))
|
||||
!streq(seat->pointer.last_custom_xcursor,
|
||||
term->mouse_user_cursor)))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
|
|
|||
12
render.h
12
render.h
|
|
@ -10,10 +10,18 @@ struct renderer;
|
|||
struct renderer *render_init(struct fdm *fdm, struct wayland *wayl);
|
||||
void render_destroy(struct renderer *renderer);
|
||||
|
||||
bool render_resize(struct terminal *term, int width, int height);
|
||||
bool render_resize_force(struct terminal *term, int width, int height);
|
||||
enum resize_options {
|
||||
RESIZE_NORMAL = 0,
|
||||
RESIZE_FORCE = 1 << 0,
|
||||
RESIZE_BY_CELLS = 1 << 1,
|
||||
RESIZE_KEEP_GRID = 1 << 2,
|
||||
};
|
||||
|
||||
bool render_resize(
|
||||
struct terminal *term, int width, int height, uint8_t resize_options);
|
||||
|
||||
void render_refresh(struct terminal *term);
|
||||
void render_refresh_app_id(struct terminal *term);
|
||||
void render_refresh_csd(struct terminal *term);
|
||||
void render_refresh_search(struct terminal *term);
|
||||
void render_refresh_title(struct terminal *term);
|
||||
|
|
|
|||
9
search.c
9
search.c
|
|
@ -1374,17 +1374,12 @@ void
|
|||
search_input(struct seat *seat, struct terminal *term,
|
||||
const struct key_binding_set *bindings, uint32_t key,
|
||||
xkb_keysym_t sym, xkb_mod_mask_t mods, xkb_mod_mask_t consumed,
|
||||
xkb_mod_mask_t locked,
|
||||
const xkb_keysym_t *raw_syms, size_t raw_count,
|
||||
uint32_t serial)
|
||||
{
|
||||
LOG_DBG("search: input: sym=%d/0x%x, mods=0x%08x, consumed=0x%08x",
|
||||
sym, sym, mods, consumed);
|
||||
|
||||
const xkb_mod_mask_t bind_mods =
|
||||
mods & seat->kbd.bind_significant & ~locked;
|
||||
const xkb_mod_mask_t bind_consumed =
|
||||
consumed & seat->kbd.bind_significant & ~locked;
|
||||
enum xkb_compose_status compose_status = seat->kbd.xkb_compose_state != NULL
|
||||
? xkb_compose_state_get_status(seat->kbd.xkb_compose_state)
|
||||
: XKB_COMPOSE_NOTHING;
|
||||
|
|
@ -1399,7 +1394,7 @@ search_input(struct seat *seat, struct terminal *term,
|
|||
|
||||
/* Match translated symbol */
|
||||
if (bind->k.sym == sym &&
|
||||
bind->mods == (bind_mods & ~bind_consumed)) {
|
||||
bind->mods == (mods & ~consumed)) {
|
||||
|
||||
if (execute_binding(seat, term, bind, serial,
|
||||
&update_search_result, &search_direction,
|
||||
|
|
@ -1410,7 +1405,7 @@ search_input(struct seat *seat, struct terminal *term,
|
|||
return;
|
||||
}
|
||||
|
||||
if (bind->mods != bind_mods || bind_mods != (mods & ~locked))
|
||||
if (bind->mods != mods)
|
||||
continue;
|
||||
|
||||
/* Match untranslated symbols */
|
||||
|
|
|
|||
1
search.h
1
search.h
|
|
@ -11,7 +11,6 @@ void search_input(
|
|||
struct seat *seat, struct terminal *term,
|
||||
const struct key_binding_set *bindings, uint32_t key,
|
||||
xkb_keysym_t sym, xkb_mod_mask_t mods, xkb_mod_mask_t consumed,
|
||||
xkb_mod_mask_t locked,
|
||||
const xkb_keysym_t *raw_syms, size_t raw_count,
|
||||
uint32_t serial);
|
||||
void search_add_chars(struct terminal *term, const char *text, size_t len);
|
||||
|
|
|
|||
42
selection.c
42
selection.c
|
|
@ -858,8 +858,8 @@ pixman_region_for_coords_block(const struct terminal *term,
|
|||
return region;
|
||||
}
|
||||
|
||||
/* Returns a pixman region representing the selection between ‘start’
|
||||
* and ‘end’ (given the current selection kind), in *scrollback
|
||||
/* Returns a pixman region representing the selection between 'start'
|
||||
* and 'end' (given the current selection kind), in *scrollback
|
||||
* relative coordinates* */
|
||||
static pixman_region32_t
|
||||
pixman_region_for_coords(const struct terminal *term,
|
||||
|
|
@ -921,17 +921,17 @@ mark_selected_region(struct terminal *term, pixman_box32_t *boxes,
|
|||
* followed by non-empty cell(s), since this
|
||||
* corresponds to what gets extracted when the
|
||||
* selection is copied (that is, empty cells
|
||||
* “between” non-empty cells are converted to
|
||||
* "between" non-empty cells are converted to
|
||||
* spaces).
|
||||
*
|
||||
* However, they way we handle selection updates
|
||||
* (diffing the “old” selection area against the
|
||||
* “new” one, using pixman regions), means we
|
||||
* can’t correctly update the state of empty
|
||||
* cells. The result is “random” empty cells being
|
||||
* rendered as selected when they shouldn’t.
|
||||
* (diffing the "old" selection area against the
|
||||
* "new" one, using pixman regions), means we
|
||||
* can't correctly update the state of empty
|
||||
* cells. The result is "random" empty cells being
|
||||
* rendered as selected when they shouldn't.
|
||||
*
|
||||
* “Fix” by *never* highlighting selected empty
|
||||
* "Fix" by *never* highlighting selected empty
|
||||
* cells (they still get converted to spaces when
|
||||
* copied, if followed by non-empty cells).
|
||||
*/
|
||||
|
|
@ -944,8 +944,8 @@ mark_selected_region(struct terminal *term, pixman_box32_t *boxes,
|
|||
*
|
||||
* This is due to how the algorithm for updating
|
||||
* the selection works; it uses regions to
|
||||
* calculate the difference between the “old” and
|
||||
* the “new” selection. This makes it impossible
|
||||
* calculate the difference between the "old" and
|
||||
* the "new" selection. This makes it impossible
|
||||
* to tell if an empty cell is a *trailing* empty
|
||||
* cell (that should not be highlighted), or an
|
||||
* empty cells between non-empty cells (that
|
||||
|
|
@ -957,7 +957,7 @@ mark_selected_region(struct terminal *term, pixman_box32_t *boxes,
|
|||
* empty cell is trailing or not.
|
||||
*
|
||||
* So, what we need to do is check if a
|
||||
* ‘selected’, and empty cell has been marked as
|
||||
* 'selected', and empty cell has been marked as
|
||||
* selected, temporarily unmark (forcing it dirty,
|
||||
* to ensure it gets re-rendered). If it is *not*
|
||||
* a trailing empty cell, it will get re-tagged as
|
||||
|
|
@ -1042,7 +1042,7 @@ set_pivot_point_for_block_and_char_wise(struct terminal *term,
|
|||
|
||||
*pivot_start = start;
|
||||
|
||||
/* First, make sure ‘start’ isn’t in the middle of a
|
||||
/* First, make sure 'start' isn't in the middle of a
|
||||
* multi-column character */
|
||||
while (true) {
|
||||
const struct row *row = term->grid->rows[pivot_start->row & (term->grid->num_rows - 1)];
|
||||
|
|
@ -1051,7 +1051,7 @@ set_pivot_point_for_block_and_char_wise(struct terminal *term,
|
|||
if (cell->wc < CELL_SPACER)
|
||||
break;
|
||||
|
||||
/* Multi-column chars don’t cross rows */
|
||||
/* Multi-column chars don't cross rows */
|
||||
xassert(pivot_start->col > 0);
|
||||
if (pivot_start->col == 0)
|
||||
break;
|
||||
|
|
@ -1876,7 +1876,7 @@ cancelled(void *data, struct wl_data_source *wl_data_source)
|
|||
clipboard->text = NULL;
|
||||
}
|
||||
|
||||
/* We don’t support dragging *from* */
|
||||
/* We don't support dragging *from* */
|
||||
static void
|
||||
dnd_drop_performed(void *data, struct wl_data_source *wl_data_source)
|
||||
{
|
||||
|
|
@ -2080,7 +2080,7 @@ decode_one_uri(struct clipboard_receive *ctx, char *uri, size_t len)
|
|||
ctx->cb(" ", 1, ctx->user);
|
||||
ctx->add_space = true;
|
||||
|
||||
if (strcmp(scheme, "file") == 0 && hostname_is_localhost(host)) {
|
||||
if (streq(scheme, "file") && hostname_is_localhost(host)) {
|
||||
if (ctx->quote_paths)
|
||||
ctx->cb("'", 1, ctx->user);
|
||||
|
||||
|
|
@ -2218,11 +2218,11 @@ fdm_receive(struct fdm *fdm, int fd, int events, void *data)
|
|||
|
||||
/*
|
||||
* In addition to stripping non-formatting C0 controls,
|
||||
* XTerm has an option, “disallowedPasteControls”, that
|
||||
* XTerm has an option, "disallowedPasteControls", that
|
||||
* defines C0 controls that will be replaced with spaces
|
||||
* when pasted.
|
||||
*
|
||||
* It’s default value is BS,DEL,ENQ,EOT,NUL
|
||||
* It's default value is BS,DEL,ENQ,EOT,NUL
|
||||
*
|
||||
* Instead of replacing them with spaces, we allow them in
|
||||
* bracketed paste mode, and strip them completely in
|
||||
|
|
@ -2534,7 +2534,7 @@ select_mime_type_for_offer(const char *_mime_type,
|
|||
if (mime_type_map[i] == NULL)
|
||||
continue;
|
||||
|
||||
if (strcmp(_mime_type, mime_type_map[i]) == 0) {
|
||||
if (streq(_mime_type, mime_type_map[i])) {
|
||||
mime_type = i;
|
||||
break;
|
||||
}
|
||||
|
|
@ -2716,7 +2716,7 @@ enter(void *data, struct wl_data_device *wl_data_device, uint32_t serial,
|
|||
|
||||
reject_offer:
|
||||
/* Either terminal is already busy sending paste data, or mouse
|
||||
* pointer isn’t over the grid */
|
||||
* pointer isn't over the grid */
|
||||
seat->clipboard.window = NULL;
|
||||
wl_data_offer_accept(offer, serial, NULL);
|
||||
wl_data_offer_set_actions(
|
||||
|
|
@ -2812,7 +2812,7 @@ drop(void *data, struct wl_data_device *wl_data_device)
|
|||
term, read_fd, clipboard->mime_type,
|
||||
&receive_dnd, &receive_dnd_done, ctx);
|
||||
|
||||
/* data offer is now “owned” by the receive context */
|
||||
/* data offer is now "owned" by the receive context */
|
||||
clipboard->data_offer = NULL;
|
||||
clipboard->mime_type = DATA_OFFER_MIME_UNSET;
|
||||
}
|
||||
|
|
|
|||
5
server.c
5
server.c
|
|
@ -301,7 +301,7 @@ fdm_client(struct fdm *fdm, int fd, int events, void *data)
|
|||
#undef CHECK_BUF_AND_NULL
|
||||
#undef CHECK_BUF
|
||||
|
||||
struct terminal_instance *instance = malloc(sizeof(struct terminal_instance));
|
||||
struct terminal_instance *instance = xmalloc(sizeof(struct terminal_instance));
|
||||
|
||||
const bool need_to_clone_conf =
|
||||
tll_length(overrides)> 0 ||
|
||||
|
|
@ -332,7 +332,8 @@ fdm_client(struct fdm *fdm, int fd, int events, void *data)
|
|||
instance->terminal = term_init(
|
||||
conf != NULL ? conf : server->conf,
|
||||
server->fdm, server->reaper, server->wayl, "footclient", cwd, token,
|
||||
cdata.argc, argv, envp, &term_shutdown_handler, instance);
|
||||
cdata.argc, argv, (const char *const *)envp,
|
||||
&term_shutdown_handler, instance);
|
||||
|
||||
if (instance->terminal == NULL) {
|
||||
LOG_ERR("failed to instantiate new terminal");
|
||||
|
|
|
|||
10
shm.c
10
shm.c
|
|
@ -489,7 +489,7 @@ get_new_buffers(struct buffer_chain *chain, size_t count,
|
|||
else
|
||||
tll_push_front(chain->bufs, buf);
|
||||
|
||||
buf->public.dirty = malloc(
|
||||
buf->public.dirty = xmalloc(
|
||||
chain->pix_instances * sizeof(buf->public.dirty[0]));
|
||||
|
||||
for (size_t j = 0; j < chain->pix_instances; j++)
|
||||
|
|
@ -511,7 +511,7 @@ get_new_buffers(struct buffer_chain *chain, size_t count,
|
|||
#endif
|
||||
|
||||
if (!(bufs[0] && shm_can_scroll(bufs[0]))) {
|
||||
/* We only need to keep the pool FD open if we’re going to SHM
|
||||
/* We only need to keep the pool FD open if we're going to SHM
|
||||
* scroll it */
|
||||
close(pool_fd);
|
||||
pool->fd = -1;
|
||||
|
|
@ -579,7 +579,7 @@ shm_get_buffer(struct buffer_chain *chain, int width, int height)
|
|||
cached = buf;
|
||||
else {
|
||||
/* We have multiple buffers eligible for
|
||||
* reuse. Pick the “youngest” one, and mark the
|
||||
* reuse. Pick the "youngest" one, and mark the
|
||||
* other one for purging */
|
||||
if (buf->public.age < cached->public.age) {
|
||||
shm_unref(&cached->public);
|
||||
|
|
@ -589,8 +589,8 @@ shm_get_buffer(struct buffer_chain *chain, int width, int height)
|
|||
* TODO: I think we _can_ use shm_unref()
|
||||
* here...
|
||||
*
|
||||
* shm_unref() may remove ‘it’, but that
|
||||
* should be safe; “our” tll_foreach() already
|
||||
* shm_unref() may remove 'it', but that
|
||||
* should be safe; "our" tll_foreach() already
|
||||
* holds the next pointer.
|
||||
*/
|
||||
if (buffer_unref_no_remove_from_chain(buf))
|
||||
|
|
|
|||
4
shm.h
4
shm.h
|
|
@ -49,7 +49,7 @@ void shm_chain_free(struct buffer_chain *chain);
|
|||
/*
|
||||
* Returns a single buffer.
|
||||
*
|
||||
* May returned a cached buffer. If so, the buffer’s age indicates how
|
||||
* May returned a cached buffer. If so, the buffer's age indicates how
|
||||
* many shm_get_buffer() calls have been made for the same
|
||||
* width/height while the buffer was still busy.
|
||||
*
|
||||
|
|
@ -57,7 +57,7 @@ void shm_chain_free(struct buffer_chain *chain);
|
|||
*/
|
||||
struct buffer *shm_get_buffer(struct buffer_chain *chain, int width, int height);
|
||||
/*
|
||||
* Returns many buffers, described by ‘info’, all sharing the same SHM
|
||||
* Returns many buffers, described by 'info', all sharing the same SHM
|
||||
* buffer pool.
|
||||
*
|
||||
* Never returns cached buffers. However, the newly created buffers
|
||||
|
|
|
|||
16
sixel.c
16
sixel.c
|
|
@ -979,7 +979,7 @@ sixel_reflow_grid(struct terminal *term, struct grid *grid)
|
|||
struct grid *active_grid = term->grid;
|
||||
term->grid = grid;
|
||||
|
||||
/* Need the “real” list to be empty from the beginning */
|
||||
/* Need the "real" list to be empty from the beginning */
|
||||
tll(struct sixel) copy = tll_init();
|
||||
tll_foreach(grid->sixel_images, it)
|
||||
tll_push_back(copy, it->item);
|
||||
|
|
@ -1028,7 +1028,7 @@ sixel_reflow_grid(struct terminal *term, struct grid *grid)
|
|||
continue;
|
||||
}
|
||||
|
||||
/* Sixels that didn’t overlap may now do so, which isn’t
|
||||
/* Sixels that didn't overlap may now do so, which isn't
|
||||
* allowed of course */
|
||||
_sixel_overwrite_by_rectangle(
|
||||
term, six->pos.row, six->pos.col, six->rows, six->cols,
|
||||
|
|
@ -1200,8 +1200,8 @@ sixel_unhook(struct terminal *term)
|
|||
* Position the text cursor based on the **upper**
|
||||
* pixel, of the last sixel.
|
||||
*
|
||||
* In most cases, that’ll end up being the very last
|
||||
* row of the sixel (which we’re already at, thanks to
|
||||
* In most cases, that'll end up being the very last
|
||||
* row of the sixel (which we're already at, thanks to
|
||||
* the linefeeds). But for some combinations of font
|
||||
* and image sizes, the final cursor position is
|
||||
* higher up.
|
||||
|
|
@ -1580,8 +1580,8 @@ decsixel_generic(struct terminal *term, uint8_t c)
|
|||
case '$':
|
||||
if (likely(term->sixel.pos.col <= term->sixel.max_width)) {
|
||||
/*
|
||||
* We set, and keep, ‘col’ outside the image boundary when
|
||||
* we’ve reached the maximum image height, to avoid also
|
||||
* We set, and keep, 'col' outside the image boundary when
|
||||
* we've reached the maximum image height, to avoid also
|
||||
* having to check the row vs image height in the common
|
||||
* path in sixel_add().
|
||||
*/
|
||||
|
|
@ -1775,12 +1775,12 @@ decgci(struct terminal *term, uint8_t c)
|
|||
int sat = min(c3, 100);
|
||||
|
||||
/*
|
||||
* Sixel’s HLS use the following primary color hues:
|
||||
* Sixel's HLS use the following primary color hues:
|
||||
* blue: 0°
|
||||
* red: 120°
|
||||
* green: 240°
|
||||
*
|
||||
* While “standard” HSL uses:
|
||||
* While "standard" HSL uses:
|
||||
* red: 0°
|
||||
* green: 120°
|
||||
* blue: 240°
|
||||
|
|
|
|||
114
slave.c
114
slave.c
|
|
@ -21,10 +21,16 @@
|
|||
#include "macros.h"
|
||||
#include "terminal.h"
|
||||
#include "tokenize.h"
|
||||
#include "util.h"
|
||||
#include "xmalloc.h"
|
||||
|
||||
extern char **environ;
|
||||
|
||||
struct environ {
|
||||
size_t count;
|
||||
char **envp;
|
||||
};
|
||||
|
||||
#if defined(__FreeBSD__)
|
||||
static char *
|
||||
find_file_in_path(const char *file)
|
||||
|
|
@ -116,7 +122,7 @@ is_valid_shell(const char *shell)
|
|||
if (line[0] == '#')
|
||||
continue;
|
||||
|
||||
if (strcmp(line, shell) == 0) {
|
||||
if (streq(line, shell)) {
|
||||
fclose(f);
|
||||
return true;
|
||||
}
|
||||
|
|
@ -303,9 +309,69 @@ err:
|
|||
_exit(errno);
|
||||
}
|
||||
|
||||
static bool
|
||||
env_matches_var_name(const char *e, const char *name)
|
||||
{
|
||||
const size_t e_len = strlen(e);
|
||||
const size_t name_len = strlen(name);
|
||||
|
||||
if (e_len <= name_len)
|
||||
return false;
|
||||
if (memcmp(e, name, name_len) != 0)
|
||||
return false;
|
||||
if (e[name_len] != '=')
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
static void
|
||||
add_to_env(struct environ *env, const char *name, const char *value)
|
||||
{
|
||||
if (env->envp == NULL)
|
||||
setenv(name, value, 1);
|
||||
else {
|
||||
char *e = xasprintf("%s=%s", name, value);
|
||||
|
||||
/* Search for existing variable. If found, replace it with the
|
||||
new value */
|
||||
for (size_t i = 0; i < env->count; i++) {
|
||||
if (env_matches_var_name(env->envp[i], name)) {
|
||||
free(env->envp[i]);
|
||||
env->envp[i] = e;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
/* If the variable does not already exist, add it */
|
||||
env->envp = xrealloc(env->envp, (env->count + 2) * sizeof(env->envp[0]));
|
||||
env->envp[env->count++] = e;
|
||||
env->envp[env->count] = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
static void
|
||||
del_from_env(struct environ *env, const char *name)
|
||||
{
|
||||
if (env->envp == NULL)
|
||||
unsetenv(name);
|
||||
else {
|
||||
for (size_t i = 0; i < env->count; i++) {
|
||||
if (env_matches_var_name(env->envp[i], name)) {
|
||||
free(env->envp[i]);
|
||||
memmove(&env->envp[i],
|
||||
&env->envp[i + 1],
|
||||
(env->count - i) * sizeof(env->envp[0]));
|
||||
env->count--;
|
||||
xassert(env->envp[env->count] == NULL);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pid_t
|
||||
slave_spawn(int ptmx, int argc, const char *cwd, char *const *argv,
|
||||
char *const *envp, const env_var_list_t *extra_env_vars,
|
||||
const char *const *envp, const env_var_list_t *extra_env_vars,
|
||||
const char *term_env, const char *conf_shell, bool login_shell,
|
||||
const user_notifications_t *notifications)
|
||||
{
|
||||
|
|
@ -350,15 +416,30 @@ slave_spawn(int ptmx, int argc, const char *cwd, char *const *argv,
|
|||
_exit(errno_copy);
|
||||
}
|
||||
|
||||
setenv("TERM", term_env, 1);
|
||||
setenv("COLORTERM", "truecolor", 1);
|
||||
setenv("PWD", cwd, 1);
|
||||
/* Create a mutable copy of the environment */
|
||||
struct environ custom_env = {0};
|
||||
if (envp != NULL) {
|
||||
for (const char *const *e = envp; *e != NULL; e++)
|
||||
custom_env.count++;
|
||||
|
||||
unsetenv("TERM_PROGRAM");
|
||||
unsetenv("TERM_PROGRAM_VERSION");
|
||||
custom_env.envp = xcalloc(
|
||||
custom_env.count + 1, sizeof(custom_env.envp[0]));
|
||||
|
||||
size_t i = 0;
|
||||
for (const char *const *e = envp; *e != NULL; e++, i++)
|
||||
custom_env.envp[i] = xstrdup(*e);
|
||||
xassert(custom_env.envp[custom_env.count] == NULL);
|
||||
}
|
||||
|
||||
add_to_env(&custom_env, "TERM", term_env);
|
||||
add_to_env(&custom_env, "COLORTERM", "truecolor");
|
||||
add_to_env(&custom_env, "PWD", cwd);
|
||||
|
||||
del_from_env(&custom_env, "TERM_PROGRAM");
|
||||
del_from_env(&custom_env, "TERM_PROGRAM_VERSION");
|
||||
|
||||
#if defined(FOOT_TERMINFO_PATH)
|
||||
setenv("TERMINFO", FOOT_TERMINFO_PATH, 1);
|
||||
add_to_env(&custom_env, "TERMINFO", FOOT_TERMINFO_PATH);
|
||||
#endif
|
||||
|
||||
if (extra_env_vars != NULL) {
|
||||
|
|
@ -367,9 +448,9 @@ slave_spawn(int ptmx, int argc, const char *cwd, char *const *argv,
|
|||
const char *value = it->item.value;
|
||||
|
||||
if (strlen(value) == 0)
|
||||
unsetenv(name);
|
||||
del_from_env(&custom_env, name);
|
||||
else
|
||||
setenv(name, value, 1);
|
||||
add_to_env(&custom_env, name, value);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -393,14 +474,23 @@ slave_spawn(int ptmx, int argc, const char *cwd, char *const *argv,
|
|||
}
|
||||
|
||||
if (is_valid_shell(shell_argv[0]))
|
||||
setenv("SHELL", shell_argv[0], 1);
|
||||
add_to_env(&custom_env, "SHELL", shell_argv[0]);
|
||||
|
||||
slave_exec(ptmx, shell_argv, envp != NULL ? envp : environ,
|
||||
slave_exec(ptmx, shell_argv,
|
||||
custom_env.envp != NULL ? custom_env.envp : environ,
|
||||
fork_pipe[1], login_shell, notifications);
|
||||
BUG("Unexpected return from slave_exec()");
|
||||
break;
|
||||
|
||||
default: {
|
||||
|
||||
/*
|
||||
* Don't stay in CWD, since it may be an ephemeral path. For
|
||||
* example, it may be a mount point of, say, a thumb drive. Us
|
||||
* keeping it open will prevent the user from unmounting it.
|
||||
*/
|
||||
(void)!!chdir("/");
|
||||
|
||||
close(fork_pipe[1]); /* Close write end */
|
||||
LOG_DBG("slave has PID %d", pid);
|
||||
|
||||
|
|
|
|||
2
slave.h
2
slave.h
|
|
@ -7,7 +7,7 @@
|
|||
#include "user-notification.h"
|
||||
|
||||
pid_t slave_spawn(
|
||||
int ptmx, int argc, const char *cwd, char *const *argv, char *const *envp,
|
||||
int ptmx, int argc, const char *cwd, char *const *argv, const char *const *envp,
|
||||
const env_var_list_t *extra_env_vars, const char *term_env,
|
||||
const char *conf_shell, bool login_shell,
|
||||
const user_notifications_t *notifications);
|
||||
|
|
|
|||
2
spawn.c
2
spawn.c
|
|
@ -145,7 +145,7 @@ spawn_expand_template(const struct config_spawn_template *template,
|
|||
expanded[len] = '\0'; \
|
||||
} while (0)
|
||||
|
||||
*argv = malloc((*argc + 1) * sizeof((*argv)[0]));
|
||||
*argv = xmalloc((*argc + 1) * sizeof((*argv)[0]));
|
||||
|
||||
/* Expand the provided keys */
|
||||
for (size_t i = 0; i < *argc; i++) {
|
||||
|
|
|
|||
287
terminal.c
287
terminal.c
|
|
@ -260,8 +260,8 @@ fdm_ptmx(struct fdm *fdm, int fd, int events, void *data)
|
|||
|
||||
if (unlikely(term->interactive_resizing.grid != NULL)) {
|
||||
/*
|
||||
* Don’t consume PTMX while we’re doing an interactive resize,
|
||||
* since the ‘normal’ grid we’re currently using is a
|
||||
* Don't consume PTMX while we're doing an interactive resize,
|
||||
* since the 'normal' grid we're currently using is a
|
||||
* temporary one - all changes done to it will be lost when
|
||||
* the interactive resize ends.
|
||||
*/
|
||||
|
|
@ -622,12 +622,35 @@ fdm_title_update_timeout(struct fdm *fdm, int fd, int events, void *data)
|
|||
|
||||
struct itimerspec reset = {{0}};
|
||||
timerfd_settime(term->render.title.timer_fd, 0, &reset, NULL);
|
||||
term->render.title.is_armed = false;
|
||||
|
||||
render_refresh_title(term);
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool
|
||||
fdm_app_id_update_timeout(struct fdm *fdm, int fd, int events, void *data)
|
||||
{
|
||||
if (events & EPOLLHUP)
|
||||
return false;
|
||||
|
||||
struct terminal *term = data;
|
||||
uint64_t unused;
|
||||
ssize_t ret = read(term->render.app_id.timer_fd, &unused, sizeof(unused));
|
||||
|
||||
if (ret < 0) {
|
||||
if (errno == EAGAIN)
|
||||
return true;
|
||||
LOG_ERRNO("failed to read app ID update throttle timer");
|
||||
return false;
|
||||
}
|
||||
|
||||
struct itimerspec reset = {{0}};
|
||||
timerfd_settime(term->render.app_id.timer_fd, 0, &reset, NULL);
|
||||
|
||||
render_refresh_app_id(term);
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool
|
||||
initialize_render_workers(struct terminal *term)
|
||||
{
|
||||
|
|
@ -784,10 +807,11 @@ term_set_fonts(struct terminal *term, struct fcft_font *fonts[static 4],
|
|||
* render_resize() after this function */
|
||||
if (resize_grid) {
|
||||
/* Use force, since cell-width/height may have changed */
|
||||
render_resize_force(
|
||||
render_resize(
|
||||
term,
|
||||
(int)roundf(term->width / term->scale),
|
||||
(int)roundf(term->height / term->scale));
|
||||
(int)roundf(term->height / term->scale),
|
||||
RESIZE_FORCE | RESIZE_KEEP_GRID);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
|
@ -816,7 +840,7 @@ get_font_dpi(const struct terminal *term)
|
|||
* downscaled by the compositor.
|
||||
*
|
||||
* With the newer fractional-scale-v1 protocol, we use the
|
||||
* monitor’s real DPI, since we scale everything to the correct
|
||||
* monitor's real DPI, since we scale everything to the correct
|
||||
* scaling factor (no downscaling done by the compositor).
|
||||
*/
|
||||
|
||||
|
|
@ -939,11 +963,7 @@ reload_fonts(struct terminal *term, bool resize_grid)
|
|||
snprintf(size, sizeof(size), ":size=%.2f",
|
||||
term->font_sizes[i][j].pt_size * scale);
|
||||
|
||||
size_t len = strlen(font->pattern) + strlen(size) + 1;
|
||||
names[i][j] = xmalloc(len);
|
||||
|
||||
strcpy(names[i][j], font->pattern);
|
||||
strcat(names[i][j], size);
|
||||
names[i][j] = xstrjoin(font->pattern, size);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -966,30 +986,14 @@ reload_fonts(struct terminal *term, bool resize_grid)
|
|||
const char **names_bold_italic = (const char **)(custom_bold_italic ? names[3] : names[0]);
|
||||
|
||||
const bool use_dpi = term->font_is_sized_by_dpi;
|
||||
char *dpi = xasprintf("dpi=%.2f", use_dpi ? term->font_dpi : 96.);
|
||||
|
||||
char *attrs[4] = {NULL};
|
||||
int attr_len[4] = {-1, -1, -1, -1}; /* -1, so that +1 (below) results in 0 */
|
||||
|
||||
for (size_t i = 0; i < 2; i++) {
|
||||
attr_len[0] = snprintf(
|
||||
attrs[0], attr_len[0] + 1, "dpi=%.2f",
|
||||
use_dpi ? term->font_dpi : 96);
|
||||
attr_len[1] = snprintf(
|
||||
attrs[1], attr_len[1] + 1, "dpi=%.2f:%s",
|
||||
use_dpi ? term->font_dpi : 96, !custom_bold ? "weight=bold" : "");
|
||||
attr_len[2] = snprintf(
|
||||
attrs[2], attr_len[2] + 1, "dpi=%.2f:%s",
|
||||
use_dpi ? term->font_dpi : 96, !custom_italic ? "slant=italic" : "");
|
||||
attr_len[3] = snprintf(
|
||||
attrs[3], attr_len[3] + 1, "dpi=%.2f:%s",
|
||||
use_dpi ? term->font_dpi : 96, !custom_bold_italic ? "weight=bold:slant=italic" : "");
|
||||
|
||||
if (i > 0)
|
||||
continue;
|
||||
|
||||
for (size_t i = 0; i < 4; i++)
|
||||
attrs[i] = xmalloc(attr_len[i] + 1);
|
||||
}
|
||||
char *attrs[4] = {
|
||||
[0] = dpi, /* Takes ownership */
|
||||
[1] = xstrjoin(dpi, !custom_bold ? ":weight=bold" : ""),
|
||||
[2] = xstrjoin(dpi, !custom_italic ? ":slant=italic" : ""),
|
||||
[3] = xstrjoin(dpi, !custom_bold_italic ? ":weight=bold:slant=italic" : ""),
|
||||
};
|
||||
|
||||
struct fcft_font *fonts[4];
|
||||
struct font_load_data data[4] = {
|
||||
|
|
@ -1061,7 +1065,7 @@ static void fdm_client_terminated(
|
|||
struct terminal *
|
||||
term_init(const struct config *conf, struct fdm *fdm, struct reaper *reaper,
|
||||
struct wayland *wayl, const char *foot_exe, const char *cwd,
|
||||
const char *token, int argc, char *const *argv, char *const *envp,
|
||||
const char *token, int argc, char *const *argv, const char *const *envp,
|
||||
void (*shutdown_cb)(void *data, int exit_code), void *shutdown_data)
|
||||
{
|
||||
int ptmx = -1;
|
||||
|
|
@ -1070,6 +1074,7 @@ term_init(const struct config *conf, struct fdm *fdm, struct reaper *reaper,
|
|||
int delay_upper_fd = -1;
|
||||
int app_sync_updates_fd = -1;
|
||||
int title_update_fd = -1;
|
||||
int app_id_update_fd = -1;
|
||||
|
||||
struct terminal *term = malloc(sizeof(*term));
|
||||
if (unlikely(term == NULL)) {
|
||||
|
|
@ -1104,6 +1109,12 @@ term_init(const struct config *conf, struct fdm *fdm, struct reaper *reaper,
|
|||
goto close_fds;
|
||||
}
|
||||
|
||||
if ((app_id_update_fd = timerfd_create(CLOCK_MONOTONIC, TFD_CLOEXEC | TFD_NONBLOCK)) < 0)
|
||||
{
|
||||
LOG_ERRNO("failed to create app ID update throttle timer FD");
|
||||
goto close_fds;
|
||||
}
|
||||
|
||||
if (ioctl(ptmx, (unsigned int)TIOCSWINSZ,
|
||||
&(struct winsize){.ws_row = 24, .ws_col = 80}) < 0)
|
||||
{
|
||||
|
|
@ -1111,8 +1122,8 @@ term_init(const struct config *conf, struct fdm *fdm, struct reaper *reaper,
|
|||
goto close_fds;
|
||||
}
|
||||
|
||||
/* Need to register *very* early (before the first “goto err”), to
|
||||
* ensure term_destroy() doesn’t unref a key-binding we haven’t
|
||||
/* Need to register *very* early (before the first "goto err"), to
|
||||
* ensure term_destroy() doesn't unref a key-binding we haven't
|
||||
* yet ref:d */
|
||||
key_binding_new_for_conf(wayl->key_binding_manager, wayl, conf);
|
||||
|
||||
|
|
@ -1134,7 +1145,8 @@ term_init(const struct config *conf, struct fdm *fdm, struct reaper *reaper,
|
|||
!fdm_add(fdm, delay_lower_fd, EPOLLIN, &fdm_delayed_render, term) ||
|
||||
!fdm_add(fdm, delay_upper_fd, EPOLLIN, &fdm_delayed_render, term) ||
|
||||
!fdm_add(fdm, app_sync_updates_fd, EPOLLIN, &fdm_app_sync_updates_timeout, term) ||
|
||||
!fdm_add(fdm, title_update_fd, EPOLLIN, &fdm_title_update_timeout, term))
|
||||
!fdm_add(fdm, title_update_fd, EPOLLIN, &fdm_title_update_timeout, term) ||
|
||||
!fdm_add(fdm, app_id_update_fd, EPOLLIN, &fdm_app_id_update_timeout, term))
|
||||
{
|
||||
goto err;
|
||||
}
|
||||
|
|
@ -1234,9 +1246,11 @@ term_init(const struct config *conf, struct fdm *fdm, struct reaper *reaper,
|
|||
.scrollback_lines = conf->scrollback.lines,
|
||||
.app_sync_updates.timer_fd = app_sync_updates_fd,
|
||||
.title = {
|
||||
.is_armed = false,
|
||||
.timer_fd = title_update_fd,
|
||||
},
|
||||
.app_id = {
|
||||
.timer_fd = app_id_update_fd,
|
||||
},
|
||||
.workers = {
|
||||
.count = conf->render_worker_count,
|
||||
.queue = tll_init(),
|
||||
|
|
@ -1345,6 +1359,7 @@ close_fds:
|
|||
fdm_del(fdm, delay_upper_fd);
|
||||
fdm_del(fdm, app_sync_updates_fd);
|
||||
fdm_del(fdm, title_update_fd);
|
||||
fdm_del(fdm, app_id_update_fd);
|
||||
|
||||
free(term);
|
||||
return NULL;
|
||||
|
|
@ -1365,11 +1380,11 @@ term_window_configured(struct terminal *term)
|
|||
*
|
||||
* A foot instance can be terminated in two ways:
|
||||
*
|
||||
* - the client application terminates (user types ‘exit’, or pressed C-d in the
|
||||
* - the client application terminates (user types 'exit', or pressed C-d in the
|
||||
* shell, etc)
|
||||
* - the foot window is closed
|
||||
*
|
||||
* Both variants need to trigger to “other” action. I.e. if the client
|
||||
* Both variants need to trigger to "other" action. I.e. if the client
|
||||
* application is terminated, then we need to close the window. If the window is
|
||||
* closed, we need to terminate the client application.
|
||||
*
|
||||
|
|
@ -1386,7 +1401,7 @@ term_window_configured(struct terminal *term)
|
|||
* - fdm_client_terminated(): reaper callback, called when the client
|
||||
* application has terminated.
|
||||
*
|
||||
* + Kills the “terminate” timeout timer
|
||||
* + Kills the "terminate" timeout timer
|
||||
* + Calls shutdown_maybe_done() if the shutdown procedure has already
|
||||
* started (i.e. the window being closed initiated the shutdown)
|
||||
* -OR-
|
||||
|
|
@ -1394,18 +1409,18 @@ term_window_configured(struct terminal *term)
|
|||
* application termination initiated the shutdown).
|
||||
*
|
||||
* - term_shutdown(): unregisters all FDM callbacks, sends SIGTERM to the client
|
||||
* application and installs a “terminate” timeout timer (if it hasn’t already
|
||||
* application and installs a "terminate" timeout timer (if it hasn't already
|
||||
* terminated). Finally registers an event FD with the FDM, which is
|
||||
* immediately triggered. This is done to ensure any pending FDM events are
|
||||
* handled before shutting down.
|
||||
*
|
||||
* - fdm_shutdown(): FDM callback, triggered by the event FD in
|
||||
* term_shutdown(). Unmaps and destroys the window resources, and ensures the
|
||||
* seats’ focused pointers don’t reference us. Finally calls
|
||||
* seats' focused pointers don't reference us. Finally calls
|
||||
* shutdown_maybe_done().
|
||||
*
|
||||
* - fdm_terminate_timeout(): FDM callback for the “terminate” timeout
|
||||
* timer. This function is called when the client application hasn’t
|
||||
* - fdm_terminate_timeout(): FDM callback for the "terminate" timeout
|
||||
* timer. This function is called when the client application hasn't
|
||||
* terminated after 60 seconds (after the SIGTERM). Sends SIGKILL to the
|
||||
* client application.
|
||||
*
|
||||
|
|
@ -1416,7 +1431,7 @@ term_window_configured(struct terminal *term)
|
|||
* It may however also be called without term_shutdown() having been called
|
||||
* (typically in error code paths - for example, when the Wayland connection
|
||||
* is closed by the compositor). In this case, the client application is
|
||||
* typically still running, and we can’t assume the FDM is running. To handle
|
||||
* typically still running, and we can't assume the FDM is running. To handle
|
||||
* this, we install configure a 60 second SIGALRM, send SIGTERM to the client
|
||||
* application, and then enter a blocking waitpid().
|
||||
*
|
||||
|
|
@ -1537,6 +1552,7 @@ term_shutdown(struct terminal *term)
|
|||
|
||||
fdm_del(term->fdm, term->selection.auto_scroll.fd);
|
||||
fdm_del(term->fdm, term->render.app_sync_updates.timer_fd);
|
||||
fdm_del(term->fdm, term->render.app_id.timer_fd);
|
||||
fdm_del(term->fdm, term->render.title.timer_fd);
|
||||
fdm_del(term->fdm, term->delayed_render_timer.lower_fd);
|
||||
fdm_del(term->fdm, term->delayed_render_timer.upper_fd);
|
||||
|
|
@ -1575,6 +1591,7 @@ term_shutdown(struct terminal *term)
|
|||
|
||||
term->selection.auto_scroll.fd = -1;
|
||||
term->render.app_sync_updates.timer_fd = -1;
|
||||
term->render.app_id.timer_fd = -1;
|
||||
term->render.title.timer_fd = -1;
|
||||
term->delayed_render_timer.lower_fd = -1;
|
||||
term->delayed_render_timer.upper_fd = -1;
|
||||
|
|
@ -1628,6 +1645,7 @@ term_destroy(struct terminal *term)
|
|||
|
||||
fdm_del(term->fdm, term->selection.auto_scroll.fd);
|
||||
fdm_del(term->fdm, term->render.app_sync_updates.timer_fd);
|
||||
fdm_del(term->fdm, term->render.app_id.timer_fd);
|
||||
fdm_del(term->fdm, term->render.title.timer_fd);
|
||||
fdm_del(term->fdm, term->delayed_render_timer.lower_fd);
|
||||
fdm_del(term->fdm, term->delayed_render_timer.upper_fd);
|
||||
|
|
@ -1671,6 +1689,7 @@ term_destroy(struct terminal *term)
|
|||
|
||||
composed_free(term->composed);
|
||||
|
||||
free(term->app_id);
|
||||
free(term->window_title);
|
||||
tll_free_and_free(term->window_title_stack, free);
|
||||
|
||||
|
|
@ -1740,7 +1759,7 @@ term_destroy(struct terminal *term)
|
|||
int ret = EXIT_SUCCESS;
|
||||
|
||||
if (term->slave > 0) {
|
||||
/* We’ll deal with this explicitly */
|
||||
/* We'll deal with this explicitly */
|
||||
reaper_del(term->reaper, term->slave);
|
||||
|
||||
int exit_status;
|
||||
|
|
@ -1754,7 +1773,7 @@ term_destroy(struct terminal *term)
|
|||
kill(-term->slave, SIGTERM);
|
||||
|
||||
/*
|
||||
* we’ve closed the ptxm, and sent SIGTERM to the client
|
||||
* we've closed the ptxm, and sent SIGTERM to the client
|
||||
* application. It *should* exit...
|
||||
*
|
||||
* But, since it is possible to write clients that ignore
|
||||
|
|
@ -1850,7 +1869,9 @@ erase_line(struct terminal *term, struct row *row)
|
|||
{
|
||||
erase_cell_range(term, row, 0, term->cols - 1);
|
||||
row->linebreak = false;
|
||||
row->prompt_marker = false;
|
||||
row->shell_integration.prompt_marker = false;
|
||||
row->shell_integration.cmd_start = -1;
|
||||
row->shell_integration.cmd_end = -1;
|
||||
}
|
||||
|
||||
void
|
||||
|
|
@ -2105,32 +2126,43 @@ term_fractional_scaling(const struct terminal *term)
|
|||
return term->wl->fractional_scale_manager != NULL && term->window->scale > 0.;
|
||||
}
|
||||
|
||||
bool
|
||||
term_preferred_buffer_scale(const struct terminal *term)
|
||||
{
|
||||
return term->wl->has_wl_compositor_v6;
|
||||
}
|
||||
|
||||
bool
|
||||
term_update_scale(struct terminal *term)
|
||||
{
|
||||
const struct wl_window *win = term->window;
|
||||
|
||||
/*
|
||||
* We have a number of “sources” we can use as scale. We choose
|
||||
* We have a number of "sources" we can use as scale. We choose
|
||||
* the scale in the following order:
|
||||
*
|
||||
* - “preferred” scale, from the fractional-scale-v1 protocol
|
||||
* - "preferred" scale, from the fractional-scale-v1 protocol
|
||||
* - "preferred" scale, from wl_compositor version 6.
|
||||
NOTE: if the compositor advertises version 6 we must use 1.0
|
||||
until wl_surface.preferred_buffer_scale is sent
|
||||
* - scaling factor of output we most recently were mapped on
|
||||
* - if we're not mapped, use the last known scaling factor
|
||||
* - if we're not mapped, and we don't have a last known scaling
|
||||
* factor, use the scaling factor from the first available
|
||||
* output.
|
||||
* - if there aren’t any outputs available, use 1.0
|
||||
* - if there aren't any outputs available, use 1.0
|
||||
*/
|
||||
const float new_scale = (term_fractional_scaling(term)
|
||||
? win->scale
|
||||
: tll_length(win->on_outputs) > 0
|
||||
? tll_back(win->on_outputs)->scale
|
||||
: term->scale_before_unmap > 0.
|
||||
? term->scale_before_unmap
|
||||
: tll_length(term->wl->monitors) > 0
|
||||
? tll_front(term->wl->monitors).scale
|
||||
: 1.);
|
||||
: term_preferred_buffer_scale(term)
|
||||
? win->preferred_buffer_scale
|
||||
: tll_length(win->on_outputs) > 0
|
||||
? tll_back(win->on_outputs)->scale
|
||||
: term->scale_before_unmap > 0.
|
||||
? term->scale_before_unmap
|
||||
: tll_length(term->wl->monitors) > 0
|
||||
? tll_front(term->wl->monitors).scale
|
||||
: 1.);
|
||||
|
||||
if (new_scale == term->scale)
|
||||
return false;
|
||||
|
|
@ -2280,7 +2312,7 @@ term_damage_scroll(struct terminal *term, enum damage_type damage_type,
|
|||
dmg->region.start == region.start &&
|
||||
dmg->region.end == region.end))
|
||||
{
|
||||
/* Make sure we don’t overflow... */
|
||||
/* Make sure we don't overflow... */
|
||||
int new_line_count = (int)dmg->lines + lines;
|
||||
if (likely(new_line_count <= UINT16_MAX)) {
|
||||
dmg->lines = new_line_count;
|
||||
|
|
@ -2344,14 +2376,14 @@ term_erase_scrollback(struct terminal *term)
|
|||
if (sel_end >= 0) {
|
||||
/*
|
||||
* Cancel selection if it touches any of the rows in the
|
||||
* scrollback, since we can’t have the selection reference
|
||||
* scrollback, since we can't have the selection reference
|
||||
* soon-to-be deleted rows.
|
||||
*
|
||||
* This is done by range checking the selection range against
|
||||
* the scrollback range.
|
||||
*
|
||||
* To make this comparison simpler, the start/end absolute row
|
||||
* numbers are “rebased” against the scrollback start, where
|
||||
* numbers are "rebased" against the scrollback start, where
|
||||
* row 0 is the *first* row in the scrollback. A high number
|
||||
* thus means the row is further *down* in the scrollback,
|
||||
* closer to the screen bottom.
|
||||
|
|
@ -3061,7 +3093,7 @@ term_mouse_grabbed(const struct terminal *term, const struct seat *seat)
|
|||
*/
|
||||
|
||||
xkb_mod_mask_t mods;
|
||||
get_current_modifiers(seat, &mods, NULL, 0);
|
||||
get_current_modifiers(seat, &mods, NULL, 0, true);
|
||||
|
||||
const struct key_binding_set *bindings =
|
||||
key_binding_for(term->wl->key_binding_manager, term->conf, seat);
|
||||
|
|
@ -3266,15 +3298,41 @@ term_set_window_title(struct terminal *term, const char *title)
|
|||
if (term->conf->locked_title && term->window_title_has_been_set)
|
||||
return;
|
||||
|
||||
if (term->window_title != NULL && strcmp(term->window_title, title) == 0)
|
||||
if (term->window_title != NULL && streq(term->window_title, title))
|
||||
return;
|
||||
|
||||
if (mbsntoc32(NULL, title, strlen(title), 0) == (char32_t)-1) {
|
||||
/* It's an xdg_toplevel::set_title() protocol violation to set
|
||||
a title with an invalid UTF-8 sequence */
|
||||
LOG_WARN("%s: title is not valid UTF-8, ignoring", title);
|
||||
return;
|
||||
}
|
||||
|
||||
free(term->window_title);
|
||||
term->window_title = xstrdup(title);
|
||||
render_refresh_title(term);
|
||||
term->window_title_has_been_set = true;
|
||||
}
|
||||
|
||||
void
|
||||
term_set_app_id(struct terminal *term, const char *app_id)
|
||||
{
|
||||
if (app_id != NULL && *app_id == '\0')
|
||||
app_id = NULL;
|
||||
if (term->app_id == NULL && app_id == NULL)
|
||||
return;
|
||||
if (term->app_id != NULL && app_id != NULL && strcmp(term->app_id, app_id) == 0)
|
||||
return;
|
||||
|
||||
free(term->app_id);
|
||||
if (app_id != NULL) {
|
||||
term->app_id = xstrdup(app_id);
|
||||
} else {
|
||||
term->app_id = NULL;
|
||||
}
|
||||
render_refresh_app_id(term);
|
||||
}
|
||||
|
||||
void
|
||||
term_flash(struct terminal *term, unsigned duration_ms)
|
||||
{
|
||||
|
|
@ -3302,7 +3360,7 @@ term_bell(struct terminal *term)
|
|||
if (!wayl_win_set_urgent(term->window)) {
|
||||
/*
|
||||
* Urgency (xdg-activation) is relatively new in
|
||||
* Wayland. Fallback to our old, “faked”, urgency -
|
||||
* Wayland. Fallback to our old, "faked", urgency -
|
||||
* rendering our window margins in red
|
||||
*/
|
||||
term->render.urgency = true;
|
||||
|
|
@ -3633,7 +3691,7 @@ term_surface_kind(const struct terminal *term, const struct wl_surface *surface)
|
|||
|
||||
static bool
|
||||
rows_to_text(const struct terminal *term, int start, int end,
|
||||
char **text, size_t *len)
|
||||
int col_start, int col_end, char **text, size_t *len)
|
||||
{
|
||||
struct extraction_context *ctx = extract_begin(SELECTION_NONE, true);
|
||||
if (ctx == NULL)
|
||||
|
|
@ -3646,15 +3704,20 @@ rows_to_text(const struct terminal *term, int start, int end,
|
|||
const struct row *row = term->grid->rows[r];
|
||||
xassert(row != NULL);
|
||||
|
||||
for (int c = 0; c < term->cols; c++)
|
||||
const int c_end = r == end ? col_end : term->cols;
|
||||
|
||||
for (int c = col_start; c < c_end; c++) {
|
||||
if (!extract_one(term, row, &row->cells[c], c, ctx))
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (r == end)
|
||||
break;
|
||||
|
||||
r++;
|
||||
r &= grid_rows - 1;
|
||||
|
||||
col_start = 0;
|
||||
}
|
||||
|
||||
out:
|
||||
|
|
@ -3686,7 +3749,7 @@ term_scrollback_to_text(const struct terminal *term, char **text, size_t *len)
|
|||
end += term->grid->num_rows;
|
||||
}
|
||||
|
||||
return rows_to_text(term, start, end, text, len);
|
||||
return rows_to_text(term, start, end, 0, term->cols, text, len);
|
||||
}
|
||||
|
||||
bool
|
||||
|
|
@ -3694,7 +3757,91 @@ term_view_to_text(const struct terminal *term, char **text, size_t *len)
|
|||
{
|
||||
int start = grid_row_absolute_in_view(term->grid, 0);
|
||||
int end = grid_row_absolute_in_view(term->grid, term->rows - 1);
|
||||
return rows_to_text(term, start, end, text, len);
|
||||
return rows_to_text(term, start, end, 0, term->cols, text, len);
|
||||
}
|
||||
|
||||
bool
|
||||
term_command_output_to_text(const struct terminal *term, char **text, size_t *len)
|
||||
{
|
||||
int start_row = -1;
|
||||
int end_row = -1;
|
||||
int start_col = -1;
|
||||
int end_col = -1;
|
||||
|
||||
const struct grid *grid = term->grid;
|
||||
const int sb_end = grid_row_absolute(grid, term->rows - 1);
|
||||
const int sb_start = (sb_end + 1) & (grid->num_rows - 1);
|
||||
int r = sb_end;
|
||||
|
||||
while (start_row < 0) {
|
||||
const struct row *row = grid->rows[r];
|
||||
if (row == NULL)
|
||||
break;
|
||||
|
||||
if (row->shell_integration.cmd_end >= 0) {
|
||||
end_row = r;
|
||||
end_col = row->shell_integration.cmd_end;
|
||||
}
|
||||
|
||||
if (end_row >= 0 && row->shell_integration.cmd_start >= 0) {
|
||||
start_row = r;
|
||||
start_col = row->shell_integration.cmd_start;
|
||||
}
|
||||
|
||||
if (r == sb_start)
|
||||
break;
|
||||
|
||||
r = (r - 1 + grid->num_rows) & (grid->num_rows - 1);
|
||||
}
|
||||
|
||||
if (start_row < 0)
|
||||
return false;
|
||||
|
||||
bool ret = rows_to_text(term, start_row, end_row, start_col, end_col, text, len);
|
||||
if (!ret)
|
||||
return false;
|
||||
|
||||
/*
|
||||
* If the FTCS_COMMAND_FINISHED marker was emitted at the *first*
|
||||
* column, then the *entire* previous line is part of the command
|
||||
* output. *Including* the newline, if any.
|
||||
*
|
||||
* Since rows_to_text() doesn't extract the column
|
||||
* FTCS_COMMAND_FINISHED was emitted at (that would be wrong -
|
||||
* FTCS_COMMAND_FINISHED is emitted *after* the command output,
|
||||
* not at its last character), the extraction logic will not see
|
||||
* the last newline (this is true for all non-line-wise selection
|
||||
* types), and the extracted text will *not* end with a newline.
|
||||
*
|
||||
* Here we try to compensate for that. Note that if 'end_col' is
|
||||
* not 0, then the command output only covers a partial row, and
|
||||
* thus we do *not* want to append a newline.
|
||||
*/
|
||||
|
||||
if (end_col > 0) {
|
||||
/* Command output covers partial row - don't append newline */
|
||||
return true;
|
||||
}
|
||||
|
||||
int next_to_last_row = (end_row - 1 + grid->num_rows) & (grid->num_rows - 1);
|
||||
const struct row *row = grid->rows[next_to_last_row];
|
||||
|
||||
/* Add newline if last row has a hard linebreak */
|
||||
if (row->linebreak) {
|
||||
char *new_text = xrealloc(*text, *len + 1 + 1);
|
||||
|
||||
if (new_text == NULL) {
|
||||
/* Ignore failure - use text as is (without inserting newline) */
|
||||
return true;
|
||||
}
|
||||
|
||||
*text = new_text;
|
||||
(*len)++;
|
||||
(*text)[*len - 1] = '\n';
|
||||
(*text)[*len] = '\0';
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
|
|
|
|||
32
terminal.h
32
terminal.h
|
|
@ -121,8 +121,11 @@ struct row {
|
|||
bool dirty;
|
||||
bool linebreak;
|
||||
|
||||
/* Shell integration */
|
||||
bool prompt_marker;
|
||||
struct {
|
||||
bool prompt_marker;
|
||||
int cmd_start; /* Column, -1 if unset */
|
||||
int cmd_end; /* Column, -1 if unset */
|
||||
} shell_integration;
|
||||
};
|
||||
|
||||
struct sixel {
|
||||
|
|
@ -149,8 +152,8 @@ struct sixel {
|
|||
* We store the cell dimensions of the time the sixel was emitted.
|
||||
*
|
||||
* If the font size is changed, we rescale the image accordingly,
|
||||
* to ensure it stays within its cell boundaries. ‘scaled’ is a
|
||||
* cached, rescaled version of ‘data’ + ‘pix’.
|
||||
* to ensure it stays within its cell boundaries. 'scaled' is a
|
||||
* cached, rescaled version of 'data' + 'pix'.
|
||||
*/
|
||||
int cell_width;
|
||||
int cell_height;
|
||||
|
|
@ -344,7 +347,7 @@ struct url {
|
|||
char32_t *key;
|
||||
struct range range;
|
||||
enum url_action action;
|
||||
bool url_mode_dont_change_url_attr; /* Entering/exiting URL mode doesn’t touch the cells’ attr.url */
|
||||
bool url_mode_dont_change_url_attr; /* Entering/exiting URL mode doesn't touch the cells' attr.url */
|
||||
bool osc8;
|
||||
bool duplicate;
|
||||
};
|
||||
|
|
@ -380,7 +383,7 @@ struct terminal {
|
|||
bool bracketed_paste;
|
||||
bool focus_events;
|
||||
bool alt_scrolling;
|
||||
bool modify_other_keys_2; /* True when modifyOtherKeys=2 (i.e. “CSI >4;2m”) */
|
||||
bool modify_other_keys_2; /* True when modifyOtherKeys=2 (i.e. "CSI >4;2m") */
|
||||
enum cursor_origin origin;
|
||||
enum cursor_keys cursor_keys_mode;
|
||||
enum keypad_keys keypad_keys_mode;
|
||||
|
|
@ -481,6 +484,7 @@ struct terminal {
|
|||
bool window_title_has_been_set;
|
||||
char *window_title;
|
||||
tll(char *) window_title_stack;
|
||||
char *app_id;
|
||||
|
||||
struct {
|
||||
bool active;
|
||||
|
|
@ -610,10 +614,14 @@ struct terminal {
|
|||
|
||||
struct {
|
||||
struct timespec last_update;
|
||||
bool is_armed;
|
||||
int timer_fd;
|
||||
} title;
|
||||
|
||||
struct {
|
||||
struct timespec last_update;
|
||||
int timer_fd;
|
||||
} app_id;
|
||||
|
||||
uint32_t scrollback_lines; /* Number of scrollback lines, from conf (TODO: move out from render struct?) */
|
||||
|
||||
struct {
|
||||
|
|
@ -658,7 +666,7 @@ struct terminal {
|
|||
} render;
|
||||
|
||||
struct {
|
||||
struct grid *grid; /* Original ‘normal’ grid, before resize started */
|
||||
struct grid *grid; /* Original 'normal' grid, before resize started */
|
||||
int old_screen_rows; /* term->rows before resize started */
|
||||
int old_cols; /* term->cols before resize started */
|
||||
int old_hide_cursor; /* term->hide_cursor before resize started */
|
||||
|
|
@ -692,7 +700,7 @@ struct terminal {
|
|||
* Pan is the vertical shape of a pixel
|
||||
* Pad is the horizontal shape of a pixel
|
||||
*
|
||||
* pan/pad is the sixel’s aspect ratio
|
||||
* pan/pad is the sixel's aspect ratio
|
||||
*/
|
||||
int pan;
|
||||
int pad;
|
||||
|
|
@ -745,7 +753,7 @@ struct config;
|
|||
struct terminal *term_init(
|
||||
const struct config *conf, struct fdm *fdm, struct reaper *reaper,
|
||||
struct wayland *wayl, const char *foot_exe, const char *cwd,
|
||||
const char *token, int argc, char *const *argv, char *const *envp,
|
||||
const char *token, int argc, char *const *argv, const char *const *envp,
|
||||
void (*shutdown_cb)(void *data, int exit_code), void *shutdown_data);
|
||||
|
||||
bool term_shutdown(struct terminal *term);
|
||||
|
|
@ -760,6 +768,7 @@ bool term_paste_data_to_slave(
|
|||
struct terminal *term, const void *data, size_t len);
|
||||
|
||||
bool term_fractional_scaling(const struct terminal *term);
|
||||
bool term_preferred_buffer_scale(const struct terminal *term);
|
||||
bool term_update_scale(struct terminal *term);
|
||||
bool term_font_size_increase(struct terminal *term);
|
||||
bool term_font_size_decrease(struct terminal *term);
|
||||
|
|
@ -846,6 +855,7 @@ void term_xcursor_update_for_seat(struct terminal *term, struct seat *seat);
|
|||
void term_set_user_mouse_cursor(struct terminal *term, const char *cursor);
|
||||
|
||||
void term_set_window_title(struct terminal *term, const char *title);
|
||||
void term_set_app_id(struct terminal *term, const char *app_id);
|
||||
void term_flash(struct terminal *term, unsigned duration_ms);
|
||||
void term_bell(struct terminal *term);
|
||||
bool term_spawn_new(const struct terminal *term);
|
||||
|
|
@ -860,6 +870,8 @@ bool term_scrollback_to_text(
|
|||
const struct terminal *term, char **text, size_t *len);
|
||||
bool term_view_to_text(
|
||||
const struct terminal *term, char **text, size_t *len);
|
||||
bool term_command_output_to_text(
|
||||
const struct terminal *term, char **text, size_t *len);
|
||||
|
||||
bool term_ime_is_enabled(const struct terminal *term);
|
||||
void term_ime_enable(struct terminal *term);
|
||||
|
|
|
|||
|
|
@ -60,7 +60,7 @@ test_string(struct context *ctx, bool (*parse_fun)(struct context *ctx),
|
|||
BUG("[%s].%s=%s: failed to parse",
|
||||
ctx->section, ctx->key, ctx->value);
|
||||
}
|
||||
if (strcmp(*ptr, input[i].value) != 0) {
|
||||
if (!streq(*ptr, input[i].value)) {
|
||||
BUG("[%s].%s=%s: set value (%s) not the expected one (%s)",
|
||||
ctx->section, ctx->key, ctx->value,
|
||||
*ptr, input[i].value);
|
||||
|
|
@ -357,9 +357,7 @@ test_spawn_template(struct context *ctx, bool (*parse_fun)(struct context *ctx),
|
|||
BUG("[%s].%s=%s: argv is NULL", ctx->section, ctx->key, ctx->value);
|
||||
|
||||
for (size_t i = 0; i < ALEN(args); i++) {
|
||||
if (ptr->argv.args[i] == NULL ||
|
||||
strcmp(ptr->argv.args[i], args[i]) != 0)
|
||||
{
|
||||
if (ptr->argv.args[i] == NULL || !streq(ptr->argv.args[i], args[i])) {
|
||||
BUG("[%s].%s=%s: set value not the expected one: "
|
||||
"mismatch of arg #%zu: expected=\"%s\", got=\"%s\"",
|
||||
ctx->section, ctx->key, ctx->value, i,
|
||||
|
|
@ -789,6 +787,17 @@ test_section_csd(void)
|
|||
config_free(&conf);
|
||||
}
|
||||
|
||||
static bool
|
||||
have_modifier(const config_modifier_list_t *mods, const char *mod)
|
||||
{
|
||||
tll_foreach(*mods, it) {
|
||||
if (strcmp(it->item, mod) == 0)
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static void
|
||||
test_key_binding(struct context *ctx, bool (*parse_fun)(struct context *ctx),
|
||||
int action, int max_action, const char *const *map,
|
||||
|
|
@ -879,7 +888,7 @@ test_key_binding(struct context *ctx, bool (*parse_fun)(struct context *ctx),
|
|||
|
||||
for (size_t i = 0; i < ALEN(args); i++) {
|
||||
if (binding->aux.pipe.args[i] == NULL ||
|
||||
strcmp(binding->aux.pipe.args[i], args[i]) != 0)
|
||||
!streq(binding->aux.pipe.args[i], args[i]))
|
||||
{
|
||||
BUG("[%s].%s=%s: pipe argv not the expected one: "
|
||||
"mismatch of arg #%zu: expected=\"%s\", got=\"%s\"",
|
||||
|
|
@ -906,17 +915,19 @@ test_key_binding(struct context *ctx, bool (*parse_fun)(struct context *ctx),
|
|||
ctx->section, ctx->key, ctx->value, binding->action, action);
|
||||
}
|
||||
|
||||
if (binding->modifiers.ctrl != ctrl ||
|
||||
binding->modifiers.alt != alt ||
|
||||
binding->modifiers.shift != shift ||
|
||||
binding->modifiers.super != super)
|
||||
bool have_ctrl = have_modifier(&binding->modifiers, XKB_MOD_NAME_CTRL);
|
||||
bool have_alt = have_modifier(&binding->modifiers, XKB_MOD_NAME_ALT);
|
||||
bool have_shift = have_modifier(&binding->modifiers, XKB_MOD_NAME_SHIFT);
|
||||
bool have_super = have_modifier(&binding->modifiers, XKB_MOD_NAME_LOGO);
|
||||
|
||||
if (have_ctrl != ctrl || have_alt != alt ||
|
||||
have_shift != shift || have_super != super)
|
||||
{
|
||||
BUG("[%s].%s=%s: modifier mismatch:\n"
|
||||
" have: ctrl=%d, alt=%d, shift=%d, super=%d\n"
|
||||
" expected: ctrl=%d, alt=%d, shift=%d, super=%d",
|
||||
ctx->section, ctx->key, ctx->value,
|
||||
binding->modifiers.ctrl, binding->modifiers.alt,
|
||||
binding->modifiers.shift, binding->modifiers.super,
|
||||
have_ctrl, have_alt, have_shift, have_super,
|
||||
ctrl, alt, shift, super);
|
||||
}
|
||||
|
||||
|
|
@ -972,14 +983,17 @@ _test_binding_collisions(struct context *ctx,
|
|||
bindings.arr[0] = (struct config_key_binding){
|
||||
.action = (test_mode == FAIL_DIFFERENT_ACTION
|
||||
? max_action - 1 : max_action),
|
||||
.modifiers = {.ctrl = true},
|
||||
.modifiers = tll_init(),
|
||||
.path = "unittest",
|
||||
};
|
||||
tll_push_back(bindings.arr[0].modifiers, xstrdup(XKB_MOD_NAME_CTRL));
|
||||
|
||||
bindings.arr[1] = (struct config_key_binding){
|
||||
.action = max_action,
|
||||
.modifiers = {.ctrl = true},
|
||||
.modifiers = tll_init(),
|
||||
.path = "unittest",
|
||||
};
|
||||
tll_push_back(bindings.arr[1].modifiers, xstrdup(XKB_MOD_NAME_CTRL));
|
||||
|
||||
switch (type) {
|
||||
case KEY_BINDING:
|
||||
|
|
@ -1000,7 +1014,8 @@ _test_binding_collisions(struct context *ctx,
|
|||
break;
|
||||
|
||||
case FAIL_MOUSE_OVERRIDE:
|
||||
ctx->conf->mouse.selection_override_modifiers.ctrl = true;
|
||||
tll_free_and_free(ctx->conf->mouse.selection_override_modifiers, free);
|
||||
tll_push_back(ctx->conf->mouse.selection_override_modifiers, xstrdup(XKB_MOD_NAME_CTRL));
|
||||
break;
|
||||
|
||||
case FAIL_DIFFERENT_ARGV:
|
||||
|
|
@ -1239,10 +1254,13 @@ test_section_text_bindings(void)
|
|||
ctx.key = "\\y";
|
||||
xassert(!parse_section_text_bindings(&ctx));
|
||||
|
||||
#if 0
|
||||
/* Invalid modifier and key names are detected later, when a
|
||||
* layout is applied */
|
||||
ctx.key = "abcd";
|
||||
ctx.value = "InvalidMod+y";
|
||||
xassert(!parse_section_text_bindings(&ctx));
|
||||
|
||||
#endif
|
||||
config_free(&conf);
|
||||
}
|
||||
|
||||
|
|
@ -1258,26 +1276,26 @@ test_section_environment(void)
|
|||
ctx.value = "bar";
|
||||
xassert(parse_section_environment(&ctx));
|
||||
xassert(tll_length(conf.env_vars) == 1);
|
||||
xassert(strcmp(tll_front(conf.env_vars).name, "FOO") == 0);
|
||||
xassert(strcmp(tll_front(conf.env_vars).value, "bar") == 0);
|
||||
xassert(streq(tll_front(conf.env_vars).name, "FOO"));
|
||||
xassert(streq(tll_front(conf.env_vars).value, "bar"));
|
||||
|
||||
/* Add a second variable */
|
||||
ctx.key = "BAR";
|
||||
ctx.value = "123";
|
||||
xassert(parse_section_environment(&ctx));
|
||||
xassert(tll_length(conf.env_vars) == 2);
|
||||
xassert(strcmp(tll_back(conf.env_vars).name, "BAR") == 0);
|
||||
xassert(strcmp(tll_back(conf.env_vars).value, "123") == 0);
|
||||
xassert(streq(tll_back(conf.env_vars).name, "BAR"));
|
||||
xassert(streq(tll_back(conf.env_vars).value, "123"));
|
||||
|
||||
/* Replace the *value* of the first variable */
|
||||
ctx.key = "FOO";
|
||||
ctx.value = "456";
|
||||
xassert(parse_section_environment(&ctx));
|
||||
xassert(tll_length(conf.env_vars) == 2);
|
||||
xassert(strcmp(tll_front(conf.env_vars).name, "FOO") == 0);
|
||||
xassert(strcmp(tll_front(conf.env_vars).value, "456") == 0);
|
||||
xassert(strcmp(tll_back(conf.env_vars).name, "BAR") == 0);
|
||||
xassert(strcmp(tll_back(conf.env_vars).value, "123") == 0);
|
||||
xassert(streq(tll_front(conf.env_vars).name, "FOO"));
|
||||
xassert(streq(tll_front(conf.env_vars).value, "456"));
|
||||
xassert(streq(tll_back(conf.env_vars).name, "BAR"));
|
||||
xassert(streq(tll_back(conf.env_vars).value, "123"));
|
||||
|
||||
config_free(&conf);
|
||||
}
|
||||
|
|
|
|||
35
themes/electrophoretic
Normal file
35
themes/electrophoretic
Normal file
|
|
@ -0,0 +1,35 @@
|
|||
# -*- conf -*-
|
||||
# Electrophoretic
|
||||
# Theme for electrophoretic displays (like e-ink) which usually supports
|
||||
# 16 levels of grays. This theme aims to maximize the contrast between the
|
||||
# text and the white background.
|
||||
# author: Eugen Rahaian <eugen@rah.ro>
|
||||
|
||||
[cursor]
|
||||
color=ffffff 515151
|
||||
|
||||
[colors]
|
||||
background= ffffff
|
||||
foreground= 000000
|
||||
|
||||
# The colors are sorted based on their luminance, so we can more easily assign
|
||||
# them a gray level.
|
||||
# grayscale order: black_0 blue_4 red_1 magenta_5 green_2 cyan_6 yellow_3 white_7
|
||||
regular0= ffffff
|
||||
regular4= 616161
|
||||
regular1= 515151
|
||||
regular5= 414141
|
||||
regular2= 313131
|
||||
regular6= 212121
|
||||
regular3= 111111
|
||||
regular7= 000000
|
||||
# Here, we also stay away from the white background by reusing the dark gray levels
|
||||
# from above, with small variations
|
||||
bright0= 818181
|
||||
bright4= 717171
|
||||
bright1= 616161
|
||||
bright5= 515151
|
||||
bright2= 414141
|
||||
bright6= 313131
|
||||
bright3= 212121
|
||||
bright7= 111111
|
||||
30
themes/poimandres
Normal file
30
themes/poimandres
Normal file
|
|
@ -0,0 +1,30 @@
|
|||
# Based on Poimandres color theme for kitti terminal emulator
|
||||
# https://github.com/ubmit/poimandres-kitty
|
||||
|
||||
[cursor]
|
||||
color=1b1e28 ffffff
|
||||
|
||||
[colors]
|
||||
foreground=a6accd
|
||||
background=1b1e28
|
||||
|
||||
regular0=1b1e28
|
||||
regular1=d0679d
|
||||
regular2=5de4c7
|
||||
regular3=fffac2
|
||||
regular4=89ddff
|
||||
regular5=fcc5e9
|
||||
regular6=add7ff
|
||||
regular7=ffffff
|
||||
|
||||
bright0=a6accd
|
||||
bright1=d0679d
|
||||
bright2=5de4c7
|
||||
bright3=fffac2
|
||||
bright4=add7ff
|
||||
bright5=fae4fc
|
||||
bright6=89ddff
|
||||
bright7=ffffff
|
||||
|
||||
selection-background=28344a
|
||||
selection-foreground=a6accd
|
||||
|
|
@ -90,6 +90,8 @@ unicode_mode_input(struct seat *seat, struct terminal *term,
|
|||
/* 0-9, a-f, A-F */
|
||||
if (sym >= XKB_KEY_0 && sym <= XKB_KEY_9)
|
||||
digit = sym - XKB_KEY_0;
|
||||
else if (sym >= XKB_KEY_KP_0 && sym <= XKB_KEY_KP_9)
|
||||
digit = sym - XKB_KEY_KP_0;
|
||||
else if (sym >= XKB_KEY_a && sym <= XKB_KEY_f)
|
||||
digit = 0xa + (sym - XKB_KEY_a);
|
||||
else if (sym >= XKB_KEY_A && sym <= XKB_KEY_F)
|
||||
|
|
|
|||
6
uri.c
6
uri.c
|
|
@ -250,7 +250,7 @@ hostname_is_localhost(const char *hostname)
|
|||
this_host[0] = '\0';
|
||||
|
||||
return (hostname != NULL && (
|
||||
strcmp(hostname, "") == 0 ||
|
||||
strcmp(hostname, "localhost") == 0 ||
|
||||
strcmp(hostname, this_host) == 0));
|
||||
streq(hostname, "") ||
|
||||
streq(hostname, "localhost") ||
|
||||
streq(hostname, this_host)));
|
||||
}
|
||||
|
|
|
|||
36
url-mode.c
36
url-mode.c
|
|
@ -145,28 +145,22 @@ void
|
|||
urls_input(struct seat *seat, struct terminal *term,
|
||||
const struct key_binding_set *bindings, uint32_t key,
|
||||
xkb_keysym_t sym, xkb_mod_mask_t mods, xkb_mod_mask_t consumed,
|
||||
xkb_mod_mask_t locked,
|
||||
const xkb_keysym_t *raw_syms, size_t raw_count,
|
||||
uint32_t serial)
|
||||
{
|
||||
const xkb_mod_mask_t bind_mods =
|
||||
mods & seat->kbd.bind_significant & ~locked;
|
||||
const xkb_mod_mask_t bind_consumed =
|
||||
consumed & seat->kbd.bind_significant & ~locked;
|
||||
|
||||
/* Key bindings */
|
||||
tll_foreach(bindings->url, it) {
|
||||
const struct key_binding *bind = &it->item;
|
||||
|
||||
/* Match translated symbol */
|
||||
if (bind->k.sym == sym &&
|
||||
bind->mods == (bind_mods & ~bind_consumed))
|
||||
bind->mods == (mods & ~consumed))
|
||||
{
|
||||
execute_binding(seat, term, bind, serial);
|
||||
return;
|
||||
}
|
||||
|
||||
if (bind->mods != bind_mods || bind_mods != (mods & ~locked))
|
||||
if (bind->mods != mods)
|
||||
continue;
|
||||
|
||||
for (size_t i = 0; i < raw_count; i++) {
|
||||
|
|
@ -196,13 +190,13 @@ urls_input(struct seat *seat, struct terminal *term,
|
|||
return;
|
||||
}
|
||||
|
||||
if (mods & ~consumed & ~locked)
|
||||
if (mods & ~consumed)
|
||||
return;
|
||||
|
||||
char32_t wc = xkb_state_key_get_utf32(seat->kbd.xkb_state, key);
|
||||
|
||||
/*
|
||||
* Determine if this is a “valid” key. I.e. if there is a URL
|
||||
* Determine if this is a "valid" key. I.e. if there is a URL
|
||||
* label with a key combo where this key is the next in
|
||||
* sequence.
|
||||
*/
|
||||
|
|
@ -364,7 +358,7 @@ auto_detected(const struct terminal *term, enum url_action action,
|
|||
if (match == NULL) {
|
||||
/*
|
||||
* Character is not a valid URI character. Emit
|
||||
* the URL we’ve collected so far, *without*
|
||||
* the URL we've collected so far, *without*
|
||||
* including _this_ character.
|
||||
*/
|
||||
emit_url = true;
|
||||
|
|
@ -416,7 +410,7 @@ auto_detected(const struct terminal *term, enum url_action action,
|
|||
|
||||
if (c >= term->cols - 1 && row->linebreak) {
|
||||
/*
|
||||
* Endpoint is inclusive, and we’ll be subtracting
|
||||
* Endpoint is inclusive, and we'll be subtracting
|
||||
* 1 from the column when emitting the URL.
|
||||
*/
|
||||
c++;
|
||||
|
|
@ -563,7 +557,7 @@ remove_overlapping(url_list_t *urls, int cols)
|
|||
(in_start >= out_start && in_end <= out_end))
|
||||
{
|
||||
/*
|
||||
* OSC-8 URLs can’t overlap with each
|
||||
* OSC-8 URLs can't overlap with each
|
||||
* other.
|
||||
*
|
||||
* Similarly, auto-detected URLs cannot overlap with
|
||||
|
|
@ -639,7 +633,7 @@ generate_key_combos(const struct config *conf,
|
|||
|
||||
xassert(hints_count - offset >= count);
|
||||
|
||||
/* Copy slice of ‘hints’ array to the caller provided array */
|
||||
/* Copy slice of 'hints' array to the caller provided array */
|
||||
for (size_t i = 0; i < hints_count; i++) {
|
||||
if (i >= offset && i < offset + count)
|
||||
combos[i - offset] = hints[i];
|
||||
|
|
@ -648,7 +642,7 @@ generate_key_combos(const struct config *conf,
|
|||
}
|
||||
free(hints);
|
||||
|
||||
/* Sorting is a kind of shuffle, since we’re sorting on the
|
||||
/* Sorting is a kind of shuffle, since we're sorting on the
|
||||
* *reversed* strings */
|
||||
qsort(combos, count, sizeof(char32_t *), &c32cmp_qsort_wrapper);
|
||||
|
||||
|
|
@ -685,7 +679,7 @@ urls_assign_key_combos(const struct config *conf, url_list_t *urls)
|
|||
break;
|
||||
|
||||
if (it->item.id == it2->item.id &&
|
||||
strcmp(it->item.url, it2->item.url) == 0)
|
||||
streq(it->item.url, it2->item.url))
|
||||
{
|
||||
id_already_seen = true;
|
||||
break;
|
||||
|
|
@ -704,7 +698,7 @@ urls_assign_key_combos(const struct config *conf, url_list_t *urls)
|
|||
if (&it->item == &it2->item)
|
||||
break;
|
||||
|
||||
if (strcmp(it->item.url, it2->item.url) == 0) {
|
||||
if (streq(it->item.url, it2->item.url)) {
|
||||
it->item.key = xc32dup(it2->item.key);
|
||||
url_already_seen = true;
|
||||
break;
|
||||
|
|
@ -715,7 +709,7 @@ urls_assign_key_combos(const struct config *conf, url_list_t *urls)
|
|||
it->item.key = combos[combo_idx++];
|
||||
}
|
||||
|
||||
/* Free combos we didn’t use up */
|
||||
/* Free combos we didn't use up */
|
||||
for (size_t i = combo_idx; i < count; i++)
|
||||
free(combos[i]);
|
||||
|
||||
|
|
@ -795,7 +789,7 @@ urls_render(struct terminal *term)
|
|||
}
|
||||
term->render.last_cursor.row = NULL;
|
||||
|
||||
/* Clear scroll damage, to ensure we don’t apply it twice (once on
|
||||
/* Clear scroll damage, to ensure we don't apply it twice (once on
|
||||
* the snapshot:ed grid, and then later again on the real grid) */
|
||||
tll_free(term->grid->scroll_damage);
|
||||
|
||||
|
|
@ -839,10 +833,10 @@ urls_reset(struct terminal *term)
|
|||
term->url_grid_snapshot = NULL;
|
||||
|
||||
/*
|
||||
* Make sure “last cursor” doesn’t point to a row in the just
|
||||
* Make sure "last cursor" doesn't point to a row in the just
|
||||
* free:d snapshot grid.
|
||||
*
|
||||
* Note that it will still be erased properly (if hasn’t already),
|
||||
* Note that it will still be erased properly (if hasn't already),
|
||||
* since we marked the cell as dirty *before* taking the grid
|
||||
* snapshot.
|
||||
*/
|
||||
|
|
|
|||
|
|
@ -23,6 +23,5 @@ void urls_reset(struct terminal *term);
|
|||
void urls_input(struct seat *seat, struct terminal *term,
|
||||
const struct key_binding_set *bindings, uint32_t key,
|
||||
xkb_keysym_t sym, xkb_mod_mask_t mods, xkb_mod_mask_t consumed,
|
||||
xkb_mod_mask_t locked,
|
||||
const xkb_keysym_t *raw_syms, size_t raw_count,
|
||||
uint32_t serial);
|
||||
|
|
|
|||
8
util.h
8
util.h
|
|
@ -1,12 +1,20 @@
|
|||
#pragma once
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include <string.h>
|
||||
#include <threads.h>
|
||||
|
||||
#define ALEN(v) (sizeof(v) / sizeof((v)[0]))
|
||||
#define min(x, y) ((x) < (y) ? (x) : (y))
|
||||
#define max(x, y) ((x) > (y) ? (x) : (y))
|
||||
|
||||
static inline bool
|
||||
streq(const char *a, const char *b)
|
||||
{
|
||||
return strcmp(a, b) == 0;
|
||||
}
|
||||
|
||||
static inline const char *
|
||||
thrd_err_as_string(int thrd_err)
|
||||
{
|
||||
|
|
|
|||
20
vt.c
20
vt.c
|
|
@ -137,12 +137,12 @@ action_execute(struct terminal *term, uint8_t c)
|
|||
/* backspace */
|
||||
#if 0
|
||||
/*
|
||||
* This is the “correct” BS behavior. However, it doesn’t play
|
||||
* This is the "correct" BS behavior. However, it doesn't play
|
||||
* nicely with bw/auto_left_margin, hence the alternative
|
||||
* implementation below.
|
||||
*
|
||||
* Note that it breaks vttest “1. Test of cursor movements ->
|
||||
* Test of autowrap”
|
||||
* Note that it breaks vttest "1. Test of cursor movements ->
|
||||
* Test of autowrap"
|
||||
*/
|
||||
term_cursor_left(term, 1);
|
||||
#else
|
||||
|
|
@ -154,7 +154,7 @@ action_execute(struct terminal *term, uint8_t c)
|
|||
likely(term->reverse_wrap && term->auto_margin))
|
||||
{
|
||||
if (term->grid->cursor.point.row <= term->scroll_region.start) {
|
||||
/* Don’t wrap past, or inside, the scrolling region(?) */
|
||||
/* Don't wrap past, or inside, the scrolling region(?) */
|
||||
} else
|
||||
term_cursor_to(
|
||||
term,
|
||||
|
|
@ -398,7 +398,7 @@ action_collect(struct terminal *term, uint8_t c)
|
|||
* more.
|
||||
*
|
||||
* As such, we optimize *reading* the private(s), and *resetting*
|
||||
* them (in action_clear()). Writing is ok if it’s a bit slow.
|
||||
* them (in action_clear()). Writing is ok if it's a bit slow.
|
||||
*/
|
||||
|
||||
if ((term->vt.private & 0xff) == 0)
|
||||
|
|
@ -783,7 +783,7 @@ action_utf8_print(struct terminal *term, char32_t wc)
|
|||
|
||||
/*
|
||||
* We may have a key collisison, so need to check that
|
||||
* it’s a true match. If not, bump the key and try
|
||||
* it's a true match. If not, bump the key and try
|
||||
* again.
|
||||
*/
|
||||
|
||||
|
|
@ -920,8 +920,8 @@ action_utf8_33(struct terminal *term, uint8_t c)
|
|||
return;
|
||||
}
|
||||
|
||||
/* Note: the E0 range contains overlong encodings. We don’t try to
|
||||
detect, as they’ll still decode to valid UTF-32. */
|
||||
/* Note: the E0 range contains overlong encodings. We don't try to
|
||||
detect, as they'll still decode to valid UTF-32. */
|
||||
|
||||
action_utf8_print(term, term->vt.utf8);
|
||||
}
|
||||
|
|
@ -960,8 +960,8 @@ action_utf8_44(struct terminal *term, uint8_t c)
|
|||
return;
|
||||
}
|
||||
|
||||
/* Note: the F0 range contains overlong encodings. We don’t try to
|
||||
detect, as they’ll still decode to valid UTF-32. */
|
||||
/* Note: the F0 range contains overlong encodings. We don't try to
|
||||
detect, as they'll still decode to valid UTF-32. */
|
||||
|
||||
action_utf8_print(term, term->vt.utf8);
|
||||
}
|
||||
|
|
|
|||
150
wayland.c
150
wayland.c
|
|
@ -392,8 +392,8 @@ static void
|
|||
update_term_for_output_change(struct terminal *term)
|
||||
{
|
||||
const float old_scale = term->scale;
|
||||
const float logical_width = term->width / term->scale;
|
||||
const float logical_height = term->height / term->scale;
|
||||
const float logical_width = term->width / old_scale;
|
||||
const float logical_height = term->height / old_scale;
|
||||
|
||||
/* Note: order matters! term_update_scale() must come first */
|
||||
bool scale_updated = term_update_scale(term);
|
||||
|
|
@ -402,24 +402,37 @@ update_term_for_output_change(struct terminal *term)
|
|||
|
||||
csd_reload_font(term->window, old_scale);
|
||||
|
||||
enum resize_options resize_opts = RESIZE_KEEP_GRID;
|
||||
|
||||
if (fonts_updated) {
|
||||
/*
|
||||
* If the fonts have been updated, the cell dimensions have
|
||||
* changed. This requires a “forced” resize, since the surface
|
||||
* changed. This requires a "forced" resize, since the surface
|
||||
* buffer dimensions may not have been updated (in which case
|
||||
* render_size() normally shortcuts and returns early).
|
||||
* render_resize() normally shortcuts and returns early).
|
||||
*/
|
||||
render_resize_force(term, (int)roundf(logical_width), (int)roundf(logical_height));
|
||||
resize_opts |= RESIZE_FORCE;
|
||||
} else if (!scale_updated) {
|
||||
/* No need to resize if neither scale nor fonts have changed */
|
||||
return;
|
||||
} else if (term->conf->dpi_aware) {
|
||||
/*
|
||||
* If fonts are sized according to DPI, it is possible for the cell
|
||||
* size to remain the same when display scale changes. This will not
|
||||
* change the surface buffer dimensions, but will change the logical
|
||||
* size of the window. To ensure that the compositor is made aware of
|
||||
* the proper logical size, force a resize rather than allowing
|
||||
* render_resize() to shortcut the notification if the buffer
|
||||
* dimensions remain the same.
|
||||
*/
|
||||
resize_opts |= RESIZE_FORCE;
|
||||
}
|
||||
|
||||
else if (scale_updated) {
|
||||
/*
|
||||
* A scale update means the surface buffer dimensions have
|
||||
* been updated, even though the window logical dimensions
|
||||
* haven’t changed.
|
||||
*/
|
||||
render_resize(term, (int)roundf(logical_width), (int)roundf(logical_height));
|
||||
}
|
||||
render_resize(
|
||||
term,
|
||||
(int)roundf(logical_width),
|
||||
(int)roundf(logical_height),
|
||||
resize_opts);
|
||||
}
|
||||
|
||||
static void
|
||||
|
|
@ -705,9 +718,37 @@ surface_leave(void *data, struct wl_surface *wl_surface,
|
|||
LOG_WARN("unmapped from unknown output");
|
||||
}
|
||||
|
||||
#if defined(WL_SURFACE_PREFERRED_BUFFER_SCALE_SINCE_VERSION)
|
||||
static void
|
||||
surface_preferred_buffer_scale(void *data, struct wl_surface *surface,
|
||||
int32_t scale)
|
||||
{
|
||||
struct wl_window *win = data;
|
||||
|
||||
if (win->preferred_buffer_scale == scale)
|
||||
return;
|
||||
|
||||
LOG_DBG("wl_surface preferred scale: %d -> %d", win->preferred_buffer_scale, scale);
|
||||
|
||||
win->preferred_buffer_scale = scale;
|
||||
update_term_for_output_change(win->term);
|
||||
}
|
||||
|
||||
static void
|
||||
surface_preferred_buffer_transform(void *data, struct wl_surface *surface,
|
||||
uint32_t transform)
|
||||
{
|
||||
|
||||
}
|
||||
#endif
|
||||
|
||||
static const struct wl_surface_listener surface_listener = {
|
||||
.enter = &surface_enter,
|
||||
.leave = &surface_leave,
|
||||
#if defined(WL_SURFACE_PREFERRED_BUFFER_SCALE_SINCE_VERSION)
|
||||
.preferred_buffer_scale = &surface_preferred_buffer_scale,
|
||||
.preferred_buffer_transform = &surface_preferred_buffer_transform,
|
||||
#endif
|
||||
};
|
||||
|
||||
static void
|
||||
|
|
@ -882,7 +923,7 @@ xdg_toplevel_wm_capabilities(void *data,
|
|||
|
||||
static const struct xdg_toplevel_listener xdg_toplevel_listener = {
|
||||
.configure = &xdg_toplevel_configure,
|
||||
/*.close = */&xdg_toplevel_close, /* epoll-shim defines a macro ‘close’... */
|
||||
/*.close = */&xdg_toplevel_close, /* epoll-shim defines a macro 'close'... */
|
||||
#if defined(XDG_TOPLEVEL_CONFIGURE_BOUNDS_SINCE_VERSION)
|
||||
.configure_bounds = &xdg_toplevel_configure_bounds,
|
||||
#endif
|
||||
|
|
@ -948,9 +989,11 @@ xdg_surface_configure(void *data, struct xdg_surface *xdg_surface,
|
|||
|
||||
xdg_surface_ack_configure(xdg_surface, serial);
|
||||
|
||||
enum resize_options opts = RESIZE_BY_CELLS;
|
||||
|
||||
#if 1
|
||||
/*
|
||||
* TODO: decide if we should do the last “forced” call when ending
|
||||
* TODO: decide if we should do the last "forced" call when ending
|
||||
* an interactive resize.
|
||||
*
|
||||
* Without it, the last TIOCSWINSZ sent to the client will be a
|
||||
|
|
@ -961,13 +1004,12 @@ xdg_surface_configure(void *data, struct xdg_surface *xdg_surface,
|
|||
* Note: if we also disable content centering while resizing, then
|
||||
* the last, forced, resize *is* necessary.
|
||||
*/
|
||||
bool resized = was_resizing && !win->is_resizing
|
||||
? render_resize_force(term, new_width, new_height)
|
||||
: render_resize(term, new_width, new_height);
|
||||
#else
|
||||
bool resized = render_resize(term, new_width, new_height);
|
||||
if (was_resizing && !win->is_resizing)
|
||||
opts |= RESIZE_FORCE;
|
||||
#endif
|
||||
|
||||
bool resized = render_resize(term, new_width, new_height, opts);
|
||||
|
||||
if (win->configure.is_activated)
|
||||
term_visual_focus_in(term);
|
||||
else
|
||||
|
|
@ -1052,16 +1094,22 @@ handle_global(void *data, struct wl_registry *registry,
|
|||
LOG_DBG("global: 0x%08x, interface=%s, version=%u", name, interface, version);
|
||||
struct wayland *wayl = data;
|
||||
|
||||
if (strcmp(interface, wl_compositor_interface.name) == 0) {
|
||||
if (streq(interface, wl_compositor_interface.name)) {
|
||||
const uint32_t required = 4;
|
||||
if (!verify_iface_version(interface, version, required))
|
||||
return;
|
||||
|
||||
#if defined (WL_SURFACE_PREFERRED_BUFFER_SCALE_SINCE_VERSION)
|
||||
const uint32_t preferred = WL_SURFACE_PREFERRED_BUFFER_SCALE_SINCE_VERSION;
|
||||
wayl->has_wl_compositor_v6 = version >= WL_SURFACE_PREFERRED_BUFFER_SCALE_SINCE_VERSION;
|
||||
#else
|
||||
const uint32_t preferred = required;
|
||||
#endif
|
||||
wayl->compositor = wl_registry_bind(
|
||||
wayl->registry, name, &wl_compositor_interface, required);
|
||||
wayl->registry, name, &wl_compositor_interface, min(version, preferred));
|
||||
}
|
||||
|
||||
else if (strcmp(interface, wl_subcompositor_interface.name) == 0) {
|
||||
else if (streq(interface, wl_subcompositor_interface.name)) {
|
||||
const uint32_t required = 1;
|
||||
if (!verify_iface_version(interface, version, required))
|
||||
return;
|
||||
|
|
@ -1070,7 +1118,7 @@ handle_global(void *data, struct wl_registry *registry,
|
|||
wayl->registry, name, &wl_subcompositor_interface, required);
|
||||
}
|
||||
|
||||
else if (strcmp(interface, wl_shm_interface.name) == 0) {
|
||||
else if (streq(interface, wl_shm_interface.name)) {
|
||||
const uint32_t required = 1;
|
||||
if (!verify_iface_version(interface, version, required))
|
||||
return;
|
||||
|
|
@ -1080,7 +1128,7 @@ handle_global(void *data, struct wl_registry *registry,
|
|||
wl_shm_add_listener(wayl->shm, &shm_listener, wayl);
|
||||
}
|
||||
|
||||
else if (strcmp(interface, xdg_wm_base_interface.name) == 0) {
|
||||
else if (streq(interface, xdg_wm_base_interface.name)) {
|
||||
const uint32_t required = 1;
|
||||
if (!verify_iface_version(interface, version, required))
|
||||
return;
|
||||
|
|
@ -1105,7 +1153,7 @@ handle_global(void *data, struct wl_registry *registry,
|
|||
xdg_wm_base_add_listener(wayl->shell, &xdg_wm_base_listener, wayl);
|
||||
}
|
||||
|
||||
else if (strcmp(interface, zxdg_decoration_manager_v1_interface.name) == 0) {
|
||||
else if (streq(interface, zxdg_decoration_manager_v1_interface.name)) {
|
||||
const uint32_t required = 1;
|
||||
if (!verify_iface_version(interface, version, required))
|
||||
return;
|
||||
|
|
@ -1114,7 +1162,7 @@ handle_global(void *data, struct wl_registry *registry,
|
|||
wayl->registry, name, &zxdg_decoration_manager_v1_interface, required);
|
||||
}
|
||||
|
||||
else if (strcmp(interface, wl_seat_interface.name) == 0) {
|
||||
else if (streq(interface, wl_seat_interface.name)) {
|
||||
const uint32_t required = 5;
|
||||
if (!verify_iface_version(interface, version, required))
|
||||
return;
|
||||
|
|
@ -1154,7 +1202,7 @@ handle_global(void *data, struct wl_registry *registry,
|
|||
wl_seat_add_listener(wl_seat, &seat_listener, seat);
|
||||
}
|
||||
|
||||
else if (strcmp(interface, zxdg_output_manager_v1_interface.name) == 0) {
|
||||
else if (streq(interface, zxdg_output_manager_v1_interface.name)) {
|
||||
const uint32_t required = 1;
|
||||
if (!verify_iface_version(interface, version, required))
|
||||
return;
|
||||
|
|
@ -1171,7 +1219,7 @@ handle_global(void *data, struct wl_registry *registry,
|
|||
}
|
||||
}
|
||||
|
||||
else if (strcmp(interface, wl_output_interface.name) == 0) {
|
||||
else if (streq(interface, wl_output_interface.name)) {
|
||||
const uint32_t required = 2;
|
||||
if (!verify_iface_version(interface, version, required))
|
||||
return;
|
||||
|
|
@ -1203,7 +1251,7 @@ handle_global(void *data, struct wl_registry *registry,
|
|||
}
|
||||
}
|
||||
|
||||
else if (strcmp(interface, wl_data_device_manager_interface.name) == 0) {
|
||||
else if (streq(interface, wl_data_device_manager_interface.name)) {
|
||||
const uint32_t required = 3;
|
||||
if (!verify_iface_version(interface, version, required))
|
||||
return;
|
||||
|
|
@ -1215,7 +1263,7 @@ handle_global(void *data, struct wl_registry *registry,
|
|||
seat_add_data_device(&it->item);
|
||||
}
|
||||
|
||||
else if (strcmp(interface, zwp_primary_selection_device_manager_v1_interface.name) == 0) {
|
||||
else if (streq(interface, zwp_primary_selection_device_manager_v1_interface.name)) {
|
||||
const uint32_t required = 1;
|
||||
if (!verify_iface_version(interface, version, required))
|
||||
return;
|
||||
|
|
@ -1228,7 +1276,7 @@ handle_global(void *data, struct wl_registry *registry,
|
|||
seat_add_primary_selection(&it->item);
|
||||
}
|
||||
|
||||
else if (strcmp(interface, wp_presentation_interface.name) == 0) {
|
||||
else if (streq(interface, wp_presentation_interface.name)) {
|
||||
if (wayl->presentation_timings) {
|
||||
const uint32_t required = 1;
|
||||
if (!verify_iface_version(interface, version, required))
|
||||
|
|
@ -1241,7 +1289,7 @@ handle_global(void *data, struct wl_registry *registry,
|
|||
}
|
||||
}
|
||||
|
||||
else if (strcmp(interface, xdg_activation_v1_interface.name) == 0) {
|
||||
else if (streq(interface, xdg_activation_v1_interface.name)) {
|
||||
const uint32_t required = 1;
|
||||
if (!verify_iface_version(interface, version, required))
|
||||
return;
|
||||
|
|
@ -1250,7 +1298,7 @@ handle_global(void *data, struct wl_registry *registry,
|
|||
wayl->registry, name, &xdg_activation_v1_interface, required);
|
||||
}
|
||||
|
||||
else if (strcmp(interface, wp_viewporter_interface.name) == 0) {
|
||||
else if (streq(interface, wp_viewporter_interface.name)) {
|
||||
const uint32_t required = 1;
|
||||
if (!verify_iface_version(interface, version, required))
|
||||
return;
|
||||
|
|
@ -1259,7 +1307,7 @@ handle_global(void *data, struct wl_registry *registry,
|
|||
wayl->registry, name, &wp_viewporter_interface, required);
|
||||
}
|
||||
|
||||
else if (strcmp(interface, wp_fractional_scale_manager_v1_interface.name) == 0) {
|
||||
else if (streq(interface, wp_fractional_scale_manager_v1_interface.name)) {
|
||||
const uint32_t required = 1;
|
||||
if (!verify_iface_version(interface, version, required))
|
||||
return;
|
||||
|
|
@ -1269,7 +1317,7 @@ handle_global(void *data, struct wl_registry *registry,
|
|||
&wp_fractional_scale_manager_v1_interface, required);
|
||||
}
|
||||
|
||||
else if (strcmp(interface, wp_cursor_shape_manager_v1_interface.name) == 0) {
|
||||
else if (streq(interface, wp_cursor_shape_manager_v1_interface.name)) {
|
||||
const uint32_t required = 1;
|
||||
if (!verify_iface_version(interface, version, required))
|
||||
return;
|
||||
|
|
@ -1279,7 +1327,7 @@ handle_global(void *data, struct wl_registry *registry,
|
|||
}
|
||||
|
||||
#if defined(FOOT_IME_ENABLED) && FOOT_IME_ENABLED
|
||||
else if (strcmp(interface, zwp_text_input_manager_v3_interface.name) == 0) {
|
||||
else if (streq(interface, zwp_text_input_manager_v3_interface.name)) {
|
||||
const uint32_t required = 1;
|
||||
if (!verify_iface_version(interface, version, required))
|
||||
return;
|
||||
|
|
@ -1700,6 +1748,10 @@ wayl_win_init(struct terminal *term, const char *token)
|
|||
win->fractional_scale, &fractional_scale_listener, win);
|
||||
}
|
||||
|
||||
if (wayl->has_wl_compositor_v6) {
|
||||
win->preferred_buffer_scale = 1;
|
||||
}
|
||||
|
||||
win->xdg_surface = xdg_wm_base_get_xdg_surface(wayl->shell, win->surface.surf);
|
||||
xdg_surface_add_listener(win->xdg_surface, &xdg_surface_listener, win);
|
||||
|
||||
|
|
@ -2020,14 +2072,26 @@ surface_scale_explicit_width_height(
|
|||
wp_viewport_set_destination(
|
||||
surf->viewport, roundf(width / scale), roundf(height / scale));
|
||||
} else {
|
||||
LOG_DBG("scaling by a factor of %.2f using legacy mode "
|
||||
"(width=%d, height=%d)", scale, width, height);
|
||||
const char *mode UNUSED = term_preferred_buffer_scale(win->term)
|
||||
? "wl_surface.preferred_buffer_scale"
|
||||
: "legacy mode";
|
||||
LOG_DBG("scaling by a factor of %.2f using %s "
|
||||
"(width=%d, height=%d)" , scale, mode, width, height);
|
||||
|
||||
xassert(scale == floorf(scale));
|
||||
|
||||
const int iscale = (int)floorf(scale);
|
||||
xassert(width % iscale == 0);
|
||||
xassert(height % iscale == 0);
|
||||
|
||||
if (verify) {
|
||||
if (width % iscale != 0) {
|
||||
BUG("width=%d is not valid with scaling factor %.2f (%d %% %d != 0)",
|
||||
width, scale, width, iscale);
|
||||
}
|
||||
|
||||
if (height % iscale != 0) {
|
||||
BUG("height=%d is not valid with scaling factor %.2f (%d %% %d != 0)",
|
||||
height, scale, height, iscale);
|
||||
}
|
||||
}
|
||||
|
||||
wl_surface_set_buffer_scale(surf->surf, iscale);
|
||||
}
|
||||
|
|
@ -2090,7 +2154,7 @@ bool
|
|||
wayl_win_set_urgent(struct wl_window *win)
|
||||
{
|
||||
if (win->urgency_token_is_pending) {
|
||||
/* We already have a pending token. Don’t request another one,
|
||||
/* We already have a pending token. Don't request another one,
|
||||
* to avoid flooding the Wayland socket */
|
||||
return true;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -128,8 +128,8 @@ struct seat {
|
|||
xkb_mod_index_t mod_caps;
|
||||
xkb_mod_index_t mod_num;
|
||||
|
||||
xkb_mod_mask_t bind_significant;
|
||||
xkb_mod_mask_t kitty_significant;
|
||||
xkb_mod_mask_t legacy_significant; /* Significant modifiers for the legacy keyboard protocol */
|
||||
xkb_mod_mask_t kitty_significant; /* Significant modifiers for the kitty keyboard protocol */
|
||||
|
||||
xkb_keycode_t key_arrow_up;
|
||||
xkb_keycode_t key_arrow_down;
|
||||
|
|
@ -363,6 +363,7 @@ struct wl_window {
|
|||
|
||||
bool unmapped;
|
||||
float scale;
|
||||
int preferred_buffer_scale;
|
||||
|
||||
struct zxdg_toplevel_decoration_v1 *xdg_toplevel_decoration;
|
||||
|
||||
|
|
@ -429,6 +430,8 @@ struct wayland {
|
|||
struct wl_subcompositor *sub_compositor;
|
||||
struct wl_shm *shm;
|
||||
|
||||
bool has_wl_compositor_v6;
|
||||
|
||||
struct zxdg_output_manager_v1 *xdg_output_manager;
|
||||
|
||||
struct xdg_wm_base *shell;
|
||||
|
|
|
|||
|
|
@ -1,8 +1,6 @@
|
|||
#include <errno.h>
|
||||
#include <stdarg.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include "xmalloc.h"
|
||||
#include "debug.h"
|
||||
|
||||
|
|
|
|||
12
xmalloc.h
12
xmalloc.h
|
|
@ -2,6 +2,7 @@
|
|||
|
||||
#include <stdarg.h>
|
||||
#include <stddef.h>
|
||||
#include <string.h>
|
||||
#include <wchar.h>
|
||||
#include <uchar.h>
|
||||
|
||||
|
|
@ -16,3 +17,14 @@ char *xstrndup(const char *str, size_t n) XSTRDUP;
|
|||
char *xasprintf(const char *format, ...) PRINTF(1) XMALLOC;
|
||||
char *xvasprintf(const char *format, va_list va) VPRINTF(1) XMALLOC;
|
||||
char32_t *xc32dup(const char32_t *str) XSTRDUP;
|
||||
|
||||
static inline char *
|
||||
xstrjoin(const char *s1, const char *s2)
|
||||
{
|
||||
size_t n1 = strlen(s1);
|
||||
size_t n2 = strlen(s2);
|
||||
char *joined = xmalloc(n1 + n2 + 1);
|
||||
memcpy(joined, s1, n1);
|
||||
memcpy(joined + n1, s2, n2 + 1);
|
||||
return joined;
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue