This option specifies the characters allowed in the auto-detected
URLs.
Any character not in this set constitutes an URL delimiter, and will
never be included in auto-detected URLs.
This option does not affect OSC-8 URLs.
Closes#654
If we have lots of URLs, we use up a *lot* of SHM buffers (and thus
pools). Each and every one is a single mmap(), of at least 4K.
Since all URL labels are created and destroyed at the same time, it
makes sense to use a single pool for all buffers.
To do this, we must now do two passes; first one to generate the
actual string (label content), and to calculate the label sizes.
Then we use this information to allocate all SHM buffers at once, from
the same pool.
Finally, we iterate the URLs again, this time to actually render them.
When tagging URL cells (in preparation for rendering URL mode), we
loop the URL’s entire range, setting the `url` attribute of all cells,
and dirtying the rows.
It is possible to create URLs that are invalid, and wrap around the
scrollback, even though the scrollback hasn’t yet been filled. For
example, by starting an OSC-8 URL, moving the cursor, and then closing
the OSC-8 URL.
These URLs are invalid, but are still rendered just fine. “Fine” being
relative - they will typically fill the entire screen. But at least
that’s a very clear indication for the user that’s something is wrong.
The problem is when we hit un-allocated scrollback rows. We didn’t
check for NULL rows, and crashed.
This has now been fixed.
Removing overlaping and duplicated URLs is done by running two nested
loops, that both iterate the same URL list.
When a duplicate is found, one of the URLs is destroyed and removed
from the list.
Deleting and removing an item *is* safe, but only as long as _no
other_ iterator has references to it.
In this case, if we remove an item from e.g. the inner iterator, we’ll
crash if the outer iterator’s *next* item is the item being removed.
Closes#627
Unlike other surface types, the SHM cookie depends on the address of
each URL instance. This means if we enable, disable, and then enable
URL mode again (thus showing exactly the same URLs as the first time),
the URLs will have new addresses, and thus the old SHM pixmaps will
not get purged automatically.
So, manually purge them when destroying the URLs.
When an auto-detected URL ended *on* the right-most column, the URL
endpoint was off by one, resulting in the underline in URL mode being
one character short.
echo -e '\e]8;;https://www.foo.bar\e\\https://www.foo\e]8;;\e\\.bar'
will produce an OSC-8 URL (https://www.foo) that is slightly shorter
than the auto-detected one (https://www.foo.bar).
This produces strange results in URL mode. For example, if
url.osc8-underline=always, the OSC8 underline will be removed when
url-mode is exited.
This patch changes the behavior so that auto-detected URLs that
overlap OSC-8 URLs are removed.
Note that OSC-8 URLs cannot overlap with each other, and that
auto-detected URLs also cannot overlap with each other.
The ‘proto_chars’ variable is kind of like a FIFO, where characters
from the grid is pushed to the back of it. I.e. the last <n> bytes are
at the *end* of the array.
This was what the protocol matching logic expected. However, it was
only true after reading ‘max_prot_len’ characters from the
grid. Before that, the last <n> bytes from the grid was in
the *beginning* of ‘proto_chars’.
This meant we did not detect URLs starting in the top left corner of
the viewport. Test case:
foot sh -c "echo http://test.com && sleep 9999"
Bindings are matched in one out of three ways:
* By translated (by XKB) symbols
* By untranslated symbols
* By raw key codes
A translated symbol is affected by pressed modifiers, some of which
can be “consumed”. Consumed modifiers to not partake in the comparison
with the binding’s modifiers. In this mode, ctrl+shift+2 maps to
ctrl+@ on a US layout.
Untranslated symbols, or un-shifted symbols refer to the “base” symbol
of the pressed key, i.e. it’s unaffected by modifiers. In this mode,
consumed modifiers *do* partake in the comparison with the binding’s
modifiers, and ctrl+shift+2 maps to ctrl+shift+2 on a US layout.
More examples: ctrl+shift+u maps to ctrl+U in the translated lookup,
while ctrl+shift+u maps to ctrl+shift+u in the untranslated lookup.
Finally, we also match raw key codes. This allows our bindings to work
using the same physical keys when the user switches between latin and
non-latin layouts.
This means key bindings in foot.ini *must* not include both +shift+
and a *shifted* key. I.e. ctrl+shift+U is not a valid combo as it
cannot be triggered. Unfortunately, this was how you were supposed to
write bindings up until now... so, we try to detect such bindings, log
a deprecation warning and then “fix” the binding for the user.
When specifying bindings in foot.ini, both ctrl+U and ctrl+shift+u are
valid, and will work. The latter is preferred though, since we cannot
detect the raw key code for the former variant. Personally, I also
prefer the latter one because it is more explicit; it’s more obvious
which keys are involved.
However, in some cases it makes more sense to use the other
variant. Typically for non-letter combos.
Clear scroll damage and damage the entire viewport before entering URL
mode. This will cause us to do a full screen redraw both when entering
URL mode, and later when exiting it.
Clearing the scroll damage is necessary to ensure we don’t apply it
twice (once for the snapshot:ed grid, and later again for the real
grid), as that would result in an incorrect pixmap.
But, since we’ve cleared the scroll damage, we need to damage the
entire view to ensure we redraw the contents correctly.
This ensures the “last cursor” cell is re-drawn (without a cursor, if
the cursor has moved), both in the snapshot:ed grid, and later, when
we switch back to the real grid.
We must also be careful and reset term->render.last_cursor.row
both when *entering* and *leaving* URL mode, to ensure it doesn’t
point to an invalid row.
Previously, we automatically exited URL mode whenever we received data
on the PTY. This was done since we don’t know _what_ has changed on
the screen, and we don’t want to display misleading jump labels.
However, this becomes a problem in curses-like applications that
periodically updates part of the screen. For example, a statusbar with
a clock.
This patch changes this behavior; instead of cancelling URL mode when
receiving PTY data, we snapshot the grid when entering URL mode.
When *rendering*, we use the snapshot:ed grid, while PTY updates
modify the “real” grid.
Snapshot:ing the grid means taking a full/deep copy of the current
grid, including sixel images etc.
Finally, it isn’t necessary to “damage” the entire view
when *entering* URL mode, since we’re at that point the renderer is in
sync with the grid. But we *do* need to damage the entire view when
exiting URL mode, since the grid changes on the “real” grid hasn’t
been tracked by the renderer.
This patch adds a new configuration option,
‘osc8-underline=url-mode|always’.
When set to ‘url-mode’, OSC-8 URLs are only
highlighted (i.e. underlined) in url-mode, just like auto-detected
URLs.
When set to ‘always’, they are always underlined, regardless of mode,
and regardless of their other attributes.
This is implemented by tagging collected URLs with a boolean,
instructing urls_render() and urls_reset() whether they should update
the cells’ ‘url’ attribute or not.
The OSC-8 collecter sets this based on the value of ‘osc8-underline’.
Finally, when closing an OSC-8 URL, the cells are immediately tagged
with the ‘url’ attribute if ‘osc8-underline’ is set to ‘always’.
By default, the URL isn’t shown on the jump-label. For auto-detect
URLs, doing so is virtually always useless, as the URL is already
visible in the grid.
For OSC-8 URLs however, the URL is often _not_ visible in the
grid. Many times, seeing the URL is still not needed (if you’re doing
‘ls --hyperlink’, you already know what the URIs are).
But it is still useful to have a way to show the URLs.
This patch adds a new key binding action that can be used in url-mode
to toggle the URL on and off in the jump label.
It is bound to ctrl+t by default.
Remove URLs with the same start and end coordinates. Such duplicate
URLs can be created by emitting an OSC-8 URL with matching grid
content:
\E]8;;http://foo\E\\http://foo\E]8;;\E\\
In case an URL is split up into multiple parts, those parts are now
treated as a single URL when it comes to key assignment.
Only the *first* URL part is actually assigned a key combo. The other
parts are ignored.
We still highlight them, but for all other purposes they are ignored.
Things left to do: since OSC-8 URLs are stored as ranges in the
per-row ‘extra’ data member, we currently do not handle line-wrapping
URLs very well; they will be considered two separate URLs, and
assigned two different key sequences.
The only time the URL is actually in UTF-32 is when we’re collecting
it (auto-detecting it) from the grid, since cells store their
character(s) in UTF-32.
Everything *after* that prefers the URL in UTF-8. So, do the
conversion while collecting the URL.
This patch also changes the URL activation code to strip the
‘file://user@host/’ prefix from file URIs that refer to files
on the *local* computer.
In many places we have the following pattern:
tll_foreach(list, it)
free(it->item.thing);
tll_free(list);
Since all tll functions are macros, and thus inlined, and since
tll_free in itself expands to a tll_foreach(), the above pattern
expands to more native code than necessary.
This is somewhat smaller:
tll_foreach(list, it) {
free(it->item.thing);
tll_remove(list, it);
}
Up until now, the various key binding modes (“normal”, “search” and
“url”) have used their own struct definitions for their key bindings.
The only reason for this was to have a properly typed “action” (using
the appropriate “action” enum).
This caused lots of duplicated code. This patch refactors this to use
a single struct definition for the “unparsed” key bindings handled by
the configuration, and another single definition for “parsed” bindings
used while handling input.
This allows us to implement configuration parsing, keymap translation
and so on using one set of functions, regardless of key binding mode.
* Use a do..while loop; this lets us drop the second half of the loop
condition.
* Call wcslen(prefix) once, *before* iterating the alphabet
characters.
* Step through the alphabet characters using a pointer, as this
avoids an indexed load (with possibly an imul instruction in
e.g. -Os builds).
* We were using the ‘back’ element of the list as prefix for the next
iteration of sequences, instead of the element at index ‘offset’
* ALEN() on a wchar_t[] includes the NULL terminator. We don’t want
that.
This is implemented by allocating one of the (few!) remaining bits in
the cells’ attribute struct to indicate the cell should be “URL
highlighted”.
render_cell() looks at this bit and draws an underline using the color
from colors.urls (defaults to regular3 - i.e. yellow).
A new function, url_tag_cells(), iterates the currently detected URLs
and sets the new ‘url’ attribute flag on the affected cells.
Note: this is done in a separate function to keep urls_collect() free
from as many dependencies as possible.
urls_reset() is updated to *clear* the ‘url’ flag (and thus implicitly
also results in a grid refresh, _if_ there were any URLs).
We now exit URL mode on *any* client application input. This needs to
be so since we can’t know if the URLs we previously detected are still
valid.
This allows us to update the jump label positions when the viewport
changes.
This in turn allows us to stay in URL mode while the user is using the
mouse to scroll in the scrollback history.
Scrolling with the keyboard is currently not possible, since input
handling in URL mode does not recognize “regular” key bindings. We
_could_ add scrollback up/down bindings to URL mode too, but lets not,
for the time being.
(Note: an alternative to this patch is to disallow mouse scrolling
too. Then we could have kept the URL start/end as viewport local
coordinates).