-
-
Notifications
You must be signed in to change notification settings - Fork 27
Support Kitty's Unicode placeholders feature and support running loooooooongcat within tmux #48
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Open
haya14busa
wants to merge
3
commits into
mattn:master
Choose a base branch
from
haya14busa:kitty-unicode-placeholder-and-tmux
base: master
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from all commits
Commits
Show all changes
3 commits
Select commit
Hold shift + click to select a range
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,94 @@ | ||
| package kitty | ||
|
|
||
| import ( | ||
| "log" | ||
| "os" | ||
| "regexp" | ||
| "strconv" | ||
| "time" | ||
|
|
||
| "golang.org/x/term" | ||
| ) | ||
|
|
||
| const ( | ||
| defaultCellWidth = 8 | ||
| defaultCellHeight = 16 | ||
| ) | ||
|
|
||
| // getCellSize attempts to query the terminal for cell size in pixels. | ||
| // Uses CSI 16 t: "Report xterm window character cell size in pixels" -> CSI 6 ; height ; width t | ||
| // Returns default values on error. | ||
| func getCellSize() (width, height int) { | ||
| // Use defaults initially | ||
| width = defaultCellWidth | ||
| height = defaultCellHeight | ||
|
|
||
| query := "\033[16t" | ||
| // Use stdin for raw mode check/restore, stdout for writing query | ||
| stdinFd := int(os.Stdin.Fd()) | ||
| stdoutFd := int(os.Stdout.Fd()) | ||
|
|
||
| // Check if stdin/stdout are terminals | ||
| if !term.IsTerminal(stdinFd) || !term.IsTerminal(stdoutFd) { | ||
| log.Printf("Warning: Cannot query cell size: stdin/stdout not a terminal.") | ||
| return | ||
| } | ||
|
|
||
| state, err := term.MakeRaw(stdinFd) | ||
| if err != nil { | ||
| log.Printf("Warning: Cannot query cell size: failed to enter raw mode: %v", err) | ||
| return | ||
| } | ||
| defer term.Restore(stdinFd, state) | ||
|
|
||
| // Write query to stdout | ||
| _, err = os.Stdout.Write([]byte(query)) | ||
| if err != nil { | ||
| log.Printf("Warning: Cannot query cell size: failed to write query: %v", err) | ||
| return | ||
| } | ||
|
|
||
| // Read response from stdin with timeout | ||
| responseChan := make(chan string) | ||
| readErrChan := make(chan error) | ||
| go func() { | ||
| var buf [64]byte // Buffer for response | ||
| n, readErr := os.Stdin.Read(buf[:]) | ||
| if readErr != nil { | ||
| readErrChan <- readErr | ||
| } else if n > 0 { | ||
| responseChan <- string(buf[:n]) | ||
| } else { | ||
| close(responseChan) // Should not happen? | ||
| } | ||
| }() | ||
|
|
||
| var response string | ||
| select { | ||
| case resp := <-responseChan: | ||
| response = resp | ||
| case err = <-readErrChan: | ||
| log.Printf("Warning: Cannot query cell size: failed to read response: %v", err) | ||
| return | ||
| case <-time.After(150 * time.Millisecond): // Increased timeout slightly | ||
| log.Printf("Warning: Cannot query cell size: timeout waiting for response.") | ||
| return | ||
| } | ||
|
|
||
| // Parse response: \033[6;<height>;<width>t | ||
| re := regexp.MustCompile(`\033\[6;(\d+);(\d+)t`) | ||
| matches := re.FindStringSubmatch(response) | ||
|
|
||
| if len(matches) == 3 { | ||
| h, e1 := strconv.Atoi(matches[1]) | ||
| w, e2 := strconv.Atoi(matches[2]) | ||
| if e1 == nil && e2 == nil && h > 0 && w > 0 { | ||
| width = w | ||
| height = h | ||
| return | ||
| } | ||
| } | ||
|
|
||
| log.Printf("Warning: Cannot query cell size: failed to parse response: %q", response) | ||
| return | ||
| } |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,74 @@ | ||
| package kitty | ||
|
|
||
| import ( | ||
| "fmt" | ||
| "os" | ||
| "strings" | ||
| "time" | ||
|
|
||
| "golang.org/x/term" | ||
| ) | ||
|
|
||
| // CheckKittyGraphicsProtocol checks for Kitty graphics protocol support using the query method. | ||
| func CheckKittyGraphicsProtocol() bool { | ||
| // Use a unique ID for the query, e.g., based on process ID or random | ||
| queryID := uint32(os.Getpid() & 0xFFFFFFFF) // Example ID | ||
| if queryID == 0 { | ||
| queryID = 1 // Ensure non-zero ID | ||
| } | ||
| // Graphics query command (dummy 1x1 RGB pixel) | ||
| // https://sw.kovidgoyal.net/kitty/graphics-protocol/#querying-support-and-available-transmission-mediums | ||
| graphicsQuery := fmt.Sprintf("\033_Ga=q,i=%d,s=1,v=1,t=d,f=24;AAAA\033\\", queryID) | ||
|
|
||
| // Need raw mode to send/receive control sequences without shell interference | ||
| fd := int(os.Stdout.Fd()) | ||
| state, err := term.MakeRaw(fd) | ||
| if err != nil { | ||
| return false // Cannot enter raw mode | ||
| } | ||
| defer term.Restore(fd, state) | ||
|
|
||
| // Wrap graphics query for tmux if necessary | ||
| isTmux := os.Getenv("TMUX") != "" | ||
| if isTmux { | ||
| escapedQuery := strings.ReplaceAll(graphicsQuery, "\033", "\033\033") | ||
| graphicsQuery = fmt.Sprintf("\033Ptmux;%s\033\\", escapedQuery) | ||
| } | ||
|
|
||
| // Write only the graphics query | ||
| _, err = os.Stdout.Write([]byte(graphicsQuery)) | ||
| if err != nil { | ||
| return false | ||
| } | ||
| os.Stdout.Sync() | ||
|
|
||
| // Read response with timeout | ||
| responseChan := make(chan string) | ||
| go func() { | ||
| var buf [256]byte | ||
| n, readErr := os.Stdout.Read(buf[:]) | ||
| if readErr == nil && n > 0 { | ||
| responseChan <- string(buf[:n]) | ||
| } else { | ||
| close(responseChan) // Signal no response or error | ||
| } | ||
| }() | ||
|
|
||
| var response string | ||
| select { | ||
| case resp, ok := <-responseChan: | ||
| if ok { | ||
| response = resp | ||
| } | ||
| case <-time.After(10 * time.Millisecond): | ||
| } | ||
|
|
||
| // Check if the response is the graphics protocol ACK | ||
| // Expected format: \033_Gi=<queryID>;OK\033\ (or an error message) | ||
| expectedGraphicsPrefix := fmt.Sprintf("\033_Gi=%d;", queryID) | ||
| if strings.HasPrefix(response, expectedGraphicsPrefix) && strings.HasSuffix(response, "\033\\") { | ||
| return true // Got a graphics response, assume support | ||
| } | ||
| // If we didn't get the graphics response, assume no support | ||
| return false | ||
| } | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,45 @@ | ||
| package kitty | ||
|
|
||
| // We only need the first 256 for row/col/id encoding. | ||
| var diacriticMap = [256]string{ | ||
| "\u0305", "\u030d", "\u030e", "\u0310", "\u0312", "\u033d", "\u033e", "\u033f", // 0-7 | ||
| "\u0346", "\u034a", "\u034b", "\u034c", "\u0350", "\u0351", "\u0352", "\u0357", // 8-15 | ||
| "\u035b", "\u0363", "\u0364", "\u0365", "\u0366", "\u0367", "\u0368", "\u0369", // 16-23 | ||
| "\u036a", "\u036b", "\u036c", "\u036d", "\u036e", "\u036f", "\u0483", "\u0484", // 24-31 | ||
| "\u0485", "\u0486", "\u0487", "\u0592", "\u0593", "\u0594", "\u0595", "\u0597", // 32-39 | ||
| "\u0598", "\u0599", "\u059c", "\u059d", "\u059e", "\u059f", "\u05a0", "\u05a1", // 40-47 | ||
| "\u05a8", "\u05a9", "\u05ab", "\u05ac", "\u05af", "\u05c4", "\u0610", "\u0611", // 48-55 | ||
| "\u0612", "\u0613", "\u0614", "\u0615", "\u0616", "\u0617", "\u0657", "\u0658", // 56-63 | ||
| "\u0659", "\u065a", "\u065b", "\u065d", "\u065e", "\u06d6", "\u06d7", "\u06d8", // 64-71 | ||
| "\u06d9", "\u06da", "\u06db", "\u06dc", "\u06df", "\u06e0", "\u06e1", "\u06e2", // 72-79 | ||
| "\u06e4", "\u06e7", "\u06e8", "\u06eb", "\u06ec", "\u0730", "\u0732", "\u0733", // 80-87 | ||
| "\u0735", "\u0736", "\u073a", "\u073d", "\u073f", "\u0740", "\u0741", "\u0743", // 88-95 | ||
| "\u0745", "\u0747", "\u0749", "\u074a", "\u07eb", "\u07ec", "\u07ed", "\u07ee", // 96-103 | ||
| "\u07ef", "\u07f0", "\u07f1", "\u07f3", "\u0816", "\u0817", "\u0818", "\u0819", // 104-111 | ||
| "\u081b", "\u081c", "\u081d", "\u081e", "\u081f", "\u0820", "\u0821", "\u0822", // 112-119 | ||
| "\u0823", "\u0825", "\u0826", "\u0827", "\u0829", "\u082a", "\u082b", "\u082c", // 120-127 | ||
| "\u082d", "\u0951", "\u0953", "\u0954", "\u0f82", "\u0f83", "\u0f86", "\u0f87", // 128-135 | ||
| "\u135d", "\u135e", "\u135f", "\u17dd", "\u193a", "\u1a17", "\u1a75", "\u1a76", // 136-143 | ||
| "\u1a77", "\u1a78", "\u1a79", "\u1a7a", "\u1a7b", "\u1a7c", "\u1b6b", "\u1b6d", // 144-151 | ||
| "\u1b6e", "\u1b6f", "\u1b70", "\u1b71", "\u1b72", "\u1b73", "\u1cd0", "\u1cd1", // 152-159 | ||
| "\u1cd2", "\u1cda", "\u1cdb", "\u1ce0", "\u1dc0", "\u1dc1", "\u1dc3", "\u1dc4", // 160-167 | ||
| "\u1dc5", "\u1dc6", "\u1dc7", "\u1dc8", "\u1dc9", "\u1dcb", "\u1dcc", "\u1dd1", // 168-175 | ||
| "\u1dd2", "\u1dd3", "\u1dd4", "\u1dd5", "\u1dd6", "\u1dd7", "\u1dd8", "\u1dd9", // 176-183 | ||
| "\u1dda", "\u1ddb", "\u1ddc", "\u1ddd", "\u1dde", "\u1ddf", "\u1de0", "\u1de1", // 184-191 | ||
| "\u1de2", "\u1de3", "\u1de4", "\u1de5", "\u1de6", "\u1dfe", "\u20d0", "\u20d1", // 192-199 | ||
| "\u20d4", "\u20d5", "\u20d6", "\u20d7", "\u20db", "\u20dc", "\u20e1", "\u20e7", // 200-207 | ||
| "\u20e9", "\u20f0", "\u2cef", "\u2cf0", "\u2cf1", "\u2de0", "\u2de1", "\u2de2", // 208-215 | ||
| "\u2de3", "\u2de4", "\u2de5", "\u2de6", "\u2de7", "\u2de8", "\u2de9", "\u2dea", // 216-223 | ||
| "\u2deb", "\u2dec", "\u2ded", "\u2dee", "\u2def", "\u2df0", "\u2df1", "\u2df2", // 224-231 | ||
| "\u2df3", "\u2df4", "\u2df5", "\u2df6", "\u2df7", "\u2df8", "\u2df9", "\u2dfa", // 232-239 | ||
| "\u2dfb", "\u2dfc", "\u2dfd", "\u2dfe", "\u2dff", "\ua66f", "\ua67c", "\ua67d", // 240-247 | ||
| "\ua6f0", "\ua6f1", "\ua8e0", "\ua8e1", "\ua8e2", "\ua8e3", "\ua8e4", "\ua8e5", // 248-255 | ||
| } | ||
|
|
||
| // getDiacritic returns the combining character for a given number 0-255. | ||
| func getDiacritic(num int) string { | ||
| if num >= 0 && num < len(diacriticMap) { | ||
| return diacriticMap[num] | ||
| } | ||
| return "" // Or return a default/error indicator | ||
| } |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Currently, option settings are required, so I thought it would be a good idea to add a comment like the one below.