mirror of
https://codeberg.org/dnkl/foot.git
synced 2026-04-26 06:46:45 -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:
|
||||
- musl-dev
|
||||
- eudev-libs
|
||||
|
|
|
|||
|
|
@ -4,7 +4,7 @@ pipeline:
|
|||
branch:
|
||||
- master
|
||||
- releases/*
|
||||
image: alpine:latest
|
||||
image: alpine:edge
|
||||
commands:
|
||||
- apk add python3
|
||||
- apk add py3-pip
|
||||
|
|
@ -16,7 +16,7 @@ pipeline:
|
|||
branch:
|
||||
- master
|
||||
- releases/*
|
||||
image: alpine:latest
|
||||
image: alpine:edge
|
||||
commands:
|
||||
- apk add git
|
||||
- mkdir -p subprojects && cd subprojects
|
||||
|
|
@ -30,7 +30,7 @@ pipeline:
|
|||
- master
|
||||
- releases/*
|
||||
group: build
|
||||
image: alpine:latest
|
||||
image: alpine:edge
|
||||
commands:
|
||||
- apk update
|
||||
- apk add musl-dev linux-headers meson ninja gcc clang scdoc ncurses
|
||||
|
|
@ -87,7 +87,7 @@ pipeline:
|
|||
- master
|
||||
- releases/*
|
||||
group: build
|
||||
image: i386/alpine:latest
|
||||
image: i386/alpine:edge
|
||||
commands:
|
||||
- apk update
|
||||
- apk add musl-dev linux-headers meson ninja gcc clang scdoc ncurses
|
||||
|
|
|
|||
164
CHANGELOG.md
164
CHANGELOG.md
|
|
@ -1,6 +1,7 @@
|
|||
# Changelog
|
||||
|
||||
* [Unreleased](#unreleased)
|
||||
* [1.15.0](#1-15-0)
|
||||
* [1.14.0](#1-14-0)
|
||||
* [1.13.1](#1-13-1)
|
||||
* [1.13.0](#1-13-0)
|
||||
|
|
@ -44,16 +45,14 @@
|
|||
|
||||
## Unreleased
|
||||
### Added
|
||||
|
||||
* VT: implemented `XTQMODKEYS` query (`CSI ? Pp m`).
|
||||
|
||||
|
||||
### Changed
|
||||
|
||||
* 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”_.
|
||||
* When window is mapped, use metadata (DPI, scaling factor, subpixel
|
||||
configuration) from the monitor we were most recently mapped on,
|
||||
instead of the one least recently.
|
||||
* 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
|
||||
graphical-session.target ([#1281][1281]).
|
||||
* If `$XDG_RUNTIME_DIR/foot-$WAYLAND_DISPLAY.sock` does not exist,
|
||||
|
|
@ -61,6 +60,8 @@
|
|||
`/tmp/foot.sock`, even if `$WAYLAND_DISPLAY` and/or
|
||||
`$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
|
||||
|
||||
|
||||
|
|
@ -68,17 +69,154 @@
|
|||
### Removed
|
||||
### Fixed
|
||||
|
||||
* Incorrect icon in dock and window switcher on Gnome ([#1317][1317])
|
||||
* Crash when scrolling after resizing the window with non-zero
|
||||
scrolling regions.
|
||||
* Use appropriate rounding when applying fractional scales.
|
||||
* Xcursor not being scaled correctly on `fractional-scale-v1` capable
|
||||
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
|
||||
### 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
|
||||
|
||||
### Added
|
||||
|
|
@ -100,7 +238,7 @@
|
|||
* “Report DA2” terminfo entries (`RV`/`rv`).
|
||||
* `XF` terminfo capability (focus in/out events available).
|
||||
* `$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
|
||||
[1225]: https://codeberg.org/dnkl/foot/issues/1225
|
||||
|
|
|
|||
26
INSTALL.md
26
INSTALL.md
|
|
@ -45,7 +45,8 @@ subprojects.
|
|||
* wayland (_client_ and _cursor_ libraries)
|
||||
* xkbcommon
|
||||
* 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]
|
||||
|
||||
[^1]: can also be built as subprojects, in which case they are
|
||||
|
|
@ -142,17 +143,18 @@ mkdir -p bld/release && cd bld/release
|
|||
|
||||
Available compile-time options:
|
||||
|
||||
| Option | Type | Default | Description | Extra dependencies |
|
||||
|--------------------------------------|---------|-------------------------|-----------------------------------------------------------|--------------------|
|
||||
| `-Ddocs` | feature | `auto` | Builds and install documentation | scdoc |
|
||||
| `-Dtests` | bool | `true` | Build tests (adds a `ninja test` build target) | none |
|
||||
| `-Dime` | bool | `true` | Enables IME support | None |
|
||||
| `-Dgrapheme-clustering` | feature | `auto` | Enables grapheme clustering | libutf8proc |
|
||||
| `-Dterminfo` | feature | `enabled` | Build and install terminfo files | tic (ncurses) |
|
||||
| `-Ddefault-terminfo` | string | `foot` | Default value of `TERM` | 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 |
|
||||
| `-Ddefault-utempter-path` | feature | `auto` | Default path to utempter binary (‘none’ disables default) | libutempter |
|
||||
| Option | Type | Default | Description | Extra dependencies |
|
||||
|--------------------------------------|---------|-------------------------|---------------------------------------------------------------------------------|---------------------|
|
||||
| `-Ddocs` | feature | `auto` | Builds and install documentation | scdoc |
|
||||
| `-Dtests` | bool | `true` | Build tests (adds a `ninja test` build target) | None |
|
||||
| `-Dime` | bool | `true` | Enables IME support | None |
|
||||
| `-Dgrapheme-clustering` | feature | `auto` | Enables grapheme clustering | libutf8proc |
|
||||
| `-Dterminfo` | feature | `enabled` | Build and install terminfo files | tic (ncurses) |
|
||||
| `-Ddefault-terminfo` | string | `foot` | Default value of `TERM` | 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 |
|
||||
| `-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
|
||||
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. [Scrollback search](#scrollback-search)
|
||||
1. [Mouse](#mouse)
|
||||
1. [Touchscreen](#touchscreen)
|
||||
1. [Server (daemon) mode](#server-daemon-mode)
|
||||
1. [URLs](#urls)
|
||||
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),
|
||||
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
|
||||
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>
|
||||
: Jump to the previous, currently not visible, prompt. Requires [shell
|
||||
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
|
||||
|
||||
|
||||
### 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
|
||||
|
||||
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
|
||||
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
|
||||
associated with a _“jump-label”_. The jump-label indicates the _key
|
||||
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
|
||||
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
|
||||
output scaling has been disabled on **all** monitors. If at least one
|
||||
monitor has output scaling enabled, fonts will instead by sized using
|
||||
the scaling factor.
|
||||
That said, on Wayland, Hi-DPI monitors are typically handled by
|
||||
configuring a _"scaling factor"_ in the compositor. This is usually
|
||||
expressed as either a rational value (e.g. _1.5_), or as a percentage
|
||||
(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
|
||||
(regardless of scaling factor), or to **never** use it, with the
|
||||
`dpi-aware` option in `foot.ini`. See the man page, **foot.ini**(5)
|
||||
for more information.
|
||||
For this reason, and because of the new _fractional scaling_ protocol
|
||||
(see below for details), and because this is how Wayland applications
|
||||
are expected to behave, foot >= 1.15 will default to scaling fonts
|
||||
using the compositor’s scaling factor, and **not** the monitor
|
||||
DPI.
|
||||
|
||||
When fonts are sized using the monitor’s DPI, glyphs should always
|
||||
have the same physical height, regardless of monitor.
|
||||
This means the (assuming the monitors are at the same viewing
|
||||
distance) the font size will appear to change when you move the foot
|
||||
window across different monitors, **unless** you have configured the
|
||||
monitors’ scaling factors correctly in the compositor.
|
||||
|
||||
Furthermore, foot will re-size the fonts on-the-fly when the window is
|
||||
moved between screens with different DPIs values. If the window covers
|
||||
multiple screens, with different DPIs, the highest DPI will be used.
|
||||
This can be changed by setting the `dpi-aware` option to `yes` in
|
||||
`foot.ini`. When enabled, fonts will **not** be sized using the
|
||||
scaling factor, but will instead be sized using the monitor’s
|
||||
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
|
||||
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
|
||||
|
||||
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)
|
||||
{
|
||||
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,
|
||||
feature_pgo() ? '+' : '-',
|
||||
feature_ime() ? '+' : '-',
|
||||
feature_graphemes() ? '+' : '-',
|
||||
feature_fractional_scaling() ? '+' : ':',
|
||||
feature_cursor_shape() ? '+' : '-',
|
||||
feature_assertions() ? '+' : '-');
|
||||
return buf;
|
||||
}
|
||||
|
|
|
|||
128
config.c
128
config.c
|
|
@ -30,8 +30,8 @@
|
|||
#include "xmalloc.h"
|
||||
#include "xsnprintf.h"
|
||||
|
||||
static const uint32_t default_foreground = 0x839496;
|
||||
static const uint32_t default_background = 0x002b36;
|
||||
static const uint32_t default_foreground = 0xffffff;
|
||||
static const uint32_t default_background = 0x242424;
|
||||
|
||||
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] = {
|
||||
// Regular
|
||||
0x073642,
|
||||
0xdc322f,
|
||||
0x859900,
|
||||
0xb58900,
|
||||
0x268bd2,
|
||||
0xd33682,
|
||||
0x2aa198,
|
||||
0xeee8d5,
|
||||
0x242424,
|
||||
0xf62b5a,
|
||||
0x47b413,
|
||||
0xe3c401,
|
||||
0x24acd4,
|
||||
0xf2affd,
|
||||
0x13c299,
|
||||
0xe6e6e6,
|
||||
|
||||
// Bright
|
||||
0x08404f,
|
||||
0xe35f5c,
|
||||
0x9fb700,
|
||||
0xd9a400,
|
||||
0x4ba1de,
|
||||
0xdc619d,
|
||||
0x32c1b6,
|
||||
0x616161,
|
||||
0xff4d51,
|
||||
0x35d450,
|
||||
0xe9e836,
|
||||
0x5dc5f8,
|
||||
0xfeabf2,
|
||||
0x24dfc4,
|
||||
0xffffff,
|
||||
|
||||
// 6x6x6 RGB cube
|
||||
|
|
@ -972,17 +972,8 @@ parse_section_main(struct context *ctx)
|
|||
else if (strcmp(key, "underline-thickness") == 0)
|
||||
return value_to_pt_or_px(ctx, &conf->underline_thickness);
|
||||
|
||||
else if (strcmp(key, "dpi-aware") == 0) {
|
||||
if (strcmp(value, "auto") == 0)
|
||||
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, "dpi-aware") == 0)
|
||||
return value_to_bool(ctx, &conf->dpi_aware);
|
||||
|
||||
else if (strcmp(key, "workers") == 0)
|
||||
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)
|
||||
return value_to_bool(ctx, &conf->box_drawings_uses_font_glyphs);
|
||||
|
||||
else if (strcmp(key, "utempter") == 0) {
|
||||
if (!value_to_str(ctx, &conf->utempter_path))
|
||||
else if (strcmp(key, "utmp-helper") == 0 || strcmp(key, "utempter") == 0) {
|
||||
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;
|
||||
|
||||
if (strcmp(conf->utempter_path, "none") == 0) {
|
||||
free(conf->utempter_path);
|
||||
conf->utempter_path = NULL;
|
||||
if (strcmp(conf->utmp_helper_path, "none") == 0) {
|
||||
free(conf->utmp_helper_path);
|
||||
conf->utmp_helper_path = NULL;
|
||||
}
|
||||
|
||||
return true;
|
||||
|
|
@ -1468,6 +1475,9 @@ parse_section_csd(struct context *ctx)
|
|||
else if (strcmp(key, "hide-when-maximized") == 0)
|
||||
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 {
|
||||
LOG_CONTEXTUAL_ERR("not a valid action: %s", key);
|
||||
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
|
||||
parse_key_value(char *kv, const char **section, const char **key, const char **value)
|
||||
{
|
||||
|
|
@ -2547,6 +2571,7 @@ enum section {
|
|||
SECTION_TEXT_BINDINGS,
|
||||
SECTION_ENVIRONMENT,
|
||||
SECTION_TWEAK,
|
||||
SECTION_TOUCH,
|
||||
SECTION_COUNT,
|
||||
};
|
||||
|
||||
|
|
@ -2572,6 +2597,7 @@ static const struct {
|
|||
[SECTION_TEXT_BINDINGS] = {&parse_section_text_bindings, "text-bindings"},
|
||||
[SECTION_ENVIRONMENT] = {&parse_section_environment, "environment"},
|
||||
[SECTION_TWEAK] = {&parse_section_tweak, "tweak"},
|
||||
[SECTION_TOUCH] = {&parse_section_touch, "touch"},
|
||||
};
|
||||
|
||||
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_KP_0}}},
|
||||
{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_NEXT, m_ctrl_shift, {{XKB_KEY_x}}},
|
||||
};
|
||||
|
|
@ -2889,7 +2916,8 @@ config_font_list_clone(struct config_font_list *dst,
|
|||
bool
|
||||
config_load(struct config *conf, const char *conf_path,
|
||||
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;
|
||||
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),
|
||||
.shell = get_shell(),
|
||||
.title = xstrdup("foot"),
|
||||
.app_id = xstrdup("foot"),
|
||||
.app_id = (as_server ? xstrdup("footclient") : xstrdup("foot")),
|
||||
.word_delimiters = xc32dup(U",│`|:\"'()[]{}<>"),
|
||||
.size = {
|
||||
.type = CONF_SIZE_PX,
|
||||
|
|
@ -2922,7 +2950,7 @@ config_load(struct config *conf, const char *conf_path,
|
|||
.use_custom_underline_offset = false,
|
||||
.box_drawings_uses_font_glyphs = false,
|
||||
.underline_thickness = {.pt = 0., .px = -1},
|
||||
.dpi_aware = DPI_AWARE_AUTO, /* DPI-aware when scaling-factor == 1 */
|
||||
.dpi_aware = false,
|
||||
.bell = {
|
||||
.urgent = false,
|
||||
.notify = false,
|
||||
|
|
@ -2984,6 +3012,7 @@ config_load(struct config *conf, const char *conf_path,
|
|||
.preferred = CONF_CSD_PREFER_SERVER,
|
||||
.font = {0},
|
||||
.hide_when_maximized = false,
|
||||
.double_click_to_maximize = true,
|
||||
.title_height = 26,
|
||||
.border_width = 5,
|
||||
.border_width_visible = 0,
|
||||
|
|
@ -3018,10 +3047,17 @@ config_load(struct config *conf, const char *conf_path,
|
|||
.sixel = true,
|
||||
},
|
||||
|
||||
.touch = {
|
||||
.long_press_delay = 400,
|
||||
},
|
||||
|
||||
.env_vars = tll_init(),
|
||||
.utempter_path = (strlen(FOOT_DEFAULT_UTEMPTER_PATH) > 0
|
||||
? xstrdup(FOOT_DEFAULT_UTEMPTER_PATH)
|
||||
: NULL),
|
||||
#if defined(UTMP_DEFAULT_HELPER_PATH)
|
||||
.utmp_helper_path = ((strlen(UTMP_DEFAULT_HELPER_PATH) > 0 &&
|
||||
access(UTMP_DEFAULT_HELPER_PATH, X_OK) == 0)
|
||||
? xstrdup(UTMP_DEFAULT_HELPER_PATH)
|
||||
: NULL),
|
||||
#endif
|
||||
.notifications = tll_init(),
|
||||
};
|
||||
|
||||
|
|
@ -3310,8 +3346,8 @@ config_clone(const struct config *old)
|
|||
tll_push_back(conf->env_vars, copy);
|
||||
}
|
||||
|
||||
conf->utempter_path =
|
||||
old->utempter_path != NULL ? xstrdup(old->utempter_path) : NULL;
|
||||
conf->utmp_helper_path =
|
||||
old->utmp_helper_path != NULL ? xstrdup(old->utmp_helper_path) : NULL;
|
||||
|
||||
conf->notifications.length = 0;
|
||||
conf->notifications.head = conf->notifications.tail = 0;
|
||||
|
|
@ -3329,7 +3365,9 @@ UNITTEST
|
|||
user_notifications_t nots = 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);
|
||||
|
||||
struct config *clone = config_clone(&original);
|
||||
|
|
@ -3340,6 +3378,8 @@ UNITTEST
|
|||
config_free(clone);
|
||||
free(clone);
|
||||
|
||||
fcft_fini();
|
||||
|
||||
tll_free(overrides);
|
||||
tll_free(nots);
|
||||
}
|
||||
|
|
@ -3379,7 +3419,7 @@ config_free(struct config *conf)
|
|||
tll_remove(conf->env_vars, it);
|
||||
}
|
||||
|
||||
free(conf->utempter_path);
|
||||
free(conf->utmp_helper_path);
|
||||
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 {DPI_AWARE_AUTO, DPI_AWARE_YES, DPI_AWARE_NO} dpi_aware;
|
||||
bool dpi_aware;
|
||||
struct config_font_list fonts[4];
|
||||
struct font_size_adjustment font_size_adjustment;
|
||||
|
||||
|
|
@ -285,6 +285,7 @@ struct config {
|
|||
uint16_t button_width;
|
||||
|
||||
bool hide_when_maximized;
|
||||
bool double_click_to_maximize;
|
||||
|
||||
struct {
|
||||
bool title_set:1;
|
||||
|
|
@ -320,7 +321,7 @@ struct config {
|
|||
|
||||
env_var_list_t env_vars;
|
||||
|
||||
char *utempter_path;
|
||||
char *utmp_helper_path;
|
||||
|
||||
struct {
|
||||
enum fcft_scaling_filter fcft_filter;
|
||||
|
|
@ -347,6 +348,10 @@ struct config {
|
|||
bool sixel;
|
||||
} tweak;
|
||||
|
||||
struct {
|
||||
uint32_t long_press_delay;
|
||||
} touch;
|
||||
|
||||
user_notifications_t notifications;
|
||||
};
|
||||
|
||||
|
|
@ -355,7 +360,8 @@ bool config_override_apply(struct config *conf, config_override_t *overrides,
|
|||
bool config_load(
|
||||
struct config *conf, const char *path,
|
||||
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);
|
||||
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': {
|
||||
/* Cursor horizontal absolute */
|
||||
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;
|
||||
}
|
||||
|
||||
|
|
@ -1206,8 +1206,10 @@ csi_dispatch(struct terminal *term, uint8_t final)
|
|||
|
||||
if (width >= 0 && height >= 0) {
|
||||
char reply[64];
|
||||
size_t n = xsnprintf(reply, sizeof(reply), "\033[4;%d;%dt",
|
||||
height / term->scale, width / term->scale);
|
||||
size_t n = xsnprintf(
|
||||
reply, sizeof(reply), "\033[4;%d;%dt",
|
||||
(int)round(height / term->scale),
|
||||
(int)(width / term->scale));
|
||||
term_to_slave(term, reply, n);
|
||||
}
|
||||
break;
|
||||
|
|
@ -1229,9 +1231,10 @@ csi_dispatch(struct terminal *term, uint8_t final)
|
|||
|
||||
case 16: { /* report cell size in pixels */
|
||||
char reply[64];
|
||||
size_t n = xsnprintf(reply, sizeof(reply), "\033[6;%d;%dt",
|
||||
term->cell_height / term->scale,
|
||||
term->cell_width / term->scale);
|
||||
size_t n = xsnprintf(
|
||||
reply, sizeof(reply), "\033[6;%d;%dt",
|
||||
(int)round(term->cell_height / term->scale),
|
||||
(int)round(term->cell_width / term->scale));
|
||||
term_to_slave(term, reply, n);
|
||||
break;
|
||||
}
|
||||
|
|
@ -1247,9 +1250,10 @@ csi_dispatch(struct terminal *term, uint8_t final)
|
|||
case 19: { /* report screen size in chars */
|
||||
tll_foreach(term->window->on_outputs, it) {
|
||||
char reply[64];
|
||||
size_t n = xsnprintf(reply, sizeof(reply), "\033[9;%d;%dt",
|
||||
it->item->dim.px_real.height / term->cell_height / term->scale,
|
||||
it->item->dim.px_real.width / term->cell_width / term->scale);
|
||||
size_t n = xsnprintf(
|
||||
reply, sizeof(reply), "\033[9;%d;%dt",
|
||||
(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);
|
||||
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 p3 = vt_param_get(term, 2, 0);
|
||||
|
||||
sixel_init(term, p1, p2, p3);
|
||||
term->vt.dcs.put_handler = &sixel_put;
|
||||
term->vt.dcs.put_handler = sixel_init(term, p1, p2, p3);
|
||||
term->vt.dcs.unhook_handler = &sixel_unhook;
|
||||
break;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -65,7 +65,7 @@ the foot command line
|
|||
|
||||
*-a*,*--app-id*=_ID_
|
||||
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*
|
||||
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
|
||||
current working directory.
|
||||
|
||||
*ctrl*+*shift*+*u*
|
||||
*ctrl*+*shift*+*o*
|
||||
Activate URL mode, allowing you to "launch" URLs.
|
||||
|
||||
*ctrl*+*shift*+*u*
|
||||
Activate Unicode input.
|
||||
|
||||
*ctrl*+*shift*+*z*
|
||||
Jump to the previous, currently not visible, prompt. Requires
|
||||
shell integration.
|
||||
|
|
@ -283,6 +286,18 @@ default) available; see *foot.ini*(5).
|
|||
*wheel*
|
||||
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
|
||||
|
||||
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
|
||||
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
|
||||
_“jump-label”_. The jump-label indicates the _key sequence_
|
||||
(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
|
||||
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
|
||||
variables may be defined in *foot.ini*(5).
|
||||
|
||||
|
|
|
|||
|
|
@ -87,7 +87,7 @@ empty string to be set, but it must be quoted: *KEY=""*)
|
|||
Examples:
|
||||
```
|
||||
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
|
||||
```
|
||||
|
||||
|
|
@ -185,7 +185,7 @@ empty string to be set, but it must be quoted: *KEY=""*)
|
|||
Default: _no_.
|
||||
|
||||
*dpi-aware*
|
||||
*auto*, *yes*, or *no*.
|
||||
Boolean.
|
||||
|
||||
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
|
||||
|
|
@ -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
|
||||
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,
|
||||
which only contains a pre-defined set of sizes, and cannot be
|
||||
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
|
||||
multiplied with the scaling factor.
|
||||
|
||||
Default: _auto_
|
||||
Default: _no_
|
||||
|
||||
*pad*
|
||||
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*
|
||||
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
|
||||
apply window management rules. Default: _foot_.
|
||||
apply window management rules. Default: _foot_ (normal mode), or
|
||||
_footclient_ (server mode).
|
||||
|
||||
*bold-text-in-bright*
|
||||
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).
|
||||
|
||||
_${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.
|
||||
|
||||
|
|
@ -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
|
||||
some cases, the number of physical _cores_ is better.
|
||||
|
||||
*utempter*
|
||||
Path to utempter helper binary. Set to *none* to disable utmp
|
||||
records. Default: _@utempter@_.
|
||||
*utmp-helper*
|
||||
Path to utmp logging helper binary.
|
||||
|
||||
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
|
||||
|
||||
|
|
@ -524,6 +535,14 @@ applications can change these at runtime.
|
|||
|
||||
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
|
||||
|
||||
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*
|
||||
The eight basic ANSI colors (Black, Red, Green, Yellow, Blue,
|
||||
Magenta, Cyan, White). Default: _073642_, _dc322f_, _859900_,
|
||||
_b58900_, _268bd2_, _d33682_, _2aa198_ and _eee8d5_ (a variant of
|
||||
the _solarized dark_ theme).
|
||||
Magenta, Cyan, White). Default: _242424_, _f62b5a_, _47b413_,
|
||||
_e3c401_, _24acd4_, _f2affd_, _13c299_, _e6e6e6_ (starlight
|
||||
theme, V4).
|
||||
|
||||
*bright0*, *bright1* *..* *bright7*
|
||||
The eight bright ANSI colors (Black, Red, Green, Yellow, Blue,
|
||||
Magenta, Cyan, White). Default: _08404f_, _e35f5c_, _9fb700_,
|
||||
_d9a400_, _4ba1de_, _dc619d_, _32c1b6_ and _ffffff_ (a variant of
|
||||
the _solarized dark_ theme).
|
||||
Magenta, Cyan, White). Default: _616161_, _ff4d51_, _35d450_,
|
||||
_e9e836_, _5dc5f8_, _feabf2_, _24dfc4_, _ffffff_ (starlight
|
||||
theme, V4).
|
||||
|
||||
*dim0*, *dim1* *..* *dim7*
|
||||
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
|
||||
instead. Default: _no_.
|
||||
|
||||
*double-click-to-maximize*
|
||||
Boolean. When enabled, double-clicking the CSD titlebar will
|
||||
(un)maximize the window. Default: _yes_.
|
||||
|
||||
*border-width*
|
||||
Width of the border, in pixels (subject to output scaling). Note
|
||||
that the border encompasses the entire window, including the title
|
||||
|
|
@ -815,7 +838,7 @@ e.g. *search-start=none*.
|
|||
*show-urls-launch*
|
||||
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
|
||||
URL mode). Default: _Control+Shift+u_.
|
||||
URL mode). Default: _Control+Shift+o_.
|
||||
|
||||
*show-urls-persistent*
|
||||
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
|
||||
etc is by using an IME.
|
||||
|
||||
Default: _none_.
|
||||
Default: _Control+Shift+u_.
|
||||
|
||||
# SECTION: search-bindings
|
||||
|
||||
|
|
|
|||
|
|
@ -31,7 +31,7 @@ terminal has terminated.
|
|||
|
||||
*-a*,*--app-id*=_ID_
|
||||
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_
|
||||
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
|
||||
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
|
||||
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)
|
||||
|
||||
if utempter_path == ''
|
||||
default_utempter_value = 'not set'
|
||||
if utmp_backend != 'none'
|
||||
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
|
||||
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
|
||||
|
||||
|
||||
conf_data = configuration_data(
|
||||
{
|
||||
'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,
|
||||
output: out,
|
||||
input: preprocessed,
|
||||
command: [sh, '-c', '@0@ < @INPUT@'.format(scdoc_prog.full_path())],
|
||||
command: scdoc_prog.full_path(),
|
||||
capture: true,
|
||||
feed: true,
|
||||
install: true,
|
||||
install_dir: join_paths(get_option('mandir'), 'man@0@'.format(section)))
|
||||
endforeach
|
||||
|
|
|
|||
|
|
@ -37,3 +37,21 @@ static inline bool feature_graphemes(void)
|
|||
return false;
|
||||
#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,
|
||||
Ss=\E[%p1%d q,
|
||||
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,
|
||||
acsc=``aaffggiijjkkllmmnnooppqqrrssttuuvvwwxxyyzz{{||}}~~,
|
||||
bel=^G,
|
||||
|
|
|
|||
51
foot.ini
51
foot.ini
|
|
@ -4,7 +4,7 @@
|
|||
# term=foot (or xterm-256color if built with -Dterminfo=disabled)
|
||||
# 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
|
||||
# locked-title=no
|
||||
|
||||
|
|
@ -20,7 +20,7 @@
|
|||
# underline-offset=<font metrics>
|
||||
# underline-thickness=<font underline thickness>
|
||||
# box-drawings-uses-font-glyphs=no
|
||||
# dpi-aware=auto
|
||||
# dpi-aware=no
|
||||
|
||||
# initial-window-size-pixels=700x500 # Or,
|
||||
# initial-window-size-chars=<COLSxROWS>
|
||||
|
|
@ -34,7 +34,8 @@
|
|||
# word-delimiters=,│`|:"'()[]{}<>
|
||||
# selection-target=primary
|
||||
# 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]
|
||||
# name=value
|
||||
|
|
@ -69,29 +70,32 @@
|
|||
# hide-when-typing=no
|
||||
# alternate-scroll-mode=yes
|
||||
|
||||
[touch]
|
||||
# long-press-delay=400
|
||||
|
||||
[colors]
|
||||
# alpha=1.0
|
||||
# background=002b36
|
||||
# foreground=839496
|
||||
# background=242424
|
||||
# foreground=ffffff
|
||||
|
||||
## Normal/regular colors (color palette 0-7)
|
||||
# regular0=073642 # black
|
||||
# regular1=dc322f # red
|
||||
# regular2=859900 # green
|
||||
# regular3=b58900 # yellow
|
||||
# regular4=268bd2 # blue
|
||||
# regular5=d33682 # magenta
|
||||
# regular6=2aa198 # cyan
|
||||
# regular7=eee8d5 # white
|
||||
# regular0=242424 # black
|
||||
# regular1=f62b5a # red
|
||||
# regular2=47b413 # green
|
||||
# regular3=e3c401 # yellow
|
||||
# regular4=24acd4 # blue
|
||||
# regular5=f2affd # magenta
|
||||
# regular6=13c299 # cyan
|
||||
# regular7=e6e6e6 # white
|
||||
|
||||
## Bright colors (color palette 8-15)
|
||||
# bright0=08404f # bright black
|
||||
# bright1=e35f5c # bright red
|
||||
# bright2=9fb700 # bright green
|
||||
# bright3=d9a400 # bright yellow
|
||||
# bright4=4ba1de # bright blue
|
||||
# bright5=dc619d # bright magenta
|
||||
# bright6=32c1b6 # bright cyan
|
||||
# bright0=616161 # bright black
|
||||
# bright1=ff4d51 # bright red
|
||||
# bright2=35d450 # bright green
|
||||
# bright3=e9e836 # bright yellow
|
||||
# bright4=5dc5f8 # bright blue
|
||||
# bright5=feabf2 # bright magenta
|
||||
# bright6=24dfc4 # bright cyan
|
||||
# bright7=ffffff # bright white
|
||||
|
||||
## dimmed colors (see foot.ini(5) man page)
|
||||
|
|
@ -118,7 +122,8 @@
|
|||
# size=26
|
||||
# font=<primary font>
|
||||
# color=<foreground color>
|
||||
# hide-when-typing=no
|
||||
# hide-when-maximized=no
|
||||
# double-click-to-maximize=yes
|
||||
# border-width=0
|
||||
# border-color=<csd.color>
|
||||
# button-width=26
|
||||
|
|
@ -148,12 +153,12 @@
|
|||
# pipe-visible=[sh -c "xurls | fuzzel | xargs -r firefox"] none
|
||||
# pipe-scrollback=[sh -c "xurls | fuzzel | xargs -r firefox"] none
|
||||
# pipe-selected=[xargs -r firefox] none
|
||||
# show-urls-launch=Control+Shift+u
|
||||
# show-urls-launch=Control+Shift+o
|
||||
# show-urls-copy=none
|
||||
# show-urls-persistent=none
|
||||
# prompt-prev=Control+Shift+z
|
||||
# prompt-next=Control+Shift+x
|
||||
# unicode-input=none
|
||||
# unicode-input=Control+Shift+u
|
||||
# noop=none
|
||||
|
||||
[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/')
|
||||
|
||||
new_version="#define FOOT_VERSION \"${new_version}\"
|
||||
#define FOOT_VERSION_SHORT \"${git_version:-${default_version}}\"
|
||||
#define FOOT_MAJOR ${major}
|
||||
#define FOOT_MINOR ${minor}
|
||||
#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) {
|
||||
int width = it->item.width;
|
||||
int height = it->item.height;
|
||||
pixman_image_t *pix = it->item.pix;
|
||||
pixman_format_code_t pix_fmt = pixman_image_get_format(pix);
|
||||
int stride = stride_for_format_and_width(pix_fmt, width);
|
||||
int original_width = it->item.original.width;
|
||||
int original_height = it->item.original.height;
|
||||
pixman_image_t *original_pix = it->item.original.pix;
|
||||
pixman_format_code_t original_pix_fmt = pixman_image_get_format(original_pix);
|
||||
int original_stride = stride_for_format_and_width(original_pix_fmt, original_width);
|
||||
|
||||
size_t size = stride * height;
|
||||
void *new_data = xmalloc(size);
|
||||
memcpy(new_data, it->item.data, size);
|
||||
size_t original_size = original_stride * original_height;
|
||||
void *new_original_data = xmalloc(original_size);
|
||||
memcpy(new_original_data, it->item.original.data, original_size);
|
||||
|
||||
pixman_image_t *new_pix = pixman_image_create_bits_no_clear(
|
||||
pix_fmt, width, height, new_data, stride);
|
||||
pixman_image_t *new_original_pix = pixman_image_create_bits_no_clear(
|
||||
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 = {
|
||||
.data = new_data,
|
||||
.pix = new_pix,
|
||||
.width = width,
|
||||
.height = height,
|
||||
.pix = (it->item.pix == it->item.original.pix
|
||||
? new_original_pix
|
||||
: (it->item.pix == it->item.scaled.pix
|
||||
? new_scaled_pix
|
||||
: NULL)),
|
||||
.width = it->item.width,
|
||||
.height = it->item.height,
|
||||
.rows = it->item.rows,
|
||||
.cols = it->item.cols,
|
||||
.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);
|
||||
|
|
|
|||
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)));
|
||||
}
|
||||
|
||||
const char *
|
||||
enum cursor_shape
|
||||
xcursor_for_csd_border(struct terminal *term, int x, int y)
|
||||
{
|
||||
if (is_top_left(term, x, y)) return XCURSOR_TOP_LEFT_CORNER;
|
||||
else if (is_top_right(term, x, y)) return XCURSOR_TOP_RIGHT_CORNER;
|
||||
else if (is_bottom_left(term, x, y)) return XCURSOR_BOTTOM_LEFT_CORNER;
|
||||
else if (is_bottom_right(term, x, y)) return XCURSOR_BOTTOM_RIGHT_CORNER;
|
||||
else if (term->active_surface == TERM_SURF_BORDER_LEFT) return XCURSOR_LEFT_SIDE;
|
||||
else if (term->active_surface == TERM_SURF_BORDER_RIGHT) return XCURSOR_RIGHT_SIDE;
|
||||
else if (term->active_surface == TERM_SURF_BORDER_TOP) return XCURSOR_TOP_SIDE;
|
||||
else if (term->active_surface == TERM_SURF_BORDER_BOTTOM) return XCURSOR_BOTTOM_SIDE;
|
||||
if (is_top_left(term, x, y)) return CURSOR_SHAPE_TOP_LEFT_CORNER;
|
||||
else if (is_top_right(term, x, y)) return CURSOR_SHAPE_TOP_RIGHT_CORNER;
|
||||
else if (is_bottom_left(term, x, y)) return CURSOR_SHAPE_BOTTOM_LEFT_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 CURSOR_SHAPE_LEFT_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 CURSOR_SHAPE_TOP_SIDE;
|
||||
else if (term->active_surface == TERM_SURF_BORDER_BOTTOM) return CURSOR_SHAPE_BOTTOM_SIDE;
|
||||
else {
|
||||
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
|
||||
wl_pointer_enter(void *data, struct wl_pointer *wl_pointer,
|
||||
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;
|
||||
|
||||
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 terminal *term = win->term;
|
||||
|
||||
|
|
@ -1759,22 +1807,7 @@ wl_pointer_enter(void *data, struct wl_pointer *wl_pointer,
|
|||
|
||||
switch (term->active_surface) {
|
||||
case TERM_SURF_GRID: {
|
||||
/*
|
||||
* 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;
|
||||
|
||||
mouse_coord_pixel_to_cell(seat, term, x, y);
|
||||
break;
|
||||
}
|
||||
|
||||
|
|
@ -1802,6 +1835,14 @@ wl_pointer_leave(void *data, struct wl_pointer *wl_pointer,
|
|||
uint32_t serial, struct wl_surface *surface)
|
||||
{
|
||||
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;
|
||||
|
||||
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 */
|
||||
seat->pointer.xcursor = NULL;
|
||||
seat->pointer.shape = CURSOR_SHAPE_NONE;
|
||||
|
||||
/* Reset mouse state */
|
||||
seat->mouse.x = seat->mouse.y = 0;
|
||||
seat->mouse.col = seat->mouse.row = 0;
|
||||
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));
|
||||
mouse_button_state_reset(seat);
|
||||
for (size_t i = 0; i < ALEN(seat->mouse.aggregated); i++)
|
||||
seat->mouse.aggregated[i] = 0.0;
|
||||
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)
|
||||
{
|
||||
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 terminal *term = seat->mouse_focus;
|
||||
|
||||
|
|
@ -2102,6 +2145,11 @@ wl_pointer_button(void *data, struct wl_pointer *wl_pointer,
|
|||
xassert(serial != 0);
|
||||
|
||||
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 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;
|
||||
|
||||
/* 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)
|
||||
xdg_toplevel_unset_maximized(win->xdg_toplevel);
|
||||
else
|
||||
|
|
@ -2559,6 +2610,9 @@ wl_pointer_axis(void *data, struct wl_pointer *wl_pointer,
|
|||
{
|
||||
struct seat *seat = data;
|
||||
|
||||
if (seat->touch.state != TOUCH_STATE_INHIBITED)
|
||||
return;
|
||||
|
||||
if (seat->mouse.have_discrete)
|
||||
return;
|
||||
|
||||
|
|
@ -2588,6 +2642,10 @@ wl_pointer_axis_discrete(void *data, struct wl_pointer *wl_pointer,
|
|||
uint32_t axis, int32_t discrete)
|
||||
{
|
||||
struct seat *seat = data;
|
||||
|
||||
if (seat->touch.state != TOUCH_STATE_INHIBITED)
|
||||
return;
|
||||
|
||||
seat->mouse.have_discrete = true;
|
||||
|
||||
int amount = discrete;
|
||||
|
|
@ -2604,6 +2662,10 @@ static void
|
|||
wl_pointer_frame(void *data, struct wl_pointer *wl_pointer)
|
||||
{
|
||||
struct seat *seat = data;
|
||||
|
||||
if (seat->touch.state != TOUCH_STATE_INHIBITED)
|
||||
return;
|
||||
|
||||
seat->mouse.have_discrete = false;
|
||||
}
|
||||
|
||||
|
|
@ -2619,6 +2681,9 @@ wl_pointer_axis_stop(void *data, struct wl_pointer *wl_pointer,
|
|||
{
|
||||
struct seat *seat = data;
|
||||
|
||||
if (seat->touch.state != TOUCH_STATE_INHIBITED)
|
||||
return;
|
||||
|
||||
xassert(axis < ALEN(seat->mouse.aggregated));
|
||||
seat->mouse.aggregated[axis] = 0.;
|
||||
}
|
||||
|
|
@ -2634,3 +2699,166 @@ const struct wl_pointer_listener pointer_listener = {
|
|||
.axis_stop = wl_pointer_axis_stop,
|
||||
.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 <wayland-client.h>
|
||||
|
||||
#include "wayland.h"
|
||||
#include "cursor-shape.h"
|
||||
#include "misc.h"
|
||||
#include "wayland.h"
|
||||
|
||||
/*
|
||||
* Custom defines for mouse wheel left/right buttons.
|
||||
|
|
@ -25,6 +26,7 @@
|
|||
|
||||
extern const struct wl_keyboard_listener keyboard_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);
|
||||
|
||||
|
|
@ -33,4 +35,4 @@ void get_current_modifiers(const struct seat *seat,
|
|||
xkb_mod_mask_t *consumed,
|
||||
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)
|
||||
{
|
||||
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,
|
||||
feature_pgo() ? '+' : '-',
|
||||
feature_ime() ? '+' : '-',
|
||||
feature_graphemes() ? '+' : '-',
|
||||
feature_fractional_scaling() ? '+' : '-',
|
||||
feature_cursor_shape() ? '+' : '-',
|
||||
feature_assertions() ? '+' : '-');
|
||||
return buf;
|
||||
}
|
||||
|
|
@ -450,6 +453,7 @@ main(int argc, char *const *argv)
|
|||
"C.UTF-8",
|
||||
"en_US.UTF-8",
|
||||
};
|
||||
char *saved_locale = xstrdup(locale);
|
||||
|
||||
/*
|
||||
* 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) {
|
||||
LOG_WARN("'%s' is not a UTF-8 locale, using '%s' instead",
|
||||
locale, fallback_locale);
|
||||
saved_locale, fallback_locale);
|
||||
|
||||
user_notification_add_fmt(
|
||||
&user_notifications, USER_NOTIFICATION_WARNING,
|
||||
"'%s' is not a UTF-8 locale, using '%s' instead",
|
||||
locale, fallback_locale);
|
||||
saved_locale, fallback_locale);
|
||||
|
||||
bad_locale = false;
|
||||
break;
|
||||
|
|
@ -476,18 +480,19 @@ main(int argc, char *const *argv)
|
|||
if (bad_locale) {
|
||||
LOG_ERR(
|
||||
"'%s' is not a UTF-8 locale, and failed to find a fallback",
|
||||
locale);
|
||||
saved_locale);
|
||||
|
||||
user_notification_add_fmt(
|
||||
&user_notifications, USER_NOTIFICATION_ERROR,
|
||||
"'%s' is not a UTF-8 locale, and failed to find a fallback",
|
||||
locale);
|
||||
saved_locale);
|
||||
}
|
||||
free(saved_locale);
|
||||
}
|
||||
|
||||
struct config conf = {NULL};
|
||||
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);
|
||||
if (!conf_successful) {
|
||||
|
|
|
|||
92
meson.build
92
meson.build
|
|
@ -1,7 +1,7 @@
|
|||
project('foot', 'c',
|
||||
version: '1.14.0',
|
||||
version: '1.15.0',
|
||||
license: 'MIT',
|
||||
meson_version: '>=0.58.0',
|
||||
meson_version: '>=0.59.0',
|
||||
default_options: [
|
||||
'c_std=c11',
|
||||
'warning_level=1',
|
||||
|
|
@ -16,30 +16,54 @@ if cc.has_function('memfd_create')
|
|||
add_project_arguments('-DMEMFD_CREATE', language: 'c')
|
||||
endif
|
||||
|
||||
utempter_path = get_option('default-utempter-path')
|
||||
if utempter_path == ''
|
||||
utempter = find_program(
|
||||
'utempter',
|
||||
required: false,
|
||||
dirs: [join_paths(get_option('prefix'), get_option('libdir'), 'utempter'),
|
||||
join_paths(get_option('prefix'), get_option('libexecdir'), 'utempter'),
|
||||
'/usr/lib/utempter',
|
||||
'/usr/libexec/utempter',
|
||||
'/lib/utempter']
|
||||
)
|
||||
if utempter.found()
|
||||
utempter_path = utempter.full_path()
|
||||
utmp_backend = get_option('utmp-backend')
|
||||
if utmp_backend == 'auto'
|
||||
host_os = host_machine.system()
|
||||
if host_os == 'linux'
|
||||
utmp_backend = 'libutempter'
|
||||
elif host_os == 'freebsd'
|
||||
utmp_backend = 'ulog'
|
||||
else
|
||||
utempter_path = ''
|
||||
utmp_backend = 'none'
|
||||
endif
|
||||
elif utempter_path == 'none'
|
||||
utempter_path = ''
|
||||
endif
|
||||
|
||||
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
|
||||
|
||||
add_project_arguments(
|
||||
['-D_GNU_SOURCE=200809L',
|
||||
'-DFOOT_DEFAULT_TERM="@0@"'.format(get_option('default-terminfo')),
|
||||
'-DFOOT_DEFAULT_UTEMPTER_PATH="@0@"'.format(utempter_path)] +
|
||||
'-DFOOT_DEFAULT_TERM="@0@"'.format(get_option('default-terminfo'))] +
|
||||
(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
|
||||
? ['-D_DEBUG']
|
||||
: [cc.get_supported_arguments('-fno-asynchronous-unwind-tables')]) +
|
||||
|
|
@ -133,6 +157,27 @@ wl_proto_xml = [
|
|||
if wayland_protocols.version().version_compare('>=1.21')
|
||||
add_project_arguments('-DHAVE_XDG_ACTIVATION', language: 'c')
|
||||
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
|
||||
|
||||
foreach prot : wl_proto_xml
|
||||
|
|
@ -188,6 +233,7 @@ vtlib = static_library(
|
|||
'vtlib',
|
||||
'base64.c', 'base64.h',
|
||||
'composed.c', 'composed.h',
|
||||
'cursor-shape.c', 'cursor-shape.h',
|
||||
'csi.c', 'csi.h',
|
||||
'dcs.c', 'dcs.h',
|
||||
'macros.h',
|
||||
|
|
@ -343,7 +389,11 @@ summary(
|
|||
'Themes': get_option('themes'),
|
||||
'IME': get_option('ime'),
|
||||
'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(),
|
||||
'Terminfo install location': terminfo_install_location,
|
||||
'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: '',
|
||||
description: 'Where to install the systemd service files (absolute path). Default: ${systemduserunitdir}')
|
||||
|
||||
option('default-utempter-path', type: 'string', value: '',
|
||||
description: 'Default path to utempter helper binary. Default: auto-detect')
|
||||
option('utmp-backend', type: 'combo', value: 'auto', choices: ['none', 'libutempter', 'ulog', 'auto'],
|
||||
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
|
||||
GenericName=Terminal
|
||||
Comment=A wayland native terminal emulator (server)
|
||||
StartupWMClass=foot
|
||||
|
|
|
|||
|
|
@ -9,4 +9,4 @@ Keywords=shell;prompt;command;commandline;
|
|||
Name=Foot Client
|
||||
GenericName=Terminal
|
||||
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:
|
||||
term->colors.bg = color;
|
||||
if (have_alpha)
|
||||
if (have_alpha) {
|
||||
const bool changed = term->colors.alpha != alpha;
|
||||
term->colors.alpha = alpha;
|
||||
|
||||
if (changed) {
|
||||
wayl_win_alpha_changed(term->window);
|
||||
term_font_subpixel_changed(term);
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case 17:
|
||||
|
|
|
|||
|
|
@ -76,15 +76,15 @@ render_xcursor_is_valid(const struct seat *seat, const char *cursor)
|
|||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
const char *
|
||||
enum cursor_shape
|
||||
xcursor_for_csd_border(struct terminal *term, int x, int y)
|
||||
{
|
||||
return XCURSOR_LEFT_PTR;
|
||||
return CURSOR_SHAPE_LEFT_PTR;
|
||||
}
|
||||
|
||||
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_alpha_changed(struct wl_window *win) {}
|
||||
bool wayl_win_set_urgent(struct wl_window *win) { return true; }
|
||||
bool wayl_fractional_scaling(const struct wayland *wayl) { return true; }
|
||||
|
||||
bool
|
||||
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++)
|
||||
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 */
|
||||
void quirk_weston_csd_on(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_urls(struct terminal *term);
|
||||
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);
|
||||
|
||||
struct render_worker_context {
|
||||
|
|
|
|||
|
|
@ -207,8 +207,8 @@ def main():
|
|||
six_height, six_width = last_size
|
||||
six_rows = (six_height + 5) // 6 # Round up; each sixel is 6 pixels
|
||||
|
||||
# Begin sixel
|
||||
out.write('\033Pq')
|
||||
# Begin sixel (with P2=1 - empty sixels are transparent)
|
||||
out.write('\033P;1q')
|
||||
|
||||
# Sixel size. Without this, sixels will be
|
||||
# auto-resized on cell-boundaries.
|
||||
|
|
|
|||
7
search.c
7
search.c
|
|
@ -15,6 +15,7 @@
|
|||
#include "input.h"
|
||||
#include "key-binding.h"
|
||||
#include "misc.h"
|
||||
#include "quirks.h"
|
||||
#include "render.h"
|
||||
#include "selection.h"
|
||||
#include "shm.h"
|
||||
|
|
@ -117,11 +118,6 @@ search_cancel_keep_selection(struct terminal *term)
|
|||
|
||||
term_xcursor_update(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
|
||||
|
|
@ -833,6 +829,7 @@ execute_binding(struct seat *seat, struct terminal *term,
|
|||
grid->view = ensure_view_is_allocated(
|
||||
term, term->search.original_view);
|
||||
}
|
||||
term_damage_view(term);
|
||||
search_cancel(term);
|
||||
return true;
|
||||
|
||||
|
|
|
|||
653
sixel.c
653
sixel.c
|
|
@ -16,6 +16,9 @@
|
|||
|
||||
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
|
||||
sixel_fini(struct terminal *term)
|
||||
{
|
||||
|
|
@ -24,7 +27,7 @@ sixel_fini(struct terminal *term)
|
|||
free(term->sixel.shared_palette);
|
||||
}
|
||||
|
||||
void
|
||||
sixel_put
|
||||
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.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.pos = (struct coord){0, 0};
|
||||
term->sixel.max_non_empty_row_no = -1;
|
||||
term->sixel.row_byte_ofs = 0;
|
||||
term->sixel.color_idx = 0;
|
||||
term->sixel.pan = pan;
|
||||
term->sixel.pad = pad;
|
||||
term->sixel.param = 0;
|
||||
term->sixel.param_idx = 0;
|
||||
memset(term->sixel.params, 0, sizeof(term->sixel.params));
|
||||
term->sixel.transparent_bg = p2 == 1;
|
||||
term->sixel.image.data = xmalloc(1 * 6 * sizeof(term->sixel.image.data[0]));
|
||||
term->sixel.image.width = 1;
|
||||
term->sixel.image.height = 6;
|
||||
term->sixel.image.data = NULL;
|
||||
term->sixel.image.width = 0;
|
||||
term->sixel.image.height = 0;
|
||||
|
||||
/* TODO: default palette */
|
||||
|
||||
|
|
@ -73,38 +90,69 @@ sixel_init(struct terminal *term, int p1, int p2, int p3)
|
|||
|
||||
switch (term->vt.attrs.bg_src) {
|
||||
case COLOR_RGB:
|
||||
bg = term->vt.attrs.bg;
|
||||
bg = 0xffu << 24 | term->vt.attrs.bg;
|
||||
break;
|
||||
|
||||
case COLOR_BASE16:
|
||||
case COLOR_BASE256:
|
||||
bg = term->colors.table[term->vt.attrs.bg];
|
||||
bg = 0xffu << 24 | term->colors.table[term->vt.attrs.bg];
|
||||
break;
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
term->sixel.default_bg = term->sixel.transparent_bg
|
||||
? 0x00000000u
|
||||
: 0xffu << 24 | bg;
|
||||
|
||||
for (size_t i = 0; i < 1 * 6; i++)
|
||||
term->sixel.image.data[i] = term->sixel.default_bg;
|
||||
: bg;
|
||||
|
||||
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
|
||||
sixel_destroy(struct sixel *sixel)
|
||||
{
|
||||
if (sixel->pix != NULL)
|
||||
pixman_image_unref(sixel->pix);
|
||||
sixel_invalidate_cache(sixel);
|
||||
|
||||
free(sixel->data);
|
||||
sixel->pix = NULL;
|
||||
sixel->data = NULL;
|
||||
if (sixel->original.pix != NULL)
|
||||
pixman_image_unref(sixel->original.pix);
|
||||
|
||||
free(sixel->original.data);
|
||||
sixel->original.pix = NULL;
|
||||
sixel->original.data = NULL;
|
||||
}
|
||||
|
||||
void
|
||||
|
|
@ -367,10 +415,14 @@ blend_new_image_over_old(const struct terminal *term,
|
|||
xassert(pix != 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;
|
||||
const int img_ofs_x = col * term->cell_width;
|
||||
const int img_ofs_y = row * term->cell_height;
|
||||
/*
|
||||
* TODO: handle images being emitted with different cell dimensions
|
||||
*/
|
||||
|
||||
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_height = pixman_image_get_height(*pix);
|
||||
|
||||
|
|
@ -400,7 +452,7 @@ blend_new_image_over_old(const struct terminal *term,
|
|||
*/
|
||||
pixman_image_composite32(
|
||||
PIXMAN_OP_OVER_REVERSE,
|
||||
six->pix, NULL, *pix,
|
||||
six->original.pix, NULL, *pix,
|
||||
box->x1 - six_ofs_x, box->y1 - six_ofs_y,
|
||||
0, 0,
|
||||
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
|
||||
* 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(
|
||||
six_ofs_x + six->width,
|
||||
(box->x2 + term->cell_width - 1) / term->cell_width * term->cell_width)
|
||||
six_ofs_x + six->original.width,
|
||||
(box->x2 + six->cell_width - 1) / six->cell_width * six->cell_width)
|
||||
: 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(
|
||||
six_ofs_y + six->height,
|
||||
(box->y2 + term->cell_height - 1) / term->cell_height * term->cell_height)
|
||||
six_ofs_y + six->original.height,
|
||||
(box->y2 + six->cell_height - 1) / six->cell_height * six->cell_height)
|
||||
: box->y2;
|
||||
|
||||
/* 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 */
|
||||
pixman_image_composite32(
|
||||
PIXMAN_OP_SRC,
|
||||
six->pix, NULL, pix2,
|
||||
six->original.pix, NULL, pix2,
|
||||
box->x1 - six_ofs_x, box->y2 - six_ofs_y,
|
||||
0, 0,
|
||||
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 */
|
||||
pixman_image_composite32(
|
||||
PIXMAN_OP_SRC,
|
||||
six->pix, NULL, pix2,
|
||||
six->original.pix, NULL, pix2,
|
||||
box->x2 - six_ofs_x, box->y1 - six_ofs_y,
|
||||
0, 0,
|
||||
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_init_rect(
|
||||
&six_rect,
|
||||
six->pos.col * term->cell_width, six->pos.row * term->cell_height,
|
||||
six->width, six->height);
|
||||
six->pos.col * six->cell_width, six->pos.row * six->cell_height,
|
||||
six->original.width, six->original.height);
|
||||
|
||||
pixman_region32_t overwrite_rect;
|
||||
pixman_region32_init_rect(
|
||||
&overwrite_rect,
|
||||
col * term->cell_width, row * term->cell_height,
|
||||
width * term->cell_width, height * term->cell_height);
|
||||
col * six->cell_width, row * six->cell_height,
|
||||
width * six->cell_width, height * six->cell_height);
|
||||
|
||||
#if defined(_DEBUG)
|
||||
pixman_region32_t cell_intersection;
|
||||
|
|
@ -568,7 +620,6 @@ sixel_overwrite(struct terminal *term, struct sixel *six,
|
|||
if (pix != NULL)
|
||||
blend_new_image_over_old(term, six, &six_rect, row, col, pix, opaque);
|
||||
|
||||
|
||||
pixman_region32_t diff;
|
||||
pixman_region32_init(&diff);
|
||||
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,
|
||||
boxes[i].x1, boxes[i].y1, boxes[i].x2, boxes[i].y2);
|
||||
|
||||
xassert(boxes[i].x1 % term->cell_width == 0);
|
||||
xassert(boxes[i].y1 % term->cell_height == 0);
|
||||
xassert(boxes[i].x1 % six->cell_width == 0);
|
||||
xassert(boxes[i].y1 % six->cell_height == 0);
|
||||
|
||||
/* New image's position, in cells */
|
||||
const int new_col = boxes[i].x1 / term->cell_width;
|
||||
const int new_row = boxes[i].y1 / term->cell_height;
|
||||
const int new_col = boxes[i].x1 / six->cell_width;
|
||||
const int new_row = boxes[i].y1 / six->cell_height;
|
||||
|
||||
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;
|
||||
|
||||
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 */
|
||||
const int x_ofs = boxes[i].x1 - six->pos.col * term->cell_width;
|
||||
const int y_ofs = boxes[i].y1 - six->pos.row * term->cell_height;
|
||||
const int x_ofs = boxes[i].x1 - six->pos.col * six->cell_width;
|
||||
const int y_ofs = boxes[i].y1 - six->pos.row * six->cell_height;
|
||||
|
||||
/* Copy image data, one row at a time */
|
||||
for (size_t j = 0; j < new_height; j++) {
|
||||
memcpy(
|
||||
&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));
|
||||
}
|
||||
|
||||
|
|
@ -616,14 +667,27 @@ sixel_overwrite(struct terminal *term, struct sixel *six,
|
|||
new_width, new_height, new_data, new_width * sizeof(uint32_t));
|
||||
|
||||
struct sixel new_six = {
|
||||
.data = new_data,
|
||||
.pix = new_pix,
|
||||
.width = new_width,
|
||||
.height = new_height,
|
||||
.pix = NULL,
|
||||
.width = -1,
|
||||
.height = -1,
|
||||
.pos = {.col = new_col, .row = new_row},
|
||||
.cols = (new_width + term->cell_width - 1) / term->cell_width,
|
||||
.rows = (new_height + term->cell_height - 1) / term->cell_height,
|
||||
.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,
|
||||
.pix = new_pix,
|
||||
.width = new_width,
|
||||
.height = new_height,
|
||||
},
|
||||
.scaled = {
|
||||
.data = NULL,
|
||||
.pix = NULL,
|
||||
.width = -1,
|
||||
.height = -1,
|
||||
},
|
||||
};
|
||||
|
||||
#if defined(_DEBUG)
|
||||
|
|
@ -818,23 +882,94 @@ sixel_overwrite_at_cursor(struct terminal *term, int width)
|
|||
void
|
||||
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->normal.sixel_images, it) {
|
||||
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;
|
||||
tll_foreach(term->alt.sixel_images, it)
|
||||
sixel_invalidate_cache(&it->item);
|
||||
}
|
||||
|
||||
void
|
||||
sixel_sync_cache(const struct terminal *term, struct sixel *six)
|
||||
{
|
||||
if (six->pix != NULL) {
|
||||
#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 = &term->alt;
|
||||
tll_foreach(term->alt.sixel_images, it) {
|
||||
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;
|
||||
}
|
||||
/* Cache should be invalid */
|
||||
xassert(six->scaled.data == NULL);
|
||||
xassert(six->scaled.pix == NULL);
|
||||
xassert(six->scaled.width < 0);
|
||||
xassert(six->scaled.height < 0);
|
||||
|
||||
term->grid = g;
|
||||
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
|
||||
|
|
@ -897,14 +1032,15 @@ sixel_reflow_grid(struct terminal *term, struct grid *grid)
|
|||
* allowed of course */
|
||||
_sixel_overwrite_by_rectangle(
|
||||
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)) {
|
||||
it->item.data = pixman_image_get_data(it->item.pix);
|
||||
it->item.width = pixman_image_get_width(it->item.pix);
|
||||
it->item.height = pixman_image_get_height(it->item.pix);
|
||||
it->item.cols = (it->item.width + term->cell_width - 1) / term->cell_width;
|
||||
it->item.rows = (it->item.height + term->cell_height - 1) / term->cell_height;
|
||||
if (it->item.original.data != pixman_image_get_data(it->item.original.pix)) {
|
||||
it->item.original.data = pixman_image_get_data(it->item.original.pix);
|
||||
it->item.original.width = pixman_image_get_width(it->item.original.pix);
|
||||
it->item.original.height = pixman_image_get_height(it->item.original.pix);
|
||||
it->item.cols = (it->item.original.width + it->item.cell_width - 1) / it->item.cell_width;
|
||||
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);
|
||||
|
|
@ -926,13 +1062,6 @@ sixel_reflow(struct terminal *term)
|
|||
void
|
||||
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_rows_left = term->sixel.image.height;
|
||||
const int stride = term->sixel.image.width * sizeof(uint32_t);
|
||||
|
|
@ -1005,13 +1134,27 @@ sixel_unhook(struct terminal *term)
|
|||
}
|
||||
|
||||
struct sixel image = {
|
||||
.data = img_data,
|
||||
.width = width,
|
||||
.height = height,
|
||||
.pix = NULL,
|
||||
.width = -1,
|
||||
.height = -1,
|
||||
.rows = (height + term->cell_height - 1) / term->cell_height,
|
||||
.cols = (width + term->cell_width - 1) / term->cell_width,
|
||||
.pos = (struct coord){start_col, cur_row},
|
||||
.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);
|
||||
|
|
@ -1022,13 +1165,65 @@ sixel_unhook(struct terminal *term)
|
|||
image.width, image.height,
|
||||
image.pos.row, image.pos.row + image.rows);
|
||||
|
||||
image.pix = pixman_image_create_bits_no_clear(
|
||||
PIXMAN_a8r8g8b8, image.width, image.height, img_data, stride);
|
||||
image.original.pix = pixman_image_create_bits_no_clear(
|
||||
PIXMAN_a8r8g8b8, image.original.width, image.original.height,
|
||||
img_data, stride);
|
||||
|
||||
pixel_row_idx += height;
|
||||
pixel_rows_left -= height;
|
||||
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 */
|
||||
for (size_t i = 0; i < image.rows; 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;
|
||||
}
|
||||
|
||||
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(
|
||||
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)) {
|
||||
image.data = pixman_image_get_data(image.pix);
|
||||
image.width = pixman_image_get_width(image.pix);
|
||||
image.height = pixman_image_get_height(image.pix);
|
||||
image.cols = (image.width + term->cell_width - 1) / term->cell_width;
|
||||
image.rows = (image.height + term->cell_height - 1) / term->cell_height;
|
||||
if (image.original.data != pixman_image_get_data(image.original.pix)) {
|
||||
image.original.data = pixman_image_get_data(image.original.pix);
|
||||
image.original.width = pixman_image_get_width(image.original.pix);
|
||||
image.original.height = pixman_image_get_height(image.original.pix);
|
||||
image.cols = (image.original.width + image.cell_width - 1) / image.cell_width;
|
||||
image.rows = (image.original.height + image.cell_height - 1) / image.cell_height;
|
||||
sixel_invalidate_cache(&image);
|
||||
}
|
||||
|
||||
sixel_insert(term, image);
|
||||
|
|
@ -1104,10 +1280,6 @@ sixel_unhook(struct terminal *term)
|
|||
static void
|
||||
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)) {
|
||||
LOG_WARN("maximum image dimensions exceeded, truncating");
|
||||
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))
|
||||
return;
|
||||
|
||||
const int sixel_row_height = 6 * term->sixel.pan;
|
||||
|
||||
uint32_t *old_data = term->sixel.image.data;
|
||||
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(alloc_height > 0);
|
||||
|
|
@ -1165,9 +1350,14 @@ resize_vertically(struct terminal *term, int new_height)
|
|||
|
||||
int alloc_height = (new_height + 6 - 1) / 6 * 6;
|
||||
|
||||
xassert(width > 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(
|
||||
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_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_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 < 6);
|
||||
xassert(alloc_new_height - new_height < sixel_row_height);
|
||||
|
||||
uint32_t *new_data = NULL;
|
||||
uint32_t bg = term->sixel.default_bg;
|
||||
|
|
@ -1261,34 +1455,86 @@ resize(struct terminal *term, int new_width, int new_height)
|
|||
}
|
||||
|
||||
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.row < term->sixel.image.height);
|
||||
|
||||
const int pan = term->sixel.pan;
|
||||
size_t ofs = term->sixel.row_byte_ofs + col;
|
||||
uint32_t *data = &term->sixel.image.data[ofs];
|
||||
|
||||
int max_non_empty_row = -1;
|
||||
int row = term->sixel.pos.row;
|
||||
|
||||
for (int i = 0; i < 6; i++, sixel >>= 1, data += width) {
|
||||
for (int i = 0; i < 6; i++, sixel >>= 1) {
|
||||
if (sixel & 1) {
|
||||
*data = color;
|
||||
max_non_empty_row = row + i;
|
||||
}
|
||||
for (int r = 0; r < pan; r++, data += width)
|
||||
*data = color;
|
||||
} else
|
||||
data += width * pan;
|
||||
}
|
||||
|
||||
xassert(sixel == 0);
|
||||
|
||||
term->sixel.max_non_empty_row_no = max(
|
||||
term->sixel.max_non_empty_row_no,
|
||||
max_non_empty_row);
|
||||
}
|
||||
|
||||
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 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;
|
||||
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;
|
||||
}
|
||||
|
||||
IGNORE_WARNING("-Wpedantic")
|
||||
|
||||
static void
|
||||
decsixel(struct terminal *term, uint8_t c)
|
||||
decsixel_generic(struct terminal *term, uint8_t c)
|
||||
{
|
||||
switch (c) {
|
||||
case '"':
|
||||
|
|
@ -1319,6 +1567,7 @@ decsixel(struct terminal *term, uint8_t c)
|
|||
term->sixel.state = SIXEL_DECGRI;
|
||||
term->sixel.param = 0;
|
||||
term->sixel.param_idx = 0;
|
||||
term->sixel.repeat_count = 1;
|
||||
break;
|
||||
|
||||
case '#':
|
||||
|
|
@ -1341,27 +1590,18 @@ decsixel(struct terminal *term, uint8_t c)
|
|||
break;
|
||||
|
||||
case '-':
|
||||
term->sixel.pos.row += 6;
|
||||
term->sixel.pos.row += 6 * term->sixel.pan;
|
||||
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 (!resize_vertically(term, term->sixel.pos.row + 6))
|
||||
term->sixel.pos.col = term->sixel.max_width + 1;
|
||||
if (!resize_vertically(term, term->sixel.pos.row + 6 * term->sixel.pan))
|
||||
term->sixel.pos.col = term->sixel.max_width + 1 * term->sixel.pad;
|
||||
}
|
||||
break;
|
||||
|
||||
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 '^': 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);
|
||||
case '?' ... '~':
|
||||
sixel_add_many_generic(term, c - 63, 1);
|
||||
break;
|
||||
|
||||
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
|
||||
decgra(struct terminal *term, uint8_t c)
|
||||
{
|
||||
|
|
@ -1404,63 +1655,88 @@ decgra(struct terminal *term, uint8_t c)
|
|||
pan = pan > 0 ? pan : 1;
|
||||
pad = pad > 0 ? pad : 1;
|
||||
|
||||
LOG_DBG("pan=%u, pad=%u (aspect ratio = %u), size=%ux%u",
|
||||
pan, pad, pan / pad, ph, pv);
|
||||
pv *= pan;
|
||||
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 &&
|
||||
ph <= term->sixel.max_height && pv <= term->sixel.max_width)
|
||||
{
|
||||
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;
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
IGNORE_WARNING("-Wpedantic")
|
||||
|
||||
static void
|
||||
decgri(struct terminal *term, uint8_t c)
|
||||
decgri_generic(struct terminal *term, uint8_t c)
|
||||
{
|
||||
switch (c) {
|
||||
case '0': case '1': case '2': case '3': case '4':
|
||||
case '5': case '6': case '7': case '8': case '9':
|
||||
term->sixel.param *= 10;
|
||||
term->sixel.param += c - '0';
|
||||
case '5': case '6': case '7': case '8': case '9': {
|
||||
unsigned param = term->sixel.param;
|
||||
param *= 10;
|
||||
param += c - '0';
|
||||
term->sixel.repeat_count = term->sixel.param = param;
|
||||
break;
|
||||
}
|
||||
|
||||
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 '^': 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 '~': {
|
||||
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);
|
||||
case '?' ... '~': {
|
||||
unsigned count = term->sixel.repeat_count;
|
||||
if (unlikely(count == 0)) {
|
||||
count = 1;
|
||||
}
|
||||
|
||||
sixel_add_many_generic(term, c - 63, count);
|
||||
term->sixel.state = SIXEL_DECSIXEL;
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
term->sixel.state = SIXEL_DECSIXEL;
|
||||
sixel_put(term, c);
|
||||
term->vt.dcs.put_handler(term, c);
|
||||
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
|
||||
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.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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
sixel_put(struct terminal *term, uint8_t c)
|
||||
static void
|
||||
sixel_put_generic(struct terminal *term, uint8_t c)
|
||||
{
|
||||
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_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;
|
||||
}
|
||||
|
||||
|
|
|
|||
6
sixel.h
6
sixel.h
|
|
@ -6,10 +6,11 @@
|
|||
#define SIXEL_MAX_WIDTH 10000u
|
||||
#define SIXEL_MAX_HEIGHT 10000u
|
||||
|
||||
typedef void (*sixel_put)(struct terminal *term, uint8_t c);
|
||||
|
||||
void sixel_fini(struct terminal *term);
|
||||
|
||||
void sixel_init(struct terminal *term, int p1, int p2, int p3);
|
||||
void sixel_put(struct terminal *term, uint8_t c);
|
||||
sixel_put sixel_init(struct terminal *term, int p1, int p2, int p3);
|
||||
void sixel_unhook(struct terminal *term);
|
||||
|
||||
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_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);
|
||||
|
||||
|
|
|
|||
6
slave.c
6
slave.c
|
|
@ -21,7 +21,6 @@
|
|||
#include "macros.h"
|
||||
#include "terminal.h"
|
||||
#include "tokenize.h"
|
||||
#include "version.h"
|
||||
#include "xmalloc.h"
|
||||
|
||||
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_PROGRAM", "foot", 1);
|
||||
setenv("TERM_PROGRAM_VERSION", FOOT_VERSION_SHORT, 1);
|
||||
setenv("COLORTERM", "truecolor", 1);
|
||||
setenv("PWD", cwd, 1);
|
||||
|
||||
unsetenv("TERM_PROGRAM");
|
||||
unsetenv("TERM_PROGRAM_VERSION");
|
||||
|
||||
#if defined(FOOT_TERMINFO_PATH)
|
||||
setenv("TERMINFO", FOOT_TERMINFO_PATH, 1);
|
||||
#endif
|
||||
|
|
|
|||
336
terminal.c
336
terminal.c
|
|
@ -47,20 +47,6 @@
|
|||
|
||||
#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
|
||||
enqueue_data_for_slave(const void *data, size_t len, size_t offset,
|
||||
ptmx_buffer_list_t *buffer_list)
|
||||
|
|
@ -207,25 +193,41 @@ fdm_ptmx_out(struct fdm *fdm, int fd, int events, void *data)
|
|||
static bool
|
||||
add_utmp_record(const struct config *conf, struct reaper *reaper, int ptmx)
|
||||
{
|
||||
#if defined(UTMP_ADD)
|
||||
if (ptmx < 0)
|
||||
return true;
|
||||
if (conf->utempter_path == NULL)
|
||||
if (conf->utmp_helper_path == NULL)
|
||||
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);
|
||||
#else
|
||||
return true;
|
||||
#endif
|
||||
}
|
||||
|
||||
static bool
|
||||
del_utmp_record(const struct config *conf, struct reaper *reaper, int ptmx)
|
||||
{
|
||||
#if defined(UTMP_DEL)
|
||||
if (ptmx < 0)
|
||||
return true;
|
||||
if (conf->utempter_path == NULL)
|
||||
if (conf->utmp_helper_path == NULL)
|
||||
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);
|
||||
#else
|
||||
return true;
|
||||
#endif
|
||||
}
|
||||
|
||||
#if PTMX_TIMING
|
||||
|
|
@ -406,11 +408,6 @@ fdm_flash(struct fdm *fdm, int fd, int events, void *data)
|
|||
|
||||
term->flash.active = false;
|
||||
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;
|
||||
}
|
||||
|
||||
|
|
@ -736,7 +733,8 @@ term_line_height_update(struct terminal *term)
|
|||
}
|
||||
|
||||
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++) {
|
||||
xassert(fonts[i] != NULL);
|
||||
|
|
@ -752,9 +750,6 @@ term_set_fonts(struct terminal *term, struct fcft_font *fonts[static 4])
|
|||
free_custom_glyphs(
|
||||
&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 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);
|
||||
|
||||
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);
|
||||
|
||||
/* Use force, since cell-width/height may have changed */
|
||||
render_resize_force(term, term->width / term->scale, term->height / term->scale);
|
||||
/* 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 */
|
||||
render_resize_force(
|
||||
term,
|
||||
round(term->width / term->scale),
|
||||
round(term->height / term->scale));
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
@ -820,41 +801,34 @@ get_font_dpi(const struct terminal *term)
|
|||
* Conceptually, we use the physical monitor specs to calculate
|
||||
* the DPI, and we ignore the output's scaling factor.
|
||||
*
|
||||
* However, to deal with fractional scaling, where we're told to
|
||||
* render at e.g. 2x, but are then downscaled by the compositor to
|
||||
* e.g. 1.25, we use the scaled DPI value multiplied by the scale
|
||||
* factor instead.
|
||||
* However, to deal with legacy fractional scaling, where we're
|
||||
* told to render at e.g. 2x, but are then downscaled by the
|
||||
* compositor to e.g. 1.25, we use the scaled DPI value multiplied
|
||||
* by the scale factor instead.
|
||||
*
|
||||
* For integral scaling factors the resulting DPI is the same as
|
||||
* if we had used the physical DPI.
|
||||
*
|
||||
* For fractional scaling factors we'll get a DPI *larger* than
|
||||
* the physical DPI, that ends up being right when later
|
||||
* For legacy fractional scaling factors we'll get a DPI *larger*
|
||||
* than the physical DPI, that ends up being right when later
|
||||
* 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 */
|
||||
double dpi = 0.0;
|
||||
xassert(term->window != NULL);
|
||||
tll_foreach(term->window->on_outputs, it) {
|
||||
if (it->item->dpi > dpi)
|
||||
dpi = it->item->dpi;
|
||||
}
|
||||
xassert(tll_length(term->wl->monitors) > 0);
|
||||
|
||||
/* If we're not mapped, use DPI from first monitor. Hopefully this is where we'll get mapped later... */
|
||||
if (dpi == 0.) {
|
||||
tll_foreach(term->wl->monitors, it) {
|
||||
dpi = it->item.dpi;
|
||||
break;
|
||||
}
|
||||
}
|
||||
const struct wl_window *win = term->window;
|
||||
const struct monitor *mon = tll_length(win->on_outputs) > 0
|
||||
? tll_back(win->on_outputs)
|
||||
: &tll_front(term->wl->monitors);
|
||||
|
||||
if (dpi == 0) {
|
||||
/* No monitors? */
|
||||
dpi = 96.;
|
||||
}
|
||||
|
||||
return dpi;
|
||||
if (wayl_fractional_scaling(term->wl))
|
||||
return mon->dpi.physical;
|
||||
else
|
||||
return mon->dpi.scaled;
|
||||
}
|
||||
|
||||
static enum fcft_subpixel
|
||||
|
|
@ -873,7 +847,8 @@ get_font_subpixel(const struct terminal *term)
|
|||
* output or not.
|
||||
*
|
||||
* 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
|
||||
* 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)
|
||||
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)
|
||||
wl_subpixel = tll_front(term->wl->monitors).subpixel;
|
||||
else
|
||||
|
|
@ -901,42 +876,6 @@ get_font_subpixel(const struct terminal *term)
|
|||
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
|
||||
term_pt_or_px_as_pixels(const struct terminal *term,
|
||||
const struct pt_or_px *pt_or_px)
|
||||
|
|
@ -966,7 +905,7 @@ font_loader_thread(void *_data)
|
|||
}
|
||||
|
||||
static bool
|
||||
reload_fonts(struct terminal *term)
|
||||
reload_fonts(struct terminal *term, bool resize_grid)
|
||||
{
|
||||
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;
|
||||
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)
|
||||
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
|
||||
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;
|
||||
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
|
||||
|
|
@ -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(
|
||||
|
|
@ -1221,7 +1160,7 @@ term_init(const struct config *conf, struct fdm *fdm, struct reaper *reaper,
|
|||
.reverse_wrap = true,
|
||||
.auto_margin = true,
|
||||
.window_title_stack = tll_init(),
|
||||
.scale = 1,
|
||||
.scale = 1.,
|
||||
.flash = {.fd = flash_fd},
|
||||
.blink = {.fd = -1},
|
||||
.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);
|
||||
|
||||
/* 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 */
|
||||
tll_foreach(term->wl->monitors, it) {
|
||||
if (it->item.scale > term->scale)
|
||||
term->scale = it->item.scale;
|
||||
}
|
||||
* output we'll be. Use scaling factor from first monitor */
|
||||
xassert(tll_length(term->wl->monitors) > 0);
|
||||
term->scale = tll_front(term->wl->monitors).scale;
|
||||
|
||||
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;
|
||||
|
||||
/* Load fonts */
|
||||
if (!term_font_dpi_changed(term, 0))
|
||||
if (!term_font_dpi_changed(term, 0.))
|
||||
goto err;
|
||||
|
||||
term->font_subpixel = get_font_subpixel(term);
|
||||
|
|
@ -1670,8 +1607,6 @@ term_destroy(struct terminal *term)
|
|||
if (term == NULL)
|
||||
return 0;
|
||||
|
||||
key_binding_unref(term->wl->key_binding_manager, term->conf);
|
||||
|
||||
tll_foreach(term->wl->terms, it) {
|
||||
if (it->item == term) {
|
||||
tll_remove(term->wl->terms, it);
|
||||
|
|
@ -1717,6 +1652,8 @@ term_destroy(struct terminal *term)
|
|||
}
|
||||
mtx_unlock(&term->render.workers.lock);
|
||||
|
||||
key_binding_unref(term->wl->key_binding_manager, term->conf);
|
||||
|
||||
urls_reset(term);
|
||||
|
||||
free(term->vt.osc.data);
|
||||
|
|
@ -1933,6 +1870,7 @@ term_reset(struct terminal *term, bool hard)
|
|||
|
||||
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->alt.kitty_kbd.flags, 0, sizeof(term->alt.kitty_kbd.flags));
|
||||
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
|
||||
|
|
@ -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
|
||||
|
|
@ -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
|
||||
|
|
@ -2140,13 +2078,43 @@ term_font_size_reset(struct terminal *term)
|
|||
}
|
||||
|
||||
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);
|
||||
xassert(term->scale > 0);
|
||||
xassert(term->scale > 0.);
|
||||
|
||||
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 =
|
||||
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);
|
||||
|
||||
if (need_font_reload) {
|
||||
LOG_DBG("DPI/scale change: DPI-awareness=%s, "
|
||||
"DPI: %.2f -> %.2f, scale: %d -> %d, "
|
||||
LOG_DBG("DPI/scale change: DPI-aware=%s, "
|
||||
"DPI: %.2f -> %.2f, scale: %.2f -> %.2f, "
|
||||
"sizing font based on monitor's %s",
|
||||
term->conf->dpi_aware == DPI_AWARE_AUTO ? "auto" :
|
||||
term->conf->dpi_aware == DPI_AWARE_YES ? "yes" : "no",
|
||||
term->conf->dpi_aware ? "yes" : "no",
|
||||
term->font_dpi, dpi, old_scale, term->scale,
|
||||
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;
|
||||
|
||||
if (!need_font_reload)
|
||||
return true;
|
||||
return false;
|
||||
|
||||
return reload_fonts(term);
|
||||
return reload_fonts(term, false);
|
||||
}
|
||||
|
||||
void
|
||||
|
|
@ -2545,6 +2512,15 @@ term_cursor_home(struct terminal *term)
|
|||
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
|
||||
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;
|
||||
|
||||
if (likely(view_follows)) {
|
||||
term_damage_scroll(term, DAMAGE_SCROLL, region, rows);
|
||||
selection_view_down(term, term->grid->offset);
|
||||
term->grid->view = term->grid->offset;
|
||||
} 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);
|
||||
}
|
||||
|
||||
term->grid->cur_row = grid_row(term->grid, term->grid->cursor.point.row);
|
||||
|
||||
#if defined(_DEBUG)
|
||||
for (int r = 0; r < term->rows; r++)
|
||||
xassert(grid_row(term->grid, r) != NULL);
|
||||
#endif
|
||||
|
||||
term_damage_scroll(term, DAMAGE_SCROLL, region, rows);
|
||||
term->grid->cur_row = grid_row(term->grid, term->grid->cursor.point.row);
|
||||
}
|
||||
|
||||
void
|
||||
|
|
@ -2779,6 +2755,7 @@ term_scroll_reverse_partial(struct terminal *term,
|
|||
xassert(term->grid->offset < term->grid->num_rows);
|
||||
|
||||
if (view_follows) {
|
||||
term_damage_scroll(term, DAMAGE_SCROLL_REVERSE, region, rows);
|
||||
selection_view_up(term, term->grid->offset);
|
||||
term->grid->view = term->grid->offset;
|
||||
}
|
||||
|
|
@ -2797,7 +2774,6 @@ term_scroll_reverse_partial(struct terminal *term,
|
|||
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);
|
||||
|
||||
#if defined(_DEBUG)
|
||||
|
|
@ -3176,44 +3152,56 @@ term_mouse_motion(struct terminal *term, int button, int row, int col,
|
|||
void
|
||||
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) {
|
||||
case TERM_SURF_GRID: {
|
||||
bool have_custom_cursor =
|
||||
render_xcursor_is_valid(seat, term->mouse_user_cursor);
|
||||
case TERM_SURF_GRID:
|
||||
if (seat->pointer.hidden)
|
||||
shape = CURSOR_SHAPE_HIDDEN;
|
||||
|
||||
xcursor = seat->pointer.hidden ? XCURSOR_HIDDEN
|
||||
: have_custom_cursor ? term->mouse_user_cursor
|
||||
: term->is_searching ? XCURSOR_LEFT_PTR
|
||||
: (seat->mouse.col >= 0 &&
|
||||
seat->mouse.row >= 0 &&
|
||||
term_mouse_grabbed(term, seat)) ? XCURSOR_TEXT
|
||||
: XCURSOR_LEFT_PTR;
|
||||
#if defined(HAVE_CURSOR_SHAPE)
|
||||
else if (cursor_string_to_server_shape(term->mouse_user_cursor) != 0
|
||||
#else
|
||||
else if (false
|
||||
#endif
|
||||
|| render_xcursor_is_valid(seat, term->mouse_user_cursor))
|
||||
{
|
||||
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_BUTTON_MINIMIZE:
|
||||
case TERM_SURF_BUTTON_MAXIMIZE:
|
||||
case TERM_SURF_BUTTON_CLOSE:
|
||||
xcursor = XCURSOR_LEFT_PTR;
|
||||
shape = CURSOR_SHAPE_LEFT_PTR;
|
||||
break;
|
||||
|
||||
case TERM_SURF_BORDER_LEFT:
|
||||
case TERM_SURF_BORDER_RIGHT:
|
||||
case TERM_SURF_BORDER_TOP:
|
||||
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;
|
||||
|
||||
case TERM_SURF_NONE:
|
||||
return;
|
||||
}
|
||||
|
||||
if (xcursor == NULL)
|
||||
if (shape == CURSOR_SHAPE_NONE)
|
||||
BUG("xcursor not set");
|
||||
|
||||
render_xcursor_set(seat, term, xcursor);
|
||||
render_xcursor_set(seat, term, shape);
|
||||
}
|
||||
|
||||
void
|
||||
|
|
@ -3548,7 +3536,7 @@ term_update_ascii_printer(struct terminal *term)
|
|||
|
||||
#if defined(_DEBUG) && LOG_ENABLE_DBG
|
||||
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",
|
||||
new_printer == &ascii_printer_fast ? "fast" : "generic");
|
||||
}
|
||||
|
|
@ -3568,23 +3556,23 @@ term_single_shift(struct terminal *term, enum charset_designator idx)
|
|||
enum term_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;
|
||||
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;
|
||||
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;
|
||||
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;
|
||||
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;
|
||||
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;
|
||||
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;
|
||||
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;
|
||||
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;
|
||||
else
|
||||
return TERM_SURF_NONE;
|
||||
|
|
@ -3764,6 +3752,8 @@ void
|
|||
term_set_user_mouse_cursor(struct terminal *term, const char *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);
|
||||
}
|
||||
|
|
|
|||
75
terminal.h
75
terminal.h
|
|
@ -126,14 +126,48 @@ struct row {
|
|||
};
|
||||
|
||||
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;
|
||||
int width;
|
||||
int height;
|
||||
|
||||
int rows;
|
||||
int cols;
|
||||
struct coord pos;
|
||||
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 {
|
||||
|
|
@ -180,8 +214,10 @@ struct grid {
|
|||
};
|
||||
|
||||
struct vt_subparams {
|
||||
unsigned value[16];
|
||||
uint8_t idx;
|
||||
unsigned *cur;
|
||||
unsigned value[16];
|
||||
unsigned dummy;
|
||||
};
|
||||
|
||||
struct vt_param {
|
||||
|
|
@ -197,8 +233,10 @@ struct vt {
|
|||
#endif
|
||||
char32_t utf8;
|
||||
struct {
|
||||
struct vt_param v[16];
|
||||
uint8_t idx;
|
||||
struct vt_param *cur;
|
||||
struct vt_param v[16];
|
||||
struct vt_param dummy;
|
||||
} params;
|
||||
|
||||
uint32_t private; /* LSB=priv0, MSB=priv3 */
|
||||
|
|
@ -450,7 +488,7 @@ struct terminal {
|
|||
int fd;
|
||||
} blink;
|
||||
|
||||
int scale;
|
||||
float scale;
|
||||
int width; /* pixels */
|
||||
int height; /* pixels */
|
||||
int stashed_width;
|
||||
|
|
@ -616,7 +654,6 @@ struct terminal {
|
|||
} state;
|
||||
|
||||
struct coord pos; /* Current sixel coordinate */
|
||||
int max_non_empty_row_no;
|
||||
size_t row_byte_ofs; /* Byte position into image, for current row */
|
||||
int color_idx; /* Current palette index */
|
||||
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 */
|
||||
} 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 use_private_palette:1; /* Private mode 1070 */
|
||||
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 param; /* Currently collecting parameter, for RASTER, COLOR_SPEC and REPEAT */
|
||||
unsigned param_idx; /* Parameters seen */
|
||||
unsigned repeat_count;
|
||||
|
||||
bool transparent_bg;
|
||||
uint32_t default_bg;
|
||||
|
|
@ -671,20 +718,6 @@ struct terminal {
|
|||
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 terminal *term_init(
|
||||
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(
|
||||
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_decrease(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);
|
||||
|
||||
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);
|
||||
void term_cursor_home(struct terminal *term);
|
||||
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_right(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
|
||||
test_boolean(struct context *ctx, bool (*parse_fun)(struct context *ctx),
|
||||
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, "term", &conf.term);
|
||||
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);
|
||||
|
||||
|
|
@ -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, "locked-title", &conf.locked_title);
|
||||
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, "line-height", &conf.line_height);
|
||||
|
|
@ -479,17 +525,6 @@ test_section_main(void)
|
|||
|
||||
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",
|
||||
4,
|
||||
(const char *[]){"none", "primary", "clipboard", "both"},
|
||||
|
|
@ -577,8 +612,8 @@ test_section_url(void)
|
|||
(int []){OSC8_UNDERLINE_URL_MODE, OSC8_UNDERLINE_ALWAYS},
|
||||
(int *)&conf.url.osc8_underline);
|
||||
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) */
|
||||
|
||||
config_free(&conf);
|
||||
|
|
@ -627,6 +662,21 @@ test_section_mouse(void)
|
|||
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
|
||||
test_section_colors(void)
|
||||
{
|
||||
|
|
@ -727,6 +777,8 @@ test_section_csd(void)
|
|||
&conf.csd.color.quit);
|
||||
test_boolean(&ctx, &parse_section_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: font */
|
||||
|
|
@ -1312,6 +1364,7 @@ main(int argc, const char *const *argv)
|
|||
test_section_url();
|
||||
test_section_cursor();
|
||||
test_section_mouse();
|
||||
test_section_touch();
|
||||
test_section_colors();
|
||||
test_section_csd();
|
||||
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
|
||||
# Based on material.io guidelines with Amber 50 background
|
||||
|
||||
# [cursor]
|
||||
# color=fff8e1 21201d
|
||||
[cursor]
|
||||
color=fff8e1 21201d
|
||||
|
||||
[colors]
|
||||
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 "grid.h"
|
||||
#include "key-binding.h"
|
||||
#include "quirks.h"
|
||||
#include "render.h"
|
||||
#include "selection.h"
|
||||
#include "spawn.h"
|
||||
|
|
@ -859,6 +860,10 @@ urls_reset(struct terminal *term)
|
|||
tll_foreach(term->window->urls, it) {
|
||||
wayl_win_subsurface_destroy(&it->item.surf);
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
135
vt.c
135
vt.c
|
|
@ -294,74 +294,31 @@ action_print(struct terminal *term, uint8_t c)
|
|||
}
|
||||
|
||||
static void
|
||||
action_param(struct terminal *term, uint8_t c)
|
||||
action_param_lazy_init(struct terminal *term)
|
||||
{
|
||||
if (term->vt.params.idx == 0) {
|
||||
struct vt_param *param = &term->vt.params.v[0];
|
||||
|
||||
term->vt.params.cur = param;
|
||||
param->value = 0;
|
||||
param->sub.idx = 0;
|
||||
param->sub.cur = NULL;
|
||||
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
|
||||
= 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 */
|
||||
if (c == ';') {
|
||||
if (unlikely(term->vt.params.idx >= max_params))
|
||||
goto excess_params;
|
||||
struct vt_param *param;
|
||||
|
||||
struct vt_param *param = &term->vt.params.v[term->vt.params.idx++];
|
||||
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:
|
||||
{
|
||||
if (unlikely(term->vt.params.idx >= max_params)) {
|
||||
static bool have_warned = false;
|
||||
if (!have_warned) {
|
||||
have_warned = true;
|
||||
|
|
@ -370,11 +327,29 @@ excess_params:
|
|||
"(will not warn again)",
|
||||
sizeof(term->vt.params.v) / sizeof(term->vt.params.v[0]));
|
||||
}
|
||||
}
|
||||
return;
|
||||
param = &term->vt.params.dummy;
|
||||
} 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;
|
||||
if (!have_warned) {
|
||||
have_warned = true;
|
||||
|
|
@ -383,8 +358,33 @@ excess_sub_params:
|
|||
"(will not warn again)",
|
||||
sizeof(term->vt.params.v[0].sub.value) / sizeof(term->vt.params.v[0].sub.value[0]));
|
||||
}
|
||||
}
|
||||
return;
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
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
|
||||
|
|
@ -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 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 0x40 ... 0x7e: action_csi_dispatch(term, data); return STATE_GROUND;
|
||||
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 0x30 ... 0x39:
|
||||
case 0x3a ... 0x3b: action_param(term, data); return STATE_CSI_PARAM;
|
||||
case 0x30 ... 0x39: 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 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 0x30 ... 0x39: action_param(term, data); return STATE_DCS_PARAM;
|
||||
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 0x40 ... 0x7e: action_hook(term, data); return STATE_DCS_PASSTHROUGH;
|
||||
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 0x30 ... 0x39: action_param(term, data); return STATE_DCS_PARAM;
|
||||
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 0x40 ... 0x7e: action_hook(term, data); return STATE_DCS_PASSTHROUGH;
|
||||
case 0x7f: action_ignore(term); return STATE_DCS_PARAM;
|
||||
|
|
|
|||
515
wayland.c
515
wayland.c
|
|
@ -14,6 +14,10 @@
|
|||
#include <wayland-cursor.h>
|
||||
#include <xkbcommon/xkbcommon-compose.h>
|
||||
|
||||
#if defined(HAVE_CURSOR_SHAPE)
|
||||
#include <cursor-shape-v1.h>
|
||||
#endif
|
||||
|
||||
#include <tllist.h>
|
||||
|
||||
#define LOG_MODULE "wayland"
|
||||
|
|
@ -32,12 +36,12 @@
|
|||
#include "xmalloc.h"
|
||||
|
||||
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;
|
||||
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;
|
||||
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;
|
||||
|
||||
char pixelsize[32];
|
||||
snprintf(pixelsize, sizeof(pixelsize),
|
||||
"pixelsize=%u", conf->csd.title_height * scale * 1 / 2);
|
||||
snprintf(pixelsize, sizeof(pixelsize), "pixelsize=%u",
|
||||
(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);
|
||||
|
||||
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++) {
|
||||
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);
|
||||
xassert(ret);
|
||||
}
|
||||
|
||||
csd_reload_font(win, -1);
|
||||
csd_reload_font(win, -1.);
|
||||
}
|
||||
|
||||
static void
|
||||
|
|
@ -187,8 +191,12 @@ seat_destroy(struct seat *seat)
|
|||
|
||||
if (seat->pointer.theme != NULL)
|
||||
wl_cursor_theme_destroy(seat->pointer.theme);
|
||||
if (seat->pointer.surface != NULL)
|
||||
wl_surface_destroy(seat->pointer.surface);
|
||||
if (seat->pointer.surface.surf != NULL)
|
||||
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)
|
||||
wl_callback_destroy(seat->pointer.xcursor_callback);
|
||||
|
||||
|
|
@ -205,10 +213,17 @@ seat_destroy(struct seat *seat)
|
|||
if (seat->data_device != NULL)
|
||||
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)
|
||||
wl_keyboard_release(seat->wl_keyboard);
|
||||
if (seat->wl_pointer != NULL)
|
||||
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 (seat->wl_text_input != NULL)
|
||||
|
|
@ -221,6 +236,7 @@ seat_destroy(struct seat *seat)
|
|||
ime_reset_pending(seat);
|
||||
free(seat->clipboard.text);
|
||||
free(seat->primary.text);
|
||||
free(seat->pointer.last_custom_xcursor);
|
||||
free(seat->name);
|
||||
}
|
||||
|
||||
|
|
@ -270,9 +286,10 @@ seat_handle_capabilities(void *data, struct wl_seat *wl_seat,
|
|||
struct seat *seat = data;
|
||||
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_POINTER) ? "yes" : "no");
|
||||
(caps & WL_SEAT_CAPABILITY_POINTER) ? "yes" : "no",
|
||||
(caps & WL_SEAT_CAPABILITY_TOUCH) ? "yes" : "no");
|
||||
|
||||
if (caps & WL_SEAT_CAPABILITY_KEYBOARD) {
|
||||
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 (seat->wl_pointer == NULL) {
|
||||
xassert(seat->pointer.surface == NULL);
|
||||
seat->pointer.surface = wl_compositor_create_surface(seat->wayl->compositor);
|
||||
xassert(seat->pointer.surface.surf == NULL);
|
||||
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);
|
||||
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);
|
||||
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 {
|
||||
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_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)
|
||||
wl_cursor_theme_destroy(seat->pointer.theme);
|
||||
|
||||
seat->wl_pointer = NULL;
|
||||
seat->pointer.surface = NULL;
|
||||
seat->pointer.surface.surf = NULL;
|
||||
seat->pointer.theme = 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
|
||||
|
|
@ -331,15 +398,35 @@ static const struct wl_seat_listener seat_listener = {
|
|||
static void
|
||||
update_term_for_output_change(struct terminal *term)
|
||||
{
|
||||
if (tll_length(term->window->on_outputs) == 0)
|
||||
return;
|
||||
const float old_scale = term->scale;
|
||||
const float logical_width = term->width / term->scale;
|
||||
const float logical_height = term->height / term->scale;
|
||||
|
||||
int old_scale = term->scale;
|
||||
|
||||
render_resize(term, term->width / term->scale, term->height / term->scale);
|
||||
term_font_dpi_changed(term, old_scale);
|
||||
/* Note: order matters! term_update_scale() must come first */
|
||||
bool scale_updated = term_update_scale(term);
|
||||
bool fonts_updated = term_font_dpi_changed(term, old_scale);
|
||||
term_font_subpixel_changed(term);
|
||||
|
||||
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
|
||||
|
|
@ -350,11 +437,6 @@ update_terms_on_monitor(struct monitor *mon)
|
|||
tll_foreach(wayl->terms, it) {
|
||||
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) {
|
||||
if (it2->item == mon) {
|
||||
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 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.y = mon->dim.px_real.height / y_inches;
|
||||
|
||||
|
|
@ -395,27 +480,36 @@ output_update_ppi(struct monitor *mon)
|
|||
break;
|
||||
}
|
||||
|
||||
int scaled_width = mon->dim.px_scaled.width;
|
||||
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;
|
||||
}
|
||||
const int scaled_width = mon->dim.px_scaled.width;
|
||||
const int scaled_height = mon->dim.px_scaled.height;
|
||||
|
||||
mon->ppi.scaled.x = scaled_width / x_inches;
|
||||
mon->ppi.scaled.y = scaled_height / y_inches;
|
||||
|
||||
double px_diag = sqrt(pow(scaled_width, 2) + pow(scaled_height, 2));
|
||||
mon->dpi = px_diag / mon->inch * mon->scale;
|
||||
const double px_diag_physical = sqrt(pow(width, 2) + pow(height, 2));
|
||||
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) {
|
||||
LOG_WARN("%s: DPI=%f is unreasonable, using 96 instead",
|
||||
mon->name, mon->dpi);
|
||||
LOG_WARN("%s: DPI=%f (physical) is unreasonable, using 96 instead",
|
||||
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_left = false;
|
||||
bool is_tiled_right = false;
|
||||
bool is_suspended UNUSED = false;
|
||||
|
||||
#if defined(LOG_ENABLE_DBG) && LOG_ENABLE_DBG
|
||||
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_TOP] = "tiled:top",
|
||||
[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
|
||||
|
||||
enum xdg_toplevel_state *state;
|
||||
wl_array_for_each(state, states) {
|
||||
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_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_RIGHT: is_tiled_right = 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_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 (*state >= XDG_TOPLEVEL_STATE_MAXIMIZED &&
|
||||
*state <= XDG_TOPLEVEL_STATE_TILED_BOTTOM)
|
||||
{
|
||||
if (*state >= 0 && *state < ALEN(strings)) {
|
||||
state_chars += snprintf(
|
||||
&state_str[state_chars], sizeof(state_str) - state_chars,
|
||||
"%s, ", strings[*state]);
|
||||
"%s, ",
|
||||
strings[*state] != NULL ? strings[*state] : "<unknown>");
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
|
@ -848,7 +949,7 @@ xdg_surface_configure(void *data, struct xdg_surface *xdg_surface,
|
|||
* anytime soon. Some compositors require a commit in
|
||||
* combination with an ack - make them happy.
|
||||
*/
|
||||
wl_surface_commit(win->surface);
|
||||
wl_surface_commit(win->surface.surf);
|
||||
}
|
||||
|
||||
if (wasnt_configured)
|
||||
|
|
@ -1121,6 +1222,38 @@ handle_global(void *data, struct wl_registry *registry,
|
|||
}
|
||||
#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
|
||||
else if (strcmp(interface, zwp_text_input_manager_v3_interface.name) == 0) {
|
||||
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)
|
||||
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) {
|
||||
|
|
@ -1214,7 +1347,7 @@ handle_global_remove(void *data, struct wl_registry *registry, uint32_t name)
|
|||
|
||||
if (seat->wl_pointer != NULL)
|
||||
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);
|
||||
|
|
@ -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?)");
|
||||
goto out;
|
||||
}
|
||||
if (tll_length(wayl->monitors) == 0) {
|
||||
LOG_ERR("no monitors available");
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (wayl->primary_selection_device_manager == NULL)
|
||||
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");
|
||||
}
|
||||
|
||||
#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) {
|
||||
LOG_ERR("presentation time interface not implemented by compositor");
|
||||
goto out;
|
||||
|
|
@ -1369,14 +1524,12 @@ wayl_init(struct fdm *fdm, struct key_binding_manager *key_binding_manager,
|
|||
|
||||
tll_foreach(wayl->monitors, it) {
|
||||
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.x, it->item.y, (int)round(it->item.refresh),
|
||||
it->item.model != NULL ? it->item.model : it->item.description,
|
||||
it->item.inch, it->item.scale,
|
||||
it->item.ppi.real.x, it->item.ppi.real.y,
|
||||
it->item.ppi.scaled.x, it->item.ppi.scaled.y,
|
||||
it->item.dpi);
|
||||
it->item.dpi.physical, it->item.dpi.scaled);
|
||||
}
|
||||
|
||||
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);
|
||||
#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 (wayl->xdg_activation != NULL)
|
||||
xdg_activation_v1_destroy(wayl->xdg_activation);
|
||||
|
|
@ -1469,6 +1632,29 @@ wayl_destroy(struct wayland *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 *
|
||||
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.move_timeout_fd = -1;
|
||||
win->resize_timeout_fd = -1;
|
||||
win->scale = -1.;
|
||||
|
||||
win->wm_capabilities.maximize = true;
|
||||
win->wm_capabilities.minimize = true;
|
||||
|
||||
win->surface = wl_compositor_create_surface(wayl->compositor);
|
||||
if (win->surface == NULL) {
|
||||
win->surface.surf = wl_compositor_create_surface(wayl->compositor);
|
||||
if (win->surface.surf == NULL) {
|
||||
LOG_ERR("failed to create wayland surface");
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (term->colors.alpha == 0xffff) {
|
||||
struct wl_region *region = wl_compositor_create_region(
|
||||
term->wl->compositor);
|
||||
wayl_win_alpha_changed(win);
|
||||
|
||||
if (region != NULL) {
|
||||
wl_region_add(region, 0, 0, INT32_MAX, INT32_MAX);
|
||||
wl_surface_set_opaque_region(win->surface, region);
|
||||
wl_region_destroy(region);
|
||||
}
|
||||
wl_surface_add_listener(win->surface.surf, &surface_listener, win);
|
||||
|
||||
#if defined(HAVE_FRACTIONAL_SCALE)
|
||||
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);
|
||||
win->xdg_surface = xdg_wm_base_get_xdg_surface(wayl->shell, win->surface.surf);
|
||||
xdg_surface_add_listener(win->xdg_surface, &xdg_surface_listener, win);
|
||||
|
||||
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");
|
||||
}
|
||||
|
||||
wl_surface_commit(win->surface);
|
||||
wl_surface_commit(win->surface.surf);
|
||||
|
||||
#if defined(HAVE_XDG_ACTIVATION)
|
||||
/* Complete XDG startup notification */
|
||||
if (token)
|
||||
xdg_activation_v1_activate(wayl->xdg_activation, token, win->surface);
|
||||
xdg_activation_v1_activate(wayl->xdg_activation, token, win->surface.surf);
|
||||
#endif
|
||||
|
||||
if (!wayl_win_subsurface_new(win, &win->overlay, false)) {
|
||||
|
|
@ -1596,33 +1786,33 @@ wayl_win_destroy(struct wl_window *win)
|
|||
* nor mouse focus).
|
||||
*/
|
||||
|
||||
if (win->render_timer.surf != NULL) {
|
||||
wl_surface_attach(win->render_timer.surf, NULL, 0, 0);
|
||||
wl_surface_commit(win->render_timer.surf);
|
||||
if (win->render_timer.surface.surf != NULL) {
|
||||
wl_surface_attach(win->render_timer.surface.surf, NULL, 0, 0);
|
||||
wl_surface_commit(win->render_timer.surface.surf);
|
||||
}
|
||||
|
||||
if (win->scrollback_indicator.surf != NULL) {
|
||||
wl_surface_attach(win->scrollback_indicator.surf, NULL, 0, 0);
|
||||
wl_surface_commit(win->scrollback_indicator.surf);
|
||||
if (win->scrollback_indicator.surface.surf != NULL) {
|
||||
wl_surface_attach(win->scrollback_indicator.surface.surf, NULL, 0, 0);
|
||||
wl_surface_commit(win->scrollback_indicator.surface.surf);
|
||||
}
|
||||
|
||||
/* Scrollback search */
|
||||
if (win->search.surf != NULL) {
|
||||
wl_surface_attach(win->search.surf, NULL, 0, 0);
|
||||
wl_surface_commit(win->search.surf);
|
||||
if (win->search.surface.surf != NULL) {
|
||||
wl_surface_attach(win->search.surface.surf, NULL, 0, 0);
|
||||
wl_surface_commit(win->search.surface.surf);
|
||||
}
|
||||
|
||||
/* URLs */
|
||||
tll_foreach(win->urls, it) {
|
||||
wl_surface_attach(it->item.surf.surf, NULL, 0, 0);
|
||||
wl_surface_commit(it->item.surf.surf);
|
||||
wl_surface_attach(it->item.surf.surface.surf, NULL, 0, 0);
|
||||
wl_surface_commit(it->item.surf.surface.surf);
|
||||
}
|
||||
|
||||
/* CSD */
|
||||
for (size_t i = 0; i < ALEN(win->csd.surface); i++) {
|
||||
if (win->csd.surface[i].surf != NULL) {
|
||||
wl_surface_attach(win->csd.surface[i].surf, NULL, 0, 0);
|
||||
wl_surface_commit(win->csd.surface[i].surf);
|
||||
if (win->csd.surface[i].surface.surf != NULL) {
|
||||
wl_surface_attach(win->csd.surface[i].surface.surf, NULL, 0, 0);
|
||||
wl_surface_commit(win->csd.surface[i].surface.surf);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1630,8 +1820,8 @@ wayl_win_destroy(struct wl_window *win)
|
|||
|
||||
/* Main window */
|
||||
win->unmapped = true;
|
||||
wl_surface_attach(win->surface, NULL, 0, 0);
|
||||
wl_surface_commit(win->surface);
|
||||
wl_surface_attach(win->surface.surf, NULL, 0, 0);
|
||||
wl_surface_commit(win->surface.surf);
|
||||
wayl_roundtrip(win->term->wl);
|
||||
|
||||
tll_free(win->on_outputs);
|
||||
|
|
@ -1661,6 +1851,12 @@ wayl_win_destroy(struct wl_window *win)
|
|||
|
||||
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
|
||||
if (win->frame_callback != NULL)
|
||||
wl_callback_destroy(win->frame_callback);
|
||||
|
|
@ -1670,8 +1866,8 @@ wayl_win_destroy(struct wl_window *win)
|
|||
xdg_toplevel_destroy(win->xdg_toplevel);
|
||||
if (win->xdg_surface != NULL)
|
||||
xdg_surface_destroy(win->xdg_surface);
|
||||
if (win->surface != NULL)
|
||||
wl_surface_destroy(win->surface);
|
||||
if (win->surface.surf != NULL)
|
||||
wl_surface_destroy(win->surface.surf);
|
||||
|
||||
wayl_roundtrip(win->term->wl);
|
||||
|
||||
|
|
@ -1681,7 +1877,7 @@ wayl_win_destroy(struct wl_window *win)
|
|||
}
|
||||
|
||||
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) {
|
||||
/* 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");
|
||||
|
||||
LOG_INFO("cursor theme: %s, size: %d, scale: %d",
|
||||
LOG_INFO("cursor theme: %s, size: %d, scale: %.2f",
|
||||
xcursor_theme ? xcursor_theme : "(null)",
|
||||
xcursor_size, new_scale);
|
||||
|
||||
|
|
@ -1798,6 +1994,86 @@ wayl_roundtrip(struct wayland *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)
|
||||
static void
|
||||
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;
|
||||
|
||||
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 */
|
||||
|
||||
|
|
@ -1851,27 +2127,43 @@ wayl_win_csd_borders_visible(const struct wl_window *win)
|
|||
bool
|
||||
wayl_win_subsurface_new_with_custom_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;
|
||||
|
||||
surf->surf = NULL;
|
||||
surf->surface.surf = NULL;
|
||||
surf->sub = NULL;
|
||||
|
||||
struct wl_surface *main_surface
|
||||
= 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;
|
||||
}
|
||||
|
||||
struct wl_subsurface *sub = wl_subcompositor_get_subsurface(
|
||||
wayl->sub_compositor, main_surface, parent);
|
||||
|
||||
if (sub == NULL) {
|
||||
LOG_ERR("failed to instantiate sub-surface");
|
||||
wl_surface_destroy(main_surface);
|
||||
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_subsurface_set_sync(sub);
|
||||
|
||||
|
|
@ -1883,31 +2175,42 @@ wayl_win_subsurface_new_with_custom_parent(
|
|||
wl_region_destroy(empty);
|
||||
}
|
||||
|
||||
surf->surf = main_surface;
|
||||
surf->surface.surf = main_surface;
|
||||
surf->sub = sub;
|
||||
#if defined(HAVE_FRACTIONAL_SCALE)
|
||||
surf->surface.viewport = viewport;
|
||||
#endif
|
||||
return true;
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
return wayl_win_subsurface_new_with_custom_parent(
|
||||
win, win->surface, surf, allow_pointer_input);
|
||||
win, win->surface.surf, surf, allow_pointer_input);
|
||||
}
|
||||
|
||||
void
|
||||
wayl_win_subsurface_destroy(struct wl_surf_subsurf *surf)
|
||||
wayl_win_subsurface_destroy(struct wayl_sub_surface *surf)
|
||||
{
|
||||
if (surf == NULL)
|
||||
return;
|
||||
if (surf->sub != NULL)
|
||||
wl_subsurface_destroy(surf->sub);
|
||||
if (surf->surf != NULL)
|
||||
wl_surface_destroy(surf->surf);
|
||||
|
||||
surf->surf = NULL;
|
||||
surf->sub = 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;
|
||||
}
|
||||
if (surf->surface.surf != NULL) {
|
||||
wl_surface_destroy(surf->surface.surf);
|
||||
surf->surface.surf = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
#if defined(HAVE_XDG_ACTIVATION)
|
||||
|
|
@ -1972,7 +2275,7 @@ wayl_get_activation_token(
|
|||
if (seat != NULL && serial != 0)
|
||||
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_commit(token);
|
||||
return true;
|
||||
|
|
|
|||
110
wayland.h
110
wayland.h
|
|
@ -20,13 +20,20 @@
|
|||
#include <xdg-activation-v1.h>
|
||||
#endif
|
||||
|
||||
#if defined(HAVE_FRACTIONAL_SCALE)
|
||||
#include <viewporter.h>
|
||||
#include <fractional-scale-v1.h>
|
||||
#endif
|
||||
|
||||
#include <fcft/fcft.h>
|
||||
#include <tllist.h>
|
||||
|
||||
#include "cursor-shape.h"
|
||||
#include "fdm.h"
|
||||
|
||||
/* Forward declarations */
|
||||
struct terminal;
|
||||
struct buffer;
|
||||
|
||||
/* Mime-types we support when dealing with data offers (e.g. copy-paste, or DnD) */
|
||||
enum data_offer_mime_type {
|
||||
|
|
@ -40,6 +47,26 @@ enum data_offer_mime_type {
|
|||
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_clipboard {
|
||||
struct wl_window *window; /* For DnD */
|
||||
|
|
@ -127,17 +154,36 @@ struct seat {
|
|||
struct {
|
||||
uint32_t serial;
|
||||
|
||||
struct wl_surface *surface;
|
||||
/* Client-side cursor */
|
||||
struct wayl_surface surface;
|
||||
struct wl_cursor_theme *theme;
|
||||
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;
|
||||
bool xcursor_pending;
|
||||
} 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 {
|
||||
int x;
|
||||
int y;
|
||||
|
|
@ -269,7 +315,10 @@ struct monitor {
|
|||
} scaled;
|
||||
} ppi;
|
||||
|
||||
float dpi;
|
||||
struct {
|
||||
float scaled;
|
||||
float physical;
|
||||
} dpi;
|
||||
|
||||
int scale;
|
||||
float refresh;
|
||||
|
|
@ -289,14 +338,9 @@ struct monitor {
|
|||
bool use_output_release;
|
||||
};
|
||||
|
||||
struct wl_surf_subsurf {
|
||||
struct wl_surface *surf;
|
||||
struct wl_subsurface *sub;
|
||||
};
|
||||
|
||||
struct wl_url {
|
||||
const struct url *url;
|
||||
struct wl_surf_subsurf surf;
|
||||
struct wayl_sub_surface surf;
|
||||
};
|
||||
|
||||
enum csd_mode {CSD_UNKNOWN, CSD_NO, CSD_YES};
|
||||
|
|
@ -320,21 +364,26 @@ struct xdg_activation_token_context {
|
|||
struct wayland;
|
||||
struct wl_window {
|
||||
struct terminal *term;
|
||||
struct wl_surface *surface;
|
||||
struct wayl_surface surface;
|
||||
struct xdg_surface *xdg_surface;
|
||||
struct xdg_toplevel *xdg_toplevel;
|
||||
#if defined(HAVE_XDG_ACTIVATION)
|
||||
tll(struct xdg_activation_token_context *) xdg_tokens;
|
||||
bool urgency_token_is_pending;
|
||||
#endif
|
||||
#if defined(HAVE_FRACTIONAL_SCALE)
|
||||
struct wp_fractional_scale_v1 *fractional_scale;
|
||||
#endif
|
||||
bool unmapped;
|
||||
|
||||
float scale;
|
||||
|
||||
struct zxdg_toplevel_decoration_v1 *xdg_toplevel_decoration;
|
||||
|
||||
enum csd_mode csd_mode;
|
||||
|
||||
struct {
|
||||
struct wl_surf_subsurf surface[CSD_SURF_COUNT];
|
||||
struct wayl_sub_surface surface[CSD_SURF_COUNT];
|
||||
struct fcft_font *font;
|
||||
int move_timeout_fd;
|
||||
uint32_t serial;
|
||||
|
|
@ -345,10 +394,10 @@ struct wl_window {
|
|||
bool minimize:1;
|
||||
} wm_capabilities;
|
||||
|
||||
struct wl_surf_subsurf search;
|
||||
struct wl_surf_subsurf scrollback_indicator;
|
||||
struct wl_surf_subsurf render_timer;
|
||||
struct wl_surf_subsurf overlay;
|
||||
struct wayl_sub_surface search;
|
||||
struct wayl_sub_surface scrollback_indicator;
|
||||
struct wayl_sub_surface render_timer;
|
||||
struct wayl_sub_surface overlay;
|
||||
|
||||
struct wl_callback *frame_callback;
|
||||
|
||||
|
|
@ -406,6 +455,10 @@ struct wayland {
|
|||
struct xdg_activation_v1 *xdg_activation;
|
||||
#endif
|
||||
|
||||
#if defined(HAVE_CURSOR_SHAPE)
|
||||
struct wp_cursor_shape_manager_v1 *cursor_shape_manager;
|
||||
#endif
|
||||
|
||||
bool presentation_timings;
|
||||
struct wp_presentation *presentation;
|
||||
uint32_t presentation_clock_id;
|
||||
|
|
@ -414,6 +467,11 @@ struct wayland {
|
|||
struct zwp_text_input_manager_v3 *text_input_manager;
|
||||
#endif
|
||||
|
||||
#if defined(HAVE_FRACTIONAL_SCALE)
|
||||
struct wp_viewporter *viewporter;
|
||||
struct wp_fractional_scale_manager_v1 *fractional_scale_manager;
|
||||
#endif
|
||||
|
||||
bool have_argb8888;
|
||||
tll(struct monitor) monitors; /* All available outputs */
|
||||
tll(struct seat) seats;
|
||||
|
|
@ -426,26 +484,36 @@ struct wayland *wayl_init(
|
|||
bool presentation_timings);
|
||||
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_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);
|
||||
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_csd_titlebar_visible(const struct wl_window *win);
|
||||
bool wayl_win_csd_borders_visible(const struct wl_window *win);
|
||||
|
||||
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 wayl_win_subsurface_new_with_custom_parent(
|
||||
struct wl_window *win, struct wl_surface *parent,
|
||||
struct wl_surf_subsurf *surf, bool allow_pointer_input);
|
||||
void wayl_win_subsurface_destroy(struct wl_surf_subsurf *surf);
|
||||
struct wayl_sub_surface *surf, bool allow_pointer_input);
|
||||
void wayl_win_subsurface_destroy(struct wayl_sub_surface *surf);
|
||||
|
||||
#if defined(HAVE_XDG_ACTIVATION)
|
||||
bool wayl_get_activation_token(
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue