mirror of
https://codeberg.org/dnkl/foot.git
synced 2026-04-27 06:46:44 -04:00
Merge CHANGELOG.md
This commit is contained in:
commit
dd647203fb
52 changed files with 2907 additions and 1116 deletions
|
|
@ -1,4 +1,4 @@
|
||||||
image: alpine/latest
|
image: alpine/edge
|
||||||
packages:
|
packages:
|
||||||
- musl-dev
|
- musl-dev
|
||||||
- eudev-libs
|
- eudev-libs
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,7 @@ pipeline:
|
||||||
branch:
|
branch:
|
||||||
- master
|
- master
|
||||||
- releases/*
|
- releases/*
|
||||||
image: alpine:latest
|
image: alpine:edge
|
||||||
commands:
|
commands:
|
||||||
- apk add python3
|
- apk add python3
|
||||||
- apk add py3-pip
|
- apk add py3-pip
|
||||||
|
|
@ -16,7 +16,7 @@ pipeline:
|
||||||
branch:
|
branch:
|
||||||
- master
|
- master
|
||||||
- releases/*
|
- releases/*
|
||||||
image: alpine:latest
|
image: alpine:edge
|
||||||
commands:
|
commands:
|
||||||
- apk add git
|
- apk add git
|
||||||
- mkdir -p subprojects && cd subprojects
|
- mkdir -p subprojects && cd subprojects
|
||||||
|
|
@ -30,7 +30,7 @@ pipeline:
|
||||||
- master
|
- master
|
||||||
- releases/*
|
- releases/*
|
||||||
group: build
|
group: build
|
||||||
image: alpine:latest
|
image: alpine:edge
|
||||||
commands:
|
commands:
|
||||||
- apk update
|
- apk update
|
||||||
- apk add musl-dev linux-headers meson ninja gcc clang scdoc ncurses
|
- apk add musl-dev linux-headers meson ninja gcc clang scdoc ncurses
|
||||||
|
|
@ -87,7 +87,7 @@ pipeline:
|
||||||
- master
|
- master
|
||||||
- releases/*
|
- releases/*
|
||||||
group: build
|
group: build
|
||||||
image: i386/alpine:latest
|
image: i386/alpine:edge
|
||||||
commands:
|
commands:
|
||||||
- apk update
|
- apk update
|
||||||
- apk add musl-dev linux-headers meson ninja gcc clang scdoc ncurses
|
- apk add musl-dev linux-headers meson ninja gcc clang scdoc ncurses
|
||||||
|
|
|
||||||
164
CHANGELOG.md
164
CHANGELOG.md
|
|
@ -1,6 +1,7 @@
|
||||||
# Changelog
|
# Changelog
|
||||||
|
|
||||||
* [Unreleased](#unreleased)
|
* [Unreleased](#unreleased)
|
||||||
|
* [1.15.0](#1-15-0)
|
||||||
* [1.14.0](#1-14-0)
|
* [1.14.0](#1-14-0)
|
||||||
* [1.13.1](#1-13-1)
|
* [1.13.1](#1-13-1)
|
||||||
* [1.13.0](#1-13-0)
|
* [1.13.0](#1-13-0)
|
||||||
|
|
@ -44,16 +45,14 @@
|
||||||
|
|
||||||
## Unreleased
|
## Unreleased
|
||||||
### Added
|
### Added
|
||||||
|
|
||||||
* VT: implemented `XTQMODKEYS` query (`CSI ? Pp m`).
|
|
||||||
|
|
||||||
|
|
||||||
### Changed
|
### Changed
|
||||||
|
|
||||||
* Kitty keyboard protocol: F3 is now encoded as `CSI 13~` instead of
|
* When window is mapped, use metadata (DPI, scaling factor, subpixel
|
||||||
`CSI R`. The kitty keyboard protocol originally allowed F3 to be
|
configuration) from the monitor we were most recently mapped on,
|
||||||
encoded as `CSI R`, but this was removed from the specification
|
instead of the one least recently.
|
||||||
since `CSI R` conflicts with the _”Cursor Position Report”_.
|
* Starlight theme (the default theme) updated to [V4][starlight-v4]
|
||||||
|
* Background transparency (alpha) is now disabled in fullscreened
|
||||||
|
windows ([#1416][1416]).
|
||||||
* Foot server systemd units now use the standard
|
* Foot server systemd units now use the standard
|
||||||
graphical-session.target ([#1281][1281]).
|
graphical-session.target ([#1281][1281]).
|
||||||
* If `$XDG_RUNTIME_DIR/foot-$WAYLAND_DISPLAY.sock` does not exist,
|
* If `$XDG_RUNTIME_DIR/foot-$WAYLAND_DISPLAY.sock` does not exist,
|
||||||
|
|
@ -61,6 +60,8 @@
|
||||||
`/tmp/foot.sock`, even if `$WAYLAND_DISPLAY` and/or
|
`/tmp/foot.sock`, even if `$WAYLAND_DISPLAY` and/or
|
||||||
`$XDG_RUNTIME_DIR` are defined ([#1281][1281]).
|
`$XDG_RUNTIME_DIR` are defined ([#1281][1281]).
|
||||||
|
|
||||||
|
[starlight-v4]: https://github.com/CosmicToast/starlight/blob/v4/CHANGELOG.md#v4
|
||||||
|
[1416]: https://codeberg.org/dnkl/foot/issues/1416
|
||||||
[1281]: https://codeberg.org/dnkl/foot/pulls/1281
|
[1281]: https://codeberg.org/dnkl/foot/pulls/1281
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -68,17 +69,154 @@
|
||||||
### Removed
|
### Removed
|
||||||
### Fixed
|
### Fixed
|
||||||
|
|
||||||
* Incorrect icon in dock and window switcher on Gnome ([#1317][1317])
|
* Use appropriate rounding when applying fractional scales.
|
||||||
* Crash when scrolling after resizing the window with non-zero
|
* Xcursor not being scaled correctly on `fractional-scale-v1` capable
|
||||||
scrolling regions.
|
compositors.
|
||||||
|
* `dpi-aware=yes` being broken on `fractional-scale-v1` capable
|
||||||
|
compositors (and when a fractional scaling factor is being used)
|
||||||
|
([#1404][1404]).
|
||||||
|
* Initial font size being wrong on `fractional-scale-v1` capable
|
||||||
|
compositors, with multiple monitors with different scaling factors
|
||||||
|
connected ([#1404][1404]).
|
||||||
|
* Crash when _pointer capability_ is removed from a seat, on
|
||||||
|
compositors without `cursor-shape-v1 support` ([#1411][1411]).
|
||||||
|
* Crash on exit, if the mouse is hovering over the foot window (does
|
||||||
|
not happen on all compositors)
|
||||||
|
* Visual glitches when CSD titlebar is transparent.
|
||||||
|
|
||||||
[1317]: https://codeberg.org/dnkl/foot/issues/1317
|
[1404]: https://codeberg.org/dnkl/foot/issues/1404
|
||||||
|
[1411]: https://codeberg.org/dnkl/foot/pulls/1411
|
||||||
|
|
||||||
|
|
||||||
### Security
|
### Security
|
||||||
### Contributors
|
### Contributors
|
||||||
|
|
||||||
|
|
||||||
|
## 1.15.0
|
||||||
|
|
||||||
|
### Added
|
||||||
|
|
||||||
|
* VT: implemented `XTQMODKEYS` query (`CSI ? Pp m`).
|
||||||
|
* Meson option `utmp-backend=none|libutempter|ulog|auto`. The default
|
||||||
|
is `auto`, which will select `libutempter` on Linux, `ulog` on
|
||||||
|
FreeBSD, and `none` for all others.
|
||||||
|
* Sixel aspect ratio.
|
||||||
|
* Support for the new `fractional-scale-v1` Wayland protocol. This
|
||||||
|
brings true fractional scaling to Wayland in general, and with this
|
||||||
|
release, to foot.
|
||||||
|
* Support for the new `cursor-shape-v1` Wayland protocol, i.e. server
|
||||||
|
side cursor shapes ([#1379][1379]).
|
||||||
|
* Support for touchscreen input ([#517][517]).
|
||||||
|
* `csd.double-click-to-maximize` option to `foot.ini`. Defaults to
|
||||||
|
`yes` ([#1293][1293]).
|
||||||
|
|
||||||
|
[1379]: https://codeberg.org/dnkl/foot/issues/1379
|
||||||
|
[517]: https://codeberg.org/dnkl/foot/issues/517
|
||||||
|
[1293]: https://codeberg.org/dnkl/foot/issues/1293
|
||||||
|
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
|
||||||
|
* Default color theme is now
|
||||||
|
[starlight](https://github.com/CosmicToast/starlight)
|
||||||
|
([#1321][1321]).
|
||||||
|
* Minimum required meson version is now 0.59 ([#1371][1371]).
|
||||||
|
* `Control+Shift+u` is now bound to `unicode-input` instead of
|
||||||
|
`show-urls-launch`, to follow the convention established in GTK and
|
||||||
|
Qt ([#1183][1183]).
|
||||||
|
* `show-urls-launch` now bound to `Control+Shift+o` ([#1183][1183]).
|
||||||
|
* 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”_.
|
||||||
|
* `[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
|
||||||
|
`utmp-default-helper-path`.
|
||||||
|
* 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
|
||||||
|
scrolling is **enabled** (the default) has been updated to match
|
||||||
|
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
|
||||||
|
sixel is visible. For example, `cat`:ing a sixel in the shell will
|
||||||
|
typically result in the last row not being visible, unless a newline
|
||||||
|
is explicitly added.
|
||||||
|
* Default sixel aspect ratio is now 2:1 instead of 1:1.
|
||||||
|
* Sixel images are no longer cropped to the last non-transparent row.
|
||||||
|
* Sixel images are now re-scaled when the font size is changed
|
||||||
|
([#1383][1383]).
|
||||||
|
* `dpi-aware` now defaults to `no`, and the `auto` value has been
|
||||||
|
removed.
|
||||||
|
* When using custom cursor colors (`cursor.color` is set in
|
||||||
|
`foot.ini`), the cursor is no longer inverted when the cell is
|
||||||
|
selected, or when the cell has the `reverse` (SGR 7) attribute set
|
||||||
|
([#1347][1347]).
|
||||||
|
|
||||||
|
[1321]: https://codeberg.org/dnkl/foot/issues/1321
|
||||||
|
[1371]: https://codeberg.org/dnkl/foot/pulls/1371
|
||||||
|
[1183]: https://codeberg.org/dnkl/foot/issues/1183
|
||||||
|
[1360]: https://codeberg.org/dnkl/foot/issues/1360
|
||||||
|
[1383]: https://codeberg.org/dnkl/foot/issues/1383
|
||||||
|
[1347]: https://codeberg.org/dnkl/foot/issues/1347
|
||||||
|
|
||||||
|
|
||||||
|
### Deprecated
|
||||||
|
|
||||||
|
* `[main].utempter` option.
|
||||||
|
|
||||||
|
|
||||||
|
### Removed
|
||||||
|
|
||||||
|
* `auto` value for the `dpi-aware` option.
|
||||||
|
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
|
||||||
|
* Incorrect icon in dock and window switcher on Gnome ([#1317][1317])
|
||||||
|
* Crash when scrolling after resizing the window with non-zero
|
||||||
|
scrolling regions.
|
||||||
|
* `XTMODKEYS` state not being reset on a terminal reset.
|
||||||
|
* In Gnome dock foot always groups under "foot client". Change
|
||||||
|
instances of footclient and foot to appear as "foot client" and
|
||||||
|
"foot" respectively. ([#1355][1355]).
|
||||||
|
* Glitchy rendering when alpha (transparency) is changed between
|
||||||
|
opaque and non-opaque at runtime (using OSC-11).
|
||||||
|
* Regression: crash when resizing the window when `resize-delay-ms >
|
||||||
|
0` ([#1377][1377]).
|
||||||
|
* Crash when scrolling up while running something that generates a lot
|
||||||
|
of output (for example, `yes`) ([#1380][1380]).
|
||||||
|
* Default key binding for URL mode conflicting with Unicode input on
|
||||||
|
some DEs; `show-urls-launched` is now mapped to `Control+Shift+o` by
|
||||||
|
default, instead of `Control+Shift+u` ([#1183][1183]).
|
||||||
|
|
||||||
|
[1317]: https://codeberg.org/dnkl/foot/issues/1317
|
||||||
|
[1355]: https://codeberg.org/dnkl/foot/issues/1355
|
||||||
|
[1377]: https://codeberg.org/dnkl/foot/issues/1377
|
||||||
|
[1380]: https://codeberg.org/dnkl/foot/issues/1380
|
||||||
|
|
||||||
|
|
||||||
|
### Contributors
|
||||||
|
|
||||||
|
* Antoine Beaupré
|
||||||
|
* CismonX
|
||||||
|
* Craig Barnes
|
||||||
|
* Dan Bungert
|
||||||
|
* jdevdevdev
|
||||||
|
* Kyle Gunger
|
||||||
|
* locture
|
||||||
|
* Phillip Susi
|
||||||
|
* sewn
|
||||||
|
* ShugarSkull
|
||||||
|
* Vivian Szczepanski
|
||||||
|
* Vladimir Bauer
|
||||||
|
* wout
|
||||||
|
* CosmicToast
|
||||||
|
|
||||||
|
|
||||||
## 1.14.0
|
## 1.14.0
|
||||||
|
|
||||||
### Added
|
### Added
|
||||||
|
|
@ -100,7 +238,7 @@
|
||||||
* “Report DA2” terminfo entries (`RV`/`rv`).
|
* “Report DA2” terminfo entries (`RV`/`rv`).
|
||||||
* `XF` terminfo capability (focus in/out events available).
|
* `XF` terminfo capability (focus in/out events available).
|
||||||
* `$TERM_PROGRAM` and `$TERM_PROGRAM_VERSION` environment variables
|
* `$TERM_PROGRAM` and `$TERM_PROGRAM_VERSION` environment variables
|
||||||
set in the slave process.
|
unset in the slave process.
|
||||||
|
|
||||||
[1136]: https://codeberg.org/dnkl/foot/issues/1136
|
[1136]: https://codeberg.org/dnkl/foot/issues/1136
|
||||||
[1225]: https://codeberg.org/dnkl/foot/issues/1225
|
[1225]: https://codeberg.org/dnkl/foot/issues/1225
|
||||||
|
|
|
||||||
12
INSTALL.md
12
INSTALL.md
|
|
@ -45,7 +45,8 @@ subprojects.
|
||||||
* wayland (_client_ and _cursor_ libraries)
|
* wayland (_client_ and _cursor_ libraries)
|
||||||
* xkbcommon
|
* xkbcommon
|
||||||
* utf8proc (_optional_, needed for grapheme clustering)
|
* utf8proc (_optional_, needed for grapheme clustering)
|
||||||
* libutempter (_optional_, needed for utmp logging)
|
* libutempter (_optional_, needed for utmp logging on Linux)
|
||||||
|
* ulog (_optional_, needed for utmp logging on FreeBSD)
|
||||||
* [fcft](https://codeberg.org/dnkl/fcft) [^1]
|
* [fcft](https://codeberg.org/dnkl/fcft) [^1]
|
||||||
|
|
||||||
[^1]: can also be built as subprojects, in which case they are
|
[^1]: can also be built as subprojects, in which case they are
|
||||||
|
|
@ -143,16 +144,17 @@ mkdir -p bld/release && cd bld/release
|
||||||
Available compile-time options:
|
Available compile-time options:
|
||||||
|
|
||||||
| Option | Type | Default | Description | Extra dependencies |
|
| Option | Type | Default | Description | Extra dependencies |
|
||||||
|--------------------------------------|---------|-------------------------|-----------------------------------------------------------|--------------------|
|
|--------------------------------------|---------|-------------------------|---------------------------------------------------------------------------------|---------------------|
|
||||||
| `-Ddocs` | feature | `auto` | Builds and install documentation | scdoc |
|
| `-Ddocs` | feature | `auto` | Builds and install documentation | scdoc |
|
||||||
| `-Dtests` | bool | `true` | Build tests (adds a `ninja test` build target) | none |
|
| `-Dtests` | bool | `true` | Build tests (adds a `ninja test` build target) | None |
|
||||||
| `-Dime` | bool | `true` | Enables IME support | None |
|
| `-Dime` | bool | `true` | Enables IME support | None |
|
||||||
| `-Dgrapheme-clustering` | feature | `auto` | Enables grapheme clustering | libutf8proc |
|
| `-Dgrapheme-clustering` | feature | `auto` | Enables grapheme clustering | libutf8proc |
|
||||||
| `-Dterminfo` | feature | `enabled` | Build and install terminfo files | tic (ncurses) |
|
| `-Dterminfo` | feature | `enabled` | Build and install terminfo files | tic (ncurses) |
|
||||||
| `-Ddefault-terminfo` | string | `foot` | Default value of `TERM` | none |
|
| `-Ddefault-terminfo` | string | `foot` | Default value of `TERM` | None |
|
||||||
| `-Dcustom-terminfo-install-location` | string | `${datadir}/terminfo` | Value to set `TERMINFO` to | None |
|
| `-Dcustom-terminfo-install-location` | string | `${datadir}/terminfo` | Value to set `TERMINFO` to | None |
|
||||||
| `-Dsystemd-units-dir` | string | `${systemduserunitdir}` | Where to install the systemd service files (absolute) | None |
|
| `-Dsystemd-units-dir` | string | `${systemduserunitdir}` | Where to install the systemd service files (absolute) | None |
|
||||||
| `-Ddefault-utempter-path` | feature | `auto` | Default path to utempter binary (‘none’ disables default) | libutempter |
|
| `-Dutmp-backend` | combo | `auto` | Which utmp backend to use (`none`, `libutempter`, `ulog` or `auto`) | libutempter or ulog |
|
||||||
|
| `-Dutmp-default-helper-path` | string | `auto` | Default path to utmp helper binary. `auto` selects path based on `utmp-backend` | None |
|
||||||
|
|
||||||
Documentation includes the man pages, readme, changelog and license
|
Documentation includes the man pages, readme, changelog and license
|
||||||
files.
|
files.
|
||||||
|
|
|
||||||
71
README.md
71
README.md
|
|
@ -22,6 +22,7 @@ The fast, lightweight and minimalistic Wayland terminal emulator.
|
||||||
1. [Normal mode](#normal-mode)
|
1. [Normal mode](#normal-mode)
|
||||||
1. [Scrollback search](#scrollback-search)
|
1. [Scrollback search](#scrollback-search)
|
||||||
1. [Mouse](#mouse)
|
1. [Mouse](#mouse)
|
||||||
|
1. [Touchscreen](#touchscreen)
|
||||||
1. [Server (daemon) mode](#server-daemon-mode)
|
1. [Server (daemon) mode](#server-daemon-mode)
|
||||||
1. [URLs](#urls)
|
1. [URLs](#urls)
|
||||||
1. [Shell integration](#shell-integration)
|
1. [Shell integration](#shell-integration)
|
||||||
|
|
@ -163,10 +164,13 @@ These are the default shortcuts. See `man foot.ini` and the example
|
||||||
sequence](https://codeberg.org/dnkl/foot/wiki#user-content-spawning-new-terminal-instances-in-the-current-working-directory),
|
sequence](https://codeberg.org/dnkl/foot/wiki#user-content-spawning-new-terminal-instances-in-the-current-working-directory),
|
||||||
the new terminal will start in the current working directory.
|
the new terminal will start in the current working directory.
|
||||||
|
|
||||||
<kbd>ctrl</kbd>+<kbd>shift</kbd>+<kbd>u</kbd>
|
<kbd>ctrl</kbd>+<kbd>shift</kbd>+<kbd>o</kbd>
|
||||||
: Enter URL mode, where all currently visible URLs are tagged with a
|
: Enter URL mode, where all currently visible URLs are tagged with a
|
||||||
jump label with a key sequence that will open the URL.
|
jump label with a key sequence that will open the URL.
|
||||||
|
|
||||||
|
<kbd>ctrl</kbd>+<kbd>shift</kbd>+<kbd>u</kbd>
|
||||||
|
: Enter Unicode input mode.
|
||||||
|
|
||||||
<kbd>ctrl</kbd>+<kbd>shift</kbd>+<kbd>z</kbd>
|
<kbd>ctrl</kbd>+<kbd>shift</kbd>+<kbd>z</kbd>
|
||||||
: Jump to the previous, currently not visible, prompt. Requires [shell
|
: Jump to the previous, currently not visible, prompt. Requires [shell
|
||||||
integration](https://codeberg.org/dnkl/foot/wiki#user-content-jumping-between-prompts).
|
integration](https://codeberg.org/dnkl/foot/wiki#user-content-jumping-between-prompts).
|
||||||
|
|
@ -246,6 +250,17 @@ These are the default shortcuts. See `man foot.ini` and the example
|
||||||
: Scroll up/down in history
|
: Scroll up/down in history
|
||||||
|
|
||||||
|
|
||||||
|
### Touchscreen
|
||||||
|
|
||||||
|
<kbd>tap</kbd>
|
||||||
|
: Emulates mouse left button click.
|
||||||
|
|
||||||
|
<kbd>drag</kbd>
|
||||||
|
: Scrolls up/down in history.
|
||||||
|
: Holding for a while before dragging (time delay can be configured)
|
||||||
|
emulates mouse dragging with left button held.
|
||||||
|
|
||||||
|
|
||||||
## Server (daemon) mode
|
## Server (daemon) mode
|
||||||
|
|
||||||
When run normally, **foot** is a single-window application; if you
|
When run normally, **foot** is a single-window application; if you
|
||||||
|
|
@ -287,7 +302,7 @@ Foot supports URL detection. But, unlike many other terminal
|
||||||
emulators, where URLs are highlighted when they are hovered and opened
|
emulators, where URLs are highlighted when they are hovered and opened
|
||||||
by clicking on them, foot uses a keyboard driven approach.
|
by clicking on them, foot uses a keyboard driven approach.
|
||||||
|
|
||||||
Pressing <kbd>ctrl</kbd>+<kbd>shift</kbd>+<kbd>u</kbd> enters _“URL
|
Pressing <kbd>ctrl</kbd>+<kbd>shift</kbd>+<kbd>o</kbd> enters _“URL
|
||||||
mode”_, where all currently visible URLs are underlined, and is
|
mode”_, where all currently visible URLs are underlined, and is
|
||||||
associated with a _“jump-label”_. The jump-label indicates the _key
|
associated with a _“jump-label”_. The jump-label indicates the _key
|
||||||
sequence_ (e.g. **”AF”**) to use to activate the URL.
|
sequence_ (e.g. **”AF”**) to use to activate the URL.
|
||||||
|
|
@ -411,27 +426,53 @@ This is not how it is meant to be. Fonts are measured in _point sizes_
|
||||||
**for a reason**; a given point size should have the same height on
|
**for a reason**; a given point size should have the same height on
|
||||||
all mediums, be it printers or monitors, regardless of their DPI.
|
all mediums, be it printers or monitors, regardless of their DPI.
|
||||||
|
|
||||||
Foot’s default behavior is to use the monitor’s DPI to size fonts when
|
That said, on Wayland, Hi-DPI monitors are typically handled by
|
||||||
output scaling has been disabled on **all** monitors. If at least one
|
configuring a _"scaling factor"_ in the compositor. This is usually
|
||||||
monitor has output scaling enabled, fonts will instead by sized using
|
expressed as either a rational value (e.g. _1.5_), or as a percentage
|
||||||
the scaling factor.
|
(e.g. _150%_), by which all fonts and window sizes are supposed to be
|
||||||
|
multiplied.
|
||||||
|
|
||||||
This can be changed to either **always** use the monitor’s DPI
|
For this reason, and because of the new _fractional scaling_ protocol
|
||||||
(regardless of scaling factor), or to **never** use it, with the
|
(see below for details), and because this is how Wayland applications
|
||||||
`dpi-aware` option in `foot.ini`. See the man page, **foot.ini**(5)
|
are expected to behave, foot >= 1.15 will default to scaling fonts
|
||||||
for more information.
|
using the compositor’s scaling factor, and **not** the monitor
|
||||||
|
DPI.
|
||||||
|
|
||||||
When fonts are sized using the monitor’s DPI, glyphs should always
|
This means the (assuming the monitors are at the same viewing
|
||||||
have the same physical height, regardless of monitor.
|
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.
|
||||||
|
|
||||||
Furthermore, foot will re-size the fonts on-the-fly when the window is
|
This can be changed by setting the `dpi-aware` option to `yes` in
|
||||||
moved between screens with different DPIs values. If the window covers
|
`foot.ini`. When enabled, fonts will **not** be sized using the
|
||||||
multiple screens, with different DPIs, the highest DPI will be used.
|
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.
|
||||||
|
|
||||||
|
This means that, assuming the monitors are **at the same viewing
|
||||||
|
distance**, the font size will appear to be the same, at all times.
|
||||||
|
|
||||||
_Note_: if you configure **pixelsize**, rather than **size**, then DPI
|
_Note_: if you configure **pixelsize**, rather than **size**, then DPI
|
||||||
changes will **not** change the font size. Pixels are always pixels.
|
changes will **not** change the font size. Pixels are always pixels.
|
||||||
|
|
||||||
|
|
||||||
|
### Fractional scaling on Wayland
|
||||||
|
|
||||||
|
For a long time, there was no **true** support for _fractional
|
||||||
|
scaling_. That is, values like 1.5 (150%), 1.8 (180%) etc, only
|
||||||
|
integer values, like 2 (200%).
|
||||||
|
|
||||||
|
Compositors that _did_ support fractional scaling did so using a hack;
|
||||||
|
all applications were told to scale to 200%, and then the compositor
|
||||||
|
would down-scale the rendered image to e.g. 150%. This works OK for
|
||||||
|
everything **except fonts**, which ended up blurry.
|
||||||
|
|
||||||
|
With _wayland-protocols 1.32_, a new protocol was introduced, that
|
||||||
|
allows compositors to tell applications the _actual_ scaling
|
||||||
|
factor. Applications can then scale the image using a _viewport_
|
||||||
|
object, instead of setting a scale factor on the raw pixel buffer.
|
||||||
|
|
||||||
|
|
||||||
## Supported OSCs
|
## Supported OSCs
|
||||||
|
|
||||||
OSC, _Operating System Command_, are escape sequences that interacts
|
OSC, _Operating System Command_, are escape sequences that interacts
|
||||||
|
|
|
||||||
5
client.c
5
client.c
|
|
@ -66,11 +66,14 @@ static const char *
|
||||||
version_and_features(void)
|
version_and_features(void)
|
||||||
{
|
{
|
||||||
static char buf[256];
|
static char buf[256];
|
||||||
snprintf(buf, sizeof(buf), "version: %s %cpgo %cime %cgraphemes %cassertions",
|
snprintf(buf, sizeof(buf),
|
||||||
|
"version: %s %cpgo %cime %cgraphemes %cfractional-scaling %ccursor-shape %cassertions",
|
||||||
FOOT_VERSION,
|
FOOT_VERSION,
|
||||||
feature_pgo() ? '+' : '-',
|
feature_pgo() ? '+' : '-',
|
||||||
feature_ime() ? '+' : '-',
|
feature_ime() ? '+' : '-',
|
||||||
feature_graphemes() ? '+' : '-',
|
feature_graphemes() ? '+' : '-',
|
||||||
|
feature_fractional_scaling() ? '+' : ':',
|
||||||
|
feature_cursor_shape() ? '+' : '-',
|
||||||
feature_assertions() ? '+' : '-');
|
feature_assertions() ? '+' : '-');
|
||||||
return buf;
|
return buf;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
126
config.c
126
config.c
|
|
@ -30,8 +30,8 @@
|
||||||
#include "xmalloc.h"
|
#include "xmalloc.h"
|
||||||
#include "xsnprintf.h"
|
#include "xsnprintf.h"
|
||||||
|
|
||||||
static const uint32_t default_foreground = 0x839496;
|
static const uint32_t default_foreground = 0xffffff;
|
||||||
static const uint32_t default_background = 0x002b36;
|
static const uint32_t default_background = 0x242424;
|
||||||
|
|
||||||
static const size_t min_csd_border_width = 5;
|
static const size_t min_csd_border_width = 5;
|
||||||
|
|
||||||
|
|
@ -48,23 +48,23 @@ static const size_t min_csd_border_width = 5;
|
||||||
|
|
||||||
static const uint32_t default_color_table[256] = {
|
static const uint32_t default_color_table[256] = {
|
||||||
// Regular
|
// Regular
|
||||||
0x073642,
|
0x242424,
|
||||||
0xdc322f,
|
0xf62b5a,
|
||||||
0x859900,
|
0x47b413,
|
||||||
0xb58900,
|
0xe3c401,
|
||||||
0x268bd2,
|
0x24acd4,
|
||||||
0xd33682,
|
0xf2affd,
|
||||||
0x2aa198,
|
0x13c299,
|
||||||
0xeee8d5,
|
0xe6e6e6,
|
||||||
|
|
||||||
// Bright
|
// Bright
|
||||||
0x08404f,
|
0x616161,
|
||||||
0xe35f5c,
|
0xff4d51,
|
||||||
0x9fb700,
|
0x35d450,
|
||||||
0xd9a400,
|
0xe9e836,
|
||||||
0x4ba1de,
|
0x5dc5f8,
|
||||||
0xdc619d,
|
0xfeabf2,
|
||||||
0x32c1b6,
|
0x24dfc4,
|
||||||
0xffffff,
|
0xffffff,
|
||||||
|
|
||||||
// 6x6x6 RGB cube
|
// 6x6x6 RGB cube
|
||||||
|
|
@ -972,17 +972,8 @@ parse_section_main(struct context *ctx)
|
||||||
else if (strcmp(key, "underline-thickness") == 0)
|
else if (strcmp(key, "underline-thickness") == 0)
|
||||||
return value_to_pt_or_px(ctx, &conf->underline_thickness);
|
return value_to_pt_or_px(ctx, &conf->underline_thickness);
|
||||||
|
|
||||||
else if (strcmp(key, "dpi-aware") == 0) {
|
else if (strcmp(key, "dpi-aware") == 0)
|
||||||
if (strcmp(value, "auto") == 0)
|
return value_to_bool(ctx, &conf->dpi_aware);
|
||||||
conf->dpi_aware = DPI_AWARE_AUTO;
|
|
||||||
else {
|
|
||||||
bool value;
|
|
||||||
if (!value_to_bool(ctx, &value))
|
|
||||||
return false;
|
|
||||||
conf->dpi_aware = value ? DPI_AWARE_YES : DPI_AWARE_NO;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
else if (strcmp(key, "workers") == 0)
|
else if (strcmp(key, "workers") == 0)
|
||||||
return value_to_uint16(ctx, 10, &conf->render_worker_count);
|
return value_to_uint16(ctx, 10, &conf->render_worker_count);
|
||||||
|
|
@ -1009,13 +1000,29 @@ parse_section_main(struct context *ctx)
|
||||||
else if (strcmp(key, "box-drawings-uses-font-glyphs") == 0)
|
else if (strcmp(key, "box-drawings-uses-font-glyphs") == 0)
|
||||||
return value_to_bool(ctx, &conf->box_drawings_uses_font_glyphs);
|
return value_to_bool(ctx, &conf->box_drawings_uses_font_glyphs);
|
||||||
|
|
||||||
else if (strcmp(key, "utempter") == 0) {
|
else if (strcmp(key, "utmp-helper") == 0 || strcmp(key, "utempter") == 0) {
|
||||||
if (!value_to_str(ctx, &conf->utempter_path))
|
if (strcmp(key, "utempter") == 0) {
|
||||||
|
struct user_notification deprecation = {
|
||||||
|
.kind = USER_NOTIFICATION_DEPRECATED,
|
||||||
|
.text = xasprintf(
|
||||||
|
"%s:%d: \033[1m[main].utempter\033[22m, "
|
||||||
|
"use \033[1m[main].utmp-helper\033[22m instead",
|
||||||
|
ctx->path, ctx->lineno),
|
||||||
|
};
|
||||||
|
tll_push_back(conf->notifications, deprecation);
|
||||||
|
|
||||||
|
LOG_WARN(
|
||||||
|
"%s:%d: [main].utempter is deprecated, "
|
||||||
|
"use [main].utmp-helper instead",
|
||||||
|
ctx->path, ctx->lineno);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!value_to_str(ctx, &conf->utmp_helper_path))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if (strcmp(conf->utempter_path, "none") == 0) {
|
if (strcmp(conf->utmp_helper_path, "none") == 0) {
|
||||||
free(conf->utempter_path);
|
free(conf->utmp_helper_path);
|
||||||
conf->utempter_path = NULL;
|
conf->utmp_helper_path = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
|
|
@ -1468,6 +1475,9 @@ parse_section_csd(struct context *ctx)
|
||||||
else if (strcmp(key, "hide-when-maximized") == 0)
|
else if (strcmp(key, "hide-when-maximized") == 0)
|
||||||
return value_to_bool(ctx, &conf->csd.hide_when_maximized);
|
return value_to_bool(ctx, &conf->csd.hide_when_maximized);
|
||||||
|
|
||||||
|
else if (strcmp(key, "double-click-to-maximize") == 0)
|
||||||
|
return value_to_bool(ctx, &conf->csd.double_click_to_maximize);
|
||||||
|
|
||||||
else {
|
else {
|
||||||
LOG_CONTEXTUAL_ERR("not a valid action: %s", key);
|
LOG_CONTEXTUAL_ERR("not a valid action: %s", key);
|
||||||
return false;
|
return false;
|
||||||
|
|
@ -2468,6 +2478,20 @@ parse_section_tweak(struct context *ctx)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static bool
|
||||||
|
parse_section_touch(struct context *ctx) {
|
||||||
|
struct config *conf = ctx->conf;
|
||||||
|
const char *key = ctx->key;
|
||||||
|
|
||||||
|
if (strcmp(key, "long-press-delay") == 0)
|
||||||
|
return value_to_uint32(ctx, 10, &conf->touch.long_press_delay);
|
||||||
|
|
||||||
|
else {
|
||||||
|
LOG_CONTEXTUAL_ERR("not a valid option: %s", key);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static bool
|
static bool
|
||||||
parse_key_value(char *kv, const char **section, const char **key, const char **value)
|
parse_key_value(char *kv, const char **section, const char **key, const char **value)
|
||||||
{
|
{
|
||||||
|
|
@ -2547,6 +2571,7 @@ enum section {
|
||||||
SECTION_TEXT_BINDINGS,
|
SECTION_TEXT_BINDINGS,
|
||||||
SECTION_ENVIRONMENT,
|
SECTION_ENVIRONMENT,
|
||||||
SECTION_TWEAK,
|
SECTION_TWEAK,
|
||||||
|
SECTION_TOUCH,
|
||||||
SECTION_COUNT,
|
SECTION_COUNT,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -2572,6 +2597,7 @@ static const struct {
|
||||||
[SECTION_TEXT_BINDINGS] = {&parse_section_text_bindings, "text-bindings"},
|
[SECTION_TEXT_BINDINGS] = {&parse_section_text_bindings, "text-bindings"},
|
||||||
[SECTION_ENVIRONMENT] = {&parse_section_environment, "environment"},
|
[SECTION_ENVIRONMENT] = {&parse_section_environment, "environment"},
|
||||||
[SECTION_TWEAK] = {&parse_section_tweak, "tweak"},
|
[SECTION_TWEAK] = {&parse_section_tweak, "tweak"},
|
||||||
|
[SECTION_TOUCH] = {&parse_section_touch, "touch"},
|
||||||
};
|
};
|
||||||
|
|
||||||
static_assert(ALEN(section_info) == SECTION_COUNT, "section info array size mismatch");
|
static_assert(ALEN(section_info) == SECTION_COUNT, "section info array size mismatch");
|
||||||
|
|
@ -2784,7 +2810,8 @@ add_default_key_bindings(struct config *conf)
|
||||||
{BIND_ACTION_FONT_SIZE_RESET, m_ctrl, {{XKB_KEY_0}}},
|
{BIND_ACTION_FONT_SIZE_RESET, m_ctrl, {{XKB_KEY_0}}},
|
||||||
{BIND_ACTION_FONT_SIZE_RESET, m_ctrl, {{XKB_KEY_KP_0}}},
|
{BIND_ACTION_FONT_SIZE_RESET, m_ctrl, {{XKB_KEY_KP_0}}},
|
||||||
{BIND_ACTION_SPAWN_TERMINAL, m_ctrl_shift, {{XKB_KEY_n}}},
|
{BIND_ACTION_SPAWN_TERMINAL, m_ctrl_shift, {{XKB_KEY_n}}},
|
||||||
{BIND_ACTION_SHOW_URLS_LAUNCH, m_ctrl_shift, {{XKB_KEY_u}}},
|
{BIND_ACTION_SHOW_URLS_LAUNCH, m_ctrl_shift, {{XKB_KEY_o}}},
|
||||||
|
{BIND_ACTION_UNICODE_INPUT, m_ctrl_shift, {{XKB_KEY_u}}},
|
||||||
{BIND_ACTION_PROMPT_PREV, m_ctrl_shift, {{XKB_KEY_z}}},
|
{BIND_ACTION_PROMPT_PREV, m_ctrl_shift, {{XKB_KEY_z}}},
|
||||||
{BIND_ACTION_PROMPT_NEXT, m_ctrl_shift, {{XKB_KEY_x}}},
|
{BIND_ACTION_PROMPT_NEXT, m_ctrl_shift, {{XKB_KEY_x}}},
|
||||||
};
|
};
|
||||||
|
|
@ -2889,7 +2916,8 @@ config_font_list_clone(struct config_font_list *dst,
|
||||||
bool
|
bool
|
||||||
config_load(struct config *conf, const char *conf_path,
|
config_load(struct config *conf, const char *conf_path,
|
||||||
user_notifications_t *initial_user_notifications,
|
user_notifications_t *initial_user_notifications,
|
||||||
config_override_t *overrides, bool errors_are_fatal)
|
config_override_t *overrides, bool errors_are_fatal,
|
||||||
|
bool as_server)
|
||||||
{
|
{
|
||||||
bool ret = false;
|
bool ret = false;
|
||||||
enum fcft_capabilities fcft_caps = fcft_capabilities();
|
enum fcft_capabilities fcft_caps = fcft_capabilities();
|
||||||
|
|
@ -2898,7 +2926,7 @@ config_load(struct config *conf, const char *conf_path,
|
||||||
.term = xstrdup(FOOT_DEFAULT_TERM),
|
.term = xstrdup(FOOT_DEFAULT_TERM),
|
||||||
.shell = get_shell(),
|
.shell = get_shell(),
|
||||||
.title = xstrdup("foot"),
|
.title = xstrdup("foot"),
|
||||||
.app_id = xstrdup("foot"),
|
.app_id = (as_server ? xstrdup("footclient") : xstrdup("foot")),
|
||||||
.word_delimiters = xc32dup(U",│`|:\"'()[]{}<>"),
|
.word_delimiters = xc32dup(U",│`|:\"'()[]{}<>"),
|
||||||
.size = {
|
.size = {
|
||||||
.type = CONF_SIZE_PX,
|
.type = CONF_SIZE_PX,
|
||||||
|
|
@ -2922,7 +2950,7 @@ config_load(struct config *conf, const char *conf_path,
|
||||||
.use_custom_underline_offset = false,
|
.use_custom_underline_offset = false,
|
||||||
.box_drawings_uses_font_glyphs = false,
|
.box_drawings_uses_font_glyphs = false,
|
||||||
.underline_thickness = {.pt = 0., .px = -1},
|
.underline_thickness = {.pt = 0., .px = -1},
|
||||||
.dpi_aware = DPI_AWARE_AUTO, /* DPI-aware when scaling-factor == 1 */
|
.dpi_aware = false,
|
||||||
.bell = {
|
.bell = {
|
||||||
.urgent = false,
|
.urgent = false,
|
||||||
.notify = false,
|
.notify = false,
|
||||||
|
|
@ -2984,6 +3012,7 @@ config_load(struct config *conf, const char *conf_path,
|
||||||
.preferred = CONF_CSD_PREFER_SERVER,
|
.preferred = CONF_CSD_PREFER_SERVER,
|
||||||
.font = {0},
|
.font = {0},
|
||||||
.hide_when_maximized = false,
|
.hide_when_maximized = false,
|
||||||
|
.double_click_to_maximize = true,
|
||||||
.title_height = 26,
|
.title_height = 26,
|
||||||
.border_width = 5,
|
.border_width = 5,
|
||||||
.border_width_visible = 0,
|
.border_width_visible = 0,
|
||||||
|
|
@ -3018,10 +3047,17 @@ config_load(struct config *conf, const char *conf_path,
|
||||||
.sixel = true,
|
.sixel = true,
|
||||||
},
|
},
|
||||||
|
|
||||||
|
.touch = {
|
||||||
|
.long_press_delay = 400,
|
||||||
|
},
|
||||||
|
|
||||||
.env_vars = tll_init(),
|
.env_vars = tll_init(),
|
||||||
.utempter_path = (strlen(FOOT_DEFAULT_UTEMPTER_PATH) > 0
|
#if defined(UTMP_DEFAULT_HELPER_PATH)
|
||||||
? xstrdup(FOOT_DEFAULT_UTEMPTER_PATH)
|
.utmp_helper_path = ((strlen(UTMP_DEFAULT_HELPER_PATH) > 0 &&
|
||||||
|
access(UTMP_DEFAULT_HELPER_PATH, X_OK) == 0)
|
||||||
|
? xstrdup(UTMP_DEFAULT_HELPER_PATH)
|
||||||
: NULL),
|
: NULL),
|
||||||
|
#endif
|
||||||
.notifications = tll_init(),
|
.notifications = tll_init(),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -3310,8 +3346,8 @@ config_clone(const struct config *old)
|
||||||
tll_push_back(conf->env_vars, copy);
|
tll_push_back(conf->env_vars, copy);
|
||||||
}
|
}
|
||||||
|
|
||||||
conf->utempter_path =
|
conf->utmp_helper_path =
|
||||||
old->utempter_path != NULL ? xstrdup(old->utempter_path) : NULL;
|
old->utmp_helper_path != NULL ? xstrdup(old->utmp_helper_path) : NULL;
|
||||||
|
|
||||||
conf->notifications.length = 0;
|
conf->notifications.length = 0;
|
||||||
conf->notifications.head = conf->notifications.tail = 0;
|
conf->notifications.head = conf->notifications.tail = 0;
|
||||||
|
|
@ -3329,7 +3365,9 @@ UNITTEST
|
||||||
user_notifications_t nots = tll_init();
|
user_notifications_t nots = tll_init();
|
||||||
config_override_t overrides = tll_init();
|
config_override_t overrides = tll_init();
|
||||||
|
|
||||||
bool ret = config_load(&original, "/dev/null", ¬s, &overrides, false);
|
fcft_init(FCFT_LOG_COLORIZE_NEVER, false, FCFT_LOG_CLASS_NONE);
|
||||||
|
|
||||||
|
bool ret = config_load(&original, "/dev/null", ¬s, &overrides, false, false);
|
||||||
xassert(ret);
|
xassert(ret);
|
||||||
|
|
||||||
struct config *clone = config_clone(&original);
|
struct config *clone = config_clone(&original);
|
||||||
|
|
@ -3340,6 +3378,8 @@ UNITTEST
|
||||||
config_free(clone);
|
config_free(clone);
|
||||||
free(clone);
|
free(clone);
|
||||||
|
|
||||||
|
fcft_fini();
|
||||||
|
|
||||||
tll_free(overrides);
|
tll_free(overrides);
|
||||||
tll_free(nots);
|
tll_free(nots);
|
||||||
}
|
}
|
||||||
|
|
@ -3379,7 +3419,7 @@ config_free(struct config *conf)
|
||||||
tll_remove(conf->env_vars, it);
|
tll_remove(conf->env_vars, it);
|
||||||
}
|
}
|
||||||
|
|
||||||
free(conf->utempter_path);
|
free(conf->utmp_helper_path);
|
||||||
user_notifications_free(&conf->notifications);
|
user_notifications_free(&conf->notifications);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
12
config.h
12
config.h
|
|
@ -137,7 +137,7 @@ struct config {
|
||||||
|
|
||||||
enum { STARTUP_WINDOWED, STARTUP_MAXIMIZED, STARTUP_FULLSCREEN } startup_mode;
|
enum { STARTUP_WINDOWED, STARTUP_MAXIMIZED, STARTUP_FULLSCREEN } startup_mode;
|
||||||
|
|
||||||
enum {DPI_AWARE_AUTO, DPI_AWARE_YES, DPI_AWARE_NO} dpi_aware;
|
bool dpi_aware;
|
||||||
struct config_font_list fonts[4];
|
struct config_font_list fonts[4];
|
||||||
struct font_size_adjustment font_size_adjustment;
|
struct font_size_adjustment font_size_adjustment;
|
||||||
|
|
||||||
|
|
@ -285,6 +285,7 @@ struct config {
|
||||||
uint16_t button_width;
|
uint16_t button_width;
|
||||||
|
|
||||||
bool hide_when_maximized;
|
bool hide_when_maximized;
|
||||||
|
bool double_click_to_maximize;
|
||||||
|
|
||||||
struct {
|
struct {
|
||||||
bool title_set:1;
|
bool title_set:1;
|
||||||
|
|
@ -320,7 +321,7 @@ struct config {
|
||||||
|
|
||||||
env_var_list_t env_vars;
|
env_var_list_t env_vars;
|
||||||
|
|
||||||
char *utempter_path;
|
char *utmp_helper_path;
|
||||||
|
|
||||||
struct {
|
struct {
|
||||||
enum fcft_scaling_filter fcft_filter;
|
enum fcft_scaling_filter fcft_filter;
|
||||||
|
|
@ -347,6 +348,10 @@ struct config {
|
||||||
bool sixel;
|
bool sixel;
|
||||||
} tweak;
|
} tweak;
|
||||||
|
|
||||||
|
struct {
|
||||||
|
uint32_t long_press_delay;
|
||||||
|
} touch;
|
||||||
|
|
||||||
user_notifications_t notifications;
|
user_notifications_t notifications;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -355,7 +360,8 @@ bool config_override_apply(struct config *conf, config_override_t *overrides,
|
||||||
bool config_load(
|
bool config_load(
|
||||||
struct config *conf, const char *path,
|
struct config *conf, const char *path,
|
||||||
user_notifications_t *initial_user_notifications,
|
user_notifications_t *initial_user_notifications,
|
||||||
config_override_t *overrides, bool errors_are_fatal);
|
config_override_t *overrides, bool errors_are_fatal,
|
||||||
|
bool as_server);
|
||||||
void config_free(struct config *conf);
|
void config_free(struct config *conf);
|
||||||
struct config *config_clone(const struct config *old);
|
struct config *config_clone(const struct config *old);
|
||||||
|
|
||||||
|
|
|
||||||
22
csi.c
22
csi.c
|
|
@ -815,7 +815,7 @@ csi_dispatch(struct terminal *term, uint8_t final)
|
||||||
case 'G': {
|
case 'G': {
|
||||||
/* Cursor horizontal absolute */
|
/* Cursor horizontal absolute */
|
||||||
int col = min(vt_param_get(term, 0, 1), term->cols) - 1;
|
int col = min(vt_param_get(term, 0, 1), term->cols) - 1;
|
||||||
term_cursor_to(term, term->grid->cursor.point.row, col);
|
term_cursor_col(term, col);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1206,8 +1206,10 @@ csi_dispatch(struct terminal *term, uint8_t final)
|
||||||
|
|
||||||
if (width >= 0 && height >= 0) {
|
if (width >= 0 && height >= 0) {
|
||||||
char reply[64];
|
char reply[64];
|
||||||
size_t n = xsnprintf(reply, sizeof(reply), "\033[4;%d;%dt",
|
size_t n = xsnprintf(
|
||||||
height / term->scale, width / term->scale);
|
reply, sizeof(reply), "\033[4;%d;%dt",
|
||||||
|
(int)round(height / term->scale),
|
||||||
|
(int)(width / term->scale));
|
||||||
term_to_slave(term, reply, n);
|
term_to_slave(term, reply, n);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
@ -1229,9 +1231,10 @@ csi_dispatch(struct terminal *term, uint8_t final)
|
||||||
|
|
||||||
case 16: { /* report cell size in pixels */
|
case 16: { /* report cell size in pixels */
|
||||||
char reply[64];
|
char reply[64];
|
||||||
size_t n = xsnprintf(reply, sizeof(reply), "\033[6;%d;%dt",
|
size_t n = xsnprintf(
|
||||||
term->cell_height / term->scale,
|
reply, sizeof(reply), "\033[6;%d;%dt",
|
||||||
term->cell_width / term->scale);
|
(int)round(term->cell_height / term->scale),
|
||||||
|
(int)round(term->cell_width / term->scale));
|
||||||
term_to_slave(term, reply, n);
|
term_to_slave(term, reply, n);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
@ -1247,9 +1250,10 @@ csi_dispatch(struct terminal *term, uint8_t final)
|
||||||
case 19: { /* report screen size in chars */
|
case 19: { /* report screen size in chars */
|
||||||
tll_foreach(term->window->on_outputs, it) {
|
tll_foreach(term->window->on_outputs, it) {
|
||||||
char reply[64];
|
char reply[64];
|
||||||
size_t n = xsnprintf(reply, sizeof(reply), "\033[9;%d;%dt",
|
size_t n = xsnprintf(
|
||||||
it->item->dim.px_real.height / term->cell_height / term->scale,
|
reply, sizeof(reply), "\033[9;%d;%dt",
|
||||||
it->item->dim.px_real.width / term->cell_width / term->scale);
|
(int)round(it->item->dim.px_real.height / term->cell_height / term->scale),
|
||||||
|
(int)round(it->item->dim.px_real.width / term->cell_width / term->scale));
|
||||||
term_to_slave(term, reply, n);
|
term_to_slave(term, reply, n);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
115
cursor-shape.c
Normal file
115
cursor-shape.c
Normal file
|
|
@ -0,0 +1,115 @@
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
|
||||||
|
#define LOG_MODULE "cursor-shape"
|
||||||
|
#define LOG_ENABLE_DBG 0
|
||||||
|
#include "log.h"
|
||||||
|
|
||||||
|
#include "cursor-shape.h"
|
||||||
|
#include "debug.h"
|
||||||
|
#include "util.h"
|
||||||
|
|
||||||
|
const char *
|
||||||
|
cursor_shape_to_string(enum cursor_shape shape)
|
||||||
|
{
|
||||||
|
static const char *const table[CURSOR_SHAPE_COUNT] = {
|
||||||
|
[CURSOR_SHAPE_NONE] = NULL,
|
||||||
|
[CURSOR_SHAPE_HIDDEN] = "hidden",
|
||||||
|
[CURSOR_SHAPE_LEFT_PTR] = "left_ptr",
|
||||||
|
[CURSOR_SHAPE_TEXT] = "text",
|
||||||
|
[CURSOR_SHAPE_TEXT_FALLBACK] = "xterm",
|
||||||
|
[CURSOR_SHAPE_TOP_LEFT_CORNER] = "top_left_corner",
|
||||||
|
[CURSOR_SHAPE_TOP_RIGHT_CORNER] = "top_right_corner",
|
||||||
|
[CURSOR_SHAPE_BOTTOM_LEFT_CORNER] = "bottom_left_corner",
|
||||||
|
[CURSOR_SHAPE_BOTTOM_RIGHT_CORNER] = "bottom_right_corner",
|
||||||
|
[CURSOR_SHAPE_LEFT_SIDE] = "left_side",
|
||||||
|
[CURSOR_SHAPE_RIGHT_SIDE] = "right_side",
|
||||||
|
[CURSOR_SHAPE_TOP_SIDE] = "top_side",
|
||||||
|
[CURSOR_SHAPE_BOTTOM_SIDE] = "bottom_side",
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
xassert(shape <= ALEN(table));
|
||||||
|
xassert(table[shape] != NULL);
|
||||||
|
return table[shape];
|
||||||
|
}
|
||||||
|
|
||||||
|
#if defined(HAVE_CURSOR_SHAPE)
|
||||||
|
|
||||||
|
enum wp_cursor_shape_device_v1_shape
|
||||||
|
cursor_shape_to_server_shape(enum cursor_shape shape)
|
||||||
|
{
|
||||||
|
static const enum wp_cursor_shape_device_v1_shape table[CURSOR_SHAPE_COUNT] = {
|
||||||
|
[CURSOR_SHAPE_LEFT_PTR] = WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_DEFAULT,
|
||||||
|
[CURSOR_SHAPE_TEXT] = WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_TEXT,
|
||||||
|
[CURSOR_SHAPE_TEXT_FALLBACK] = WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_TEXT,
|
||||||
|
[CURSOR_SHAPE_TOP_LEFT_CORNER] = WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_NW_RESIZE,
|
||||||
|
[CURSOR_SHAPE_TOP_RIGHT_CORNER] = WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_NE_RESIZE,
|
||||||
|
[CURSOR_SHAPE_BOTTOM_LEFT_CORNER] = WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_SW_RESIZE,
|
||||||
|
[CURSOR_SHAPE_BOTTOM_RIGHT_CORNER] = WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_SE_RESIZE,
|
||||||
|
[CURSOR_SHAPE_LEFT_SIDE] = WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_W_RESIZE,
|
||||||
|
[CURSOR_SHAPE_RIGHT_SIDE] = WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_E_RESIZE,
|
||||||
|
[CURSOR_SHAPE_TOP_SIDE] = WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_N_RESIZE,
|
||||||
|
[CURSOR_SHAPE_BOTTOM_SIDE] = WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_S_RESIZE,
|
||||||
|
};
|
||||||
|
|
||||||
|
xassert(shape <= ALEN(table));
|
||||||
|
xassert(table[shape] != 0);
|
||||||
|
return table[shape];
|
||||||
|
}
|
||||||
|
|
||||||
|
enum wp_cursor_shape_device_v1_shape
|
||||||
|
cursor_string_to_server_shape(const char *xcursor)
|
||||||
|
{
|
||||||
|
if (xcursor == NULL)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
static const char *const table[][2] = {
|
||||||
|
[WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_DEFAULT] = {"default", "left_ptr"},
|
||||||
|
[WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_CONTEXT_MENU] = {"context-menu"},
|
||||||
|
[WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_HELP] = {"help", "question_arrow"},
|
||||||
|
[WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_POINTER] = {"pointer", "hand"},
|
||||||
|
[WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_PROGRESS] = {"progress", "left_ptr_watch"},
|
||||||
|
[WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_WAIT] = {"wait", "watch"},
|
||||||
|
[WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_CELL] = {"cell"},
|
||||||
|
[WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_CROSSHAIR] = {"crosshair", "cross"},
|
||||||
|
[WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_TEXT] = {"text", "xterm"},
|
||||||
|
[WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_VERTICAL_TEXT] = {"vertical-text"},
|
||||||
|
[WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_ALIAS] = {"alias", "dnd-link"},
|
||||||
|
[WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_COPY] = {"copy", "dnd-copy"},
|
||||||
|
[WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_MOVE] = {"move"}, /* dnd-move? */
|
||||||
|
[WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_NO_DROP] = {"no-drop", "dnd-no-drop"},
|
||||||
|
[WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_NOT_ALLOWED] = {"not-allowed", "crossed_circle"},
|
||||||
|
[WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_GRAB] = {"grab", "hand1"},
|
||||||
|
[WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_GRABBING] = {"grabbing"},
|
||||||
|
[WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_E_RESIZE] = {"e-resize", "right_side"},
|
||||||
|
[WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_N_RESIZE] = {"n-resize", "top_side"},
|
||||||
|
[WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_NE_RESIZE] = {"ne-resize", "top_right_corner"},
|
||||||
|
[WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_NW_RESIZE] = {"nw-resize", "top_left_corner"},
|
||||||
|
[WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_S_RESIZE] = {"s-resize", "bottom_side"},
|
||||||
|
[WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_SE_RESIZE] = {"se-resize", "bottom_right_corner"},
|
||||||
|
[WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_SW_RESIZE] = {"sw-resize", "bottom_left_corner"},
|
||||||
|
[WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_W_RESIZE] = {"w-resize", "left_side"},
|
||||||
|
[WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_EW_RESIZE] = {"ew-resize", "sb_h_double_arrow"},
|
||||||
|
[WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_NS_RESIZE] = {"ns-resize", "sb_v_double_arrow"},
|
||||||
|
[WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_NESW_RESIZE] = {"nesw-resize", "fd_double_arrow"},
|
||||||
|
[WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_NWSE_RESIZE] = {"nwse-resize", "bd_double_arrow"},
|
||||||
|
[WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_COL_RESIZE] = {"col-resize", "sb_h_double_arrow"},
|
||||||
|
[WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_ROW_RESIZE] = {"row-resize", "sb_v_double_arrow"},
|
||||||
|
[WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_ALL_SCROLL] = {"all-scroll", "fleur"},
|
||||||
|
[WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_ZOOM_IN] = {"zoom-in"},
|
||||||
|
[WP_CURSOR_SHAPE_DEVICE_V1_SHAPE_ZOOM_OUT] = {"zoom-out"},
|
||||||
|
};
|
||||||
|
|
||||||
|
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) {
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif /* HAVE_CURSOR_SHAPE */
|
||||||
34
cursor-shape.h
Normal file
34
cursor-shape.h
Normal file
|
|
@ -0,0 +1,34 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#if defined(HAVE_CURSOR_SHAPE)
|
||||||
|
#include <cursor-shape-v1.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
enum cursor_shape {
|
||||||
|
CURSOR_SHAPE_NONE,
|
||||||
|
CURSOR_SHAPE_CUSTOM,
|
||||||
|
CURSOR_SHAPE_HIDDEN,
|
||||||
|
|
||||||
|
CURSOR_SHAPE_LEFT_PTR,
|
||||||
|
CURSOR_SHAPE_TEXT,
|
||||||
|
CURSOR_SHAPE_TEXT_FALLBACK,
|
||||||
|
CURSOR_SHAPE_TOP_LEFT_CORNER,
|
||||||
|
CURSOR_SHAPE_TOP_RIGHT_CORNER,
|
||||||
|
CURSOR_SHAPE_BOTTOM_LEFT_CORNER,
|
||||||
|
CURSOR_SHAPE_BOTTOM_RIGHT_CORNER,
|
||||||
|
CURSOR_SHAPE_LEFT_SIDE,
|
||||||
|
CURSOR_SHAPE_RIGHT_SIDE,
|
||||||
|
CURSOR_SHAPE_TOP_SIDE,
|
||||||
|
CURSOR_SHAPE_BOTTOM_SIDE,
|
||||||
|
|
||||||
|
CURSOR_SHAPE_COUNT,
|
||||||
|
};
|
||||||
|
|
||||||
|
const char *cursor_shape_to_string(enum cursor_shape shape);
|
||||||
|
|
||||||
|
#if defined(HAVE_CURSOR_SHAPE)
|
||||||
|
enum wp_cursor_shape_device_v1_shape cursor_shape_to_server_shape(
|
||||||
|
enum cursor_shape shape);
|
||||||
|
enum wp_cursor_shape_device_v1_shape cursor_string_to_server_shape(
|
||||||
|
const char *xcursor);
|
||||||
|
#endif
|
||||||
3
dcs.c
3
dcs.c
|
|
@ -427,8 +427,7 @@ dcs_hook(struct terminal *term, uint8_t final)
|
||||||
int p2 = vt_param_get(term, 1,0);
|
int p2 = vt_param_get(term, 1,0);
|
||||||
int p3 = vt_param_get(term, 2, 0);
|
int p3 = vt_param_get(term, 2, 0);
|
||||||
|
|
||||||
sixel_init(term, p1, p2, p3);
|
term->vt.dcs.put_handler = sixel_init(term, p1, p2, p3);
|
||||||
term->vt.dcs.put_handler = &sixel_put;
|
|
||||||
term->vt.dcs.unhook_handler = &sixel_unhook;
|
term->vt.dcs.unhook_handler = &sixel_unhook;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -65,7 +65,7 @@ the foot command line
|
||||||
|
|
||||||
*-a*,*--app-id*=_ID_
|
*-a*,*--app-id*=_ID_
|
||||||
Value to set the *app-id* property on the Wayland window
|
Value to set the *app-id* property on the Wayland window
|
||||||
to. Default: _foot_.
|
to. Default: _foot_ (normal mode), or _footclient_ (server mode).
|
||||||
|
|
||||||
*-m*,*--maximized*
|
*-m*,*--maximized*
|
||||||
Start in maximized mode. If both *--maximized* and *--fullscreen*
|
Start in maximized mode. If both *--maximized* and *--fullscreen*
|
||||||
|
|
@ -202,9 +202,12 @@ default) available; see *foot.ini*(5).
|
||||||
_OSC 7_ escape sequence, the new terminal will start in the
|
_OSC 7_ escape sequence, the new terminal will start in the
|
||||||
current working directory.
|
current working directory.
|
||||||
|
|
||||||
*ctrl*+*shift*+*u*
|
*ctrl*+*shift*+*o*
|
||||||
Activate URL mode, allowing you to "launch" URLs.
|
Activate URL mode, allowing you to "launch" URLs.
|
||||||
|
|
||||||
|
*ctrl*+*shift*+*u*
|
||||||
|
Activate Unicode input.
|
||||||
|
|
||||||
*ctrl*+*shift*+*z*
|
*ctrl*+*shift*+*z*
|
||||||
Jump to the previous, currently not visible, prompt. Requires
|
Jump to the previous, currently not visible, prompt. Requires
|
||||||
shell integration.
|
shell integration.
|
||||||
|
|
@ -283,6 +286,18 @@ default) available; see *foot.ini*(5).
|
||||||
*wheel*
|
*wheel*
|
||||||
Scroll up/down in history
|
Scroll up/down in history
|
||||||
|
|
||||||
|
## TOUCHSCREEN
|
||||||
|
|
||||||
|
*tap*
|
||||||
|
Emulates mouse left button click.
|
||||||
|
|
||||||
|
*drag*
|
||||||
|
Scrolls up/down in history.
|
||||||
|
|
||||||
|
Holding for a while before dragging (time delay can be configured)
|
||||||
|
emulates mouse dragging with left button held.
|
||||||
|
|
||||||
|
|
||||||
# FONT FORMAT
|
# FONT FORMAT
|
||||||
|
|
||||||
The font is specified in FontConfig syntax. That is, a colon-separated
|
The font is specified in FontConfig syntax. That is, a colon-separated
|
||||||
|
|
@ -298,7 +313,7 @@ Foot supports URL detection. But, unlike many other terminal
|
||||||
emulators, where URLs are highlighted when they are hovered and opened
|
emulators, where URLs are highlighted when they are hovered and opened
|
||||||
by clicking on them, foot uses a keyboard driven approach.
|
by clicking on them, foot uses a keyboard driven approach.
|
||||||
|
|
||||||
Pressing *ctrl*+*shift*+*u* enters _“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
|
visible URLs are underlined, and is associated with a
|
||||||
_“jump-label”_. The jump-label indicates the _key sequence_
|
_“jump-label”_. The jump-label indicates the _key sequence_
|
||||||
(e.g. *”AF”*) to use to activate the URL.
|
(e.g. *”AF”*) to use to activate the URL.
|
||||||
|
|
@ -546,17 +561,6 @@ In all other cases, the exit code is that of the client application
|
||||||
This variable is set to *truecolor*, to indicate to client
|
This variable is set to *truecolor*, to indicate to client
|
||||||
applications that 24-bit RGB colors are supported.
|
applications that 24-bit RGB colors are supported.
|
||||||
|
|
||||||
*TERM_PROGRAM*
|
|
||||||
Always set to *foot*. This can be used by client applications to
|
|
||||||
check which terminal is in use, but with the caveat that it may
|
|
||||||
have been inherited from a parent process in other terminals that
|
|
||||||
aren't known to set the variable.
|
|
||||||
|
|
||||||
*TERM_PROGRAM_VERSION*
|
|
||||||
Set to the foot version string, in the format _major_*.*_minor_*.*_patch_
|
|
||||||
or _major_*.*_minor_*.*_patch_*-*_revision_*-\g*_commit_ for inter-release
|
|
||||||
builds. The same caveat as for *TERM_PROGRAM* applies.
|
|
||||||
|
|
||||||
In addition to the variables listed above, custom environment
|
In addition to the variables listed above, custom environment
|
||||||
variables may be defined in *foot.ini*(5).
|
variables may be defined in *foot.ini*(5).
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -87,7 +87,7 @@ empty string to be set, but it must be quoted: *KEY=""*)
|
||||||
Examples:
|
Examples:
|
||||||
```
|
```
|
||||||
font-size-adjustment=0.5 # Adjust by 0.5 points
|
font-size-adjustment=0.5 # Adjust by 0.5 points
|
||||||
font-size-adjustment=10xp # Adjust by 10 pixels
|
font-size-adjustment=10px # Adjust by 10 pixels
|
||||||
font-size-adjustment=7.5% # Adjust by 7.5 percent
|
font-size-adjustment=7.5% # Adjust by 7.5 percent
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
@ -185,7 +185,7 @@ empty string to be set, but it must be quoted: *KEY=""*)
|
||||||
Default: _no_.
|
Default: _no_.
|
||||||
|
|
||||||
*dpi-aware*
|
*dpi-aware*
|
||||||
*auto*, *yes*, or *no*.
|
Boolean.
|
||||||
|
|
||||||
When set to *yes*, fonts are sized using the monitor's DPI, making
|
When set to *yes*, fonts are sized using the monitor's DPI, making
|
||||||
a font of a given size have the same physical size, regardless of
|
a font of a given size have the same physical size, regardless of
|
||||||
|
|
@ -199,12 +199,6 @@ empty string to be set, but it must be quoted: *KEY=""*)
|
||||||
instead sized using the monitor's scaling factor; doubling the
|
instead sized using the monitor's scaling factor; doubling the
|
||||||
scaling factor *does* double the font size.
|
scaling factor *does* double the font size.
|
||||||
|
|
||||||
Finally, if set to *auto*, fonts will be sized using the monitor's
|
|
||||||
DPI if _all_ monitors have a scaling factor of 1. If at least one
|
|
||||||
monitor as a scaling factor larger than 1 (regardless of whether
|
|
||||||
the foot window is mapped on that monitor or not), fonts will be
|
|
||||||
scaled using the scaling factor.
|
|
||||||
|
|
||||||
Note that this option typically does not work with bitmap fonts,
|
Note that this option typically does not work with bitmap fonts,
|
||||||
which only contains a pre-defined set of sizes, and cannot be
|
which only contains a pre-defined set of sizes, and cannot be
|
||||||
dynamically scaled. Whichever size (of the available ones) that
|
dynamically scaled. Whichever size (of the available ones) that
|
||||||
|
|
@ -217,7 +211,7 @@ empty string to be set, but it must be quoted: *KEY=""*)
|
||||||
to size the font (*dpi-aware=no*), the font's pixel size will be
|
to size the font (*dpi-aware=no*), the font's pixel size will be
|
||||||
multiplied with the scaling factor.
|
multiplied with the scaling factor.
|
||||||
|
|
||||||
Default: _auto_
|
Default: _no_
|
||||||
|
|
||||||
*pad*
|
*pad*
|
||||||
Padding between border and glyphs, in pixels (subject to output
|
Padding between border and glyphs, in pixels (subject to output
|
||||||
|
|
@ -289,7 +283,8 @@ empty string to be set, but it must be quoted: *KEY=""*)
|
||||||
*app-id*
|
*app-id*
|
||||||
Value to set the *app-id* property on the Wayland window to. The
|
Value to set the *app-id* property on the Wayland window to. The
|
||||||
compositor can use this value to e.g. group multiple windows, or
|
compositor can use this value to e.g. group multiple windows, or
|
||||||
apply window management rules. Default: _foot_.
|
apply window management rules. Default: _foot_ (normal mode), or
|
||||||
|
_footclient_ (server mode).
|
||||||
|
|
||||||
*bold-text-in-bright*
|
*bold-text-in-bright*
|
||||||
Semi-boolean. When enabled, bold text is rendered in a brighter
|
Semi-boolean. When enabled, bold text is rendered in a brighter
|
||||||
|
|
@ -314,7 +309,8 @@ empty string to be set, but it must be quoted: *KEY=""*)
|
||||||
and _body_ (message content).
|
and _body_ (message content).
|
||||||
|
|
||||||
_${app-id}_ is replaced with the value of the command line option
|
_${app-id}_ is replaced with the value of the command line option
|
||||||
_--app-id_, and defaults to *foot*.
|
_--app-id_, and defaults to *foot* (normal mode), or
|
||||||
|
*footclient* (server mode).
|
||||||
|
|
||||||
_${window-title}_ is replaced with the current window title.
|
_${window-title}_ is replaced with the current window title.
|
||||||
|
|
||||||
|
|
@ -343,9 +339,24 @@ empty string to be set, but it must be quoted: *KEY=""*)
|
||||||
(including SMT). Note that this is not always the best value. In
|
(including SMT). Note that this is not always the best value. In
|
||||||
some cases, the number of physical _cores_ is better.
|
some cases, the number of physical _cores_ is better.
|
||||||
|
|
||||||
*utempter*
|
*utmp-helper*
|
||||||
Path to utempter helper binary. Set to *none* to disable utmp
|
Path to utmp logging helper binary.
|
||||||
records. Default: _@utempter@_.
|
|
||||||
|
When starting foot, an utmp record is created by launching the
|
||||||
|
helper binary with the following arguments:
|
||||||
|
|
||||||
|
```
|
||||||
|
@utmp_add_args@
|
||||||
|
```
|
||||||
|
|
||||||
|
When foot is closed, the utmp record is removed by launching the
|
||||||
|
helper binary with the following arguments:
|
||||||
|
|
||||||
|
```
|
||||||
|
@utmp_del_args@
|
||||||
|
```
|
||||||
|
|
||||||
|
Set to *none* to disable utmp records. Default: _@utmp_helper_path@_.
|
||||||
|
|
||||||
# SECTION: environment
|
# SECTION: environment
|
||||||
|
|
||||||
|
|
@ -524,6 +535,14 @@ applications can change these at runtime.
|
||||||
|
|
||||||
Default: _yes_.
|
Default: _yes_.
|
||||||
|
|
||||||
|
# SECTION: touch
|
||||||
|
|
||||||
|
*long-press-delay*
|
||||||
|
Number of milliseconds to distinguish between a short press and
|
||||||
|
a long press on the touchscreen.
|
||||||
|
|
||||||
|
Default: _400_.
|
||||||
|
|
||||||
# SECTION: colors
|
# SECTION: colors
|
||||||
|
|
||||||
This section controls the 16 ANSI colors, the default foreground and
|
This section controls the 16 ANSI colors, the default foreground and
|
||||||
|
|
@ -544,15 +563,15 @@ can configure the background transparency with the _alpha_ option.
|
||||||
|
|
||||||
*regular0*, *regular1* *..* *regular7*
|
*regular0*, *regular1* *..* *regular7*
|
||||||
The eight basic ANSI colors (Black, Red, Green, Yellow, Blue,
|
The eight basic ANSI colors (Black, Red, Green, Yellow, Blue,
|
||||||
Magenta, Cyan, White). Default: _073642_, _dc322f_, _859900_,
|
Magenta, Cyan, White). Default: _242424_, _f62b5a_, _47b413_,
|
||||||
_b58900_, _268bd2_, _d33682_, _2aa198_ and _eee8d5_ (a variant of
|
_e3c401_, _24acd4_, _f2affd_, _13c299_, _e6e6e6_ (starlight
|
||||||
the _solarized dark_ theme).
|
theme, V4).
|
||||||
|
|
||||||
*bright0*, *bright1* *..* *bright7*
|
*bright0*, *bright1* *..* *bright7*
|
||||||
The eight bright ANSI colors (Black, Red, Green, Yellow, Blue,
|
The eight bright ANSI colors (Black, Red, Green, Yellow, Blue,
|
||||||
Magenta, Cyan, White). Default: _08404f_, _e35f5c_, _9fb700_,
|
Magenta, Cyan, White). Default: _616161_, _ff4d51_, _35d450_,
|
||||||
_d9a400_, _4ba1de_, _dc619d_, _32c1b6_ and _ffffff_ (a variant of
|
_e9e836_, _5dc5f8_, _feabf2_, _24dfc4_, _ffffff_ (starlight
|
||||||
the _solarized dark_ theme).
|
theme, V4).
|
||||||
|
|
||||||
*dim0*, *dim1* *..* *dim7*
|
*dim0*, *dim1* *..* *dim7*
|
||||||
Custom colors to use with dimmed colors. Dimmed colors do not have
|
Custom colors to use with dimmed colors. Dimmed colors do not have
|
||||||
|
|
@ -673,6 +692,10 @@ Examples:
|
||||||
is maximized. The completely disable the titlebar, set *size* to 0
|
is maximized. The completely disable the titlebar, set *size* to 0
|
||||||
instead. Default: _no_.
|
instead. Default: _no_.
|
||||||
|
|
||||||
|
*double-click-to-maximize*
|
||||||
|
Boolean. When enabled, double-clicking the CSD titlebar will
|
||||||
|
(un)maximize the window. Default: _yes_.
|
||||||
|
|
||||||
*border-width*
|
*border-width*
|
||||||
Width of the border, in pixels (subject to output scaling). Note
|
Width of the border, in pixels (subject to output scaling). Note
|
||||||
that the border encompasses the entire window, including the title
|
that the border encompasses the entire window, including the title
|
||||||
|
|
@ -815,7 +838,7 @@ e.g. *search-start=none*.
|
||||||
*show-urls-launch*
|
*show-urls-launch*
|
||||||
Enter URL mode, where all currently visible URLs are tagged with a
|
Enter URL mode, where all currently visible URLs are tagged with a
|
||||||
jump label with a key sequence that will open the URL (and exit
|
jump label with a key sequence that will open the URL (and exit
|
||||||
URL mode). Default: _Control+Shift+u_.
|
URL mode). Default: _Control+Shift+o_.
|
||||||
|
|
||||||
*show-urls-persistent*
|
*show-urls-persistent*
|
||||||
Similar to *show-urls-launch*, but does not automatically exit URL
|
Similar to *show-urls-launch*, but does not automatically exit URL
|
||||||
|
|
@ -858,7 +881,7 @@ e.g. *search-start=none*.
|
||||||
fallback. The preferred way of entering Unicode characters, emojis
|
fallback. The preferred way of entering Unicode characters, emojis
|
||||||
etc is by using an IME.
|
etc is by using an IME.
|
||||||
|
|
||||||
Default: _none_.
|
Default: _Control+Shift+u_.
|
||||||
|
|
||||||
# SECTION: search-bindings
|
# SECTION: search-bindings
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -31,7 +31,7 @@ terminal has terminated.
|
||||||
|
|
||||||
*-a*,*--app-id*=_ID_
|
*-a*,*--app-id*=_ID_
|
||||||
Value to set the *app-id* property on the Wayland window
|
Value to set the *app-id* property on the Wayland window
|
||||||
to. Default: _foot_.
|
to. Default: _foot_ (normal mode), or _footclient_ (server mode).
|
||||||
|
|
||||||
*-w*,*--window-size-pixels*=_WIDTHxHEIGHT_
|
*-w*,*--window-size-pixels*=_WIDTHxHEIGHT_
|
||||||
Set initial window width and height, in pixels. Default: _700x500_.
|
Set initial window width and height, in pixels. Default: _700x500_.
|
||||||
|
|
@ -163,17 +163,6 @@ fallback to the less specific path, with the following priority:
|
||||||
This variable is set to *truecolor*, to indicate to client
|
This variable is set to *truecolor*, to indicate to client
|
||||||
applications that 24-bit RGB colors are supported.
|
applications that 24-bit RGB colors are supported.
|
||||||
|
|
||||||
*TERM_PROGRAM*
|
|
||||||
Always set to *foot*. This can be used by client applications to
|
|
||||||
check which terminal is in use, but with the caveat that it may
|
|
||||||
have been inherited from a parent process in other terminals that
|
|
||||||
aren't known to set the variable.
|
|
||||||
|
|
||||||
*TERM_PROGRAM_VERSION*
|
|
||||||
Set to the foot version string, in the format _major_*.*_minor_*.*_patch_
|
|
||||||
or _major_*.*_minor_*.*_patch_*-*_revision_*-\g*_commit_ for inter-release
|
|
||||||
builds. The same caveat as for *TERM_PROGRAM* applies.
|
|
||||||
|
|
||||||
In addition to the variables listed above, custom environment
|
In addition to the variables listed above, custom environment
|
||||||
variables may be defined in *foot.ini*(5).
|
variables may be defined in *foot.ini*(5).
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,17 +1,25 @@
|
||||||
sh = find_program('sh', native: true)
|
|
||||||
|
|
||||||
scdoc_prog = find_program(scdoc.get_variable('scdoc'), native: true)
|
scdoc_prog = find_program(scdoc.get_variable('scdoc'), native: true)
|
||||||
|
|
||||||
if utempter_path == ''
|
if utmp_backend != 'none'
|
||||||
default_utempter_value = 'not set'
|
utmp_add_args = '@0@ $WAYLAND_DISPLAY'.format(utmp_add)
|
||||||
|
utmp_del_args = (utmp_del_have_argument
|
||||||
|
? '@0@ $WAYLAND_DISPLAY'.format(utmp_del)
|
||||||
|
: '@0@'.format(utmp_del))
|
||||||
|
utmp_path = utmp_default_helper_path
|
||||||
else
|
else
|
||||||
default_utempter_value = utempter_path
|
utmp_add_args = '<no utmp support in foot>'
|
||||||
|
utmp_del_args = '<no utmp support in foot>'
|
||||||
|
utmp_path = 'none'
|
||||||
endif
|
endif
|
||||||
|
|
||||||
|
|
||||||
conf_data = configuration_data(
|
conf_data = configuration_data(
|
||||||
{
|
{
|
||||||
'default_terminfo': get_option('default-terminfo'),
|
'default_terminfo': get_option('default-terminfo'),
|
||||||
'utempter': default_utempter_value,
|
'utmp_backend': utmp_backend,
|
||||||
|
'utmp_add_args': utmp_add_args,
|
||||||
|
'utmp_del_args': utmp_del_args,
|
||||||
|
'utmp_helper_path': utmp_path,
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
@ -33,8 +41,9 @@ foreach man_src : [{'name': 'foot', 'section' : 1},
|
||||||
out,
|
out,
|
||||||
output: out,
|
output: out,
|
||||||
input: preprocessed,
|
input: preprocessed,
|
||||||
command: [sh, '-c', '@0@ < @INPUT@'.format(scdoc_prog.full_path())],
|
command: scdoc_prog.full_path(),
|
||||||
capture: true,
|
capture: true,
|
||||||
|
feed: true,
|
||||||
install: true,
|
install: true,
|
||||||
install_dir: join_paths(get_option('mandir'), 'man@0@'.format(section)))
|
install_dir: join_paths(get_option('mandir'), 'man@0@'.format(section)))
|
||||||
endforeach
|
endforeach
|
||||||
|
|
|
||||||
|
|
@ -37,3 +37,21 @@ static inline bool feature_graphemes(void)
|
||||||
return false;
|
return false;
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static inline bool feature_fractional_scaling(void)
|
||||||
|
{
|
||||||
|
#if defined(HAVE_FRACTIONAL_SCALE)
|
||||||
|
return true;
|
||||||
|
#else
|
||||||
|
return false;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
static inline bool feature_cursor_shape(void)
|
||||||
|
{
|
||||||
|
#if defined(HAVE_CURSOR_SHAPE)
|
||||||
|
return true;
|
||||||
|
#else
|
||||||
|
return false;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -41,7 +41,8 @@
|
||||||
Se=\E[ q,
|
Se=\E[ q,
|
||||||
Ss=\E[%p1%d q,
|
Ss=\E[%p1%d q,
|
||||||
Sync=\E[?2026%?%p1%{1}%-%tl%eh,
|
Sync=\E[?2026%?%p1%{1}%-%tl%eh,
|
||||||
XM=\E[?1006;1000%?%p1%{1}%=%th%el%;,
|
TS=\E]2;,
|
||||||
|
XM=\E[?1006;1004;1000%?%p1%{1}%=%th%el%;,
|
||||||
XR=\E[>0q,
|
XR=\E[>0q,
|
||||||
acsc=``aaffggiijjkkllmmnnooppqqrrssttuuvvwwxxyyzz{{||}}~~,
|
acsc=``aaffggiijjkkllmmnnooppqqrrssttuuvvwwxxyyzz{{||}}~~,
|
||||||
bel=^G,
|
bel=^G,
|
||||||
|
|
|
||||||
51
foot.ini
51
foot.ini
|
|
@ -4,7 +4,7 @@
|
||||||
# term=foot (or xterm-256color if built with -Dterminfo=disabled)
|
# term=foot (or xterm-256color if built with -Dterminfo=disabled)
|
||||||
# login-shell=no
|
# login-shell=no
|
||||||
|
|
||||||
# app-id=foot
|
# app-id=foot # globally set wayland app-id. Default values are "foot" and "footclient" for desktop and server mode
|
||||||
# title=foot
|
# title=foot
|
||||||
# locked-title=no
|
# locked-title=no
|
||||||
|
|
||||||
|
|
@ -20,7 +20,7 @@
|
||||||
# underline-offset=<font metrics>
|
# underline-offset=<font metrics>
|
||||||
# underline-thickness=<font underline thickness>
|
# underline-thickness=<font underline thickness>
|
||||||
# box-drawings-uses-font-glyphs=no
|
# box-drawings-uses-font-glyphs=no
|
||||||
# dpi-aware=auto
|
# dpi-aware=no
|
||||||
|
|
||||||
# initial-window-size-pixels=700x500 # Or,
|
# initial-window-size-pixels=700x500 # Or,
|
||||||
# initial-window-size-chars=<COLSxROWS>
|
# initial-window-size-chars=<COLSxROWS>
|
||||||
|
|
@ -34,7 +34,8 @@
|
||||||
# word-delimiters=,│`|:"'()[]{}<>
|
# word-delimiters=,│`|:"'()[]{}<>
|
||||||
# selection-target=primary
|
# selection-target=primary
|
||||||
# workers=<number of logical CPUs>
|
# workers=<number of logical CPUs>
|
||||||
# utempter=/usr/lib/utempter/utempter
|
# utmp-helper=/usr/lib/utempter/utempter # When utmp backend is ‘libutempter’ (Linux)
|
||||||
|
# utmp-helper=/usr/libexec/ulog-helper # When utmp backend is ‘ulog’ (FreeBSD)
|
||||||
|
|
||||||
[environment]
|
[environment]
|
||||||
# name=value
|
# name=value
|
||||||
|
|
@ -69,29 +70,32 @@
|
||||||
# hide-when-typing=no
|
# hide-when-typing=no
|
||||||
# alternate-scroll-mode=yes
|
# alternate-scroll-mode=yes
|
||||||
|
|
||||||
|
[touch]
|
||||||
|
# long-press-delay=400
|
||||||
|
|
||||||
[colors]
|
[colors]
|
||||||
# alpha=1.0
|
# alpha=1.0
|
||||||
# background=002b36
|
# background=242424
|
||||||
# foreground=839496
|
# foreground=ffffff
|
||||||
|
|
||||||
## Normal/regular colors (color palette 0-7)
|
## Normal/regular colors (color palette 0-7)
|
||||||
# regular0=073642 # black
|
# regular0=242424 # black
|
||||||
# regular1=dc322f # red
|
# regular1=f62b5a # red
|
||||||
# regular2=859900 # green
|
# regular2=47b413 # green
|
||||||
# regular3=b58900 # yellow
|
# regular3=e3c401 # yellow
|
||||||
# regular4=268bd2 # blue
|
# regular4=24acd4 # blue
|
||||||
# regular5=d33682 # magenta
|
# regular5=f2affd # magenta
|
||||||
# regular6=2aa198 # cyan
|
# regular6=13c299 # cyan
|
||||||
# regular7=eee8d5 # white
|
# regular7=e6e6e6 # white
|
||||||
|
|
||||||
## Bright colors (color palette 8-15)
|
## Bright colors (color palette 8-15)
|
||||||
# bright0=08404f # bright black
|
# bright0=616161 # bright black
|
||||||
# bright1=e35f5c # bright red
|
# bright1=ff4d51 # bright red
|
||||||
# bright2=9fb700 # bright green
|
# bright2=35d450 # bright green
|
||||||
# bright3=d9a400 # bright yellow
|
# bright3=e9e836 # bright yellow
|
||||||
# bright4=4ba1de # bright blue
|
# bright4=5dc5f8 # bright blue
|
||||||
# bright5=dc619d # bright magenta
|
# bright5=feabf2 # bright magenta
|
||||||
# bright6=32c1b6 # bright cyan
|
# bright6=24dfc4 # bright cyan
|
||||||
# bright7=ffffff # bright white
|
# bright7=ffffff # bright white
|
||||||
|
|
||||||
## dimmed colors (see foot.ini(5) man page)
|
## dimmed colors (see foot.ini(5) man page)
|
||||||
|
|
@ -118,7 +122,8 @@
|
||||||
# size=26
|
# size=26
|
||||||
# font=<primary font>
|
# font=<primary font>
|
||||||
# color=<foreground color>
|
# color=<foreground color>
|
||||||
# hide-when-typing=no
|
# hide-when-maximized=no
|
||||||
|
# double-click-to-maximize=yes
|
||||||
# border-width=0
|
# border-width=0
|
||||||
# border-color=<csd.color>
|
# border-color=<csd.color>
|
||||||
# button-width=26
|
# button-width=26
|
||||||
|
|
@ -148,12 +153,12 @@
|
||||||
# pipe-visible=[sh -c "xurls | fuzzel | xargs -r firefox"] none
|
# pipe-visible=[sh -c "xurls | fuzzel | xargs -r firefox"] none
|
||||||
# pipe-scrollback=[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-selected=[xargs -r firefox] none
|
||||||
# show-urls-launch=Control+Shift+u
|
# show-urls-launch=Control+Shift+o
|
||||||
# show-urls-copy=none
|
# show-urls-copy=none
|
||||||
# show-urls-persistent=none
|
# show-urls-persistent=none
|
||||||
# prompt-prev=Control+Shift+z
|
# prompt-prev=Control+Shift+z
|
||||||
# prompt-next=Control+Shift+x
|
# prompt-next=Control+Shift+x
|
||||||
# unicode-input=none
|
# unicode-input=Control+Shift+u
|
||||||
# noop=none
|
# noop=none
|
||||||
|
|
||||||
[search-bindings]
|
[search-bindings]
|
||||||
|
|
|
||||||
|
|
@ -41,7 +41,6 @@ patch=$(echo "${new_version}" | sed -r 's/([0-9]+)\.([0-9]+)\.([0-9]+).*/\3/')
|
||||||
extra=$(echo "${new_version}" | sed -r 's/([0-9]+)\.([0-9]+)\.([0-9]+)(-([0-9]+-g[a-z0-9]+) .*)?.*/\5/')
|
extra=$(echo "${new_version}" | sed -r 's/([0-9]+)\.([0-9]+)\.([0-9]+)(-([0-9]+-g[a-z0-9]+) .*)?.*/\5/')
|
||||||
|
|
||||||
new_version="#define FOOT_VERSION \"${new_version}\"
|
new_version="#define FOOT_VERSION \"${new_version}\"
|
||||||
#define FOOT_VERSION_SHORT \"${git_version:-${default_version}}\"
|
|
||||||
#define FOOT_MAJOR ${major}
|
#define FOOT_MAJOR ${major}
|
||||||
#define FOOT_MINOR ${minor}
|
#define FOOT_MINOR ${minor}
|
||||||
#define FOOT_PATCH ${patch}
|
#define FOOT_PATCH ${patch}
|
||||||
|
|
|
||||||
69
grid.c
69
grid.c
|
|
@ -255,27 +255,68 @@ grid_snapshot(const struct grid *grid)
|
||||||
}
|
}
|
||||||
|
|
||||||
tll_foreach(grid->sixel_images, it) {
|
tll_foreach(grid->sixel_images, it) {
|
||||||
int width = it->item.width;
|
int original_width = it->item.original.width;
|
||||||
int height = it->item.height;
|
int original_height = it->item.original.height;
|
||||||
pixman_image_t *pix = it->item.pix;
|
pixman_image_t *original_pix = it->item.original.pix;
|
||||||
pixman_format_code_t pix_fmt = pixman_image_get_format(pix);
|
pixman_format_code_t original_pix_fmt = pixman_image_get_format(original_pix);
|
||||||
int stride = stride_for_format_and_width(pix_fmt, width);
|
int original_stride = stride_for_format_and_width(original_pix_fmt, original_width);
|
||||||
|
|
||||||
size_t size = stride * height;
|
size_t original_size = original_stride * original_height;
|
||||||
void *new_data = xmalloc(size);
|
void *new_original_data = xmalloc(original_size);
|
||||||
memcpy(new_data, it->item.data, size);
|
memcpy(new_original_data, it->item.original.data, original_size);
|
||||||
|
|
||||||
pixman_image_t *new_pix = pixman_image_create_bits_no_clear(
|
pixman_image_t *new_original_pix = pixman_image_create_bits_no_clear(
|
||||||
pix_fmt, width, height, new_data, stride);
|
original_pix_fmt, original_width, original_height,
|
||||||
|
new_original_data, original_stride);
|
||||||
|
|
||||||
|
void *new_scaled_data = NULL;
|
||||||
|
pixman_image_t *new_scaled_pix = NULL;
|
||||||
|
int scaled_width = -1;
|
||||||
|
int scaled_height = -1;
|
||||||
|
|
||||||
|
if (it->item.scaled.data != NULL) {
|
||||||
|
scaled_width = it->item.scaled.width;
|
||||||
|
scaled_height = it->item.scaled.height;
|
||||||
|
|
||||||
|
pixman_image_t *scaled_pix = it->item.scaled.pix;
|
||||||
|
pixman_format_code_t scaled_pix_fmt = pixman_image_get_format(scaled_pix);
|
||||||
|
int scaled_stride = stride_for_format_and_width(scaled_pix_fmt, scaled_width);
|
||||||
|
|
||||||
|
size_t scaled_size = scaled_stride * scaled_height;
|
||||||
|
new_scaled_data = xmalloc(scaled_size);
|
||||||
|
memcpy(new_scaled_data, it->item.scaled.data, scaled_size);
|
||||||
|
|
||||||
|
new_scaled_pix = pixman_image_create_bits_no_clear(
|
||||||
|
scaled_pix_fmt, scaled_width, scaled_height, new_scaled_data,
|
||||||
|
scaled_stride);
|
||||||
|
}
|
||||||
|
|
||||||
struct sixel six = {
|
struct sixel six = {
|
||||||
.data = new_data,
|
.pix = (it->item.pix == it->item.original.pix
|
||||||
.pix = new_pix,
|
? new_original_pix
|
||||||
.width = width,
|
: (it->item.pix == it->item.scaled.pix
|
||||||
.height = height,
|
? new_scaled_pix
|
||||||
|
: NULL)),
|
||||||
|
.width = it->item.width,
|
||||||
|
.height = it->item.height,
|
||||||
.rows = it->item.rows,
|
.rows = it->item.rows,
|
||||||
.cols = it->item.cols,
|
.cols = it->item.cols,
|
||||||
.pos = it->item.pos,
|
.pos = it->item.pos,
|
||||||
|
.opaque = it->item.opaque,
|
||||||
|
.cell_width = it->item.cell_width,
|
||||||
|
.cell_height = it->item.cell_height,
|
||||||
|
.original = {
|
||||||
|
.data = new_original_data,
|
||||||
|
.pix = new_original_pix,
|
||||||
|
.width = original_width,
|
||||||
|
.height = original_height,
|
||||||
|
},
|
||||||
|
.scaled = {
|
||||||
|
.data = new_scaled_data,
|
||||||
|
.pix = new_scaled_pix,
|
||||||
|
.width = scaled_width,
|
||||||
|
.height = scaled_height,
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
tll_push_back(clone->sixel_images, six);
|
tll_push_back(clone->sixel_images, six);
|
||||||
|
|
|
||||||
292
input.c
292
input.c
|
|
@ -1704,23 +1704,53 @@ is_bottom_right(const struct terminal *term, int x, int y)
|
||||||
(term->active_surface == TERM_SURF_BORDER_BOTTOM && x > term->width + 1 * csd_border_size * term->scale - 10 * term->scale)));
|
(term->active_surface == TERM_SURF_BORDER_BOTTOM && x > term->width + 1 * csd_border_size * term->scale - 10 * term->scale)));
|
||||||
}
|
}
|
||||||
|
|
||||||
const char *
|
enum cursor_shape
|
||||||
xcursor_for_csd_border(struct terminal *term, int x, int y)
|
xcursor_for_csd_border(struct terminal *term, int x, int y)
|
||||||
{
|
{
|
||||||
if (is_top_left(term, x, y)) return XCURSOR_TOP_LEFT_CORNER;
|
if (is_top_left(term, x, y)) return CURSOR_SHAPE_TOP_LEFT_CORNER;
|
||||||
else if (is_top_right(term, x, y)) return XCURSOR_TOP_RIGHT_CORNER;
|
else if (is_top_right(term, x, y)) return CURSOR_SHAPE_TOP_RIGHT_CORNER;
|
||||||
else if (is_bottom_left(term, x, y)) return XCURSOR_BOTTOM_LEFT_CORNER;
|
else if (is_bottom_left(term, x, y)) return CURSOR_SHAPE_BOTTOM_LEFT_CORNER;
|
||||||
else if (is_bottom_right(term, x, y)) return XCURSOR_BOTTOM_RIGHT_CORNER;
|
else if (is_bottom_right(term, x, y)) return CURSOR_SHAPE_BOTTOM_RIGHT_CORNER;
|
||||||
else if (term->active_surface == TERM_SURF_BORDER_LEFT) return XCURSOR_LEFT_SIDE;
|
else if (term->active_surface == TERM_SURF_BORDER_LEFT) return CURSOR_SHAPE_LEFT_SIDE;
|
||||||
else if (term->active_surface == TERM_SURF_BORDER_RIGHT) return XCURSOR_RIGHT_SIDE;
|
else if (term->active_surface == TERM_SURF_BORDER_RIGHT) return CURSOR_SHAPE_RIGHT_SIDE;
|
||||||
else if (term->active_surface == TERM_SURF_BORDER_TOP) return XCURSOR_TOP_SIDE;
|
else if (term->active_surface == TERM_SURF_BORDER_TOP) return CURSOR_SHAPE_TOP_SIDE;
|
||||||
else if (term->active_surface == TERM_SURF_BORDER_BOTTOM) return XCURSOR_BOTTOM_SIDE;
|
else if (term->active_surface == TERM_SURF_BORDER_BOTTOM) return CURSOR_SHAPE_BOTTOM_SIDE;
|
||||||
else {
|
else {
|
||||||
BUG("Unreachable");
|
BUG("Unreachable");
|
||||||
return NULL;
|
return CURSOR_SHAPE_NONE;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
mouse_button_state_reset(struct seat *seat)
|
||||||
|
{
|
||||||
|
tll_free(seat->mouse.buttons);
|
||||||
|
seat->mouse.count = 0;
|
||||||
|
seat->mouse.last_released_button = 0;
|
||||||
|
memset(&seat->mouse.last_time, 0, sizeof(seat->mouse.last_time));
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
mouse_coord_pixel_to_cell(struct seat *seat, const struct terminal *term,
|
||||||
|
int x, int y)
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
* Translate x,y pixel coordinate to a cell coordinate, or -1
|
||||||
|
* if the cursor is outside the grid. I.e. if it is inside the
|
||||||
|
* margins.
|
||||||
|
*/
|
||||||
|
|
||||||
|
if (x < term->margins.left || x >= term->width - term->margins.right)
|
||||||
|
seat->mouse.col = -1;
|
||||||
|
else
|
||||||
|
seat->mouse.col = (x - term->margins.left) / term->cell_width;
|
||||||
|
|
||||||
|
if (y < term->margins.top || y >= term->height - term->margins.bottom)
|
||||||
|
seat->mouse.row = -1;
|
||||||
|
else
|
||||||
|
seat->mouse.row = (y - term->margins.top) / term->cell_height;
|
||||||
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
wl_pointer_enter(void *data, struct wl_pointer *wl_pointer,
|
wl_pointer_enter(void *data, struct wl_pointer *wl_pointer,
|
||||||
uint32_t serial, struct wl_surface *surface,
|
uint32_t serial, struct wl_surface *surface,
|
||||||
|
|
@ -1733,6 +1763,24 @@ wl_pointer_enter(void *data, struct wl_pointer *wl_pointer,
|
||||||
}
|
}
|
||||||
|
|
||||||
struct seat *seat = data;
|
struct seat *seat = data;
|
||||||
|
|
||||||
|
if (seat->wl_touch != NULL) {
|
||||||
|
switch (seat->touch.state) {
|
||||||
|
case TOUCH_STATE_IDLE:
|
||||||
|
mouse_button_state_reset(seat);
|
||||||
|
seat->touch.state = TOUCH_STATE_INHIBITED;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case TOUCH_STATE_INHIBITED:
|
||||||
|
break;
|
||||||
|
|
||||||
|
case TOUCH_STATE_HELD:
|
||||||
|
case TOUCH_STATE_DRAGGING:
|
||||||
|
case TOUCH_STATE_SCROLLING:
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
struct wl_window *win = wl_surface_get_user_data(surface);
|
struct wl_window *win = wl_surface_get_user_data(surface);
|
||||||
struct terminal *term = win->term;
|
struct terminal *term = win->term;
|
||||||
|
|
||||||
|
|
@ -1759,22 +1807,7 @@ wl_pointer_enter(void *data, struct wl_pointer *wl_pointer,
|
||||||
|
|
||||||
switch (term->active_surface) {
|
switch (term->active_surface) {
|
||||||
case TERM_SURF_GRID: {
|
case TERM_SURF_GRID: {
|
||||||
/*
|
mouse_coord_pixel_to_cell(seat, term, x, y);
|
||||||
* Translate x,y pixel coordinate to a cell coordinate, or -1
|
|
||||||
* if the cursor is outside the grid. I.e. if it is inside the
|
|
||||||
* margins.
|
|
||||||
*/
|
|
||||||
|
|
||||||
if (x < term->margins.left || x >= term->width - term->margins.right)
|
|
||||||
seat->mouse.col = -1;
|
|
||||||
else
|
|
||||||
seat->mouse.col = (x - term->margins.left) / term->cell_width;
|
|
||||||
|
|
||||||
if (y < term->margins.top || y >= term->height - term->margins.bottom)
|
|
||||||
seat->mouse.row = -1;
|
|
||||||
else
|
|
||||||
seat->mouse.row = (y - term->margins.top) / term->cell_height;
|
|
||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1802,6 +1835,14 @@ wl_pointer_leave(void *data, struct wl_pointer *wl_pointer,
|
||||||
uint32_t serial, struct wl_surface *surface)
|
uint32_t serial, struct wl_surface *surface)
|
||||||
{
|
{
|
||||||
struct seat *seat = data;
|
struct seat *seat = data;
|
||||||
|
|
||||||
|
if (seat->wl_touch != NULL) {
|
||||||
|
if (seat->touch.state != TOUCH_STATE_INHIBITED) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
seat->touch.state = TOUCH_STATE_IDLE;
|
||||||
|
}
|
||||||
|
|
||||||
struct terminal *old_moused = seat->mouse_focus;
|
struct terminal *old_moused = seat->mouse_focus;
|
||||||
|
|
||||||
LOG_DBG(
|
LOG_DBG(
|
||||||
|
|
@ -1819,15 +1860,12 @@ wl_pointer_leave(void *data, struct wl_pointer *wl_pointer,
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Reset last-set-xcursor, to ensure we update it on a pointer-enter event */
|
/* Reset last-set-xcursor, to ensure we update it on a pointer-enter event */
|
||||||
seat->pointer.xcursor = NULL;
|
seat->pointer.shape = CURSOR_SHAPE_NONE;
|
||||||
|
|
||||||
/* Reset mouse state */
|
/* Reset mouse state */
|
||||||
seat->mouse.x = seat->mouse.y = 0;
|
seat->mouse.x = seat->mouse.y = 0;
|
||||||
seat->mouse.col = seat->mouse.row = 0;
|
seat->mouse.col = seat->mouse.row = 0;
|
||||||
tll_free(seat->mouse.buttons);
|
mouse_button_state_reset(seat);
|
||||||
seat->mouse.count = 0;
|
|
||||||
seat->mouse.last_released_button = 0;
|
|
||||||
memset(&seat->mouse.last_time, 0, sizeof(seat->mouse.last_time));
|
|
||||||
for (size_t i = 0; i < ALEN(seat->mouse.aggregated); i++)
|
for (size_t i = 0; i < ALEN(seat->mouse.aggregated); i++)
|
||||||
seat->mouse.aggregated[i] = 0.0;
|
seat->mouse.aggregated[i] = 0.0;
|
||||||
seat->mouse.have_discrete = false;
|
seat->mouse.have_discrete = false;
|
||||||
|
|
@ -1879,6 +1917,11 @@ wl_pointer_motion(void *data, struct wl_pointer *wl_pointer,
|
||||||
uint32_t time, wl_fixed_t surface_x, wl_fixed_t surface_y)
|
uint32_t time, wl_fixed_t surface_x, wl_fixed_t surface_y)
|
||||||
{
|
{
|
||||||
struct seat *seat = data;
|
struct seat *seat = data;
|
||||||
|
|
||||||
|
/* Touch-emulated pointer events have wl_pointer == NULL. */
|
||||||
|
if (wl_pointer != NULL && seat->touch.state != TOUCH_STATE_INHIBITED)
|
||||||
|
return;
|
||||||
|
|
||||||
struct wayland *wayl = seat->wayl;
|
struct wayland *wayl = seat->wayl;
|
||||||
struct terminal *term = seat->mouse_focus;
|
struct terminal *term = seat->mouse_focus;
|
||||||
|
|
||||||
|
|
@ -2102,6 +2145,11 @@ wl_pointer_button(void *data, struct wl_pointer *wl_pointer,
|
||||||
xassert(serial != 0);
|
xassert(serial != 0);
|
||||||
|
|
||||||
struct seat *seat = data;
|
struct seat *seat = data;
|
||||||
|
|
||||||
|
/* Touch-emulated pointer events have wl_pointer == NULL. */
|
||||||
|
if (wl_pointer != NULL && seat->touch.state != TOUCH_STATE_INHIBITED)
|
||||||
|
return;
|
||||||
|
|
||||||
struct wayland *wayl = seat->wayl;
|
struct wayland *wayl = seat->wayl;
|
||||||
struct terminal *term = seat->mouse_focus;
|
struct terminal *term = seat->mouse_focus;
|
||||||
|
|
||||||
|
|
@ -2239,7 +2287,10 @@ wl_pointer_button(void *data, struct wl_pointer *wl_pointer,
|
||||||
struct wl_window *win = term->window;
|
struct wl_window *win = term->window;
|
||||||
|
|
||||||
/* Toggle maximized state on double-click */
|
/* Toggle maximized state on double-click */
|
||||||
if (button == BTN_LEFT && seat->mouse.count == 2) {
|
if (term->conf->csd.double_click_to_maximize &&
|
||||||
|
button == BTN_LEFT &&
|
||||||
|
seat->mouse.count == 2)
|
||||||
|
{
|
||||||
if (win->is_maximized)
|
if (win->is_maximized)
|
||||||
xdg_toplevel_unset_maximized(win->xdg_toplevel);
|
xdg_toplevel_unset_maximized(win->xdg_toplevel);
|
||||||
else
|
else
|
||||||
|
|
@ -2559,6 +2610,9 @@ wl_pointer_axis(void *data, struct wl_pointer *wl_pointer,
|
||||||
{
|
{
|
||||||
struct seat *seat = data;
|
struct seat *seat = data;
|
||||||
|
|
||||||
|
if (seat->touch.state != TOUCH_STATE_INHIBITED)
|
||||||
|
return;
|
||||||
|
|
||||||
if (seat->mouse.have_discrete)
|
if (seat->mouse.have_discrete)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
|
@ -2588,6 +2642,10 @@ wl_pointer_axis_discrete(void *data, struct wl_pointer *wl_pointer,
|
||||||
uint32_t axis, int32_t discrete)
|
uint32_t axis, int32_t discrete)
|
||||||
{
|
{
|
||||||
struct seat *seat = data;
|
struct seat *seat = data;
|
||||||
|
|
||||||
|
if (seat->touch.state != TOUCH_STATE_INHIBITED)
|
||||||
|
return;
|
||||||
|
|
||||||
seat->mouse.have_discrete = true;
|
seat->mouse.have_discrete = true;
|
||||||
|
|
||||||
int amount = discrete;
|
int amount = discrete;
|
||||||
|
|
@ -2604,6 +2662,10 @@ static void
|
||||||
wl_pointer_frame(void *data, struct wl_pointer *wl_pointer)
|
wl_pointer_frame(void *data, struct wl_pointer *wl_pointer)
|
||||||
{
|
{
|
||||||
struct seat *seat = data;
|
struct seat *seat = data;
|
||||||
|
|
||||||
|
if (seat->touch.state != TOUCH_STATE_INHIBITED)
|
||||||
|
return;
|
||||||
|
|
||||||
seat->mouse.have_discrete = false;
|
seat->mouse.have_discrete = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -2619,6 +2681,9 @@ wl_pointer_axis_stop(void *data, struct wl_pointer *wl_pointer,
|
||||||
{
|
{
|
||||||
struct seat *seat = data;
|
struct seat *seat = data;
|
||||||
|
|
||||||
|
if (seat->touch.state != TOUCH_STATE_INHIBITED)
|
||||||
|
return;
|
||||||
|
|
||||||
xassert(axis < ALEN(seat->mouse.aggregated));
|
xassert(axis < ALEN(seat->mouse.aggregated));
|
||||||
seat->mouse.aggregated[axis] = 0.;
|
seat->mouse.aggregated[axis] = 0.;
|
||||||
}
|
}
|
||||||
|
|
@ -2634,3 +2699,166 @@ const struct wl_pointer_listener pointer_listener = {
|
||||||
.axis_stop = wl_pointer_axis_stop,
|
.axis_stop = wl_pointer_axis_stop,
|
||||||
.axis_discrete = wl_pointer_axis_discrete,
|
.axis_discrete = wl_pointer_axis_discrete,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
static bool
|
||||||
|
touch_to_scroll(struct seat *seat, struct terminal *term,
|
||||||
|
wl_fixed_t surface_x, wl_fixed_t surface_y)
|
||||||
|
{
|
||||||
|
bool coord_updated = false;
|
||||||
|
|
||||||
|
int y = wl_fixed_to_int(surface_y) * term->scale;
|
||||||
|
int rows = (y - seat->mouse.y) / term->cell_height;
|
||||||
|
if (rows != 0) {
|
||||||
|
mouse_scroll(seat, -rows, WL_POINTER_AXIS_VERTICAL_SCROLL);
|
||||||
|
seat->mouse.y += rows * term->cell_height;
|
||||||
|
coord_updated = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
int x = wl_fixed_to_int(surface_x) * term->scale;
|
||||||
|
int cols = (x - seat->mouse.x) / term->cell_width;
|
||||||
|
if (cols != 0) {
|
||||||
|
mouse_scroll(seat, -cols, WL_POINTER_AXIS_HORIZONTAL_SCROLL);
|
||||||
|
seat->mouse.x += cols * term->cell_width;
|
||||||
|
coord_updated = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return coord_updated;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
wl_touch_down(void *data, struct wl_touch *wl_touch, uint32_t serial,
|
||||||
|
uint32_t time, struct wl_surface *surface, int32_t id,
|
||||||
|
wl_fixed_t surface_x, wl_fixed_t surface_y)
|
||||||
|
{
|
||||||
|
struct seat *seat = data;
|
||||||
|
|
||||||
|
if (seat->touch.state != TOUCH_STATE_IDLE)
|
||||||
|
return;
|
||||||
|
|
||||||
|
struct wl_window *win = wl_surface_get_user_data(surface);
|
||||||
|
struct terminal *term = win->term;
|
||||||
|
|
||||||
|
term->active_surface = term_surface_kind(term, surface);
|
||||||
|
|
||||||
|
LOG_DBG("touch_down: touch=%p, x=%d, y=%d", (void *)wl_touch,
|
||||||
|
wl_fixed_to_int(surface_x), wl_fixed_to_int(surface_y));
|
||||||
|
|
||||||
|
int x = wl_fixed_to_int(surface_x) * term->scale;
|
||||||
|
int y = wl_fixed_to_int(surface_y) * term->scale;
|
||||||
|
|
||||||
|
seat->mouse.x = x;
|
||||||
|
seat->mouse.y = y;
|
||||||
|
mouse_coord_pixel_to_cell(seat, term, x, y);
|
||||||
|
|
||||||
|
seat->touch.state = TOUCH_STATE_HELD;
|
||||||
|
seat->touch.serial = serial;
|
||||||
|
seat->touch.time = time + term->conf->touch.long_press_delay;
|
||||||
|
seat->touch.surface = surface;
|
||||||
|
seat->touch.id = id;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
wl_touch_up(void *data, struct wl_touch *wl_touch, uint32_t serial,
|
||||||
|
uint32_t time, int32_t id)
|
||||||
|
{
|
||||||
|
struct seat *seat = data;
|
||||||
|
|
||||||
|
if (seat->touch.state <= TOUCH_STATE_IDLE || id != seat->touch.id)
|
||||||
|
return;
|
||||||
|
|
||||||
|
LOG_DBG("touch_up: touch=%p", (void *)wl_touch);
|
||||||
|
|
||||||
|
struct wl_window *win = wl_surface_get_user_data(seat->touch.surface);
|
||||||
|
struct terminal *term = win->term;
|
||||||
|
|
||||||
|
seat->mouse_focus = term;
|
||||||
|
|
||||||
|
switch (seat->touch.state) {
|
||||||
|
case TOUCH_STATE_HELD:
|
||||||
|
wl_pointer_button(seat, NULL, seat->touch.serial, time, BTN_LEFT,
|
||||||
|
WL_POINTER_BUTTON_STATE_PRESSED);
|
||||||
|
/* fallthrough */
|
||||||
|
case TOUCH_STATE_DRAGGING:
|
||||||
|
wl_pointer_button(seat, NULL, serial, time, BTN_LEFT,
|
||||||
|
WL_POINTER_BUTTON_STATE_RELEASED);
|
||||||
|
/* fallthrough */
|
||||||
|
case TOUCH_STATE_SCROLLING:
|
||||||
|
term->active_surface = TERM_SURF_NONE;
|
||||||
|
seat->touch.state = TOUCH_STATE_IDLE;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case TOUCH_STATE_INHIBITED:
|
||||||
|
case TOUCH_STATE_IDLE:
|
||||||
|
BUG("Bad touch state: %d", seat->touch.state);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
seat->mouse_focus = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
wl_touch_motion(void *data, struct wl_touch *wl_touch, uint32_t time,
|
||||||
|
int32_t id, wl_fixed_t surface_x, wl_fixed_t surface_y)
|
||||||
|
{
|
||||||
|
struct seat *seat = data;
|
||||||
|
if (seat->touch.state <= TOUCH_STATE_IDLE || id != seat->touch.id)
|
||||||
|
return;
|
||||||
|
|
||||||
|
LOG_DBG("touch_motion: touch=%p, x=%d, y=%d", (void *)wl_touch,
|
||||||
|
wl_fixed_to_int(surface_x), wl_fixed_to_int(surface_y));
|
||||||
|
|
||||||
|
struct wl_window *win = wl_surface_get_user_data(seat->touch.surface);
|
||||||
|
struct terminal *term = win->term;
|
||||||
|
|
||||||
|
seat->mouse_focus = term;
|
||||||
|
|
||||||
|
switch (seat->touch.state) {
|
||||||
|
case TOUCH_STATE_HELD:
|
||||||
|
if (time <= seat->touch.time && term->active_surface == TERM_SURF_GRID) {
|
||||||
|
if (touch_to_scroll(seat, term, surface_x, surface_y))
|
||||||
|
seat->touch.state = TOUCH_STATE_SCROLLING;
|
||||||
|
break;
|
||||||
|
} else {
|
||||||
|
wl_pointer_button(seat, NULL, seat->touch.serial, time, BTN_LEFT,
|
||||||
|
WL_POINTER_BUTTON_STATE_PRESSED);
|
||||||
|
seat->touch.state = TOUCH_STATE_DRAGGING;
|
||||||
|
/* fallthrough */
|
||||||
|
}
|
||||||
|
case TOUCH_STATE_DRAGGING:
|
||||||
|
wl_pointer_motion(seat, NULL, time, surface_x, surface_y);
|
||||||
|
break;
|
||||||
|
case TOUCH_STATE_SCROLLING:
|
||||||
|
touch_to_scroll(seat, term, surface_x, surface_y);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case TOUCH_STATE_INHIBITED:
|
||||||
|
case TOUCH_STATE_IDLE:
|
||||||
|
BUG("Bad touch state: %d", seat->touch.state);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
seat->mouse_focus = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
wl_touch_frame(void *data, struct wl_touch *wl_touch)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
wl_touch_cancel(void *data, struct wl_touch *wl_touch)
|
||||||
|
{
|
||||||
|
struct seat *seat = data;
|
||||||
|
if (seat->touch.state == TOUCH_STATE_INHIBITED)
|
||||||
|
return;
|
||||||
|
|
||||||
|
seat->touch.state = TOUCH_STATE_IDLE;
|
||||||
|
}
|
||||||
|
|
||||||
|
const struct wl_touch_listener touch_listener = {
|
||||||
|
.down = wl_touch_down,
|
||||||
|
.up = wl_touch_up,
|
||||||
|
.motion = wl_touch_motion,
|
||||||
|
.frame = wl_touch_frame,
|
||||||
|
.cancel = wl_touch_cancel,
|
||||||
|
};
|
||||||
|
|
|
||||||
6
input.h
6
input.h
|
|
@ -3,8 +3,9 @@
|
||||||
#include <stdint.h>
|
#include <stdint.h>
|
||||||
#include <wayland-client.h>
|
#include <wayland-client.h>
|
||||||
|
|
||||||
#include "wayland.h"
|
#include "cursor-shape.h"
|
||||||
#include "misc.h"
|
#include "misc.h"
|
||||||
|
#include "wayland.h"
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Custom defines for mouse wheel left/right buttons.
|
* Custom defines for mouse wheel left/right buttons.
|
||||||
|
|
@ -25,6 +26,7 @@
|
||||||
|
|
||||||
extern const struct wl_keyboard_listener keyboard_listener;
|
extern const struct wl_keyboard_listener keyboard_listener;
|
||||||
extern const struct wl_pointer_listener pointer_listener;
|
extern const struct wl_pointer_listener pointer_listener;
|
||||||
|
extern const struct wl_touch_listener touch_listener;
|
||||||
|
|
||||||
void input_repeat(struct seat *seat, uint32_t key);
|
void input_repeat(struct seat *seat, uint32_t key);
|
||||||
|
|
||||||
|
|
@ -33,4 +35,4 @@ void get_current_modifiers(const struct seat *seat,
|
||||||
xkb_mod_mask_t *consumed,
|
xkb_mod_mask_t *consumed,
|
||||||
uint32_t key);
|
uint32_t key);
|
||||||
|
|
||||||
const char *xcursor_for_csd_border(struct terminal *term, int x, int y);
|
enum cursor_shape xcursor_for_csd_border(struct terminal *term, int x, int y);
|
||||||
|
|
|
||||||
17
main.c
17
main.c
|
|
@ -52,11 +52,14 @@ static const char *
|
||||||
version_and_features(void)
|
version_and_features(void)
|
||||||
{
|
{
|
||||||
static char buf[256];
|
static char buf[256];
|
||||||
snprintf(buf, sizeof(buf), "version: %s %cpgo %cime %cgraphemes %cassertions",
|
snprintf(buf, sizeof(buf),
|
||||||
|
"version: %s %cpgo %cime %cgraphemes %cfractional-scaling %ccursor-shape %cassertions",
|
||||||
FOOT_VERSION,
|
FOOT_VERSION,
|
||||||
feature_pgo() ? '+' : '-',
|
feature_pgo() ? '+' : '-',
|
||||||
feature_ime() ? '+' : '-',
|
feature_ime() ? '+' : '-',
|
||||||
feature_graphemes() ? '+' : '-',
|
feature_graphemes() ? '+' : '-',
|
||||||
|
feature_fractional_scaling() ? '+' : '-',
|
||||||
|
feature_cursor_shape() ? '+' : '-',
|
||||||
feature_assertions() ? '+' : '-');
|
feature_assertions() ? '+' : '-');
|
||||||
return buf;
|
return buf;
|
||||||
}
|
}
|
||||||
|
|
@ -450,6 +453,7 @@ main(int argc, char *const *argv)
|
||||||
"C.UTF-8",
|
"C.UTF-8",
|
||||||
"en_US.UTF-8",
|
"en_US.UTF-8",
|
||||||
};
|
};
|
||||||
|
char *saved_locale = xstrdup(locale);
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Try to force an UTF-8 locale. If we succeed, launch the
|
* Try to force an UTF-8 locale. If we succeed, launch the
|
||||||
|
|
@ -461,12 +465,12 @@ main(int argc, char *const *argv)
|
||||||
|
|
||||||
if (setlocale(LC_CTYPE, fallback_locale) != NULL) {
|
if (setlocale(LC_CTYPE, fallback_locale) != NULL) {
|
||||||
LOG_WARN("'%s' is not a UTF-8 locale, using '%s' instead",
|
LOG_WARN("'%s' is not a UTF-8 locale, using '%s' instead",
|
||||||
locale, fallback_locale);
|
saved_locale, fallback_locale);
|
||||||
|
|
||||||
user_notification_add_fmt(
|
user_notification_add_fmt(
|
||||||
&user_notifications, USER_NOTIFICATION_WARNING,
|
&user_notifications, USER_NOTIFICATION_WARNING,
|
||||||
"'%s' is not a UTF-8 locale, using '%s' instead",
|
"'%s' is not a UTF-8 locale, using '%s' instead",
|
||||||
locale, fallback_locale);
|
saved_locale, fallback_locale);
|
||||||
|
|
||||||
bad_locale = false;
|
bad_locale = false;
|
||||||
break;
|
break;
|
||||||
|
|
@ -476,18 +480,19 @@ main(int argc, char *const *argv)
|
||||||
if (bad_locale) {
|
if (bad_locale) {
|
||||||
LOG_ERR(
|
LOG_ERR(
|
||||||
"'%s' is not a UTF-8 locale, and failed to find a fallback",
|
"'%s' is not a UTF-8 locale, and failed to find a fallback",
|
||||||
locale);
|
saved_locale);
|
||||||
|
|
||||||
user_notification_add_fmt(
|
user_notification_add_fmt(
|
||||||
&user_notifications, USER_NOTIFICATION_ERROR,
|
&user_notifications, USER_NOTIFICATION_ERROR,
|
||||||
"'%s' is not a UTF-8 locale, and failed to find a fallback",
|
"'%s' is not a UTF-8 locale, and failed to find a fallback",
|
||||||
locale);
|
saved_locale);
|
||||||
}
|
}
|
||||||
|
free(saved_locale);
|
||||||
}
|
}
|
||||||
|
|
||||||
struct config conf = {NULL};
|
struct config conf = {NULL};
|
||||||
bool conf_successful = config_load(
|
bool conf_successful = config_load(
|
||||||
&conf, conf_path, &user_notifications, &overrides, check_config);
|
&conf, conf_path, &user_notifications, &overrides, check_config, as_server);
|
||||||
|
|
||||||
tll_free(overrides);
|
tll_free(overrides);
|
||||||
if (!conf_successful) {
|
if (!conf_successful) {
|
||||||
|
|
|
||||||
92
meson.build
92
meson.build
|
|
@ -1,7 +1,7 @@
|
||||||
project('foot', 'c',
|
project('foot', 'c',
|
||||||
version: '1.14.0',
|
version: '1.15.0',
|
||||||
license: 'MIT',
|
license: 'MIT',
|
||||||
meson_version: '>=0.58.0',
|
meson_version: '>=0.59.0',
|
||||||
default_options: [
|
default_options: [
|
||||||
'c_std=c11',
|
'c_std=c11',
|
||||||
'warning_level=1',
|
'warning_level=1',
|
||||||
|
|
@ -16,30 +16,54 @@ if cc.has_function('memfd_create')
|
||||||
add_project_arguments('-DMEMFD_CREATE', language: 'c')
|
add_project_arguments('-DMEMFD_CREATE', language: 'c')
|
||||||
endif
|
endif
|
||||||
|
|
||||||
utempter_path = get_option('default-utempter-path')
|
utmp_backend = get_option('utmp-backend')
|
||||||
if utempter_path == ''
|
if utmp_backend == 'auto'
|
||||||
utempter = find_program(
|
host_os = host_machine.system()
|
||||||
'utempter',
|
if host_os == 'linux'
|
||||||
required: false,
|
utmp_backend = 'libutempter'
|
||||||
dirs: [join_paths(get_option('prefix'), get_option('libdir'), 'utempter'),
|
elif host_os == 'freebsd'
|
||||||
join_paths(get_option('prefix'), get_option('libexecdir'), 'utempter'),
|
utmp_backend = 'ulog'
|
||||||
'/usr/lib/utempter',
|
|
||||||
'/usr/libexec/utempter',
|
|
||||||
'/lib/utempter']
|
|
||||||
)
|
|
||||||
if utempter.found()
|
|
||||||
utempter_path = utempter.full_path()
|
|
||||||
else
|
else
|
||||||
utempter_path = ''
|
utmp_backend = 'none'
|
||||||
endif
|
endif
|
||||||
elif utempter_path == 'none'
|
endif
|
||||||
utempter_path = ''
|
|
||||||
|
utmp_default_helper_path = get_option('utmp-default-helper-path')
|
||||||
|
|
||||||
|
if utmp_backend == 'none'
|
||||||
|
utmp_add = ''
|
||||||
|
utmp_del = ''
|
||||||
|
utmp_del_have_argument = false
|
||||||
|
utmp_default_helper_path = ''
|
||||||
|
elif utmp_backend == 'libutempter'
|
||||||
|
utmp_add = 'add'
|
||||||
|
utmp_del = 'del'
|
||||||
|
utmp_del_have_argument = true
|
||||||
|
if utmp_default_helper_path == 'auto'
|
||||||
|
utmp_default_helper_path = join_paths('/usr', get_option('libdir'), 'utempter', 'utempter')
|
||||||
|
endif
|
||||||
|
elif utmp_backend == 'ulog'
|
||||||
|
utmp_add = 'login'
|
||||||
|
utmp_del = 'logout'
|
||||||
|
utmp_del_have_argument = false
|
||||||
|
if utmp_default_helper_path == 'auto'
|
||||||
|
utmp_default_helper_path = join_paths('/usr', get_option('libexecdir'), 'ulog-helper')
|
||||||
|
endif
|
||||||
|
else
|
||||||
|
error('invalid utmp backend')
|
||||||
endif
|
endif
|
||||||
|
|
||||||
add_project_arguments(
|
add_project_arguments(
|
||||||
['-D_GNU_SOURCE=200809L',
|
['-D_GNU_SOURCE=200809L',
|
||||||
'-DFOOT_DEFAULT_TERM="@0@"'.format(get_option('default-terminfo')),
|
'-DFOOT_DEFAULT_TERM="@0@"'.format(get_option('default-terminfo'))] +
|
||||||
'-DFOOT_DEFAULT_UTEMPTER_PATH="@0@"'.format(utempter_path)] +
|
(utmp_backend != 'none'
|
||||||
|
? ['-DUTMP_ADD="@0@"'.format(utmp_add),
|
||||||
|
'-DUTMP_DEL="@0@"'.format(utmp_del),
|
||||||
|
'-DUTMP_DEFAULT_HELPER_PATH="@0@"'.format(utmp_default_helper_path)]
|
||||||
|
: []) +
|
||||||
|
(utmp_del_have_argument
|
||||||
|
? ['-DUTMP_DEL_HAVE_ARGUMENT=1']
|
||||||
|
: []) +
|
||||||
(is_debug_build
|
(is_debug_build
|
||||||
? ['-D_DEBUG']
|
? ['-D_DEBUG']
|
||||||
: [cc.get_supported_arguments('-fno-asynchronous-unwind-tables')]) +
|
: [cc.get_supported_arguments('-fno-asynchronous-unwind-tables')]) +
|
||||||
|
|
@ -133,6 +157,27 @@ wl_proto_xml = [
|
||||||
if wayland_protocols.version().version_compare('>=1.21')
|
if wayland_protocols.version().version_compare('>=1.21')
|
||||||
add_project_arguments('-DHAVE_XDG_ACTIVATION', language: 'c')
|
add_project_arguments('-DHAVE_XDG_ACTIVATION', language: 'c')
|
||||||
wl_proto_xml += [wayland_protocols_datadir + '/staging/xdg-activation/xdg-activation-v1.xml']
|
wl_proto_xml += [wayland_protocols_datadir + '/staging/xdg-activation/xdg-activation-v1.xml']
|
||||||
|
xdg_activation = true
|
||||||
|
else
|
||||||
|
xdg_activation = false
|
||||||
|
endif
|
||||||
|
if wayland_protocols.version().version_compare('>=1.31')
|
||||||
|
add_project_arguments('-DHAVE_FRACTIONAL_SCALE', language: 'c')
|
||||||
|
wl_proto_xml += [wayland_protocols_datadir + '/stable/viewporter/viewporter.xml']
|
||||||
|
wl_proto_xml += [wayland_protocols_datadir + '/staging/fractional-scale/fractional-scale-v1.xml']
|
||||||
|
fractional_scale = true
|
||||||
|
else
|
||||||
|
fractional_scale = false
|
||||||
|
endif
|
||||||
|
if wayland_protocols.version().version_compare('>=1.32')
|
||||||
|
wl_proto_xml += [
|
||||||
|
wayland_protocols_datadir + '/unstable/tablet/tablet-unstable-v2.xml', # required by cursor-shape-v1
|
||||||
|
wayland_protocols_datadir + '/staging/cursor-shape/cursor-shape-v1.xml',
|
||||||
|
]
|
||||||
|
add_project_arguments('-DHAVE_CURSOR_SHAPE', language: 'c')
|
||||||
|
cursor_shape = true
|
||||||
|
else
|
||||||
|
cursor_shape = false
|
||||||
endif
|
endif
|
||||||
|
|
||||||
foreach prot : wl_proto_xml
|
foreach prot : wl_proto_xml
|
||||||
|
|
@ -188,6 +233,7 @@ vtlib = static_library(
|
||||||
'vtlib',
|
'vtlib',
|
||||||
'base64.c', 'base64.h',
|
'base64.c', 'base64.h',
|
||||||
'composed.c', 'composed.h',
|
'composed.c', 'composed.h',
|
||||||
|
'cursor-shape.c', 'cursor-shape.h',
|
||||||
'csi.c', 'csi.h',
|
'csi.c', 'csi.h',
|
||||||
'dcs.c', 'dcs.h',
|
'dcs.c', 'dcs.h',
|
||||||
'macros.h',
|
'macros.h',
|
||||||
|
|
@ -343,7 +389,11 @@ summary(
|
||||||
'Themes': get_option('themes'),
|
'Themes': get_option('themes'),
|
||||||
'IME': get_option('ime'),
|
'IME': get_option('ime'),
|
||||||
'Grapheme clustering': utf8proc.found(),
|
'Grapheme clustering': utf8proc.found(),
|
||||||
'Utempter path': utempter_path,
|
'Wayland: xdg-activation-v1': xdg_activation,
|
||||||
|
'Wayland: fractional-scale-v1': fractional_scale,
|
||||||
|
'Wayland: cursor-shape-v1': cursor_shape,
|
||||||
|
'utmp backend': utmp_backend,
|
||||||
|
'utmp helper default path': utmp_default_helper_path,
|
||||||
'Build terminfo': tic.found(),
|
'Build terminfo': tic.found(),
|
||||||
'Terminfo install location': terminfo_install_location,
|
'Terminfo install location': terminfo_install_location,
|
||||||
'Default TERM': get_option('default-terminfo'),
|
'Default TERM': get_option('default-terminfo'),
|
||||||
|
|
|
||||||
|
|
@ -22,5 +22,7 @@ option('custom-terminfo-install-location', type: 'string', value: '',
|
||||||
option('systemd-units-dir', type: 'string', value: '',
|
option('systemd-units-dir', type: 'string', value: '',
|
||||||
description: 'Where to install the systemd service files (absolute path). Default: ${systemduserunitdir}')
|
description: 'Where to install the systemd service files (absolute path). Default: ${systemduserunitdir}')
|
||||||
|
|
||||||
option('default-utempter-path', type: 'string', value: '',
|
option('utmp-backend', type: 'combo', value: 'auto', choices: ['none', 'libutempter', 'ulog', 'auto'],
|
||||||
description: 'Default path to utempter helper binary. Default: auto-detect')
|
description: 'Which utmp logging backend to use. This affects how (with what arguments) the utmp helper binary (see \'utmp-default-helper-path\')is called. Default: auto (linux=libutempter, freebsd=ulog, others=none)')
|
||||||
|
option('utmp-default-helper-path', type: 'string', value: 'auto',
|
||||||
|
description: 'Default path to the utmp helper binary. Default: auto-detect')
|
||||||
|
|
|
||||||
|
|
@ -9,4 +9,3 @@ Keywords=shell;prompt;command;commandline;
|
||||||
Name=Foot Server
|
Name=Foot Server
|
||||||
GenericName=Terminal
|
GenericName=Terminal
|
||||||
Comment=A wayland native terminal emulator (server)
|
Comment=A wayland native terminal emulator (server)
|
||||||
StartupWMClass=foot
|
|
||||||
|
|
|
||||||
|
|
@ -9,4 +9,4 @@ Keywords=shell;prompt;command;commandline;
|
||||||
Name=Foot Client
|
Name=Foot Client
|
||||||
GenericName=Terminal
|
GenericName=Terminal
|
||||||
Comment=A wayland native terminal emulator (client)
|
Comment=A wayland native terminal emulator (client)
|
||||||
StartupWMClass=foot
|
StartupWMClass=footclient
|
||||||
|
|
|
||||||
9
osc.c
9
osc.c
|
|
@ -729,8 +729,15 @@ osc_dispatch(struct terminal *term)
|
||||||
|
|
||||||
case 11:
|
case 11:
|
||||||
term->colors.bg = color;
|
term->colors.bg = color;
|
||||||
if (have_alpha)
|
if (have_alpha) {
|
||||||
|
const bool changed = term->colors.alpha != alpha;
|
||||||
term->colors.alpha = alpha;
|
term->colors.alpha = alpha;
|
||||||
|
|
||||||
|
if (changed) {
|
||||||
|
wayl_win_alpha_changed(term->window);
|
||||||
|
term_font_subpixel_changed(term);
|
||||||
|
}
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 17:
|
case 17:
|
||||||
|
|
|
||||||
|
|
@ -76,15 +76,15 @@ render_xcursor_is_valid(const struct seat *seat, const char *cursor)
|
||||||
}
|
}
|
||||||
|
|
||||||
bool
|
bool
|
||||||
render_xcursor_set(struct seat *seat, struct terminal *term, const char *xcursor)
|
render_xcursor_set(struct seat *seat, struct terminal *term, enum cursor_shape shape)
|
||||||
{
|
{
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
const char *
|
enum cursor_shape
|
||||||
xcursor_for_csd_border(struct terminal *term, int x, int y)
|
xcursor_for_csd_border(struct terminal *term, int x, int y)
|
||||||
{
|
{
|
||||||
return XCURSOR_LEFT_PTR;
|
return CURSOR_SHAPE_LEFT_PTR;
|
||||||
}
|
}
|
||||||
|
|
||||||
struct wl_window *
|
struct wl_window *
|
||||||
|
|
@ -94,7 +94,9 @@ wayl_win_init(struct terminal *term, const char *token)
|
||||||
}
|
}
|
||||||
|
|
||||||
void wayl_win_destroy(struct wl_window *win) {}
|
void wayl_win_destroy(struct wl_window *win) {}
|
||||||
|
void wayl_win_alpha_changed(struct wl_window *win) {}
|
||||||
bool wayl_win_set_urgent(struct wl_window *win) { return true; }
|
bool wayl_win_set_urgent(struct wl_window *win) { return true; }
|
||||||
|
bool wayl_fractional_scaling(const struct wayland *wayl) { return true; }
|
||||||
|
|
||||||
bool
|
bool
|
||||||
spawn(struct reaper *reaper, const char *cwd, char *const argv[],
|
spawn(struct reaper *reaper, const char *cwd, char *const argv[],
|
||||||
|
|
|
||||||
25
quirks.c
25
quirks.c
|
|
@ -66,3 +66,28 @@ quirk_weston_csd_off(struct terminal *term)
|
||||||
for (int i = 0; i < ALEN(term->window->csd.surface); i++)
|
for (int i = 0; i < ALEN(term->window->csd.surface); i++)
|
||||||
quirk_weston_subsurface_desync_off(term->window->csd.surface[i].sub);
|
quirk_weston_subsurface_desync_off(term->window->csd.surface[i].sub);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static bool
|
||||||
|
is_sway(void)
|
||||||
|
{
|
||||||
|
static bool is_sway = false;
|
||||||
|
static bool initialized = false;
|
||||||
|
|
||||||
|
if (!initialized) {
|
||||||
|
initialized = true;
|
||||||
|
is_sway = getenv("SWAYSOCK") != NULL;
|
||||||
|
if (is_sway)
|
||||||
|
LOG_WARN("applying wl_surface_damage_buffer() workaround for Sway");
|
||||||
|
}
|
||||||
|
|
||||||
|
return is_sway;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
quirk_sway_subsurface_unmap(struct terminal *term)
|
||||||
|
{
|
||||||
|
if (!is_sway())
|
||||||
|
return;
|
||||||
|
|
||||||
|
wl_surface_damage_buffer(term->window->surface.surf, 0, 0, INT32_MAX, INT32_MAX);
|
||||||
|
}
|
||||||
|
|
|
||||||
2
quirks.h
2
quirks.h
|
|
@ -21,3 +21,5 @@ void quirk_weston_subsurface_desync_off(struct wl_subsurface *sub);
|
||||||
/* Shortcuts to call desync_{on,off} on all CSD subsurfaces */
|
/* Shortcuts to call desync_{on,off} on all CSD subsurfaces */
|
||||||
void quirk_weston_csd_on(struct terminal *term);
|
void quirk_weston_csd_on(struct terminal *term);
|
||||||
void quirk_weston_csd_off(struct terminal *term);
|
void quirk_weston_csd_off(struct terminal *term);
|
||||||
|
|
||||||
|
void quirk_sway_subsurface_unmap(struct terminal *term);
|
||||||
|
|
|
||||||
2
render.h
2
render.h
|
|
@ -19,7 +19,7 @@ void render_refresh_search(struct terminal *term);
|
||||||
void render_refresh_title(struct terminal *term);
|
void render_refresh_title(struct terminal *term);
|
||||||
void render_refresh_urls(struct terminal *term);
|
void render_refresh_urls(struct terminal *term);
|
||||||
bool render_xcursor_set(
|
bool render_xcursor_set(
|
||||||
struct seat *seat, struct terminal *term, const char *xcursor);
|
struct seat *seat, struct terminal *term, enum cursor_shape shape);
|
||||||
bool render_xcursor_is_valid(const struct seat *seat, const char *cursor);
|
bool render_xcursor_is_valid(const struct seat *seat, const char *cursor);
|
||||||
|
|
||||||
struct render_worker_context {
|
struct render_worker_context {
|
||||||
|
|
|
||||||
|
|
@ -207,8 +207,8 @@ def main():
|
||||||
six_height, six_width = last_size
|
six_height, six_width = last_size
|
||||||
six_rows = (six_height + 5) // 6 # Round up; each sixel is 6 pixels
|
six_rows = (six_height + 5) // 6 # Round up; each sixel is 6 pixels
|
||||||
|
|
||||||
# Begin sixel
|
# Begin sixel (with P2=1 - empty sixels are transparent)
|
||||||
out.write('\033Pq')
|
out.write('\033P;1q')
|
||||||
|
|
||||||
# Sixel size. Without this, sixels will be
|
# Sixel size. Without this, sixels will be
|
||||||
# auto-resized on cell-boundaries.
|
# auto-resized on cell-boundaries.
|
||||||
|
|
|
||||||
7
search.c
7
search.c
|
|
@ -15,6 +15,7 @@
|
||||||
#include "input.h"
|
#include "input.h"
|
||||||
#include "key-binding.h"
|
#include "key-binding.h"
|
||||||
#include "misc.h"
|
#include "misc.h"
|
||||||
|
#include "quirks.h"
|
||||||
#include "render.h"
|
#include "render.h"
|
||||||
#include "selection.h"
|
#include "selection.h"
|
||||||
#include "shm.h"
|
#include "shm.h"
|
||||||
|
|
@ -117,11 +118,6 @@ search_cancel_keep_selection(struct terminal *term)
|
||||||
|
|
||||||
term_xcursor_update(term);
|
term_xcursor_update(term);
|
||||||
render_refresh(term);
|
render_refresh(term);
|
||||||
|
|
||||||
/* Work around Sway bug - unmapping a sub-surface does not damage
|
|
||||||
* the underlying surface */
|
|
||||||
term_damage_margins(term);
|
|
||||||
term_damage_view(term);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
|
|
@ -833,6 +829,7 @@ execute_binding(struct seat *seat, struct terminal *term,
|
||||||
grid->view = ensure_view_is_allocated(
|
grid->view = ensure_view_is_allocated(
|
||||||
term, term->search.original_view);
|
term, term->search.original_view);
|
||||||
}
|
}
|
||||||
|
term_damage_view(term);
|
||||||
search_cancel(term);
|
search_cancel(term);
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
|
|
|
||||||
645
sixel.c
645
sixel.c
|
|
@ -16,6 +16,9 @@
|
||||||
|
|
||||||
static size_t count;
|
static size_t count;
|
||||||
|
|
||||||
|
static void sixel_put_generic(struct terminal *term, uint8_t c);
|
||||||
|
static void sixel_put_ar_11(struct terminal *term, uint8_t c);
|
||||||
|
|
||||||
void
|
void
|
||||||
sixel_fini(struct terminal *term)
|
sixel_fini(struct terminal *term)
|
||||||
{
|
{
|
||||||
|
|
@ -24,7 +27,7 @@ sixel_fini(struct terminal *term)
|
||||||
free(term->sixel.shared_palette);
|
free(term->sixel.shared_palette);
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
sixel_put
|
||||||
sixel_init(struct terminal *term, int p1, int p2, int p3)
|
sixel_init(struct terminal *term, int p1, int p2, int p3)
|
||||||
{
|
{
|
||||||
/*
|
/*
|
||||||
|
|
@ -38,18 +41,32 @@ sixel_init(struct terminal *term, int p1, int p2, int p3)
|
||||||
xassert(term->sixel.image.data == NULL);
|
xassert(term->sixel.image.data == NULL);
|
||||||
xassert(term->sixel.palette_size <= SIXEL_MAX_COLORS);
|
xassert(term->sixel.palette_size <= SIXEL_MAX_COLORS);
|
||||||
|
|
||||||
|
/* Default aspect ratio is 2:1 */
|
||||||
|
const int pad = 1;
|
||||||
|
const int pan =
|
||||||
|
(p1 == 2) ? 5 :
|
||||||
|
(p1 == 3 || p1 == 4) ? 3 :
|
||||||
|
(p1 == 7 || p1 == 8 || p1 == 9) ? 1 : 2;
|
||||||
|
|
||||||
|
LOG_DBG("initializing sixel with "
|
||||||
|
"p1=%d (pan=%d, pad=%d, aspect-ratio=%d:%d), "
|
||||||
|
"p2=%d (transparent=%s), "
|
||||||
|
"p3=%d (ignored)",
|
||||||
|
p1, pan, pad, pan, pad, p2, p2 == 1 ? "yes" : "no", p3);
|
||||||
|
|
||||||
term->sixel.state = SIXEL_DECSIXEL;
|
term->sixel.state = SIXEL_DECSIXEL;
|
||||||
term->sixel.pos = (struct coord){0, 0};
|
term->sixel.pos = (struct coord){0, 0};
|
||||||
term->sixel.max_non_empty_row_no = -1;
|
|
||||||
term->sixel.row_byte_ofs = 0;
|
term->sixel.row_byte_ofs = 0;
|
||||||
term->sixel.color_idx = 0;
|
term->sixel.color_idx = 0;
|
||||||
|
term->sixel.pan = pan;
|
||||||
|
term->sixel.pad = pad;
|
||||||
term->sixel.param = 0;
|
term->sixel.param = 0;
|
||||||
term->sixel.param_idx = 0;
|
term->sixel.param_idx = 0;
|
||||||
memset(term->sixel.params, 0, sizeof(term->sixel.params));
|
memset(term->sixel.params, 0, sizeof(term->sixel.params));
|
||||||
term->sixel.transparent_bg = p2 == 1;
|
term->sixel.transparent_bg = p2 == 1;
|
||||||
term->sixel.image.data = xmalloc(1 * 6 * sizeof(term->sixel.image.data[0]));
|
term->sixel.image.data = NULL;
|
||||||
term->sixel.image.width = 1;
|
term->sixel.image.width = 0;
|
||||||
term->sixel.image.height = 6;
|
term->sixel.image.height = 0;
|
||||||
|
|
||||||
/* TODO: default palette */
|
/* TODO: default palette */
|
||||||
|
|
||||||
|
|
@ -73,38 +90,69 @@ sixel_init(struct terminal *term, int p1, int p2, int p3)
|
||||||
|
|
||||||
switch (term->vt.attrs.bg_src) {
|
switch (term->vt.attrs.bg_src) {
|
||||||
case COLOR_RGB:
|
case COLOR_RGB:
|
||||||
bg = term->vt.attrs.bg;
|
bg = 0xffu << 24 | term->vt.attrs.bg;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case COLOR_BASE16:
|
case COLOR_BASE16:
|
||||||
case COLOR_BASE256:
|
case COLOR_BASE256:
|
||||||
bg = term->colors.table[term->vt.attrs.bg];
|
bg = 0xffu << 24 | term->colors.table[term->vt.attrs.bg];
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case COLOR_DEFAULT:
|
case COLOR_DEFAULT:
|
||||||
bg = term->colors.bg;
|
if (term->colors.alpha == 0xffff)
|
||||||
|
bg = 0xffu << 24 | term->colors.bg;
|
||||||
|
else {
|
||||||
|
/* Alpha needs to be pre-multiplied */
|
||||||
|
uint32_t r = (term->colors.bg >> 16) & 0xff;
|
||||||
|
uint32_t g = (term->colors.bg >> 8) & 0xff;
|
||||||
|
uint32_t b = (term->colors.bg >> 0) & 0xff;
|
||||||
|
|
||||||
|
uint32_t alpha = term->colors.alpha;
|
||||||
|
r *= alpha; r /= 0xffff;
|
||||||
|
g *= alpha; g /= 0xffff;
|
||||||
|
b *= alpha; b /= 0xffff;
|
||||||
|
|
||||||
|
bg = (alpha >> 8) << 24 | (r & 0xff) << 16 | (g & 0xff) << 8 | (b & 0xff);
|
||||||
|
}
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
term->sixel.default_bg = term->sixel.transparent_bg
|
term->sixel.default_bg = term->sixel.transparent_bg
|
||||||
? 0x00000000u
|
? 0x00000000u
|
||||||
: 0xffu << 24 | bg;
|
: bg;
|
||||||
|
|
||||||
for (size_t i = 0; i < 1 * 6; i++)
|
|
||||||
term->sixel.image.data[i] = term->sixel.default_bg;
|
|
||||||
|
|
||||||
count = 0;
|
count = 0;
|
||||||
|
return pan == 1 && pad == 1 ? &sixel_put_ar_11 : &sixel_put_generic;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
sixel_invalidate_cache(struct sixel *sixel)
|
||||||
|
{
|
||||||
|
if (sixel->scaled.pix != NULL)
|
||||||
|
pixman_image_unref(sixel->scaled.pix);
|
||||||
|
|
||||||
|
free(sixel->scaled.data);
|
||||||
|
sixel->scaled.pix = NULL;
|
||||||
|
sixel->scaled.data = NULL;
|
||||||
|
sixel->scaled.width = -1;
|
||||||
|
sixel->scaled.height = -1;
|
||||||
|
|
||||||
|
sixel->pix = NULL;
|
||||||
|
sixel->width = -1;
|
||||||
|
sixel->height = -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
sixel_destroy(struct sixel *sixel)
|
sixel_destroy(struct sixel *sixel)
|
||||||
{
|
{
|
||||||
if (sixel->pix != NULL)
|
sixel_invalidate_cache(sixel);
|
||||||
pixman_image_unref(sixel->pix);
|
|
||||||
|
|
||||||
free(sixel->data);
|
if (sixel->original.pix != NULL)
|
||||||
sixel->pix = NULL;
|
pixman_image_unref(sixel->original.pix);
|
||||||
sixel->data = NULL;
|
|
||||||
|
free(sixel->original.data);
|
||||||
|
sixel->original.pix = NULL;
|
||||||
|
sixel->original.data = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
|
|
@ -367,10 +415,14 @@ blend_new_image_over_old(const struct terminal *term,
|
||||||
xassert(pix != NULL);
|
xassert(pix != NULL);
|
||||||
xassert(opaque != NULL);
|
xassert(opaque != NULL);
|
||||||
|
|
||||||
const int six_ofs_x = six->pos.col * term->cell_width;
|
/*
|
||||||
const int six_ofs_y = six->pos.row * term->cell_height;
|
* TODO: handle images being emitted with different cell dimensions
|
||||||
const int img_ofs_x = col * term->cell_width;
|
*/
|
||||||
const int img_ofs_y = row * term->cell_height;
|
|
||||||
|
const int six_ofs_x = six->pos.col * six->cell_width;
|
||||||
|
const int six_ofs_y = six->pos.row * six->cell_height;
|
||||||
|
const int img_ofs_x = col * six->cell_width;
|
||||||
|
const int img_ofs_y = row * six->cell_height;
|
||||||
const int img_width = pixman_image_get_width(*pix);
|
const int img_width = pixman_image_get_width(*pix);
|
||||||
const int img_height = pixman_image_get_height(*pix);
|
const int img_height = pixman_image_get_height(*pix);
|
||||||
|
|
||||||
|
|
@ -400,7 +452,7 @@ blend_new_image_over_old(const struct terminal *term,
|
||||||
*/
|
*/
|
||||||
pixman_image_composite32(
|
pixman_image_composite32(
|
||||||
PIXMAN_OP_OVER_REVERSE,
|
PIXMAN_OP_OVER_REVERSE,
|
||||||
six->pix, NULL, *pix,
|
six->original.pix, NULL, *pix,
|
||||||
box->x1 - six_ofs_x, box->y1 - six_ofs_y,
|
box->x1 - six_ofs_x, box->y1 - six_ofs_y,
|
||||||
0, 0,
|
0, 0,
|
||||||
box->x1 - img_ofs_x, box->y1 - img_ofs_y,
|
box->x1 - img_ofs_x, box->y1 - img_ofs_y,
|
||||||
|
|
@ -417,15 +469,15 @@ blend_new_image_over_old(const struct terminal *term,
|
||||||
* old image, or the next cell boundary, whichever comes
|
* old image, or the next cell boundary, whichever comes
|
||||||
* first.
|
* first.
|
||||||
*/
|
*/
|
||||||
int bounding_x = six_ofs_x + six->width > img_ofs_x + img_width
|
int bounding_x = six_ofs_x + six->original.width > img_ofs_x + img_width
|
||||||
? min(
|
? min(
|
||||||
six_ofs_x + six->width,
|
six_ofs_x + six->original.width,
|
||||||
(box->x2 + term->cell_width - 1) / term->cell_width * term->cell_width)
|
(box->x2 + six->cell_width - 1) / six->cell_width * six->cell_width)
|
||||||
: box->x2;
|
: box->x2;
|
||||||
int bounding_y = six_ofs_y + six->height > img_ofs_y + img_height
|
int bounding_y = six_ofs_y + six->original.height > img_ofs_y + img_height
|
||||||
? min(
|
? min(
|
||||||
six_ofs_y + six->height,
|
six_ofs_y + six->original.height,
|
||||||
(box->y2 + term->cell_height - 1) / term->cell_height * term->cell_height)
|
(box->y2 + six->cell_height - 1) / six->cell_height * six->cell_height)
|
||||||
: box->y2;
|
: box->y2;
|
||||||
|
|
||||||
/* The required size of the new image */
|
/* The required size of the new image */
|
||||||
|
|
@ -465,7 +517,7 @@ blend_new_image_over_old(const struct terminal *term,
|
||||||
/* Copy the bottom tile of the old sixel image into the new pixmap */
|
/* Copy the bottom tile of the old sixel image into the new pixmap */
|
||||||
pixman_image_composite32(
|
pixman_image_composite32(
|
||||||
PIXMAN_OP_SRC,
|
PIXMAN_OP_SRC,
|
||||||
six->pix, NULL, pix2,
|
six->original.pix, NULL, pix2,
|
||||||
box->x1 - six_ofs_x, box->y2 - six_ofs_y,
|
box->x1 - six_ofs_x, box->y2 - six_ofs_y,
|
||||||
0, 0,
|
0, 0,
|
||||||
box->x1 - img_ofs_x, box->y2 - img_ofs_y,
|
box->x1 - img_ofs_x, box->y2 - img_ofs_y,
|
||||||
|
|
@ -474,7 +526,7 @@ blend_new_image_over_old(const struct terminal *term,
|
||||||
/* Copy the right tile of the old sixel image into the new pixmap */
|
/* Copy the right tile of the old sixel image into the new pixmap */
|
||||||
pixman_image_composite32(
|
pixman_image_composite32(
|
||||||
PIXMAN_OP_SRC,
|
PIXMAN_OP_SRC,
|
||||||
six->pix, NULL, pix2,
|
six->original.pix, NULL, pix2,
|
||||||
box->x2 - six_ofs_x, box->y1 - six_ofs_y,
|
box->x2 - six_ofs_x, box->y1 - six_ofs_y,
|
||||||
0, 0,
|
0, 0,
|
||||||
box->x2 - img_ofs_x, box->y1 - img_ofs_y,
|
box->x2 - img_ofs_x, box->y1 - img_ofs_y,
|
||||||
|
|
@ -548,14 +600,14 @@ sixel_overwrite(struct terminal *term, struct sixel *six,
|
||||||
pixman_region32_t six_rect;
|
pixman_region32_t six_rect;
|
||||||
pixman_region32_init_rect(
|
pixman_region32_init_rect(
|
||||||
&six_rect,
|
&six_rect,
|
||||||
six->pos.col * term->cell_width, six->pos.row * term->cell_height,
|
six->pos.col * six->cell_width, six->pos.row * six->cell_height,
|
||||||
six->width, six->height);
|
six->original.width, six->original.height);
|
||||||
|
|
||||||
pixman_region32_t overwrite_rect;
|
pixman_region32_t overwrite_rect;
|
||||||
pixman_region32_init_rect(
|
pixman_region32_init_rect(
|
||||||
&overwrite_rect,
|
&overwrite_rect,
|
||||||
col * term->cell_width, row * term->cell_height,
|
col * six->cell_width, row * six->cell_height,
|
||||||
width * term->cell_width, height * term->cell_height);
|
width * six->cell_width, height * six->cell_height);
|
||||||
|
|
||||||
#if defined(_DEBUG)
|
#if defined(_DEBUG)
|
||||||
pixman_region32_t cell_intersection;
|
pixman_region32_t cell_intersection;
|
||||||
|
|
@ -568,7 +620,6 @@ sixel_overwrite(struct terminal *term, struct sixel *six,
|
||||||
if (pix != NULL)
|
if (pix != NULL)
|
||||||
blend_new_image_over_old(term, six, &six_rect, row, col, pix, opaque);
|
blend_new_image_over_old(term, six, &six_rect, row, col, pix, opaque);
|
||||||
|
|
||||||
|
|
||||||
pixman_region32_t diff;
|
pixman_region32_t diff;
|
||||||
pixman_region32_init(&diff);
|
pixman_region32_init(&diff);
|
||||||
pixman_region32_subtract(&diff, &six_rect, &overwrite_rect);
|
pixman_region32_subtract(&diff, &six_rect, &overwrite_rect);
|
||||||
|
|
@ -583,12 +634,12 @@ sixel_overwrite(struct terminal *term, struct sixel *six,
|
||||||
LOG_DBG("box #%d: x1=%d, y1=%d, x2=%d, y2=%d", i,
|
LOG_DBG("box #%d: x1=%d, y1=%d, x2=%d, y2=%d", i,
|
||||||
boxes[i].x1, boxes[i].y1, boxes[i].x2, boxes[i].y2);
|
boxes[i].x1, boxes[i].y1, boxes[i].x2, boxes[i].y2);
|
||||||
|
|
||||||
xassert(boxes[i].x1 % term->cell_width == 0);
|
xassert(boxes[i].x1 % six->cell_width == 0);
|
||||||
xassert(boxes[i].y1 % term->cell_height == 0);
|
xassert(boxes[i].y1 % six->cell_height == 0);
|
||||||
|
|
||||||
/* New image's position, in cells */
|
/* New image's position, in cells */
|
||||||
const int new_col = boxes[i].x1 / term->cell_width;
|
const int new_col = boxes[i].x1 / six->cell_width;
|
||||||
const int new_row = boxes[i].y1 / term->cell_height;
|
const int new_row = boxes[i].y1 / six->cell_height;
|
||||||
|
|
||||||
xassert(new_row < term->grid->num_rows);
|
xassert(new_row < term->grid->num_rows);
|
||||||
|
|
||||||
|
|
@ -597,17 +648,17 @@ sixel_overwrite(struct terminal *term, struct sixel *six,
|
||||||
const int new_height = boxes[i].y2 - boxes[i].y1;
|
const int new_height = boxes[i].y2 - boxes[i].y1;
|
||||||
|
|
||||||
uint32_t *new_data = xmalloc(new_width * new_height * sizeof(uint32_t));
|
uint32_t *new_data = xmalloc(new_width * new_height * sizeof(uint32_t));
|
||||||
const uint32_t *old_data = six->data;
|
const uint32_t *old_data = six->original.data;
|
||||||
|
|
||||||
/* Pixel offsets into old image backing memory */
|
/* Pixel offsets into old image backing memory */
|
||||||
const int x_ofs = boxes[i].x1 - six->pos.col * term->cell_width;
|
const int x_ofs = boxes[i].x1 - six->pos.col * six->cell_width;
|
||||||
const int y_ofs = boxes[i].y1 - six->pos.row * term->cell_height;
|
const int y_ofs = boxes[i].y1 - six->pos.row * six->cell_height;
|
||||||
|
|
||||||
/* Copy image data, one row at a time */
|
/* Copy image data, one row at a time */
|
||||||
for (size_t j = 0; j < new_height; j++) {
|
for (size_t j = 0; j < new_height; j++) {
|
||||||
memcpy(
|
memcpy(
|
||||||
&new_data[(0 + j) * new_width],
|
&new_data[(0 + j) * new_width],
|
||||||
&old_data[(y_ofs + j) * six->width + x_ofs],
|
&old_data[(y_ofs + j) * six->original.width + x_ofs],
|
||||||
new_width * sizeof(uint32_t));
|
new_width * sizeof(uint32_t));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -616,14 +667,27 @@ sixel_overwrite(struct terminal *term, struct sixel *six,
|
||||||
new_width, new_height, new_data, new_width * sizeof(uint32_t));
|
new_width, new_height, new_data, new_width * sizeof(uint32_t));
|
||||||
|
|
||||||
struct sixel new_six = {
|
struct sixel new_six = {
|
||||||
|
.pix = NULL,
|
||||||
|
.width = -1,
|
||||||
|
.height = -1,
|
||||||
|
.pos = {.col = new_col, .row = new_row},
|
||||||
|
.cols = (new_width + six->cell_width - 1) / six->cell_width,
|
||||||
|
.rows = (new_height + six->cell_height - 1) / six->cell_height,
|
||||||
|
.opaque = six->opaque,
|
||||||
|
.cell_width = six->cell_width,
|
||||||
|
.cell_height = six->cell_height,
|
||||||
|
.original = {
|
||||||
.data = new_data,
|
.data = new_data,
|
||||||
.pix = new_pix,
|
.pix = new_pix,
|
||||||
.width = new_width,
|
.width = new_width,
|
||||||
.height = new_height,
|
.height = new_height,
|
||||||
.pos = {.col = new_col, .row = new_row},
|
},
|
||||||
.cols = (new_width + term->cell_width - 1) / term->cell_width,
|
.scaled = {
|
||||||
.rows = (new_height + term->cell_height - 1) / term->cell_height,
|
.data = NULL,
|
||||||
.opaque = six->opaque,
|
.pix = NULL,
|
||||||
|
.width = -1,
|
||||||
|
.height = -1,
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
#if defined(_DEBUG)
|
#if defined(_DEBUG)
|
||||||
|
|
@ -818,23 +882,94 @@ sixel_overwrite_at_cursor(struct terminal *term, int width)
|
||||||
void
|
void
|
||||||
sixel_cell_size_changed(struct terminal *term)
|
sixel_cell_size_changed(struct terminal *term)
|
||||||
{
|
{
|
||||||
struct grid *g = term->grid;
|
tll_foreach(term->normal.sixel_images, it)
|
||||||
|
sixel_invalidate_cache(&it->item);
|
||||||
|
|
||||||
term->grid = &term->normal;
|
tll_foreach(term->alt.sixel_images, it)
|
||||||
tll_foreach(term->normal.sixel_images, it) {
|
sixel_invalidate_cache(&it->item);
|
||||||
struct sixel *six = &it->item;
|
|
||||||
six->rows = (six->height + term->cell_height - 1) / term->cell_height;
|
|
||||||
six->cols = (six->width + term->cell_width - 1) / term->cell_width;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
term->grid = &term->alt;
|
void
|
||||||
tll_foreach(term->alt.sixel_images, it) {
|
sixel_sync_cache(const struct terminal *term, struct sixel *six)
|
||||||
struct sixel *six = &it->item;
|
{
|
||||||
six->rows = (six->height + term->cell_height - 1) / term->cell_height;
|
if (six->pix != NULL) {
|
||||||
six->cols = (six->width + term->cell_width - 1) / term->cell_width;
|
#if defined(_DEBUG)
|
||||||
|
if (six->cell_width == term->cell_width &&
|
||||||
|
six->cell_height == term->cell_height)
|
||||||
|
{
|
||||||
|
xassert(six->pix == six->original.pix);
|
||||||
|
xassert(six->width == six->original.width);
|
||||||
|
xassert(six->height == six->original.height);
|
||||||
|
|
||||||
|
xassert(six->scaled.data == NULL);
|
||||||
|
xassert(six->scaled.pix == NULL);
|
||||||
|
xassert(six->scaled.width < 0);
|
||||||
|
xassert(six->scaled.height < 0);
|
||||||
|
} else {
|
||||||
|
xassert(six->pix == six->scaled.pix);
|
||||||
|
xassert(six->width == six->scaled.width);
|
||||||
|
xassert(six->height == six->scaled.height);
|
||||||
|
|
||||||
|
xassert(six->scaled.data != NULL);
|
||||||
|
xassert(six->scaled.pix != NULL);
|
||||||
|
|
||||||
|
/* TODO: check ratio */
|
||||||
|
xassert(six->scaled.width >= 0);
|
||||||
|
xassert(six->scaled.height >= 0);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
term->grid = g;
|
/* Cache should be invalid */
|
||||||
|
xassert(six->scaled.data == NULL);
|
||||||
|
xassert(six->scaled.pix == NULL);
|
||||||
|
xassert(six->scaled.width < 0);
|
||||||
|
xassert(six->scaled.height < 0);
|
||||||
|
|
||||||
|
if (six->cell_width == term->cell_width &&
|
||||||
|
six->cell_height == term->cell_height)
|
||||||
|
{
|
||||||
|
six->pix = six->original.pix;
|
||||||
|
six->width = six->original.width;
|
||||||
|
six->height = six->original.height;
|
||||||
|
} else {
|
||||||
|
const double width_ratio = (double)term->cell_width / six->cell_width;
|
||||||
|
const double height_ratio = (double)term->cell_height / six->cell_height;
|
||||||
|
|
||||||
|
struct pixman_f_transform scale;
|
||||||
|
pixman_f_transform_init_scale(
|
||||||
|
&scale, 1. / width_ratio, 1. / height_ratio);
|
||||||
|
|
||||||
|
struct pixman_transform _scale;
|
||||||
|
pixman_transform_from_pixman_f_transform(&_scale, &scale);
|
||||||
|
pixman_image_set_transform(six->original.pix, &_scale);
|
||||||
|
pixman_image_set_filter(six->original.pix, PIXMAN_FILTER_BILINEAR, NULL, 0);
|
||||||
|
|
||||||
|
int scaled_width = (double)six->original.width * width_ratio;
|
||||||
|
int scaled_height = (double)six->original.height * height_ratio;
|
||||||
|
int scaled_stride = scaled_width * sizeof(uint32_t);
|
||||||
|
|
||||||
|
LOG_DBG("scaling sixel: %dx%d -> %dx%d",
|
||||||
|
six->original.width, six->original.height,
|
||||||
|
scaled_width, scaled_height);
|
||||||
|
|
||||||
|
uint8_t *scaled_data = xmalloc(scaled_height * scaled_stride);
|
||||||
|
pixman_image_t *scaled_pix = pixman_image_create_bits_no_clear(
|
||||||
|
PIXMAN_a8r8g8b8, scaled_width, scaled_height,
|
||||||
|
(uint32_t *)scaled_data, scaled_stride);
|
||||||
|
|
||||||
|
pixman_image_composite32(
|
||||||
|
PIXMAN_OP_SRC, six->original.pix, NULL, scaled_pix, 0, 0, 0, 0,
|
||||||
|
0, 0, scaled_width, scaled_height);
|
||||||
|
|
||||||
|
pixman_image_set_transform(six->original.pix, NULL);
|
||||||
|
|
||||||
|
six->scaled.data = scaled_data;
|
||||||
|
six->scaled.pix = six->pix = scaled_pix;
|
||||||
|
six->scaled.width = six->width = scaled_width;
|
||||||
|
six->scaled.height = six->height = scaled_height;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
|
|
@ -897,14 +1032,15 @@ sixel_reflow_grid(struct terminal *term, struct grid *grid)
|
||||||
* allowed of course */
|
* allowed of course */
|
||||||
_sixel_overwrite_by_rectangle(
|
_sixel_overwrite_by_rectangle(
|
||||||
term, six->pos.row, six->pos.col, six->rows, six->cols,
|
term, six->pos.row, six->pos.col, six->rows, six->cols,
|
||||||
&it->item.pix, &it->item.opaque);
|
&it->item.original.pix, &it->item.opaque);
|
||||||
|
|
||||||
if (it->item.data != pixman_image_get_data(it->item.pix)) {
|
if (it->item.original.data != pixman_image_get_data(it->item.original.pix)) {
|
||||||
it->item.data = pixman_image_get_data(it->item.pix);
|
it->item.original.data = pixman_image_get_data(it->item.original.pix);
|
||||||
it->item.width = pixman_image_get_width(it->item.pix);
|
it->item.original.width = pixman_image_get_width(it->item.original.pix);
|
||||||
it->item.height = pixman_image_get_height(it->item.pix);
|
it->item.original.height = pixman_image_get_height(it->item.original.pix);
|
||||||
it->item.cols = (it->item.width + term->cell_width - 1) / term->cell_width;
|
it->item.cols = (it->item.original.width + it->item.cell_width - 1) / it->item.cell_width;
|
||||||
it->item.rows = (it->item.height + term->cell_height - 1) / term->cell_height;
|
it->item.rows = (it->item.original.height + it->item.cell_height - 1) / it->item.cell_height;
|
||||||
|
sixel_invalidate_cache(&it->item);
|
||||||
}
|
}
|
||||||
|
|
||||||
sixel_insert(term, it->item);
|
sixel_insert(term, it->item);
|
||||||
|
|
@ -926,13 +1062,6 @@ sixel_reflow(struct terminal *term)
|
||||||
void
|
void
|
||||||
sixel_unhook(struct terminal *term)
|
sixel_unhook(struct terminal *term)
|
||||||
{
|
{
|
||||||
if (term->sixel.image.height > term->sixel.max_non_empty_row_no + 1) {
|
|
||||||
LOG_DBG(
|
|
||||||
"last row only partially filled, reducing image height: %d -> %d",
|
|
||||||
term->sixel.image.height, term->sixel.max_non_empty_row_no + 1);
|
|
||||||
term->sixel.image.height = term->sixel.max_non_empty_row_no + 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
int pixel_row_idx = 0;
|
int pixel_row_idx = 0;
|
||||||
int pixel_rows_left = term->sixel.image.height;
|
int pixel_rows_left = term->sixel.image.height;
|
||||||
const int stride = term->sixel.image.width * sizeof(uint32_t);
|
const int stride = term->sixel.image.width * sizeof(uint32_t);
|
||||||
|
|
@ -1005,13 +1134,27 @@ sixel_unhook(struct terminal *term)
|
||||||
}
|
}
|
||||||
|
|
||||||
struct sixel image = {
|
struct sixel image = {
|
||||||
.data = img_data,
|
.pix = NULL,
|
||||||
.width = width,
|
.width = -1,
|
||||||
.height = height,
|
.height = -1,
|
||||||
.rows = (height + term->cell_height - 1) / term->cell_height,
|
.rows = (height + term->cell_height - 1) / term->cell_height,
|
||||||
.cols = (width + term->cell_width - 1) / term->cell_width,
|
.cols = (width + term->cell_width - 1) / term->cell_width,
|
||||||
.pos = (struct coord){start_col, cur_row},
|
.pos = (struct coord){start_col, cur_row},
|
||||||
.opaque = !term->sixel.transparent_bg,
|
.opaque = !term->sixel.transparent_bg,
|
||||||
|
.cell_width = term->cell_width,
|
||||||
|
.cell_height = term->cell_height,
|
||||||
|
.original = {
|
||||||
|
.data = img_data,
|
||||||
|
.pix = NULL,
|
||||||
|
.width = width,
|
||||||
|
.height = height,
|
||||||
|
},
|
||||||
|
.scaled = {
|
||||||
|
.data = NULL,
|
||||||
|
.pix = NULL,
|
||||||
|
.width = -1,
|
||||||
|
.height = -1,
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
xassert(image.rows <= term->grid->num_rows);
|
xassert(image.rows <= term->grid->num_rows);
|
||||||
|
|
@ -1022,13 +1165,65 @@ sixel_unhook(struct terminal *term)
|
||||||
image.width, image.height,
|
image.width, image.height,
|
||||||
image.pos.row, image.pos.row + image.rows);
|
image.pos.row, image.pos.row + image.rows);
|
||||||
|
|
||||||
image.pix = pixman_image_create_bits_no_clear(
|
image.original.pix = pixman_image_create_bits_no_clear(
|
||||||
PIXMAN_a8r8g8b8, image.width, image.height, img_data, stride);
|
PIXMAN_a8r8g8b8, image.original.width, image.original.height,
|
||||||
|
img_data, stride);
|
||||||
|
|
||||||
pixel_row_idx += height;
|
pixel_row_idx += height;
|
||||||
pixel_rows_left -= height;
|
pixel_rows_left -= height;
|
||||||
rows_avail -= image.rows;
|
rows_avail -= image.rows;
|
||||||
|
|
||||||
|
if (do_scroll) {
|
||||||
|
/*
|
||||||
|
* Linefeeds - always one less than the number of rows
|
||||||
|
* occupied by the image.
|
||||||
|
*
|
||||||
|
* Unless this is *not* the last chunk. In that case,
|
||||||
|
* linefeed past the chunk, so that the next chunk
|
||||||
|
* "starts" at a "new" row.
|
||||||
|
*/
|
||||||
|
const int linefeed_count = rows_avail == 0
|
||||||
|
? max(0, image.rows - 1)
|
||||||
|
: image.rows;
|
||||||
|
|
||||||
|
xassert(rows_avail == 0 ||
|
||||||
|
image.original.height % term->cell_height == 0);
|
||||||
|
|
||||||
|
for (size_t i = 0; i < linefeed_count; i++)
|
||||||
|
term_linefeed(term);
|
||||||
|
|
||||||
|
/* Position text cursor if this is the last image chunk */
|
||||||
|
if (rows_avail == 0) {
|
||||||
|
int row = term->grid->cursor.point.row;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 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
|
||||||
|
* the linefeeds). But for some combinations of font
|
||||||
|
* and image sizes, the final cursor position is
|
||||||
|
* higher up.
|
||||||
|
*/
|
||||||
|
const int sixel_row_height = 6 * term->sixel.pan;
|
||||||
|
const int sixel_rows = (image.original.height + sixel_row_height - 1) / sixel_row_height;
|
||||||
|
const int upper_pixel_last_sixel = (sixel_rows - 1) * sixel_row_height;
|
||||||
|
const int term_rows = (upper_pixel_last_sixel + term->cell_height - 1) / term->cell_height;
|
||||||
|
|
||||||
|
xassert(term_rows <= image.rows);
|
||||||
|
|
||||||
|
row -= (image.rows - term_rows);
|
||||||
|
|
||||||
|
term_cursor_to(
|
||||||
|
term,
|
||||||
|
max(0, row),
|
||||||
|
(term->sixel.cursor_right_of_graphics
|
||||||
|
? min(image.pos.col + image.cols, term->cols - 1)
|
||||||
|
: image.pos.col));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/* Dirty touched cells, and scroll terminal content if necessary */
|
/* Dirty touched cells, and scroll terminal content if necessary */
|
||||||
for (size_t i = 0; i < image.rows; i++) {
|
for (size_t i = 0; i < image.rows; i++) {
|
||||||
struct row *row = term->grid->rows[cur_row + i];
|
struct row *row = term->grid->rows[cur_row + i];
|
||||||
|
|
@ -1041,38 +1236,19 @@ sixel_unhook(struct terminal *term)
|
||||||
row->cells[col].attrs.clean = 0;
|
row->cells[col].attrs.clean = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (do_scroll) {
|
|
||||||
/*
|
|
||||||
* Linefeed, *unless* we're on the very last row of
|
|
||||||
* the final image (not just this chunk) and private
|
|
||||||
* mode 8452 (leave cursor at the right of graphics)
|
|
||||||
* is enabled.
|
|
||||||
*/
|
|
||||||
if (term->sixel.cursor_right_of_graphics &&
|
|
||||||
rows_avail == 0 &&
|
|
||||||
i >= image.rows - 1)
|
|
||||||
{
|
|
||||||
term_cursor_to(
|
|
||||||
term,
|
|
||||||
term->grid->cursor.point.row,
|
|
||||||
min(image.pos.col + image.cols, term->cols - 1));
|
|
||||||
} else {
|
|
||||||
term_linefeed(term);
|
|
||||||
term_carriage_return(term);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
_sixel_overwrite_by_rectangle(
|
_sixel_overwrite_by_rectangle(
|
||||||
term, image.pos.row, image.pos.col, image.rows, image.cols,
|
term, image.pos.row, image.pos.col, image.rows, image.cols,
|
||||||
&image.pix, &image.opaque);
|
&image.original.pix, &image.opaque);
|
||||||
|
|
||||||
if (image.data != pixman_image_get_data(image.pix)) {
|
if (image.original.data != pixman_image_get_data(image.original.pix)) {
|
||||||
image.data = pixman_image_get_data(image.pix);
|
image.original.data = pixman_image_get_data(image.original.pix);
|
||||||
image.width = pixman_image_get_width(image.pix);
|
image.original.width = pixman_image_get_width(image.original.pix);
|
||||||
image.height = pixman_image_get_height(image.pix);
|
image.original.height = pixman_image_get_height(image.original.pix);
|
||||||
image.cols = (image.width + term->cell_width - 1) / term->cell_width;
|
image.cols = (image.original.width + image.cell_width - 1) / image.cell_width;
|
||||||
image.rows = (image.height + term->cell_height - 1) / term->cell_height;
|
image.rows = (image.original.height + image.cell_height - 1) / image.cell_height;
|
||||||
|
sixel_invalidate_cache(&image);
|
||||||
}
|
}
|
||||||
|
|
||||||
sixel_insert(term, image);
|
sixel_insert(term, image);
|
||||||
|
|
@ -1104,10 +1280,6 @@ sixel_unhook(struct terminal *term)
|
||||||
static void
|
static void
|
||||||
resize_horizontally(struct terminal *term, int new_width)
|
resize_horizontally(struct terminal *term, int new_width)
|
||||||
{
|
{
|
||||||
LOG_DBG("resizing image horizontally: %dx(%d) -> %dx(%d)",
|
|
||||||
term->sixel.image.width, term->sixel.image.height,
|
|
||||||
new_width, term->sixel.image.height);
|
|
||||||
|
|
||||||
if (unlikely(new_width > term->sixel.max_width)) {
|
if (unlikely(new_width > term->sixel.max_width)) {
|
||||||
LOG_WARN("maximum image dimensions exceeded, truncating");
|
LOG_WARN("maximum image dimensions exceeded, truncating");
|
||||||
new_width = term->sixel.max_width;
|
new_width = term->sixel.max_width;
|
||||||
|
|
@ -1116,11 +1288,24 @@ resize_horizontally(struct terminal *term, int new_width)
|
||||||
if (unlikely(term->sixel.image.width == new_width))
|
if (unlikely(term->sixel.image.width == new_width))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
const int sixel_row_height = 6 * term->sixel.pan;
|
||||||
|
|
||||||
uint32_t *old_data = term->sixel.image.data;
|
uint32_t *old_data = term->sixel.image.data;
|
||||||
const int old_width = term->sixel.image.width;
|
const int old_width = term->sixel.image.width;
|
||||||
const int height = term->sixel.image.height;
|
|
||||||
|
|
||||||
int alloc_height = (height + 6 - 1) / 6 * 6;
|
int height;
|
||||||
|
if (unlikely(term->sixel.image.height == 0)) {
|
||||||
|
/* Lazy initialize height on first printed sixel */
|
||||||
|
xassert(old_width == 0);
|
||||||
|
term->sixel.image.height = height = sixel_row_height;
|
||||||
|
} else
|
||||||
|
height = term->sixel.image.height;
|
||||||
|
|
||||||
|
LOG_DBG("resizing image horizontally: %dx(%d) -> %dx(%d)",
|
||||||
|
term->sixel.image.width, term->sixel.image.height,
|
||||||
|
new_width, height);
|
||||||
|
|
||||||
|
int alloc_height = (height + sixel_row_height - 1) / sixel_row_height * sixel_row_height;
|
||||||
|
|
||||||
xassert(new_width > 0);
|
xassert(new_width > 0);
|
||||||
xassert(alloc_height > 0);
|
xassert(alloc_height > 0);
|
||||||
|
|
@ -1165,9 +1350,14 @@ resize_vertically(struct terminal *term, int new_height)
|
||||||
|
|
||||||
int alloc_height = (new_height + 6 - 1) / 6 * 6;
|
int alloc_height = (new_height + 6 - 1) / 6 * 6;
|
||||||
|
|
||||||
xassert(width > 0);
|
|
||||||
xassert(new_height > 0);
|
xassert(new_height > 0);
|
||||||
|
|
||||||
|
if (unlikely(width == 0)) {
|
||||||
|
xassert(term->sixel.image.data == NULL);
|
||||||
|
term->sixel.image.height = new_height;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
uint32_t *new_data = realloc(
|
uint32_t *new_data = realloc(
|
||||||
old_data, width * alloc_height * sizeof(uint32_t));
|
old_data, width * alloc_height * sizeof(uint32_t));
|
||||||
|
|
||||||
|
|
@ -1210,10 +1400,14 @@ resize(struct terminal *term, int new_width, int new_height)
|
||||||
const int old_width = term->sixel.image.width;
|
const int old_width = term->sixel.image.width;
|
||||||
const int old_height = term->sixel.image.height;
|
const int old_height = term->sixel.image.height;
|
||||||
|
|
||||||
|
if (unlikely(old_width == new_width && old_height == new_height))
|
||||||
|
return true;
|
||||||
|
|
||||||
|
const int sixel_row_height = 6 * term->sixel.pan;
|
||||||
int alloc_new_width = new_width;
|
int alloc_new_width = new_width;
|
||||||
int alloc_new_height = (new_height + 6 - 1) / 6 * 6;
|
int alloc_new_height = (new_height + sixel_row_height - 1) / sixel_row_height * sixel_row_height;
|
||||||
xassert(alloc_new_height >= new_height);
|
xassert(alloc_new_height >= new_height);
|
||||||
xassert(alloc_new_height - new_height < 6);
|
xassert(alloc_new_height - new_height < sixel_row_height);
|
||||||
|
|
||||||
uint32_t *new_data = NULL;
|
uint32_t *new_data = NULL;
|
||||||
uint32_t bg = term->sixel.default_bg;
|
uint32_t bg = term->sixel.default_bg;
|
||||||
|
|
@ -1261,34 +1455,86 @@ resize(struct terminal *term, int new_width, int new_height)
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
sixel_add(struct terminal *term, int col, int width, uint32_t color, uint8_t sixel)
|
sixel_add_generic(struct terminal *term, int col, int width, uint32_t color,
|
||||||
|
uint8_t sixel)
|
||||||
{
|
{
|
||||||
xassert(term->sixel.pos.col < term->sixel.image.width);
|
xassert(term->sixel.pos.col < term->sixel.image.width);
|
||||||
xassert(term->sixel.pos.row < term->sixel.image.height);
|
xassert(term->sixel.pos.row < term->sixel.image.height);
|
||||||
|
|
||||||
|
const int pan = term->sixel.pan;
|
||||||
size_t ofs = term->sixel.row_byte_ofs + col;
|
size_t ofs = term->sixel.row_byte_ofs + col;
|
||||||
uint32_t *data = &term->sixel.image.data[ofs];
|
uint32_t *data = &term->sixel.image.data[ofs];
|
||||||
|
|
||||||
int max_non_empty_row = -1;
|
for (int i = 0; i < 6; i++, sixel >>= 1) {
|
||||||
int row = term->sixel.pos.row;
|
|
||||||
|
|
||||||
for (int i = 0; i < 6; i++, sixel >>= 1, data += width) {
|
|
||||||
if (sixel & 1) {
|
if (sixel & 1) {
|
||||||
|
for (int r = 0; r < pan; r++, data += width)
|
||||||
*data = color;
|
*data = color;
|
||||||
max_non_empty_row = row + i;
|
} else
|
||||||
}
|
data += width * pan;
|
||||||
}
|
}
|
||||||
|
|
||||||
xassert(sixel == 0);
|
xassert(sixel == 0);
|
||||||
|
|
||||||
term->sixel.max_non_empty_row_no = max(
|
|
||||||
term->sixel.max_non_empty_row_no,
|
|
||||||
max_non_empty_row);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
sixel_add_many(struct terminal *term, uint8_t c, unsigned count)
|
sixel_add_ar_11(struct terminal *term, int col, int width, uint32_t color,
|
||||||
|
uint8_t sixel)
|
||||||
{
|
{
|
||||||
|
xassert(term->sixel.pos.col < term->sixel.image.width);
|
||||||
|
xassert(term->sixel.pos.row < term->sixel.image.height);
|
||||||
|
xassert(term->sixel.pan == 1);
|
||||||
|
|
||||||
|
const size_t ofs = term->sixel.row_byte_ofs + col;
|
||||||
|
uint32_t *data = &term->sixel.image.data[ofs];
|
||||||
|
|
||||||
|
if (sixel & 0x01)
|
||||||
|
*data = color;
|
||||||
|
data += width;
|
||||||
|
if (sixel & 0x02)
|
||||||
|
*data = color;
|
||||||
|
data += width;
|
||||||
|
if (sixel & 0x04)
|
||||||
|
*data = color;
|
||||||
|
data += width;
|
||||||
|
if (sixel & 0x08)
|
||||||
|
*data = color;
|
||||||
|
data += width;
|
||||||
|
if (sixel & 0x10)
|
||||||
|
*data = color;
|
||||||
|
data += width;
|
||||||
|
if (sixel & 0x20)
|
||||||
|
*data = color;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
sixel_add_many_generic(struct terminal *term, uint8_t c, unsigned count)
|
||||||
|
{
|
||||||
|
int col = term->sixel.pos.col;
|
||||||
|
int width = term->sixel.image.width;
|
||||||
|
|
||||||
|
count *= term->sixel.pad;
|
||||||
|
|
||||||
|
if (unlikely(col + count - 1 >= width)) {
|
||||||
|
resize_horizontally(term, col + count);
|
||||||
|
width = term->sixel.image.width;
|
||||||
|
count = min(count, max(width - col, 0));
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t color = term->sixel.color;
|
||||||
|
for (unsigned i = 0; i < count; i++, col++) {
|
||||||
|
/* TODO: is it worth dynamically dispatching to either generic or AR-11? */
|
||||||
|
sixel_add_generic(term, col, width, color, c);
|
||||||
|
}
|
||||||
|
|
||||||
|
term->sixel.pos.col = col;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
sixel_add_many_ar_11(struct terminal *term, uint8_t c, unsigned count)
|
||||||
|
{
|
||||||
|
xassert(term->sixel.pan == 1);
|
||||||
|
xassert(term->sixel.pad == 1);
|
||||||
|
|
||||||
int col = term->sixel.pos.col;
|
int col = term->sixel.pos.col;
|
||||||
int width = term->sixel.image.width;
|
int width = term->sixel.image.width;
|
||||||
|
|
||||||
|
|
@ -1300,13 +1546,15 @@ sixel_add_many(struct terminal *term, uint8_t c, unsigned count)
|
||||||
|
|
||||||
uint32_t color = term->sixel.color;
|
uint32_t color = term->sixel.color;
|
||||||
for (unsigned i = 0; i < count; i++, col++)
|
for (unsigned i = 0; i < count; i++, col++)
|
||||||
sixel_add(term, col, width, color, c);
|
sixel_add_ar_11(term, col, width, color, c);
|
||||||
|
|
||||||
term->sixel.pos.col = col;
|
term->sixel.pos.col = col;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
IGNORE_WARNING("-Wpedantic")
|
||||||
|
|
||||||
static void
|
static void
|
||||||
decsixel(struct terminal *term, uint8_t c)
|
decsixel_generic(struct terminal *term, uint8_t c)
|
||||||
{
|
{
|
||||||
switch (c) {
|
switch (c) {
|
||||||
case '"':
|
case '"':
|
||||||
|
|
@ -1319,6 +1567,7 @@ decsixel(struct terminal *term, uint8_t c)
|
||||||
term->sixel.state = SIXEL_DECGRI;
|
term->sixel.state = SIXEL_DECGRI;
|
||||||
term->sixel.param = 0;
|
term->sixel.param = 0;
|
||||||
term->sixel.param_idx = 0;
|
term->sixel.param_idx = 0;
|
||||||
|
term->sixel.repeat_count = 1;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case '#':
|
case '#':
|
||||||
|
|
@ -1341,27 +1590,18 @@ decsixel(struct terminal *term, uint8_t c)
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case '-':
|
case '-':
|
||||||
term->sixel.pos.row += 6;
|
term->sixel.pos.row += 6 * term->sixel.pan;
|
||||||
term->sixel.pos.col = 0;
|
term->sixel.pos.col = 0;
|
||||||
term->sixel.row_byte_ofs += term->sixel.image.width * 6;
|
term->sixel.row_byte_ofs += term->sixel.image.width * 6 * term->sixel.pan;
|
||||||
|
|
||||||
if (term->sixel.pos.row >= term->sixel.image.height) {
|
if (term->sixel.pos.row >= term->sixel.image.height) {
|
||||||
if (!resize_vertically(term, term->sixel.pos.row + 6))
|
if (!resize_vertically(term, term->sixel.pos.row + 6 * term->sixel.pan))
|
||||||
term->sixel.pos.col = term->sixel.max_width + 1;
|
term->sixel.pos.col = term->sixel.max_width + 1 * term->sixel.pad;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case '?': case '@': case 'A': case 'B': case 'C': case 'D': case 'E':
|
case '?' ... '~':
|
||||||
case 'F': case 'G': case 'H': case 'I': case 'J': case 'K': case 'L':
|
sixel_add_many_generic(term, c - 63, 1);
|
||||||
case 'M': case 'N': case 'O': case 'P': case 'Q': case 'R': case 'S':
|
|
||||||
case 'T': case 'U': case 'V': case 'W': case 'X': case 'Y': case 'Z':
|
|
||||||
case '[': case '\\': case ']': case '^': case '_': case '`': case 'a':
|
|
||||||
case 'b': case 'c': case 'd': case 'e': case 'f': case 'g': case 'h':
|
|
||||||
case 'i': case 'j': case 'k': case 'l': case 'm': case 'n': case 'o':
|
|
||||||
case 'p': case 'q': case 'r': case 's': case 't': case 'u': case 'v':
|
|
||||||
case 'w': case 'x': case 'y': case 'z': case '{': case '|': case '}':
|
|
||||||
case '~':
|
|
||||||
sixel_add_many(term, c - 63, 1);
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case ' ':
|
case ' ':
|
||||||
|
|
@ -1375,6 +1615,17 @@ decsixel(struct terminal *term, uint8_t c)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
UNIGNORE_WARNINGS
|
||||||
|
|
||||||
|
static void
|
||||||
|
decsixel_ar_11(struct terminal *term, uint8_t c)
|
||||||
|
{
|
||||||
|
if (likely(c >= '?' && c <= '~'))
|
||||||
|
sixel_add_many_ar_11(term, c - 63, 1);
|
||||||
|
else
|
||||||
|
decsixel_generic(term, c);
|
||||||
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
decgra(struct terminal *term, uint8_t c)
|
decgra(struct terminal *term, uint8_t c)
|
||||||
{
|
{
|
||||||
|
|
@ -1404,63 +1655,88 @@ decgra(struct terminal *term, uint8_t c)
|
||||||
pan = pan > 0 ? pan : 1;
|
pan = pan > 0 ? pan : 1;
|
||||||
pad = pad > 0 ? pad : 1;
|
pad = pad > 0 ? pad : 1;
|
||||||
|
|
||||||
LOG_DBG("pan=%u, pad=%u (aspect ratio = %u), size=%ux%u",
|
pv *= pan;
|
||||||
pan, pad, pan / pad, ph, pv);
|
ph *= pad;
|
||||||
|
|
||||||
|
term->sixel.pan = pan;
|
||||||
|
term->sixel.pad = pad;
|
||||||
|
|
||||||
|
LOG_DBG("pan=%u, pad=%u (aspect ratio = %d:%d), size=%ux%u",
|
||||||
|
pan, pad, pan, pad, ph, pv);
|
||||||
|
|
||||||
if (ph >= term->sixel.image.height && pv >= term->sixel.image.width &&
|
if (ph >= term->sixel.image.height && pv >= term->sixel.image.width &&
|
||||||
ph <= term->sixel.max_height && pv <= term->sixel.max_width)
|
ph <= term->sixel.max_height && pv <= term->sixel.max_width)
|
||||||
{
|
{
|
||||||
resize(term, ph, pv);
|
resize(term, ph, pv);
|
||||||
|
|
||||||
/* This ensures the sixel’s final image size is *at least*
|
|
||||||
* this large */
|
|
||||||
term->sixel.max_non_empty_row_no =
|
|
||||||
min(pv, term->sixel.image.height) - 1;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
term->sixel.state = SIXEL_DECSIXEL;
|
term->sixel.state = SIXEL_DECSIXEL;
|
||||||
decsixel(term, c);
|
|
||||||
|
/* Update DCS put handler, since pan/pad may have changed */
|
||||||
|
term->vt.dcs.put_handler = pan == 1 && pad == 1
|
||||||
|
? &sixel_put_ar_11
|
||||||
|
: &sixel_put_generic;
|
||||||
|
|
||||||
|
if (likely(pan == 1 && pad == 1))
|
||||||
|
decsixel_ar_11(term, c);
|
||||||
|
else
|
||||||
|
decsixel_generic(term, c);
|
||||||
|
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
IGNORE_WARNING("-Wpedantic")
|
||||||
|
|
||||||
static void
|
static void
|
||||||
decgri(struct terminal *term, uint8_t c)
|
decgri_generic(struct terminal *term, uint8_t c)
|
||||||
{
|
{
|
||||||
switch (c) {
|
switch (c) {
|
||||||
case '0': case '1': case '2': case '3': case '4':
|
case '0': case '1': case '2': case '3': case '4':
|
||||||
case '5': case '6': case '7': case '8': case '9':
|
case '5': case '6': case '7': case '8': case '9': {
|
||||||
term->sixel.param *= 10;
|
unsigned param = term->sixel.param;
|
||||||
term->sixel.param += c - '0';
|
param *= 10;
|
||||||
|
param += c - '0';
|
||||||
|
term->sixel.repeat_count = term->sixel.param = param;
|
||||||
break;
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
case '?': case '@': case 'A': case 'B': case 'C': case 'D': case 'E':
|
case '?' ... '~': {
|
||||||
case 'F': case 'G': case 'H': case 'I': case 'J': case 'K': case 'L':
|
unsigned count = term->sixel.repeat_count;
|
||||||
case 'M': case 'N': case 'O': case 'P': case 'Q': case 'R': case 'S':
|
if (unlikely(count == 0)) {
|
||||||
case 'T': case 'U': case 'V': case 'W': case 'X': case 'Y': case 'Z':
|
count = 1;
|
||||||
case '[': case '\\': case ']': case '^': case '_': case '`': case 'a':
|
}
|
||||||
case 'b': case 'c': case 'd': case 'e': case 'f': case 'g': case 'h':
|
|
||||||
case 'i': case 'j': case 'k': case 'l': case 'm': case 'n': case 'o':
|
sixel_add_many_generic(term, c - 63, count);
|
||||||
case 'p': case 'q': case 'r': case 's': case 't': case 'u': case 'v':
|
|
||||||
case 'w': case 'x': case 'y': case 'z': case '{': case '|': case '}':
|
|
||||||
case '~': {
|
|
||||||
unsigned count = term->sixel.param;
|
|
||||||
if (likely(count > 0))
|
|
||||||
sixel_add_many(term, c - 63, count);
|
|
||||||
else if (unlikely(count == 0))
|
|
||||||
sixel_add_many(term, c - 63, 1);
|
|
||||||
term->sixel.state = SIXEL_DECSIXEL;
|
term->sixel.state = SIXEL_DECSIXEL;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
default:
|
default:
|
||||||
term->sixel.state = SIXEL_DECSIXEL;
|
term->sixel.state = SIXEL_DECSIXEL;
|
||||||
sixel_put(term, c);
|
term->vt.dcs.put_handler(term, c);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
UNIGNORE_WARNINGS
|
||||||
|
|
||||||
|
static void
|
||||||
|
decgri_ar_11(struct terminal *term, uint8_t c)
|
||||||
|
{
|
||||||
|
if (likely(c >= '?' && c <= '~')) {
|
||||||
|
unsigned count = term->sixel.repeat_count;
|
||||||
|
if (unlikely(count == 0)) {
|
||||||
|
count = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
sixel_add_many_ar_11(term, c - 63, count);
|
||||||
|
term->sixel.state = SIXEL_DECSIXEL;
|
||||||
|
} else
|
||||||
|
decgri_generic(term, c);
|
||||||
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
decgci(struct terminal *term, uint8_t c)
|
decgci(struct terminal *term, uint8_t c)
|
||||||
{
|
{
|
||||||
|
|
@ -1537,19 +1813,36 @@ decgci(struct terminal *term, uint8_t c)
|
||||||
term->sixel.color = term->sixel.palette[term->sixel.color_idx];
|
term->sixel.color = term->sixel.palette[term->sixel.color_idx];
|
||||||
|
|
||||||
term->sixel.state = SIXEL_DECSIXEL;
|
term->sixel.state = SIXEL_DECSIXEL;
|
||||||
decsixel(term, c);
|
|
||||||
|
if (likely(term->sixel.pan == 1 && term->sixel.pad == 1))
|
||||||
|
decsixel_ar_11(term, c);
|
||||||
|
else
|
||||||
|
decsixel_generic(term, c);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
static void
|
||||||
sixel_put(struct terminal *term, uint8_t c)
|
sixel_put_generic(struct terminal *term, uint8_t c)
|
||||||
{
|
{
|
||||||
switch (term->sixel.state) {
|
switch (term->sixel.state) {
|
||||||
case SIXEL_DECSIXEL: decsixel(term, c); break;
|
case SIXEL_DECSIXEL: decsixel_generic(term, c); break;
|
||||||
case SIXEL_DECGRA: decgra(term, c); break;
|
case SIXEL_DECGRA: decgra(term, c); break;
|
||||||
case SIXEL_DECGRI: decgri(term, c); break;
|
case SIXEL_DECGRI: decgri_generic(term, c); break;
|
||||||
|
case SIXEL_DECGCI: decgci(term, c); break;
|
||||||
|
}
|
||||||
|
|
||||||
|
count++;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
sixel_put_ar_11(struct terminal *term, uint8_t c)
|
||||||
|
{
|
||||||
|
switch (term->sixel.state) {
|
||||||
|
case SIXEL_DECSIXEL: decsixel_ar_11(term, c); break;
|
||||||
|
case SIXEL_DECGRA: decgra(term, c); break;
|
||||||
|
case SIXEL_DECGRI: decgri_ar_11(term, c); break;
|
||||||
case SIXEL_DECGCI: decgci(term, c); break;
|
case SIXEL_DECGCI: decgci(term, c); break;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
6
sixel.h
6
sixel.h
|
|
@ -6,10 +6,11 @@
|
||||||
#define SIXEL_MAX_WIDTH 10000u
|
#define SIXEL_MAX_WIDTH 10000u
|
||||||
#define SIXEL_MAX_HEIGHT 10000u
|
#define SIXEL_MAX_HEIGHT 10000u
|
||||||
|
|
||||||
|
typedef void (*sixel_put)(struct terminal *term, uint8_t c);
|
||||||
|
|
||||||
void sixel_fini(struct terminal *term);
|
void sixel_fini(struct terminal *term);
|
||||||
|
|
||||||
void sixel_init(struct terminal *term, int p1, int p2, int p3);
|
sixel_put sixel_init(struct terminal *term, int p1, int p2, int p3);
|
||||||
void sixel_put(struct terminal *term, uint8_t c);
|
|
||||||
void sixel_unhook(struct terminal *term);
|
void sixel_unhook(struct terminal *term);
|
||||||
|
|
||||||
void sixel_destroy(struct sixel *sixel);
|
void sixel_destroy(struct sixel *sixel);
|
||||||
|
|
@ -19,6 +20,7 @@ void sixel_scroll_up(struct terminal *term, int rows);
|
||||||
void sixel_scroll_down(struct terminal *term, int rows);
|
void sixel_scroll_down(struct terminal *term, int rows);
|
||||||
|
|
||||||
void sixel_cell_size_changed(struct terminal *term);
|
void sixel_cell_size_changed(struct terminal *term);
|
||||||
|
void sixel_sync_cache(const struct terminal *term, struct sixel *sixel);
|
||||||
|
|
||||||
void sixel_reflow_grid(struct terminal *term, struct grid *grid);
|
void sixel_reflow_grid(struct terminal *term, struct grid *grid);
|
||||||
|
|
||||||
|
|
|
||||||
6
slave.c
6
slave.c
|
|
@ -21,7 +21,6 @@
|
||||||
#include "macros.h"
|
#include "macros.h"
|
||||||
#include "terminal.h"
|
#include "terminal.h"
|
||||||
#include "tokenize.h"
|
#include "tokenize.h"
|
||||||
#include "version.h"
|
|
||||||
#include "xmalloc.h"
|
#include "xmalloc.h"
|
||||||
|
|
||||||
extern char **environ;
|
extern char **environ;
|
||||||
|
|
@ -352,11 +351,12 @@ slave_spawn(int ptmx, int argc, const char *cwd, char *const *argv,
|
||||||
}
|
}
|
||||||
|
|
||||||
setenv("TERM", term_env, 1);
|
setenv("TERM", term_env, 1);
|
||||||
setenv("TERM_PROGRAM", "foot", 1);
|
|
||||||
setenv("TERM_PROGRAM_VERSION", FOOT_VERSION_SHORT, 1);
|
|
||||||
setenv("COLORTERM", "truecolor", 1);
|
setenv("COLORTERM", "truecolor", 1);
|
||||||
setenv("PWD", cwd, 1);
|
setenv("PWD", cwd, 1);
|
||||||
|
|
||||||
|
unsetenv("TERM_PROGRAM");
|
||||||
|
unsetenv("TERM_PROGRAM_VERSION");
|
||||||
|
|
||||||
#if defined(FOOT_TERMINFO_PATH)
|
#if defined(FOOT_TERMINFO_PATH)
|
||||||
setenv("TERMINFO", FOOT_TERMINFO_PATH, 1);
|
setenv("TERMINFO", FOOT_TERMINFO_PATH, 1);
|
||||||
#endif
|
#endif
|
||||||
|
|
|
||||||
332
terminal.c
332
terminal.c
|
|
@ -47,20 +47,6 @@
|
||||||
|
|
||||||
#define PTMX_TIMING 0
|
#define PTMX_TIMING 0
|
||||||
|
|
||||||
const char *const XCURSOR_HIDDEN = "hidden";
|
|
||||||
const char *const XCURSOR_LEFT_PTR = "left_ptr";
|
|
||||||
const char *const XCURSOR_TEXT = "text";
|
|
||||||
const char *const XCURSOR_TEXT_FALLBACK = "xterm";
|
|
||||||
//const char *const XCURSOR_HAND2 = "hand2";
|
|
||||||
const char *const XCURSOR_TOP_LEFT_CORNER = "top_left_corner";
|
|
||||||
const char *const XCURSOR_TOP_RIGHT_CORNER = "top_right_corner";
|
|
||||||
const char *const XCURSOR_BOTTOM_LEFT_CORNER = "bottom_left_corner";
|
|
||||||
const char *const XCURSOR_BOTTOM_RIGHT_CORNER = "bottom_right_corner";
|
|
||||||
const char *const XCURSOR_LEFT_SIDE = "left_side";
|
|
||||||
const char *const XCURSOR_RIGHT_SIDE = "right_side";
|
|
||||||
const char *const XCURSOR_TOP_SIDE = "top_side";
|
|
||||||
const char *const XCURSOR_BOTTOM_SIDE = "bottom_side";
|
|
||||||
|
|
||||||
static void
|
static void
|
||||||
enqueue_data_for_slave(const void *data, size_t len, size_t offset,
|
enqueue_data_for_slave(const void *data, size_t len, size_t offset,
|
||||||
ptmx_buffer_list_t *buffer_list)
|
ptmx_buffer_list_t *buffer_list)
|
||||||
|
|
@ -207,25 +193,41 @@ fdm_ptmx_out(struct fdm *fdm, int fd, int events, void *data)
|
||||||
static bool
|
static bool
|
||||||
add_utmp_record(const struct config *conf, struct reaper *reaper, int ptmx)
|
add_utmp_record(const struct config *conf, struct reaper *reaper, int ptmx)
|
||||||
{
|
{
|
||||||
|
#if defined(UTMP_ADD)
|
||||||
if (ptmx < 0)
|
if (ptmx < 0)
|
||||||
return true;
|
return true;
|
||||||
if (conf->utempter_path == NULL)
|
if (conf->utmp_helper_path == NULL)
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
char *const argv[] = {conf->utempter_path, "add", getenv("WAYLAND_DISPLAY"), NULL};
|
char *const argv[] = {conf->utmp_helper_path, UTMP_ADD, getenv("WAYLAND_DISPLAY"), NULL};
|
||||||
return spawn(reaper, NULL, argv, ptmx, ptmx, -1, NULL);
|
return spawn(reaper, NULL, argv, ptmx, ptmx, -1, NULL);
|
||||||
|
#else
|
||||||
|
return true;
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool
|
static bool
|
||||||
del_utmp_record(const struct config *conf, struct reaper *reaper, int ptmx)
|
del_utmp_record(const struct config *conf, struct reaper *reaper, int ptmx)
|
||||||
{
|
{
|
||||||
|
#if defined(UTMP_DEL)
|
||||||
if (ptmx < 0)
|
if (ptmx < 0)
|
||||||
return true;
|
return true;
|
||||||
if (conf->utempter_path == NULL)
|
if (conf->utmp_helper_path == NULL)
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
char *const argv[] = {conf->utempter_path, "del", getenv("WAYLAND_DISPLAY"), NULL};
|
char *del_argument =
|
||||||
|
#if defined(UTMP_DEL_HAVE_ARGUMENT)
|
||||||
|
getenv("WAYLAND_DISPLAY")
|
||||||
|
#else
|
||||||
|
NULL
|
||||||
|
#endif
|
||||||
|
;
|
||||||
|
|
||||||
|
char *const argv[] = {conf->utmp_helper_path, UTMP_DEL, del_argument, NULL};
|
||||||
return spawn(reaper, NULL, argv, ptmx, ptmx, -1, NULL);
|
return spawn(reaper, NULL, argv, ptmx, ptmx, -1, NULL);
|
||||||
|
#else
|
||||||
|
return true;
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
#if PTMX_TIMING
|
#if PTMX_TIMING
|
||||||
|
|
@ -406,11 +408,6 @@ fdm_flash(struct fdm *fdm, int fd, int events, void *data)
|
||||||
|
|
||||||
term->flash.active = false;
|
term->flash.active = false;
|
||||||
render_refresh(term);
|
render_refresh(term);
|
||||||
|
|
||||||
/* Work around Sway bug - unmapping a sub-surface does not damage
|
|
||||||
* the underlying surface */
|
|
||||||
term_damage_margins(term);
|
|
||||||
term_damage_view(term);
|
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -736,7 +733,8 @@ term_line_height_update(struct terminal *term)
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool
|
static bool
|
||||||
term_set_fonts(struct terminal *term, struct fcft_font *fonts[static 4])
|
term_set_fonts(struct terminal *term, struct fcft_font *fonts[static 4],
|
||||||
|
bool resize_grid)
|
||||||
{
|
{
|
||||||
for (size_t i = 0; i < 4; i++) {
|
for (size_t i = 0; i < 4; i++) {
|
||||||
xassert(fonts[i] != NULL);
|
xassert(fonts[i] != NULL);
|
||||||
|
|
@ -752,9 +750,6 @@ term_set_fonts(struct terminal *term, struct fcft_font *fonts[static 4])
|
||||||
free_custom_glyphs(
|
free_custom_glyphs(
|
||||||
&term->custom_glyphs.legacy, GLYPH_LEGACY_COUNT);
|
&term->custom_glyphs.legacy, GLYPH_LEGACY_COUNT);
|
||||||
|
|
||||||
const int old_cell_width = term->cell_width;
|
|
||||||
const int old_cell_height = term->cell_height;
|
|
||||||
|
|
||||||
const struct config *conf = term->conf;
|
const struct config *conf = term->conf;
|
||||||
|
|
||||||
const struct fcft_glyph *M = fcft_rasterize_char_utf32(
|
const struct fcft_glyph *M = fcft_rasterize_char_utf32(
|
||||||
|
|
@ -781,31 +776,17 @@ term_set_fonts(struct terminal *term, struct fcft_font *fonts[static 4])
|
||||||
|
|
||||||
LOG_INFO("cell width=%d, height=%d", term->cell_width, term->cell_height);
|
LOG_INFO("cell width=%d, height=%d", term->cell_width, term->cell_height);
|
||||||
|
|
||||||
if (term->cell_width < old_cell_width ||
|
|
||||||
term->cell_height < old_cell_height)
|
|
||||||
{
|
|
||||||
/*
|
|
||||||
* The cell size has decreased.
|
|
||||||
*
|
|
||||||
* This means sixels, which we cannot resize, no longer fit
|
|
||||||
* into their "allocated" grid space.
|
|
||||||
*
|
|
||||||
* To be able to fit them, we would have to change the grid
|
|
||||||
* content. Inserting empty lines _might_ seem acceptable, but
|
|
||||||
* we'd also need to insert empty columns, which would break
|
|
||||||
* existing layout completely.
|
|
||||||
*
|
|
||||||
* So we delete them.
|
|
||||||
*/
|
|
||||||
sixel_destroy_all(term);
|
|
||||||
} else if (term->cell_width != old_cell_width ||
|
|
||||||
term->cell_height != old_cell_height)
|
|
||||||
{
|
|
||||||
sixel_cell_size_changed(term);
|
sixel_cell_size_changed(term);
|
||||||
}
|
|
||||||
|
|
||||||
|
/* Optimization - some code paths (are forced to) call
|
||||||
|
* render_resize() after this function */
|
||||||
|
if (resize_grid) {
|
||||||
/* Use force, since cell-width/height may have changed */
|
/* Use force, since cell-width/height may have changed */
|
||||||
render_resize_force(term, term->width / term->scale, term->height / term->scale);
|
render_resize_force(
|
||||||
|
term,
|
||||||
|
round(term->width / term->scale),
|
||||||
|
round(term->height / term->scale));
|
||||||
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -820,41 +801,34 @@ get_font_dpi(const struct terminal *term)
|
||||||
* Conceptually, we use the physical monitor specs to calculate
|
* Conceptually, we use the physical monitor specs to calculate
|
||||||
* the DPI, and we ignore the output's scaling factor.
|
* the DPI, and we ignore the output's scaling factor.
|
||||||
*
|
*
|
||||||
* However, to deal with fractional scaling, where we're told to
|
* However, to deal with legacy fractional scaling, where we're
|
||||||
* render at e.g. 2x, but are then downscaled by the compositor to
|
* told to render at e.g. 2x, but are then downscaled by the
|
||||||
* e.g. 1.25, we use the scaled DPI value multiplied by the scale
|
* compositor to e.g. 1.25, we use the scaled DPI value multiplied
|
||||||
* factor instead.
|
* by the scale factor instead.
|
||||||
*
|
*
|
||||||
* For integral scaling factors the resulting DPI is the same as
|
* For integral scaling factors the resulting DPI is the same as
|
||||||
* if we had used the physical DPI.
|
* if we had used the physical DPI.
|
||||||
*
|
*
|
||||||
* For fractional scaling factors we'll get a DPI *larger* than
|
* For legacy fractional scaling factors we'll get a DPI *larger*
|
||||||
* the physical DPI, that ends up being right when later
|
* than the physical DPI, that ends up being right when later
|
||||||
* downscaled by the compositor.
|
* 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
|
||||||
|
* scaling factor (no downscaling done by the compositor).
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/* Use highest DPI from outputs we're mapped on */
|
xassert(tll_length(term->wl->monitors) > 0);
|
||||||
double dpi = 0.0;
|
|
||||||
xassert(term->window != NULL);
|
|
||||||
tll_foreach(term->window->on_outputs, it) {
|
|
||||||
if (it->item->dpi > dpi)
|
|
||||||
dpi = it->item->dpi;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* If we're not mapped, use DPI from first monitor. Hopefully this is where we'll get mapped later... */
|
const struct wl_window *win = term->window;
|
||||||
if (dpi == 0.) {
|
const struct monitor *mon = tll_length(win->on_outputs) > 0
|
||||||
tll_foreach(term->wl->monitors, it) {
|
? tll_back(win->on_outputs)
|
||||||
dpi = it->item.dpi;
|
: &tll_front(term->wl->monitors);
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (dpi == 0) {
|
if (wayl_fractional_scaling(term->wl))
|
||||||
/* No monitors? */
|
return mon->dpi.physical;
|
||||||
dpi = 96.;
|
else
|
||||||
}
|
return mon->dpi.scaled;
|
||||||
|
|
||||||
return dpi;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static enum fcft_subpixel
|
static enum fcft_subpixel
|
||||||
|
|
@ -873,7 +847,8 @@ get_font_subpixel(const struct terminal *term)
|
||||||
* output or not.
|
* output or not.
|
||||||
*
|
*
|
||||||
* Thus, when determining which subpixel mode to use, we can't do
|
* Thus, when determining which subpixel mode to use, we can't do
|
||||||
* much but select *an* output. So, we pick the first one.
|
* much but select *an* output. So, we pick the one we were most
|
||||||
|
* recently mapped on.
|
||||||
*
|
*
|
||||||
* If we're not mapped at all, we pick the first available
|
* If we're not mapped at all, we pick the first available
|
||||||
* monitor, and hope that's where we'll eventually get mapped.
|
* monitor, and hope that's where we'll eventually get mapped.
|
||||||
|
|
@ -883,7 +858,7 @@ get_font_subpixel(const struct terminal *term)
|
||||||
*/
|
*/
|
||||||
|
|
||||||
if (tll_length(term->window->on_outputs) > 0)
|
if (tll_length(term->window->on_outputs) > 0)
|
||||||
wl_subpixel = tll_front(term->window->on_outputs)->subpixel;
|
wl_subpixel = tll_back(term->window->on_outputs)->subpixel;
|
||||||
else if (tll_length(term->wl->monitors) > 0)
|
else if (tll_length(term->wl->monitors) > 0)
|
||||||
wl_subpixel = tll_front(term->wl->monitors).subpixel;
|
wl_subpixel = tll_front(term->wl->monitors).subpixel;
|
||||||
else
|
else
|
||||||
|
|
@ -901,42 +876,6 @@ get_font_subpixel(const struct terminal *term)
|
||||||
return FCFT_SUBPIXEL_DEFAULT;
|
return FCFT_SUBPIXEL_DEFAULT;
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool
|
|
||||||
term_font_size_by_dpi(const struct terminal *term)
|
|
||||||
{
|
|
||||||
switch (term->conf->dpi_aware) {
|
|
||||||
case DPI_AWARE_YES: return true;
|
|
||||||
case DPI_AWARE_NO: return false;
|
|
||||||
|
|
||||||
case DPI_AWARE_AUTO:
|
|
||||||
/*
|
|
||||||
* Scale using DPI if all monitors have a scaling factor or 1.
|
|
||||||
*
|
|
||||||
* The idea is this: if a user, with multiple monitors, have
|
|
||||||
* enabled scaling on at least one monitor, then he/she has
|
|
||||||
* most likely done so to match the size of his/hers other
|
|
||||||
* monitors.
|
|
||||||
*
|
|
||||||
* I.e. if the user has one monitor with a scaling factor of
|
|
||||||
* one, and another with a scaling factor of two, he/she
|
|
||||||
* expects things to be twice as large on the second
|
|
||||||
* monitor.
|
|
||||||
*
|
|
||||||
* If we (foot) scale using DPI on the first monitor, and
|
|
||||||
* using the scaling factor on the second monitor, foot will
|
|
||||||
* *not* look twice as big on the second monitor.
|
|
||||||
*/
|
|
||||||
tll_foreach(term->wl->monitors, it) {
|
|
||||||
const struct monitor *mon = &it->item;
|
|
||||||
if (mon->scale > 1)
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
BUG("unhandled DPI awareness value");
|
|
||||||
}
|
|
||||||
|
|
||||||
int
|
int
|
||||||
term_pt_or_px_as_pixels(const struct terminal *term,
|
term_pt_or_px_as_pixels(const struct terminal *term,
|
||||||
const struct pt_or_px *pt_or_px)
|
const struct pt_or_px *pt_or_px)
|
||||||
|
|
@ -966,7 +905,7 @@ font_loader_thread(void *_data)
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool
|
static bool
|
||||||
reload_fonts(struct terminal *term)
|
reload_fonts(struct terminal *term, bool resize_grid)
|
||||||
{
|
{
|
||||||
const struct config *conf = term->conf;
|
const struct config *conf = term->conf;
|
||||||
|
|
||||||
|
|
@ -989,14 +928,14 @@ reload_fonts(struct terminal *term)
|
||||||
bool use_px_size = term->font_sizes[i][j].px_size > 0;
|
bool use_px_size = term->font_sizes[i][j].px_size > 0;
|
||||||
char size[64];
|
char size[64];
|
||||||
|
|
||||||
const int scale = term->font_is_sized_by_dpi ? 1 : term->scale;
|
const float scale = term->font_is_sized_by_dpi ? 1. : term->scale;
|
||||||
|
|
||||||
if (use_px_size)
|
if (use_px_size)
|
||||||
snprintf(size, sizeof(size), ":pixelsize=%d",
|
snprintf(size, sizeof(size), ":pixelsize=%d",
|
||||||
term->font_sizes[i][j].px_size * scale);
|
(int)round(term->font_sizes[i][j].px_size * scale));
|
||||||
else
|
else
|
||||||
snprintf(size, sizeof(size), ":size=%.2f",
|
snprintf(size, sizeof(size), ":size=%.2f",
|
||||||
term->font_sizes[i][j].pt_size * (double)scale);
|
term->font_sizes[i][j].pt_size * scale);
|
||||||
|
|
||||||
size_t len = strlen(font->pattern) + strlen(size) + 1;
|
size_t len = strlen(font->pattern) + strlen(size) + 1;
|
||||||
names[i][j] = xmalloc(len);
|
names[i][j] = xmalloc(len);
|
||||||
|
|
@ -1093,7 +1032,7 @@ reload_fonts(struct terminal *term)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return success ? term_set_fonts(term, fonts) : success;
|
return success ? term_set_fonts(term, fonts, resize_grid) : success;
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool
|
static bool
|
||||||
|
|
@ -1111,7 +1050,7 @@ load_fonts_from_conf(struct terminal *term)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return reload_fonts(term);
|
return reload_fonts(term, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void fdm_client_terminated(
|
static void fdm_client_terminated(
|
||||||
|
|
@ -1221,7 +1160,7 @@ term_init(const struct config *conf, struct fdm *fdm, struct reaper *reaper,
|
||||||
.reverse_wrap = true,
|
.reverse_wrap = true,
|
||||||
.auto_margin = true,
|
.auto_margin = true,
|
||||||
.window_title_stack = tll_init(),
|
.window_title_stack = tll_init(),
|
||||||
.scale = 1,
|
.scale = 1.,
|
||||||
.flash = {.fd = flash_fd},
|
.flash = {.fd = flash_fd},
|
||||||
.blink = {.fd = -1},
|
.blink = {.fd = -1},
|
||||||
.vt = {
|
.vt = {
|
||||||
|
|
@ -1345,11 +1284,9 @@ term_init(const struct config *conf, struct fdm *fdm, struct reaper *reaper,
|
||||||
reaper_add(term->reaper, term->slave, &fdm_client_terminated, term);
|
reaper_add(term->reaper, term->slave, &fdm_client_terminated, term);
|
||||||
|
|
||||||
/* Guess scale; we're not mapped yet, so we don't know on which
|
/* Guess scale; we're not mapped yet, so we don't know on which
|
||||||
* output we'll be. Pick highest scale we find for now */
|
* output we'll be. Use scaling factor from first monitor */
|
||||||
tll_foreach(term->wl->monitors, it) {
|
xassert(tll_length(term->wl->monitors) > 0);
|
||||||
if (it->item.scale > term->scale)
|
term->scale = tll_front(term->wl->monitors).scale;
|
||||||
term->scale = it->item.scale;
|
|
||||||
}
|
|
||||||
|
|
||||||
memcpy(term->colors.table, term->conf->colors.table, sizeof(term->colors.table));
|
memcpy(term->colors.table, term->conf->colors.table, sizeof(term->colors.table));
|
||||||
|
|
||||||
|
|
@ -1358,7 +1295,7 @@ term_init(const struct config *conf, struct fdm *fdm, struct reaper *reaper,
|
||||||
goto err;
|
goto err;
|
||||||
|
|
||||||
/* Load fonts */
|
/* Load fonts */
|
||||||
if (!term_font_dpi_changed(term, 0))
|
if (!term_font_dpi_changed(term, 0.))
|
||||||
goto err;
|
goto err;
|
||||||
|
|
||||||
term->font_subpixel = get_font_subpixel(term);
|
term->font_subpixel = get_font_subpixel(term);
|
||||||
|
|
@ -1670,8 +1607,6 @@ term_destroy(struct terminal *term)
|
||||||
if (term == NULL)
|
if (term == NULL)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
key_binding_unref(term->wl->key_binding_manager, term->conf);
|
|
||||||
|
|
||||||
tll_foreach(term->wl->terms, it) {
|
tll_foreach(term->wl->terms, it) {
|
||||||
if (it->item == term) {
|
if (it->item == term) {
|
||||||
tll_remove(term->wl->terms, it);
|
tll_remove(term->wl->terms, it);
|
||||||
|
|
@ -1717,6 +1652,8 @@ term_destroy(struct terminal *term)
|
||||||
}
|
}
|
||||||
mtx_unlock(&term->render.workers.lock);
|
mtx_unlock(&term->render.workers.lock);
|
||||||
|
|
||||||
|
key_binding_unref(term->wl->key_binding_manager, term->conf);
|
||||||
|
|
||||||
urls_reset(term);
|
urls_reset(term);
|
||||||
|
|
||||||
free(term->vt.osc.data);
|
free(term->vt.osc.data);
|
||||||
|
|
@ -1933,6 +1870,7 @@ term_reset(struct terminal *term, bool hard)
|
||||||
|
|
||||||
term_set_user_mouse_cursor(term, NULL);
|
term_set_user_mouse_cursor(term, NULL);
|
||||||
|
|
||||||
|
term->modify_other_keys_2 = false;
|
||||||
memset(term->normal.kitty_kbd.flags, 0, sizeof(term->normal.kitty_kbd.flags));
|
memset(term->normal.kitty_kbd.flags, 0, sizeof(term->normal.kitty_kbd.flags));
|
||||||
memset(term->alt.kitty_kbd.flags, 0, sizeof(term->alt.kitty_kbd.flags));
|
memset(term->alt.kitty_kbd.flags, 0, sizeof(term->alt.kitty_kbd.flags));
|
||||||
term->normal.kitty_kbd.idx = term->alt.kitty_kbd.idx = 0;
|
term->normal.kitty_kbd.idx = term->alt.kitty_kbd.idx = 0;
|
||||||
|
|
@ -2055,7 +1993,7 @@ term_font_size_adjust_by_points(struct terminal *term, float amount)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return reload_fonts(term);
|
return reload_fonts(term, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool
|
static bool
|
||||||
|
|
@ -2078,7 +2016,7 @@ term_font_size_adjust_by_pixels(struct terminal *term, int amount)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return reload_fonts(term);
|
return reload_fonts(term, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool
|
static bool
|
||||||
|
|
@ -2102,7 +2040,7 @@ term_font_size_adjust_by_percent(struct terminal *term, bool increment, float pe
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return reload_fonts(term);
|
return reload_fonts(term, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool
|
bool
|
||||||
|
|
@ -2140,13 +2078,43 @@ term_font_size_reset(struct terminal *term)
|
||||||
}
|
}
|
||||||
|
|
||||||
bool
|
bool
|
||||||
term_font_dpi_changed(struct terminal *term, int old_scale)
|
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
|
||||||
|
* the scale in the following order:
|
||||||
|
*
|
||||||
|
* - “preferred” scale, from the fractional-scale-v1 protocol
|
||||||
|
* - scaling factor of output we most recently were mapped on
|
||||||
|
* - if we’re not mapped, use the scaling factor from the first
|
||||||
|
* available output.
|
||||||
|
* - if there aren’t any outputs available, use 1.0
|
||||||
|
*/
|
||||||
|
const float new_scale =
|
||||||
|
(wayl_fractional_scaling(term->wl) && win->scale > 0.
|
||||||
|
? win->scale
|
||||||
|
: (tll_length(win->on_outputs) > 0
|
||||||
|
? tll_back(win->on_outputs)->scale
|
||||||
|
: 1.));
|
||||||
|
|
||||||
|
if (new_scale == term->scale)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
LOG_DBG("scaling factor changed: %.2f -> %.2f", term->scale, new_scale);
|
||||||
|
term->scale = new_scale;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool
|
||||||
|
term_font_dpi_changed(struct terminal *term, float old_scale)
|
||||||
{
|
{
|
||||||
float dpi = get_font_dpi(term);
|
float dpi = get_font_dpi(term);
|
||||||
xassert(term->scale > 0);
|
xassert(term->scale > 0.);
|
||||||
|
|
||||||
bool was_scaled_using_dpi = term->font_is_sized_by_dpi;
|
bool was_scaled_using_dpi = term->font_is_sized_by_dpi;
|
||||||
bool will_scale_using_dpi = term_font_size_by_dpi(term);
|
bool will_scale_using_dpi = term->conf->dpi_aware;
|
||||||
|
|
||||||
bool need_font_reload =
|
bool need_font_reload =
|
||||||
was_scaled_using_dpi != will_scale_using_dpi ||
|
was_scaled_using_dpi != will_scale_using_dpi ||
|
||||||
|
|
@ -2155,11 +2123,10 @@ term_font_dpi_changed(struct terminal *term, int old_scale)
|
||||||
: old_scale != term->scale);
|
: old_scale != term->scale);
|
||||||
|
|
||||||
if (need_font_reload) {
|
if (need_font_reload) {
|
||||||
LOG_DBG("DPI/scale change: DPI-awareness=%s, "
|
LOG_DBG("DPI/scale change: DPI-aware=%s, "
|
||||||
"DPI: %.2f -> %.2f, scale: %d -> %d, "
|
"DPI: %.2f -> %.2f, scale: %.2f -> %.2f, "
|
||||||
"sizing font based on monitor's %s",
|
"sizing font based on monitor's %s",
|
||||||
term->conf->dpi_aware == DPI_AWARE_AUTO ? "auto" :
|
term->conf->dpi_aware ? "yes" : "no",
|
||||||
term->conf->dpi_aware == DPI_AWARE_YES ? "yes" : "no",
|
|
||||||
term->font_dpi, dpi, old_scale, term->scale,
|
term->font_dpi, dpi, old_scale, term->scale,
|
||||||
will_scale_using_dpi ? "DPI" : "scaling factor");
|
will_scale_using_dpi ? "DPI" : "scaling factor");
|
||||||
}
|
}
|
||||||
|
|
@ -2168,9 +2135,9 @@ term_font_dpi_changed(struct terminal *term, int old_scale)
|
||||||
term->font_is_sized_by_dpi = will_scale_using_dpi;
|
term->font_is_sized_by_dpi = will_scale_using_dpi;
|
||||||
|
|
||||||
if (!need_font_reload)
|
if (!need_font_reload)
|
||||||
return true;
|
return false;
|
||||||
|
|
||||||
return reload_fonts(term);
|
return reload_fonts(term, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
|
|
@ -2545,6 +2512,15 @@ term_cursor_home(struct terminal *term)
|
||||||
term_cursor_to(term, term_row_rel_to_abs(term, 0), 0);
|
term_cursor_to(term, term_row_rel_to_abs(term, 0), 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
term_cursor_col(struct terminal *term, int col)
|
||||||
|
{
|
||||||
|
xassert(col < term->cols);
|
||||||
|
|
||||||
|
term->grid->cursor.lcf = false;
|
||||||
|
term->grid->cursor.point.col = col;
|
||||||
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
term_cursor_left(struct terminal *term, int count)
|
term_cursor_left(struct terminal *term, int count)
|
||||||
{
|
{
|
||||||
|
|
@ -2693,6 +2669,7 @@ term_scroll_partial(struct terminal *term, struct scroll_region region, int rows
|
||||||
term->grid->offset &= term->grid->num_rows - 1;
|
term->grid->offset &= term->grid->num_rows - 1;
|
||||||
|
|
||||||
if (likely(view_follows)) {
|
if (likely(view_follows)) {
|
||||||
|
term_damage_scroll(term, DAMAGE_SCROLL, region, rows);
|
||||||
selection_view_down(term, term->grid->offset);
|
selection_view_down(term, term->grid->offset);
|
||||||
term->grid->view = term->grid->offset;
|
term->grid->view = term->grid->offset;
|
||||||
} else if (unlikely(rows > view_sb_start_distance)) {
|
} else if (unlikely(rows > view_sb_start_distance)) {
|
||||||
|
|
@ -2716,13 +2693,12 @@ term_scroll_partial(struct terminal *term, struct scroll_region region, int rows
|
||||||
erase_line(term, row);
|
erase_line(term, row);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
term->grid->cur_row = grid_row(term->grid, term->grid->cursor.point.row);
|
||||||
|
|
||||||
#if defined(_DEBUG)
|
#if defined(_DEBUG)
|
||||||
for (int r = 0; r < term->rows; r++)
|
for (int r = 0; r < term->rows; r++)
|
||||||
xassert(grid_row(term->grid, r) != NULL);
|
xassert(grid_row(term->grid, r) != NULL);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
term_damage_scroll(term, DAMAGE_SCROLL, region, rows);
|
|
||||||
term->grid->cur_row = grid_row(term->grid, term->grid->cursor.point.row);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
|
|
@ -2779,6 +2755,7 @@ term_scroll_reverse_partial(struct terminal *term,
|
||||||
xassert(term->grid->offset < term->grid->num_rows);
|
xassert(term->grid->offset < term->grid->num_rows);
|
||||||
|
|
||||||
if (view_follows) {
|
if (view_follows) {
|
||||||
|
term_damage_scroll(term, DAMAGE_SCROLL_REVERSE, region, rows);
|
||||||
selection_view_up(term, term->grid->offset);
|
selection_view_up(term, term->grid->offset);
|
||||||
term->grid->view = term->grid->offset;
|
term->grid->view = term->grid->offset;
|
||||||
}
|
}
|
||||||
|
|
@ -2797,7 +2774,6 @@ term_scroll_reverse_partial(struct terminal *term,
|
||||||
erase_line(term, row);
|
erase_line(term, row);
|
||||||
}
|
}
|
||||||
|
|
||||||
term_damage_scroll(term, DAMAGE_SCROLL_REVERSE, region, rows);
|
|
||||||
term->grid->cur_row = grid_row(term->grid, term->grid->cursor.point.row);
|
term->grid->cur_row = grid_row(term->grid, term->grid->cursor.point.row);
|
||||||
|
|
||||||
#if defined(_DEBUG)
|
#if defined(_DEBUG)
|
||||||
|
|
@ -3176,44 +3152,56 @@ term_mouse_motion(struct terminal *term, int button, int row, int col,
|
||||||
void
|
void
|
||||||
term_xcursor_update_for_seat(struct terminal *term, struct seat *seat)
|
term_xcursor_update_for_seat(struct terminal *term, struct seat *seat)
|
||||||
{
|
{
|
||||||
const char *xcursor = NULL;
|
enum cursor_shape shape = CURSOR_SHAPE_NONE;
|
||||||
|
|
||||||
switch (term->active_surface) {
|
switch (term->active_surface) {
|
||||||
case TERM_SURF_GRID: {
|
case TERM_SURF_GRID:
|
||||||
bool have_custom_cursor =
|
if (seat->pointer.hidden)
|
||||||
render_xcursor_is_valid(seat, term->mouse_user_cursor);
|
shape = CURSOR_SHAPE_HIDDEN;
|
||||||
|
|
||||||
xcursor = seat->pointer.hidden ? XCURSOR_HIDDEN
|
#if defined(HAVE_CURSOR_SHAPE)
|
||||||
: have_custom_cursor ? term->mouse_user_cursor
|
else if (cursor_string_to_server_shape(term->mouse_user_cursor) != 0
|
||||||
: term->is_searching ? XCURSOR_LEFT_PTR
|
#else
|
||||||
: (seat->mouse.col >= 0 &&
|
else if (false
|
||||||
seat->mouse.row >= 0 &&
|
#endif
|
||||||
term_mouse_grabbed(term, seat)) ? XCURSOR_TEXT
|
|| render_xcursor_is_valid(seat, term->mouse_user_cursor))
|
||||||
: XCURSOR_LEFT_PTR;
|
{
|
||||||
break;
|
shape = CURSOR_SHAPE_CUSTOM;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
else if (seat->mouse.col >= 0 &&
|
||||||
|
seat->mouse.row >= 0 &&
|
||||||
|
term_mouse_grabbed(term, seat))
|
||||||
|
{
|
||||||
|
shape = CURSOR_SHAPE_TEXT;
|
||||||
|
}
|
||||||
|
|
||||||
|
else
|
||||||
|
shape = CURSOR_SHAPE_LEFT_PTR;
|
||||||
|
break;
|
||||||
|
|
||||||
case TERM_SURF_TITLE:
|
case TERM_SURF_TITLE:
|
||||||
case TERM_SURF_BUTTON_MINIMIZE:
|
case TERM_SURF_BUTTON_MINIMIZE:
|
||||||
case TERM_SURF_BUTTON_MAXIMIZE:
|
case TERM_SURF_BUTTON_MAXIMIZE:
|
||||||
case TERM_SURF_BUTTON_CLOSE:
|
case TERM_SURF_BUTTON_CLOSE:
|
||||||
xcursor = XCURSOR_LEFT_PTR;
|
shape = CURSOR_SHAPE_LEFT_PTR;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case TERM_SURF_BORDER_LEFT:
|
case TERM_SURF_BORDER_LEFT:
|
||||||
case TERM_SURF_BORDER_RIGHT:
|
case TERM_SURF_BORDER_RIGHT:
|
||||||
case TERM_SURF_BORDER_TOP:
|
case TERM_SURF_BORDER_TOP:
|
||||||
case TERM_SURF_BORDER_BOTTOM:
|
case TERM_SURF_BORDER_BOTTOM:
|
||||||
xcursor = xcursor_for_csd_border(term, seat->mouse.x, seat->mouse.y);
|
shape = xcursor_for_csd_border(term, seat->mouse.x, seat->mouse.y);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case TERM_SURF_NONE:
|
case TERM_SURF_NONE:
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (xcursor == NULL)
|
if (shape == CURSOR_SHAPE_NONE)
|
||||||
BUG("xcursor not set");
|
BUG("xcursor not set");
|
||||||
|
|
||||||
render_xcursor_set(seat, term, xcursor);
|
render_xcursor_set(seat, term, shape);
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
|
|
@ -3548,7 +3536,7 @@ term_update_ascii_printer(struct terminal *term)
|
||||||
|
|
||||||
#if defined(_DEBUG) && LOG_ENABLE_DBG
|
#if defined(_DEBUG) && LOG_ENABLE_DBG
|
||||||
if (term->ascii_printer != new_printer) {
|
if (term->ascii_printer != new_printer) {
|
||||||
LOG_DBG("§switching ASCII printer %s -> %s",
|
LOG_DBG("switching ASCII printer %s -> %s",
|
||||||
term->ascii_printer == &ascii_printer_fast ? "fast" : "generic",
|
term->ascii_printer == &ascii_printer_fast ? "fast" : "generic",
|
||||||
new_printer == &ascii_printer_fast ? "fast" : "generic");
|
new_printer == &ascii_printer_fast ? "fast" : "generic");
|
||||||
}
|
}
|
||||||
|
|
@ -3568,23 +3556,23 @@ term_single_shift(struct terminal *term, enum charset_designator idx)
|
||||||
enum term_surface
|
enum term_surface
|
||||||
term_surface_kind(const struct terminal *term, const struct wl_surface *surface)
|
term_surface_kind(const struct terminal *term, const struct wl_surface *surface)
|
||||||
{
|
{
|
||||||
if (likely(surface == term->window->surface))
|
if (likely(surface == term->window->surface.surf))
|
||||||
return TERM_SURF_GRID;
|
return TERM_SURF_GRID;
|
||||||
else if (surface == term->window->csd.surface[CSD_SURF_TITLE].surf)
|
else if (surface == term->window->csd.surface[CSD_SURF_TITLE].surface.surf)
|
||||||
return TERM_SURF_TITLE;
|
return TERM_SURF_TITLE;
|
||||||
else if (surface == term->window->csd.surface[CSD_SURF_LEFT].surf)
|
else if (surface == term->window->csd.surface[CSD_SURF_LEFT].surface.surf)
|
||||||
return TERM_SURF_BORDER_LEFT;
|
return TERM_SURF_BORDER_LEFT;
|
||||||
else if (surface == term->window->csd.surface[CSD_SURF_RIGHT].surf)
|
else if (surface == term->window->csd.surface[CSD_SURF_RIGHT].surface.surf)
|
||||||
return TERM_SURF_BORDER_RIGHT;
|
return TERM_SURF_BORDER_RIGHT;
|
||||||
else if (surface == term->window->csd.surface[CSD_SURF_TOP].surf)
|
else if (surface == term->window->csd.surface[CSD_SURF_TOP].surface.surf)
|
||||||
return TERM_SURF_BORDER_TOP;
|
return TERM_SURF_BORDER_TOP;
|
||||||
else if (surface == term->window->csd.surface[CSD_SURF_BOTTOM].surf)
|
else if (surface == term->window->csd.surface[CSD_SURF_BOTTOM].surface.surf)
|
||||||
return TERM_SURF_BORDER_BOTTOM;
|
return TERM_SURF_BORDER_BOTTOM;
|
||||||
else if (surface == term->window->csd.surface[CSD_SURF_MINIMIZE].surf)
|
else if (surface == term->window->csd.surface[CSD_SURF_MINIMIZE].surface.surf)
|
||||||
return TERM_SURF_BUTTON_MINIMIZE;
|
return TERM_SURF_BUTTON_MINIMIZE;
|
||||||
else if (surface == term->window->csd.surface[CSD_SURF_MAXIMIZE].surf)
|
else if (surface == term->window->csd.surface[CSD_SURF_MAXIMIZE].surface.surf)
|
||||||
return TERM_SURF_BUTTON_MAXIMIZE;
|
return TERM_SURF_BUTTON_MAXIMIZE;
|
||||||
else if (surface == term->window->csd.surface[CSD_SURF_CLOSE].surf)
|
else if (surface == term->window->csd.surface[CSD_SURF_CLOSE].surface.surf)
|
||||||
return TERM_SURF_BUTTON_CLOSE;
|
return TERM_SURF_BUTTON_CLOSE;
|
||||||
else
|
else
|
||||||
return TERM_SURF_NONE;
|
return TERM_SURF_NONE;
|
||||||
|
|
@ -3764,6 +3752,8 @@ void
|
||||||
term_set_user_mouse_cursor(struct terminal *term, const char *cursor)
|
term_set_user_mouse_cursor(struct terminal *term, const char *cursor)
|
||||||
{
|
{
|
||||||
free(term->mouse_user_cursor);
|
free(term->mouse_user_cursor);
|
||||||
term->mouse_user_cursor = cursor != NULL ? xstrdup(cursor) : NULL;
|
term->mouse_user_cursor = cursor != NULL && strlen(cursor) > 0
|
||||||
|
? xstrdup(cursor)
|
||||||
|
: NULL;
|
||||||
term_xcursor_update(term);
|
term_xcursor_update(term);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
75
terminal.h
75
terminal.h
|
|
@ -126,14 +126,48 @@ struct row {
|
||||||
};
|
};
|
||||||
|
|
||||||
struct sixel {
|
struct sixel {
|
||||||
void *data;
|
/*
|
||||||
|
* These three members reflect the "current", maybe scaled version
|
||||||
|
* of the image.
|
||||||
|
*
|
||||||
|
* The values will either be NULL/-1/-1, or match either the
|
||||||
|
* values in "original", or "scaled".
|
||||||
|
*
|
||||||
|
* They are typically reset when we need to invalidate the cached
|
||||||
|
* version (e.g. when the cell dimensions change).
|
||||||
|
*/
|
||||||
pixman_image_t *pix;
|
pixman_image_t *pix;
|
||||||
int width;
|
int width;
|
||||||
int height;
|
int height;
|
||||||
|
|
||||||
int rows;
|
int rows;
|
||||||
int cols;
|
int cols;
|
||||||
struct coord pos;
|
struct coord pos;
|
||||||
bool opaque;
|
bool opaque;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* 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’.
|
||||||
|
*/
|
||||||
|
int cell_width;
|
||||||
|
int cell_height;
|
||||||
|
|
||||||
|
struct {
|
||||||
|
void *data;
|
||||||
|
pixman_image_t *pix;
|
||||||
|
int width;
|
||||||
|
int height;
|
||||||
|
} original;
|
||||||
|
|
||||||
|
struct {
|
||||||
|
void *data;
|
||||||
|
pixman_image_t *pix;
|
||||||
|
int width;
|
||||||
|
int height;
|
||||||
|
} scaled;
|
||||||
};
|
};
|
||||||
|
|
||||||
enum kitty_kbd_flags {
|
enum kitty_kbd_flags {
|
||||||
|
|
@ -180,8 +214,10 @@ struct grid {
|
||||||
};
|
};
|
||||||
|
|
||||||
struct vt_subparams {
|
struct vt_subparams {
|
||||||
unsigned value[16];
|
|
||||||
uint8_t idx;
|
uint8_t idx;
|
||||||
|
unsigned *cur;
|
||||||
|
unsigned value[16];
|
||||||
|
unsigned dummy;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct vt_param {
|
struct vt_param {
|
||||||
|
|
@ -197,8 +233,10 @@ struct vt {
|
||||||
#endif
|
#endif
|
||||||
char32_t utf8;
|
char32_t utf8;
|
||||||
struct {
|
struct {
|
||||||
struct vt_param v[16];
|
|
||||||
uint8_t idx;
|
uint8_t idx;
|
||||||
|
struct vt_param *cur;
|
||||||
|
struct vt_param v[16];
|
||||||
|
struct vt_param dummy;
|
||||||
} params;
|
} params;
|
||||||
|
|
||||||
uint32_t private; /* LSB=priv0, MSB=priv3 */
|
uint32_t private; /* LSB=priv0, MSB=priv3 */
|
||||||
|
|
@ -450,7 +488,7 @@ struct terminal {
|
||||||
int fd;
|
int fd;
|
||||||
} blink;
|
} blink;
|
||||||
|
|
||||||
int scale;
|
float scale;
|
||||||
int width; /* pixels */
|
int width; /* pixels */
|
||||||
int height; /* pixels */
|
int height; /* pixels */
|
||||||
int stashed_width;
|
int stashed_width;
|
||||||
|
|
@ -616,7 +654,6 @@ struct terminal {
|
||||||
} state;
|
} state;
|
||||||
|
|
||||||
struct coord pos; /* Current sixel coordinate */
|
struct coord pos; /* Current sixel coordinate */
|
||||||
int max_non_empty_row_no;
|
|
||||||
size_t row_byte_ofs; /* Byte position into image, for current row */
|
size_t row_byte_ofs; /* Byte position into image, for current row */
|
||||||
int color_idx; /* Current palette index */
|
int color_idx; /* Current palette index */
|
||||||
uint32_t *private_palette; /* Private palette, used when private mode 1070 is enabled */
|
uint32_t *private_palette; /* Private palette, used when private mode 1070 is enabled */
|
||||||
|
|
@ -630,6 +667,15 @@ struct terminal {
|
||||||
int height; /* Image height, in pixels */
|
int height; /* Image height, in pixels */
|
||||||
} image;
|
} image;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Pan is the vertical shape of a pixel
|
||||||
|
* Pad is the horizontal shape of a pixel
|
||||||
|
*
|
||||||
|
* pan/pad is the sixel’s aspect ratio
|
||||||
|
*/
|
||||||
|
int pan;
|
||||||
|
int pad;
|
||||||
|
|
||||||
bool scrolling:1; /* Private mode 80 */
|
bool scrolling:1; /* Private mode 80 */
|
||||||
bool use_private_palette:1; /* Private mode 1070 */
|
bool use_private_palette:1; /* Private mode 1070 */
|
||||||
bool cursor_right_of_graphics:1; /* Private mode 8452 */
|
bool cursor_right_of_graphics:1; /* Private mode 8452 */
|
||||||
|
|
@ -637,6 +683,7 @@ struct terminal {
|
||||||
unsigned params[5]; /* Collected parameters, for RASTER, COLOR_SPEC */
|
unsigned params[5]; /* Collected parameters, for RASTER, COLOR_SPEC */
|
||||||
unsigned param; /* Currently collecting parameter, for RASTER, COLOR_SPEC and REPEAT */
|
unsigned param; /* Currently collecting parameter, for RASTER, COLOR_SPEC and REPEAT */
|
||||||
unsigned param_idx; /* Parameters seen */
|
unsigned param_idx; /* Parameters seen */
|
||||||
|
unsigned repeat_count;
|
||||||
|
|
||||||
bool transparent_bg;
|
bool transparent_bg;
|
||||||
uint32_t default_bg;
|
uint32_t default_bg;
|
||||||
|
|
@ -671,20 +718,6 @@ struct terminal {
|
||||||
char *cwd;
|
char *cwd;
|
||||||
};
|
};
|
||||||
|
|
||||||
extern const char *const XCURSOR_HIDDEN;
|
|
||||||
extern const char *const XCURSOR_LEFT_PTR;
|
|
||||||
extern const char *const XCURSOR_TEXT;
|
|
||||||
extern const char *const XCURSOR_TEXT_FALLBACK;
|
|
||||||
//extern const char *const XCURSOR_HAND2;
|
|
||||||
extern const char *const XCURSOR_TOP_LEFT_CORNER;
|
|
||||||
extern const char *const XCURSOR_TOP_RIGHT_CORNER;
|
|
||||||
extern const char *const XCURSOR_BOTTOM_LEFT_CORNER;
|
|
||||||
extern const char *const XCURSOR_BOTTOM_RIGHT_CORNER;
|
|
||||||
extern const char *const XCURSOR_LEFT_SIDE;
|
|
||||||
extern const char *const XCURSOR_RIGHT_SIDE;
|
|
||||||
extern const char *const XCURSOR_TOP_SIDE;
|
|
||||||
extern const char *const XCURSOR_BOTTOM_SIDE;
|
|
||||||
|
|
||||||
struct config;
|
struct config;
|
||||||
struct terminal *term_init(
|
struct terminal *term_init(
|
||||||
const struct config *conf, struct fdm *fdm, struct reaper *reaper,
|
const struct config *conf, struct fdm *fdm, struct reaper *reaper,
|
||||||
|
|
@ -703,10 +736,11 @@ bool term_to_slave(struct terminal *term, const void *data, size_t len);
|
||||||
bool term_paste_data_to_slave(
|
bool term_paste_data_to_slave(
|
||||||
struct terminal *term, const void *data, size_t len);
|
struct terminal *term, const void *data, size_t len);
|
||||||
|
|
||||||
|
bool term_update_scale(struct terminal *term);
|
||||||
bool term_font_size_increase(struct terminal *term);
|
bool term_font_size_increase(struct terminal *term);
|
||||||
bool term_font_size_decrease(struct terminal *term);
|
bool term_font_size_decrease(struct terminal *term);
|
||||||
bool term_font_size_reset(struct terminal *term);
|
bool term_font_size_reset(struct terminal *term);
|
||||||
bool term_font_dpi_changed(struct terminal *term, int old_scale);
|
bool term_font_dpi_changed(struct terminal *term, float old_scale);
|
||||||
void term_font_subpixel_changed(struct terminal *term);
|
void term_font_subpixel_changed(struct terminal *term);
|
||||||
|
|
||||||
int term_pt_or_px_as_pixels(
|
int term_pt_or_px_as_pixels(
|
||||||
|
|
@ -739,6 +773,7 @@ void term_erase_scrollback(struct terminal *term);
|
||||||
int term_row_rel_to_abs(const struct terminal *term, int row);
|
int term_row_rel_to_abs(const struct terminal *term, int row);
|
||||||
void term_cursor_home(struct terminal *term);
|
void term_cursor_home(struct terminal *term);
|
||||||
void term_cursor_to(struct terminal *term, int row, int col);
|
void term_cursor_to(struct terminal *term, int row, int col);
|
||||||
|
void term_cursor_col(struct terminal *term, int col);
|
||||||
void term_cursor_left(struct terminal *term, int count);
|
void term_cursor_left(struct terminal *term, int count);
|
||||||
void term_cursor_right(struct terminal *term, int count);
|
void term_cursor_right(struct terminal *term, int count);
|
||||||
void term_cursor_up(struct terminal *term, int count);
|
void term_cursor_up(struct terminal *term, int count);
|
||||||
|
|
|
||||||
|
|
@ -106,6 +106,50 @@ test_c32string(struct context *ctx, bool (*parse_fun)(struct context *ctx),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
test_protocols(struct context *ctx, bool (*parse_fun)(struct context *ctx),
|
||||||
|
const char *key, char32_t **const *ptr)
|
||||||
|
{
|
||||||
|
ctx->key = key;
|
||||||
|
|
||||||
|
static const struct {
|
||||||
|
const char *option_string;
|
||||||
|
int count;
|
||||||
|
const char32_t *value[2];
|
||||||
|
bool invalid;
|
||||||
|
} input[] = {
|
||||||
|
{""},
|
||||||
|
{"http", 1, {U"http://"}},
|
||||||
|
{" http", 1, {U"http://"}},
|
||||||
|
{"http, https", 2, {U"http://", U"https://"}},
|
||||||
|
{"longprotocolislong", 1, {U"longprotocolislong://"}},
|
||||||
|
};
|
||||||
|
|
||||||
|
for (size_t i = 0; i < ALEN(input); i++) {
|
||||||
|
ctx->value = input[i].option_string;
|
||||||
|
|
||||||
|
if (input[i].invalid) {
|
||||||
|
if (parse_fun(ctx)) {
|
||||||
|
BUG("[%s].%s=%s: did not fail to parse as expected",
|
||||||
|
ctx->section, ctx->key, &ctx->value[0]);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (!parse_fun(ctx)) {
|
||||||
|
BUG("[%s].%s=%s: failed to parse",
|
||||||
|
ctx->section, ctx->key, &ctx->value[0]);
|
||||||
|
}
|
||||||
|
for (int c = 0; c < input[i].count; c++) {
|
||||||
|
if (c32cmp((*ptr)[c], input[i].value[c]) != 0) {
|
||||||
|
BUG("[%s].%s=%s: set value[%d] (%ls) not the expected one (%ls)",
|
||||||
|
ctx->section, ctx->key, &ctx->value[c], c,
|
||||||
|
(const wchar_t *)(*ptr)[c],
|
||||||
|
(const wchar_t *)input[i].value[c]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
test_boolean(struct context *ctx, bool (*parse_fun)(struct context *ctx),
|
test_boolean(struct context *ctx, bool (*parse_fun)(struct context *ctx),
|
||||||
const char *key, const bool *ptr)
|
const char *key, const bool *ptr)
|
||||||
|
|
@ -458,7 +502,8 @@ test_section_main(void)
|
||||||
test_string(&ctx, &parse_section_main, "shell", &conf.shell);
|
test_string(&ctx, &parse_section_main, "shell", &conf.shell);
|
||||||
test_string(&ctx, &parse_section_main, "term", &conf.term);
|
test_string(&ctx, &parse_section_main, "term", &conf.term);
|
||||||
test_string(&ctx, &parse_section_main, "app-id", &conf.app_id);
|
test_string(&ctx, &parse_section_main, "app-id", &conf.app_id);
|
||||||
test_string(&ctx, &parse_section_main, "utempter", &conf.utempter_path);
|
test_string(&ctx, &parse_section_main, "utempter", &conf.utmp_helper_path);
|
||||||
|
test_string(&ctx, &parse_section_main, "utmp-helper", &conf.utmp_helper_path);
|
||||||
|
|
||||||
test_c32string(&ctx, &parse_section_main, "word-delimiters", &conf.word_delimiters);
|
test_c32string(&ctx, &parse_section_main, "word-delimiters", &conf.word_delimiters);
|
||||||
|
|
||||||
|
|
@ -466,6 +511,7 @@ test_section_main(void)
|
||||||
test_boolean(&ctx, &parse_section_main, "box-drawings-uses-font-glyphs", &conf.box_drawings_uses_font_glyphs);
|
test_boolean(&ctx, &parse_section_main, "box-drawings-uses-font-glyphs", &conf.box_drawings_uses_font_glyphs);
|
||||||
test_boolean(&ctx, &parse_section_main, "locked-title", &conf.locked_title);
|
test_boolean(&ctx, &parse_section_main, "locked-title", &conf.locked_title);
|
||||||
test_boolean(&ctx, &parse_section_main, "notify-focus-inhibit", &conf.notify_focus_inhibit);
|
test_boolean(&ctx, &parse_section_main, "notify-focus-inhibit", &conf.notify_focus_inhibit);
|
||||||
|
test_boolean(&ctx, &parse_section_main, "dpi-aware", &conf.dpi_aware);
|
||||||
|
|
||||||
test_pt_or_px(&ctx, &parse_section_main, "font-size-adjustment", &conf.font_size_adjustment.pt_or_px); /* TODO: test ‘N%’ values too */
|
test_pt_or_px(&ctx, &parse_section_main, "font-size-adjustment", &conf.font_size_adjustment.pt_or_px); /* TODO: test ‘N%’ values too */
|
||||||
test_pt_or_px(&ctx, &parse_section_main, "line-height", &conf.line_height);
|
test_pt_or_px(&ctx, &parse_section_main, "line-height", &conf.line_height);
|
||||||
|
|
@ -479,17 +525,6 @@ test_section_main(void)
|
||||||
|
|
||||||
test_spawn_template(&ctx, &parse_section_main, "notify", &conf.notify);
|
test_spawn_template(&ctx, &parse_section_main, "notify", &conf.notify);
|
||||||
|
|
||||||
test_enum(
|
|
||||||
&ctx, &parse_section_main, "dpi-aware",
|
|
||||||
9,
|
|
||||||
(const char *[]){"on", "true", "yes", "1",
|
|
||||||
"off", "false", "no", "0",
|
|
||||||
"auto"},
|
|
||||||
(int []){DPI_AWARE_YES, DPI_AWARE_YES, DPI_AWARE_YES, DPI_AWARE_YES,
|
|
||||||
DPI_AWARE_NO, DPI_AWARE_NO, DPI_AWARE_NO, DPI_AWARE_NO,
|
|
||||||
DPI_AWARE_AUTO},
|
|
||||||
(int *)&conf.dpi_aware);
|
|
||||||
|
|
||||||
test_enum(&ctx, &parse_section_main, "selection-target",
|
test_enum(&ctx, &parse_section_main, "selection-target",
|
||||||
4,
|
4,
|
||||||
(const char *[]){"none", "primary", "clipboard", "both"},
|
(const char *[]){"none", "primary", "clipboard", "both"},
|
||||||
|
|
@ -577,8 +612,8 @@ test_section_url(void)
|
||||||
(int []){OSC8_UNDERLINE_URL_MODE, OSC8_UNDERLINE_ALWAYS},
|
(int []){OSC8_UNDERLINE_URL_MODE, OSC8_UNDERLINE_ALWAYS},
|
||||||
(int *)&conf.url.osc8_underline);
|
(int *)&conf.url.osc8_underline);
|
||||||
test_c32string(&ctx, &parse_section_url, "label-letters", &conf.url.label_letters);
|
test_c32string(&ctx, &parse_section_url, "label-letters", &conf.url.label_letters);
|
||||||
|
test_protocols(&ctx, &parse_section_url, "protocols", &conf.url.protocols);
|
||||||
|
|
||||||
/* TODO: protocols (list of wchars) */
|
|
||||||
/* TODO: uri-characters (wchar string, but sorted) */
|
/* TODO: uri-characters (wchar string, but sorted) */
|
||||||
|
|
||||||
config_free(&conf);
|
config_free(&conf);
|
||||||
|
|
@ -627,6 +662,21 @@ test_section_mouse(void)
|
||||||
config_free(&conf);
|
config_free(&conf);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
test_section_touch(void)
|
||||||
|
{
|
||||||
|
struct config conf = {0};
|
||||||
|
struct context ctx = {
|
||||||
|
.conf = &conf, .section = "touch", .path = "unittest"};
|
||||||
|
|
||||||
|
test_invalid_key(&ctx, &parse_section_touch, "invalid-key");
|
||||||
|
|
||||||
|
test_uint32(&ctx, &parse_section_touch, "long-press-delay",
|
||||||
|
&conf.touch.long_press_delay);
|
||||||
|
|
||||||
|
config_free(&conf);
|
||||||
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
test_section_colors(void)
|
test_section_colors(void)
|
||||||
{
|
{
|
||||||
|
|
@ -727,6 +777,8 @@ test_section_csd(void)
|
||||||
&conf.csd.color.quit);
|
&conf.csd.color.quit);
|
||||||
test_boolean(&ctx, &parse_section_csd, "hide-when-maximized",
|
test_boolean(&ctx, &parse_section_csd, "hide-when-maximized",
|
||||||
&conf.csd.hide_when_maximized);
|
&conf.csd.hide_when_maximized);
|
||||||
|
test_boolean(&ctx, &parse_section_csd, "double-click-to-maximize",
|
||||||
|
&conf.csd.double_click_to_maximize);
|
||||||
|
|
||||||
/* TODO: verify the ‘set’ bit is actually set for colors */
|
/* TODO: verify the ‘set’ bit is actually set for colors */
|
||||||
/* TODO: font */
|
/* TODO: font */
|
||||||
|
|
@ -1312,6 +1364,7 @@ main(int argc, const char *const *argv)
|
||||||
test_section_url();
|
test_section_url();
|
||||||
test_section_cursor();
|
test_section_cursor();
|
||||||
test_section_mouse();
|
test_section_mouse();
|
||||||
|
test_section_touch();
|
||||||
test_section_colors();
|
test_section_colors();
|
||||||
test_section_csd();
|
test_section_csd();
|
||||||
test_section_key_bindings();
|
test_section_key_bindings();
|
||||||
|
|
|
||||||
36
themes/aeroroot
Normal file
36
themes/aeroroot
Normal file
|
|
@ -0,0 +1,36 @@
|
||||||
|
# -*- conf -*-
|
||||||
|
# Aero root theme
|
||||||
|
|
||||||
|
[cursor]
|
||||||
|
color=1a1a1a 9fd5f5
|
||||||
|
|
||||||
|
[colors]
|
||||||
|
foreground=dedeef
|
||||||
|
background=1a1a1a
|
||||||
|
|
||||||
|
regular0=1a1a1a
|
||||||
|
regular1=ff3a3a
|
||||||
|
regular2=3aef3a
|
||||||
|
regular3=e6e61a
|
||||||
|
regular4=1a7eff
|
||||||
|
regular5=df3adf
|
||||||
|
regular6=3ff0e0
|
||||||
|
regular7=dadada
|
||||||
|
|
||||||
|
bright0=5a5a5a
|
||||||
|
bright1=ffaaaa
|
||||||
|
bright2=aaf3aa
|
||||||
|
bright3=f3f35a
|
||||||
|
bright4=6abaff
|
||||||
|
bright5=e5aae5
|
||||||
|
bright6=aafff0
|
||||||
|
bright7=f3f3f3
|
||||||
|
|
||||||
|
dim0=000000
|
||||||
|
dim1=b71a1a
|
||||||
|
dim2=1ab71a
|
||||||
|
dim3=b5b50a
|
||||||
|
dim4=0A4FAA
|
||||||
|
dim5=a71aa7
|
||||||
|
dim6=1AA59F
|
||||||
|
dim7=a5a5a5
|
||||||
28
themes/ayu-mirage
Normal file
28
themes/ayu-mirage
Normal file
|
|
@ -0,0 +1,28 @@
|
||||||
|
# -*- conf -*-
|
||||||
|
# theme: Ayu Mirage
|
||||||
|
# description: a theme based on Ayu Mirage for Sublime Text (original: https://github.com/dempfi/ayu)
|
||||||
|
|
||||||
|
[cursor]
|
||||||
|
color = ffcc66 665a44
|
||||||
|
|
||||||
|
[colors]
|
||||||
|
foreground = cccac2
|
||||||
|
background = 242936
|
||||||
|
|
||||||
|
regular0 = 242936 # black
|
||||||
|
regular1 = f28779 # red
|
||||||
|
regular2 = d5ff80 # green
|
||||||
|
regular3 = ffd173 # yellow
|
||||||
|
regular4 = 73d0ff # blue
|
||||||
|
regular5 = dfbfff # magenta
|
||||||
|
regular6 = 5ccfe6 # cyan
|
||||||
|
regular7 = cccac2 # white
|
||||||
|
|
||||||
|
bright0 = fcfcfc # bright black
|
||||||
|
bright1 = f07171 # bright red
|
||||||
|
bright2 = 86b300 # bright gree
|
||||||
|
bright3 = f2ae49 # bright yellow
|
||||||
|
bright4 = 399ee6 # bright blue
|
||||||
|
bright5 = a37acc # bright magenta
|
||||||
|
bright6 = 55b4d4 # bright cyan
|
||||||
|
bright7 = 5c6166 # bright white
|
||||||
27
themes/chiba-dark
Normal file
27
themes/chiba-dark
Normal file
|
|
@ -0,0 +1,27 @@
|
||||||
|
# -*- conf -*-
|
||||||
|
# theme: Chiba Dark
|
||||||
|
# author: ayushnix (https://sr.ht/~ayushnix)
|
||||||
|
# description: A dark theme with bright cyberpunk colors (WCAG AAA compliant)
|
||||||
|
|
||||||
|
[cursor]
|
||||||
|
color = 181818 cdcdcd
|
||||||
|
|
||||||
|
[colors]
|
||||||
|
foreground = cdcdcd
|
||||||
|
background = 181818
|
||||||
|
regular0 = 181818
|
||||||
|
regular1 = ff8599
|
||||||
|
regular2 = 00c545
|
||||||
|
regular3 = de9d00
|
||||||
|
regular4 = 00b4ff
|
||||||
|
regular5 = fd71f8
|
||||||
|
regular6 = 00bfae
|
||||||
|
regular7 = cdcdcd
|
||||||
|
bright0 = 262626
|
||||||
|
bright1 = ff9eb2
|
||||||
|
bright2 = 19de5e
|
||||||
|
bright3 = f7b619
|
||||||
|
bright4 = 19cdff
|
||||||
|
bright5 = ff8aff
|
||||||
|
bright6 = 19d8c7
|
||||||
|
bright7 = dadada
|
||||||
|
|
@ -2,8 +2,8 @@
|
||||||
# Material Amber
|
# Material Amber
|
||||||
# Based on material.io guidelines with Amber 50 background
|
# Based on material.io guidelines with Amber 50 background
|
||||||
|
|
||||||
# [cursor]
|
[cursor]
|
||||||
# color=fff8e1 21201d
|
color=fff8e1 21201d
|
||||||
|
|
||||||
[colors]
|
[colors]
|
||||||
foreground = 21201d
|
foreground = 21201d
|
||||||
|
|
|
||||||
26
themes/srcery
Normal file
26
themes/srcery
Normal file
|
|
@ -0,0 +1,26 @@
|
||||||
|
# srcery
|
||||||
|
|
||||||
|
[colors]
|
||||||
|
background= 1c1b19
|
||||||
|
foreground= fce8c3
|
||||||
|
regular0= 1c1b19
|
||||||
|
regular1= ef2f27
|
||||||
|
regular2= 519f50
|
||||||
|
regular3= fbb829
|
||||||
|
regular4= 2c78bf
|
||||||
|
regular5= e02c6d
|
||||||
|
regular6= 0aaeb3
|
||||||
|
regular7= baa67f
|
||||||
|
bright0= 918175
|
||||||
|
bright1= f75341
|
||||||
|
bright2= 98bc37
|
||||||
|
bright3= fed06e
|
||||||
|
bright4= 68a8e4
|
||||||
|
bright5= ff5c8f
|
||||||
|
bright6= 2be4d0
|
||||||
|
bright7= fce8c3
|
||||||
|
|
||||||
|
## Enable if prefer solarized colors instead of inverterd fg/bg for
|
||||||
|
## highlighting (mouse selection)
|
||||||
|
# selection-foreground=93a1a1
|
||||||
|
# selection-background=073642
|
||||||
24
themes/starlight
Normal file
24
themes/starlight
Normal file
|
|
@ -0,0 +1,24 @@
|
||||||
|
# -*- conf -*-
|
||||||
|
# Theme: starlight V4 (https://github.com/CosmicToast/starlight)
|
||||||
|
|
||||||
|
[colors]
|
||||||
|
foreground = FFFFFF
|
||||||
|
background = 242424
|
||||||
|
|
||||||
|
regular0 = 242424
|
||||||
|
regular1 = f62b5a
|
||||||
|
regular2 = 47b413
|
||||||
|
regular3 = e3c401
|
||||||
|
regular4 = 24acd4
|
||||||
|
regular5 = f2affd
|
||||||
|
regular6 = 13c299
|
||||||
|
regular7 = e6e6e6
|
||||||
|
|
||||||
|
bright0 = 616161
|
||||||
|
bright1 = ff4d51
|
||||||
|
bright2 = 35d450
|
||||||
|
bright3 = e9e836
|
||||||
|
bright4 = 5dc5f8
|
||||||
|
bright5 = feabf2
|
||||||
|
bright6 = 24dfc4
|
||||||
|
bright7 = ffffff
|
||||||
|
|
@ -14,6 +14,7 @@
|
||||||
#include "char32.h"
|
#include "char32.h"
|
||||||
#include "grid.h"
|
#include "grid.h"
|
||||||
#include "key-binding.h"
|
#include "key-binding.h"
|
||||||
|
#include "quirks.h"
|
||||||
#include "render.h"
|
#include "render.h"
|
||||||
#include "selection.h"
|
#include "selection.h"
|
||||||
#include "spawn.h"
|
#include "spawn.h"
|
||||||
|
|
@ -859,6 +860,10 @@ urls_reset(struct terminal *term)
|
||||||
tll_foreach(term->window->urls, it) {
|
tll_foreach(term->window->urls, it) {
|
||||||
wayl_win_subsurface_destroy(&it->item.surf);
|
wayl_win_subsurface_destroy(&it->item.surf);
|
||||||
tll_remove(term->window->urls, it);
|
tll_remove(term->window->urls, it);
|
||||||
|
|
||||||
|
/* Work around Sway bug - unmapping a sub-surface does not
|
||||||
|
* damage the underlying surface */
|
||||||
|
quirk_sway_subsurface_unmap(term);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
131
vt.c
131
vt.c
|
|
@ -294,74 +294,31 @@ action_print(struct terminal *term, uint8_t c)
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
action_param(struct terminal *term, uint8_t c)
|
action_param_lazy_init(struct terminal *term)
|
||||||
{
|
{
|
||||||
if (term->vt.params.idx == 0) {
|
if (term->vt.params.idx == 0) {
|
||||||
struct vt_param *param = &term->vt.params.v[0];
|
struct vt_param *param = &term->vt.params.v[0];
|
||||||
|
|
||||||
|
term->vt.params.cur = param;
|
||||||
param->value = 0;
|
param->value = 0;
|
||||||
param->sub.idx = 0;
|
param->sub.idx = 0;
|
||||||
|
param->sub.cur = NULL;
|
||||||
term->vt.params.idx = 1;
|
term->vt.params.idx = 1;
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
xassert(term->vt.params.idx > 0);
|
static void
|
||||||
|
action_param_new(struct terminal *term, uint8_t c)
|
||||||
|
{
|
||||||
|
xassert(c == ';');
|
||||||
|
action_param_lazy_init(term);
|
||||||
|
|
||||||
const size_t max_params
|
const size_t max_params
|
||||||
= sizeof(term->vt.params.v) / sizeof(term->vt.params.v[0]);
|
= sizeof(term->vt.params.v) / sizeof(term->vt.params.v[0]);
|
||||||
const size_t max_sub_params
|
|
||||||
= sizeof(term->vt.params.v[0].sub.value) / sizeof(term->vt.params.v[0].sub.value[0]);
|
|
||||||
|
|
||||||
/* New parameter */
|
struct vt_param *param;
|
||||||
if (c == ';') {
|
|
||||||
if (unlikely(term->vt.params.idx >= max_params))
|
|
||||||
goto excess_params;
|
|
||||||
|
|
||||||
struct vt_param *param = &term->vt.params.v[term->vt.params.idx++];
|
if (unlikely(term->vt.params.idx >= max_params)) {
|
||||||
param->value = 0;
|
|
||||||
param->sub.idx = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* New sub-parameter */
|
|
||||||
else if (c == ':') {
|
|
||||||
if (unlikely(term->vt.params.idx - 1 >= max_params))
|
|
||||||
goto excess_params;
|
|
||||||
|
|
||||||
struct vt_param *param = &term->vt.params.v[term->vt.params.idx - 1];
|
|
||||||
if (unlikely(param->sub.idx >= max_sub_params))
|
|
||||||
goto excess_sub_params;
|
|
||||||
|
|
||||||
param->sub.value[param->sub.idx++] = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* New digit for current parameter/sub-parameter */
|
|
||||||
else {
|
|
||||||
if (unlikely(term->vt.params.idx - 1 >= max_params))
|
|
||||||
goto excess_params;
|
|
||||||
|
|
||||||
struct vt_param *param = &term->vt.params.v[term->vt.params.idx - 1];
|
|
||||||
unsigned *value;
|
|
||||||
|
|
||||||
if (param->sub.idx > 0) {
|
|
||||||
if (unlikely(param->sub.idx - 1 >= max_sub_params))
|
|
||||||
goto excess_sub_params;
|
|
||||||
value = ¶m->sub.value[param->sub.idx - 1];
|
|
||||||
} else
|
|
||||||
value = ¶m->value;
|
|
||||||
|
|
||||||
*value *= 10;
|
|
||||||
*value += c - '0';
|
|
||||||
}
|
|
||||||
|
|
||||||
#if defined(_DEBUG)
|
|
||||||
/* The rest of the code assumes 'idx' *never* points outside the array */
|
|
||||||
xassert(term->vt.params.idx <= max_params);
|
|
||||||
for (size_t i = 0; i < term->vt.params.idx; i++)
|
|
||||||
xassert(term->vt.params.v[i].sub.idx <= max_sub_params);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
return;
|
|
||||||
|
|
||||||
excess_params:
|
|
||||||
{
|
|
||||||
static bool have_warned = false;
|
static bool have_warned = false;
|
||||||
if (!have_warned) {
|
if (!have_warned) {
|
||||||
have_warned = true;
|
have_warned = true;
|
||||||
|
|
@ -370,11 +327,29 @@ excess_params:
|
||||||
"(will not warn again)",
|
"(will not warn again)",
|
||||||
sizeof(term->vt.params.v) / sizeof(term->vt.params.v[0]));
|
sizeof(term->vt.params.v) / sizeof(term->vt.params.v[0]));
|
||||||
}
|
}
|
||||||
}
|
param = &term->vt.params.dummy;
|
||||||
return;
|
} else
|
||||||
|
param = &term->vt.params.v[term->vt.params.idx++];
|
||||||
|
|
||||||
excess_sub_params:
|
term->vt.params.cur = param;
|
||||||
|
param->value = 0;
|
||||||
|
param->sub.idx = 0;
|
||||||
|
param->sub.cur = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
action_param_new_subparam(struct terminal *term, uint8_t c)
|
||||||
{
|
{
|
||||||
|
xassert(c == ':');
|
||||||
|
action_param_lazy_init(term);
|
||||||
|
|
||||||
|
const size_t max_sub_params
|
||||||
|
= sizeof(term->vt.params.v[0].sub.value) / sizeof(term->vt.params.v[0].sub.value[0]);
|
||||||
|
|
||||||
|
struct vt_param *param = term->vt.params.cur;
|
||||||
|
unsigned *sub_param_value;
|
||||||
|
|
||||||
|
if (unlikely(param->sub.idx >= max_sub_params)) {
|
||||||
static bool have_warned = false;
|
static bool have_warned = false;
|
||||||
if (!have_warned) {
|
if (!have_warned) {
|
||||||
have_warned = true;
|
have_warned = true;
|
||||||
|
|
@ -383,8 +358,33 @@ excess_sub_params:
|
||||||
"(will not warn again)",
|
"(will not warn again)",
|
||||||
sizeof(term->vt.params.v[0].sub.value) / sizeof(term->vt.params.v[0].sub.value[0]));
|
sizeof(term->vt.params.v[0].sub.value) / sizeof(term->vt.params.v[0].sub.value[0]));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
sub_param_value = ¶m->sub.dummy;
|
||||||
|
} else
|
||||||
|
sub_param_value = ¶m->sub.value[param->sub.idx++];
|
||||||
|
|
||||||
|
param->sub.cur = sub_param_value;
|
||||||
|
*sub_param_value = 0;
|
||||||
}
|
}
|
||||||
return;
|
|
||||||
|
static void
|
||||||
|
action_param(struct terminal *term, uint8_t c)
|
||||||
|
{
|
||||||
|
action_param_lazy_init(term);
|
||||||
|
xassert(term->vt.params.cur != NULL);
|
||||||
|
|
||||||
|
struct vt_param *param = term->vt.params.cur;
|
||||||
|
unsigned *value;
|
||||||
|
|
||||||
|
if (unlikely(param->sub.cur != NULL))
|
||||||
|
value = param->sub.cur;
|
||||||
|
else
|
||||||
|
value = ¶m->value;
|
||||||
|
|
||||||
|
unsigned v = *value;
|
||||||
|
v *= 10;
|
||||||
|
v += c - '0';
|
||||||
|
*value = v;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
|
|
@ -1024,7 +1024,9 @@ state_csi_entry_switch(struct terminal *term, uint8_t data)
|
||||||
|
|
||||||
case 0x20 ... 0x2f: action_collect(term, data); return STATE_CSI_INTERMEDIATE;
|
case 0x20 ... 0x2f: action_collect(term, data); return STATE_CSI_INTERMEDIATE;
|
||||||
case 0x30 ... 0x39: action_param(term, data); return STATE_CSI_PARAM;
|
case 0x30 ... 0x39: action_param(term, data); return STATE_CSI_PARAM;
|
||||||
case 0x3a ... 0x3b: action_param(term, data); return STATE_CSI_PARAM;
|
case 0x3a: action_param_new_subparam(term, data); return STATE_CSI_PARAM;
|
||||||
|
case 0x3b: action_param_new(term, data); return STATE_CSI_PARAM;
|
||||||
|
|
||||||
case 0x3c ... 0x3f: action_collect(term, data); return STATE_CSI_PARAM;
|
case 0x3c ... 0x3f: action_collect(term, data); return STATE_CSI_PARAM;
|
||||||
case 0x40 ... 0x7e: action_csi_dispatch(term, data); return STATE_GROUND;
|
case 0x40 ... 0x7e: action_csi_dispatch(term, data); return STATE_GROUND;
|
||||||
case 0x7f: action_ignore(term); return STATE_CSI_ENTRY;
|
case 0x7f: action_ignore(term); return STATE_CSI_ENTRY;
|
||||||
|
|
@ -1044,8 +1046,9 @@ state_csi_param_switch(struct terminal *term, uint8_t data)
|
||||||
|
|
||||||
case 0x20 ... 0x2f: action_collect(term, data); return STATE_CSI_INTERMEDIATE;
|
case 0x20 ... 0x2f: action_collect(term, data); return STATE_CSI_INTERMEDIATE;
|
||||||
|
|
||||||
case 0x30 ... 0x39:
|
case 0x30 ... 0x39: action_param(term, data); return STATE_CSI_PARAM;
|
||||||
case 0x3a ... 0x3b: action_param(term, data); return STATE_CSI_PARAM;
|
case 0x3a: action_param_new_subparam(term, data); return STATE_CSI_PARAM;
|
||||||
|
case 0x3b: action_param_new(term, data); return STATE_CSI_PARAM;
|
||||||
|
|
||||||
case 0x3c ... 0x3f: return STATE_CSI_IGNORE;
|
case 0x3c ... 0x3f: return STATE_CSI_IGNORE;
|
||||||
case 0x40 ... 0x7e: action_csi_dispatch(term, data); return STATE_GROUND;
|
case 0x40 ... 0x7e: action_csi_dispatch(term, data); return STATE_GROUND;
|
||||||
|
|
@ -1126,7 +1129,7 @@ state_dcs_entry_switch(struct terminal *term, uint8_t data)
|
||||||
case 0x20 ... 0x2f: action_collect(term, data); return STATE_DCS_INTERMEDIATE;
|
case 0x20 ... 0x2f: action_collect(term, data); return STATE_DCS_INTERMEDIATE;
|
||||||
case 0x30 ... 0x39: action_param(term, data); return STATE_DCS_PARAM;
|
case 0x30 ... 0x39: action_param(term, data); return STATE_DCS_PARAM;
|
||||||
case 0x3a: return STATE_DCS_IGNORE;
|
case 0x3a: return STATE_DCS_IGNORE;
|
||||||
case 0x3b: action_param(term, data); return STATE_DCS_PARAM;
|
case 0x3b: action_param_new(term, data); return STATE_DCS_PARAM;
|
||||||
case 0x3c ... 0x3f: action_collect(term, data); return STATE_DCS_PARAM;
|
case 0x3c ... 0x3f: action_collect(term, data); return STATE_DCS_PARAM;
|
||||||
case 0x40 ... 0x7e: action_hook(term, data); return STATE_DCS_PASSTHROUGH;
|
case 0x40 ... 0x7e: action_hook(term, data); return STATE_DCS_PASSTHROUGH;
|
||||||
case 0x7f: action_ignore(term); return STATE_DCS_ENTRY;
|
case 0x7f: action_ignore(term); return STATE_DCS_ENTRY;
|
||||||
|
|
@ -1147,7 +1150,7 @@ state_dcs_param_switch(struct terminal *term, uint8_t data)
|
||||||
case 0x20 ... 0x2f: action_collect(term, data); return STATE_DCS_INTERMEDIATE;
|
case 0x20 ... 0x2f: action_collect(term, data); return STATE_DCS_INTERMEDIATE;
|
||||||
case 0x30 ... 0x39: action_param(term, data); return STATE_DCS_PARAM;
|
case 0x30 ... 0x39: action_param(term, data); return STATE_DCS_PARAM;
|
||||||
case 0x3a: return STATE_DCS_IGNORE;
|
case 0x3a: return STATE_DCS_IGNORE;
|
||||||
case 0x3b: action_param(term, data); return STATE_DCS_PARAM;
|
case 0x3b: action_param_new(term, data); return STATE_DCS_PARAM;
|
||||||
case 0x3c ... 0x3f: return STATE_DCS_IGNORE;
|
case 0x3c ... 0x3f: return STATE_DCS_IGNORE;
|
||||||
case 0x40 ... 0x7e: action_hook(term, data); return STATE_DCS_PASSTHROUGH;
|
case 0x40 ... 0x7e: action_hook(term, data); return STATE_DCS_PASSTHROUGH;
|
||||||
case 0x7f: action_ignore(term); return STATE_DCS_PARAM;
|
case 0x7f: action_ignore(term); return STATE_DCS_PARAM;
|
||||||
|
|
|
||||||
511
wayland.c
511
wayland.c
|
|
@ -14,6 +14,10 @@
|
||||||
#include <wayland-cursor.h>
|
#include <wayland-cursor.h>
|
||||||
#include <xkbcommon/xkbcommon-compose.h>
|
#include <xkbcommon/xkbcommon-compose.h>
|
||||||
|
|
||||||
|
#if defined(HAVE_CURSOR_SHAPE)
|
||||||
|
#include <cursor-shape-v1.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
#include <tllist.h>
|
#include <tllist.h>
|
||||||
|
|
||||||
#define LOG_MODULE "wayland"
|
#define LOG_MODULE "wayland"
|
||||||
|
|
@ -32,12 +36,12 @@
|
||||||
#include "xmalloc.h"
|
#include "xmalloc.h"
|
||||||
|
|
||||||
static void
|
static void
|
||||||
csd_reload_font(struct wl_window *win, int old_scale)
|
csd_reload_font(struct wl_window *win, float old_scale)
|
||||||
{
|
{
|
||||||
struct terminal *term = win->term;
|
struct terminal *term = win->term;
|
||||||
const struct config *conf = term->conf;
|
const struct config *conf = term->conf;
|
||||||
|
|
||||||
const int scale = term->scale;
|
const float scale = term->scale;
|
||||||
|
|
||||||
bool enable_csd = win->csd_mode == CSD_YES && !win->is_fullscreen;
|
bool enable_csd = win->csd_mode == CSD_YES && !win->is_fullscreen;
|
||||||
if (!enable_csd)
|
if (!enable_csd)
|
||||||
|
|
@ -52,10 +56,10 @@ csd_reload_font(struct wl_window *win, int old_scale)
|
||||||
patterns[i] = conf->csd.font.arr[i].pattern;
|
patterns[i] = conf->csd.font.arr[i].pattern;
|
||||||
|
|
||||||
char pixelsize[32];
|
char pixelsize[32];
|
||||||
snprintf(pixelsize, sizeof(pixelsize),
|
snprintf(pixelsize, sizeof(pixelsize), "pixelsize=%u",
|
||||||
"pixelsize=%u", conf->csd.title_height * scale * 1 / 2);
|
(int)round(conf->csd.title_height * scale * 1 / 2));
|
||||||
|
|
||||||
LOG_DBG("loading CSD font \"%s:%s\" (old-scale=%d, scale=%d)",
|
LOG_DBG("loading CSD font \"%s:%s\" (old-scale=%.2f, scale=%.2f)",
|
||||||
patterns[0], pixelsize, old_scale, scale);
|
patterns[0], pixelsize, old_scale, scale);
|
||||||
|
|
||||||
win->csd.font = fcft_from_name(conf->csd.font.count, patterns, pixelsize);
|
win->csd.font = fcft_from_name(conf->csd.font.count, patterns, pixelsize);
|
||||||
|
|
@ -74,12 +78,12 @@ csd_instantiate(struct wl_window *win)
|
||||||
|
|
||||||
for (size_t i = CSD_SURF_MINIMIZE; i < CSD_SURF_COUNT; i++) {
|
for (size_t i = CSD_SURF_MINIMIZE; i < CSD_SURF_COUNT; i++) {
|
||||||
bool ret = wayl_win_subsurface_new_with_custom_parent(
|
bool ret = wayl_win_subsurface_new_with_custom_parent(
|
||||||
win, win->csd.surface[CSD_SURF_TITLE].surf, &win->csd.surface[i],
|
win, win->csd.surface[CSD_SURF_TITLE].surface.surf, &win->csd.surface[i],
|
||||||
true);
|
true);
|
||||||
xassert(ret);
|
xassert(ret);
|
||||||
}
|
}
|
||||||
|
|
||||||
csd_reload_font(win, -1);
|
csd_reload_font(win, -1.);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
|
|
@ -187,8 +191,12 @@ seat_destroy(struct seat *seat)
|
||||||
|
|
||||||
if (seat->pointer.theme != NULL)
|
if (seat->pointer.theme != NULL)
|
||||||
wl_cursor_theme_destroy(seat->pointer.theme);
|
wl_cursor_theme_destroy(seat->pointer.theme);
|
||||||
if (seat->pointer.surface != NULL)
|
if (seat->pointer.surface.surf != NULL)
|
||||||
wl_surface_destroy(seat->pointer.surface);
|
wl_surface_destroy(seat->pointer.surface.surf);
|
||||||
|
#if defined(HAVE_FRACTIONAL_SCALE)
|
||||||
|
if (seat->pointer.surface.viewport != NULL)
|
||||||
|
wp_viewport_destroy(seat->pointer.surface.viewport);
|
||||||
|
#endif
|
||||||
if (seat->pointer.xcursor_callback != NULL)
|
if (seat->pointer.xcursor_callback != NULL)
|
||||||
wl_callback_destroy(seat->pointer.xcursor_callback);
|
wl_callback_destroy(seat->pointer.xcursor_callback);
|
||||||
|
|
||||||
|
|
@ -205,10 +213,17 @@ seat_destroy(struct seat *seat)
|
||||||
if (seat->data_device != NULL)
|
if (seat->data_device != NULL)
|
||||||
wl_data_device_release(seat->data_device);
|
wl_data_device_release(seat->data_device);
|
||||||
|
|
||||||
|
#if defined(HAVE_CURSOR_SHAPE)
|
||||||
|
if (seat->pointer.shape_device != NULL)
|
||||||
|
wp_cursor_shape_device_v1_destroy(seat->pointer.shape_device);
|
||||||
|
#endif
|
||||||
|
|
||||||
if (seat->wl_keyboard != NULL)
|
if (seat->wl_keyboard != NULL)
|
||||||
wl_keyboard_release(seat->wl_keyboard);
|
wl_keyboard_release(seat->wl_keyboard);
|
||||||
if (seat->wl_pointer != NULL)
|
if (seat->wl_pointer != NULL)
|
||||||
wl_pointer_release(seat->wl_pointer);
|
wl_pointer_release(seat->wl_pointer);
|
||||||
|
if (seat->wl_touch != NULL)
|
||||||
|
wl_touch_release(seat->wl_touch);
|
||||||
|
|
||||||
#if defined(FOOT_IME_ENABLED) && FOOT_IME_ENABLED
|
#if defined(FOOT_IME_ENABLED) && FOOT_IME_ENABLED
|
||||||
if (seat->wl_text_input != NULL)
|
if (seat->wl_text_input != NULL)
|
||||||
|
|
@ -221,6 +236,7 @@ seat_destroy(struct seat *seat)
|
||||||
ime_reset_pending(seat);
|
ime_reset_pending(seat);
|
||||||
free(seat->clipboard.text);
|
free(seat->clipboard.text);
|
||||||
free(seat->primary.text);
|
free(seat->primary.text);
|
||||||
|
free(seat->pointer.last_custom_xcursor);
|
||||||
free(seat->name);
|
free(seat->name);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -270,9 +286,10 @@ seat_handle_capabilities(void *data, struct wl_seat *wl_seat,
|
||||||
struct seat *seat = data;
|
struct seat *seat = data;
|
||||||
xassert(seat->wl_seat == wl_seat);
|
xassert(seat->wl_seat == wl_seat);
|
||||||
|
|
||||||
LOG_DBG("%s: keyboard=%s, pointer=%s", seat->name,
|
LOG_DBG("%s: keyboard=%s, pointer=%s, touch=%s", seat->name,
|
||||||
(caps & WL_SEAT_CAPABILITY_KEYBOARD) ? "yes" : "no",
|
(caps & WL_SEAT_CAPABILITY_KEYBOARD) ? "yes" : "no",
|
||||||
(caps & WL_SEAT_CAPABILITY_POINTER) ? "yes" : "no");
|
(caps & WL_SEAT_CAPABILITY_POINTER) ? "yes" : "no",
|
||||||
|
(caps & WL_SEAT_CAPABILITY_TOUCH) ? "yes" : "no");
|
||||||
|
|
||||||
if (caps & WL_SEAT_CAPABILITY_KEYBOARD) {
|
if (caps & WL_SEAT_CAPABILITY_KEYBOARD) {
|
||||||
if (seat->wl_keyboard == NULL) {
|
if (seat->wl_keyboard == NULL) {
|
||||||
|
|
@ -288,31 +305,81 @@ seat_handle_capabilities(void *data, struct wl_seat *wl_seat,
|
||||||
|
|
||||||
if (caps & WL_SEAT_CAPABILITY_POINTER) {
|
if (caps & WL_SEAT_CAPABILITY_POINTER) {
|
||||||
if (seat->wl_pointer == NULL) {
|
if (seat->wl_pointer == NULL) {
|
||||||
xassert(seat->pointer.surface == NULL);
|
xassert(seat->pointer.surface.surf == NULL);
|
||||||
seat->pointer.surface = wl_compositor_create_surface(seat->wayl->compositor);
|
seat->pointer.surface.surf =
|
||||||
|
wl_compositor_create_surface(seat->wayl->compositor);
|
||||||
|
|
||||||
if (seat->pointer.surface == NULL) {
|
if (seat->pointer.surface.surf == NULL) {
|
||||||
LOG_ERR("%s: failed to create pointer surface", seat->name);
|
LOG_ERR("%s: failed to create pointer surface", seat->name);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if defined(HAVE_FRACTIONAL_SCALE)
|
||||||
|
xassert(seat->pointer.surface.viewport == NULL);
|
||||||
|
seat->pointer.surface.viewport = wp_viewporter_get_viewport(
|
||||||
|
seat->wayl->viewporter, seat->pointer.surface.surf);
|
||||||
|
|
||||||
|
if (seat->pointer.surface.viewport == NULL) {
|
||||||
|
LOG_ERR("%s: failed to create pointer viewport", seat->name);
|
||||||
|
wl_surface_destroy(seat->pointer.surface.surf);
|
||||||
|
seat->pointer.surface.surf = NULL;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
seat->wl_pointer = wl_seat_get_pointer(wl_seat);
|
seat->wl_pointer = wl_seat_get_pointer(wl_seat);
|
||||||
wl_pointer_add_listener(seat->wl_pointer, &pointer_listener, seat);
|
wl_pointer_add_listener(seat->wl_pointer, &pointer_listener, seat);
|
||||||
|
|
||||||
|
#if defined(HAVE_CURSOR_SHAPE)
|
||||||
|
if (seat->wayl->cursor_shape_manager != NULL) {
|
||||||
|
xassert(seat->pointer.shape_device == NULL);
|
||||||
|
seat->pointer.shape_device = wp_cursor_shape_manager_v1_get_pointer(
|
||||||
|
seat->wayl->cursor_shape_manager, seat->wl_pointer);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (seat->wl_pointer != NULL) {
|
if (seat->wl_pointer != NULL) {
|
||||||
|
#if defined(HAVE_CURSOR_SHAPE)
|
||||||
|
if (seat->pointer.shape_device != NULL) {
|
||||||
|
wp_cursor_shape_device_v1_destroy(seat->pointer.shape_device);
|
||||||
|
seat->pointer.shape_device = NULL;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
wl_pointer_release(seat->wl_pointer);
|
wl_pointer_release(seat->wl_pointer);
|
||||||
wl_surface_destroy(seat->pointer.surface);
|
wl_surface_destroy(seat->pointer.surface.surf);
|
||||||
|
|
||||||
|
#if defined(HAVE_FRACTIONAL_SCALE)
|
||||||
|
wp_viewport_destroy(seat->pointer.surface.viewport);
|
||||||
|
seat->pointer.surface.viewport = NULL;
|
||||||
|
#endif
|
||||||
|
|
||||||
if (seat->pointer.theme != NULL)
|
if (seat->pointer.theme != NULL)
|
||||||
wl_cursor_theme_destroy(seat->pointer.theme);
|
wl_cursor_theme_destroy(seat->pointer.theme);
|
||||||
|
|
||||||
seat->wl_pointer = NULL;
|
seat->wl_pointer = NULL;
|
||||||
seat->pointer.surface = NULL;
|
seat->pointer.surface.surf = NULL;
|
||||||
seat->pointer.theme = NULL;
|
seat->pointer.theme = NULL;
|
||||||
seat->pointer.cursor = NULL;
|
seat->pointer.cursor = NULL;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (caps & WL_SEAT_CAPABILITY_TOUCH) {
|
||||||
|
if (seat->wl_touch == NULL) {
|
||||||
|
seat->wl_touch = wl_seat_get_touch(wl_seat);
|
||||||
|
wl_touch_add_listener(seat->wl_touch, &touch_listener, seat);
|
||||||
|
|
||||||
|
seat->touch.state = TOUCH_STATE_IDLE;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (seat->wl_touch != NULL) {
|
||||||
|
wl_touch_release(seat->wl_touch);
|
||||||
|
seat->wl_touch = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
seat->touch.state = TOUCH_STATE_INHIBITED;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
|
|
@ -331,15 +398,35 @@ static const struct wl_seat_listener seat_listener = {
|
||||||
static void
|
static void
|
||||||
update_term_for_output_change(struct terminal *term)
|
update_term_for_output_change(struct terminal *term)
|
||||||
{
|
{
|
||||||
if (tll_length(term->window->on_outputs) == 0)
|
const float old_scale = term->scale;
|
||||||
return;
|
const float logical_width = term->width / term->scale;
|
||||||
|
const float logical_height = term->height / term->scale;
|
||||||
|
|
||||||
int old_scale = term->scale;
|
/* Note: order matters! term_update_scale() must come first */
|
||||||
|
bool scale_updated = term_update_scale(term);
|
||||||
render_resize(term, term->width / term->scale, term->height / term->scale);
|
bool fonts_updated = term_font_dpi_changed(term, old_scale);
|
||||||
term_font_dpi_changed(term, old_scale);
|
|
||||||
term_font_subpixel_changed(term);
|
term_font_subpixel_changed(term);
|
||||||
|
|
||||||
csd_reload_font(term->window, old_scale);
|
csd_reload_font(term->window, old_scale);
|
||||||
|
|
||||||
|
if (fonts_updated) {
|
||||||
|
/*
|
||||||
|
* If the fonts have been updated, the cell dimensions have
|
||||||
|
* 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_force(term, round(logical_width), round(logical_height));
|
||||||
|
}
|
||||||
|
|
||||||
|
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, round(logical_width), round(logical_height));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
|
|
@ -350,11 +437,6 @@ update_terms_on_monitor(struct monitor *mon)
|
||||||
tll_foreach(wayl->terms, it) {
|
tll_foreach(wayl->terms, it) {
|
||||||
struct terminal *term = it->item;
|
struct terminal *term = it->item;
|
||||||
|
|
||||||
if (term->conf->dpi_aware == DPI_AWARE_AUTO) {
|
|
||||||
update_term_for_output_change(term);
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
tll_foreach(term->window->on_outputs, it2) {
|
tll_foreach(term->window->on_outputs, it2) {
|
||||||
if (it2->item == mon) {
|
if (it2->item == mon) {
|
||||||
update_term_for_output_change(term);
|
update_term_for_output_change(term);
|
||||||
|
|
@ -373,6 +455,9 @@ output_update_ppi(struct monitor *mon)
|
||||||
double x_inches = mon->dim.mm.width * 0.03937008;
|
double x_inches = mon->dim.mm.width * 0.03937008;
|
||||||
double y_inches = mon->dim.mm.height * 0.03937008;
|
double y_inches = mon->dim.mm.height * 0.03937008;
|
||||||
|
|
||||||
|
const int width = mon->dim.px_real.width;
|
||||||
|
const int height = mon->dim.px_real.height;
|
||||||
|
|
||||||
mon->ppi.real.x = mon->dim.px_real.width / x_inches;
|
mon->ppi.real.x = mon->dim.px_real.width / x_inches;
|
||||||
mon->ppi.real.y = mon->dim.px_real.height / y_inches;
|
mon->ppi.real.y = mon->dim.px_real.height / y_inches;
|
||||||
|
|
||||||
|
|
@ -395,27 +480,36 @@ output_update_ppi(struct monitor *mon)
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
int scaled_width = mon->dim.px_scaled.width;
|
const int scaled_width = mon->dim.px_scaled.width;
|
||||||
int scaled_height = mon->dim.px_scaled.height;
|
const int scaled_height = mon->dim.px_scaled.height;
|
||||||
|
|
||||||
if (scaled_width == 0 && scaled_height == 0 && mon->scale > 0) {
|
|
||||||
/* Estimate scaled width/height if none has been provided */
|
|
||||||
scaled_width = mon->dim.px_real.width / mon->scale;
|
|
||||||
scaled_height = mon->dim.px_real.height / mon->scale;
|
|
||||||
}
|
|
||||||
|
|
||||||
mon->ppi.scaled.x = scaled_width / x_inches;
|
mon->ppi.scaled.x = scaled_width / x_inches;
|
||||||
mon->ppi.scaled.y = scaled_height / y_inches;
|
mon->ppi.scaled.y = scaled_height / y_inches;
|
||||||
|
|
||||||
double px_diag = sqrt(pow(scaled_width, 2) + pow(scaled_height, 2));
|
const double px_diag_physical = sqrt(pow(width, 2) + pow(height, 2));
|
||||||
mon->dpi = px_diag / mon->inch * mon->scale;
|
mon->dpi.physical = width == 0 && height == 0
|
||||||
|
? 96.
|
||||||
|
: px_diag_physical / mon->inch;
|
||||||
|
|
||||||
if (mon->dpi > 1000) {
|
const double px_diag_scaled = sqrt(pow(scaled_width, 2) + pow(scaled_height, 2));
|
||||||
|
mon->dpi.scaled = scaled_width == 0 && scaled_height == 0
|
||||||
|
? 96.
|
||||||
|
: px_diag_scaled / mon->inch * mon->scale;
|
||||||
|
|
||||||
|
if (mon->dpi.physical > 1000) {
|
||||||
if (mon->name != NULL) {
|
if (mon->name != NULL) {
|
||||||
LOG_WARN("%s: DPI=%f is unreasonable, using 96 instead",
|
LOG_WARN("%s: DPI=%f (physical) is unreasonable, using 96 instead",
|
||||||
mon->name, mon->dpi);
|
mon->name, mon->dpi.physical);
|
||||||
}
|
}
|
||||||
mon->dpi = 96;
|
mon->dpi.physical = 96;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (mon->dpi.scaled > 1000) {
|
||||||
|
if (mon->name != NULL) {
|
||||||
|
LOG_WARN("%s: DPI=%f (logical) is unreasonable, using 96 instead",
|
||||||
|
mon->name, mon->dpi.scaled);
|
||||||
|
}
|
||||||
|
mon->dpi.scaled = 96;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -633,6 +727,7 @@ xdg_toplevel_configure(void *data, struct xdg_toplevel *xdg_toplevel,
|
||||||
bool is_tiled_bottom = false;
|
bool is_tiled_bottom = false;
|
||||||
bool is_tiled_left = false;
|
bool is_tiled_left = false;
|
||||||
bool is_tiled_right = false;
|
bool is_tiled_right = false;
|
||||||
|
bool is_suspended UNUSED = false;
|
||||||
|
|
||||||
#if defined(LOG_ENABLE_DBG) && LOG_ENABLE_DBG
|
#if defined(LOG_ENABLE_DBG) && LOG_ENABLE_DBG
|
||||||
char state_str[2048];
|
char state_str[2048];
|
||||||
|
|
@ -647,29 +742,35 @@ xdg_toplevel_configure(void *data, struct xdg_toplevel *xdg_toplevel,
|
||||||
[XDG_TOPLEVEL_STATE_TILED_RIGHT] = "tiled:right",
|
[XDG_TOPLEVEL_STATE_TILED_RIGHT] = "tiled:right",
|
||||||
[XDG_TOPLEVEL_STATE_TILED_TOP] = "tiled:top",
|
[XDG_TOPLEVEL_STATE_TILED_TOP] = "tiled:top",
|
||||||
[XDG_TOPLEVEL_STATE_TILED_BOTTOM] = "tiled:bottom",
|
[XDG_TOPLEVEL_STATE_TILED_BOTTOM] = "tiled:bottom",
|
||||||
|
#if defined(XDG_TOPLEVEL_STATE_SUSPENDED_SINCE_VERSION) /* wayland-protocols >= 1.32 */
|
||||||
|
[XDG_TOPLEVEL_STATE_SUSPENDED] = "suspended",
|
||||||
|
#endif
|
||||||
};
|
};
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
enum xdg_toplevel_state *state;
|
enum xdg_toplevel_state *state;
|
||||||
wl_array_for_each(state, states) {
|
wl_array_for_each(state, states) {
|
||||||
switch (*state) {
|
switch (*state) {
|
||||||
case XDG_TOPLEVEL_STATE_ACTIVATED: is_activated = true; break;
|
|
||||||
case XDG_TOPLEVEL_STATE_FULLSCREEN: is_fullscreen = true; break;
|
|
||||||
case XDG_TOPLEVEL_STATE_MAXIMIZED: is_maximized = true; break;
|
case XDG_TOPLEVEL_STATE_MAXIMIZED: is_maximized = true; break;
|
||||||
|
case XDG_TOPLEVEL_STATE_FULLSCREEN: is_fullscreen = true; break;
|
||||||
|
case XDG_TOPLEVEL_STATE_RESIZING: is_resizing = true; break;
|
||||||
|
case XDG_TOPLEVEL_STATE_ACTIVATED: is_activated = true; break;
|
||||||
case XDG_TOPLEVEL_STATE_TILED_LEFT: is_tiled_left = true; break;
|
case XDG_TOPLEVEL_STATE_TILED_LEFT: is_tiled_left = true; break;
|
||||||
case XDG_TOPLEVEL_STATE_TILED_RIGHT: is_tiled_right = true; break;
|
case XDG_TOPLEVEL_STATE_TILED_RIGHT: is_tiled_right = true; break;
|
||||||
case XDG_TOPLEVEL_STATE_TILED_TOP: is_tiled_top = true; break;
|
case XDG_TOPLEVEL_STATE_TILED_TOP: is_tiled_top = true; break;
|
||||||
case XDG_TOPLEVEL_STATE_TILED_BOTTOM: is_tiled_bottom = true; break;
|
case XDG_TOPLEVEL_STATE_TILED_BOTTOM: is_tiled_bottom = true; break;
|
||||||
case XDG_TOPLEVEL_STATE_RESIZING: is_resizing = true; break;
|
|
||||||
|
#if defined(XDG_TOPLEVEL_STATE_SUSPENDED_SINCE_VERSION)
|
||||||
|
case XDG_TOPLEVEL_STATE_SUSPENDED: is_suspended = true; break;
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
#if defined(LOG_ENABLE_DBG) && LOG_ENABLE_DBG
|
#if defined(LOG_ENABLE_DBG) && LOG_ENABLE_DBG
|
||||||
if (*state >= XDG_TOPLEVEL_STATE_MAXIMIZED &&
|
if (*state >= 0 && *state < ALEN(strings)) {
|
||||||
*state <= XDG_TOPLEVEL_STATE_TILED_BOTTOM)
|
|
||||||
{
|
|
||||||
state_chars += snprintf(
|
state_chars += snprintf(
|
||||||
&state_str[state_chars], sizeof(state_str) - state_chars,
|
&state_str[state_chars], sizeof(state_str) - state_chars,
|
||||||
"%s, ", strings[*state]);
|
"%s, ",
|
||||||
|
strings[*state] != NULL ? strings[*state] : "<unknown>");
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
@ -848,7 +949,7 @@ xdg_surface_configure(void *data, struct xdg_surface *xdg_surface,
|
||||||
* anytime soon. Some compositors require a commit in
|
* anytime soon. Some compositors require a commit in
|
||||||
* combination with an ack - make them happy.
|
* combination with an ack - make them happy.
|
||||||
*/
|
*/
|
||||||
wl_surface_commit(win->surface);
|
wl_surface_commit(win->surface.surf);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (wasnt_configured)
|
if (wasnt_configured)
|
||||||
|
|
@ -1121,6 +1222,38 @@ handle_global(void *data, struct wl_registry *registry,
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#if defined(HAVE_FRACTIONAL_SCALE)
|
||||||
|
else if (strcmp(interface, wp_viewporter_interface.name) == 0) {
|
||||||
|
const uint32_t required = 1;
|
||||||
|
if (!verify_iface_version(interface, version, required))
|
||||||
|
return;
|
||||||
|
|
||||||
|
wayl->viewporter = wl_registry_bind(
|
||||||
|
wayl->registry, name, &wp_viewporter_interface, required);
|
||||||
|
}
|
||||||
|
|
||||||
|
else if (strcmp(interface, wp_fractional_scale_manager_v1_interface.name) == 0) {
|
||||||
|
const uint32_t required = 1;
|
||||||
|
if (!verify_iface_version(interface, version, required))
|
||||||
|
return;
|
||||||
|
|
||||||
|
wayl->fractional_scale_manager = wl_registry_bind(
|
||||||
|
wayl->registry, name,
|
||||||
|
&wp_fractional_scale_manager_v1_interface, required);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if defined(HAVE_CURSOR_SHAPE)
|
||||||
|
else if (strcmp(interface, wp_cursor_shape_manager_v1_interface.name) == 0) {
|
||||||
|
const uint32_t required = 1;
|
||||||
|
if (!verify_iface_version(interface, version, required))
|
||||||
|
return;
|
||||||
|
|
||||||
|
wayl->cursor_shape_manager = wl_registry_bind(
|
||||||
|
wayl->registry, name, &wp_cursor_shape_manager_v1_interface, required);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
#if defined(FOOT_IME_ENABLED) && FOOT_IME_ENABLED
|
#if defined(FOOT_IME_ENABLED) && FOOT_IME_ENABLED
|
||||||
else if (strcmp(interface, zwp_text_input_manager_v3_interface.name) == 0) {
|
else if (strcmp(interface, zwp_text_input_manager_v3_interface.name) == 0) {
|
||||||
const uint32_t required = 1;
|
const uint32_t required = 1;
|
||||||
|
|
@ -1204,7 +1337,7 @@ handle_global_remove(void *data, struct wl_registry *registry, uint32_t name)
|
||||||
|
|
||||||
if (seat->wl_keyboard != NULL)
|
if (seat->wl_keyboard != NULL)
|
||||||
keyboard_listener.leave(
|
keyboard_listener.leave(
|
||||||
seat, seat->wl_keyboard, -1, seat->kbd_focus->window->surface);
|
seat, seat->wl_keyboard, -1, seat->kbd_focus->window->surface.surf);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (seat->mouse_focus != NULL) {
|
if (seat->mouse_focus != NULL) {
|
||||||
|
|
@ -1214,7 +1347,7 @@ handle_global_remove(void *data, struct wl_registry *registry, uint32_t name)
|
||||||
|
|
||||||
if (seat->wl_pointer != NULL)
|
if (seat->wl_pointer != NULL)
|
||||||
pointer_listener.leave(
|
pointer_listener.leave(
|
||||||
seat, seat->wl_pointer, -1, seat->mouse_focus->window->surface);
|
seat, seat->wl_pointer, -1, seat->mouse_focus->window->surface.surf);
|
||||||
}
|
}
|
||||||
|
|
||||||
seat_destroy(seat);
|
seat_destroy(seat);
|
||||||
|
|
@ -1334,6 +1467,11 @@ wayl_init(struct fdm *fdm, struct key_binding_manager *key_binding_manager,
|
||||||
LOG_ERR("no seats available (wl_seat interface too old?)");
|
LOG_ERR("no seats available (wl_seat interface too old?)");
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
|
if (tll_length(wayl->monitors) == 0) {
|
||||||
|
LOG_ERR("no monitors available");
|
||||||
|
goto out;
|
||||||
|
}
|
||||||
|
|
||||||
if (wayl->primary_selection_device_manager == NULL)
|
if (wayl->primary_selection_device_manager == NULL)
|
||||||
LOG_WARN("no primary selection available");
|
LOG_WARN("no primary selection available");
|
||||||
|
|
||||||
|
|
@ -1347,6 +1485,23 @@ wayl_init(struct fdm *fdm, struct key_binding_manager *key_binding_manager,
|
||||||
"bell.urgent will fall back to coloring the window margins red");
|
"bell.urgent will fall back to coloring the window margins red");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if defined(HAVE_FRACTIONAL_SCALE)
|
||||||
|
if (wayl->fractional_scale_manager == NULL || wayl->viewporter == NULL) {
|
||||||
|
#else
|
||||||
|
if (true) {
|
||||||
|
#endif
|
||||||
|
LOG_WARN("fractional scaling not available");
|
||||||
|
}
|
||||||
|
|
||||||
|
#if defined(HAVE_CURSOR_SHAPE)
|
||||||
|
if (wayl->cursor_shape_manager == NULL) {
|
||||||
|
#else
|
||||||
|
if (true) {
|
||||||
|
#endif
|
||||||
|
LOG_WARN("no server-side cursors available, "
|
||||||
|
"falling back to client-side cursors");
|
||||||
|
}
|
||||||
|
|
||||||
if (presentation_timings && wayl->presentation == NULL) {
|
if (presentation_timings && wayl->presentation == NULL) {
|
||||||
LOG_ERR("presentation time interface not implemented by compositor");
|
LOG_ERR("presentation time interface not implemented by compositor");
|
||||||
goto out;
|
goto out;
|
||||||
|
|
@ -1369,14 +1524,12 @@ wayl_init(struct fdm *fdm, struct key_binding_manager *key_binding_manager,
|
||||||
|
|
||||||
tll_foreach(wayl->monitors, it) {
|
tll_foreach(wayl->monitors, it) {
|
||||||
LOG_INFO(
|
LOG_INFO(
|
||||||
"%s: %dx%d+%dx%d@%dHz %s %.2f\" scale=%d PPI=%dx%d (physical) PPI=%dx%d (logical), DPI=%.2f",
|
"%s: %dx%d+%dx%d@%dHz %s %.2f\" scale=%d, DPI=%.2f/%.2f (physical/scaled)",
|
||||||
it->item.name, it->item.dim.px_real.width, it->item.dim.px_real.height,
|
it->item.name, it->item.dim.px_real.width, it->item.dim.px_real.height,
|
||||||
it->item.x, it->item.y, (int)round(it->item.refresh),
|
it->item.x, it->item.y, (int)round(it->item.refresh),
|
||||||
it->item.model != NULL ? it->item.model : it->item.description,
|
it->item.model != NULL ? it->item.model : it->item.description,
|
||||||
it->item.inch, it->item.scale,
|
it->item.inch, it->item.scale,
|
||||||
it->item.ppi.real.x, it->item.ppi.real.y,
|
it->item.dpi.physical, it->item.dpi.scaled);
|
||||||
it->item.ppi.scaled.x, it->item.ppi.scaled.y,
|
|
||||||
it->item.dpi);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
wayl->fd = wl_display_get_fd(wayl->display);
|
wayl->fd = wl_display_get_fd(wayl->display);
|
||||||
|
|
@ -1435,6 +1588,16 @@ wayl_destroy(struct wayland *wayl)
|
||||||
zwp_text_input_manager_v3_destroy(wayl->text_input_manager);
|
zwp_text_input_manager_v3_destroy(wayl->text_input_manager);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#if defined(HAVE_FRACTIONAL_SCALE)
|
||||||
|
if (wayl->fractional_scale_manager != NULL)
|
||||||
|
wp_fractional_scale_manager_v1_destroy(wayl->fractional_scale_manager);
|
||||||
|
if (wayl->viewporter != NULL)
|
||||||
|
wp_viewporter_destroy(wayl->viewporter);
|
||||||
|
#endif
|
||||||
|
#if defined(HAVE_CURSOR_SHAPE)
|
||||||
|
if (wayl->cursor_shape_manager != NULL)
|
||||||
|
wp_cursor_shape_manager_v1_destroy(wayl->cursor_shape_manager);
|
||||||
|
#endif
|
||||||
#if defined(HAVE_XDG_ACTIVATION)
|
#if defined(HAVE_XDG_ACTIVATION)
|
||||||
if (wayl->xdg_activation != NULL)
|
if (wayl->xdg_activation != NULL)
|
||||||
xdg_activation_v1_destroy(wayl->xdg_activation);
|
xdg_activation_v1_destroy(wayl->xdg_activation);
|
||||||
|
|
@ -1469,6 +1632,29 @@ wayl_destroy(struct wayland *wayl)
|
||||||
free(wayl);
|
free(wayl);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if defined(HAVE_FRACTIONAL_SCALE)
|
||||||
|
static void fractional_scale_preferred_scale(
|
||||||
|
void *data, struct wp_fractional_scale_v1 *wp_fractional_scale_v1,
|
||||||
|
uint32_t scale)
|
||||||
|
{
|
||||||
|
struct wl_window *win = data;
|
||||||
|
|
||||||
|
const float new_scale = (float)scale / 120.;
|
||||||
|
|
||||||
|
if (win->scale == new_scale)
|
||||||
|
return;
|
||||||
|
|
||||||
|
LOG_DBG("fractional scale: %.2f -> %.2f", win->scale, new_scale);
|
||||||
|
|
||||||
|
win->scale = new_scale;
|
||||||
|
update_term_for_output_change(win->term);
|
||||||
|
}
|
||||||
|
|
||||||
|
static const struct wp_fractional_scale_v1_listener fractional_scale_listener = {
|
||||||
|
.preferred_scale = &fractional_scale_preferred_scale,
|
||||||
|
};
|
||||||
|
#endif
|
||||||
|
|
||||||
struct wl_window *
|
struct wl_window *
|
||||||
wayl_win_init(struct terminal *term, const char *token)
|
wayl_win_init(struct terminal *term, const char *token)
|
||||||
{
|
{
|
||||||
|
|
@ -1485,30 +1671,34 @@ wayl_win_init(struct terminal *term, const char *token)
|
||||||
win->csd_mode = CSD_UNKNOWN;
|
win->csd_mode = CSD_UNKNOWN;
|
||||||
win->csd.move_timeout_fd = -1;
|
win->csd.move_timeout_fd = -1;
|
||||||
win->resize_timeout_fd = -1;
|
win->resize_timeout_fd = -1;
|
||||||
|
win->scale = -1.;
|
||||||
|
|
||||||
win->wm_capabilities.maximize = true;
|
win->wm_capabilities.maximize = true;
|
||||||
win->wm_capabilities.minimize = true;
|
win->wm_capabilities.minimize = true;
|
||||||
|
|
||||||
win->surface = wl_compositor_create_surface(wayl->compositor);
|
win->surface.surf = wl_compositor_create_surface(wayl->compositor);
|
||||||
if (win->surface == NULL) {
|
if (win->surface.surf == NULL) {
|
||||||
LOG_ERR("failed to create wayland surface");
|
LOG_ERR("failed to create wayland surface");
|
||||||
goto out;
|
goto out;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (term->colors.alpha == 0xffff) {
|
wayl_win_alpha_changed(win);
|
||||||
struct wl_region *region = wl_compositor_create_region(
|
|
||||||
term->wl->compositor);
|
|
||||||
|
|
||||||
if (region != NULL) {
|
wl_surface_add_listener(win->surface.surf, &surface_listener, win);
|
||||||
wl_region_add(region, 0, 0, INT32_MAX, INT32_MAX);
|
|
||||||
wl_surface_set_opaque_region(win->surface, region);
|
#if defined(HAVE_FRACTIONAL_SCALE)
|
||||||
wl_region_destroy(region);
|
if (wayl->fractional_scale_manager != NULL && wayl->viewporter != NULL) {
|
||||||
}
|
win->surface.viewport = wp_viewporter_get_viewport(wayl->viewporter, win->surface.surf);
|
||||||
|
|
||||||
|
win->fractional_scale =
|
||||||
|
wp_fractional_scale_manager_v1_get_fractional_scale(
|
||||||
|
wayl->fractional_scale_manager, win->surface.surf);
|
||||||
|
wp_fractional_scale_v1_add_listener(
|
||||||
|
win->fractional_scale, &fractional_scale_listener, win);
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
wl_surface_add_listener(win->surface, &surface_listener, win);
|
win->xdg_surface = xdg_wm_base_get_xdg_surface(wayl->shell, win->surface.surf);
|
||||||
|
|
||||||
win->xdg_surface = xdg_wm_base_get_xdg_surface(wayl->shell, win->surface);
|
|
||||||
xdg_surface_add_listener(win->xdg_surface, &xdg_surface_listener, win);
|
xdg_surface_add_listener(win->xdg_surface, &xdg_surface_listener, win);
|
||||||
|
|
||||||
win->xdg_toplevel = xdg_surface_get_toplevel(win->xdg_surface);
|
win->xdg_toplevel = xdg_surface_get_toplevel(win->xdg_surface);
|
||||||
|
|
@ -1541,12 +1731,12 @@ wayl_win_init(struct terminal *term, const char *token)
|
||||||
LOG_WARN("no decoration manager available - using CSDs unconditionally");
|
LOG_WARN("no decoration manager available - using CSDs unconditionally");
|
||||||
}
|
}
|
||||||
|
|
||||||
wl_surface_commit(win->surface);
|
wl_surface_commit(win->surface.surf);
|
||||||
|
|
||||||
#if defined(HAVE_XDG_ACTIVATION)
|
#if defined(HAVE_XDG_ACTIVATION)
|
||||||
/* Complete XDG startup notification */
|
/* Complete XDG startup notification */
|
||||||
if (token)
|
if (token)
|
||||||
xdg_activation_v1_activate(wayl->xdg_activation, token, win->surface);
|
xdg_activation_v1_activate(wayl->xdg_activation, token, win->surface.surf);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
if (!wayl_win_subsurface_new(win, &win->overlay, false)) {
|
if (!wayl_win_subsurface_new(win, &win->overlay, false)) {
|
||||||
|
|
@ -1596,33 +1786,33 @@ wayl_win_destroy(struct wl_window *win)
|
||||||
* nor mouse focus).
|
* nor mouse focus).
|
||||||
*/
|
*/
|
||||||
|
|
||||||
if (win->render_timer.surf != NULL) {
|
if (win->render_timer.surface.surf != NULL) {
|
||||||
wl_surface_attach(win->render_timer.surf, NULL, 0, 0);
|
wl_surface_attach(win->render_timer.surface.surf, NULL, 0, 0);
|
||||||
wl_surface_commit(win->render_timer.surf);
|
wl_surface_commit(win->render_timer.surface.surf);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (win->scrollback_indicator.surf != NULL) {
|
if (win->scrollback_indicator.surface.surf != NULL) {
|
||||||
wl_surface_attach(win->scrollback_indicator.surf, NULL, 0, 0);
|
wl_surface_attach(win->scrollback_indicator.surface.surf, NULL, 0, 0);
|
||||||
wl_surface_commit(win->scrollback_indicator.surf);
|
wl_surface_commit(win->scrollback_indicator.surface.surf);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Scrollback search */
|
/* Scrollback search */
|
||||||
if (win->search.surf != NULL) {
|
if (win->search.surface.surf != NULL) {
|
||||||
wl_surface_attach(win->search.surf, NULL, 0, 0);
|
wl_surface_attach(win->search.surface.surf, NULL, 0, 0);
|
||||||
wl_surface_commit(win->search.surf);
|
wl_surface_commit(win->search.surface.surf);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* URLs */
|
/* URLs */
|
||||||
tll_foreach(win->urls, it) {
|
tll_foreach(win->urls, it) {
|
||||||
wl_surface_attach(it->item.surf.surf, NULL, 0, 0);
|
wl_surface_attach(it->item.surf.surface.surf, NULL, 0, 0);
|
||||||
wl_surface_commit(it->item.surf.surf);
|
wl_surface_commit(it->item.surf.surface.surf);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* CSD */
|
/* CSD */
|
||||||
for (size_t i = 0; i < ALEN(win->csd.surface); i++) {
|
for (size_t i = 0; i < ALEN(win->csd.surface); i++) {
|
||||||
if (win->csd.surface[i].surf != NULL) {
|
if (win->csd.surface[i].surface.surf != NULL) {
|
||||||
wl_surface_attach(win->csd.surface[i].surf, NULL, 0, 0);
|
wl_surface_attach(win->csd.surface[i].surface.surf, NULL, 0, 0);
|
||||||
wl_surface_commit(win->csd.surface[i].surf);
|
wl_surface_commit(win->csd.surface[i].surface.surf);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1630,8 +1820,8 @@ wayl_win_destroy(struct wl_window *win)
|
||||||
|
|
||||||
/* Main window */
|
/* Main window */
|
||||||
win->unmapped = true;
|
win->unmapped = true;
|
||||||
wl_surface_attach(win->surface, NULL, 0, 0);
|
wl_surface_attach(win->surface.surf, NULL, 0, 0);
|
||||||
wl_surface_commit(win->surface);
|
wl_surface_commit(win->surface.surf);
|
||||||
wayl_roundtrip(win->term->wl);
|
wayl_roundtrip(win->term->wl);
|
||||||
|
|
||||||
tll_free(win->on_outputs);
|
tll_free(win->on_outputs);
|
||||||
|
|
@ -1661,6 +1851,12 @@ wayl_win_destroy(struct wl_window *win)
|
||||||
|
|
||||||
tll_remove(win->xdg_tokens, it);
|
tll_remove(win->xdg_tokens, it);
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
#if defined(HAVE_FRACTIONAL_SCALE)
|
||||||
|
if (win->fractional_scale != NULL)
|
||||||
|
wp_fractional_scale_v1_destroy(win->fractional_scale);
|
||||||
|
if (win->surface.viewport != NULL)
|
||||||
|
wp_viewport_destroy(win->surface.viewport);
|
||||||
#endif
|
#endif
|
||||||
if (win->frame_callback != NULL)
|
if (win->frame_callback != NULL)
|
||||||
wl_callback_destroy(win->frame_callback);
|
wl_callback_destroy(win->frame_callback);
|
||||||
|
|
@ -1670,8 +1866,8 @@ wayl_win_destroy(struct wl_window *win)
|
||||||
xdg_toplevel_destroy(win->xdg_toplevel);
|
xdg_toplevel_destroy(win->xdg_toplevel);
|
||||||
if (win->xdg_surface != NULL)
|
if (win->xdg_surface != NULL)
|
||||||
xdg_surface_destroy(win->xdg_surface);
|
xdg_surface_destroy(win->xdg_surface);
|
||||||
if (win->surface != NULL)
|
if (win->surface.surf != NULL)
|
||||||
wl_surface_destroy(win->surface);
|
wl_surface_destroy(win->surface.surf);
|
||||||
|
|
||||||
wayl_roundtrip(win->term->wl);
|
wayl_roundtrip(win->term->wl);
|
||||||
|
|
||||||
|
|
@ -1681,7 +1877,7 @@ wayl_win_destroy(struct wl_window *win)
|
||||||
}
|
}
|
||||||
|
|
||||||
bool
|
bool
|
||||||
wayl_reload_xcursor_theme(struct seat *seat, int new_scale)
|
wayl_reload_xcursor_theme(struct seat *seat, float new_scale)
|
||||||
{
|
{
|
||||||
if (seat->pointer.theme != NULL && seat->pointer.scale == new_scale) {
|
if (seat->pointer.theme != NULL && seat->pointer.scale == new_scale) {
|
||||||
/* We already have a theme loaded, and the scale hasn't changed */
|
/* We already have a theme loaded, and the scale hasn't changed */
|
||||||
|
|
@ -1714,7 +1910,7 @@ wayl_reload_xcursor_theme(struct seat *seat, int new_scale)
|
||||||
|
|
||||||
const char *xcursor_theme = getenv("XCURSOR_THEME");
|
const char *xcursor_theme = getenv("XCURSOR_THEME");
|
||||||
|
|
||||||
LOG_INFO("cursor theme: %s, size: %d, scale: %d",
|
LOG_INFO("cursor theme: %s, size: %d, scale: %.2f",
|
||||||
xcursor_theme ? xcursor_theme : "(null)",
|
xcursor_theme ? xcursor_theme : "(null)",
|
||||||
xcursor_size, new_scale);
|
xcursor_size, new_scale);
|
||||||
|
|
||||||
|
|
@ -1798,6 +1994,86 @@ wayl_roundtrip(struct wayland *wayl)
|
||||||
wayl_flush(wayl);
|
wayl_flush(wayl);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
bool
|
||||||
|
wayl_fractional_scaling(const struct wayland *wayl)
|
||||||
|
{
|
||||||
|
#if defined(HAVE_FRACTIONAL_SCALE)
|
||||||
|
return wayl->fractional_scale_manager != NULL;
|
||||||
|
#else
|
||||||
|
return false;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
wayl_surface_scale_explicit_width_height(
|
||||||
|
const struct wl_window *win, const struct wayl_surface *surf,
|
||||||
|
int width, int height, float scale)
|
||||||
|
{
|
||||||
|
|
||||||
|
if (wayl_fractional_scaling(win->term->wl) && win->scale > 0.) {
|
||||||
|
#if defined(HAVE_FRACTIONAL_SCALE)
|
||||||
|
LOG_DBG("scaling by a factor of %.2f using fractional scaling "
|
||||||
|
"(width=%d, height=%d) ", scale, width, height);
|
||||||
|
|
||||||
|
wl_surface_set_buffer_scale(surf->surf, 1);
|
||||||
|
wp_viewport_set_destination(
|
||||||
|
surf->viewport,
|
||||||
|
round((float)width / scale),
|
||||||
|
round((float)height / scale));
|
||||||
|
#else
|
||||||
|
BUG("wayl_fraction_scaling() returned true, "
|
||||||
|
"but fractional scaling was not available at compile time");
|
||||||
|
#endif
|
||||||
|
} else {
|
||||||
|
LOG_DBG("scaling by a factor of %.2f using legacy mode "
|
||||||
|
"(width=%d, height=%d)", scale, width, height);
|
||||||
|
|
||||||
|
xassert(scale == floor(scale));
|
||||||
|
|
||||||
|
const int iscale = (int)scale;
|
||||||
|
xassert(width % iscale == 0);
|
||||||
|
xassert(height % iscale == 0);
|
||||||
|
|
||||||
|
wl_surface_set_buffer_scale(surf->surf, iscale);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
wayl_surface_scale(const struct wl_window *win, const struct wayl_surface *surf,
|
||||||
|
const struct buffer *buf, float scale)
|
||||||
|
{
|
||||||
|
wayl_surface_scale_explicit_width_height(
|
||||||
|
win, surf, buf->width, buf->height, scale);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
wayl_win_scale(struct wl_window *win, const struct buffer *buf)
|
||||||
|
{
|
||||||
|
const struct terminal *term = win->term;
|
||||||
|
const float scale = term->scale;
|
||||||
|
|
||||||
|
wayl_surface_scale(win, &win->surface, buf, scale);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
wayl_win_alpha_changed(struct wl_window *win)
|
||||||
|
{
|
||||||
|
struct terminal *term = win->term;
|
||||||
|
|
||||||
|
if (term->colors.alpha == 0xffff) {
|
||||||
|
struct wl_region *region = wl_compositor_create_region(
|
||||||
|
term->wl->compositor);
|
||||||
|
|
||||||
|
if (region != NULL) {
|
||||||
|
wl_region_add(region, 0, 0, INT32_MAX, INT32_MAX);
|
||||||
|
wl_surface_set_opaque_region(win->surface.surf, region);
|
||||||
|
wl_region_destroy(region);
|
||||||
|
}
|
||||||
|
} else
|
||||||
|
wl_surface_set_opaque_region(win->surface.surf, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
#if defined(HAVE_XDG_ACTIVATION)
|
#if defined(HAVE_XDG_ACTIVATION)
|
||||||
static void
|
static void
|
||||||
activation_token_for_urgency_done(const char *token, void *data)
|
activation_token_for_urgency_done(const char *token, void *data)
|
||||||
|
|
@ -1806,7 +2082,7 @@ activation_token_for_urgency_done(const char *token, void *data)
|
||||||
struct wayland *wayl = win->term->wl;
|
struct wayland *wayl = win->term->wl;
|
||||||
|
|
||||||
win->urgency_token_is_pending = false;
|
win->urgency_token_is_pending = false;
|
||||||
xdg_activation_v1_activate(wayl->xdg_activation, token, win->surface);
|
xdg_activation_v1_activate(wayl->xdg_activation, token, win->surface.surf);
|
||||||
}
|
}
|
||||||
#endif /* HAVE_XDG_ACTIVATION */
|
#endif /* HAVE_XDG_ACTIVATION */
|
||||||
|
|
||||||
|
|
@ -1851,27 +2127,43 @@ wayl_win_csd_borders_visible(const struct wl_window *win)
|
||||||
bool
|
bool
|
||||||
wayl_win_subsurface_new_with_custom_parent(
|
wayl_win_subsurface_new_with_custom_parent(
|
||||||
struct wl_window *win, struct wl_surface *parent,
|
struct wl_window *win, struct wl_surface *parent,
|
||||||
struct wl_surf_subsurf *surf, bool allow_pointer_input)
|
struct wayl_sub_surface *surf, bool allow_pointer_input)
|
||||||
{
|
{
|
||||||
struct wayland *wayl = win->term->wl;
|
struct wayland *wayl = win->term->wl;
|
||||||
|
|
||||||
surf->surf = NULL;
|
surf->surface.surf = NULL;
|
||||||
surf->sub = NULL;
|
surf->sub = NULL;
|
||||||
|
|
||||||
struct wl_surface *main_surface
|
struct wl_surface *main_surface
|
||||||
= wl_compositor_create_surface(wayl->compositor);
|
= wl_compositor_create_surface(wayl->compositor);
|
||||||
|
|
||||||
if (main_surface == NULL)
|
if (main_surface == NULL) {
|
||||||
|
LOG_ERR("failed to instantiate surface for sub-surface");
|
||||||
return false;
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
struct wl_subsurface *sub = wl_subcompositor_get_subsurface(
|
struct wl_subsurface *sub = wl_subcompositor_get_subsurface(
|
||||||
wayl->sub_compositor, main_surface, parent);
|
wayl->sub_compositor, main_surface, parent);
|
||||||
|
|
||||||
if (sub == NULL) {
|
if (sub == NULL) {
|
||||||
|
LOG_ERR("failed to instantiate sub-surface");
|
||||||
wl_surface_destroy(main_surface);
|
wl_surface_destroy(main_surface);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if defined(HAVE_FRACTIONAL_SCALE)
|
||||||
|
struct wp_viewport *viewport = NULL;
|
||||||
|
if (wayl->fractional_scale_manager != NULL && wayl->viewporter != NULL) {
|
||||||
|
viewport = wp_viewporter_get_viewport(wayl->viewporter, main_surface);
|
||||||
|
if (viewport == NULL) {
|
||||||
|
LOG_ERR("failed to instantiate viewport for sub-surface");
|
||||||
|
wl_subsurface_destroy(sub);
|
||||||
|
wl_surface_destroy(main_surface);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
wl_surface_set_user_data(main_surface, win);
|
wl_surface_set_user_data(main_surface, win);
|
||||||
wl_subsurface_set_sync(sub);
|
wl_subsurface_set_sync(sub);
|
||||||
|
|
||||||
|
|
@ -1883,32 +2175,43 @@ wayl_win_subsurface_new_with_custom_parent(
|
||||||
wl_region_destroy(empty);
|
wl_region_destroy(empty);
|
||||||
}
|
}
|
||||||
|
|
||||||
surf->surf = main_surface;
|
surf->surface.surf = main_surface;
|
||||||
surf->sub = sub;
|
surf->sub = sub;
|
||||||
|
#if defined(HAVE_FRACTIONAL_SCALE)
|
||||||
|
surf->surface.viewport = viewport;
|
||||||
|
#endif
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool
|
bool
|
||||||
wayl_win_subsurface_new(struct wl_window *win, struct wl_surf_subsurf *surf,
|
wayl_win_subsurface_new(struct wl_window *win, struct wayl_sub_surface *surf,
|
||||||
bool allow_pointer_input)
|
bool allow_pointer_input)
|
||||||
{
|
{
|
||||||
return wayl_win_subsurface_new_with_custom_parent(
|
return wayl_win_subsurface_new_with_custom_parent(
|
||||||
win, win->surface, surf, allow_pointer_input);
|
win, win->surface.surf, surf, allow_pointer_input);
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
wayl_win_subsurface_destroy(struct wl_surf_subsurf *surf)
|
wayl_win_subsurface_destroy(struct wayl_sub_surface *surf)
|
||||||
{
|
{
|
||||||
if (surf == NULL)
|
if (surf == NULL)
|
||||||
return;
|
return;
|
||||||
if (surf->sub != NULL)
|
|
||||||
wl_subsurface_destroy(surf->sub);
|
|
||||||
if (surf->surf != NULL)
|
|
||||||
wl_surface_destroy(surf->surf);
|
|
||||||
|
|
||||||
surf->surf = NULL;
|
#if defined(HAVE_FRACTIONAL_SCALE)
|
||||||
|
if (surf->surface.viewport != NULL) {
|
||||||
|
wp_viewport_destroy(surf->surface.viewport);
|
||||||
|
surf->surface.viewport = NULL;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
if (surf->sub != NULL) {
|
||||||
|
wl_subsurface_destroy(surf->sub);
|
||||||
surf->sub = NULL;
|
surf->sub = NULL;
|
||||||
}
|
}
|
||||||
|
if (surf->surface.surf != NULL) {
|
||||||
|
wl_surface_destroy(surf->surface.surf);
|
||||||
|
surf->surface.surf = NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#if defined(HAVE_XDG_ACTIVATION)
|
#if defined(HAVE_XDG_ACTIVATION)
|
||||||
|
|
||||||
|
|
@ -1972,7 +2275,7 @@ wayl_get_activation_token(
|
||||||
if (seat != NULL && serial != 0)
|
if (seat != NULL && serial != 0)
|
||||||
xdg_activation_token_v1_set_serial(token, serial, seat->wl_seat);
|
xdg_activation_token_v1_set_serial(token, serial, seat->wl_seat);
|
||||||
|
|
||||||
xdg_activation_token_v1_set_surface(token, win->surface);
|
xdg_activation_token_v1_set_surface(token, win->surface.surf);
|
||||||
xdg_activation_token_v1_add_listener(token, &activation_token_listener, ctx);
|
xdg_activation_token_v1_add_listener(token, &activation_token_listener, ctx);
|
||||||
xdg_activation_token_v1_commit(token);
|
xdg_activation_token_v1_commit(token);
|
||||||
return true;
|
return true;
|
||||||
|
|
|
||||||
110
wayland.h
110
wayland.h
|
|
@ -20,13 +20,20 @@
|
||||||
#include <xdg-activation-v1.h>
|
#include <xdg-activation-v1.h>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#if defined(HAVE_FRACTIONAL_SCALE)
|
||||||
|
#include <viewporter.h>
|
||||||
|
#include <fractional-scale-v1.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
#include <fcft/fcft.h>
|
#include <fcft/fcft.h>
|
||||||
#include <tllist.h>
|
#include <tllist.h>
|
||||||
|
|
||||||
|
#include "cursor-shape.h"
|
||||||
#include "fdm.h"
|
#include "fdm.h"
|
||||||
|
|
||||||
/* Forward declarations */
|
/* Forward declarations */
|
||||||
struct terminal;
|
struct terminal;
|
||||||
|
struct buffer;
|
||||||
|
|
||||||
/* Mime-types we support when dealing with data offers (e.g. copy-paste, or DnD) */
|
/* Mime-types we support when dealing with data offers (e.g. copy-paste, or DnD) */
|
||||||
enum data_offer_mime_type {
|
enum data_offer_mime_type {
|
||||||
|
|
@ -40,6 +47,26 @@ enum data_offer_mime_type {
|
||||||
DATA_OFFER_MIME_TEXT_UTF8_STRING,
|
DATA_OFFER_MIME_TEXT_UTF8_STRING,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
enum touch_state {
|
||||||
|
TOUCH_STATE_INHIBITED = -1,
|
||||||
|
TOUCH_STATE_IDLE,
|
||||||
|
TOUCH_STATE_HELD,
|
||||||
|
TOUCH_STATE_DRAGGING,
|
||||||
|
TOUCH_STATE_SCROLLING,
|
||||||
|
};
|
||||||
|
|
||||||
|
struct wayl_surface {
|
||||||
|
struct wl_surface *surf;
|
||||||
|
#if defined(HAVE_FRACTIONAL_SCALE)
|
||||||
|
struct wp_viewport *viewport;
|
||||||
|
#endif
|
||||||
|
};
|
||||||
|
|
||||||
|
struct wayl_sub_surface {
|
||||||
|
struct wayl_surface surface;
|
||||||
|
struct wl_subsurface *sub;
|
||||||
|
};
|
||||||
|
|
||||||
struct wl_window;
|
struct wl_window;
|
||||||
struct wl_clipboard {
|
struct wl_clipboard {
|
||||||
struct wl_window *window; /* For DnD */
|
struct wl_window *window; /* For DnD */
|
||||||
|
|
@ -127,17 +154,36 @@ struct seat {
|
||||||
struct {
|
struct {
|
||||||
uint32_t serial;
|
uint32_t serial;
|
||||||
|
|
||||||
struct wl_surface *surface;
|
/* Client-side cursor */
|
||||||
|
struct wayl_surface surface;
|
||||||
struct wl_cursor_theme *theme;
|
struct wl_cursor_theme *theme;
|
||||||
struct wl_cursor *cursor;
|
struct wl_cursor *cursor;
|
||||||
int scale;
|
|
||||||
bool hidden;
|
|
||||||
|
|
||||||
const char *xcursor;
|
/* Server-side cursor */
|
||||||
|
#if defined(HAVE_CURSOR_SHAPE)
|
||||||
|
struct wp_cursor_shape_device_v1 *shape_device;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
float scale;
|
||||||
|
bool hidden;
|
||||||
|
enum cursor_shape shape;
|
||||||
|
char *last_custom_xcursor;
|
||||||
|
|
||||||
struct wl_callback *xcursor_callback;
|
struct wl_callback *xcursor_callback;
|
||||||
bool xcursor_pending;
|
bool xcursor_pending;
|
||||||
} pointer;
|
} pointer;
|
||||||
|
|
||||||
|
/* Touch state */
|
||||||
|
struct wl_touch *wl_touch;
|
||||||
|
struct {
|
||||||
|
enum touch_state state;
|
||||||
|
|
||||||
|
uint32_t serial;
|
||||||
|
uint32_t time;
|
||||||
|
struct wl_surface *surface;
|
||||||
|
int32_t id;
|
||||||
|
} touch;
|
||||||
|
|
||||||
struct {
|
struct {
|
||||||
int x;
|
int x;
|
||||||
int y;
|
int y;
|
||||||
|
|
@ -269,7 +315,10 @@ struct monitor {
|
||||||
} scaled;
|
} scaled;
|
||||||
} ppi;
|
} ppi;
|
||||||
|
|
||||||
float dpi;
|
struct {
|
||||||
|
float scaled;
|
||||||
|
float physical;
|
||||||
|
} dpi;
|
||||||
|
|
||||||
int scale;
|
int scale;
|
||||||
float refresh;
|
float refresh;
|
||||||
|
|
@ -289,14 +338,9 @@ struct monitor {
|
||||||
bool use_output_release;
|
bool use_output_release;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct wl_surf_subsurf {
|
|
||||||
struct wl_surface *surf;
|
|
||||||
struct wl_subsurface *sub;
|
|
||||||
};
|
|
||||||
|
|
||||||
struct wl_url {
|
struct wl_url {
|
||||||
const struct url *url;
|
const struct url *url;
|
||||||
struct wl_surf_subsurf surf;
|
struct wayl_sub_surface surf;
|
||||||
};
|
};
|
||||||
|
|
||||||
enum csd_mode {CSD_UNKNOWN, CSD_NO, CSD_YES};
|
enum csd_mode {CSD_UNKNOWN, CSD_NO, CSD_YES};
|
||||||
|
|
@ -320,21 +364,26 @@ struct xdg_activation_token_context {
|
||||||
struct wayland;
|
struct wayland;
|
||||||
struct wl_window {
|
struct wl_window {
|
||||||
struct terminal *term;
|
struct terminal *term;
|
||||||
struct wl_surface *surface;
|
struct wayl_surface surface;
|
||||||
struct xdg_surface *xdg_surface;
|
struct xdg_surface *xdg_surface;
|
||||||
struct xdg_toplevel *xdg_toplevel;
|
struct xdg_toplevel *xdg_toplevel;
|
||||||
#if defined(HAVE_XDG_ACTIVATION)
|
#if defined(HAVE_XDG_ACTIVATION)
|
||||||
tll(struct xdg_activation_token_context *) xdg_tokens;
|
tll(struct xdg_activation_token_context *) xdg_tokens;
|
||||||
bool urgency_token_is_pending;
|
bool urgency_token_is_pending;
|
||||||
|
#endif
|
||||||
|
#if defined(HAVE_FRACTIONAL_SCALE)
|
||||||
|
struct wp_fractional_scale_v1 *fractional_scale;
|
||||||
#endif
|
#endif
|
||||||
bool unmapped;
|
bool unmapped;
|
||||||
|
|
||||||
|
float scale;
|
||||||
|
|
||||||
struct zxdg_toplevel_decoration_v1 *xdg_toplevel_decoration;
|
struct zxdg_toplevel_decoration_v1 *xdg_toplevel_decoration;
|
||||||
|
|
||||||
enum csd_mode csd_mode;
|
enum csd_mode csd_mode;
|
||||||
|
|
||||||
struct {
|
struct {
|
||||||
struct wl_surf_subsurf surface[CSD_SURF_COUNT];
|
struct wayl_sub_surface surface[CSD_SURF_COUNT];
|
||||||
struct fcft_font *font;
|
struct fcft_font *font;
|
||||||
int move_timeout_fd;
|
int move_timeout_fd;
|
||||||
uint32_t serial;
|
uint32_t serial;
|
||||||
|
|
@ -345,10 +394,10 @@ struct wl_window {
|
||||||
bool minimize:1;
|
bool minimize:1;
|
||||||
} wm_capabilities;
|
} wm_capabilities;
|
||||||
|
|
||||||
struct wl_surf_subsurf search;
|
struct wayl_sub_surface search;
|
||||||
struct wl_surf_subsurf scrollback_indicator;
|
struct wayl_sub_surface scrollback_indicator;
|
||||||
struct wl_surf_subsurf render_timer;
|
struct wayl_sub_surface render_timer;
|
||||||
struct wl_surf_subsurf overlay;
|
struct wayl_sub_surface overlay;
|
||||||
|
|
||||||
struct wl_callback *frame_callback;
|
struct wl_callback *frame_callback;
|
||||||
|
|
||||||
|
|
@ -406,6 +455,10 @@ struct wayland {
|
||||||
struct xdg_activation_v1 *xdg_activation;
|
struct xdg_activation_v1 *xdg_activation;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#if defined(HAVE_CURSOR_SHAPE)
|
||||||
|
struct wp_cursor_shape_manager_v1 *cursor_shape_manager;
|
||||||
|
#endif
|
||||||
|
|
||||||
bool presentation_timings;
|
bool presentation_timings;
|
||||||
struct wp_presentation *presentation;
|
struct wp_presentation *presentation;
|
||||||
uint32_t presentation_clock_id;
|
uint32_t presentation_clock_id;
|
||||||
|
|
@ -414,6 +467,11 @@ struct wayland {
|
||||||
struct zwp_text_input_manager_v3 *text_input_manager;
|
struct zwp_text_input_manager_v3 *text_input_manager;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#if defined(HAVE_FRACTIONAL_SCALE)
|
||||||
|
struct wp_viewporter *viewporter;
|
||||||
|
struct wp_fractional_scale_manager_v1 *fractional_scale_manager;
|
||||||
|
#endif
|
||||||
|
|
||||||
bool have_argb8888;
|
bool have_argb8888;
|
||||||
tll(struct monitor) monitors; /* All available outputs */
|
tll(struct monitor) monitors; /* All available outputs */
|
||||||
tll(struct seat) seats;
|
tll(struct seat) seats;
|
||||||
|
|
@ -426,26 +484,36 @@ struct wayland *wayl_init(
|
||||||
bool presentation_timings);
|
bool presentation_timings);
|
||||||
void wayl_destroy(struct wayland *wayl);
|
void wayl_destroy(struct wayland *wayl);
|
||||||
|
|
||||||
bool wayl_reload_xcursor_theme(struct seat *seat, int new_scale);
|
bool wayl_reload_xcursor_theme(struct seat *seat, float new_scale);
|
||||||
|
|
||||||
void wayl_flush(struct wayland *wayl);
|
void wayl_flush(struct wayland *wayl);
|
||||||
void wayl_roundtrip(struct wayland *wayl);
|
void wayl_roundtrip(struct wayland *wayl);
|
||||||
|
|
||||||
|
bool wayl_fractional_scaling(const struct wayland *wayl);
|
||||||
|
void wayl_surface_scale(
|
||||||
|
const struct wl_window *win, const struct wayl_surface *surf,
|
||||||
|
const struct buffer *buf, float scale);
|
||||||
|
void wayl_surface_scale_explicit_width_height(
|
||||||
|
const struct wl_window *win, const struct wayl_surface *surf,
|
||||||
|
int width, int height, float scale);
|
||||||
|
|
||||||
struct wl_window *wayl_win_init(struct terminal *term, const char *token);
|
struct wl_window *wayl_win_init(struct terminal *term, const char *token);
|
||||||
void wayl_win_destroy(struct wl_window *win);
|
void wayl_win_destroy(struct wl_window *win);
|
||||||
|
|
||||||
|
void wayl_win_scale(struct wl_window *win, const struct buffer *buf);
|
||||||
|
void wayl_win_alpha_changed(struct wl_window *win);
|
||||||
bool wayl_win_set_urgent(struct wl_window *win);
|
bool wayl_win_set_urgent(struct wl_window *win);
|
||||||
|
|
||||||
bool wayl_win_csd_titlebar_visible(const struct wl_window *win);
|
bool wayl_win_csd_titlebar_visible(const struct wl_window *win);
|
||||||
bool wayl_win_csd_borders_visible(const struct wl_window *win);
|
bool wayl_win_csd_borders_visible(const struct wl_window *win);
|
||||||
|
|
||||||
bool wayl_win_subsurface_new(
|
bool wayl_win_subsurface_new(
|
||||||
struct wl_window *win, struct wl_surf_subsurf *surf,
|
struct wl_window *win, struct wayl_sub_surface *surf,
|
||||||
bool allow_pointer_input);
|
bool allow_pointer_input);
|
||||||
bool wayl_win_subsurface_new_with_custom_parent(
|
bool wayl_win_subsurface_new_with_custom_parent(
|
||||||
struct wl_window *win, struct wl_surface *parent,
|
struct wl_window *win, struct wl_surface *parent,
|
||||||
struct wl_surf_subsurf *surf, bool allow_pointer_input);
|
struct wayl_sub_surface *surf, bool allow_pointer_input);
|
||||||
void wayl_win_subsurface_destroy(struct wl_surf_subsurf *surf);
|
void wayl_win_subsurface_destroy(struct wayl_sub_surface *surf);
|
||||||
|
|
||||||
#if defined(HAVE_XDG_ACTIVATION)
|
#if defined(HAVE_XDG_ACTIVATION)
|
||||||
bool wayl_get_activation_token(
|
bool wayl_get_activation_token(
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue