Script 'mail_helper' called by obssrc
Hello community,

here is the log from the commit of package foot for openSUSE:Factory checked in 
at 2021-11-23 22:10:31
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Comparing /work/SRC/openSUSE:Factory/foot (Old)
 and      /work/SRC/openSUSE:Factory/.foot.new.1895 (New)
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Package is "foot"

Tue Nov 23 22:10:31 2021 rev:10 rq:933175 version:1.10.1

Changes:
--------
--- /work/SRC/openSUSE:Factory/foot/foot.changes        2021-11-15 
15:28:29.653858682 +0100
+++ /work/SRC/openSUSE:Factory/.foot.new.1895/foot.changes      2021-11-23 
22:12:45.838442643 +0100
@@ -1,0 +2,15 @@
+Mon Nov 22 22:44:13 UTC 2021 - Arnav Singh <[email protected]>
+
+- Update to 1.10.1:
+  * Fixed bugs in parsing foot.ini for letter-spacing, pipe-* key bindings, etc
+    that were introduced in 1.10.0.
+  * Added XDG desktop file for footclient.
+  * See https://codeberg.org/dnkl/foot/releases/tag/1.10.1 for more details.
+
+-------------------------------------------------------------------
+Sun Nov 21 01:44:45 UTC 2021 - Arnav Singh <[email protected]>
+
+- Rename foot-direct-extra terminfo file to foot-extra-direct, to match
+  upstream's wishes.
+
+-------------------------------------------------------------------

Old:
----
  foot-1.10.0.tar.gz

New:
----
  foot-1.10.1.tar.gz

++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++

Other differences:
------------------
++++++ foot.spec ++++++
--- /var/tmp/diff_new_pack.BWw4oY/_old  2021-11-23 22:12:46.250441280 +0100
+++ /var/tmp/diff_new_pack.BWw4oY/_new  2021-11-23 22:12:46.250441280 +0100
@@ -17,7 +17,7 @@
 
 
 Name:           foot
-Version:        1.10.0
+Version:        1.10.1
 Release:        0
 Summary:        A Wayland terminal emulator
 License:        MIT
@@ -50,7 +50,7 @@
 %description extra-terminfo
 This package contains extra terminfo files for the foot terminal emulator
 that provide more features than the files in the terminfo-base package.
-Set term=foot-extra or term=foot-direct-extra in foot.ini to
+Set term=foot-extra or term=foot-extra-direct in foot.ini to
 take advantage of the files in this package.
 
 %prep
@@ -63,7 +63,7 @@
 %install
 %meson_install
 mv %{buildroot}/%{_datadir}/terminfo/f/foot 
%{buildroot}/%{_datadir}/terminfo/f/foot-extra
-mv %{buildroot}/%{_datadir}/terminfo/f/foot-direct 
%{buildroot}/%{_datadir}/terminfo/f/foot-direct-extra
+mv %{buildroot}/%{_datadir}/terminfo/f/foot-direct 
%{buildroot}/%{_datadir}/terminfo/f/foot-extra-direct
 
 %files
 %license LICENSE
@@ -71,6 +71,7 @@
 %{_bindir}/foot
 %{_bindir}/footclient
 %{_datadir}/applications/foot.desktop
+%{_datadir}/applications/footclient.desktop
 %{_datadir}/applications/foot-server.desktop
 %{_datadir}/bash-completion/
 %{_datadir}/doc/%{name}/
@@ -85,6 +86,6 @@
 
 %files extra-terminfo
 %{_datadir}/terminfo/f/foot-extra
-%{_datadir}/terminfo/f/foot-direct-extra
+%{_datadir}/terminfo/f/foot-extra-direct
 
 %changelog

++++++ foot-1.10.0.tar.gz -> foot-1.10.1.tar.gz ++++++
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/foot/.builds/alpine-x64.yml 
new/foot/.builds/alpine-x64.yml
--- old/foot/.builds/alpine-x64.yml     2021-11-14 18:12:30.000000000 +0100
+++ new/foot/.builds/alpine-x64.yml     2021-11-22 22:01:28.000000000 +0100
@@ -34,12 +34,12 @@
 tasks:
   - debug: |
       mkdir -p bld/debug
-      meson --buildtype=debug -Dgrapheme-clustering=enabled 
-Dfcft:text-shaping=enabled  -Dfcft:test-text-shaping=true foot bld/debug
+      meson --buildtype=debug -Dgrapheme-clustering=enabled 
-Dfcft:grapheme-shaping=enabled -Dfcft:run-shaping=enabled  
-Dfcft:test-text-shaping=true foot bld/debug
       ninja -C bld/debug -k0
       meson test -C bld/debug --print-errorlogs
   - release: |
       mkdir -p bld/release
-      meson --buildtype=minsize -Dgrapheme-clustering=enabled 
-Dfcft:text-shaping=enabled  -Dfcft:test-text-shaping=true foot bld/release
+      meson --buildtype=minsize -Dgrapheme-clustering=enabled 
-Dfcft:grapheme-shaping=enabled -Dfcft:run-shaping=enabled  
-Dfcft:test-text-shaping=true foot bld/release
       ninja -C bld/release -k0
       meson test -C bld/release --print-errorlogs
   - codespell: |
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/foot/.builds/alpine-x86.yml.disabled 
new/foot/.builds/alpine-x86.yml.disabled
--- old/foot/.builds/alpine-x86.yml.disabled    2021-11-14 18:12:30.000000000 
+0100
+++ new/foot/.builds/alpine-x86.yml.disabled    2021-11-22 22:01:28.000000000 
+0100
@@ -33,11 +33,11 @@
 tasks:
   - debug: |
       mkdir -p bld/debug
-      meson --buildtype=debug -Dgrapheme-clustering=enabled 
-Dfcft:text-shaping=enabled  -Dfcft:test-text-shaping=true foot bld/debug
+      meson --buildtype=debug -Dgrapheme-clustering=enabled 
-Dfcft:grapheme-shaping=enabled -Dfcft:run-shaping=enabled  
-Dfcft:test-text-shaping=true foot bld/debug
       ninja -C bld/debug -k0
       meson test -C bld/debug --print-errorlogs
   - release: |
       mkdir -p bld/release
-      meson --buildtype=minsize -Dgrapheme-clustering=enabled 
-Dfcft:text-shaping=enabled  -Dfcft:test-text-shaping=true foot bld/release
+      meson --buildtype=minsize -Dgrapheme-clustering=enabled 
-Dfcft:grapheme-shaping=enabled -Dfcft:run-shaping=enabled  
-Dfcft:test-text-shaping=true foot bld/release
       ninja -C bld/release -k0
       meson test -C bld/release --print-errorlogs
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/foot/.builds/freebsd-x64.yml 
new/foot/.builds/freebsd-x64.yml
--- old/foot/.builds/freebsd-x64.yml    2021-11-14 18:12:30.000000000 +0100
+++ new/foot/.builds/freebsd-x64.yml    2021-11-22 22:01:28.000000000 +0100
@@ -29,11 +29,11 @@
 tasks:
   - debug: |
       mkdir -p bld/debug
-      meson --buildtype=debug -Dterminfo=disabled 
-Dgrapheme-clustering=enabled -Dfcft:text-shaping=enabled  
-Dfcft:test-text-shaping=true foot bld/debug
+      meson --buildtype=debug -Dterminfo=disabled 
-Dgrapheme-clustering=enabled -Dfcft:grapheme-shaping=enabled 
-Dfcft:run-shaping=enabled -Dfcft:test-text-shaping=true foot bld/debug
       ninja -C bld/debug -k0
       meson test -C bld/debug --print-errorlogs
   - release: |
       mkdir -p bld/release
-      meson --buildtype=minsize -Dterminfo=disabled 
-Dgrapheme-clustering=enabled -Dfcft:text-shaping=enabled  
-Dfcft:test-text-shaping=true foot bld/release
+      meson --buildtype=minsize -Dterminfo=disabled 
-Dgrapheme-clustering=enabled -Dfcft:grapheme-shaping=enabled 
-Dfcft:run-shaping=enabled  -Dfcft:test-text-shaping=true foot bld/release
       ninja -C bld/release -k0
       meson test -C bld/release --print-errorlogs
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/foot/.gitlab-ci.yml new/foot/.gitlab-ci.yml
--- old/foot/.gitlab-ci.yml     2021-11-14 18:12:30.000000000 +0100
+++ new/foot/.gitlab-ci.yml     2021-11-22 22:01:28.000000000 +0100
@@ -19,7 +19,7 @@
   script:
     - mkdir -p bld/debug
     - cd bld/debug
-    - meson --buildtype=debug -Dgrapheme-clustering=enabled 
-Dfcft:text-shaping=enabled -Dfcft:test-text-shaping=true ../../
+    - meson --buildtype=debug -Dgrapheme-clustering=enabled 
-Dfcft:grapheme-shaping=enabled -Dfcft:run-shaping=enabled 
-Dfcft:test-text-shaping=true ../../
     - ninja -v -k0
     - ninja -v test
   artifacts:
@@ -46,7 +46,7 @@
   script:
     - mkdir -p bld/release
     - cd bld/release
-    - meson --buildtype=release -Dgrapheme-clustering=enabled 
-Dfcft:text-shaping=enabled  -Dfcft:test-text-shaping=true ../../
+    - meson --buildtype=release -Dgrapheme-clustering=enabled 
-Dfcft:grapheme-shaping=enabled -Dfcft:run-shaping=enabled  
-Dfcft:test-text-shaping=true ../../
     - ninja -v -k0
     - ninja -v test
   artifacts:
@@ -59,7 +59,7 @@
   script:
     - mkdir -p bld/debug
     - cd bld/debug
-    - meson --buildtype=debug -Dgrapheme-clustering=enabled 
-Dfcft:text-shaping=enabled  -Dfcft:test-text-shaping=true ../../
+    - meson --buildtype=debug -Dgrapheme-clustering=enabled 
-Dfcft:grapheme-shaping=enabled -Dfcft:run-shaping=enabled  
-Dfcft:test-text-shaping=true ../../
     - ninja -v -k0
     - ninja -v test
   artifacts:
@@ -72,7 +72,7 @@
   script:
     - mkdir -p bld/release
     - cd bld/release
-    - meson --buildtype=release -Dgrapheme-clustering=enabled 
-Dfcft:text-shaping=enabled  -Dfcft:test-text-shaping=true ../../
+    - meson --buildtype=release -Dgrapheme-clustering=enabled 
-Dfcft:grapheme-shaping=enabled -Dfcft:run-shaping=enabled  
-Dfcft:test-text-shaping=true ../../
     - ninja -v -k0
     - ninja -v test
   artifacts:
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/foot/.woodpecker.yml new/foot/.woodpecker.yml
--- old/foot/.woodpecker.yml    2021-11-14 18:12:30.000000000 +0100
+++ new/foot/.woodpecker.yml    2021-11-22 22:01:28.000000000 +0100
@@ -43,7 +43,7 @@
       # Debug
       - mkdir -p bld/debug-x64
       - cd bld/debug-x64
-      - meson --buildtype=debug -Dgrapheme-clustering=enabled 
-Dfcft:text-shaping=enabled -Dfcft:test-text-shaping=true ../..
+      - meson --buildtype=debug -Dgrapheme-clustering=enabled 
-Dfcft:grapheme-shaping=enabled -Dfcft:run-shaping=enabled 
-Dfcft:test-text-shaping=true ../..
       - ninja -v -k0
       - ninja -v test
       - ./foot --version
@@ -53,7 +53,7 @@
       # Release
       - mkdir -p bld/release-x64
       - cd bld/release-x64
-      - meson --buildtype=release -Dgrapheme-clustering=enabled 
-Dfcft:text-shaping=enabled  -Dfcft:test-text-shaping=true ../..
+      - meson --buildtype=release -Dgrapheme-clustering=enabled 
-Dfcft:grapheme-shaping=enabled -Dfcft:run-shaping=enabled  
-Dfcft:test-text-shaping=true ../..
       - ninja -v -k0
       - ninja -v test
       - ./foot --version
@@ -90,7 +90,7 @@
       # Debug
       - mkdir -p bld/debug-x86
       - cd bld/debug-x86
-      - meson --buildtype=debug -Dgrapheme-clustering=enabled 
-Dfcft:text-shaping=enabled  -Dfcft:test-text-shaping=true ../..
+      - meson --buildtype=debug -Dgrapheme-clustering=enabled 
-Dfcft:grapheme-shaping=enabled -Dfcft:run-shaping=enabled 
-Dfcft:test-text-shaping=true ../..
       - ninja -v -k0
       - ninja -v test
       - ./foot --version
@@ -100,7 +100,7 @@
       # Release
       - mkdir -p bld/release-x86
       - cd bld/release-x86
-      - meson --buildtype=release -Dgrapheme-clustering=enabled 
-Dfcft:text-shaping=enabled  -Dfcft:test-text-shaping=true ../..
+      - meson --buildtype=release -Dgrapheme-clustering=enabled 
-Dfcft:grapheme-shaping=enabled -Dfcft:run-shaping=enabled 
-Dfcft:test-text-shaping=true ../..
       - ninja -v -k0
       - ninja -v test
       - ./foot --version
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/foot/CHANGELOG.md new/foot/CHANGELOG.md
--- old/foot/CHANGELOG.md       2021-11-14 18:12:30.000000000 +0100
+++ new/foot/CHANGELOG.md       2021-11-22 22:01:28.000000000 +0100
@@ -1,5 +1,6 @@
 # Changelog
 
+* [1.10.1](#1-10-1)
 * [1.10.0](#1-10-0)
 * [1.9.2](#1-9-2)
 * [1.9.1](#1-9-1)
@@ -32,6 +33,35 @@
 * [1.2.0](#1-2-0)
 
 
+## 1.10.1
+
+### Added
+
+* `-Dthemes=false|true` meson command line option. When disabled,
+  example theme files are **not** installed.
+* XDG desktop file for footclient.
+
+
+### Fixed
+
+* Regression: `letter-spacing` resulting in a ???not a valid option???
+  error (https://codeberg.org/dnkl/foot/issues/795).
+* Regression: bad section name in configuration error messages.
+* Regression: `pipe-*` key bindings not being parsed correctly,
+  resulting in invalid error messages
+  (https://codeberg.org/dnkl/foot/issues/809).
+* OSC-8 data not being cleared when cell is overwritten
+  (https://codeberg.org/dnkl/foot/issues/804).
+
+
+### Contributors
+
+* Arnavion
+* Craig Barnes
+* Soc Virnyl Silab Estela
+* Xiretza
+
+
 ## 1.10.0
 
 ### Added
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/foot/config.c new/foot/config.c
--- old/foot/config.c   2021-11-14 18:12:30.000000000 +0100
+++ new/foot/config.c   2021-11-22 22:01:28.000000000 +0100
@@ -27,6 +27,7 @@
 #include "util.h"
 #include "wayland.h"
 #include "xmalloc.h"
+#include "xsnprintf.h"
 
 static const uint32_t default_foreground = 0xdcdccc;
 static const uint32_t default_background = 0x111111;
@@ -604,11 +605,12 @@
     }
 
     const size_t size = str_len + count * 4 + 1;
-    char *valid_values = xmalloc(size);
-    int idx = 0;
+    char valid_values[512];
+    size_t idx = 0;
+    xassert(size < sizeof(valid_values));
 
     for (size_t i = 0; i < count; i++)
-        idx += snprintf(&valid_values[idx], size - idx, "'%s', ", 
value_map[i]);
+        idx += xsnprintf(&valid_values[idx], size - idx, "'%s', ", 
value_map[i]);
 
     if (count > 0)
         valid_values[idx - 2] = '\0';
@@ -956,7 +958,7 @@
         return value_to_pt_or_px(ctx, &conf->line_height);
 
     else if (strcmp(key, "letter-spacing") == 0)
-        value_to_pt_or_px(ctx, &conf->letter_spacing);
+        return value_to_pt_or_px(ctx, &conf->letter_spacing);
 
     else if (strcmp(key, "horizontal-letter-offset") == 0)
         return value_to_pt_or_px(ctx, &conf->horizontal_letter_offset);
@@ -1012,8 +1014,6 @@
         LOG_CONTEXTUAL_ERR("not a valid option: %s", key);
         return false;
     }
-
-    UNREACHABLE();
 }
 
 static bool
@@ -1557,11 +1557,40 @@
     return false;
 }
 
+static int
+argv_compare(const struct argv *argv1, const struct argv *argv2)
+{
+    if (argv1->args == NULL && argv2->args == NULL)
+        return 0;
+
+    if (argv1->args == NULL)
+        return -1;
+    if (argv2->args == NULL)
+        return 1;
+
+    for (size_t i = 0; ; i++) {
+        if (argv1->args[i] == NULL && argv2->args[i] == NULL)
+            return 0;
+        if (argv1->args[i] == NULL)
+            return -1;
+        if (argv2->args[i] == NULL)
+            return 1;
+
+        int ret = strcmp(argv1->args[i], argv2->args[i]);
+        if (ret != 0)
+            return ret;
+    }
+
+    BUG("unexpected loop break");
+    return 1;
+}
+
 static bool
 has_key_binding_collisions(struct context *ctx,
                            int action, const char *const action_map[],
                            const struct config_key_binding_list *bindings,
-                           const struct key_combo_list *key_combos)
+                           const struct key_combo_list *key_combos,
+                           const struct argv *pipe_argv)
 {
     for (size_t j = 0; j < bindings->count; j++) {
         const struct config_key_binding *combo1 = &bindings->arr[j];
@@ -1569,8 +1598,10 @@
         if (combo1->action == BIND_ACTION_NONE)
             continue;
 
-        if (combo1->action == action)
-            continue;
+        if (combo1->action == action) {
+            if (argv_compare(&combo1->pipe.argv, pipe_argv) == 0)
+                continue;
+        }
 
         for (size_t i = 0; i < key_combos->count; i++) {
             const struct key_combo *combo2 = &key_combos->combos[i];
@@ -1600,29 +1631,6 @@
     return false;
 }
 
-static int
-argv_compare(char *const *argv1, char *const *argv2)
-{
-    xassert(argv1 != NULL);
-    xassert(argv2 != NULL);
-
-    for (size_t i = 0; ; i++) {
-        if (argv1[i] == NULL && argv2[i] == NULL)
-            return 0;
-        if (argv1[i] == NULL)
-            return -1;
-        if (argv2[i] == NULL)
-            return 1;
-
-        int ret = strcmp(argv1[i], argv2[i]);
-        if (ret != 0)
-            return ret;
-    }
-
-    BUG("unexpected loop break");
-    return 1;
-}
-
 /*
  * Parses a key binding value on the form
  *  "[cmd-to-exec arg1 arg2] Mods+Key"
@@ -1637,17 +1645,17 @@
  *          filled with {'cmd-to-exec', 'arg1', 'arg2', NULL}
  *
  * Returns:
- *  - ssize_t, number of bytes to strip from 'value' to remove the '[]'
+ *  - ssize_t, number of bytes that were stripped from 'value' to remove the 
'[]'
  *    enclosed cmd and its arguments, including any subsequent
  *    whitespace characters. I.e. if 'value' is "[cmd] BTN_RIGHT", the
  *    return value is 6 (strlen("[cmd] ")).
  *  - cmd: allocated string containing "cmd arg1 arg2...". Caller frees.
- *  - argv: allocatd array containing {"cmd", "arg1", "arg2", NULL}. Caller 
frees.
+ *  - argv: allocated array containing {"cmd", "arg1", "arg2", NULL}. Caller 
frees.
  */
 static ssize_t
-pipe_argv_from_value(struct context *ctx, char ***argv)
+pipe_argv_from_value(struct context *ctx, struct argv *argv)
 {
-    *argv = NULL;
+    argv->args = NULL;
 
     if (ctx->value[0] != '[')
         return 0;
@@ -1661,7 +1669,7 @@
     size_t pipe_len = pipe_cmd_end - ctx->value - 1;
     char *cmd = xstrndup(&ctx->value[1], pipe_len);
 
-    if (!tokenize_cmdline(cmd, argv)) {
+    if (!tokenize_cmdline(cmd, &argv->args)) {
         LOG_CONTEXTUAL_ERR("syntax error in command line");
         free(cmd);
         return -1;
@@ -1680,7 +1688,7 @@
 
 static void NOINLINE
 remove_action_from_key_bindings_list(struct config_key_binding_list *bindings,
-                                     int action, char **pipe_argv)
+                                     int action, const struct argv *pipe_argv)
 {
     size_t remove_first_idx = 0;
     size_t remove_count = 0;
@@ -1688,11 +1696,10 @@
     for (size_t i = 0; i < bindings->count; i++) {
         struct config_key_binding *binding = &bindings->arr[i];
 
-        if (binding->action == action &&
-            ((binding->pipe.argv.args == NULL && pipe_argv == NULL) ||
-             (binding->pipe.argv.args != NULL && pipe_argv != NULL &&
-              argv_compare(binding->pipe.argv.args, pipe_argv) == 0)))
-        {
+        if (binding->action != action)
+            continue;
+
+        if (argv_compare(&binding->pipe.argv, pipe_argv) == 0) {
             if (remove_count++ == 0)
                 remove_first_idx = i;
 
@@ -1721,14 +1728,12 @@
                           const char *const action_map[static action_count],
                           struct config_key_binding_list *bindings)
 {
-    char **pipe_argv;
+    struct argv pipe_argv;
 
     ssize_t pipe_remove_len = pipe_argv_from_value(ctx, &pipe_argv);
     if (pipe_remove_len < 0)
         return false;
 
-    ctx->value += pipe_remove_len;
-
     for (int action = 0; action < action_count; action++) {
         if (action_map[action] == NULL)
             continue;
@@ -1738,22 +1743,22 @@
 
         /* Unset binding */
         if (strcasecmp(ctx->value, "none") == 0) {
-            remove_action_from_key_bindings_list(bindings, action, pipe_argv);
-            free(pipe_argv);
+            remove_action_from_key_bindings_list(bindings, action, &pipe_argv);
+            free_argv(&pipe_argv);
             return true;
         }
 
         struct key_combo_list key_combos = {0};
         if (!value_to_key_combos(ctx, &key_combos) ||
             has_key_binding_collisions(
-                ctx, action, action_map, bindings, &key_combos))
+                ctx, action, action_map, bindings, &key_combos, &pipe_argv))
         {
-            free(pipe_argv);
+            free_argv(&pipe_argv);
             free_key_combo_list(&key_combos);
             return false;
         }
 
-        remove_action_from_key_bindings_list(bindings, action, pipe_argv);
+        remove_action_from_key_bindings_list(bindings, action, &pipe_argv);
 
         /* Emit key bindings */
         size_t ofs = bindings->count;
@@ -1769,9 +1774,7 @@
                 .modifiers = combo->modifiers,
                 .sym = combo->sym,
                 .pipe = {
-                    .argv = {
-                        .args = pipe_argv,
-                    },
+                    .argv = pipe_argv,
                     .master_copy = first,
                 },
             };
@@ -1786,7 +1789,7 @@
     }
 
     LOG_CONTEXTUAL_ERR("not a valid action: %s", ctx->key);
-    free(pipe_argv);
+    free_argv(&pipe_argv);
     return false;
 }
 
@@ -2099,14 +2102,12 @@
     const char *key = ctx->key;
     const char *value = ctx->value;
 
-    char **pipe_argv;
+    struct argv pipe_argv;
 
     ssize_t pipe_remove_len = pipe_argv_from_value(ctx, &pipe_argv);
     if (pipe_remove_len < 0)
         return false;
 
-    value += pipe_remove_len;
-
     for (enum bind_action_normal action = 0;
          action < BIND_ACTION_COUNT;
          action++)
@@ -2129,7 +2130,7 @@
                     binding->action = BIND_ACTION_NONE;
                 }
             }
-            free(pipe_argv);
+            free_argv(&pipe_argv);
             return true;
         }
 
@@ -2137,7 +2138,7 @@
         if (!value_to_mouse_combos(ctx, &key_combos) ||
             has_mouse_binding_collisions(ctx, &key_combos))
         {
-            free(pipe_argv);
+            free_argv(&pipe_argv);
             free_key_combo_list(&key_combos);
             return false;
         }
@@ -2146,11 +2147,10 @@
         for (size_t i = 0; i < conf->bindings.mouse.count; i++) {
             struct config_mouse_binding *binding = 
&conf->bindings.mouse.arr[i];
 
-            if (binding->action == action &&
-                ((binding->pipe.argv.args == NULL && pipe_argv == NULL) ||
-                 (binding->pipe.argv.args != NULL && pipe_argv != NULL &&
-                  argv_compare(binding->pipe.argv.args, pipe_argv) == 0)))
-            {
+            if (binding->action != action)
+                continue;
+
+            if (argv_compare(&binding->pipe.argv, &pipe_argv) == 0) {
                 if (binding->pipe.master_copy)
                     free_argv(&binding->pipe.argv);
                 binding->action = BIND_ACTION_NONE;
@@ -2173,9 +2173,7 @@
                 .button = combo->m.button,
                 .count = combo->m.count,
                 .pipe = {
-                    .argv = {
-                        .args = pipe_argv,
-                    },
+                    .argv = pipe_argv,
                     .master_copy = first,
                 },
             };
@@ -2189,7 +2187,7 @@
     }
 
     LOG_CONTEXTUAL_ERR("not a valid option: %s", key);
-    free(pipe_argv);
+    free_argv(&pipe_argv);
     return false;
 }
 
@@ -2265,29 +2263,10 @@
             return false;
         }
 
-        switch (mode) {
-        case 0:
-            conf->tweak.render_timer_osd = false;
-            conf->tweak.render_timer_log = false;
-            return true;
-
-        case 1:
-            conf->tweak.render_timer_osd = true;
-            conf->tweak.render_timer_log = false;
-            return true;
-
-        case 2:
-            conf->tweak.render_timer_osd = false;
-            conf->tweak.render_timer_log = true;
-            return true;
-
-        case 3:
-            conf->tweak.render_timer_osd = true;
-            conf->tweak.render_timer_log = true;
-            return true;
-        }
-
-        UNREACHABLE();
+        xassert(0 <= mode && mode <= 3);
+        conf->tweak.render_timer_osd = mode == 1 || mode == 3;
+        conf->tweak.render_timer_log = mode == 2 || mode == 3;
+        return true;
     }
 
     else if (strcmp(key, "delayed-render-lower") == 0) {
@@ -2340,8 +2319,6 @@
         LOG_CONTEXTUAL_ERR("not a valid option: %s", key);
         return false;
     }
-
-    UNREACHABLE();
 }
 
 static bool
@@ -2467,9 +2444,11 @@
             continue;                           \
     }
 
+    char *section_name = xstrdup("main");
+
     struct context context = {
         .conf = conf,
-        .section = "main",
+        .section = section_name,
         .path = path,
         .lineno = 0,
         .errors_are_fatal = errors_are_fatal,
@@ -2537,7 +2516,9 @@
                 error_or_continue();
             }
 
-            context.section = &key_value[1];
+            free(section_name);
+            section_name = xstrdup(&key_value[1]);
+            context.section = section_name;
 
             /* Process next line */
             continue;
@@ -2567,10 +2548,12 @@
             error_or_continue();
     }
 
+    free(section_name);
     free(_line);
     return true;
 
 err:
+    free(section_name);
     free(_line);
     return false;
 }
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/foot/csi.c new/foot/csi.c
--- old/foot/csi.c      2021-11-14 18:12:30.000000000 +0100
+++ new/foot/csi.c      2021-11-22 22:01:28.000000000 +0100
@@ -115,7 +115,7 @@
         case 35:
         case 36:
         case 37:
-            term->vt.attrs.have_fg = 1;
+            term->vt.attrs.fg_src = COLOR_BASE16;
             term->vt.attrs.fg = term->colors.table[param - 30];
             break;
 
@@ -125,7 +125,7 @@
                 term->vt.params.v[i + 1].value == 5)
             {
                 uint8_t idx = term->vt.params.v[i + 2].value;
-                term->vt.attrs.have_fg = 1;
+                term->vt.attrs.fg_src = COLOR_BASE256;
                 term->vt.attrs.fg = term->colors.table[idx];
                 i += 2;
 
@@ -138,7 +138,7 @@
                 uint8_t r = term->vt.params.v[i + 2].value;
                 uint8_t g = term->vt.params.v[i + 3].value;
                 uint8_t b = term->vt.params.v[i + 4].value;
-                term->vt.attrs.have_fg = 1;
+                term->vt.attrs.fg_src = COLOR_RGB;
                 term->vt.attrs.fg = r << 16 | g << 8 | b;
                 i += 4;
             }
@@ -150,7 +150,7 @@
                 const struct vt_param *param = &term->vt.params.v[i];
 
                 uint8_t idx = param->sub.value[1];
-                term->vt.attrs.have_fg = 1;
+                term->vt.attrs.fg_src = COLOR_BASE256;
                 term->vt.attrs.fg = term->colors.table[idx];
             }
 
@@ -180,7 +180,7 @@
                 uint8_t g = param->sub.value[g_idx];
                 uint8_t b = param->sub.value[b_idx];
 
-                term->vt.attrs.have_fg = 1;
+                term->vt.attrs.fg_src = COLOR_RGB;
                 term->vt.attrs.fg = r << 16 | g << 8 | b;
             }
 
@@ -196,7 +196,7 @@
         }
 
         case 39:
-            term->vt.attrs.have_fg = 0;
+            term->vt.attrs.fg_src = COLOR_DEFAULT;
             break;
 
         /* Regular background colors */
@@ -208,7 +208,7 @@
         case 45:
         case 46:
         case 47:
-            term->vt.attrs.have_bg = 1;
+            term->vt.attrs.bg_src = COLOR_BASE16;
             term->vt.attrs.bg = term->colors.table[param - 40];
             break;
 
@@ -218,7 +218,7 @@
                 term->vt.params.v[i + 1].value == 5)
             {
                 uint8_t idx = term->vt.params.v[i + 2].value;
-                term->vt.attrs.have_bg = 1;
+                term->vt.attrs.bg_src = COLOR_BASE256;
                 term->vt.attrs.bg = term->colors.table[idx];
                 i += 2;
 
@@ -231,7 +231,7 @@
                 uint8_t r = term->vt.params.v[i + 2].value;
                 uint8_t g = term->vt.params.v[i + 3].value;
                 uint8_t b = term->vt.params.v[i + 4].value;
-                term->vt.attrs.have_bg = 1;
+                term->vt.attrs.bg_src = COLOR_RGB;
                 term->vt.attrs.bg = r << 16 | g << 8 | b;
                 i += 4;
             }
@@ -243,7 +243,7 @@
                 const struct vt_param *param = &term->vt.params.v[i];
 
                 uint8_t idx = param->sub.value[1];
-                term->vt.attrs.have_bg = 1;
+                term->vt.attrs.bg_src = COLOR_BASE256;
                 term->vt.attrs.bg = term->colors.table[idx];
             }
 
@@ -273,7 +273,7 @@
                 uint8_t g = param->sub.value[g_idx];
                 uint8_t b = param->sub.value[b_idx];
 
-                term->vt.attrs.have_bg = 1;
+                term->vt.attrs.bg_src = COLOR_RGB;
                 term->vt.attrs.bg = r << 16 | g << 8 | b;
             }
 
@@ -287,7 +287,7 @@
             break;
         }
         case 49:
-            term->vt.attrs.have_bg = 0;
+            term->vt.attrs.bg_src = COLOR_DEFAULT;
             break;
 
         /* Bright foreground colors */
@@ -299,7 +299,7 @@
         case 95:
         case 96:
         case 97:
-            term->vt.attrs.have_fg = 1;
+            term->vt.attrs.fg_src = COLOR_BASE16;
             term->vt.attrs.fg = term->colors.table[param - 90 + 8];
             break;
 
@@ -312,7 +312,7 @@
         case 105:
         case 106:
         case 107:
-            term->vt.attrs.have_bg = 1;
+            term->vt.attrs.bg_src = COLOR_BASE16;
             term->vt.attrs.bg = term->colors.table[param - 100 + 8];
             break;
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/foot/foot-server.desktop new/foot/foot-server.desktop
--- old/foot/foot-server.desktop        2021-11-14 18:12:30.000000000 +0100
+++ new/foot/foot-server.desktop        2021-11-22 22:01:28.000000000 +0100
@@ -8,4 +8,4 @@
 
 Name=Foot Server
 GenericName=Terminal
-Comment=A wayland native terminal emulator
+Comment=A wayland native terminal emulator (server)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/foot/footclient.desktop new/foot/footclient.desktop
--- old/foot/footclient.desktop 1970-01-01 01:00:00.000000000 +0100
+++ new/foot/footclient.desktop 2021-11-22 22:01:28.000000000 +0100
@@ -0,0 +1,11 @@
+[Desktop Entry]
+Type=Application
+Exec=footclient
+Icon=foot
+Terminal=false
+Categories=System;TerminalEmulator;
+Keywords=shell;prompt;command;commandline;
+
+Name=Foot Client
+GenericName=Terminal
+Comment=A wayland native terminal emulator (client)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/foot/grid.c new/foot/grid.c
--- old/foot/grid.c     2021-11-14 18:12:30.000000000 +0100
+++ new/foot/grid.c     2021-11-22 22:01:28.000000000 +0100
@@ -236,7 +236,7 @@
                 .id = it->item.id,
                 .uri = xstrdup(it->item.uri),
             };
-            grid_row_add_uri_range(new_row, range);
+            grid_row_uri_range_add(new_row, range);
         }
     }
 
@@ -303,7 +303,7 @@
         .uri = range->uri,
     };
     range->uri = NULL;
-    grid_row_add_uri_range(new_row, new_range);
+    grid_row_uri_range_add(new_row, new_range);
 }
 
 static void
@@ -366,7 +366,7 @@
                 .id = range->id,
                 .uri = xstrdup(range->uri),
             };
-            grid_row_add_uri_range(new_row, new_range);
+            grid_row_uri_range_add(new_row, new_range);
         }
     }
 
@@ -845,14 +845,148 @@
 }
 
 void
-grid_row_add_uri_range(struct row *row, struct row_uri_range range)
+grid_row_uri_range_add(struct row *row, struct row_uri_range range)
 {
     ensure_row_has_extra_data(row);
     tll_rforeach(row->extra->uri_ranges, it) {
         if (it->item.end < range.start) {
             tll_insert_after(row->extra->uri_ranges, it, range);
-            return;
+            goto out;
         }
     }
+
     tll_push_front(row->extra->uri_ranges, range);
+
+out:
+    ;
+#if defined(_DEBUG)
+        tll_foreach(row->extra->uri_ranges, it1) {
+            tll_foreach(row->extra->uri_ranges, it2) {
+                if (&it1->item == &it2->item)
+                    continue;
+
+                xassert(it1->item.start != it2->item.start);
+                xassert(it1->item.start != it2->item.end);
+                xassert(it1->item.end != it2->item.start);
+                xassert(it1->item.end != it2->item.end);
+            }
+        }
+#endif
+}
+
+void
+grid_row_uri_range_erase(struct row *row, int start, int end)
+{
+    xassert(row->extra != NULL);
+    xassert(start <= end);
+
+    /* Split up, or remove, URI ranges affected by the erase */
+    tll_foreach(row->extra->uri_ranges, it) {
+        struct row_uri_range *old = &it->item;
+
+        if (old->end < start)
+            continue;
+
+        if (old->start > end)
+            return;
+
+        if (start <= old->start && end >= old->end) {
+            /* Erase range covers URI completely - remove it */
+            grid_row_uri_range_destroy(old);
+            tll_remove(row->extra->uri_ranges, it);
+        }
+
+        else if (start > old->start && end < old->end) {
+            /* Erase range erases a part in the middle of the URI */
+            struct row_uri_range old_tail = {
+                .start = end + 1,
+                .end = old->end,
+                .id = old->id,
+                .uri = old->uri != NULL ? xstrdup(old->uri) : NULL,
+            };
+            tll_insert_after(row->extra->uri_ranges, it, old_tail);
+            old->end = start - 1;
+            return;  /* There can be no more URIs affected by the erase range 
*/
+        }
+
+        else if (start <= old->start && end >= old->start) {
+            /* Erase range erases the head of the URI */
+            xassert(start <= old->start);
+            old->start = end + 1;
+            return;  /* There can be no more overlapping URIs */
+        }
+
+        else if (start <= old->end && end >= old->end) {
+            /* Erase range erases the tail of the URI */
+            xassert(end >= old->end);
+            old->end = start - 1;
+        }
+    }
+}
+
+UNITTEST
+{
+    struct row_data row_data = {.uri_ranges = tll_init()};
+    struct row row = {.extra = &row_data};
+
+#define row_has_no_overlapping_uris(row)                                \
+    do {                                                                \
+        tll_foreach((row)->extra->uri_ranges, it1) {                    \
+            tll_foreach((row)->extra->uri_ranges, it2) {                \
+                if (&it1->item == &it2->item)                           \
+                    continue;                                           \
+                xassert(it1->item.start != it2->item.start);            \
+                xassert(it1->item.start != it2->item.end);              \
+                xassert(it1->item.end != it2->item.start);              \
+                xassert(it1->item.end != it2->item.end);                \
+            }                                                           \
+        }                                                               \
+    } while (0)
+
+    grid_row_uri_range_add(&row, (struct row_uri_range){1, 10});
+    xassert(tll_length(row_data.uri_ranges) == 1);
+    xassert(tll_front(row_data.uri_ranges).start == 1);
+    xassert(tll_front(row_data.uri_ranges).end == 10);
+    row_has_no_overlapping_uris(&row);
+
+    grid_row_uri_range_add(&row, (struct row_uri_range){11, 20});
+    xassert(tll_length(row_data.uri_ranges) == 2);
+    xassert(tll_back(row_data.uri_ranges).start == 11);
+    xassert(tll_back(row_data.uri_ranges).end == 20);
+    row_has_no_overlapping_uris(&row);
+
+    /* Erase both URis */
+    grid_row_uri_range_erase(&row, 1, 20);
+    xassert(tll_length(row_data.uri_ranges) == 0);
+    row_has_no_overlapping_uris(&row);
+
+    /* Two URIs, then erase second half of the first, first half of
+       the second */
+    grid_row_uri_range_add(&row, (struct row_uri_range){1, 10});
+    grid_row_uri_range_add(&row, (struct row_uri_range){11, 20});
+    grid_row_uri_range_erase(&row, 5, 15);
+    xassert(tll_length(row_data.uri_ranges) == 2);
+    xassert(tll_front(row_data.uri_ranges).start == 1);
+    xassert(tll_front(row_data.uri_ranges).end == 4);
+    xassert(tll_back(row_data.uri_ranges).start == 16);
+    xassert(tll_back(row_data.uri_ranges).end == 20);
+    row_has_no_overlapping_uris(&row);
+
+    tll_pop_back(row_data.uri_ranges);
+    tll_pop_back(row_data.uri_ranges);
+    xassert(tll_length(row_data.uri_ranges) == 0);
+
+    /* One URI, erase middle part of it */
+    grid_row_uri_range_add(&row, (struct row_uri_range){1, 10});
+    grid_row_uri_range_erase(&row, 5, 6);
+    xassert(tll_length(row_data.uri_ranges) == 2);
+    xassert(tll_front(row_data.uri_ranges).start == 1);
+    xassert(tll_front(row_data.uri_ranges).end == 4);
+    xassert(tll_back(row_data.uri_ranges).start == 7);
+    xassert(tll_back(row_data.uri_ranges).end == 10);
+    row_has_no_overlapping_uris(&row);
+
+#undef row_has_no_overlapping_uris
+
+    tll_free(row_data.uri_ranges);
 }
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/foot/grid.h new/foot/grid.h
--- old/foot/grid.h     2021-11-14 18:12:30.000000000 +0100
+++ new/foot/grid.h     2021-11-22 22:01:28.000000000 +0100
@@ -74,7 +74,8 @@
     return row;
 }
 
-void grid_row_add_uri_range(struct row *row, struct row_uri_range range);
+void grid_row_uri_range_add(struct row *row, struct row_uri_range range);
+void grid_row_uri_range_erase(struct row *row, int start, int end);
 
 static inline void
 grid_row_uri_range_destroy(struct row_uri_range *range)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/foot/meson.build new/foot/meson.build
--- old/foot/meson.build        2021-11-14 18:12:30.000000000 +0100
+++ new/foot/meson.build        2021-11-22 22:01:28.000000000 +0100
@@ -1,5 +1,5 @@
 project('foot', 'c',
-        version: '1.10.0',
+        version: '1.10.1',
         license: 'MIT',
         meson_version: '>=0.54.0',
         default_options: [
@@ -231,7 +231,7 @@
   install: true)
 
 install_data(
-  'foot.desktop', 'foot-server.desktop',
+  'foot.desktop', 'foot-server.desktop', 'footclient.desktop',
   install_dir: join_paths(get_option('datadir'), 'applications'))
 
 scdoc = dependency('scdoc', native: true, required: get_option('docs'))
@@ -240,10 +240,13 @@
     'LICENSE', 'README.md', 'CHANGELOG.md',
     install_dir: join_paths(get_option('datadir'), 'doc', 'foot'))
   install_data('foot.ini', install_dir: join_paths(get_option('datadir'), 
'foot'))
-  install_subdir('themes', install_dir: join_paths(get_option('datadir'), 
'foot'))
   subdir('doc')
 endif
 
+if get_option('themes')
+  install_subdir('themes', install_dir: join_paths(get_option('datadir'), 
'foot'))
+endif
+
 tic = find_program('tic', native: true, required: get_option('terminfo'))
 if tic.found()
   conf_data = configuration_data(
@@ -273,6 +276,7 @@
 summary(
   {
     'Documentation': scdoc.found(),
+    'Themes': get_option('themes'),
     'IME': get_option('ime'),
     'Grapheme clustering': utf8proc.found(),
     'Build terminfo': tic.found(),
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/foot/meson_options.txt new/foot/meson_options.txt
--- old/foot/meson_options.txt  2021-11-14 18:12:30.000000000 +0100
+++ new/foot/meson_options.txt  2021-11-22 22:01:28.000000000 +0100
@@ -1,6 +1,9 @@
 option('docs', type: 'feature',
        description: 'Build and install documentation (man pages, example 
foot.ini, readme, changelog, license etc).')
 
+option('themes', type: 'boolean', value: true,
+       description: 'Install themes (predefined color schemes)')
+
 option('ime', type: 'boolean', value: true,
        description: 'IME (Input Method Editor) support')
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/foot/osc.c new/foot/osc.c
--- old/foot/osc.c      2021-11-14 18:12:30.000000000 +0100
+++ new/foot/osc.c      2021-11-22 22:01:28.000000000 +0100
@@ -503,7 +503,7 @@
      * (https://pub.phyks.me/scripts/urxvt/notify) is very simple:
      *
      * #!/usr/bin/perl
-     * 
+     *
      * sub on_osc_seq_perl {
      *   my ($term, $osc, $resp) = @_;
      *   if ($osc =~ /^notify;(\S+);(.*)$/) {
@@ -560,7 +560,7 @@
 
             for (size_t c = 0; c < term->grid->num_cols; c++) {
                 struct cell *cell = &row->cells[c];
-                if (cell->attrs.have_fg &&
+                if (cell->attrs.fg_src != COLOR_DEFAULT &&
                     cell->attrs.fg == old_color)
                 {
                     cell->attrs.fg = new_color;
@@ -568,7 +568,7 @@
                     row->dirty = true;
                 }
 
-                if ( cell->attrs.have_bg &&
+                if (cell->attrs.bg_src != COLOR_DEFAULT &&
                     cell->attrs.bg == old_color)
                 {
                     cell->attrs.bg = new_color;
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/foot/render.c new/foot/render.c
--- old/foot/render.c   2021-11-14 18:12:30.000000000 +0100
+++ new/foot/render.c   2021-11-22 22:01:28.000000000 +0100
@@ -494,14 +494,14 @@
         _bg = term->colors.selection_bg;
     } else {
         /* Use cell specific color, if set, otherwise the default colors 
(possible reversed) */
-        _fg = cell->attrs.have_fg ? cell->attrs.fg : term->reverse ? 
term->colors.bg : term->colors.fg;
-        _bg = cell->attrs.have_bg ? cell->attrs.bg : term->reverse ? 
term->colors.fg : term->colors.bg;
+        _fg = cell->attrs.fg_src != COLOR_DEFAULT ? cell->attrs.fg : 
term->reverse ? term->colors.bg : term->colors.fg;
+        _bg = cell->attrs.bg_src != COLOR_DEFAULT ? cell->attrs.bg : 
term->reverse ? term->colors.fg : term->colors.bg;
 
         if (cell->attrs.reverse ^ is_selected) {
             uint32_t swap = _fg;
             _fg = _bg;
             _bg = swap;
-        } else if (!cell->attrs.have_bg)
+        } else if (cell->attrs.bg_src == COLOR_DEFAULT)
             alpha = term->colors.alpha;
     }
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/foot/sixel.c new/foot/sixel.c
--- old/foot/sixel.c    2021-11-14 18:12:30.000000000 +0100
+++ new/foot/sixel.c    2021-11-22 22:01:28.000000000 +0100
@@ -70,7 +70,7 @@
 
     term->sixel.default_bg = term->sixel.transparent_bg
         ? 0x00000000u
-        : 0xffu << 24 | (term->vt.attrs.have_bg
+        : 0xffu << 24 | (term->vt.attrs.bg_src != COLOR_DEFAULT
                          ? term->vt.attrs.bg
                          : term->colors.bg);
 
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/foot/terminal.c new/foot/terminal.c
--- old/foot/terminal.c 2021-11-14 18:12:30.000000000 +0100
+++ new/foot/terminal.c 2021-11-22 22:01:28.000000000 +0100
@@ -1770,72 +1770,19 @@
 
     row->dirty = true;
 
-    if (unlikely(term->vt.attrs.have_bg)) {
+    const enum color_source bg_src = term->vt.attrs.bg_src;
+
+    if (unlikely(bg_src != COLOR_DEFAULT)) {
         for (int col = start; col <= end; col++) {
             struct cell *c = &row->cells[col];
             c->wc = 0;
-            c->attrs = (struct attributes){.have_bg = 1, .bg = 
term->vt.attrs.bg};
+            c->attrs = (struct attributes){.bg_src = bg_src, .bg = 
term->vt.attrs.bg};
         }
     } else
         memset(&row->cells[start], 0, (end - start + 1) * 
sizeof(row->cells[0]));
 
-    if (likely(row->extra == NULL))
-        return;
-
-    /* Split up, or remove, URI ranges affected by the erase */
-    tll_foreach(row->extra->uri_ranges, it) {
-        if (it->item.start > end) {
-            /* This range, and all subsequent ranges, start *after*
-             * the erase range */
-            break;
-        }
-
-        if (it->item.start < start && it->item.end >= start) {
-            /*
-             * URI crosses the erase *start* point.
-             *
-             * Create a new range for the URI part *before* the erased
-             * cells.
-             *
-             * Also modify this URI range???s start point so that we can
-             * remove it below.
-             */
-            struct row_uri_range range_before = {
-                .start = it->item.start,
-                .end = start - 1,
-                .id = it->item.id,
-                .uri = xstrdup(it->item.uri),
-            };
-            tll_insert_before(row->extra->uri_ranges, it, range_before);
-            it->item.start = start;
-        }
-
-        if (it->item.start <= end && it->item.end > end) {
-            /*
-             * URI crosses the erase *end* point.
-             *
-             * Create a new range for the URI part *after* the erased
-             * cells.
-             *
-             * Also modify the URI range???s end point so that we can
-             * remove it below.
-             */
-            struct row_uri_range range_after = {
-                .start = end + 1,
-                .end = it->item.end,
-                .id = it->item.id,
-                .uri = xstrdup(it->item.uri),
-            };
-            tll_insert_before(row->extra->uri_ranges, it, range_after);
-            it->item.end = end;
-        }
-
-        if (it->item.start >= start && it->item.end <= end) {
-            /* URI range completey covered by the erase - remove it */
-            free(it->item.uri);
-            tll_remove(row->extra->uri_ranges, it);
-        }
-    }
+    if (unlikely(row->extra != NULL))
+        grid_row_uri_range_erase(row, start, end);
 }
 
 static inline void
@@ -3216,6 +3163,8 @@
 {
     xassert(width > 0);
 
+    struct grid *grid = term->grid;
+
     if (unlikely(term->charsets.set[term->charsets.selected] == 
CHARSET_GRAPHIC) &&
         wc >= 0x60 && wc <= 0x7e)
     {
@@ -3234,43 +3183,52 @@
     print_linewrap(term);
     print_insert(term, width);
 
+    int col = grid->cursor.point.col;
+
     if (unlikely(width > 1) && likely(term->auto_margin) &&
-        term->grid->cursor.point.col + width > term->cols)
+        col + width > term->cols)
     {
         /* Multi-column character that doesn't fit on current line -
          * pad with spacers */
-        for (size_t i = term->grid->cursor.point.col; i < term->cols; i++)
+        for (size_t i = col; i < term->cols; i++)
             print_spacer(term, i, 0);
 
         /* And force a line-wrap */
-        term->grid->cursor.lcf = 1;
+        grid->cursor.lcf = 1;
         print_linewrap(term);
+        col = 0;
     }
 
     sixel_overwrite_at_cursor(term, width);
 
     /* *Must* get current cell *after* linewrap+insert */
-    struct row *row = term->grid->cur_row;
-    struct cell *cell = &row->cells[term->grid->cursor.point.col];
+    struct row *row = grid->cur_row;
+    row->dirty = true;
+    row->linebreak = true;
 
+    struct cell *cell = &row->cells[col];
     cell->wc = term->vt.last_printed = wc;
     cell->attrs = term->vt.attrs;
 
-    row->dirty = true;
-    row->linebreak = true;
+    const int uri_start = col;
 
     /* Advance cursor the 'additional' columns while dirty:ing the cells */
-    for (int i = 1; i < width && term->grid->cursor.point.col < term->cols - 
1; i++) {
-        term->grid->cursor.point.col++;
-        print_spacer(term, term->grid->cursor.point.col, width - i);
+    for (int i = 1; i < width && col < term->cols - 1; i++) {
+        col++;
+        print_spacer(term, col, width - i);
     }
 
     /* Advance cursor */
-    if (unlikely(++term->grid->cursor.point.col >= term->cols)) {
-        term->grid->cursor.lcf = true;
-        term->grid->cursor.point.col--;
+    if (unlikely(++col >= term->cols)) {
+        grid->cursor.lcf = true;
+        col--;
     } else
-        xassert(!term->grid->cursor.lcf);
+        xassert(!grid->cursor.lcf);
+
+    grid->cursor.point.col = col;
+
+    if (unlikely(row->extra != NULL))
+        grid_row_uri_range_erase(row, uri_start, uri_start + width - 1);
 }
 
 static void
@@ -3282,28 +3240,38 @@
 static void
 ascii_printer_fast(struct terminal *term, wchar_t wc)
 {
+    struct grid *grid = term->grid;
+
     xassert(term->charsets.set[term->charsets.selected] == CHARSET_ASCII);
     xassert(!term->insert_mode);
-    xassert(tll_length(term->grid->sixel_images) == 0);
+    xassert(tll_length(grid->sixel_images) == 0);
 
     print_linewrap(term);
 
     /* *Must* get current cell *after* linewrap+insert */
-    struct row *row = term->grid->cur_row;
-    struct cell *cell = &row->cells[term->grid->cursor.point.col];
+    int col = grid->cursor.point.col;
+    const int uri_start = col;
+
+    struct row *row = grid->cur_row;
+    row->dirty = true;
+    row->linebreak = true;
 
+    struct cell *cell = &row->cells[col];
     cell->wc = term->vt.last_printed = wc;
     cell->attrs = term->vt.attrs;
 
-    row->dirty = true;
-    row->linebreak = true;
 
     /* Advance cursor */
-    if (unlikely(++term->grid->cursor.point.col >= term->cols)) {
-        term->grid->cursor.lcf = true;
-        term->grid->cursor.point.col--;
+    if (unlikely(++col >= term->cols)) {
+        grid->cursor.lcf = true;
+        col--;
     } else
-        xassert(!term->grid->cursor.lcf);
+        xassert(!grid->cursor.lcf);
+
+    grid->cursor.point.col = col;
+
+    if (unlikely(row->extra != NULL))
+        grid_row_uri_range_erase(row, uri_start, uri_start);
 }
 
 static void
@@ -3588,21 +3556,7 @@
             .id = term->vt.osc8.id,
             .uri = xstrdup(term->vt.osc8.uri),
         };
-        grid_row_add_uri_range(row, range);
-
-#if defined(_DEBUG)
-        tll_foreach(row->extra->uri_ranges, it1) {
-            tll_foreach(row->extra->uri_ranges, it2) {
-                if (&it1->item == &it2->item)
-                    continue;
-
-                xassert(it1->item.start != it2->item.start);
-                xassert(it1->item.start != it2->item.end);
-                xassert(it1->item.end != it2->item.start);
-                xassert(it1->item.end != it2->item.end);
-            }
-        }
-#endif
+        grid_row_uri_range_add(row, range);
         start_col = 0;
 
         if (r == end.row)
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/foot/terminal.h new/foot/terminal.h
--- old/foot/terminal.h 2021-11-14 18:12:30.000000000 +0100
+++ new/foot/terminal.h 2021-11-22 22:01:28.000000000 +0100
@@ -24,6 +24,13 @@
 #include "shm.h"
 #include "wayland.h"
 
+enum color_source {
+    COLOR_DEFAULT,
+    COLOR_BASE16,
+    COLOR_BASE256,
+    COLOR_RGB,
+};
+
 /*
  *  Note: we want the cells to be as small as possible. Larger cells
  *  means fewer scrollback lines (or performance drops due to cache
@@ -43,12 +50,11 @@
     uint32_t fg:24;
 
     bool clean:1;
+    enum color_source fg_src:2;
+    enum color_source bg_src:2;
     bool confined:1;
-    bool have_fg:1;
-    bool have_bg:1;
     bool selected:1;
     bool url:1;
-    uint32_t reserved:2;
     uint32_t bg:24;
 };
 static_assert(sizeof(struct attributes) == 8, "VT attribute struct too large");
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/foot/themes/PaperColorDark 
new/foot/themes/PaperColorDark
--- old/foot/themes/PaperColorDark      1970-01-01 01:00:00.000000000 +0100
+++ new/foot/themes/PaperColorDark      2021-11-22 22:01:28.000000000 +0100
@@ -0,0 +1,28 @@
+# PaperColorDark
+# Palette based on https://github.com/NLKNguyen/papercolor-theme
+
+[cursor]
+ color=1c1c1c eeeeee
+
+[colors]
+ alpha=0.80
+ background=1c1c1c
+ foreground=eeeeee
+ regular0=1c1c1c  # black
+ regular1=af005f  # red
+ regular2=5faf00  # green
+ regular3=d7af5f  # yellow
+ regular4=5fafd7  # blue
+ regular5=808080  # magenta
+ regular6=d7875f  # cyan
+ regular7=d0d0d0  # white
+ bright0=bcbcbc   # bright black
+ bright1=5faf5f   # bright red
+ bright2=afd700   # bright green
+ bright3=af87d7   # bright yellow
+ bright4=ffaf00   # bright blue
+ bright5=ff5faf   # bright magenta
+ bright6=00afaf   # bright cyan
+ bright7=5f8787   # bright white
+ #selection-foreground=1c1c1c
+ #selection-background=af87d7
diff -urN '--exclude=CVS' '--exclude=.cvsignore' '--exclude=.svn' 
'--exclude=.svnignore' old/foot/themes/PaperColorLight 
new/foot/themes/PaperColorLight
--- old/foot/themes/PaperColorLight     1970-01-01 01:00:00.000000000 +0100
+++ new/foot/themes/PaperColorLight     2021-11-22 22:01:28.000000000 +0100
@@ -0,0 +1,28 @@
+# PaperColor Light
+# Palette based on https://github.com/NLKNguyen/papercolor-theme
+
+[cursor]
+ color=eeeeee 444444
+
+[colors]
+ alpha=1.0
+ background=eeeeee
+ foreground=444444
+ regular0=eeeeee  # black
+ regular1=af0000  # red
+ regular2=008700  # green
+ regular3=5f8700  # yellow
+ regular4=0087af  # blue
+ regular5=878787  # magenta
+ regular6=005f87  # cyan
+ regular7=764e37  # white
+ bright0=bcbcbc   # bright black
+ bright1=d70000   # bright red
+ bright2=d70087   # bright green
+ bright3=8700af   # bright yellow
+ bright4=d75f00   # bright blue
+ bright5=d75f00   # bright magenta
+ bright6=4c7a5d   # bright cyan
+ bright7=005faf   # bright white
+ #selection-foreground=eeeeee
+ #selection-background=0087af

Reply via email to