mirror of
https://codeberg.org/dnkl/foot.git
synced 2026-04-28 06:46:38 -04:00
Merge branch 'master' of https://codeberg.org/dnkl/foot
This commit is contained in:
commit
fd187cc491
50 changed files with 1530 additions and 481 deletions
|
|
@ -24,7 +24,7 @@ packages:
|
||||||
- font-noto-emoji
|
- font-noto-emoji
|
||||||
|
|
||||||
sources:
|
sources:
|
||||||
- https://codeberg.org/dnkl/foot
|
- https://git.sr.ht/~dnkl/foot
|
||||||
|
|
||||||
# triggers:
|
# triggers:
|
||||||
# - action: email
|
# - action: email
|
||||||
|
|
@ -49,4 +49,4 @@ tasks:
|
||||||
- codespell: |
|
- codespell: |
|
||||||
pip install codespell
|
pip install codespell
|
||||||
cd foot
|
cd foot
|
||||||
~/.local/bin/codespell -Lser,doas README.md INSTALL.md CHANGELOG.md *.c *.h doc/*.scd
|
~/.local/bin/codespell -Lser,doas,zar README.md INSTALL.md CHANGELOG.md *.c *.h doc/*.scd
|
||||||
|
|
|
||||||
|
|
@ -23,7 +23,7 @@ packages:
|
||||||
- font-noto-emoji
|
- font-noto-emoji
|
||||||
|
|
||||||
sources:
|
sources:
|
||||||
- https://codeberg.org/dnkl/foot
|
- https://git.sr.ht/~dnkl/foot
|
||||||
|
|
||||||
# triggers:
|
# triggers:
|
||||||
# - action: email
|
# - action: email
|
||||||
|
|
|
||||||
|
|
@ -19,7 +19,7 @@ packages:
|
||||||
- noto-emoji
|
- noto-emoji
|
||||||
|
|
||||||
sources:
|
sources:
|
||||||
- https://codeberg.org/dnkl/foot
|
- https://git.sr.ht/~dnkl/foot
|
||||||
|
|
||||||
# triggers:
|
# triggers:
|
||||||
# - action: email
|
# - action: email
|
||||||
|
|
|
||||||
|
|
@ -109,4 +109,4 @@ codespell:
|
||||||
- apk add python3
|
- apk add python3
|
||||||
- apk add py3-pip
|
- apk add py3-pip
|
||||||
- pip install codespell
|
- pip install codespell
|
||||||
- codespell -Lser,doas README.md INSTALL.md CHANGELOG.md *.c *.h doc/*.scd
|
- codespell -Lser,doas,zar README.md INSTALL.md CHANGELOG.md *.c *.h doc/*.scd
|
||||||
|
|
|
||||||
|
|
@ -9,7 +9,7 @@ pipeline:
|
||||||
- apk add python3
|
- apk add python3
|
||||||
- apk add py3-pip
|
- apk add py3-pip
|
||||||
- pip install codespell
|
- pip install codespell
|
||||||
- codespell -Lser,doas README.md INSTALL.md CHANGELOG.md *.c *.h doc/*.scd
|
- codespell -Lser,doas,zar README.md INSTALL.md CHANGELOG.md *.c *.h doc/*.scd
|
||||||
|
|
||||||
subprojects:
|
subprojects:
|
||||||
when:
|
when:
|
||||||
|
|
|
||||||
121
CHANGELOG.md
121
CHANGELOG.md
|
|
@ -1,6 +1,8 @@
|
||||||
# Changelog
|
# Changelog
|
||||||
|
|
||||||
* [Unreleased](#unreleased)
|
* [Unreleased](#unreleased)
|
||||||
|
* [1.13.1](#1-13-1)
|
||||||
|
* [1.13.0](#1-13-0)
|
||||||
* [1.12.1](#1-12-1)
|
* [1.12.1](#1-12-1)
|
||||||
* [1.12.0](#1-12-0)
|
* [1.12.0](#1-12-0)
|
||||||
* [1.11.0](#1-11-0)
|
* [1.11.0](#1-11-0)
|
||||||
|
|
@ -43,6 +45,80 @@
|
||||||
|
|
||||||
### Added
|
### Added
|
||||||
|
|
||||||
|
* Support for adjusting the thickness of regular underlines ([#1136][1136]).
|
||||||
|
* Support (optional) for utmp logging with libutempter.
|
||||||
|
* `kxIN` and `kxOUT` (focus in/out events) to terminfo.
|
||||||
|
* `name` capability to `XTGETTCAP`.
|
||||||
|
|
||||||
|
[1136]: https://codeberg.org/dnkl/foot/issues/1136
|
||||||
|
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
|
||||||
|
* Default color theme from a variant of the Zenburn theme, to a
|
||||||
|
variant of the Solarized dark theme.
|
||||||
|
* Default `pad` from 2x2 to 0x0 (i.e. no padding at all).
|
||||||
|
* Current working directory (as set by OSC-7) is now passed to the
|
||||||
|
program executed by the `pipe-*` key bindings ([#1166][1166]).
|
||||||
|
* `DECRPM` replies (to `DECRQM` queries) now report a value of `4`
|
||||||
|
("permanently reset") instead of `2` ("reset") for DEC private
|
||||||
|
modes that are known but unsupported.
|
||||||
|
* Set `PWD` environment variable in the slave process ([#1179][1179]).
|
||||||
|
|
||||||
|
[1166]: https://codeberg.org/dnkl/foot/issues/1166
|
||||||
|
[1179]: https://codeberg.org/dnkl/foot/issues/1179
|
||||||
|
|
||||||
|
|
||||||
|
### Deprecated
|
||||||
|
### Removed
|
||||||
|
### Fixed
|
||||||
|
|
||||||
|
* Crash in `foot --server` on key press, after another `footclient`
|
||||||
|
has terminated very early (for example, by trying to launch a
|
||||||
|
non-existing shell/client).
|
||||||
|
* Glitchy rendering when scrolling in the scrollback, on compositors
|
||||||
|
that does not allow Wayland buffer re-use (e.g. KDE/plasma)
|
||||||
|
([#1173][1173])
|
||||||
|
* Scrollback search matches not being highlighted correctly, on
|
||||||
|
compositors that does now allow Wayland buffer re-use
|
||||||
|
(e.g. KDE/plasma).
|
||||||
|
|
||||||
|
[1173]: https://codeberg.org/dnkl/foot/issues/1173
|
||||||
|
|
||||||
|
|
||||||
|
### Security
|
||||||
|
### Contributors
|
||||||
|
|
||||||
|
* Craig Barnes
|
||||||
|
|
||||||
|
|
||||||
|
## 1.13.1
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
|
||||||
|
* Window is now dimmed while in Unicode input mode.
|
||||||
|
|
||||||
|
|
||||||
|
### Fixed
|
||||||
|
|
||||||
|
* Compiling against wayland-protocols < 1.25
|
||||||
|
* Crash on buggy compositors (GNOME) that sometimes send pointer-enter
|
||||||
|
events with a NULL surface. Foot now ignores these events, and the
|
||||||
|
subsequent motion and leave events.
|
||||||
|
* Regression: “random” selected empty cells being highlighted as
|
||||||
|
selected when they should not.
|
||||||
|
* Crash when either resizing the terminal window, or scrolling in the
|
||||||
|
scrollback history ([#1074][1074])
|
||||||
|
* OSC-8 URLs with matching IDs, but mismatching URIs being incorrectly
|
||||||
|
connected.
|
||||||
|
|
||||||
|
[1074]: https://codeberg.org/dnkl/foot/pulls/1074
|
||||||
|
|
||||||
|
|
||||||
|
## 1.13.0
|
||||||
|
|
||||||
|
### Added
|
||||||
|
|
||||||
* XDG activation support when opening URLs ([#1058][1058]).
|
* XDG activation support when opening URLs ([#1058][1058]).
|
||||||
* `-Dsystemd-units-dir=<path>` meson command line option.
|
* `-Dsystemd-units-dir=<path>` meson command line option.
|
||||||
* Support for custom environment variables in `foot.ini`
|
* Support for custom environment variables in `foot.ini`
|
||||||
|
|
@ -50,10 +126,21 @@
|
||||||
* Support for jumping to previous/next prompt (requires shell
|
* Support for jumping to previous/next prompt (requires shell
|
||||||
integration). By default bound to `ctrl`+`shift`+`z` and
|
integration). By default bound to `ctrl`+`shift`+`z` and
|
||||||
`ctrl`+`shift`+`x` respectively ([#30][30]).
|
`ctrl`+`shift`+`x` respectively ([#30][30]).
|
||||||
|
* `colors.search-box-no-match` and `colors.search-box-match` options
|
||||||
|
to `foot.ini` ([#1112][1112]).
|
||||||
|
* Very basic Unicode input mode via the new
|
||||||
|
`key-bindings.unicode-input` and `search-bindings.unicode-input` key
|
||||||
|
bindings. Note that there is no visual feedback, as the preferred
|
||||||
|
way of entering Unicode characters is with an IME ([#1116][1116]).
|
||||||
|
* Support for `xdg_toplevel.wm_capabilities`, to adapt the client-side
|
||||||
|
decoration buttons to the compositor capabilities ([#1061][1061]).
|
||||||
|
|
||||||
[1058]: https://codeberg.org/dnkl/foot/issues/1058
|
[1058]: https://codeberg.org/dnkl/foot/issues/1058
|
||||||
[1070]: https://codeberg.org/dnkl/foot/issues/1070
|
[1070]: https://codeberg.org/dnkl/foot/issues/1070
|
||||||
[30]: https://codeberg.org/dnkl/foot/issues/30
|
[30]: https://codeberg.org/dnkl/foot/issues/30
|
||||||
|
[1112]: https://codeberg.org/dnkl/foot/issues/1112
|
||||||
|
[1116]: https://codeberg.org/dnkl/foot/issues/1116
|
||||||
|
[1061]: https://codeberg.org/dnkl/foot/pulls/1061
|
||||||
|
|
||||||
|
|
||||||
### Changed
|
### Changed
|
||||||
|
|
@ -64,12 +151,13 @@
|
||||||
mode ([#1084][1084]).
|
mode ([#1084][1084]).
|
||||||
* NUL is now stripped when pasting in non-bracketed mode
|
* NUL is now stripped when pasting in non-bracketed mode
|
||||||
([#1084][1084]).
|
([#1084][1084]).
|
||||||
|
* `alt`+`escape` now emits `\E\E` instead of a `CSI 27` sequence
|
||||||
|
([#1105][1105]).
|
||||||
|
|
||||||
[1084]: https://codeberg.org/dnkl/foot/issues/1084
|
[1084]: https://codeberg.org/dnkl/foot/issues/1084
|
||||||
|
[1105]: https://codeberg.org/dnkl/foot/issues/1105
|
||||||
|
|
||||||
|
|
||||||
### Deprecated
|
|
||||||
### Removed
|
|
||||||
### Fixed
|
### Fixed
|
||||||
|
|
||||||
* Graphical corruption when viewport is at the top of the scrollback,
|
* Graphical corruption when viewport is at the top of the scrollback,
|
||||||
|
|
@ -80,14 +168,41 @@
|
||||||
* Workaround for buggy compositors (e.g. some versions of GNOME)
|
* Workaround for buggy compositors (e.g. some versions of GNOME)
|
||||||
allowing drag-and-drops even though foot has reported it does not
|
allowing drag-and-drops even though foot has reported it does not
|
||||||
support the offered mime-types ([#1092][1092]).
|
support the offered mime-types ([#1092][1092]).
|
||||||
|
* Keyboard enter/leave events being ignored if there is no keymap
|
||||||
|
([#1097][1097]).
|
||||||
|
* Crash when application emitted an invalid `CSI 38;5;<idx>m`, `CSI
|
||||||
|
38:5:<idx>m`, `CSI 48;5;<idx>m` or `CSI 48:5:<idx>m` sequence
|
||||||
|
([#1111][1111]).
|
||||||
|
* Certain dead-key combinations resulting in different escape
|
||||||
|
sequences compared to kitty, when the kitty keyboard protocol is
|
||||||
|
used ([#1120][1120]).
|
||||||
|
* Search matches ending with a double-width character not being
|
||||||
|
highlighted correctly.
|
||||||
|
* Selection not being cancelled correctly when scrolled out.
|
||||||
|
* Extending a multi-page selection behaving inconsistently.
|
||||||
|
* Poor performance when making very large selections ([#1114][1114]).
|
||||||
|
* Bogus error message when using systemd socket activation for server
|
||||||
|
mode ([#1107][1107])
|
||||||
|
* Empty line at the bottom after a window resize ([#1108][1108]).
|
||||||
|
|
||||||
[1055]: https://codeberg.org/dnkl/foot/issues/1055
|
[1055]: https://codeberg.org/dnkl/foot/issues/1055
|
||||||
[1092]: https://codeberg.org/dnkl/foot/issues/1092
|
[1092]: https://codeberg.org/dnkl/foot/issues/1092
|
||||||
|
[1097]: https://codeberg.org/dnkl/foot/issues/1097
|
||||||
|
[1111]: https://codeberg.org/dnkl/foot/issues/1111
|
||||||
|
[1120]: https://codeberg.org/dnkl/foot/issues/1120
|
||||||
|
[1114]: https://codeberg.org/dnkl/foot/issues/1114
|
||||||
|
[1107]: https://codeberg.org/dnkl/foot/issues/1107
|
||||||
|
[1108]: https://codeberg.org/dnkl/foot/issues/1108
|
||||||
|
|
||||||
|
|
||||||
### Security
|
|
||||||
### Contributors
|
### Contributors
|
||||||
|
|
||||||
|
* Craig Barnes
|
||||||
|
* Lorenz
|
||||||
|
* Max Gautier
|
||||||
|
* Simon Ser
|
||||||
|
* Stefan Prosiegel
|
||||||
|
|
||||||
|
|
||||||
## 1.12.1
|
## 1.12.1
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -44,6 +44,8 @@ subprojects.
|
||||||
* pixman
|
* pixman
|
||||||
* wayland (_client_ and _cursor_ libraries)
|
* wayland (_client_ and _cursor_ libraries)
|
||||||
* xkbcommon
|
* xkbcommon
|
||||||
|
* utf8proc (_optional_, needed for grapheme clustering)
|
||||||
|
* libutempter (_optional_, needed for utmp logging)
|
||||||
* [fcft](https://codeberg.org/dnkl/fcft) [^1]
|
* [fcft](https://codeberg.org/dnkl/fcft) [^1]
|
||||||
|
|
||||||
[^1]: can also be built as subprojects, in which case they are
|
[^1]: can also be built as subprojects, in which case they are
|
||||||
|
|
@ -141,7 +143,7 @@ mkdir -p bld/release && cd bld/release
|
||||||
Available compile-time options:
|
Available compile-time options:
|
||||||
|
|
||||||
| Option | Type | Default | Description | Extra dependencies |
|
| Option | Type | Default | Description | Extra dependencies |
|
||||||
|--------------------------------------|---------|-------------------------|-------------------------------------------------------|--------------------|
|
|--------------------------------------|---------|-------------------------|-----------------------------------------------------------|--------------------|
|
||||||
| `-Ddocs` | feature | `auto` | Builds and install documentation | scdoc |
|
| `-Ddocs` | feature | `auto` | Builds and install documentation | scdoc |
|
||||||
| `-Dtests` | bool | `true` | Build tests (adds a `ninja test` build target) | none |
|
| `-Dtests` | bool | `true` | Build tests (adds a `ninja test` build target) | none |
|
||||||
| `-Dime` | bool | `true` | Enables IME support | None |
|
| `-Dime` | bool | `true` | Enables IME support | None |
|
||||||
|
|
@ -150,6 +152,7 @@ Available compile-time options:
|
||||||
| `-Ddefault-terminfo` | string | `foot` | Default value of `TERM` | none |
|
| `-Ddefault-terminfo` | string | `foot` | Default value of `TERM` | none |
|
||||||
| `-Dcustom-terminfo-install-location` | string | `${datadir}/terminfo` | Value to set `TERMINFO` to | None |
|
| `-Dcustom-terminfo-install-location` | string | `${datadir}/terminfo` | Value to set `TERMINFO` to | None |
|
||||||
| `-Dsystemd-units-dir` | string | `${systemduserunitdir}` | Where to install the systemd service files (absolute) | None |
|
| `-Dsystemd-units-dir` | string | `${systemduserunitdir}` | Where to install the systemd service files (absolute) | None |
|
||||||
|
| `-Ddefault-utempter-path` | feature | `auto` | Default path to utempter binary (‘none’ disables default) | libutempter |
|
||||||
|
|
||||||
Documentation includes the man pages, readme, changelog and license
|
Documentation includes the man pages, readme, changelog and license
|
||||||
files.
|
files.
|
||||||
|
|
|
||||||
22
client.c
22
client.c
|
|
@ -408,6 +408,28 @@ main(int argc, char *const *argv)
|
||||||
cwd = _cwd;
|
cwd = _cwd;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const char *pwd = getenv("PWD");
|
||||||
|
if (pwd != NULL) {
|
||||||
|
char *resolved_path_cwd = realpath(cwd, NULL);
|
||||||
|
char *resolved_path_pwd = realpath(pwd, NULL);
|
||||||
|
|
||||||
|
if (resolved_path_cwd != NULL &&
|
||||||
|
resolved_path_pwd != NULL &&
|
||||||
|
strcmp(resolved_path_cwd, resolved_path_pwd) == 0)
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
* The resolved path of $PWD matches the resolved path of
|
||||||
|
* the *actual* working directory - use $PWD.
|
||||||
|
*
|
||||||
|
* This makes a difference when $PWD refers to a symlink.
|
||||||
|
*/
|
||||||
|
cwd = pwd;
|
||||||
|
}
|
||||||
|
|
||||||
|
free(resolved_path_cwd);
|
||||||
|
free(resolved_path_pwd);
|
||||||
|
}
|
||||||
|
|
||||||
if (client_environment) {
|
if (client_environment) {
|
||||||
for (char **e = environ; *e != NULL; e++) {
|
for (char **e = environ; *e != NULL; e++) {
|
||||||
if (!push_string(&envp, *e, &total_len))
|
if (!push_string(&envp, *e, &total_len))
|
||||||
|
|
|
||||||
97
config.c
97
config.c
|
|
@ -30,8 +30,8 @@
|
||||||
#include "xmalloc.h"
|
#include "xmalloc.h"
|
||||||
#include "xsnprintf.h"
|
#include "xsnprintf.h"
|
||||||
|
|
||||||
static const uint32_t default_foreground = 0xdcdccc;
|
static const uint32_t default_foreground = 0x839496;
|
||||||
static const uint32_t default_background = 0x111111;
|
static const uint32_t default_background = 0x002b36;
|
||||||
|
|
||||||
static const size_t min_csd_border_width = 5;
|
static const size_t min_csd_border_width = 5;
|
||||||
|
|
||||||
|
|
@ -48,23 +48,23 @@ static const size_t min_csd_border_width = 5;
|
||||||
|
|
||||||
static const uint32_t default_color_table[256] = {
|
static const uint32_t default_color_table[256] = {
|
||||||
// Regular
|
// Regular
|
||||||
0x222222,
|
0x073642,
|
||||||
0xcc9393,
|
0xdc322f,
|
||||||
0x7f9f7f,
|
0x859900,
|
||||||
0xd0bf8f,
|
0xb58900,
|
||||||
0x6ca0a3,
|
0x268bd2,
|
||||||
0xdc8cc3,
|
0xd33682,
|
||||||
0x93e0e3,
|
0x2aa198,
|
||||||
0xdcdccc,
|
0xeee8d5,
|
||||||
|
|
||||||
// Bright
|
// Bright
|
||||||
0x666666,
|
0x08404f,
|
||||||
0xdca3a3,
|
0xe35f5c,
|
||||||
0xbfebbf,
|
0x9fb700,
|
||||||
0xf0dfaf,
|
0xd9a400,
|
||||||
0x8cd0d3,
|
0x4ba1de,
|
||||||
0xfcace3,
|
0xdc619d,
|
||||||
0xb3ffff,
|
0x32c1b6,
|
||||||
0xffffff,
|
0xffffff,
|
||||||
|
|
||||||
// 6x6x6 RGB cube
|
// 6x6x6 RGB cube
|
||||||
|
|
@ -117,6 +117,7 @@ static const char *const binding_action_map[] = {
|
||||||
[BIND_ACTION_TEXT_BINDING] = "text-binding",
|
[BIND_ACTION_TEXT_BINDING] = "text-binding",
|
||||||
[BIND_ACTION_PROMPT_PREV] = "prompt-prev",
|
[BIND_ACTION_PROMPT_PREV] = "prompt-prev",
|
||||||
[BIND_ACTION_PROMPT_NEXT] = "prompt-next",
|
[BIND_ACTION_PROMPT_NEXT] = "prompt-next",
|
||||||
|
[BIND_ACTION_UNICODE_INPUT] = "unicode-input",
|
||||||
|
|
||||||
/* Mouse-specific actions */
|
/* Mouse-specific actions */
|
||||||
[BIND_ACTION_SELECT_BEGIN] = "select-begin",
|
[BIND_ACTION_SELECT_BEGIN] = "select-begin",
|
||||||
|
|
@ -149,6 +150,7 @@ static const char *const search_binding_action_map[] = {
|
||||||
[BIND_ACTION_SEARCH_EXTEND_LINE] = "extend-to-end-line",
|
[BIND_ACTION_SEARCH_EXTEND_LINE] = "extend-to-end-line",
|
||||||
[BIND_ACTION_SEARCH_CLIPBOARD_PASTE] = "clipboard-paste",
|
[BIND_ACTION_SEARCH_CLIPBOARD_PASTE] = "clipboard-paste",
|
||||||
[BIND_ACTION_SEARCH_PRIMARY_PASTE] = "primary-paste",
|
[BIND_ACTION_SEARCH_PRIMARY_PASTE] = "primary-paste",
|
||||||
|
[BIND_ACTION_SEARCH_UNICODE_INPUT] = "unicode-input",
|
||||||
};
|
};
|
||||||
|
|
||||||
static const char *const url_binding_action_map[] = {
|
static const char *const url_binding_action_map[] = {
|
||||||
|
|
@ -903,6 +905,9 @@ parse_section_main(struct context *ctx)
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
else if (strcmp(key, "underline-thickness") == 0)
|
||||||
|
return value_to_pt_or_px(ctx, &conf->underline_thickness);
|
||||||
|
|
||||||
else if (strcmp(key, "dpi-aware") == 0) {
|
else if (strcmp(key, "dpi-aware") == 0) {
|
||||||
if (strcmp(value, "auto") == 0)
|
if (strcmp(value, "auto") == 0)
|
||||||
conf->dpi_aware = DPI_AWARE_AUTO;
|
conf->dpi_aware = DPI_AWARE_AUTO;
|
||||||
|
|
@ -940,6 +945,18 @@ parse_section_main(struct context *ctx)
|
||||||
else if (strcmp(key, "box-drawings-uses-font-glyphs") == 0)
|
else if (strcmp(key, "box-drawings-uses-font-glyphs") == 0)
|
||||||
return value_to_bool(ctx, &conf->box_drawings_uses_font_glyphs);
|
return value_to_bool(ctx, &conf->box_drawings_uses_font_glyphs);
|
||||||
|
|
||||||
|
else if (strcmp(key, "utempter") == 0) {
|
||||||
|
if (!value_to_str(ctx, &conf->utempter_path))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
if (strcmp(conf->utempter_path, "none") == 0) {
|
||||||
|
free(conf->utempter_path);
|
||||||
|
conf->utempter_path = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
else {
|
else {
|
||||||
LOG_CONTEXTUAL_ERR("not a valid option: %s", key);
|
LOG_CONTEXTUAL_ERR("not a valid option: %s", key);
|
||||||
return false;
|
return false;
|
||||||
|
|
@ -1172,6 +1189,34 @@ parse_section_colors(struct context *ctx)
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
else if (strcmp(key, "search-box-no-match") == 0) {
|
||||||
|
if (!value_to_two_colors(
|
||||||
|
ctx,
|
||||||
|
&conf->colors.search_box.no_match.fg,
|
||||||
|
&conf->colors.search_box.no_match.bg,
|
||||||
|
false))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
conf->colors.use_custom.search_box_no_match = true;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
else if (strcmp(key, "search-box-match") == 0) {
|
||||||
|
if (!value_to_two_colors(
|
||||||
|
ctx,
|
||||||
|
&conf->colors.search_box.match.fg,
|
||||||
|
&conf->colors.search_box.match.bg,
|
||||||
|
false))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
conf->colors.use_custom.search_box_match = true;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
else if (strcmp(key, "urls") == 0) {
|
else if (strcmp(key, "urls") == 0) {
|
||||||
if (!value_to_color(ctx, &conf->colors.url, false))
|
if (!value_to_color(ctx, &conf->colors.url, false))
|
||||||
return false;
|
return false;
|
||||||
|
|
@ -1976,6 +2021,9 @@ resolve_key_binding_collisions(struct config *conf, const char *section_name,
|
||||||
sym_equal = (binding1->m.button == binding2->m.button &&
|
sym_equal = (binding1->m.button == binding2->m.button &&
|
||||||
binding1->m.count == binding2->m.count);
|
binding1->m.count == binding2->m.count);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
BUG("unhandled key binding type");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!mods_equal || !sym_equal)
|
if (!mods_equal || !sym_equal)
|
||||||
|
|
@ -2610,6 +2658,9 @@ parse_config_file(FILE *f, struct config *conf, const char *path, bool errors_ar
|
||||||
|
|
||||||
if (!section_parser(ctx))
|
if (!section_parser(ctx))
|
||||||
error_or_continue();
|
error_or_continue();
|
||||||
|
|
||||||
|
/* For next iteration of getline() */
|
||||||
|
errno = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (errno != 0) {
|
if (errno != 0) {
|
||||||
|
|
@ -2787,8 +2838,8 @@ config_load(struct config *conf, const char *conf_path,
|
||||||
.width = 700,
|
.width = 700,
|
||||||
.height = 500,
|
.height = 500,
|
||||||
},
|
},
|
||||||
.pad_x = 2,
|
.pad_x = 0,
|
||||||
.pad_y = 2,
|
.pad_y = 0,
|
||||||
.resize_delay_ms = 100,
|
.resize_delay_ms = 100,
|
||||||
.bold_in_bright = {
|
.bold_in_bright = {
|
||||||
.enabled = false,
|
.enabled = false,
|
||||||
|
|
@ -2802,6 +2853,7 @@ config_load(struct config *conf, const char *conf_path,
|
||||||
.vertical_letter_offset = {.pt = 0, .px = 0},
|
.vertical_letter_offset = {.pt = 0, .px = 0},
|
||||||
.use_custom_underline_offset = false,
|
.use_custom_underline_offset = false,
|
||||||
.box_drawings_uses_font_glyphs = false,
|
.box_drawings_uses_font_glyphs = false,
|
||||||
|
.underline_thickness = {.pt = 0., .px = -1},
|
||||||
.dpi_aware = DPI_AWARE_AUTO, /* DPI-aware when scaling-factor == 1 */
|
.dpi_aware = DPI_AWARE_AUTO, /* DPI-aware when scaling-factor == 1 */
|
||||||
.bell = {
|
.bell = {
|
||||||
.urgent = false,
|
.urgent = false,
|
||||||
|
|
@ -2899,6 +2951,9 @@ config_load(struct config *conf, const char *conf_path,
|
||||||
},
|
},
|
||||||
|
|
||||||
.env_vars = tll_init(),
|
.env_vars = tll_init(),
|
||||||
|
.utempter_path = (strlen(FOOT_DEFAULT_UTEMPTER_PATH) > 0
|
||||||
|
? xstrdup(FOOT_DEFAULT_UTEMPTER_PATH)
|
||||||
|
: NULL),
|
||||||
.notifications = tll_init(),
|
.notifications = tll_init(),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -3187,6 +3242,9 @@ config_clone(const struct config *old)
|
||||||
tll_push_back(conf->env_vars, copy);
|
tll_push_back(conf->env_vars, copy);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
conf->utempter_path =
|
||||||
|
old->utempter_path != NULL ? xstrdup(old->utempter_path) : NULL;
|
||||||
|
|
||||||
conf->notifications.length = 0;
|
conf->notifications.length = 0;
|
||||||
conf->notifications.head = conf->notifications.tail = 0;
|
conf->notifications.head = conf->notifications.tail = 0;
|
||||||
tll_foreach(old->notifications, it) {
|
tll_foreach(old->notifications, it) {
|
||||||
|
|
@ -3253,6 +3311,7 @@ config_free(struct config *conf)
|
||||||
tll_remove(conf->env_vars, it);
|
tll_remove(conf->env_vars, it);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
free(conf->utempter_path);
|
||||||
user_notifications_free(&conf->notifications);
|
user_notifications_free(&conf->notifications);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
17
config.h
17
config.h
|
|
@ -150,6 +150,7 @@ struct config {
|
||||||
|
|
||||||
bool use_custom_underline_offset;
|
bool use_custom_underline_offset;
|
||||||
struct pt_or_px underline_offset;
|
struct pt_or_px underline_offset;
|
||||||
|
struct pt_or_px underline_thickness;
|
||||||
|
|
||||||
bool box_drawings_uses_font_glyphs;
|
bool box_drawings_uses_font_glyphs;
|
||||||
bool can_shape_grapheme;
|
bool can_shape_grapheme;
|
||||||
|
|
@ -217,11 +218,25 @@ struct config {
|
||||||
uint32_t bg;
|
uint32_t bg;
|
||||||
} scrollback_indicator;
|
} scrollback_indicator;
|
||||||
|
|
||||||
|
struct {
|
||||||
|
struct {
|
||||||
|
uint32_t fg;
|
||||||
|
uint32_t bg;
|
||||||
|
} no_match;
|
||||||
|
|
||||||
|
struct {
|
||||||
|
uint32_t fg;
|
||||||
|
uint32_t bg;
|
||||||
|
} match;
|
||||||
|
} search_box;
|
||||||
|
|
||||||
struct {
|
struct {
|
||||||
bool selection:1;
|
bool selection:1;
|
||||||
bool jump_label:1;
|
bool jump_label:1;
|
||||||
bool scrollback_indicator:1;
|
bool scrollback_indicator:1;
|
||||||
bool url:1;
|
bool url:1;
|
||||||
|
bool search_box_no_match:1;
|
||||||
|
bool search_box_match:1;
|
||||||
uint8_t dim;
|
uint8_t dim;
|
||||||
} use_custom;
|
} use_custom;
|
||||||
} colors;
|
} colors;
|
||||||
|
|
@ -304,6 +319,8 @@ struct config {
|
||||||
|
|
||||||
env_var_list_t env_vars;
|
env_var_list_t env_vars;
|
||||||
|
|
||||||
|
char *utempter_path;
|
||||||
|
|
||||||
struct {
|
struct {
|
||||||
enum fcft_scaling_filter fcft_filter;
|
enum fcft_scaling_filter fcft_filter;
|
||||||
bool overflowing_glyphs;
|
bool overflowing_glyphs;
|
||||||
|
|
|
||||||
106
csi.c
106
csi.c
|
|
@ -128,7 +128,8 @@ csi_sgr(struct terminal *term)
|
||||||
term->vt.params.v[i + 1].value == 5)
|
term->vt.params.v[i + 1].value == 5)
|
||||||
{
|
{
|
||||||
src = COLOR_BASE256;
|
src = COLOR_BASE256;
|
||||||
color = term->vt.params.v[i + 2].value;
|
color = min(term->vt.params.v[i + 2].value,
|
||||||
|
ALEN(term->colors.table) - 1);
|
||||||
i += 2;
|
i += 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -149,7 +150,8 @@ csi_sgr(struct terminal *term)
|
||||||
term->vt.params.v[i].sub.value[0] == 5)
|
term->vt.params.v[i].sub.value[0] == 5)
|
||||||
{
|
{
|
||||||
src = COLOR_BASE256;
|
src = COLOR_BASE256;
|
||||||
color = term->vt.params.v[i].sub.value[1];
|
color = min(term->vt.params.v[i].sub.value[1],
|
||||||
|
ALEN(term->colors.table) - 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|
@ -484,7 +486,7 @@ decset_decrst(struct terminal *term, unsigned param, bool enable)
|
||||||
}
|
}
|
||||||
|
|
||||||
tll_free(term->alt.scroll_damage);
|
tll_free(term->alt.scroll_damage);
|
||||||
term_damage_all(term);
|
term_damage_view(term);
|
||||||
}
|
}
|
||||||
term_update_ascii_printer(term);
|
term_update_ascii_printer(term);
|
||||||
break;
|
break;
|
||||||
|
|
@ -533,47 +535,65 @@ decrst(struct terminal *term, unsigned param)
|
||||||
decset_decrst(term, param, false);
|
decset_decrst(term, param, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool
|
/*
|
||||||
decrqm(const struct terminal *term, unsigned param, bool *enabled)
|
* These values represent the current state of a DEC private mode,
|
||||||
|
* as returned in the DECRPM reply to a DECRQM query.
|
||||||
|
*/
|
||||||
|
enum decrpm_status {
|
||||||
|
DECRPM_NOT_RECOGNIZED = 0,
|
||||||
|
DECRPM_SET = 1,
|
||||||
|
DECRPM_RESET = 2,
|
||||||
|
DECRPM_PERMANENTLY_SET = 3,
|
||||||
|
DECRPM_PERMANENTLY_RESET = 4,
|
||||||
|
};
|
||||||
|
|
||||||
|
static enum decrpm_status
|
||||||
|
decrpm(bool enabled)
|
||||||
|
{
|
||||||
|
return enabled ? DECRPM_SET : DECRPM_RESET;
|
||||||
|
}
|
||||||
|
|
||||||
|
static enum decrpm_status
|
||||||
|
decrqm(const struct terminal *term, unsigned param)
|
||||||
{
|
{
|
||||||
switch (param) {
|
switch (param) {
|
||||||
case 1: *enabled = term->cursor_keys_mode == CURSOR_KEYS_APPLICATION; return true;
|
case 1: return decrpm(term->cursor_keys_mode == CURSOR_KEYS_APPLICATION);
|
||||||
case 3: *enabled = false; return true;
|
case 3: return DECRPM_PERMANENTLY_RESET;
|
||||||
case 4: *enabled = false; return true;
|
case 4: return DECRPM_PERMANENTLY_RESET;
|
||||||
case 5: *enabled = term->reverse; return true;
|
case 5: return decrpm(term->reverse);
|
||||||
case 6: *enabled = term->origin; return true;
|
case 6: return decrpm(term->origin);
|
||||||
case 7: *enabled = term->auto_margin; return true;
|
case 7: return decrpm(term->auto_margin);
|
||||||
case 9: *enabled = false; /* term->mouse_tracking == MOUSE_X10; */ return true;
|
case 9: return DECRPM_PERMANENTLY_RESET; /* term->mouse_tracking == MOUSE_X10; */
|
||||||
case 12: *enabled = term->cursor_blink.decset; return true;
|
case 12: return decrpm(term->cursor_blink.decset);
|
||||||
case 25: *enabled = !term->hide_cursor; return true;
|
case 25: return decrpm(!term->hide_cursor);
|
||||||
case 45: *enabled = term->reverse_wrap; return true;
|
case 45: return decrpm(term->reverse_wrap);
|
||||||
case 66: *enabled = term->keypad_keys_mode == KEYPAD_APPLICATION; return true;
|
case 66: return decrpm(term->keypad_keys_mode == KEYPAD_APPLICATION);
|
||||||
case 80: *enabled = !term->sixel.scrolling; return true;
|
case 80: return decrpm(!term->sixel.scrolling);
|
||||||
case 1000: *enabled = term->mouse_tracking == MOUSE_CLICK; return true;
|
case 1000: return decrpm(term->mouse_tracking == MOUSE_CLICK);
|
||||||
case 1001: *enabled = false; return true;
|
case 1001: return DECRPM_PERMANENTLY_RESET;
|
||||||
case 1002: *enabled = term->mouse_tracking == MOUSE_DRAG; return true;
|
case 1002: return decrpm(term->mouse_tracking == MOUSE_DRAG);
|
||||||
case 1003: *enabled = term->mouse_tracking == MOUSE_MOTION; return true;
|
case 1003: return decrpm(term->mouse_tracking == MOUSE_MOTION);
|
||||||
case 1004: *enabled = term->focus_events; return true;
|
case 1004: return decrpm(term->focus_events);
|
||||||
case 1005: *enabled = false; /* term->mouse_reporting == MOUSE_UTF8; */ return true;
|
case 1005: return DECRPM_PERMANENTLY_RESET; /* term->mouse_reporting == MOUSE_UTF8; */
|
||||||
case 1006: *enabled = term->mouse_reporting == MOUSE_SGR; return true;
|
case 1006: return decrpm(term->mouse_reporting == MOUSE_SGR);
|
||||||
case 1007: *enabled = term->alt_scrolling; return true;
|
case 1007: return decrpm(term->alt_scrolling);
|
||||||
case 1015: *enabled = term->mouse_reporting == MOUSE_URXVT; return true;
|
case 1015: return decrpm(term->mouse_reporting == MOUSE_URXVT);
|
||||||
case 1016: *enabled = term->mouse_reporting == MOUSE_SGR_PIXELS; return true;
|
case 1016: return decrpm(term->mouse_reporting == MOUSE_SGR_PIXELS);
|
||||||
case 1034: *enabled = term->meta.eight_bit; return true;
|
case 1034: return decrpm(term->meta.eight_bit);
|
||||||
case 1035: *enabled = term->num_lock_modifier; return true;
|
case 1035: return decrpm(term->num_lock_modifier);
|
||||||
case 1036: *enabled = term->meta.esc_prefix; return true;
|
case 1036: return decrpm(term->meta.esc_prefix);
|
||||||
case 1042: *enabled = term->bell_action_enabled; return true;
|
case 1042: return decrpm(term->bell_action_enabled);
|
||||||
case 47: /* FALLTHROUGH */
|
case 47: /* FALLTHROUGH */
|
||||||
case 1047: /* FALLTHROUGH */
|
case 1047: /* FALLTHROUGH */
|
||||||
case 1049: *enabled = term->grid == &term->alt; return true;
|
case 1049: return decrpm(term->grid == &term->alt);
|
||||||
case 1070: *enabled = term->sixel.use_private_palette; return true;
|
case 1070: return decrpm(term->sixel.use_private_palette);
|
||||||
case 2004: *enabled = term->bracketed_paste; return true;
|
case 2004: return decrpm(term->bracketed_paste);
|
||||||
case 2026: *enabled = term->render.app_sync_updates.enabled; return true;
|
case 2026: return decrpm(term->render.app_sync_updates.enabled);
|
||||||
case 8452: *enabled = term->sixel.cursor_right_of_graphics; return true;
|
case 8452: return decrpm(term->sixel.cursor_right_of_graphics);
|
||||||
case 737769: *enabled = term_ime_is_enabled(term); return true;
|
case 737769: return decrpm(term_ime_is_enabled(term));
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return DECRPM_NOT_RECOGNIZED;
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
|
|
@ -1719,15 +1739,9 @@ csi_dispatch(struct terminal *term, uint8_t final)
|
||||||
* 3 - permanently set
|
* 3 - permanently set
|
||||||
* 4 - permantently reset
|
* 4 - permantently reset
|
||||||
*/
|
*/
|
||||||
bool enabled;
|
unsigned status = decrqm(term, param);
|
||||||
unsigned value;
|
|
||||||
if (decrqm(term, param, &enabled))
|
|
||||||
value = enabled ? 1 : 2;
|
|
||||||
else
|
|
||||||
value = 0;
|
|
||||||
|
|
||||||
char reply[32];
|
char reply[32];
|
||||||
size_t n = xsnprintf(reply, sizeof(reply), "\033[?%u;%u$y", param, value);
|
size_t n = xsnprintf(reply, sizeof(reply), "\033[?%u;%u$y", param, status);
|
||||||
term_to_slave(term, reply, n);
|
term_to_slave(term, reply, n);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -132,6 +132,18 @@ commented out will usually be installed to */etc/xdg/foot/foot.ini*.
|
||||||
|
|
||||||
Default: _unset_.
|
Default: _unset_.
|
||||||
|
|
||||||
|
*underline-thickness*
|
||||||
|
Use a custom thickness (height) for underlines. The thickness is, by
|
||||||
|
default, in _points_.
|
||||||
|
|
||||||
|
To specify a thickness in _pixels_, append *px*:
|
||||||
|
*underline-thickness=1px*.
|
||||||
|
|
||||||
|
If left unset (the default), the thickness specified in the font is
|
||||||
|
used.
|
||||||
|
|
||||||
|
Default: _unset_
|
||||||
|
|
||||||
*box-drawings-uses-font-glyphs* Boolean. When disabled, foot generates
|
*box-drawings-uses-font-glyphs* Boolean. When disabled, foot generates
|
||||||
box/line drawing characters itself. The are several advantages to
|
box/line drawing characters itself. The are several advantages to
|
||||||
doing this instead of using font glyphs:
|
doing this instead of using font glyphs:
|
||||||
|
|
@ -198,7 +210,7 @@ commented out will usually be installed to */etc/xdg/foot/foot.ini*.
|
||||||
To instead center the grid content, append *center* (e.g. *pad=5x5
|
To instead center the grid content, append *center* (e.g. *pad=5x5
|
||||||
center*).
|
center*).
|
||||||
|
|
||||||
Default: _2x2_.
|
Default: _0x0_.
|
||||||
|
|
||||||
*resize-delay-ms*
|
*resize-delay-ms*
|
||||||
Time, in milliseconds, of "idle time" before foot sends the new
|
Time, in milliseconds, of "idle time" before foot sends the new
|
||||||
|
|
@ -305,6 +317,10 @@ commented out will usually be installed to */etc/xdg/foot/foot.ini*.
|
||||||
(including SMT). Note that this is not always the best value. In
|
(including SMT). Note that this is not always the best value. In
|
||||||
some cases, the number of physical _cores_ is better.
|
some cases, the number of physical _cores_ is better.
|
||||||
|
|
||||||
|
*utempter*
|
||||||
|
Path to utempter helper binary. Set to *none* to disable utmp
|
||||||
|
records. Default: _@utempter@_.
|
||||||
|
|
||||||
# SECTION: environment
|
# SECTION: environment
|
||||||
|
|
||||||
This section is used to define environment variables that will be set
|
This section is used to define environment variables that will be set
|
||||||
|
|
@ -495,21 +511,23 @@ can configure the background transparency with the _alpha_ option.
|
||||||
|
|
||||||
*foreground*
|
*foreground*
|
||||||
Default foreground color. This is the color used when no ANSI
|
Default foreground color. This is the color used when no ANSI
|
||||||
color is being used. Default: _dcdccc_.
|
color is being used. Default: _839496_.
|
||||||
|
|
||||||
*background*
|
*background*
|
||||||
Default background color. This is the color used when no ANSI
|
Default background color. This is the color used when no ANSI
|
||||||
color is being used. Default: _111111_.
|
color is being used. Default: _002b36_.
|
||||||
|
|
||||||
*regular0*, *regular1* *..* *regular7*
|
*regular0*, *regular1* *..* *regular7*
|
||||||
The eight basic ANSI colors (Black, Red, Green, Yellow, Blue,
|
The eight basic ANSI colors (Black, Red, Green, Yellow, Blue,
|
||||||
Magenta, Cyan, White). Default: _222222_, _cc9393_, _7f9f7f_, _d0bf8f_,
|
Magenta, Cyan, White). Default: _073642_, _dc322f_, _859900_,
|
||||||
_6ca0a3_, _dc8cc3_, _93e0e3_ and _dcdccc_ (a variant of the _zenburn_ theme).
|
_b58900_, _268bd2_, _d33682_, _2aa198_ and _eee8d5_ (a variant of
|
||||||
|
the _solarized dark_ theme).
|
||||||
|
|
||||||
*bright0*, *bright1* *..* *bright7*
|
*bright0*, *bright1* *..* *bright7*
|
||||||
The eight bright ANSI colors (Black, Red, Green, Yellow, Blue,
|
The eight bright ANSI colors (Black, Red, Green, Yellow, Blue,
|
||||||
Magenta, Cyan, White). Default: _666666_, _dca3a3_, _bfebbf_, _f0dfaf_,
|
Magenta, Cyan, White). Default: _08404f_, _e35f5c_, _9fb700_,
|
||||||
_8cd0d3_, _fcace3_, _b3ffff_ and _ffffff_ (a variant of the _zenburn_ theme).
|
_d9a400_, _4ba1de_, _dc619d_, _32c1b6_ and _ffffff_ (a variant of
|
||||||
|
the _solarized dark_ theme).
|
||||||
|
|
||||||
*dim0*, *dim1* *..* *dim7*
|
*dim0*, *dim1* *..* *dim7*
|
||||||
Custom colors to use with dimmed colors. Dimmed colors do not have
|
Custom colors to use with dimmed colors. Dimmed colors do not have
|
||||||
|
|
@ -566,6 +584,16 @@ can configure the background transparency with the _alpha_ option.
|
||||||
(indicator itself) colors for the scrollback indicator. Default:
|
(indicator itself) colors for the scrollback indicator. Default:
|
||||||
_regular0 bright4_.
|
_regular0 bright4_.
|
||||||
|
|
||||||
|
*search-box-no-match*
|
||||||
|
Two color values specifying the foreground (text) and background
|
||||||
|
colors for the scrollback search box, when there are no
|
||||||
|
matches. Default: _regular0 regular1_.
|
||||||
|
|
||||||
|
*search-box-match*
|
||||||
|
Two color values specifying the foreground (text) and background
|
||||||
|
colors for the scrollback search box, when the search box is
|
||||||
|
either empty, or there are matches. Default: _regular0 regular3_.
|
||||||
|
|
||||||
*urls*
|
*urls*
|
||||||
Color to use for the underline used to highlight URLs in URL
|
Color to use for the underline used to highlight URLs in URL
|
||||||
mode. Default: _regular3_.
|
mode. Default: _regular3_.
|
||||||
|
|
@ -781,6 +809,32 @@ e.g. *search-start=none*.
|
||||||
Jump the next prompt (requires shell integration, see
|
Jump the next prompt (requires shell integration, see
|
||||||
*foot*(1)). Default: _Control+Shift+x_.
|
*foot*(1)). Default: _Control+Shift+x_.
|
||||||
|
|
||||||
|
*unicode-input*
|
||||||
|
Input a Unicode character by typing its codepoint in hexadecimal,
|
||||||
|
followed by *Enter* or *Space*.
|
||||||
|
|
||||||
|
For example, to input the character _ö_ (LATIN SMALL LETTER O WITH
|
||||||
|
DIAERESIS, Unicode codepoint 0xf6), you would first activate this
|
||||||
|
key binding, then type: *f*, *6*, *Enter*.
|
||||||
|
|
||||||
|
Another example: to input 😍 (SMILING FACE WITH HEART-SHAPED EYES,
|
||||||
|
Unicode codepoint 0x1f60d), activate this key binding, then type:
|
||||||
|
*1*, *f*, *6*, *0*, *d*, *Enter*.
|
||||||
|
|
||||||
|
Recognized key bindings in Unicode input mode:
|
||||||
|
|
||||||
|
- Enter, Space: commit the Unicode character, then exit this mode.
|
||||||
|
- Escape, q, Ctrl+c, Ctrl+d, Ctrl+g: abort input, then exit this mode.
|
||||||
|
- 0-9, a-f: append next digit to the Unicode's codepoint.
|
||||||
|
- Backspace: undo the last digit.
|
||||||
|
|
||||||
|
Note that there is no visual feedback while in this mode. This is
|
||||||
|
by design; foot's Unicode input mode is considered to be a
|
||||||
|
fallback. The preferred way of entering Unicode characters, emojis
|
||||||
|
etc is by using an IME.
|
||||||
|
|
||||||
|
Default: _none_.
|
||||||
|
|
||||||
# SECTION: search-bindings
|
# SECTION: search-bindings
|
||||||
|
|
||||||
This section lets you override the default key bindings used in
|
This section lets you override the default key bindings used in
|
||||||
|
|
@ -863,6 +917,10 @@ scrollback search mode. The syntax is exactly the same as the regular
|
||||||
Paste from the _primary selection_ into the search
|
Paste from the _primary selection_ into the search
|
||||||
buffer. Default: _Shift+Insert_.
|
buffer. Default: _Shift+Insert_.
|
||||||
|
|
||||||
|
*unicode-input*
|
||||||
|
Unicode input mode. See _key-bindings.unicode-input_ for
|
||||||
|
details. Default: _none_.
|
||||||
|
|
||||||
# SECTION: url-bindings
|
# SECTION: url-bindings
|
||||||
|
|
||||||
This section lets you override the default key bindings used in URL
|
This section lets you override the default key bindings used in URL
|
||||||
|
|
|
||||||
|
|
@ -2,9 +2,16 @@ sh = find_program('sh', native: true)
|
||||||
|
|
||||||
scdoc_prog = find_program(scdoc.get_variable('scdoc'), native: true)
|
scdoc_prog = find_program(scdoc.get_variable('scdoc'), native: true)
|
||||||
|
|
||||||
|
if utempter_path == ''
|
||||||
|
default_utempter_value = 'not set'
|
||||||
|
else
|
||||||
|
default_utempter_value = utempter_path
|
||||||
|
endif
|
||||||
|
|
||||||
conf_data = configuration_data(
|
conf_data = configuration_data(
|
||||||
{
|
{
|
||||||
'default_terminfo': get_option('default-terminfo'),
|
'default_terminfo': get_option('default-terminfo'),
|
||||||
|
'utempter': default_utempter_value,
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,8 @@
|
||||||
[Service]
|
[Service]
|
||||||
ExecStart=@bindir@/foot --server=0
|
ExecStart=@bindir@/foot --server=3
|
||||||
Environment=WAYLAND_DISPLAY=%i
|
Environment=WAYLAND_DISPLAY=%i
|
||||||
|
UnsetEnvironment=LISTEN_PID LISTEN_FDS LISTEN_FDNAMES
|
||||||
NonBlocking=true
|
NonBlocking=true
|
||||||
StandardInput=socket
|
|
||||||
|
|
||||||
[Unit]
|
[Unit]
|
||||||
Requires=%N.socket
|
Requires=%N.socket
|
||||||
|
|
|
||||||
|
|
@ -218,6 +218,8 @@
|
||||||
knp=\E[6~,
|
knp=\E[6~,
|
||||||
kpp=\E[5~,
|
kpp=\E[5~,
|
||||||
kri=\E[1;2A,
|
kri=\E[1;2A,
|
||||||
|
kxIN=\E[I,
|
||||||
|
kxOUT=\E[O,
|
||||||
oc=\E]104\E\\,
|
oc=\E]104\E\\,
|
||||||
op=\E[39;49m,
|
op=\E[39;49m,
|
||||||
rc=\E8,
|
rc=\E8,
|
||||||
|
|
|
||||||
46
foot.ini
46
foot.ini
|
|
@ -17,13 +17,14 @@
|
||||||
# horizontal-letter-offset=0
|
# horizontal-letter-offset=0
|
||||||
# vertical-letter-offset=0
|
# vertical-letter-offset=0
|
||||||
# underline-offset=<font metrics>
|
# underline-offset=<font metrics>
|
||||||
|
# underline-thickness=<font underline thickness>
|
||||||
# box-drawings-uses-font-glyphs=no
|
# box-drawings-uses-font-glyphs=no
|
||||||
# dpi-aware=auto
|
# dpi-aware=auto
|
||||||
|
|
||||||
# initial-window-size-pixels=700x500 # Or,
|
# initial-window-size-pixels=700x500 # Or,
|
||||||
# initial-window-size-chars=<COLSxROWS>
|
# initial-window-size-chars=<COLSxROWS>
|
||||||
# initial-window-mode=windowed
|
# initial-window-mode=windowed
|
||||||
# pad=2x2 # optionally append 'center'
|
# pad=0x0 # optionally append 'center'
|
||||||
# resize-delay-ms=100
|
# resize-delay-ms=100
|
||||||
|
|
||||||
# notify=notify-send -a ${app-id} -i ${app-id} ${title} ${body}
|
# notify=notify-send -a ${app-id} -i ${app-id} ${title} ${body}
|
||||||
|
|
@ -32,6 +33,7 @@
|
||||||
# word-delimiters=,│`|:"'()[]{}<>
|
# word-delimiters=,│`|:"'()[]{}<>
|
||||||
# selection-target=primary
|
# selection-target=primary
|
||||||
# workers=<number of logical CPUs>
|
# workers=<number of logical CPUs>
|
||||||
|
# utempter=/usr/lib/utempter/utempter
|
||||||
|
|
||||||
[environment]
|
[environment]
|
||||||
# name=value
|
# name=value
|
||||||
|
|
@ -68,27 +70,27 @@
|
||||||
|
|
||||||
[colors]
|
[colors]
|
||||||
# alpha=1.0
|
# alpha=1.0
|
||||||
# foreground=dcdccc
|
# background=002b36
|
||||||
# background=111111
|
# foreground=839496
|
||||||
|
|
||||||
## Normal/regular colors (color palette 0-7)
|
## Normal/regular colors (color palette 0-7)
|
||||||
# regular0=222222 # black
|
# regular0=073642 # black
|
||||||
# regular1=cc9393 # red
|
# regular1=dc322f # red
|
||||||
# regular2=7f9f7f # green
|
# regular2=859900 # green
|
||||||
# regular3=d0bf8f # yellow
|
# regular3=b58900 # yellow
|
||||||
# regular4=6ca0a3 # blue
|
# regular4=268bd2 # blue
|
||||||
# regular5=dc8cc3 # magenta
|
# regular5=d33682 # magenta
|
||||||
# regular6=93e0e3 # cyan
|
# regular6=2aa198 # cyan
|
||||||
# regular7=dcdccc # white
|
# regular7=eee8d5 # white
|
||||||
|
|
||||||
## Bright colors (color palette 8-15)
|
## Bright colors (color palette 8-15)
|
||||||
# bright0=666666 # bright black
|
# bright0=08404f # bright black
|
||||||
# bright1=dca3a3 # bright red
|
# bright1=e35f5c # bright red
|
||||||
# bright2=bfebbf # bright green
|
# bright2=9fb700 # bright green
|
||||||
# bright3=f0dfaf # bright yellow
|
# bright3=d9a400 # bright yellow
|
||||||
# bright4=8cd0d3 # bright blue
|
# bright4=4ba1de # bright blue
|
||||||
# bright5=fcace3 # bright magenta
|
# bright5=dc619d # bright magenta
|
||||||
# bright6=b3ffff # bright cyan
|
# bright6=32c1b6 # bright cyan
|
||||||
# bright7=ffffff # bright white
|
# bright7=ffffff # bright white
|
||||||
|
|
||||||
## dimmed colors (see foot.ini(5) man page)
|
## dimmed colors (see foot.ini(5) man page)
|
||||||
|
|
@ -104,9 +106,11 @@
|
||||||
## Misc colors
|
## Misc colors
|
||||||
# selection-foreground=<inverse foreground/background>
|
# selection-foreground=<inverse foreground/background>
|
||||||
# selection-background=<inverse foreground/background>
|
# selection-background=<inverse foreground/background>
|
||||||
# jump-labels=<regular0> <regular3>
|
# jump-labels=<regular0> <regular3> # black-on-yellow
|
||||||
|
# scrollback-indicator=<regular0> <bright4> # black-on-bright-blue
|
||||||
|
# search-box-no-match=<regular0> <regular1> # black-on-red
|
||||||
|
# search-box-match=<regular0> <regular3> # black-on-yellow
|
||||||
# urls=<regular3>
|
# urls=<regular3>
|
||||||
# scrollback-indicator=<regular0> <bright4>
|
|
||||||
|
|
||||||
[csd]
|
[csd]
|
||||||
# preferred=server
|
# preferred=server
|
||||||
|
|
@ -148,6 +152,7 @@
|
||||||
# show-urls-persistent=none
|
# show-urls-persistent=none
|
||||||
# prompt-prev=Control+Shift+z
|
# prompt-prev=Control+Shift+z
|
||||||
# prompt-next=Control+Shift+x
|
# prompt-next=Control+Shift+x
|
||||||
|
# unicode-input=none
|
||||||
# noop=none
|
# noop=none
|
||||||
|
|
||||||
[search-bindings]
|
[search-bindings]
|
||||||
|
|
@ -170,6 +175,7 @@
|
||||||
# extend-to-end-line=Control+l
|
# extend-to-end-line=Control+l
|
||||||
# clipboard-paste=Control+v Control+Shift+v Control+y XF86Paste
|
# clipboard-paste=Control+v Control+Shift+v Control+y XF86Paste
|
||||||
# primary-paste=Shift+Insert
|
# primary-paste=Shift+Insert
|
||||||
|
# unicode-input=none
|
||||||
|
|
||||||
[url-bindings]
|
[url-bindings]
|
||||||
# cancel=Control+g Control+c Control+d Escape
|
# cancel=Control+g Control+c Control+d Escape
|
||||||
|
|
|
||||||
63
grid.c
63
grid.c
|
|
@ -318,7 +318,7 @@ grid_row_alloc(int cols, bool initialize)
|
||||||
{
|
{
|
||||||
struct row *row = xmalloc(sizeof(*row));
|
struct row *row = xmalloc(sizeof(*row));
|
||||||
row->dirty = false;
|
row->dirty = false;
|
||||||
row->linebreak = true;
|
row->linebreak = false;
|
||||||
row->extra = NULL;
|
row->extra = NULL;
|
||||||
row->prompt_marker = false;
|
row->prompt_marker = false;
|
||||||
|
|
||||||
|
|
@ -538,7 +538,7 @@ _line_wrap(struct grid *old_grid, struct row **new_grid, struct row *row,
|
||||||
} else {
|
} else {
|
||||||
/* Scrollback is full, need to re-use a row */
|
/* Scrollback is full, need to re-use a row */
|
||||||
grid_row_reset_extra(new_row);
|
grid_row_reset_extra(new_row);
|
||||||
new_row->linebreak = true;
|
new_row->linebreak = false;
|
||||||
new_row->prompt_marker = false;
|
new_row->prompt_marker = false;
|
||||||
|
|
||||||
tll_foreach(old_grid->sixel_images, it) {
|
tll_foreach(old_grid->sixel_images, it) {
|
||||||
|
|
@ -740,7 +740,7 @@ grid_resize_and_reflow(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!old_row->linebreak /*&& col_count > 0*/) {
|
if (!old_row->linebreak && col_count > 0) {
|
||||||
/* Don’t truncate logical lines */
|
/* Don’t truncate logical lines */
|
||||||
col_count = old_cols;
|
col_count = old_cols;
|
||||||
}
|
}
|
||||||
|
|
@ -878,14 +878,6 @@ grid_resize_and_reflow(
|
||||||
&new_row->cells[new_col_idx], &old_row->cells[from],
|
&new_row->cells[new_col_idx], &old_row->cells[from],
|
||||||
amount * sizeof(struct cell));
|
amount * sizeof(struct cell));
|
||||||
|
|
||||||
/*
|
|
||||||
* We’ve “printed” to this line - reset linebreak.
|
|
||||||
*
|
|
||||||
* If the old line ends with a hard linebreak, we’ll
|
|
||||||
* set linebreak=true on the last new row we print to.
|
|
||||||
*/
|
|
||||||
new_row->linebreak = false;
|
|
||||||
|
|
||||||
count -= amount;
|
count -= amount;
|
||||||
from += amount;
|
from += amount;
|
||||||
new_col_idx += amount;
|
new_col_idx += amount;
|
||||||
|
|
@ -943,13 +935,29 @@ grid_resize_and_reflow(
|
||||||
start += cols;
|
start += cols;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (old_row->linebreak) {
|
||||||
if (old_row->linebreak && col_count > 0) {
|
|
||||||
/* Erase the remaining cells */
|
/* Erase the remaining cells */
|
||||||
memset(&new_row->cells[new_col_idx], 0,
|
memset(&new_row->cells[new_col_idx], 0,
|
||||||
(new_cols - new_col_idx) * sizeof(new_row->cells[0]));
|
(new_cols - new_col_idx) * sizeof(new_row->cells[0]));
|
||||||
new_row->linebreak = true;
|
new_row->linebreak = true;
|
||||||
|
|
||||||
|
if (r + 1 < old_rows)
|
||||||
line_wrap();
|
line_wrap();
|
||||||
|
else if (new_row->extra != NULL &&
|
||||||
|
new_row->extra->uri_ranges.count > 0)
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
* line_wrap() "closes" still-open URIs. Since this is
|
||||||
|
* the *last* row, and since we’re line-breaking due
|
||||||
|
* to a hard line-break (rather than running out of
|
||||||
|
* cells in the "new_row"), there shouldn’t be an open
|
||||||
|
* URI (it would have been closed when we reached the
|
||||||
|
* end of the URI while reflowing the last "old"
|
||||||
|
* row).
|
||||||
|
*/
|
||||||
|
uint32_t last_idx = new_row->extra->uri_ranges.count - 1;
|
||||||
|
xassert(new_row->extra->uri_ranges.v[last_idx].end >= 0);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
grid_row_free(old_grid[old_row_idx]);
|
grid_row_free(old_grid[old_row_idx]);
|
||||||
|
|
@ -992,6 +1000,7 @@ grid_resize_and_reflow(
|
||||||
|
|
||||||
/* Set offset such that the last reflowed row is at the bottom */
|
/* Set offset such that the last reflowed row is at the bottom */
|
||||||
grid->offset = new_row_idx - new_screen_rows + 1;
|
grid->offset = new_row_idx - new_screen_rows + 1;
|
||||||
|
|
||||||
while (grid->offset < 0)
|
while (grid->offset < 0)
|
||||||
grid->offset += new_rows;
|
grid->offset += new_rows;
|
||||||
while (new_grid[grid->offset] == NULL)
|
while (new_grid[grid->offset] == NULL)
|
||||||
|
|
@ -1004,23 +1013,6 @@ grid_resize_and_reflow(
|
||||||
new_grid[idx] = grid_row_alloc(new_cols, true);
|
new_grid[idx] = grid_row_alloc(new_cols, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
grid->view = view_follows ? grid->offset : viewport.row;
|
|
||||||
|
|
||||||
/* If enlarging the window, the old viewport may be too far down,
|
|
||||||
* with unallocated rows. Make sure this cannot happen */
|
|
||||||
while (true) {
|
|
||||||
int idx = (grid->view + new_screen_rows - 1) & (new_rows - 1);
|
|
||||||
if (new_grid[idx] != NULL)
|
|
||||||
break;
|
|
||||||
grid->view--;
|
|
||||||
if (grid->view < 0)
|
|
||||||
grid->view += new_rows;
|
|
||||||
}
|
|
||||||
for (size_t r = 0; r < new_screen_rows; r++) {
|
|
||||||
int UNUSED idx = (grid->view + r) & (new_rows - 1);
|
|
||||||
xassert(new_grid[idx] != NULL);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Free old grid (rows already free:d) */
|
/* Free old grid (rows already free:d) */
|
||||||
free(grid->rows);
|
free(grid->rows);
|
||||||
|
|
||||||
|
|
@ -1028,6 +1020,17 @@ grid_resize_and_reflow(
|
||||||
grid->num_rows = new_rows;
|
grid->num_rows = new_rows;
|
||||||
grid->num_cols = new_cols;
|
grid->num_cols = new_cols;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Set new viewport, making sure it’s not too far down.
|
||||||
|
*
|
||||||
|
* This is done by using scrollback-start relative cooardinates,
|
||||||
|
* and bounding the new viewport to (grid_rows - screen_rows).
|
||||||
|
*/
|
||||||
|
int sb_view = grid_row_abs_to_sb(
|
||||||
|
grid, new_screen_rows, view_follows ? grid->offset : viewport.row);
|
||||||
|
grid->view = grid_row_sb_to_abs(
|
||||||
|
grid, new_screen_rows, min(sb_view, new_rows - new_screen_rows));
|
||||||
|
|
||||||
/* Convert absolute coordinates to screen relative */
|
/* Convert absolute coordinates to screen relative */
|
||||||
cursor.row -= grid->offset;
|
cursor.row -= grid->offset;
|
||||||
while (cursor.row < 0)
|
while (cursor.row < 0)
|
||||||
|
|
|
||||||
42
input.c
42
input.c
|
|
@ -36,6 +36,7 @@
|
||||||
#include "spawn.h"
|
#include "spawn.h"
|
||||||
#include "terminal.h"
|
#include "terminal.h"
|
||||||
#include "tokenize.h"
|
#include "tokenize.h"
|
||||||
|
#include "unicode-mode.h"
|
||||||
#include "url-mode.h"
|
#include "url-mode.h"
|
||||||
#include "util.h"
|
#include "util.h"
|
||||||
#include "vt.h"
|
#include "vt.h"
|
||||||
|
|
@ -282,7 +283,7 @@ execute_binding(struct seat *seat, struct terminal *term,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!spawn(term->reaper, NULL, binding->aux->pipe.args,
|
if (!spawn(term->reaper, term->cwd, binding->aux->pipe.args,
|
||||||
pipe_fd[0], stdout_fd, stderr_fd, NULL))
|
pipe_fd[0], stdout_fd, stderr_fd, NULL))
|
||||||
goto pipe_err;
|
goto pipe_err;
|
||||||
|
|
||||||
|
|
@ -416,6 +417,10 @@ execute_binding(struct seat *seat, struct terminal *term,
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
case BIND_ACTION_UNICODE_INPUT:
|
||||||
|
unicode_mode_activate(seat);
|
||||||
|
return true;
|
||||||
|
|
||||||
case BIND_ACTION_SELECT_BEGIN:
|
case BIND_ACTION_SELECT_BEGIN:
|
||||||
selection_start(
|
selection_start(
|
||||||
term, seat->mouse.col, seat->mouse.row, SELECTION_CHAR_WISE, false);
|
term, seat->mouse.col, seat->mouse.row, SELECTION_CHAR_WISE, false);
|
||||||
|
|
@ -591,9 +596,6 @@ keyboard_enter(void *data, struct wl_keyboard *wl_keyboard, uint32_t serial,
|
||||||
LOG_DBG("%s: keyboard_enter: keyboard=%p, serial=%u, surface=%p",
|
LOG_DBG("%s: keyboard_enter: keyboard=%p, serial=%u, surface=%p",
|
||||||
seat->name, (void *)wl_keyboard, serial, (void *)surface);
|
seat->name, (void *)wl_keyboard, serial, (void *)surface);
|
||||||
|
|
||||||
if (seat->kbd.xkb == NULL)
|
|
||||||
return;
|
|
||||||
|
|
||||||
term_kbd_focus_in(term);
|
term_kbd_focus_in(term);
|
||||||
seat->kbd_focus = term;
|
seat->kbd_focus = term;
|
||||||
seat->kbd.serial = serial;
|
seat->kbd.serial = serial;
|
||||||
|
|
@ -653,9 +655,6 @@ keyboard_leave(void *data, struct wl_keyboard *wl_keyboard, uint32_t serial,
|
||||||
LOG_DBG("keyboard_leave: keyboard=%p, serial=%u, surface=%p",
|
LOG_DBG("keyboard_leave: keyboard=%p, serial=%u, surface=%p",
|
||||||
(void *)wl_keyboard, serial, (void *)surface);
|
(void *)wl_keyboard, serial, (void *)surface);
|
||||||
|
|
||||||
if (seat->kbd.xkb == NULL)
|
|
||||||
return;
|
|
||||||
|
|
||||||
xassert(
|
xassert(
|
||||||
seat->kbd_focus == NULL ||
|
seat->kbd_focus == NULL ||
|
||||||
surface == NULL || /* Seen on Sway 1.2 */
|
surface == NULL || /* Seen on Sway 1.2 */
|
||||||
|
|
@ -1245,7 +1244,7 @@ emit_escapes:
|
||||||
? ctx->level0_syms.syms[0]
|
? ctx->level0_syms.syms[0]
|
||||||
: sym;
|
: sym;
|
||||||
|
|
||||||
if (composed && is_text)
|
if (composed)
|
||||||
key = utf32;
|
key = utf32;
|
||||||
else {
|
else {
|
||||||
key = xkb_keysym_to_utf32(sym_to_use);
|
key = xkb_keysym_to_utf32(sym_to_use);
|
||||||
|
|
@ -1407,11 +1406,16 @@ key_press_release(struct seat *seat, struct terminal *term, uint32_t serial,
|
||||||
seat->kbd.xkb_keymap, key, layout_idx, 0, &raw_syms);
|
seat->kbd.xkb_keymap, key, layout_idx, 0, &raw_syms);
|
||||||
|
|
||||||
const struct key_binding_set *bindings = key_binding_for(
|
const struct key_binding_set *bindings = key_binding_for(
|
||||||
seat->wayl->key_binding_manager, term, seat);
|
seat->wayl->key_binding_manager, term->conf, seat);
|
||||||
xassert(bindings != NULL);
|
xassert(bindings != NULL);
|
||||||
|
|
||||||
if (pressed) {
|
if (pressed) {
|
||||||
if (term->is_searching) {
|
if (seat->unicode_mode.active) {
|
||||||
|
unicode_mode_input(seat, term, sym);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
else if (term->is_searching) {
|
||||||
if (should_repeat)
|
if (should_repeat)
|
||||||
start_repeater(seat, key);
|
start_repeater(seat, key);
|
||||||
|
|
||||||
|
|
@ -1705,11 +1709,9 @@ wl_pointer_enter(void *data, struct wl_pointer *wl_pointer,
|
||||||
uint32_t serial, struct wl_surface *surface,
|
uint32_t serial, struct wl_surface *surface,
|
||||||
wl_fixed_t surface_x, wl_fixed_t surface_y)
|
wl_fixed_t surface_x, wl_fixed_t surface_y)
|
||||||
{
|
{
|
||||||
xassert(surface != NULL);
|
if (unlikely(surface == NULL)) {
|
||||||
xassert(serial != 0);
|
|
||||||
|
|
||||||
if (surface == NULL) {
|
|
||||||
/* Seen on mutter-3.38 */
|
/* Seen on mutter-3.38 */
|
||||||
|
LOG_WARN("compositor sent pointer_enter event with a NULL surface");
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1862,6 +1864,16 @@ wl_pointer_motion(void *data, struct wl_pointer *wl_pointer,
|
||||||
struct seat *seat = data;
|
struct seat *seat = data;
|
||||||
struct wayland *wayl = seat->wayl;
|
struct wayland *wayl = seat->wayl;
|
||||||
struct terminal *term = seat->mouse_focus;
|
struct terminal *term = seat->mouse_focus;
|
||||||
|
|
||||||
|
if (unlikely(term == NULL)) {
|
||||||
|
/* Typically happens when the compositor sent a pointer enter
|
||||||
|
* event with a NULL surface - see wl_pointer_enter().
|
||||||
|
*
|
||||||
|
* In this case, we never set seat->mouse_focus (since we
|
||||||
|
* can’t map the enter event to a specific window). */
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
struct wl_window *win = term->window;
|
struct wl_window *win = term->window;
|
||||||
|
|
||||||
LOG_DBG("pointer_motion: pointer=%p, x=%d, y=%d", (void *)wl_pointer,
|
LOG_DBG("pointer_motion: pointer=%p, x=%d, y=%d", (void *)wl_pointer,
|
||||||
|
|
@ -2323,7 +2335,7 @@ wl_pointer_button(void *data, struct wl_pointer *wl_pointer,
|
||||||
/* Seat has keyboard - use mouse bindings *with* modifiers */
|
/* Seat has keyboard - use mouse bindings *with* modifiers */
|
||||||
|
|
||||||
const struct key_binding_set *bindings = key_binding_for(
|
const struct key_binding_set *bindings = key_binding_for(
|
||||||
wayl->key_binding_manager, term, seat);
|
wayl->key_binding_manager, term->conf, seat);
|
||||||
xassert(bindings != NULL);
|
xassert(bindings != NULL);
|
||||||
|
|
||||||
xkb_mod_mask_t mods;
|
xkb_mod_mask_t mods;
|
||||||
|
|
|
||||||
|
|
@ -80,17 +80,14 @@ key_binding_new_for_seat(struct key_binding_manager *mgr,
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
key_binding_new_for_term(struct key_binding_manager *mgr,
|
key_binding_new_for_conf(struct key_binding_manager *mgr,
|
||||||
const struct terminal *term)
|
const struct wayland *wayl, const struct config *conf)
|
||||||
{
|
{
|
||||||
const struct config *conf = term->conf;
|
|
||||||
const struct wayland *wayl = term->wl;
|
|
||||||
|
|
||||||
tll_foreach(wayl->seats, it) {
|
tll_foreach(wayl->seats, it) {
|
||||||
struct seat *seat = &it->item;
|
struct seat *seat = &it->item;
|
||||||
|
|
||||||
struct key_set *existing =
|
struct key_set *existing =
|
||||||
(struct key_set *)key_binding_for(mgr, term, seat);
|
(struct key_set *)key_binding_for(mgr, conf, seat);
|
||||||
|
|
||||||
if (existing != NULL) {
|
if (existing != NULL) {
|
||||||
existing->conf_ref_count++;
|
existing->conf_ref_count++;
|
||||||
|
|
@ -116,21 +113,19 @@ key_binding_new_for_term(struct key_binding_manager *mgr,
|
||||||
/* Chances are high this set will be requested next */
|
/* Chances are high this set will be requested next */
|
||||||
mgr->last_used_set = &tll_back(mgr->binding_sets);
|
mgr->last_used_set = &tll_back(mgr->binding_sets);
|
||||||
|
|
||||||
LOG_DBG("new (term): set=%p, seat=%p, conf=%p, ref-count=1",
|
LOG_DBG("new (conf): set=%p, seat=%p, conf=%p, ref-count=1",
|
||||||
(void *)&tll_back(mgr->binding_sets),
|
(void *)&tll_back(mgr->binding_sets),
|
||||||
(void *)set.seat, (void *)set.conf);
|
(void *)set.seat, (void *)set.conf);
|
||||||
}
|
}
|
||||||
|
|
||||||
LOG_DBG("new (term): total number of sets: %zu",
|
LOG_DBG("new (conf): total number of sets: %zu",
|
||||||
tll_length(mgr->binding_sets));
|
tll_length(mgr->binding_sets));
|
||||||
}
|
}
|
||||||
|
|
||||||
struct key_binding_set * NOINLINE
|
struct key_binding_set * NOINLINE
|
||||||
key_binding_for(struct key_binding_manager *mgr, const struct terminal *term,
|
key_binding_for(struct key_binding_manager *mgr, const struct config *conf,
|
||||||
const struct seat *seat)
|
const struct seat *seat)
|
||||||
{
|
{
|
||||||
const struct config *conf = term->conf;
|
|
||||||
|
|
||||||
struct key_set *last_used = mgr->last_used_set;
|
struct key_set *last_used = mgr->last_used_set;
|
||||||
if (last_used != NULL &&
|
if (last_used != NULL &&
|
||||||
last_used->conf == conf &&
|
last_used->conf == conf &&
|
||||||
|
|
@ -192,11 +187,8 @@ key_binding_remove_seat(struct key_binding_manager *mgr,
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
key_binding_unref_term(struct key_binding_manager *mgr,
|
key_binding_unref(struct key_binding_manager *mgr, const struct config *conf)
|
||||||
const struct terminal *term)
|
|
||||||
{
|
{
|
||||||
const struct config *conf = term->conf;
|
|
||||||
|
|
||||||
tll_foreach(mgr->binding_sets, it) {
|
tll_foreach(mgr->binding_sets, it) {
|
||||||
struct key_set *set = &it->item;
|
struct key_set *set = &it->item;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -38,6 +38,7 @@ enum bind_action_normal {
|
||||||
BIND_ACTION_TEXT_BINDING,
|
BIND_ACTION_TEXT_BINDING,
|
||||||
BIND_ACTION_PROMPT_PREV,
|
BIND_ACTION_PROMPT_PREV,
|
||||||
BIND_ACTION_PROMPT_NEXT,
|
BIND_ACTION_PROMPT_NEXT,
|
||||||
|
BIND_ACTION_UNICODE_INPUT,
|
||||||
|
|
||||||
/* Mouse specific actions - i.e. they require a mouse coordinate */
|
/* Mouse specific actions - i.e. they require a mouse coordinate */
|
||||||
BIND_ACTION_SELECT_BEGIN,
|
BIND_ACTION_SELECT_BEGIN,
|
||||||
|
|
@ -48,7 +49,7 @@ enum bind_action_normal {
|
||||||
BIND_ACTION_SELECT_WORD_WS,
|
BIND_ACTION_SELECT_WORD_WS,
|
||||||
BIND_ACTION_SELECT_ROW,
|
BIND_ACTION_SELECT_ROW,
|
||||||
|
|
||||||
BIND_ACTION_KEY_COUNT = BIND_ACTION_PROMPT_NEXT + 1,
|
BIND_ACTION_KEY_COUNT = BIND_ACTION_UNICODE_INPUT + 1,
|
||||||
BIND_ACTION_COUNT = BIND_ACTION_SELECT_ROW + 1,
|
BIND_ACTION_COUNT = BIND_ACTION_SELECT_ROW + 1,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -73,6 +74,7 @@ enum bind_action_search {
|
||||||
BIND_ACTION_SEARCH_EXTEND_LINE,
|
BIND_ACTION_SEARCH_EXTEND_LINE,
|
||||||
BIND_ACTION_SEARCH_CLIPBOARD_PASTE,
|
BIND_ACTION_SEARCH_CLIPBOARD_PASTE,
|
||||||
BIND_ACTION_SEARCH_PRIMARY_PASTE,
|
BIND_ACTION_SEARCH_PRIMARY_PASTE,
|
||||||
|
BIND_ACTION_SEARCH_UNICODE_INPUT,
|
||||||
BIND_ACTION_SEARCH_COUNT,
|
BIND_ACTION_SEARCH_COUNT,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -109,6 +111,7 @@ typedef tll(struct key_binding) key_binding_list_t;
|
||||||
|
|
||||||
struct terminal;
|
struct terminal;
|
||||||
struct seat;
|
struct seat;
|
||||||
|
struct wayland;
|
||||||
|
|
||||||
struct key_binding_set {
|
struct key_binding_set {
|
||||||
key_binding_list_t key;
|
key_binding_list_t key;
|
||||||
|
|
@ -126,20 +129,21 @@ void key_binding_manager_destroy(struct key_binding_manager *mgr);
|
||||||
void key_binding_new_for_seat(
|
void key_binding_new_for_seat(
|
||||||
struct key_binding_manager *mgr, const struct seat *seat);
|
struct key_binding_manager *mgr, const struct seat *seat);
|
||||||
|
|
||||||
void key_binding_new_for_term(
|
void key_binding_new_for_conf(
|
||||||
struct key_binding_manager *mgr, const struct terminal *term);
|
struct key_binding_manager *mgr, const struct wayland *wayl,
|
||||||
|
const struct config *conf);
|
||||||
|
|
||||||
/* Returns the set of key bindings associated with this seat/term pair */
|
/* Returns the set of key bindings associated with this seat/conf pair */
|
||||||
struct key_binding_set *key_binding_for(
|
struct key_binding_set *key_binding_for(
|
||||||
struct key_binding_manager *mgr, const struct terminal *term,
|
struct key_binding_manager *mgr, const struct config *conf,
|
||||||
const struct seat *seat);
|
const struct seat *seat);
|
||||||
|
|
||||||
/* Remove all key bindings tied to the specified seat */
|
/* Remove all key bindings tied to the specified seat */
|
||||||
void key_binding_remove_seat(
|
void key_binding_remove_seat(
|
||||||
struct key_binding_manager *mgr, const struct seat *seat);
|
struct key_binding_manager *mgr, const struct seat *seat);
|
||||||
|
|
||||||
void key_binding_unref_term(
|
void key_binding_unref(
|
||||||
struct key_binding_manager *mgr, const struct terminal *term);
|
struct key_binding_manager *mgr, const struct config *conf);
|
||||||
|
|
||||||
void key_binding_load_keymap(
|
void key_binding_load_keymap(
|
||||||
struct key_binding_manager *mgr, const struct seat *seat);
|
struct key_binding_manager *mgr, const struct seat *seat);
|
||||||
|
|
|
||||||
2
keymap.h
2
keymap.h
|
|
@ -24,7 +24,7 @@ struct key_data {
|
||||||
|
|
||||||
static const struct key_data key_escape[] = {
|
static const struct key_data key_escape[] = {
|
||||||
{MOD_SHIFT, CURSOR_KEYS_DONTCARE, KEYPAD_DONTCARE, "\033[27;2;27~"},
|
{MOD_SHIFT, CURSOR_KEYS_DONTCARE, KEYPAD_DONTCARE, "\033[27;2;27~"},
|
||||||
{MOD_ALT, CURSOR_KEYS_DONTCARE, KEYPAD_DONTCARE, "\033[27;3;27~"},
|
{MOD_ALT, CURSOR_KEYS_DONTCARE, KEYPAD_DONTCARE, "\033\033"},
|
||||||
{MOD_SHIFT | MOD_ALT, CURSOR_KEYS_DONTCARE, KEYPAD_DONTCARE, "\033[27;4;27~"},
|
{MOD_SHIFT | MOD_ALT, CURSOR_KEYS_DONTCARE, KEYPAD_DONTCARE, "\033[27;4;27~"},
|
||||||
{MOD_CTRL, CURSOR_KEYS_DONTCARE, KEYPAD_DONTCARE, "\033[27;5;27~"},
|
{MOD_CTRL, CURSOR_KEYS_DONTCARE, KEYPAD_DONTCARE, "\033[27;5;27~"},
|
||||||
{MOD_SHIFT | MOD_CTRL, CURSOR_KEYS_DONTCARE, KEYPAD_DONTCARE, "\033[27;6;27~"},
|
{MOD_SHIFT | MOD_CTRL, CURSOR_KEYS_DONTCARE, KEYPAD_DONTCARE, "\033[27;6;27~"},
|
||||||
|
|
|
||||||
22
main.c
22
main.c
|
|
@ -594,6 +594,28 @@ main(int argc, char *const *argv)
|
||||||
cwd = _cwd;
|
cwd = _cwd;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const char *pwd = getenv("PWD");
|
||||||
|
if (pwd != NULL) {
|
||||||
|
char *resolved_path_cwd = realpath(cwd, NULL);
|
||||||
|
char *resolved_path_pwd = realpath(pwd, NULL);
|
||||||
|
|
||||||
|
if (resolved_path_cwd != NULL &&
|
||||||
|
resolved_path_pwd != NULL &&
|
||||||
|
strcmp(resolved_path_cwd, resolved_path_pwd) == 0)
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
* The resolved path of $PWD matches the resolved path of
|
||||||
|
* the *actual* working directory - use $PWD.
|
||||||
|
*
|
||||||
|
* This makes a difference when $PWD refers to a symlink.
|
||||||
|
*/
|
||||||
|
cwd = pwd;
|
||||||
|
}
|
||||||
|
|
||||||
|
free(resolved_path_cwd);
|
||||||
|
free(resolved_path_pwd);
|
||||||
|
}
|
||||||
|
|
||||||
shm_set_max_pool_size(conf.tweak.max_shm_pool_size);
|
shm_set_max_pool_size(conf.tweak.max_shm_pool_size);
|
||||||
|
|
||||||
if ((fdm = fdm_init()) == NULL)
|
if ((fdm = fdm_init()) == NULL)
|
||||||
|
|
|
||||||
29
meson.build
29
meson.build
|
|
@ -1,5 +1,5 @@
|
||||||
project('foot', 'c',
|
project('foot', 'c',
|
||||||
version: '1.12.1',
|
version: '1.13.1',
|
||||||
license: 'MIT',
|
license: 'MIT',
|
||||||
meson_version: '>=0.58.0',
|
meson_version: '>=0.58.0',
|
||||||
default_options: [
|
default_options: [
|
||||||
|
|
@ -16,9 +16,30 @@ if cc.has_function('memfd_create')
|
||||||
add_project_arguments('-DMEMFD_CREATE', language: 'c')
|
add_project_arguments('-DMEMFD_CREATE', language: 'c')
|
||||||
endif
|
endif
|
||||||
|
|
||||||
|
utempter_path = get_option('default-utempter-path')
|
||||||
|
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()
|
||||||
|
else
|
||||||
|
utempter_path = ''
|
||||||
|
endif
|
||||||
|
elif utempter_path == 'none'
|
||||||
|
utempter_path = ''
|
||||||
|
endif
|
||||||
|
|
||||||
add_project_arguments(
|
add_project_arguments(
|
||||||
['-D_GNU_SOURCE=200809L',
|
['-D_GNU_SOURCE=200809L',
|
||||||
'-DFOOT_DEFAULT_TERM="@0@"'.format(get_option('default-terminfo'))] +
|
'-DFOOT_DEFAULT_TERM="@0@"'.format(get_option('default-terminfo')),
|
||||||
|
'-DFOOT_DEFAULT_UTEMPTER_PATH="@0@"'.format(utempter_path)] +
|
||||||
(is_debug_build
|
(is_debug_build
|
||||||
? ['-D_DEBUG']
|
? ['-D_DEBUG']
|
||||||
: [cc.get_supported_arguments('-fno-asynchronous-unwind-tables')]) +
|
: [cc.get_supported_arguments('-fno-asynchronous-unwind-tables')]) +
|
||||||
|
|
@ -222,6 +243,7 @@ executable(
|
||||||
'slave.c', 'slave.h',
|
'slave.c', 'slave.h',
|
||||||
'spawn.c', 'spawn.h',
|
'spawn.c', 'spawn.h',
|
||||||
'tokenize.c', 'tokenize.h',
|
'tokenize.c', 'tokenize.h',
|
||||||
|
'unicode-mode.c', 'unicode-mode.h',
|
||||||
'url-mode.c', 'url-mode.h',
|
'url-mode.c', 'url-mode.h',
|
||||||
'user-notification.c', 'user-notification.h',
|
'user-notification.c', 'user-notification.h',
|
||||||
'wayland.c', 'wayland.h', 'shm-formats.h',
|
'wayland.c', 'wayland.h', 'shm-formats.h',
|
||||||
|
|
@ -243,7 +265,7 @@ executable(
|
||||||
install: true)
|
install: true)
|
||||||
|
|
||||||
install_data(
|
install_data(
|
||||||
'foot.desktop', 'foot-server.desktop', 'footclient.desktop',
|
'org.codeberg.dnkl.foot.desktop', 'org.codeberg.dnkl.foot-server.desktop', 'org.codeberg.dnkl.footclient.desktop',
|
||||||
install_dir: join_paths(get_option('datadir'), 'applications'))
|
install_dir: join_paths(get_option('datadir'), 'applications'))
|
||||||
|
|
||||||
systemd = dependency('systemd', required: false)
|
systemd = dependency('systemd', required: false)
|
||||||
|
|
@ -320,6 +342,7 @@ summary(
|
||||||
'Themes': get_option('themes'),
|
'Themes': get_option('themes'),
|
||||||
'IME': get_option('ime'),
|
'IME': get_option('ime'),
|
||||||
'Grapheme clustering': utf8proc.found(),
|
'Grapheme clustering': utf8proc.found(),
|
||||||
|
'Utempter path': utempter_path,
|
||||||
'Build terminfo': tic.found(),
|
'Build terminfo': tic.found(),
|
||||||
'Terminfo install location': terminfo_install_location,
|
'Terminfo install location': terminfo_install_location,
|
||||||
'Default TERM': get_option('default-terminfo'),
|
'Default TERM': get_option('default-terminfo'),
|
||||||
|
|
|
||||||
|
|
@ -21,3 +21,6 @@ option('custom-terminfo-install-location', type: 'string', value: '',
|
||||||
|
|
||||||
option('systemd-units-dir', type: 'string', value: '',
|
option('systemd-units-dir', type: 'string', value: '',
|
||||||
description: 'Where to install the systemd service files (absolute path). Default: ${systemduserunitdir}')
|
description: 'Where to install the systemd service files (absolute path). Default: ${systemduserunitdir}')
|
||||||
|
|
||||||
|
option('default-utempter-path', type: 'string', value: '',
|
||||||
|
description: 'Default path to utempter helper binary. Default: auto-detect')
|
||||||
|
|
|
||||||
19
misc.c
19
misc.c
|
|
@ -13,15 +13,32 @@ isword(char32_t wc, bool spaces_only, const char32_t *delimiters)
|
||||||
return isc32graph(wc);
|
return isc32graph(wc);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
timespec_add(const struct timespec *a, const struct timespec *b,
|
||||||
|
struct timespec *res)
|
||||||
|
{
|
||||||
|
const long one_sec_in_ns = 1000000000;
|
||||||
|
|
||||||
|
res->tv_sec = a->tv_sec + b->tv_sec;
|
||||||
|
res->tv_nsec = a->tv_nsec + b->tv_nsec;
|
||||||
|
/* tv_nsec may be negative */
|
||||||
|
if (res->tv_nsec >= one_sec_in_ns) {
|
||||||
|
res->tv_sec++;
|
||||||
|
res->tv_nsec -= one_sec_in_ns;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
timespec_sub(const struct timespec *a, const struct timespec *b,
|
timespec_sub(const struct timespec *a, const struct timespec *b,
|
||||||
struct timespec *res)
|
struct timespec *res)
|
||||||
{
|
{
|
||||||
|
const long one_sec_in_ns = 1000000000;
|
||||||
|
|
||||||
res->tv_sec = a->tv_sec - b->tv_sec;
|
res->tv_sec = a->tv_sec - b->tv_sec;
|
||||||
res->tv_nsec = a->tv_nsec - b->tv_nsec;
|
res->tv_nsec = a->tv_nsec - b->tv_nsec;
|
||||||
/* tv_nsec may be negative */
|
/* tv_nsec may be negative */
|
||||||
if (res->tv_nsec < 0) {
|
if (res->tv_nsec < 0) {
|
||||||
res->tv_sec--;
|
res->tv_sec--;
|
||||||
res->tv_nsec += 1000 * 1000 * 1000;
|
res->tv_nsec += one_sec_in_ns;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
1
misc.h
1
misc.h
|
|
@ -6,4 +6,5 @@
|
||||||
|
|
||||||
bool isword(char32_t wc, bool spaces_only, const char32_t *delimiters);
|
bool isword(char32_t wc, bool spaces_only, const char32_t *delimiters);
|
||||||
|
|
||||||
|
void timespec_add(const struct timespec *a, const struct timespec *b, struct timespec *res);
|
||||||
void timespec_sub(const struct timespec *a, const struct timespec *b, struct timespec *res);
|
void timespec_sub(const struct timespec *a, const struct timespec *b, struct timespec *res);
|
||||||
|
|
|
||||||
45
org.codeberg.dnkl.foot.metainfo.xml
Normal file
45
org.codeberg.dnkl.foot.metainfo.xml
Normal file
|
|
@ -0,0 +1,45 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<component type="desktop-application">
|
||||||
|
<id>org.codeberg.dnkl.foot</id>
|
||||||
|
<metadata_license>CC0-1.0</metadata_license>
|
||||||
|
<project_license>MIT</project_license>
|
||||||
|
<developer_name>dnkl</developer_name>
|
||||||
|
<name>foot</name>
|
||||||
|
<summary>The fast, lightweight and minimalistic Wayland terminal emulator.</summary>
|
||||||
|
<description>
|
||||||
|
<ul>
|
||||||
|
<li>Fast</li>
|
||||||
|
<li>Lightweight, in dependencies, on-disk and in-memory</li>
|
||||||
|
<li>Wayland native</li>
|
||||||
|
<li>DE agnostic</li>
|
||||||
|
<li>Server/daemon mode</li>
|
||||||
|
<li>User configurable font fallback</li>
|
||||||
|
<li>On-the-fly font resize</li>
|
||||||
|
<li>On-the-fly DPI font size adjustment</li>
|
||||||
|
<li>Scrollback search</li>
|
||||||
|
<li>Keyboard driven URL detection</li>
|
||||||
|
<li>Color emoji support</li>
|
||||||
|
<li>IME (via text-input-v3)</li>
|
||||||
|
<li>Multi-seat</li>
|
||||||
|
<li>True Color (24bpp)</li>
|
||||||
|
<li>Synchronized Updates support</li>
|
||||||
|
<li>Sixel image support</li>
|
||||||
|
</ul>
|
||||||
|
</description>
|
||||||
|
<screenshots>
|
||||||
|
<screenshot type="default">
|
||||||
|
<caption>Foot with sixel graphics</caption>
|
||||||
|
<image>https://codeberg.org/dnkl/foot/media/branch/master/doc/sixel-wow.png</image>
|
||||||
|
</screenshot>
|
||||||
|
</screenshots>
|
||||||
|
<releases>
|
||||||
|
<release version="1.13.1" date="2022-08-31">
|
||||||
|
</release>
|
||||||
|
<release version="1.13.0" date="2022-08-07">
|
||||||
|
</release>
|
||||||
|
</releases>
|
||||||
|
<launchable type="desktop-id">org.codeberg.dnkl.foot.desktop</launchable>
|
||||||
|
<url type="homepage">https://codeberg.org/dnkl/foot</url>
|
||||||
|
<url type="bugtracker">https://codeberg.org/dnkl/foot/issues</url>
|
||||||
|
<content_rating type="oars-1.1"/>
|
||||||
|
</component>
|
||||||
|
|
@ -178,15 +178,16 @@ static bool kbd_initialized = false;
|
||||||
|
|
||||||
struct key_binding_set *
|
struct key_binding_set *
|
||||||
key_binding_for(
|
key_binding_for(
|
||||||
struct key_binding_manager *mgr, const struct terminal *term,
|
struct key_binding_manager *mgr, const struct config *conf,
|
||||||
const struct seat *seat)
|
const struct seat *seat)
|
||||||
{
|
{
|
||||||
return &kbd;
|
return &kbd;
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
key_binding_new_for_term(
|
key_binding_new_for_conf(
|
||||||
struct key_binding_manager *mgr, const struct terminal *term)
|
struct key_binding_manager *mgr, const struct wayland *wayl,
|
||||||
|
const struct config *conf)
|
||||||
{
|
{
|
||||||
if (!kbd_initialized) {
|
if (!kbd_initialized) {
|
||||||
kbd_initialized = true;
|
kbd_initialized = true;
|
||||||
|
|
@ -201,7 +202,7 @@ key_binding_new_for_term(
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
key_binding_unref_term(struct key_binding_manager *mgr, const struct terminal *term)
|
key_binding_unref(struct key_binding_manager *mgr, const struct config *conf)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
208
render.c
208
render.c
|
|
@ -372,7 +372,10 @@ draw_underline(const struct terminal *term, pixman_image_t *pix,
|
||||||
const struct fcft_font *font,
|
const struct fcft_font *font,
|
||||||
const pixman_color_t *color, int x, int y, int cols)
|
const pixman_color_t *color, int x, int y, int cols)
|
||||||
{
|
{
|
||||||
const int thickness = font->underline.thickness;
|
const int thickness = term->conf->underline_thickness.px >= 0
|
||||||
|
? term_pt_or_px_as_pixels(
|
||||||
|
term, &term->conf->underline_thickness)
|
||||||
|
: font->underline.thickness;
|
||||||
|
|
||||||
/* Make sure the line isn't positioned below the cell */
|
/* Make sure the line isn't positioned below the cell */
|
||||||
const int y_ofs = min(underline_offset(term, font),
|
const int y_ofs = min(underline_offset(term, font),
|
||||||
|
|
@ -699,7 +702,7 @@ render_cell(struct terminal *term, pixman_image_t *pix,
|
||||||
mtx_unlock(&term->render.workers.lock);
|
mtx_unlock(&term->render.workers.lock);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (has_cursor && term->cursor_style == CURSOR_BLOCK && term->kbd_focus)
|
if (unlikely(has_cursor && term->cursor_style == CURSOR_BLOCK && term->kbd_focus))
|
||||||
draw_cursor(term, cell, font, pix, &fg, &bg, x, y, cell_cols);
|
draw_cursor(term, cell, font, pix, &fg, &bg, x, y, cell_cols);
|
||||||
|
|
||||||
if (cell->wc == 0 || cell->wc >= CELL_SPACER || cell->wc == U'\t' ||
|
if (cell->wc == 0 || cell->wc >= CELL_SPACER || cell->wc == U'\t' ||
|
||||||
|
|
@ -1011,6 +1014,13 @@ grid_render_scroll(struct terminal *term, struct buffer *buf,
|
||||||
wl_surface_damage_buffer(
|
wl_surface_damage_buffer(
|
||||||
term->window->surface, term->margins.left, dst_y,
|
term->window->surface, term->margins.left, dst_y,
|
||||||
term->width - term->margins.left - term->margins.right, height);
|
term->width - term->margins.left - term->margins.right, height);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* TODO: remove this if re-enabling scroll damage when re-applying
|
||||||
|
* last frame’s damage (see reapply_old_damage()
|
||||||
|
*/
|
||||||
|
pixman_region32_union_rect(
|
||||||
|
&buf->dirty, &buf->dirty, 0, dst_y, buf->width, height);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
|
|
@ -1076,6 +1086,13 @@ grid_render_scroll_reverse(struct terminal *term, struct buffer *buf,
|
||||||
wl_surface_damage_buffer(
|
wl_surface_damage_buffer(
|
||||||
term->window->surface, term->margins.left, dst_y,
|
term->window->surface, term->margins.left, dst_y,
|
||||||
term->width - term->margins.left - term->margins.right, height);
|
term->width - term->margins.left - term->margins.right, height);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* TODO: remove this if re-enabling scroll damage when re-applying
|
||||||
|
* last frame’s damage (see reapply_old_damage()
|
||||||
|
*/
|
||||||
|
pixman_region32_union_rect(
|
||||||
|
&buf->dirty, &buf->dirty, 0, dst_y, buf->width, height);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
|
|
@ -1466,10 +1483,21 @@ static void
|
||||||
render_overlay(struct terminal *term)
|
render_overlay(struct terminal *term)
|
||||||
{
|
{
|
||||||
struct wl_surf_subsurf *overlay = &term->window->overlay;
|
struct wl_surf_subsurf *overlay = &term->window->overlay;
|
||||||
|
bool unicode_mode_active = false;
|
||||||
|
|
||||||
|
/* Check if unicode mode is active on at least one seat focusing
|
||||||
|
* this terminal instance */
|
||||||
|
tll_foreach(term->wl->seats, it) {
|
||||||
|
if (it->item.unicode_mode.active) {
|
||||||
|
unicode_mode_active = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const enum overlay_style style =
|
const enum overlay_style style =
|
||||||
term->is_searching ? OVERLAY_SEARCH :
|
term->is_searching ? OVERLAY_SEARCH :
|
||||||
term->flash.active ? OVERLAY_FLASH :
|
term->flash.active ? OVERLAY_FLASH :
|
||||||
|
unicode_mode_active ? OVERLAY_UNICODE_MODE :
|
||||||
OVERLAY_NONE;
|
OVERLAY_NONE;
|
||||||
|
|
||||||
if (likely(style == OVERLAY_NONE)) {
|
if (likely(style == OVERLAY_NONE)) {
|
||||||
|
|
@ -1488,9 +1516,21 @@ render_overlay(struct terminal *term)
|
||||||
|
|
||||||
pixman_image_set_clip_region32(buf->pix[0], NULL);
|
pixman_image_set_clip_region32(buf->pix[0], NULL);
|
||||||
|
|
||||||
pixman_color_t color = style == OVERLAY_SEARCH
|
pixman_color_t color;
|
||||||
? (pixman_color_t){0, 0, 0, 0x7fff}
|
|
||||||
: (pixman_color_t){.red=0x7fff, .green=0x7fff, .blue=0, .alpha=0x7fff};
|
switch (style) {
|
||||||
|
case OVERLAY_NONE:
|
||||||
|
break;
|
||||||
|
|
||||||
|
case OVERLAY_SEARCH:
|
||||||
|
case OVERLAY_UNICODE_MODE:
|
||||||
|
color = (pixman_color_t){0, 0, 0, 0x7fff};
|
||||||
|
break;
|
||||||
|
|
||||||
|
case OVERLAY_FLASH:
|
||||||
|
color = (pixman_color_t){.red=0x7fff, .green=0x7fff, .blue=0, .alpha=0x7fff};
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
/* Bounding rectangle of damaged areas - for wl_surface_damage_buffer() */
|
/* Bounding rectangle of damaged areas - for wl_surface_damage_buffer() */
|
||||||
pixman_box32_t damage_bounds;
|
pixman_box32_t damage_bounds;
|
||||||
|
|
@ -1517,17 +1557,18 @@ render_overlay(struct terminal *term)
|
||||||
* region that needs to be *cleared* in this frame.
|
* region that needs to be *cleared* in this frame.
|
||||||
*
|
*
|
||||||
* Finally, the union of the two “diff” regions above, gives
|
* Finally, the union of the two “diff” regions above, gives
|
||||||
* us the total region affecte by a change, in either way. We
|
* us the total region affected by a change, in either way. We
|
||||||
* use this as the bounding box for the
|
* use this as the bounding box for the
|
||||||
* wl_surface_damage_buffer() call.
|
* wl_surface_damage_buffer() call.
|
||||||
*/
|
*/
|
||||||
pixman_region32_t *see_through = &term->render.last_overlay_clip;
|
pixman_region32_t *see_through = &term->render.last_overlay_clip;
|
||||||
pixman_region32_t old_see_through;
|
pixman_region32_t old_see_through;
|
||||||
|
const bool buffer_reuse =
|
||||||
if (!(buf == term->render.last_overlay_buf &&
|
buf == term->render.last_overlay_buf &&
|
||||||
style == term->render.last_overlay_style &&
|
style == term->render.last_overlay_style &&
|
||||||
buf->age == 0))
|
buf->age == 0;
|
||||||
{
|
|
||||||
|
if (!buffer_reuse) {
|
||||||
/* Can’t re-use last frame’s damage - set to full window,
|
/* Can’t re-use last frame’s damage - set to full window,
|
||||||
* to ensure *everything* is updated */
|
* to ensure *everything* is updated */
|
||||||
pixman_region32_init_rect(
|
pixman_region32_init_rect(
|
||||||
|
|
@ -1540,8 +1581,8 @@ render_overlay(struct terminal *term)
|
||||||
|
|
||||||
pixman_region32_clear(see_through);
|
pixman_region32_clear(see_through);
|
||||||
|
|
||||||
|
/* Build region consisting of all current search matches */
|
||||||
struct search_match_iterator iter = search_matches_new_iter(term);
|
struct search_match_iterator iter = search_matches_new_iter(term);
|
||||||
|
|
||||||
for (struct range match = search_matches_next(&iter);
|
for (struct range match = search_matches_next(&iter);
|
||||||
match.start.row >= 0;
|
match.start.row >= 0;
|
||||||
match = search_matches_next(&iter))
|
match = search_matches_next(&iter))
|
||||||
|
|
@ -1569,20 +1610,28 @@ render_overlay(struct terminal *term)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Current see-through, minus old see-through - aka cells that
|
/* Areas that need to be cleared: cells that were dimmed in
|
||||||
* need to be cleared */
|
* the last frame but is now see-through */
|
||||||
pixman_region32_t new_see_through;
|
pixman_region32_t new_see_through;
|
||||||
pixman_region32_init(&new_see_through);
|
pixman_region32_init(&new_see_through);
|
||||||
|
|
||||||
|
if (buffer_reuse)
|
||||||
pixman_region32_subtract(&new_see_through, see_through, &old_see_through);
|
pixman_region32_subtract(&new_see_through, see_through, &old_see_through);
|
||||||
|
else {
|
||||||
|
/* Buffer content is unknown - explicitly clear *all*
|
||||||
|
* current see-through areas */
|
||||||
|
pixman_region32_copy(&new_see_through, see_through);
|
||||||
|
}
|
||||||
pixman_image_set_clip_region32(buf->pix[0], &new_see_through);
|
pixman_image_set_clip_region32(buf->pix[0], &new_see_through);
|
||||||
|
|
||||||
/* Old see-through, minus new see-through - aka cells that
|
/* Areas that need to be dimmed: cells that were cleared in
|
||||||
* needs to be dimmed */
|
* the last frame but is not anymore */
|
||||||
pixman_region32_t new_dimmed;
|
pixman_region32_t new_dimmed;
|
||||||
pixman_region32_init(&new_dimmed);
|
pixman_region32_init(&new_dimmed);
|
||||||
pixman_region32_subtract(&new_dimmed, &old_see_through, see_through);
|
pixman_region32_subtract(&new_dimmed, &old_see_through, see_through);
|
||||||
pixman_region32_fini(&old_see_through);
|
pixman_region32_fini(&old_see_through);
|
||||||
|
|
||||||
|
/* Total affected area */
|
||||||
pixman_region32_t damage;
|
pixman_region32_t damage;
|
||||||
pixman_region32_init(&damage);
|
pixman_region32_init(&damage);
|
||||||
pixman_region32_union(&damage, &new_see_through, &new_dimmed);
|
pixman_region32_union(&damage, &new_see_through, &new_dimmed);
|
||||||
|
|
@ -1605,7 +1654,7 @@ render_overlay(struct terminal *term)
|
||||||
else if (buf == term->render.last_overlay_buf &&
|
else if (buf == term->render.last_overlay_buf &&
|
||||||
style == term->render.last_overlay_style)
|
style == term->render.last_overlay_style)
|
||||||
{
|
{
|
||||||
xassert(style == OVERLAY_FLASH);
|
xassert(style == OVERLAY_FLASH || style == OVERLAY_UNICODE_MODE);
|
||||||
shm_did_not_use_buf(buf);
|
shm_did_not_use_buf(buf);
|
||||||
return;
|
return;
|
||||||
} else {
|
} else {
|
||||||
|
|
@ -1726,10 +1775,12 @@ get_csd_data(const struct terminal *term, enum csd_surface surf_idx)
|
||||||
const int button_close_width = term->width >= 1 * button_width
|
const int button_close_width = term->width >= 1 * button_width
|
||||||
? button_width : 0;
|
? button_width : 0;
|
||||||
|
|
||||||
const int button_maximize_width = term->width >= 2 * button_width
|
const int button_maximize_width =
|
||||||
|
term->width >= 2 * button_width && term->window->wm_capabilities.maximize
|
||||||
? button_width : 0;
|
? button_width : 0;
|
||||||
|
|
||||||
const int button_minimize_width = term->width >= 3 * button_width
|
const int button_minimize_width =
|
||||||
|
term->width >= 3 * button_width && term->window->wm_capabilities.minimize
|
||||||
? button_width : 0;
|
? button_width : 0;
|
||||||
|
|
||||||
switch (surf_idx) {
|
switch (surf_idx) {
|
||||||
|
|
@ -2510,22 +2561,27 @@ reapply_old_damage(struct terminal *term, struct buffer *new, struct buffer *old
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
/*
|
|
||||||
* TODO: remove this frame’s damage from the region we copy from
|
|
||||||
* the old frame.
|
|
||||||
*
|
|
||||||
* - this frame’s dirty region is only valid *after* we’ve applied
|
|
||||||
* its scroll damage.
|
|
||||||
* - last frame’s dirty region is only valid *before* we’ve
|
|
||||||
* applied this frame’s scroll damage.
|
|
||||||
*
|
|
||||||
* Can we transform one of the regions? It’s not trivial, since
|
|
||||||
* scroll damage isn’t just about counting lines; there may be
|
|
||||||
* multiple damage records, each with different scrolling regions.
|
|
||||||
*/
|
|
||||||
pixman_region32_t dirty;
|
pixman_region32_t dirty;
|
||||||
pixman_region32_init(&dirty);
|
pixman_region32_init(&dirty);
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Figure out current frame’s damage region
|
||||||
|
*
|
||||||
|
* If current frame doesn’t have any scroll damage, we can simply
|
||||||
|
* subtract this frame’s damage from the last frame’s damage. That
|
||||||
|
* way, we don’t have to copy areas from the old frame that’ll
|
||||||
|
* just get overwritten by current frame.
|
||||||
|
*
|
||||||
|
* Note that this is row based. A “half damaged” row is not
|
||||||
|
* excluded. I.e. the entire row will be copied from the old frame
|
||||||
|
* to the new, and then when actually rendering the new frame, the
|
||||||
|
* updated cells will overwrite parts of the copied row.
|
||||||
|
*
|
||||||
|
* Since we’re scanning the entire viewport anyway, we also track
|
||||||
|
* whether *all* cells are to be updated. In this case, just force
|
||||||
|
* a full re-rendering, and don’t copy anything from the old
|
||||||
|
* frame.
|
||||||
|
*/
|
||||||
bool full_repaint_needed = true;
|
bool full_repaint_needed = true;
|
||||||
|
|
||||||
for (int r = 0; r < term->rows; r++) {
|
for (int r = 0; r < term->rows; r++) {
|
||||||
|
|
@ -2555,35 +2611,31 @@ reapply_old_damage(struct terminal *term, struct buffer *new, struct buffer *old
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
for (size_t i = 0; i < old->scroll_damage_count; i++) {
|
/*
|
||||||
const struct damage *dmg = &old->scroll_damage[i];
|
* TODO: re-apply last frame’s scroll damage
|
||||||
|
*
|
||||||
switch (dmg->type) {
|
* We used to do this, but it turned out to be buggy. If we decide
|
||||||
case DAMAGE_SCROLL:
|
* to re-add it, this is where to do it. Note that we’d also have
|
||||||
if (term->grid->view == term->grid->offset)
|
* to remove the updates to buf->dirty from grid_render_scroll()
|
||||||
grid_render_scroll(term, new, dmg);
|
* and grid_render_scroll_reverse().
|
||||||
break;
|
*/
|
||||||
|
|
||||||
case DAMAGE_SCROLL_REVERSE:
|
|
||||||
if (term->grid->view == term->grid->offset)
|
|
||||||
grid_render_scroll_reverse(term, new, dmg);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case DAMAGE_SCROLL_IN_VIEW:
|
|
||||||
grid_render_scroll(term, new, dmg);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case DAMAGE_SCROLL_REVERSE_IN_VIEW:
|
|
||||||
grid_render_scroll_reverse(term, new, dmg);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (tll_length(term->grid->scroll_damage) == 0) {
|
if (tll_length(term->grid->scroll_damage) == 0) {
|
||||||
|
/*
|
||||||
|
* We can only subtract current frame’s damage from the old
|
||||||
|
* frame’s if we don’t have any scroll damage.
|
||||||
|
*
|
||||||
|
* If we do have scroll damage, the damage region we
|
||||||
|
* calculated above is not (yet) valid - we need to apply the
|
||||||
|
* current frame’s scroll damage *first*. This is done later,
|
||||||
|
* when rendering the frame.
|
||||||
|
*/
|
||||||
pixman_region32_subtract(&dirty, &old->dirty, &dirty);
|
pixman_region32_subtract(&dirty, &old->dirty, &dirty);
|
||||||
pixman_image_set_clip_region32(new->pix[0], &dirty);
|
pixman_image_set_clip_region32(new->pix[0], &dirty);
|
||||||
} else
|
} else {
|
||||||
|
/* Copy *all* of last frame’s damaged areas */
|
||||||
pixman_image_set_clip_region32(new->pix[0], &old->dirty);
|
pixman_image_set_clip_region32(new->pix[0], &old->dirty);
|
||||||
|
}
|
||||||
|
|
||||||
pixman_image_composite32(
|
pixman_image_composite32(
|
||||||
PIXMAN_OP_SRC, old->pix[0], NULL, new->pix[0],
|
PIXMAN_OP_SRC, old->pix[0], NULL, new->pix[0],
|
||||||
|
|
@ -2674,16 +2726,8 @@ grid_render(struct terminal *term)
|
||||||
shm_addref(buf);
|
shm_addref(buf);
|
||||||
buf->age = 0;
|
buf->age = 0;
|
||||||
|
|
||||||
free(term->render.last_buf->scroll_damage);
|
|
||||||
buf->scroll_damage_count = tll_length(term->grid->scroll_damage);
|
|
||||||
buf->scroll_damage = xmalloc(
|
|
||||||
buf->scroll_damage_count * sizeof(buf->scroll_damage[0]));
|
|
||||||
|
|
||||||
{
|
|
||||||
size_t i = 0;
|
|
||||||
tll_foreach(term->grid->scroll_damage, it) {
|
tll_foreach(term->grid->scroll_damage, it) {
|
||||||
buf->scroll_damage[i++] = it->item;
|
|
||||||
|
|
||||||
switch (it->item.type) {
|
switch (it->item.type) {
|
||||||
case DAMAGE_SCROLL:
|
case DAMAGE_SCROLL:
|
||||||
if (term->grid->view == term->grid->offset)
|
if (term->grid->view == term->grid->offset)
|
||||||
|
|
@ -2706,7 +2750,6 @@ grid_render(struct terminal *term)
|
||||||
|
|
||||||
tll_remove(term->grid->scroll_damage, it);
|
tll_remove(term->grid->scroll_damage, it);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Ensure selected cells have their 'selected' bit set. This is
|
* Ensure selected cells have their 'selected' bit set. This is
|
||||||
|
|
@ -2885,11 +2928,17 @@ grid_render(struct terminal *term)
|
||||||
struct timespec double_buffering_time;
|
struct timespec double_buffering_time;
|
||||||
timespec_sub(&stop_double_buffering, &start_double_buffering, &double_buffering_time);
|
timespec_sub(&stop_double_buffering, &start_double_buffering, &double_buffering_time);
|
||||||
|
|
||||||
|
struct timespec total_render_time;
|
||||||
|
timespec_add(&render_time, &double_buffering_time, &total_render_time);
|
||||||
|
|
||||||
switch (term->conf->tweak.render_timer) {
|
switch (term->conf->tweak.render_timer) {
|
||||||
case RENDER_TIMER_LOG:
|
case RENDER_TIMER_LOG:
|
||||||
case RENDER_TIMER_BOTH:
|
case RENDER_TIMER_BOTH:
|
||||||
LOG_INFO("frame rendered in %lds %ldns "
|
LOG_INFO(
|
||||||
"(%lds %ldns double buffering)",
|
"frame rendered in %lds %9ldns "
|
||||||
|
"(%lds %9ldns rendering, %lds %9ldns double buffering)",
|
||||||
|
(long)total_render_time.tv_sec,
|
||||||
|
total_render_time.tv_nsec,
|
||||||
(long)render_time.tv_sec,
|
(long)render_time.tv_sec,
|
||||||
render_time.tv_nsec,
|
render_time.tv_nsec,
|
||||||
(long)double_buffering_time.tv_sec,
|
(long)double_buffering_time.tv_sec,
|
||||||
|
|
@ -2904,7 +2953,7 @@ grid_render(struct terminal *term)
|
||||||
switch (term->conf->tweak.render_timer) {
|
switch (term->conf->tweak.render_timer) {
|
||||||
case RENDER_TIMER_OSD:
|
case RENDER_TIMER_OSD:
|
||||||
case RENDER_TIMER_BOTH:
|
case RENDER_TIMER_BOTH:
|
||||||
render_render_timer(term, render_time);
|
render_render_timer(term, total_render_time);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case RENDER_TIMER_LOG:
|
case RENDER_TIMER_LOG:
|
||||||
|
|
@ -3046,10 +3095,20 @@ render_search_box(struct terminal *term)
|
||||||
#define WINDOW_X(x) (margin + x)
|
#define WINDOW_X(x) (margin + x)
|
||||||
#define WINDOW_Y(y) (term->height - margin - height + y)
|
#define WINDOW_Y(y) (term->height - margin - height + y)
|
||||||
|
|
||||||
/* Background - yellow on empty/match, red on mismatch */
|
const bool is_match = term->search.match_len == text_len;
|
||||||
pixman_color_t color = color_hex_to_pixman(
|
const bool custom_colors = is_match
|
||||||
term->search.match_len == text_len
|
? term->conf->colors.use_custom.search_box_match
|
||||||
? term->colors.table[3] : term->colors.table[1]);
|
: term->conf->colors.use_custom.search_box_no_match;
|
||||||
|
|
||||||
|
/* Background - yellow on empty/match, red on mismatch (default) */
|
||||||
|
const pixman_color_t color = color_hex_to_pixman(
|
||||||
|
is_match
|
||||||
|
? (custom_colors
|
||||||
|
? term->conf->colors.search_box.match.bg
|
||||||
|
: term->colors.table[3])
|
||||||
|
: (custom_colors
|
||||||
|
? term->conf->colors.search_box.no_match.bg
|
||||||
|
: term->colors.table[1]));
|
||||||
|
|
||||||
pixman_image_fill_rectangles(
|
pixman_image_fill_rectangles(
|
||||||
PIXMAN_OP_SRC, buf->pix[0], &color,
|
PIXMAN_OP_SRC, buf->pix[0], &color,
|
||||||
|
|
@ -3065,7 +3124,12 @@ render_search_box(struct terminal *term)
|
||||||
const int x_ofs = term->font_x_ofs;
|
const int x_ofs = term->font_x_ofs;
|
||||||
int x = x_left;
|
int x = x_left;
|
||||||
int y = margin;
|
int y = margin;
|
||||||
pixman_color_t fg = color_hex_to_pixman(term->colors.table[0]);
|
pixman_color_t fg = color_hex_to_pixman(
|
||||||
|
custom_colors
|
||||||
|
? (is_match
|
||||||
|
? term->conf->colors.search_box.match.fg
|
||||||
|
: term->conf->colors.search_box.no_match.fg)
|
||||||
|
: term->colors.table[0]);
|
||||||
|
|
||||||
/* Move offset we start rendering at, to ensure the cursor is visible */
|
/* Move offset we start rendering at, to ensure the cursor is visible */
|
||||||
for (size_t i = 0, cell_idx = 0; i <= term->search.cursor; cell_idx += widths[i], i++) {
|
for (size_t i = 0, cell_idx = 0; i <= term->search.cursor; cell_idx += widths[i], i++) {
|
||||||
|
|
|
||||||
|
|
@ -166,6 +166,7 @@ def main():
|
||||||
|
|
||||||
entry.add_capability(IntCapability('Co', 256))
|
entry.add_capability(IntCapability('Co', 256))
|
||||||
entry.add_capability(StringCapability('TN', target_entry_name))
|
entry.add_capability(StringCapability('TN', target_entry_name))
|
||||||
|
entry.add_capability(StringCapability('name', target_entry_name))
|
||||||
entry.add_capability(IntCapability('RGB', 8)) # 8 bits per channel
|
entry.add_capability(IntCapability('RGB', 8)) # 8 bits per channel
|
||||||
|
|
||||||
terminfo_parts = []
|
terminfo_parts = []
|
||||||
|
|
|
||||||
8
search.c
8
search.c
|
|
@ -18,6 +18,7 @@
|
||||||
#include "render.h"
|
#include "render.h"
|
||||||
#include "selection.h"
|
#include "selection.h"
|
||||||
#include "shm.h"
|
#include "shm.h"
|
||||||
|
#include "unicode-mode.h"
|
||||||
#include "util.h"
|
#include "util.h"
|
||||||
#include "xmalloc.h"
|
#include "xmalloc.h"
|
||||||
|
|
||||||
|
|
@ -371,6 +372,9 @@ find_next(struct terminal *term, enum search_direction direction,
|
||||||
i += additional_chars;
|
i += additional_chars;
|
||||||
match_len += additional_chars;
|
match_len += additional_chars;
|
||||||
match_end_col++;
|
match_end_col++;
|
||||||
|
|
||||||
|
while (match_row->cells[match_end_col].wc > CELL_SPACER)
|
||||||
|
match_end_col++;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (match_len != term->search.len) {
|
if (match_len != term->search.len) {
|
||||||
|
|
@ -1000,6 +1004,10 @@ execute_binding(struct seat *seat, struct terminal *term,
|
||||||
*update_search_result = *redraw = true;
|
*update_search_result = *redraw = true;
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
|
case BIND_ACTION_SEARCH_UNICODE_INPUT:
|
||||||
|
unicode_mode_activate(seat);
|
||||||
|
return true;
|
||||||
|
|
||||||
case BIND_ACTION_SEARCH_COUNT:
|
case BIND_ACTION_SEARCH_COUNT:
|
||||||
BUG("Invalid action type");
|
BUG("Invalid action type");
|
||||||
return true;
|
return true;
|
||||||
|
|
|
||||||
537
selection.c
537
selection.c
|
|
@ -9,6 +9,8 @@
|
||||||
#include <sys/epoll.h>
|
#include <sys/epoll.h>
|
||||||
#include <sys/timerfd.h>
|
#include <sys/timerfd.h>
|
||||||
|
|
||||||
|
#include <pixman.h>
|
||||||
|
|
||||||
#define LOG_MODULE "selection"
|
#define LOG_MODULE "selection"
|
||||||
#define LOG_ENABLE_DBG 0
|
#define LOG_ENABLE_DBG 0
|
||||||
#include "log.h"
|
#include "log.h"
|
||||||
|
|
@ -64,41 +66,85 @@ selection_get_end(const struct terminal *term)
|
||||||
bool
|
bool
|
||||||
selection_on_rows(const struct terminal *term, int row_start, int row_end)
|
selection_on_rows(const struct terminal *term, int row_start, int row_end)
|
||||||
{
|
{
|
||||||
|
xassert(term->selection.coords.end.row >= 0);
|
||||||
|
|
||||||
LOG_DBG("on rows: %d-%d, range: %d-%d (offset=%d)",
|
LOG_DBG("on rows: %d-%d, range: %d-%d (offset=%d)",
|
||||||
term->selection.coords.start.row, term->selection.coords.end.row,
|
term->selection.coords.start.row, term->selection.coords.end.row,
|
||||||
row_start, row_end, term->grid->offset);
|
row_start, row_end, term->grid->offset);
|
||||||
|
|
||||||
if (term->selection.coords.end.row < 0)
|
|
||||||
return false;
|
|
||||||
|
|
||||||
xassert(term->selection.coords.start.row != -1);
|
|
||||||
|
|
||||||
row_start += term->grid->offset;
|
row_start += term->grid->offset;
|
||||||
row_end += term->grid->offset;
|
row_end += term->grid->offset;
|
||||||
|
xassert(row_end >= row_start);
|
||||||
|
|
||||||
const struct coord *start = &term->selection.coords.start;
|
const struct coord *start = &term->selection.coords.start;
|
||||||
const struct coord *end = &term->selection.coords.end;
|
const struct coord *end = &term->selection.coords.end;
|
||||||
|
|
||||||
if ((row_start <= start->row && row_end >= start->row) ||
|
const struct grid *grid = term->grid;
|
||||||
(row_start <= end->row && row_end >= end->row))
|
const int sb_start = grid->offset + term->rows;
|
||||||
|
|
||||||
|
/* Use scrollback relative coords when checking for overlap */
|
||||||
|
const int rel_row_start =
|
||||||
|
grid_row_abs_to_sb_precalc_sb_start(grid, sb_start, row_start);
|
||||||
|
const int rel_row_end =
|
||||||
|
grid_row_abs_to_sb_precalc_sb_start(grid, sb_start, row_start);
|
||||||
|
int rel_sel_start =
|
||||||
|
grid_row_abs_to_sb_precalc_sb_start(grid, sb_start, start->row);
|
||||||
|
int rel_sel_end =
|
||||||
|
grid_row_abs_to_sb_precalc_sb_start(grid, sb_start, end->row);
|
||||||
|
|
||||||
|
if (rel_sel_start > rel_sel_end) {
|
||||||
|
int tmp = rel_sel_start;
|
||||||
|
rel_sel_start = rel_sel_end;
|
||||||
|
rel_sel_end = tmp;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((rel_row_start <= rel_sel_start && rel_row_end >= rel_sel_start) ||
|
||||||
|
(rel_row_start <= rel_sel_end && rel_row_end >= rel_sel_end))
|
||||||
{
|
{
|
||||||
/* The range crosses one of the selection boundaries */
|
/* The range crosses one of the selection boundaries */
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* For the last check we must ensure start <= end */
|
if (rel_row_start >= rel_sel_start && rel_row_end <= rel_sel_end)
|
||||||
if (start->row > end->row) {
|
|
||||||
const struct coord *tmp = start;
|
|
||||||
start = end;
|
|
||||||
end = tmp;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (row_start >= start->row && row_end <= end->row)
|
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
selection_scroll_up(struct terminal *term, int rows)
|
||||||
|
{
|
||||||
|
xassert(term->selection.coords.end.row >= 0);
|
||||||
|
|
||||||
|
const int rel_row_start =
|
||||||
|
grid_row_abs_to_sb(term->grid, term->rows, term->selection.coords.start.row);
|
||||||
|
const int rel_row_end =
|
||||||
|
grid_row_abs_to_sb(term->grid, term->rows, term->selection.coords.end.row);
|
||||||
|
const int actual_start = min(rel_row_start, rel_row_end);
|
||||||
|
|
||||||
|
if (actual_start - rows < 0) {
|
||||||
|
/* Part of the selection will be scrolled out, cancel it */
|
||||||
|
selection_cancel(term);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
selection_scroll_down(struct terminal *term, int rows)
|
||||||
|
{
|
||||||
|
xassert(term->selection.coords.end.row >= 0);
|
||||||
|
|
||||||
|
const int rel_row_start =
|
||||||
|
grid_row_abs_to_sb(term->grid, term->rows, term->selection.coords.start.row);
|
||||||
|
const int rel_row_end =
|
||||||
|
grid_row_abs_to_sb(term->grid, term->rows, term->selection.coords.end.row);
|
||||||
|
const int actual_end = max(rel_row_start, rel_row_end);
|
||||||
|
|
||||||
|
if (actual_end + rows <= term->grid->num_rows) {
|
||||||
|
/* Part of the selection will be scrolled out, cancel it */
|
||||||
|
selection_cancel(term);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
selection_view_up(struct terminal *term, int new_view)
|
selection_view_up(struct terminal *term, int new_view)
|
||||||
{
|
{
|
||||||
|
|
@ -137,14 +183,14 @@ foreach_selected_normal(
|
||||||
const struct coord *start = &_start;
|
const struct coord *start = &_start;
|
||||||
const struct coord *end = &_end;
|
const struct coord *end = &_end;
|
||||||
|
|
||||||
const int scrollback_start = term->grid->offset + term->rows;
|
|
||||||
const int grid_rows = term->grid->num_rows;
|
const int grid_rows = term->grid->num_rows;
|
||||||
|
|
||||||
|
/* Start/end rows, relative to the scrollback start */
|
||||||
/* Start/end rows, relative to the scrollback start */
|
/* Start/end rows, relative to the scrollback start */
|
||||||
const int rel_start_row =
|
const int rel_start_row =
|
||||||
(start->row - scrollback_start + grid_rows) & (grid_rows - 1);
|
grid_row_abs_to_sb(term->grid, term->rows, start->row);
|
||||||
const int rel_end_row =
|
const int rel_end_row =
|
||||||
(end->row - scrollback_start + grid_rows) & (grid_rows - 1);
|
grid_row_abs_to_sb(term->grid, term->rows, end->row);
|
||||||
|
|
||||||
int start_row, end_row;
|
int start_row, end_row;
|
||||||
int start_col, end_col;
|
int start_col, end_col;
|
||||||
|
|
@ -200,14 +246,13 @@ foreach_selected_block(
|
||||||
const struct coord *start = &_start;
|
const struct coord *start = &_start;
|
||||||
const struct coord *end = &_end;
|
const struct coord *end = &_end;
|
||||||
|
|
||||||
const int scrollback_start = term->grid->offset + term->rows;
|
|
||||||
const int grid_rows = term->grid->num_rows;
|
const int grid_rows = term->grid->num_rows;
|
||||||
|
|
||||||
/* Start/end rows, relative to the scrollback start */
|
/* Start/end rows, relative to the scrollback start */
|
||||||
const int rel_start_row =
|
const int rel_start_row =
|
||||||
(start->row - scrollback_start + grid_rows) & (grid_rows - 1);
|
grid_row_abs_to_sb(term->grid, term->rows, start->row);
|
||||||
const int rel_end_row =
|
const int rel_end_row =
|
||||||
(end->row - scrollback_start + grid_rows) & (grid_rows - 1);
|
grid_row_abs_to_sb(term->grid, term->rows, end->row);
|
||||||
|
|
||||||
struct coord top_left = {
|
struct coord top_left = {
|
||||||
.row = (rel_start_row < rel_end_row
|
.row = (rel_start_row < rel_end_row
|
||||||
|
|
@ -564,111 +609,216 @@ selection_start(struct terminal *term, int col, int row,
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Context used while (un)marking selected cells, to be able to
|
static pixman_region32_t
|
||||||
* exclude empty cells */
|
pixman_region_for_coords_normal(const struct terminal *term,
|
||||||
struct mark_context {
|
const struct coord *start,
|
||||||
const struct row *last_row;
|
const struct coord *end)
|
||||||
int empty_count;
|
{
|
||||||
uint8_t **keep_selection;
|
pixman_region32_t region;
|
||||||
|
pixman_region32_init(®ion);
|
||||||
|
|
||||||
|
const int rel_start_row =
|
||||||
|
grid_row_abs_to_sb(term->grid, term->rows, start->row);
|
||||||
|
const int rel_end_row =
|
||||||
|
grid_row_abs_to_sb(term->grid, term->rows, end->row);
|
||||||
|
|
||||||
|
if (rel_start_row < rel_end_row) {
|
||||||
|
/* First partial row (start ->)*/
|
||||||
|
pixman_region32_union_rect(
|
||||||
|
®ion, ®ion,
|
||||||
|
start->col, rel_start_row,
|
||||||
|
term->cols - start->col, 1);
|
||||||
|
|
||||||
|
/* Full rows between start and end */
|
||||||
|
if (rel_start_row + 1 < rel_end_row) {
|
||||||
|
pixman_region32_union_rect(
|
||||||
|
®ion, ®ion,
|
||||||
|
0, rel_start_row + 1,
|
||||||
|
term->cols, rel_end_row - rel_start_row - 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Last partial row (-> end) */
|
||||||
|
pixman_region32_union_rect(
|
||||||
|
®ion, ®ion,
|
||||||
|
0, rel_end_row,
|
||||||
|
end->col + 1, 1);
|
||||||
|
|
||||||
|
} else if (rel_start_row > rel_end_row) {
|
||||||
|
/* First partial row (end ->) */
|
||||||
|
pixman_region32_union_rect(
|
||||||
|
®ion, ®ion,
|
||||||
|
end->col, rel_end_row,
|
||||||
|
term->cols - end->col, 1);
|
||||||
|
|
||||||
|
/* Full rows between end and start */
|
||||||
|
if (rel_end_row + 1 < rel_start_row) {
|
||||||
|
pixman_region32_union_rect(
|
||||||
|
®ion, ®ion,
|
||||||
|
0, rel_end_row + 1,
|
||||||
|
term->cols, rel_start_row - rel_end_row - 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Last partial row (-> start) */
|
||||||
|
pixman_region32_union_rect(
|
||||||
|
®ion, ®ion,
|
||||||
|
0, rel_start_row,
|
||||||
|
start->col + 1, 1);
|
||||||
|
} else {
|
||||||
|
const int start_col = min(start->col, end->col);
|
||||||
|
const int end_col = max(start->col, end->col);
|
||||||
|
|
||||||
|
pixman_region32_union_rect(
|
||||||
|
®ion, ®ion,
|
||||||
|
start_col, rel_start_row,
|
||||||
|
end_col + 1 - start_col, 1);
|
||||||
|
}
|
||||||
|
|
||||||
|
return region;
|
||||||
|
}
|
||||||
|
|
||||||
|
static pixman_region32_t
|
||||||
|
pixman_region_for_coords_block(const struct terminal *term,
|
||||||
|
const struct coord *start, const struct coord *end)
|
||||||
|
{
|
||||||
|
pixman_region32_t region;
|
||||||
|
pixman_region32_init(®ion);
|
||||||
|
|
||||||
|
const int rel_start_row =
|
||||||
|
grid_row_abs_to_sb(term->grid, term->rows, start->row);
|
||||||
|
const int rel_end_row =
|
||||||
|
grid_row_abs_to_sb(term->grid, term->rows, end->row);
|
||||||
|
|
||||||
|
pixman_region32_union_rect(
|
||||||
|
®ion, ®ion,
|
||||||
|
min(start->col, end->col), min(rel_start_row, rel_end_row),
|
||||||
|
abs(start->col - end->col) + 1, abs(rel_start_row - rel_end_row) + 1);
|
||||||
|
|
||||||
|
return region;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Returns a pixman region representing the selection between ‘start’
|
||||||
|
* and ‘end’ (given the current selection kind), in *scrollback
|
||||||
|
* relative coordinates* */
|
||||||
|
static pixman_region32_t
|
||||||
|
pixman_region_for_coords(const struct terminal *term,
|
||||||
|
const struct coord *start, const struct coord *end)
|
||||||
|
{
|
||||||
|
switch (term->selection.kind) {
|
||||||
|
default: return pixman_region_for_coords_normal(term, start, end);
|
||||||
|
case SELECTION_BLOCK: return pixman_region_for_coords_block(term, start, end);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
enum mark_selection_variant {
|
||||||
|
MARK_SELECTION_MARK_AND_DIRTY,
|
||||||
|
MARK_SELECTION_UNMARK_AND_DIRTY,
|
||||||
|
MARK_SELECTION_MARK_FOR_RENDER,
|
||||||
};
|
};
|
||||||
|
|
||||||
static bool
|
|
||||||
unmark_selected(struct terminal *term, struct row *row, struct cell *cell,
|
|
||||||
int row_no, int col, void *data)
|
|
||||||
{
|
|
||||||
if (!cell->attrs.selected)
|
|
||||||
return true;
|
|
||||||
|
|
||||||
struct mark_context *ctx = data;
|
|
||||||
const uint8_t *keep_selection =
|
|
||||||
ctx->keep_selection != NULL ? ctx->keep_selection[row_no] : NULL;
|
|
||||||
|
|
||||||
if (keep_selection != NULL) {
|
|
||||||
unsigned idx = (unsigned)col / 8;
|
|
||||||
unsigned ofs = (unsigned)col % 8;
|
|
||||||
|
|
||||||
if (keep_selection[idx] & (1 << ofs)) {
|
|
||||||
/* We’re updating the selection, and this cell is still
|
|
||||||
* going to be selected */
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
row->dirty = true;
|
|
||||||
cell->attrs.selected = false;
|
|
||||||
cell->attrs.clean = false;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool
|
|
||||||
premark_selected(struct terminal *term, struct row *row, struct cell *cell,
|
|
||||||
int row_no, int col, void *data)
|
|
||||||
{
|
|
||||||
struct mark_context *ctx = data;
|
|
||||||
xassert(ctx != NULL);
|
|
||||||
|
|
||||||
if (ctx->last_row != row) {
|
|
||||||
ctx->last_row = row;
|
|
||||||
ctx->empty_count = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (cell->wc == 0 && term->selection.kind != SELECTION_BLOCK) {
|
|
||||||
ctx->empty_count++;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
uint8_t *keep_selection = ctx->keep_selection[row_no];
|
|
||||||
if (keep_selection == NULL) {
|
|
||||||
keep_selection = xcalloc((term->grid->num_cols + 7) / 8, sizeof(keep_selection[0]));
|
|
||||||
ctx->keep_selection[row_no] = keep_selection;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Tell unmark to leave this be */
|
|
||||||
for (int i = 0; i < ctx->empty_count + 1; i++) {
|
|
||||||
unsigned idx = (unsigned)(col - i) / 8;
|
|
||||||
unsigned ofs = (unsigned)(col - i) % 8;
|
|
||||||
keep_selection[idx] |= 1 << ofs;
|
|
||||||
}
|
|
||||||
|
|
||||||
ctx->empty_count = 0;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
static bool
|
|
||||||
mark_selected(struct terminal *term, struct row *row, struct cell *cell,
|
|
||||||
int row_no, int col, void *data)
|
|
||||||
{
|
|
||||||
struct mark_context *ctx = data;
|
|
||||||
xassert(ctx != NULL);
|
|
||||||
|
|
||||||
if (ctx->last_row != row) {
|
|
||||||
ctx->last_row = row;
|
|
||||||
ctx->empty_count = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (cell->wc == 0 && term->selection.kind != SELECTION_BLOCK) {
|
|
||||||
ctx->empty_count++;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (int i = 0; i < ctx->empty_count + 1; i++) {
|
|
||||||
struct cell *c = &row->cells[col - i];
|
|
||||||
if (!c->attrs.selected) {
|
|
||||||
row->dirty = true;
|
|
||||||
c->attrs.selected = true;
|
|
||||||
c->attrs.clean = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ctx->empty_count = 0;
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
static void
|
static void
|
||||||
reset_modify_context(struct mark_context *ctx)
|
mark_selected_region(struct terminal *term, pixman_box32_t *boxes,
|
||||||
|
size_t count, enum mark_selection_variant mark_variant)
|
||||||
{
|
{
|
||||||
ctx->last_row = NULL;
|
const bool selected =
|
||||||
ctx->empty_count = 0;
|
mark_variant == MARK_SELECTION_MARK_AND_DIRTY ||
|
||||||
|
mark_variant == MARK_SELECTION_MARK_FOR_RENDER;
|
||||||
|
const bool dirty_cells =
|
||||||
|
mark_variant == MARK_SELECTION_MARK_AND_DIRTY ||
|
||||||
|
mark_variant == MARK_SELECTION_UNMARK_AND_DIRTY;
|
||||||
|
const bool highlight_empty =
|
||||||
|
mark_variant != MARK_SELECTION_MARK_FOR_RENDER ||
|
||||||
|
term->selection.kind == SELECTION_BLOCK;
|
||||||
|
|
||||||
|
for (size_t i = 0; i < count; i++) {
|
||||||
|
const pixman_box32_t *box = &boxes[i];
|
||||||
|
|
||||||
|
LOG_DBG("%s selection in region: %dx%d - %dx%d",
|
||||||
|
selected ? "marking" : "unmarking",
|
||||||
|
box->x1, box->y1,
|
||||||
|
box->x2, box->y2);
|
||||||
|
|
||||||
|
int abs_row_start = grid_row_sb_to_abs(
|
||||||
|
term->grid, term->rows, box->y1);
|
||||||
|
|
||||||
|
for (int r = abs_row_start, rel_r = box->y1;
|
||||||
|
rel_r < box->y2;
|
||||||
|
r = (r + 1) & (term->grid->num_rows - 1), rel_r++)
|
||||||
|
{
|
||||||
|
struct row *row = term->grid->rows[r];
|
||||||
|
xassert(row != NULL);
|
||||||
|
|
||||||
|
if (dirty_cells)
|
||||||
|
row->dirty = true;
|
||||||
|
|
||||||
|
for (int c = box->x1, empty_count = 0; c < box->x2; c++) {
|
||||||
|
struct cell *cell = &row->cells[c];
|
||||||
|
|
||||||
|
if (cell->wc == 0 && !highlight_empty) {
|
||||||
|
/*
|
||||||
|
* We used to highlight empty cells *if* they were
|
||||||
|
* followed by non-empty cell(s), since this
|
||||||
|
* corresponds to what gets extracted when the
|
||||||
|
* selection is copied (that is, empty cells
|
||||||
|
* “between” non-empty cells are converted to
|
||||||
|
* spaces).
|
||||||
|
*
|
||||||
|
* However, they way we handle selection updates
|
||||||
|
* (diffing the “old” selection area against the
|
||||||
|
* “new” one, using pixman regions), means we
|
||||||
|
* can’t correctly update the state of empty
|
||||||
|
* cells. The result is “random” empty cells being
|
||||||
|
* rendered as selected when they shouldn’t.
|
||||||
|
*
|
||||||
|
* “Fix” by *never* highlighting selected empty
|
||||||
|
* cells (they still get converted to spaces when
|
||||||
|
* copied, if followed by non-empty cells).
|
||||||
|
*/
|
||||||
|
empty_count++;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* When the selection is *modified*, empty cells
|
||||||
|
* are treated just like non-empty cells; they are
|
||||||
|
* marked as selected, and dirtied.
|
||||||
|
*
|
||||||
|
* This is due to how the algorithm for updating
|
||||||
|
* the selection works; it uses regions to
|
||||||
|
* calculate the difference between the “old” and
|
||||||
|
* the “new” selection. This makes it impossible
|
||||||
|
* to tell if an empty cell is a *trailing* empty
|
||||||
|
* cell (that should not be highlighted), or an
|
||||||
|
* empty cells between non-empty cells (that
|
||||||
|
* *should* be highlighted).
|
||||||
|
*
|
||||||
|
* Then, when a frame is rendered, we loop the
|
||||||
|
* *visibible* cells that belong to the
|
||||||
|
* selection. At this point, we *can* tell if an
|
||||||
|
* empty cell is trailing or not.
|
||||||
|
*
|
||||||
|
* So, what we need to do is check if a
|
||||||
|
* ‘selected’, and empty cell has been marked as
|
||||||
|
* selected, temporarily unmark (forcing it dirty,
|
||||||
|
* to ensure it gets re-rendered). If it is *not*
|
||||||
|
* a trailing empty cell, it will get re-tagged as
|
||||||
|
* selected in the for-loop below.
|
||||||
|
*/
|
||||||
|
cell->attrs.clean = false;
|
||||||
|
cell->attrs.selected = false;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (int j = 0; j < empty_count + 1; j++) {
|
||||||
|
xassert(c - j >= 0);
|
||||||
|
struct cell *cell = &row->cells[c - j];
|
||||||
|
|
||||||
|
if (dirty_cells)
|
||||||
|
cell->attrs.clean = false;
|
||||||
|
cell->attrs.selected = selected;
|
||||||
|
}
|
||||||
|
|
||||||
|
empty_count = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
|
|
@ -678,33 +828,46 @@ selection_modify(struct terminal *term, struct coord start, struct coord end)
|
||||||
xassert(start.row != -1 && start.col != -1);
|
xassert(start.row != -1 && start.col != -1);
|
||||||
xassert(end.row != -1 && end.col != -1);
|
xassert(end.row != -1 && end.col != -1);
|
||||||
|
|
||||||
uint8_t **keep_selection =
|
pixman_region32_t previous_selection;
|
||||||
xcalloc(term->grid->num_rows, sizeof(keep_selection[0]));
|
|
||||||
|
|
||||||
struct mark_context ctx = {.keep_selection = keep_selection};
|
|
||||||
|
|
||||||
/* Premark all cells that *will* be selected */
|
|
||||||
foreach_selected(term, start, end, &premark_selected, &ctx);
|
|
||||||
reset_modify_context(&ctx);
|
|
||||||
|
|
||||||
if (term->selection.coords.end.row >= 0) {
|
if (term->selection.coords.end.row >= 0) {
|
||||||
/* Unmark previous selection, ignoring cells that are part of
|
previous_selection = pixman_region_for_coords(
|
||||||
* the new selection */
|
term,
|
||||||
foreach_selected(term, term->selection.coords.start, term->selection.coords.end,
|
&term->selection.coords.start,
|
||||||
&unmark_selected, &ctx);
|
&term->selection.coords.end);
|
||||||
reset_modify_context(&ctx);
|
} else
|
||||||
}
|
pixman_region32_init(&previous_selection);
|
||||||
|
|
||||||
|
pixman_region32_t current_selection = pixman_region_for_coords(
|
||||||
|
term, &start, &end);
|
||||||
|
|
||||||
|
pixman_region32_t no_longer_selected;
|
||||||
|
pixman_region32_init(&no_longer_selected);
|
||||||
|
pixman_region32_subtract(
|
||||||
|
&no_longer_selected, &previous_selection, ¤t_selection);
|
||||||
|
|
||||||
|
pixman_region32_t newly_selected;
|
||||||
|
pixman_region32_init(&newly_selected);
|
||||||
|
pixman_region32_subtract(
|
||||||
|
&newly_selected, ¤t_selection, &previous_selection);
|
||||||
|
|
||||||
|
/* Clear selection in cells no longer selected */
|
||||||
|
int n_rects = -1;
|
||||||
|
pixman_box32_t *boxes = NULL;
|
||||||
|
|
||||||
|
boxes = pixman_region32_rectangles(&no_longer_selected, &n_rects);
|
||||||
|
mark_selected_region(term, boxes, n_rects, MARK_SELECTION_UNMARK_AND_DIRTY);
|
||||||
|
|
||||||
|
boxes = pixman_region32_rectangles(&newly_selected, &n_rects);
|
||||||
|
mark_selected_region(term, boxes, n_rects, MARK_SELECTION_MARK_AND_DIRTY);
|
||||||
|
|
||||||
|
pixman_region32_fini(&newly_selected);
|
||||||
|
pixman_region32_fini(&no_longer_selected);
|
||||||
|
pixman_region32_fini(¤t_selection);
|
||||||
|
pixman_region32_fini(&previous_selection);
|
||||||
|
|
||||||
term->selection.coords.start = start;
|
term->selection.coords.start = start;
|
||||||
term->selection.coords.end = end;
|
term->selection.coords.end = end;
|
||||||
|
|
||||||
/* Mark new selection */
|
|
||||||
foreach_selected(term, start, end, &mark_selected, &ctx);
|
|
||||||
render_refresh(term);
|
render_refresh(term);
|
||||||
|
|
||||||
for (size_t i = 0; i < term->grid->num_rows; i++)
|
|
||||||
free(keep_selection[i]);
|
|
||||||
free(keep_selection);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
|
|
@ -945,9 +1108,26 @@ selection_dirty_cells(struct terminal *term)
|
||||||
if (term->selection.coords.start.row < 0 || term->selection.coords.end.row < 0)
|
if (term->selection.coords.start.row < 0 || term->selection.coords.end.row < 0)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
foreach_selected(
|
pixman_region32_t selection = pixman_region_for_coords(
|
||||||
term, term->selection.coords.start, term->selection.coords.end, &mark_selected,
|
term, &term->selection.coords.start, &term->selection.coords.end);
|
||||||
&(struct mark_context){0});
|
|
||||||
|
pixman_region32_t view = pixman_region_for_coords(
|
||||||
|
term,
|
||||||
|
&(struct coord){0, term->grid->view},
|
||||||
|
&(struct coord){term->cols - 1, term->grid->view + term->rows - 1});
|
||||||
|
|
||||||
|
pixman_region32_t visible_and_selected;
|
||||||
|
pixman_region32_init(&visible_and_selected);
|
||||||
|
pixman_region32_intersect(&visible_and_selected, &selection, &view);
|
||||||
|
|
||||||
|
int n_rects = -1;
|
||||||
|
pixman_box32_t *boxes =
|
||||||
|
pixman_region32_rectangles(&visible_and_selected, &n_rects);
|
||||||
|
mark_selected_region(term, boxes, n_rects, MARK_SELECTION_MARK_FOR_RENDER);
|
||||||
|
|
||||||
|
pixman_region32_fini(&visible_and_selected);
|
||||||
|
pixman_region32_fini(&view);
|
||||||
|
pixman_region32_fini(&selection);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
|
|
@ -957,27 +1137,37 @@ selection_extend_normal(struct terminal *term, int col, int row,
|
||||||
const struct coord *start = &term->selection.coords.start;
|
const struct coord *start = &term->selection.coords.start;
|
||||||
const struct coord *end = &term->selection.coords.end;
|
const struct coord *end = &term->selection.coords.end;
|
||||||
|
|
||||||
if (start->row > end->row ||
|
const int rel_row = grid_row_abs_to_sb(term->grid, term->rows, row);
|
||||||
(start->row == end->row && start->col > end->col))
|
int rel_start_row = grid_row_abs_to_sb(term->grid, term->rows, start->row);
|
||||||
|
int rel_end_row = grid_row_abs_to_sb(term->grid, term->rows, end->row);
|
||||||
|
|
||||||
|
if (rel_start_row > rel_end_row ||
|
||||||
|
(rel_start_row == rel_end_row && start->col > end->col))
|
||||||
{
|
{
|
||||||
const struct coord *tmp = start;
|
const struct coord *tmp = start;
|
||||||
start = end;
|
start = end;
|
||||||
end = tmp;
|
end = tmp;
|
||||||
}
|
|
||||||
|
|
||||||
xassert(start->row < end->row || start->col < end->col);
|
int tmp_row = rel_start_row;
|
||||||
|
rel_start_row = rel_end_row;
|
||||||
|
rel_end_row = tmp_row;
|
||||||
|
}
|
||||||
|
|
||||||
struct coord new_start, new_end;
|
struct coord new_start, new_end;
|
||||||
enum selection_direction direction;
|
enum selection_direction direction;
|
||||||
|
|
||||||
if (row < start->row || (row == start->row && col < start->col)) {
|
if (rel_row < rel_start_row ||
|
||||||
|
(rel_row == rel_start_row && col < start->col))
|
||||||
|
{
|
||||||
/* Extend selection to start *before* current start */
|
/* Extend selection to start *before* current start */
|
||||||
new_start = *end;
|
new_start = *end;
|
||||||
new_end = (struct coord){col, row};
|
new_end = (struct coord){col, row};
|
||||||
direction = SELECTION_LEFT;
|
direction = SELECTION_LEFT;
|
||||||
}
|
}
|
||||||
|
|
||||||
else if (row > end->row || (row == end->row && col > end->col)) {
|
else if (rel_row > rel_end_row ||
|
||||||
|
(rel_row == rel_end_row && col > end->col))
|
||||||
|
{
|
||||||
/* Extend selection to end *after* current end */
|
/* Extend selection to end *after* current end */
|
||||||
new_start = *start;
|
new_start = *start;
|
||||||
new_end = (struct coord){col, row};
|
new_end = (struct coord){col, row};
|
||||||
|
|
@ -987,10 +1177,10 @@ selection_extend_normal(struct terminal *term, int col, int row,
|
||||||
else {
|
else {
|
||||||
/* Shrink selection from start or end, depending on which one is closest */
|
/* Shrink selection from start or end, depending on which one is closest */
|
||||||
|
|
||||||
const int linear = row * term->cols + col;
|
const int linear = rel_row * term->cols + col;
|
||||||
|
|
||||||
if (abs(linear - (start->row * term->cols + start->col)) <
|
if (abs(linear - (rel_start_row * term->cols + start->col)) <
|
||||||
abs(linear - (end->row * term->cols + end->col)))
|
abs(linear - (rel_end_row * term->cols + end->col)))
|
||||||
{
|
{
|
||||||
/* Move start point */
|
/* Move start point */
|
||||||
new_start = *end;
|
new_start = *end;
|
||||||
|
|
@ -1065,33 +1255,41 @@ selection_extend_block(struct terminal *term, int col, int row)
|
||||||
const struct coord *start = &term->selection.coords.start;
|
const struct coord *start = &term->selection.coords.start;
|
||||||
const struct coord *end = &term->selection.coords.end;
|
const struct coord *end = &term->selection.coords.end;
|
||||||
|
|
||||||
|
const int rel_start_row =
|
||||||
|
grid_row_abs_to_sb(term->grid, term->rows, start->row);
|
||||||
|
const int rel_end_row =
|
||||||
|
grid_row_abs_to_sb(term->grid, term->rows, end->row);
|
||||||
|
|
||||||
struct coord top_left = {
|
struct coord top_left = {
|
||||||
.row = min(start->row, end->row),
|
.row = rel_start_row < rel_end_row ? start->row : end->row,
|
||||||
.col = min(start->col, end->col),
|
.col = min(start->col, end->col),
|
||||||
};
|
};
|
||||||
|
|
||||||
struct coord top_right = {
|
struct coord top_right = {
|
||||||
.row = min(start->row, end->row),
|
.row = top_left.row,
|
||||||
.col = max(start->col, end->col),
|
.col = max(start->col, end->col),
|
||||||
};
|
};
|
||||||
|
|
||||||
struct coord bottom_left = {
|
struct coord bottom_left = {
|
||||||
.row = max(start->row, end->row),
|
.row = rel_start_row > rel_end_row ? start->row : end->row,
|
||||||
.col = min(start->col, end->col),
|
.col = min(start->col, end->col),
|
||||||
};
|
};
|
||||||
|
|
||||||
struct coord bottom_right = {
|
struct coord bottom_right = {
|
||||||
.row = max(start->row, end->row),
|
.row = bottom_left.row,
|
||||||
.col = max(start->col, end->col),
|
.col = max(start->col, end->col),
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const int rel_row = grid_row_abs_to_sb(term->grid, term->rows, row);
|
||||||
|
const int rel_top_row = grid_row_abs_to_sb(term->grid, term->rows, top_left.row);
|
||||||
|
const int rel_bottom_row = grid_row_abs_to_sb(term->grid, term->rows, bottom_left.row);
|
||||||
struct coord new_start;
|
struct coord new_start;
|
||||||
struct coord new_end;
|
struct coord new_end;
|
||||||
|
|
||||||
enum selection_direction direction = SELECTION_UNDIR;
|
enum selection_direction direction = SELECTION_UNDIR;
|
||||||
|
|
||||||
if (row <= top_left.row ||
|
if (rel_row <= rel_top_row ||
|
||||||
abs(row - top_left.row) < abs(row - bottom_left.row))
|
abs(rel_row - rel_top_row) < abs(rel_row - rel_bottom_row))
|
||||||
{
|
{
|
||||||
/* Move one of the top corners */
|
/* Move one of the top corners */
|
||||||
|
|
||||||
|
|
@ -1207,6 +1405,19 @@ selection_finalize(struct seat *seat, struct terminal *term, uint32_t serial)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static bool
|
||||||
|
unmark_selected(struct terminal *term, struct row *row, struct cell *cell,
|
||||||
|
int row_no, int col, void *data)
|
||||||
|
{
|
||||||
|
if (!cell->attrs.selected)
|
||||||
|
return true;
|
||||||
|
|
||||||
|
row->dirty = true;
|
||||||
|
cell->attrs.selected = false;
|
||||||
|
cell->attrs.clean = false;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
selection_cancel(struct terminal *term)
|
selection_cancel(struct terminal *term)
|
||||||
{
|
{
|
||||||
|
|
@ -1219,7 +1430,7 @@ selection_cancel(struct terminal *term)
|
||||||
if (term->selection.coords.start.row >= 0 && term->selection.coords.end.row >= 0) {
|
if (term->selection.coords.start.row >= 0 && term->selection.coords.end.row >= 0) {
|
||||||
foreach_selected(
|
foreach_selected(
|
||||||
term, term->selection.coords.start, term->selection.coords.end,
|
term, term->selection.coords.start, term->selection.coords.end,
|
||||||
&unmark_selected, &(struct mark_context){0});
|
&unmark_selected, NULL);
|
||||||
render_refresh(term);
|
render_refresh(term);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -22,6 +22,8 @@ void selection_extend(
|
||||||
|
|
||||||
bool selection_on_rows(const struct terminal *term, int start, int end);
|
bool selection_on_rows(const struct terminal *term, int start, int end);
|
||||||
|
|
||||||
|
void selection_scroll_up(struct terminal *term, int rows);
|
||||||
|
void selection_scroll_down(struct terminal *term, int rows);
|
||||||
void selection_view_up(struct terminal *term, int new_view);
|
void selection_view_up(struct terminal *term, int new_view);
|
||||||
void selection_view_down(struct terminal *term, int new_view);
|
void selection_view_down(struct terminal *term, int new_view);
|
||||||
|
|
||||||
|
|
|
||||||
3
shm.c
3
shm.c
|
|
@ -151,7 +151,6 @@ buffer_destroy(struct buffer_private *buf)
|
||||||
pool_unref(buf->pool);
|
pool_unref(buf->pool);
|
||||||
buf->pool = NULL;
|
buf->pool = NULL;
|
||||||
|
|
||||||
free(buf->public.scroll_damage);
|
|
||||||
pixman_region32_fini(&buf->public.dirty);
|
pixman_region32_fini(&buf->public.dirty);
|
||||||
free(buf);
|
free(buf);
|
||||||
}
|
}
|
||||||
|
|
@ -581,8 +580,6 @@ shm_get_buffer(struct buffer_chain *chain, int width, int height)
|
||||||
LOG_DBG("re-using buffer %p from cache", (void *)cached);
|
LOG_DBG("re-using buffer %p from cache", (void *)cached);
|
||||||
cached->busy = true;
|
cached->busy = true;
|
||||||
pixman_region32_clear(&cached->public.dirty);
|
pixman_region32_clear(&cached->public.dirty);
|
||||||
free(cached->public.scroll_damage);
|
|
||||||
cached->public.scroll_damage = NULL;
|
|
||||||
xassert(cached->public.pix_instances == chain->pix_instances);
|
xassert(cached->public.pix_instances == chain->pix_instances);
|
||||||
return &cached->public;
|
return &cached->public;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
2
shm.h
2
shm.h
|
|
@ -24,8 +24,6 @@ struct buffer {
|
||||||
|
|
||||||
unsigned age;
|
unsigned age;
|
||||||
|
|
||||||
struct damage *scroll_damage;
|
|
||||||
size_t scroll_damage_count;
|
|
||||||
pixman_region32_t dirty;
|
pixman_region32_t dirty;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
1
slave.c
1
slave.c
|
|
@ -352,6 +352,7 @@ slave_spawn(int ptmx, int argc, const char *cwd, char *const *argv,
|
||||||
|
|
||||||
setenv("TERM", term_env, 1);
|
setenv("TERM", term_env, 1);
|
||||||
setenv("COLORTERM", "truecolor", 1);
|
setenv("COLORTERM", "truecolor", 1);
|
||||||
|
setenv("PWD", cwd, 1);
|
||||||
|
|
||||||
#if defined(FOOT_TERMINFO_PATH)
|
#if defined(FOOT_TERMINFO_PATH)
|
||||||
setenv("TERMINFO", FOOT_TERMINFO_PATH, 1);
|
setenv("TERMINFO", FOOT_TERMINFO_PATH, 1);
|
||||||
|
|
|
||||||
5
spawn.c
5
spawn.c
|
|
@ -54,10 +54,13 @@ spawn(struct reaper *reaper, const char *cwd, char *const argv[],
|
||||||
goto child_err;
|
goto child_err;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (cwd != NULL && chdir(cwd) < 0) {
|
if (cwd != NULL) {
|
||||||
|
setenv("PWD", cwd, 1);
|
||||||
|
if (chdir(cwd) < 0) {
|
||||||
LOG_WARN("failed to change working directory to %s: %s",
|
LOG_WARN("failed to change working directory to %s: %s",
|
||||||
cwd, strerror(errno));
|
cwd, strerror(errno));
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (xdg_activation_token != NULL) {
|
if (xdg_activation_token != NULL) {
|
||||||
setenv("XDG_ACTIVATION_TOKEN", xdg_activation_token, 1);
|
setenv("XDG_ACTIVATION_TOKEN", xdg_activation_token, 1);
|
||||||
|
|
|
||||||
58
terminal.c
58
terminal.c
|
|
@ -203,6 +203,30 @@ fdm_ptmx_out(struct fdm *fdm, int fd, int events, void *data)
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static bool
|
||||||
|
add_utmp_record(const struct config *conf, struct reaper *reaper, int ptmx)
|
||||||
|
{
|
||||||
|
if (ptmx < 0)
|
||||||
|
return true;
|
||||||
|
if (conf->utempter_path == NULL)
|
||||||
|
return true;
|
||||||
|
|
||||||
|
char *const argv[] = {conf->utempter_path, "add", getenv("WAYLAND_DISPLAY"), NULL};
|
||||||
|
return spawn(reaper, NULL, argv, ptmx, ptmx, -1, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool
|
||||||
|
del_utmp_record(const struct config *conf, struct reaper *reaper, int ptmx)
|
||||||
|
{
|
||||||
|
if (ptmx < 0)
|
||||||
|
return true;
|
||||||
|
if (conf->utempter_path == NULL)
|
||||||
|
return true;
|
||||||
|
|
||||||
|
char *const argv[] = {conf->utempter_path, "del", getenv("WAYLAND_DISPLAY"), NULL};
|
||||||
|
return spawn(reaper, NULL, argv, ptmx, ptmx, -1, NULL);
|
||||||
|
}
|
||||||
|
|
||||||
#if PTMX_TIMING
|
#if PTMX_TIMING
|
||||||
static struct timespec last = {0};
|
static struct timespec last = {0};
|
||||||
#endif
|
#endif
|
||||||
|
|
@ -326,6 +350,7 @@ fdm_ptmx(struct fdm *fdm, int fd, int events, void *data)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (hup) {
|
if (hup) {
|
||||||
|
del_utmp_record(term->conf, term->reaper, term->ptmx);
|
||||||
fdm_del(fdm, fd);
|
fdm_del(fdm, fd);
|
||||||
term->ptmx = -1;
|
term->ptmx = -1;
|
||||||
}
|
}
|
||||||
|
|
@ -1089,6 +1114,11 @@ term_init(const struct config *conf, struct fdm *fdm, struct reaper *reaper,
|
||||||
goto close_fds;
|
goto close_fds;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* Need to register *very* early (before the first “goto err”), to
|
||||||
|
* ensure term_destroy() doesn’t unref a key-binding we haven’t
|
||||||
|
* yet ref:d */
|
||||||
|
key_binding_new_for_conf(wayl->key_binding_manager, wayl, conf);
|
||||||
|
|
||||||
int ptmx_flags;
|
int ptmx_flags;
|
||||||
if ((ptmx_flags = fcntl(ptmx, F_GETFL)) < 0 ||
|
if ((ptmx_flags = fcntl(ptmx, F_GETFL)) < 0 ||
|
||||||
fcntl(ptmx, F_SETFL, ptmx_flags | O_NONBLOCK) < 0)
|
fcntl(ptmx, F_SETFL, ptmx_flags | O_NONBLOCK) < 0)
|
||||||
|
|
@ -1246,6 +1276,8 @@ term_init(const struct config *conf, struct fdm *fdm, struct reaper *reaper,
|
||||||
}
|
}
|
||||||
term->font_line_height = conf->line_height;
|
term->font_line_height = conf->line_height;
|
||||||
|
|
||||||
|
add_utmp_record(conf, reaper, ptmx);
|
||||||
|
|
||||||
/* Start the slave/client */
|
/* Start the slave/client */
|
||||||
if ((term->slave = slave_spawn(
|
if ((term->slave = slave_spawn(
|
||||||
term->ptmx, argc, term->cwd, argv, envp, &conf->env_vars,
|
term->ptmx, argc, term->cwd, argv, envp, &conf->env_vars,
|
||||||
|
|
@ -1266,8 +1298,6 @@ term_init(const struct config *conf, struct fdm *fdm, struct reaper *reaper,
|
||||||
|
|
||||||
memcpy(term->colors.table, term->conf->colors.table, sizeof(term->colors.table));
|
memcpy(term->colors.table, term->conf->colors.table, sizeof(term->colors.table));
|
||||||
|
|
||||||
key_binding_new_for_term(wayl->key_binding_manager, term);
|
|
||||||
|
|
||||||
/* Initialize the Wayland window backend */
|
/* Initialize the Wayland window backend */
|
||||||
if ((term->window = wayl_win_init(term, token)) == NULL)
|
if ((term->window = wayl_win_init(term, token)) == NULL)
|
||||||
goto err;
|
goto err;
|
||||||
|
|
@ -1511,6 +1541,8 @@ term_shutdown(struct terminal *term)
|
||||||
fdm_del(term->fdm, term->blink.fd);
|
fdm_del(term->fdm, term->blink.fd);
|
||||||
fdm_del(term->fdm, term->flash.fd);
|
fdm_del(term->fdm, term->flash.fd);
|
||||||
|
|
||||||
|
del_utmp_record(term->conf, term->reaper, term->ptmx);
|
||||||
|
|
||||||
if (term->window != NULL && term->window->is_configured)
|
if (term->window != NULL && term->window->is_configured)
|
||||||
fdm_del(term->fdm, term->ptmx);
|
fdm_del(term->fdm, term->ptmx);
|
||||||
else
|
else
|
||||||
|
|
@ -1583,7 +1615,7 @@ term_destroy(struct terminal *term)
|
||||||
if (term == NULL)
|
if (term == NULL)
|
||||||
return 0;
|
return 0;
|
||||||
|
|
||||||
key_binding_unref_term(term->wl->key_binding_manager, term);
|
key_binding_unref(term->wl->key_binding_manager, term->conf);
|
||||||
|
|
||||||
tll_foreach(term->wl->terms, it) {
|
tll_foreach(term->wl->terms, it) {
|
||||||
if (it->item == term) {
|
if (it->item == term) {
|
||||||
|
|
@ -1592,6 +1624,8 @@ term_destroy(struct terminal *term)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
del_utmp_record(term->conf, term->reaper, term->ptmx);
|
||||||
|
|
||||||
fdm_del(term->fdm, term->selection.auto_scroll.fd);
|
fdm_del(term->fdm, term->selection.auto_scroll.fd);
|
||||||
fdm_del(term->fdm, term->render.app_sync_updates.timer_fd);
|
fdm_del(term->fdm, term->render.app_sync_updates.timer_fd);
|
||||||
fdm_del(term->fdm, term->render.title.timer_fd);
|
fdm_del(term->fdm, term->render.title.timer_fd);
|
||||||
|
|
@ -1811,7 +1845,7 @@ static inline void
|
||||||
erase_line(struct terminal *term, struct row *row)
|
erase_line(struct terminal *term, struct row *row)
|
||||||
{
|
{
|
||||||
erase_cell_range(term, row, 0, term->cols - 1);
|
erase_cell_range(term, row, 0, term->cols - 1);
|
||||||
row->linebreak = true;
|
row->linebreak = false;
|
||||||
row->prompt_marker = false;
|
row->prompt_marker = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -2537,11 +2571,11 @@ term_scroll_partial(struct terminal *term, struct scroll_region region, int rows
|
||||||
* scrolled in (i.e. re-used lines).
|
* scrolled in (i.e. re-used lines).
|
||||||
*/
|
*/
|
||||||
if (selection_on_top_region(term, region) ||
|
if (selection_on_top_region(term, region) ||
|
||||||
selection_on_bottom_region(term, region) ||
|
selection_on_bottom_region(term, region))
|
||||||
selection_on_rows(term, region.end - rows, region.end - 1))
|
|
||||||
{
|
{
|
||||||
selection_cancel(term);
|
selection_cancel(term);
|
||||||
}
|
} else
|
||||||
|
selection_scroll_up(term, rows);
|
||||||
}
|
}
|
||||||
|
|
||||||
sixel_scroll_up(term, rows);
|
sixel_scroll_up(term, rows);
|
||||||
|
|
@ -2611,11 +2645,11 @@ term_scroll_reverse_partial(struct terminal *term,
|
||||||
* scrolled in (i.e. re-used lines).
|
* scrolled in (i.e. re-used lines).
|
||||||
*/
|
*/
|
||||||
if (selection_on_top_region(term, region) ||
|
if (selection_on_top_region(term, region) ||
|
||||||
selection_on_bottom_region(term, region) ||
|
selection_on_bottom_region(term, region))
|
||||||
selection_on_rows(term, region.start, region.start + rows - 1))
|
|
||||||
{
|
{
|
||||||
selection_cancel(term);
|
selection_cancel(term);
|
||||||
}
|
} else
|
||||||
|
selection_scroll_down(term, rows);
|
||||||
}
|
}
|
||||||
|
|
||||||
sixel_scroll_down(term, rows);
|
sixel_scroll_down(term, rows);
|
||||||
|
|
@ -2885,7 +2919,7 @@ term_mouse_grabbed(const struct terminal *term, const struct seat *seat)
|
||||||
get_current_modifiers(seat, &mods, NULL, 0);
|
get_current_modifiers(seat, &mods, NULL, 0);
|
||||||
|
|
||||||
const struct key_binding_set *bindings =
|
const struct key_binding_set *bindings =
|
||||||
key_binding_for(term->wl->key_binding_manager, term, seat);
|
key_binding_for(term->wl->key_binding_manager, term->conf, seat);
|
||||||
const xkb_mod_mask_t override_modmask = bindings->selection_overrides;
|
const xkb_mod_mask_t override_modmask = bindings->selection_overrides;
|
||||||
bool override_mods_pressed = (mods & override_modmask) == override_modmask;
|
bool override_mods_pressed = (mods & override_modmask) == override_modmask;
|
||||||
|
|
||||||
|
|
@ -3298,6 +3332,7 @@ term_print(struct terminal *term, char32_t wc, int width)
|
||||||
/* *Must* get current cell *after* linewrap+insert */
|
/* *Must* get current cell *after* linewrap+insert */
|
||||||
struct row *row = grid->cur_row;
|
struct row *row = grid->cur_row;
|
||||||
row->dirty = true;
|
row->dirty = true;
|
||||||
|
row->linebreak = true;
|
||||||
|
|
||||||
struct cell *cell = &row->cells[col];
|
struct cell *cell = &row->cells[col];
|
||||||
cell->wc = term->vt.last_printed = wc;
|
cell->wc = term->vt.last_printed = wc;
|
||||||
|
|
@ -3357,6 +3392,7 @@ ascii_printer_fast(struct terminal *term, char32_t wc)
|
||||||
|
|
||||||
struct row *row = grid->cur_row;
|
struct row *row = grid->cur_row;
|
||||||
row->dirty = true;
|
row->dirty = true;
|
||||||
|
row->linebreak = true;
|
||||||
|
|
||||||
struct cell *cell = &row->cells[col];
|
struct cell *cell = &row->cells[col];
|
||||||
cell->wc = term->vt.last_printed = wc;
|
cell->wc = term->vt.last_printed = wc;
|
||||||
|
|
|
||||||
|
|
@ -289,9 +289,10 @@ enum term_surface {
|
||||||
};
|
};
|
||||||
|
|
||||||
enum overlay_style {
|
enum overlay_style {
|
||||||
OVERLAY_NONE = 0,
|
OVERLAY_NONE,
|
||||||
OVERLAY_SEARCH = 1,
|
OVERLAY_SEARCH,
|
||||||
OVERLAY_FLASH = 2,
|
OVERLAY_FLASH,
|
||||||
|
OVERLAY_UNICODE_MODE,
|
||||||
};
|
};
|
||||||
|
|
||||||
typedef tll(struct ptmx_buffer) ptmx_buffer_list_t;
|
typedef tll(struct ptmx_buffer) ptmx_buffer_list_t;
|
||||||
|
|
|
||||||
|
|
@ -401,6 +401,52 @@ test_color(struct context *ctx, bool (*parse_fun)(struct context *ctx),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static void
|
||||||
|
test_two_colors(struct context *ctx, bool (*parse_fun)(struct context *ctx),
|
||||||
|
const char *key, bool alpha_allowed,
|
||||||
|
uint32_t *ptr1, uint32_t *ptr2)
|
||||||
|
{
|
||||||
|
ctx->key = key;
|
||||||
|
|
||||||
|
const struct {
|
||||||
|
const char *option_string;
|
||||||
|
uint32_t color1;
|
||||||
|
uint32_t color2;
|
||||||
|
bool invalid;
|
||||||
|
} input[] = {
|
||||||
|
{"000000 000000", 0, 0},
|
||||||
|
|
||||||
|
/* No alpha */
|
||||||
|
{"999999 888888", 0x999999, 0x888888},
|
||||||
|
{"ffffff aaaaaa", 0xffffff, 0xaaaaaa},
|
||||||
|
|
||||||
|
/* Both colors have alpha component */
|
||||||
|
{"ffffffff 00000000", 0xffffffff, 0x00000000, !alpha_allowed},
|
||||||
|
{"aabbccdd, ee112233", 0xaabbccdd, 0xee112233, !alpha_allowed},
|
||||||
|
|
||||||
|
/* Only one color has alpha component */
|
||||||
|
{"ffffffff 112233", 0xffffffff, 0x112233, !alpha_allowed},
|
||||||
|
{"ffffff ff112233", 0x00ffffff, 0xff112233, !alpha_allowed},
|
||||||
|
|
||||||
|
{"unittest-invalid-color", 0, 0, true},
|
||||||
|
};
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (!parse_fun(ctx)) {
|
||||||
|
BUG("[%s].%s=%s: failed to parse",
|
||||||
|
ctx->section, ctx->key, ctx->value);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
test_section_main(void)
|
test_section_main(void)
|
||||||
{
|
{
|
||||||
|
|
@ -412,6 +458,7 @@ test_section_main(void)
|
||||||
test_string(&ctx, &parse_section_main, "shell", &conf.shell);
|
test_string(&ctx, &parse_section_main, "shell", &conf.shell);
|
||||||
test_string(&ctx, &parse_section_main, "term", &conf.term);
|
test_string(&ctx, &parse_section_main, "term", &conf.term);
|
||||||
test_string(&ctx, &parse_section_main, "app-id", &conf.app_id);
|
test_string(&ctx, &parse_section_main, "app-id", &conf.app_id);
|
||||||
|
test_string(&ctx, &parse_section_main, "utempter", &conf.utempter_path);
|
||||||
|
|
||||||
test_c32string(&ctx, &parse_section_main, "word-delimiters", &conf.word_delimiters);
|
test_c32string(&ctx, &parse_section_main, "word-delimiters", &conf.word_delimiters);
|
||||||
|
|
||||||
|
|
@ -424,6 +471,7 @@ test_section_main(void)
|
||||||
test_pt_or_px(&ctx, &parse_section_main, "letter-spacing", &conf.letter_spacing);
|
test_pt_or_px(&ctx, &parse_section_main, "letter-spacing", &conf.letter_spacing);
|
||||||
test_pt_or_px(&ctx, &parse_section_main, "horizontal-letter-offset", &conf.horizontal_letter_offset);
|
test_pt_or_px(&ctx, &parse_section_main, "horizontal-letter-offset", &conf.horizontal_letter_offset);
|
||||||
test_pt_or_px(&ctx, &parse_section_main, "vertical-letter-offset", &conf.vertical_letter_offset);
|
test_pt_or_px(&ctx, &parse_section_main, "vertical-letter-offset", &conf.vertical_letter_offset);
|
||||||
|
test_pt_or_px(&ctx, &parse_section_main, "underline-thickness", &conf.underline_thickness);
|
||||||
|
|
||||||
test_uint16(&ctx, &parse_section_main, "resize-delay-ms", &conf.resize_delay_ms);
|
test_uint16(&ctx, &parse_section_main, "resize-delay-ms", &conf.resize_delay_ms);
|
||||||
test_uint16(&ctx, &parse_section_main, "workers", &conf.render_worker_count);
|
test_uint16(&ctx, &parse_section_main, "workers", &conf.render_worker_count);
|
||||||
|
|
@ -616,6 +664,18 @@ test_section_colors(void)
|
||||||
test_color(&ctx, &parse_section_colors, "selection-foreground", false, &conf.colors.selection_fg);
|
test_color(&ctx, &parse_section_colors, "selection-foreground", false, &conf.colors.selection_fg);
|
||||||
test_color(&ctx, &parse_section_colors, "selection-background", false, &conf.colors.selection_bg);
|
test_color(&ctx, &parse_section_colors, "selection-background", false, &conf.colors.selection_bg);
|
||||||
test_color(&ctx, &parse_section_colors, "urls", false, &conf.colors.url);
|
test_color(&ctx, &parse_section_colors, "urls", false, &conf.colors.url);
|
||||||
|
test_two_colors(&ctx, &parse_section_colors, "jump-labels", false,
|
||||||
|
&conf.colors.jump_label.fg,
|
||||||
|
&conf.colors.jump_label.bg);
|
||||||
|
test_two_colors(&ctx, &parse_section_colors, "scrollback-indicator", false,
|
||||||
|
&conf.colors.scrollback_indicator.fg,
|
||||||
|
&conf.colors.scrollback_indicator.bg);
|
||||||
|
test_two_colors(&ctx, &parse_section_colors, "search-box-no-match", false,
|
||||||
|
&conf.colors.search_box.no_match.fg,
|
||||||
|
&conf.colors.search_box.no_match.bg);
|
||||||
|
test_two_colors(&ctx, &parse_section_colors, "search-box-match", false,
|
||||||
|
&conf.colors.search_box.match.fg,
|
||||||
|
&conf.colors.search_box.match.bg);
|
||||||
|
|
||||||
for (size_t i = 0; i < 255; i++) {
|
for (size_t i = 0; i < 255; i++) {
|
||||||
char key_name[4];
|
char key_name[4];
|
||||||
|
|
@ -627,8 +687,6 @@ test_section_colors(void)
|
||||||
test_invalid_key(&ctx, &parse_section_colors, "256");
|
test_invalid_key(&ctx, &parse_section_colors, "256");
|
||||||
|
|
||||||
/* TODO: alpha (float in range 0-1, converted to uint16_t) */
|
/* TODO: alpha (float in range 0-1, converted to uint16_t) */
|
||||||
/* TODO: jump-labels (two colors) */
|
|
||||||
/* TODO: scrollback-indicator (two colors) */
|
|
||||||
|
|
||||||
config_free(&conf);
|
config_free(&conf);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
29
themes/moonfly
Normal file
29
themes/moonfly
Normal file
|
|
@ -0,0 +1,29 @@
|
||||||
|
# moonfly
|
||||||
|
# Based on https://github.com/bluz71/vim-moonfly-colors
|
||||||
|
|
||||||
|
[cursor]
|
||||||
|
color = 080808 9e9e9e
|
||||||
|
|
||||||
|
[colors]
|
||||||
|
foreground = b2b2b2
|
||||||
|
background = 080808
|
||||||
|
selection-foreground = 080808
|
||||||
|
selection-background = b2ceee
|
||||||
|
|
||||||
|
regular0 = 323437
|
||||||
|
regular1 = ff5454
|
||||||
|
regular2 = 8cc85f
|
||||||
|
regular3 = e3c78a
|
||||||
|
regular4 = 80a0ff
|
||||||
|
regular5 = d183e8
|
||||||
|
regular6 = 79dac8
|
||||||
|
regular7 = c6c6c6
|
||||||
|
|
||||||
|
bright0 = 949494
|
||||||
|
bright1 = ff5189
|
||||||
|
bright2 = 36c692
|
||||||
|
bright3 = c2c292
|
||||||
|
bright4 = 74b2ff
|
||||||
|
bright5 = ae81ff
|
||||||
|
bright6 = 85dc85
|
||||||
|
bright7 = e4e4e4
|
||||||
29
themes/nightfly
Normal file
29
themes/nightfly
Normal file
|
|
@ -0,0 +1,29 @@
|
||||||
|
# nightfly
|
||||||
|
# Based on https://github.com/bluz71/vim-nightfly-guicolors
|
||||||
|
|
||||||
|
[cursor]
|
||||||
|
color = 080808 9ca1aa
|
||||||
|
|
||||||
|
[colors]
|
||||||
|
foreground = acb4c2
|
||||||
|
background = 011627
|
||||||
|
selection-foreground = 080808
|
||||||
|
selection-background = b2ceee
|
||||||
|
|
||||||
|
regular0 = 1d3b53
|
||||||
|
regular1 = fc514e
|
||||||
|
regular2 = a1cd5e
|
||||||
|
regular3 = e3d18a
|
||||||
|
regular4 = 82aaff
|
||||||
|
regular5 = c792ea
|
||||||
|
regular6 = 7fdbca
|
||||||
|
regular7 = a1aab8
|
||||||
|
|
||||||
|
bright0 = 7c8f8f
|
||||||
|
bright1 = ff5874
|
||||||
|
bright2 = 21c7a8
|
||||||
|
bright3 = ecc48d
|
||||||
|
bright4 = 82aaff
|
||||||
|
bright5 = ae81ff
|
||||||
|
bright6 = ae81ff
|
||||||
|
bright7 = d6deeb
|
||||||
106
unicode-mode.c
Normal file
106
unicode-mode.c
Normal file
|
|
@ -0,0 +1,106 @@
|
||||||
|
#include "unicode-mode.h"
|
||||||
|
|
||||||
|
#define LOG_MODULE "unicode-input"
|
||||||
|
#define LOG_ENABLE_DBG 0
|
||||||
|
#include "log.h"
|
||||||
|
#include "render.h"
|
||||||
|
#include "search.h"
|
||||||
|
|
||||||
|
void
|
||||||
|
unicode_mode_activate(struct seat *seat)
|
||||||
|
{
|
||||||
|
if (seat->unicode_mode.active)
|
||||||
|
return;
|
||||||
|
|
||||||
|
seat->unicode_mode.active = true;
|
||||||
|
seat->unicode_mode.character = u'\0';
|
||||||
|
seat->unicode_mode.count = 0;
|
||||||
|
unicode_mode_updated(seat);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
unicode_mode_deactivate(struct seat *seat)
|
||||||
|
{
|
||||||
|
if (!seat->unicode_mode.active)
|
||||||
|
return;
|
||||||
|
|
||||||
|
seat->unicode_mode.active = false;
|
||||||
|
unicode_mode_updated(seat);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
unicode_mode_updated(struct seat *seat)
|
||||||
|
{
|
||||||
|
struct terminal *term = seat->kbd_focus;
|
||||||
|
if (term == NULL)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (term->is_searching)
|
||||||
|
render_refresh_search(term);
|
||||||
|
else
|
||||||
|
render_refresh(term);
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
unicode_mode_input(struct seat *seat, struct terminal *term,
|
||||||
|
xkb_keysym_t sym)
|
||||||
|
{
|
||||||
|
if (sym == XKB_KEY_Return ||
|
||||||
|
sym == XKB_KEY_space ||
|
||||||
|
sym == XKB_KEY_KP_Enter ||
|
||||||
|
sym == XKB_KEY_KP_Space)
|
||||||
|
{
|
||||||
|
char utf8[MB_CUR_MAX];
|
||||||
|
size_t chars = c32rtomb(
|
||||||
|
utf8, seat->unicode_mode.character, &(mbstate_t){0});
|
||||||
|
|
||||||
|
LOG_DBG("Unicode input: 0x%06x -> %.*s",
|
||||||
|
seat->unicode_mode.character, (int)chars, utf8);
|
||||||
|
|
||||||
|
if (chars != (size_t)-1) {
|
||||||
|
if (term->is_searching)
|
||||||
|
search_add_chars(term, utf8, chars);
|
||||||
|
else
|
||||||
|
term_to_slave(term, utf8, chars);
|
||||||
|
}
|
||||||
|
|
||||||
|
unicode_mode_deactivate(seat);
|
||||||
|
}
|
||||||
|
|
||||||
|
else if (sym == XKB_KEY_Escape ||
|
||||||
|
sym == XKB_KEY_q ||
|
||||||
|
(seat->kbd.ctrl && (sym == XKB_KEY_c ||
|
||||||
|
sym == XKB_KEY_d ||
|
||||||
|
sym == XKB_KEY_g)))
|
||||||
|
{
|
||||||
|
unicode_mode_deactivate(seat);
|
||||||
|
}
|
||||||
|
|
||||||
|
else if (sym == XKB_KEY_BackSpace) {
|
||||||
|
if (seat->unicode_mode.count > 0) {
|
||||||
|
seat->unicode_mode.character >>= 4;
|
||||||
|
seat->unicode_mode.count--;
|
||||||
|
unicode_mode_updated(seat);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
else if (seat->unicode_mode.count < 6) {
|
||||||
|
int digit = -1;
|
||||||
|
|
||||||
|
/* 0-9, a-f, A-F */
|
||||||
|
if (sym >= XKB_KEY_0 && sym <= XKB_KEY_9)
|
||||||
|
digit = sym - XKB_KEY_0;
|
||||||
|
else if (sym >= XKB_KEY_a && sym <= XKB_KEY_f)
|
||||||
|
digit = 0xa + (sym - XKB_KEY_a);
|
||||||
|
else if (sym >= XKB_KEY_A && sym <= XKB_KEY_F)
|
||||||
|
digit = 0xa + (sym - XKB_KEY_A);
|
||||||
|
|
||||||
|
if (digit >= 0) {
|
||||||
|
xassert(digit >= 0 && digit <= 0xf);
|
||||||
|
seat->unicode_mode.character <<= 4;
|
||||||
|
seat->unicode_mode.character |= digit;
|
||||||
|
seat->unicode_mode.count++;
|
||||||
|
unicode_mode_updated(seat);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
11
unicode-mode.h
Normal file
11
unicode-mode.h
Normal file
|
|
@ -0,0 +1,11 @@
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <xkbcommon/xkbcommon-keysyms.h>
|
||||||
|
|
||||||
|
#include "wayland.h"
|
||||||
|
|
||||||
|
void unicode_mode_activate(struct seat *seat);
|
||||||
|
void unicode_mode_deactivate(struct seat *seat);
|
||||||
|
void unicode_mode_updated(struct seat *seat);
|
||||||
|
void unicode_mode_input(struct seat *seat, struct terminal *term,
|
||||||
|
xkb_keysym_t sym);
|
||||||
16
url-mode.c
16
url-mode.c
|
|
@ -677,18 +677,23 @@ urls_assign_key_combos(const struct config *conf, url_list_t *urls)
|
||||||
if (count == 0)
|
if (count == 0)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
uint64_t seen_ids[count];
|
|
||||||
char32_t *combos[count];
|
char32_t *combos[count];
|
||||||
generate_key_combos(conf, count, combos);
|
generate_key_combos(conf, count, combos);
|
||||||
|
|
||||||
size_t combo_idx = 0;
|
size_t combo_idx = 0;
|
||||||
size_t id_idx = 0;
|
|
||||||
|
|
||||||
tll_foreach(*urls, it) {
|
tll_foreach(*urls, it) {
|
||||||
bool id_already_seen = false;
|
bool id_already_seen = false;
|
||||||
|
|
||||||
for (size_t i = 0; i < id_idx; i++) {
|
/* Look for already processed URLs where both the URI and the
|
||||||
if (it->item.id == seen_ids[i]) {
|
* ID matches */
|
||||||
|
tll_foreach(*urls, it2) {
|
||||||
|
if (&it->item == &it2->item)
|
||||||
|
break;
|
||||||
|
|
||||||
|
if (it->item.id == it2->item.id &&
|
||||||
|
strcmp(it->item.url, it2->item.url) == 0)
|
||||||
|
{
|
||||||
id_already_seen = true;
|
id_already_seen = true;
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
@ -696,7 +701,6 @@ urls_assign_key_combos(const struct config *conf, url_list_t *urls)
|
||||||
|
|
||||||
if (id_already_seen)
|
if (id_already_seen)
|
||||||
continue;
|
continue;
|
||||||
seen_ids[id_idx++] = it->item.id;
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* Scan previous URLs, and check if *this* URL matches any of
|
* Scan previous URLs, and check if *this* URL matches any of
|
||||||
|
|
@ -730,7 +734,7 @@ urls_assign_key_combos(const struct config *conf, url_list_t *urls)
|
||||||
char *key = ac32tombs(it->item.key);
|
char *key = ac32tombs(it->item.key);
|
||||||
xassert(key != NULL);
|
xassert(key != NULL);
|
||||||
|
|
||||||
LOG_DBG("URL: %s (%s)", it->item.url, key);
|
LOG_DBG("URL: %s (key=%s, id=%"PRIu64")", it->item.url, key, it->item.id);
|
||||||
free(key);
|
free(key);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
|
||||||
61
wayland.c
61
wayland.c
|
|
@ -367,7 +367,7 @@ update_terms_on_monitor(struct monitor *mon)
|
||||||
static void
|
static void
|
||||||
output_update_ppi(struct monitor *mon)
|
output_update_ppi(struct monitor *mon)
|
||||||
{
|
{
|
||||||
if (mon->dim.mm.width == 0 || mon->dim.mm.height == 0)
|
if (mon->dim.mm.width <= 0 || mon->dim.mm.height <= 0)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
int x_inches = mon->dim.mm.width * 0.03937008;
|
int x_inches = mon->dim.mm.width * 0.03937008;
|
||||||
|
|
@ -706,9 +706,50 @@ xdg_toplevel_close(void *data, struct xdg_toplevel *xdg_toplevel)
|
||||||
term_shutdown(term);
|
term_shutdown(term);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if defined(XDG_TOPLEVEL_CONFIGURE_BOUNDS_SINCE_VERSION)
|
||||||
|
static void
|
||||||
|
xdg_toplevel_configure_bounds(void *data,
|
||||||
|
struct xdg_toplevel *xdg_toplevel,
|
||||||
|
int32_t width, int32_t height)
|
||||||
|
{
|
||||||
|
/* TODO: ensure we don't pick a bigger size */
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if defined(XDG_TOPLEVEL_WM_CAPABILITIES_SINCE_VERSION)
|
||||||
|
static void
|
||||||
|
xdg_toplevel_wm_capabilities(void *data,
|
||||||
|
struct xdg_toplevel *xdg_toplevel,
|
||||||
|
struct wl_array *caps)
|
||||||
|
{
|
||||||
|
struct wl_window *win = data;
|
||||||
|
|
||||||
|
win->wm_capabilities.maximize = false;
|
||||||
|
win->wm_capabilities.minimize = false;
|
||||||
|
|
||||||
|
uint32_t *cap_ptr;
|
||||||
|
wl_array_for_each(cap_ptr, caps) {
|
||||||
|
switch (*cap_ptr) {
|
||||||
|
case XDG_TOPLEVEL_WM_CAPABILITIES_MAXIMIZE:
|
||||||
|
win->wm_capabilities.maximize = true;
|
||||||
|
break;
|
||||||
|
case XDG_TOPLEVEL_WM_CAPABILITIES_MINIMIZE:
|
||||||
|
win->wm_capabilities.minimize = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
static const struct xdg_toplevel_listener xdg_toplevel_listener = {
|
static const struct xdg_toplevel_listener xdg_toplevel_listener = {
|
||||||
.configure = &xdg_toplevel_configure,
|
.configure = &xdg_toplevel_configure,
|
||||||
/*.close = */&xdg_toplevel_close, /* epoll-shim defines a macro ‘close’... */
|
/*.close = */&xdg_toplevel_close, /* epoll-shim defines a macro ‘close’... */
|
||||||
|
#if defined(XDG_TOPLEVEL_CONFIGURE_BOUNDS_SINCE_VERSION)
|
||||||
|
.configure_bounds = &xdg_toplevel_configure_bounds,
|
||||||
|
#endif
|
||||||
|
#if defined(XDG_TOPLEVEL_WM_CAPABILITIES_SINCE_VERSION)
|
||||||
|
.wm_capabilities = xdg_toplevel_wm_capabilities,
|
||||||
|
#endif
|
||||||
};
|
};
|
||||||
|
|
||||||
static void
|
static void
|
||||||
|
|
@ -896,13 +937,22 @@ handle_global(void *data, struct wl_registry *registry,
|
||||||
return;
|
return;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
* We *require* version 1, but _can_ use version 2. Version 2
|
* We *require* version 1, but _can_ use version 5. Version 2
|
||||||
* adds 'tiled' window states. We use that information to
|
* adds 'tiled' window states. We use that information to
|
||||||
* restore the window size when window is un-tiled.
|
* restore the window size when window is un-tiled. Version 5
|
||||||
|
* adds 'wm_capabilities'. We use that information to draw
|
||||||
|
* window decorations.
|
||||||
*/
|
*/
|
||||||
|
#if defined(XDG_TOPLEVEL_WM_CAPABILITIES_SINCE_VERSION)
|
||||||
|
const uint32_t preferred = XDG_TOPLEVEL_WM_CAPABILITIES_SINCE_VERSION;
|
||||||
|
#elif defined(XDG_TOPLEVEL_STATE_TILED_LEFT_SINCE_VERSION)
|
||||||
|
const uint32_t preferred = XDG_TOPLEVEL_STATE_TILED_LEFT_SINCE_VERSION;
|
||||||
|
#else
|
||||||
|
const uint32_t preferred = required;
|
||||||
|
#endif
|
||||||
|
|
||||||
wayl->shell = wl_registry_bind(
|
wayl->shell = wl_registry_bind(
|
||||||
wayl->registry, name, &xdg_wm_base_interface, min(version, 2));
|
wayl->registry, name, &xdg_wm_base_interface, min(version, preferred));
|
||||||
xdg_wm_base_add_listener(wayl->shell, &xdg_wm_base_listener, wayl);
|
xdg_wm_base_add_listener(wayl->shell, &xdg_wm_base_listener, wayl);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -1418,6 +1468,9 @@ wayl_win_init(struct terminal *term, const char *token)
|
||||||
win->csd.move_timeout_fd = -1;
|
win->csd.move_timeout_fd = -1;
|
||||||
win->resize_timeout_fd = -1;
|
win->resize_timeout_fd = -1;
|
||||||
|
|
||||||
|
win->wm_capabilities.maximize = true;
|
||||||
|
win->wm_capabilities.minimize = true;
|
||||||
|
|
||||||
win->surface = wl_compositor_create_surface(wayl->compositor);
|
win->surface = wl_compositor_create_surface(wayl->compositor);
|
||||||
if (win->surface == NULL) {
|
if (win->surface == NULL) {
|
||||||
LOG_ERR("failed to create wayland surface");
|
LOG_ERR("failed to create wayland surface");
|
||||||
|
|
|
||||||
11
wayland.h
11
wayland.h
|
|
@ -206,6 +206,12 @@ struct seat {
|
||||||
uint32_t serial;
|
uint32_t serial;
|
||||||
} ime;
|
} ime;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
struct {
|
||||||
|
bool active;
|
||||||
|
int count;
|
||||||
|
char32_t character;
|
||||||
|
} unicode_mode;
|
||||||
};
|
};
|
||||||
|
|
||||||
enum csd_surface {
|
enum csd_surface {
|
||||||
|
|
@ -333,6 +339,11 @@ struct wl_window {
|
||||||
uint32_t serial;
|
uint32_t serial;
|
||||||
} csd;
|
} csd;
|
||||||
|
|
||||||
|
struct {
|
||||||
|
bool maximize:1;
|
||||||
|
bool minimize:1;
|
||||||
|
} wm_capabilities;
|
||||||
|
|
||||||
struct wl_surf_subsurf search;
|
struct wl_surf_subsurf search;
|
||||||
struct wl_surf_subsurf scrollback_indicator;
|
struct wl_surf_subsurf scrollback_indicator;
|
||||||
struct wl_surf_subsurf render_timer;
|
struct wl_surf_subsurf render_timer;
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue