Control: tags 1134281 + patch
Control: tags 1134281 + pending

Dear maintainer,

I've prepared an NMU for golang-code.rocketnine-tslocum-cbind (versioned as 
0.1.5-3.1)
and uploaded it to DELAYED/2. Please feel free to tell me if I should 
cancel it.

I have seen #1139314, this is a short-term fix.

cu
Adrian
diffstat for golang-code.rocketnine-tslocum-cbind-0.1.5 golang-code.rocketnine-tslocum-cbind-0.1.5

 changelog                                                         |    7 
 control                                                           |    2 
 patches/0001-Support-CSI-u-mode-introduced-in-tcell-v2.10.0.patch |  212 ++++++++++
 patches/series                                                    |    1 
 4 files changed, 221 insertions(+), 1 deletion(-)

diff -Nru golang-code.rocketnine-tslocum-cbind-0.1.5/debian/changelog golang-code.rocketnine-tslocum-cbind-0.1.5/debian/changelog
--- golang-code.rocketnine-tslocum-cbind-0.1.5/debian/changelog	2021-06-25 11:43:38.000000000 +0300
+++ golang-code.rocketnine-tslocum-cbind-0.1.5/debian/changelog	2026-06-19 18:28:47.000000000 +0300
@@ -1,3 +1,10 @@
+golang-code.rocketnine-tslocum-cbind (0.1.5-3.1) unstable; urgency=medium
+
+  * Non-maintainer upload.
+  * Backport upstream fix for FTBFS with tcell >= 2.10. (Closes: #1134281)
+
+ -- Adrian Bunk <[email protected]>  Fri, 19 Jun 2026 18:28:47 +0300
+
 golang-code.rocketnine-tslocum-cbind (0.1.5-3) unstable; urgency=medium
 
   * Team Upload.
diff -Nru golang-code.rocketnine-tslocum-cbind-0.1.5/debian/control golang-code.rocketnine-tslocum-cbind-0.1.5/debian/control
--- golang-code.rocketnine-tslocum-cbind-0.1.5/debian/control	2021-04-05 12:58:47.000000000 +0300
+++ golang-code.rocketnine-tslocum-cbind-0.1.5/debian/control	2026-06-19 18:20:41.000000000 +0300
@@ -6,7 +6,7 @@
 Build-Depends: debhelper-compat (= 13),
                dh-golang,
                golang-any,
-               golang-github-gdamore-tcell.v2-dev
+               golang-github-gdamore-tcell.v2-dev (>= 2.12)
 Standards-Version: 4.5.1
 Homepage: https://code.rocketnine.space/tslocum/cbind
 Vcs-Browser: https://salsa.debian.org/go-team/packages/golang-code.rocketnine-tslocum-cbind
diff -Nru golang-code.rocketnine-tslocum-cbind-0.1.5/debian/patches/0001-Support-CSI-u-mode-introduced-in-tcell-v2.10.0.patch golang-code.rocketnine-tslocum-cbind-0.1.5/debian/patches/0001-Support-CSI-u-mode-introduced-in-tcell-v2.10.0.patch
--- golang-code.rocketnine-tslocum-cbind-0.1.5/debian/patches/0001-Support-CSI-u-mode-introduced-in-tcell-v2.10.0.patch	1970-01-01 02:00:00.000000000 +0200
+++ golang-code.rocketnine-tslocum-cbind-0.1.5/debian/patches/0001-Support-CSI-u-mode-introduced-in-tcell-v2.10.0.patch	2026-06-19 18:19:53.000000000 +0300
@@ -0,0 +1,212 @@
+From 30873c5326cba1b1f127e037f4e1c3e1ba1ca119 Mon Sep 17 00:00:00 2001
+From: Noboru Saito <[email protected]>
+Date: Wed, 26 Nov 2025 09:24:07 +0900
+Subject: Support CSI u mode introduced in tcell v2.10.0
+
+Fixes #1
+
+Starting from tcell v2.10.0, CSI u mode support was added which changes
+how Ctrl+letter key events are reported. Previously, Ctrl+C was reported
+as KeyCtrlC (key code 3), but now it's reported as ModCtrl + KeyRune + 'c'.
+
+This commit adapts cbind to handle both the legacy KeyCtrlA-Z format and
+the new CSI u format:
+
+- Modified Capture() to convert KeyCtrlA-Z events to the new rune-based
+  format (ModCtrl + KeyRune + 'a'-'z') for consistent internal handling
+- Modified Encode() to convert KeyCtrlA-Z to rune format when encoding
+- Modified Decode() to normalize Ctrl+letter keys to lowercase
+- Removed old ctrlKeys mapping logic from SetKey() and SetRune() that is
+  no longer needed with the new approach
+- Updated tests to use Set() method for consistent handler registration
+- Simplified TestConfiguration by removing tcell event normalization
+  checks that were causing false failures
+- Updated test cases to reflect the new rune-based Ctrl key format
+- Commented out KeyCtrlRightSq test due to upstream tcell bug (PR #853)
+---
+ configuration.go      | 37 ++++++++++++++++---------------------
+ configuration_test.go | 29 ++++++-----------------------
+ key.go                | 18 ++++++++----------
+ key_test.go           | 11 ++++++-----
+ 4 files changed, 36 insertions(+), 59 deletions(-)
+
+diff --git a/configuration.go b/configuration.go
+index 2b42a93..57d2519 100644
+--- a/configuration.go
++++ b/configuration.go
+@@ -3,7 +3,6 @@ package cbind
+ import (
+ 	"fmt"
+ 	"sync"
+-	"unicode"
+ 
+ 	"github.com/gdamore/tcell/v2"
+ )
+@@ -51,15 +50,6 @@ func (c *Configuration) SetKey(mod tcell.ModMask, key tcell.Key, handler func(ev
+ 		key = tcell.KeyBacktab
+ 	}
+ 
+-	if mod&tcell.ModCtrl == 0 && key != tcell.KeyBackspace && key != tcell.KeyTab && key != tcell.KeyEnter {
+-		for _, ctrlKey := range ctrlKeys {
+-			if key == ctrlKey {
+-				mod |= tcell.ModCtrl
+-				break
+-			}
+-		}
+-	}
+-
+ 	c.handlers[fmt.Sprintf("%d-%d", mod, key)] = handler
+ }
+ 
+@@ -76,14 +66,6 @@ func (c *Configuration) SetRune(mod tcell.ModMask, ch rune, handler func(ev *tce
+ 		return
+ 	}
+ 
+-	if mod&tcell.ModCtrl != 0 {
+-		k, ok := ctrlKeys[unicode.ToLower(ch)]
+-		if ok {
+-			c.SetKey(mod, k, handler)
+-			return
+-		}
+-	}
+-
+ 	c.mutex.Lock()
+ 	defer c.mutex.Unlock()
+ 
+@@ -99,11 +81,24 @@ func (c *Configuration) Capture(ev *tcell.EventKey) *tcell.EventKey {
+ 		return nil
+ 	}
+ 
++	mod := ev.Modifiers()
++	key := ev.Key()
++	ch := ev.Rune()
++
++	if key != tcell.KeyRune && key != tcell.KeyBackspace && key != tcell.KeyTab && key != tcell.KeyEnter && key != tcell.KeyEscape {
++		// Convert KeyCtrlA-Z to rune format, but skip special keys first
++		if key >= tcell.KeyCtrlA && key <= tcell.KeyCtrlZ {
++			mod |= tcell.ModCtrl
++			ch = rune('a' + (key - tcell.KeyCtrlA))
++			key = tcell.KeyRune
++		}
++	}
++
+ 	var keyName string
+-	if ev.Key() != tcell.KeyRune {
+-		keyName = fmt.Sprintf("%d-%d", ev.Modifiers(), ev.Key())
++	if key != tcell.KeyRune {
++		keyName = fmt.Sprintf("%d-%d", mod, key)
+ 	} else {
+-		keyName = fmt.Sprintf("%d:%d", ev.Modifiers(), ev.Rune())
++		keyName = fmt.Sprintf("%d:%d", mod, ch)
+ 	}
+ 
+ 	handler := c.handlers[keyName]
+diff --git a/configuration_test.go b/configuration_test.go
+index d6c98fa..68830ca 100644
+--- a/configuration_test.go
++++ b/configuration_test.go
+@@ -23,18 +23,13 @@ func TestConfiguration(t *testing.T) {
+ 		wg[i].Add(pressTimes)
+ 
+ 		i := i // Capture
+-		if c.key != tcell.KeyRune {
+-			config.SetKey(c.mod, c.key, func(ev *tcell.EventKey) *tcell.EventKey {
+-				wg[i].Done()
+-				return nil
+-			})
+-		} else {
+-			config.SetRune(c.mod, c.ch, func(ev *tcell.EventKey) *tcell.EventKey {
+-				wg[i].Done()
+-				return nil
+-			})
++		err := config.Set(c.encoded, func(ev *tcell.EventKey) *tcell.EventKey {
++			wg[i].Done()
++			return nil
++		})
++		if err != nil {
++			t.Fatalf("failed to set keybind for %s: %s", c.encoded, err)
+ 		}
+-
+ 	}
+ 
+ 	done := make(chan struct{})
+@@ -53,18 +48,6 @@ func TestConfiguration(t *testing.T) {
+ 		for i, c := range testCases {
+ 			i, c := i, c // Capture
+ 			go func() {
+-				k := tcell.NewEventKey(c.key, c.ch, c.mod)
+-				if k.Key() != c.key {
+-					errs <- fmt.Errorf("failed to test capturing keybinds: tcell modified EventKey.Key: expected %d, got %d", c.key, k.Key())
+-					return
+-				} else if k.Rune() != c.ch {
+-					errs <- fmt.Errorf("failed to test capturing keybinds: tcell modified EventKey.Rune: expected %d, got %d", c.ch, k.Rune())
+-					return
+-				} else if k.Modifiers() != c.mod {
+-					errs <- fmt.Errorf("failed to test capturing keybinds: tcell modified EventKey.Modifiers: expected %d, got %d", c.mod, k.Modifiers())
+-					return
+-				}
+-
+ 				ev := config.Capture(tcell.NewEventKey(c.key, c.ch, c.mod))
+ 				if ev != nil {
+ 					errs <- fmt.Errorf("failed to test capturing keybinds: failed to register case %d event %d %d %d", i, c.mod, c.key, c.ch)
+diff --git a/key.go b/key.go
+index b30fec8..1465242 100644
+--- a/key.go
++++ b/key.go
+@@ -140,16 +140,9 @@ DECODEPIECE:
+ 		ch = rune(piece[0])
+ 	}
+ 
+-	if mod&tcell.ModCtrl != 0 {
+-		k, ok := ctrlKeys[unicode.ToLower(ch)]
+-		if ok {
+-			key = k
+-			if UnifyEnterKeys && key == ctrlKeys['j'] {
+-				key = tcell.KeyEnter
+-			} else if key < 0x80 {
+-				ch = rune(key)
+-			}
+-		}
++	// Normalize Ctrl+A-Z to lowercase
++	if mod&tcell.ModCtrl != 0 && key == tcell.KeyRune {
++		ch = unicode.ToLower(ch)
+ 	}
+ 
+ 	return mod, key, ch, nil
+@@ -164,6 +157,11 @@ func Encode(mod tcell.ModMask, key tcell.Key, ch rune) (string, error) {
+ 		if key == tcell.KeyBackspace || key == tcell.KeyTab || key == tcell.KeyEnter {
+ 			mod ^= tcell.ModCtrl
+ 		} else {
++			if key >= tcell.KeyCtrlA && key <= tcell.KeyCtrlZ {
++				mod |= tcell.ModCtrl
++				ch = rune('a' + (key - tcell.KeyCtrlA))
++				key = tcell.KeyRune
++			}
+ 			for _, ctrlKey := range ctrlKeys {
+ 				if key == ctrlKey {
+ 					mod ^= tcell.ModCtrl
+diff --git a/key_test.go b/key_test.go
+index 7063583..8271fa0 100644
+--- a/key_test.go
++++ b/key_test.go
+@@ -27,11 +27,12 @@ var testCases = []testCase{
+ 	{mod: tcell.ModAlt, key: tcell.KeyRune, ch: '1', encoded: "Alt+1"},
+ 	{mod: tcell.ModAlt, key: tcell.KeyTab, ch: rune(tcell.KeyTab), encoded: "Alt+Tab"},
+ 	{mod: tcell.ModAlt, key: tcell.KeyEnter, ch: rune(tcell.KeyEnter), encoded: "Alt+Enter"},
+-	{mod: tcell.ModAlt, key: tcell.KeyBackspace2, ch: rune(tcell.KeyBackspace2), encoded: "Alt+Backspace"},
+-	{mod: tcell.ModCtrl, key: tcell.KeyCtrlC, ch: rune(tcell.KeyCtrlC), encoded: "Ctrl+C"},
+-	{mod: tcell.ModCtrl, key: tcell.KeyCtrlD, ch: rune(tcell.KeyCtrlD), encoded: "Ctrl+D"},
+-	{mod: tcell.ModCtrl, key: tcell.KeyCtrlSpace, ch: rune(tcell.KeyCtrlSpace), encoded: "Ctrl+Space"},
+-	{mod: tcell.ModCtrl, key: tcell.KeyCtrlRightSq, ch: rune(tcell.KeyCtrlRightSq), encoded: "Ctrl+]"},
++	{mod: tcell.ModAlt, key: tcell.KeyDelete, ch: 0, encoded: "Alt+Delete"},
++	{mod: tcell.ModCtrl, key: tcell.KeyRune, ch: 'c', encoded: "Ctrl+c"},
++	{mod: tcell.ModCtrl, key: tcell.KeyRune, ch: 'd', encoded: "Ctrl+d"},
++	{mod: tcell.ModCtrl, key: tcell.KeyRune, ch: ' ', encoded: "Ctrl+Space"},
++	// The test for tcell.KeyCtrlRightSq has been avoided due to a problem with tcell (#853).
++	//{mod: tcell.ModCtrl, key: tcell.KeyCtrlRightSq, ch: rune(tcell.KeyCtrlRightSq), encoded: "Ctrl+]"},
+ 	{mod: tcell.ModCtrl | tcell.ModAlt, key: tcell.KeyRune, ch: '+', encoded: "Ctrl+Alt++"},
+ 	{mod: tcell.ModCtrl | tcell.ModShift, key: tcell.KeyRune, ch: '+', encoded: "Ctrl+Shift++"},
+ }
+-- 
+2.47.3
+
diff -Nru golang-code.rocketnine-tslocum-cbind-0.1.5/debian/patches/series golang-code.rocketnine-tslocum-cbind-0.1.5/debian/patches/series
--- golang-code.rocketnine-tslocum-cbind-0.1.5/debian/patches/series	1970-01-01 02:00:00.000000000 +0200
+++ golang-code.rocketnine-tslocum-cbind-0.1.5/debian/patches/series	2026-06-19 18:20:07.000000000 +0300
@@ -0,0 +1 @@
+0001-Support-CSI-u-mode-introduced-in-tcell-v2.10.0.patch

Reply via email to