this repo has no description
13
fork

Configure Feed

Select the types of activity you want to include in your feed.

caps!: implement explicit width extension

Implement explicit width hint extension, developed by kitty. When
both explicit width and mode 2027 are available, we default to explicit
width. Custom event loop authors will need to update their loops to add
support for this by setting the new capability value.

For simplicity, we don't actually add a flag in the parser for checking
between a cursor position and an F3 key. Instead, we send the cursor
home, then do an explicit width command, *then* check the cursor
position. If the cursor has moved - meaning the extension is supported -
we will see an F3 key with the shift modifier. The response will be
something like `\x1b[1;2R` which we parse as a shift+F3. But in the
loop, we check the flag if we have sent queries and handle this specific
event differently.

Reference: https://github.com/kovidgoyal/kitty/issues/8226

+57 -3
+1
README.md
··· 30 30 - Color Mode Updates (Mode 2031) 31 31 - [In-Band Resize Reports](https://gist.github.com/rockorager/e695fb2924d36b2bcf1fff4a3704bd83) (Mode 2048) 32 32 - Images ([kitty graphics protocol](https://sw.kovidgoyal.net/kitty/graphics-protocol/)) 33 + - [Explicit Width](https://github.com/kovidgoyal/kitty/blob/master/docs/text-sizing-protocol.rst) (width modifiers only) 33 34 34 35 ## Usage 35 36
+26
src/Loop.zig
··· 177 177 } 178 178 }, 179 179 .key_press => |key| { 180 + // Check for a cursor position response for our explicity width query. This will 181 + // always be an F3 key with shift = true, and we must be looking for queries 182 + if (key.codepoint == vaxis.Key.f3 and 183 + key.mods.shift and 184 + !vx.queries_done.load(.unordered)) 185 + { 186 + log.info("explicit width capability detected", .{}); 187 + vx.caps.explicit_width = true; 188 + vx.caps.unicode = .unicode; 189 + vx.screen.width_method = .unicode; 190 + return; 191 + } 180 192 if (@hasField(Event, "key_press")) { 181 193 // HACK: yuck. there has to be a better way 182 194 var mut_key = key; ··· 198 210 }, 199 211 .cap_da1 => { 200 212 std.Thread.Futex.wake(&vx.query_futex, 10); 213 + vx.queries_done.store(true, .unordered); 201 214 }, 202 215 .mouse => |mouse| { 203 216 if (@hasField(Event, "mouse")) { ··· 220 233 else => { 221 234 switch (event) { 222 235 .key_press => |key| { 236 + // Check for a cursor position response for our explicity width query. This will 237 + // always be an F3 key with shift = true, and we must be looking for queries 238 + if (key.codepoint == vaxis.Key.f3 and 239 + key.mods.shift and 240 + !vx.queries_done.load(.unordered)) 241 + { 242 + log.info("explicit width capability detected", .{}); 243 + vx.caps.explicit_width = true; 244 + vx.caps.unicode = .unicode; 245 + vx.screen.width_method = .unicode; 246 + return; 247 + } 223 248 if (@hasField(Event, "key_press")) { 224 249 // HACK: yuck. there has to be a better way 225 250 var mut_key = key; ··· 311 336 }, 312 337 .cap_da1 => { 313 338 std.Thread.Futex.wake(&vx.query_futex, 10); 339 + vx.queries_done.store(true, .unordered); 314 340 }, 315 341 .winsize => |winsize| { 316 342 vx.state.in_band_resize = true;
+27 -3
src/Vaxis.zig
··· 36 36 unicode: gwidth.Method = .wcwidth, 37 37 sgr_pixels: bool = false, 38 38 color_scheme_updates: bool = false, 39 + explicit_width: bool = false, 39 40 }; 40 41 41 42 pub const Options = struct { ··· 62 63 /// blocks the main thread until a DA1 query has been received, or the 63 64 /// futex times out 64 65 query_futex: atomic.Value(u32) = atomic.Value(u32).init(0), 66 + 67 + /// If Queries were sent, we set this to false. We reset to true when all queries are complete. This 68 + /// is used because we do explicit cursor position reports in the queries, which interfere with F3 69 + /// key encoding. This can be used as a flag to determine how we should evaluate this sequence 70 + queries_done: atomic.Value(bool) = atomic.Value(bool).init(true), 65 71 66 72 // images 67 73 next_img_id: u32 = 1, ··· 236 242 try self.queryTerminalSend(tty); 237 243 // 1 second timeout 238 244 std.Thread.Futex.timedWait(&self.query_futex, 0, timeout_ns) catch {}; 245 + self.queries_done.store(true, .unordered); 239 246 try self.enableDetectedFeatures(tty); 240 247 } 241 248 242 249 /// write queries to the terminal to determine capabilities. This function 243 250 /// is only for use with a custom main loop. Call Vaxis.queryTerminal() if 244 251 /// you are using Loop.run() 245 - pub fn queryTerminalSend(_: Vaxis, tty: AnyWriter) !void { 252 + pub fn queryTerminalSend(vx: *Vaxis, tty: AnyWriter) !void { 253 + vx.queries_done.store(false, .unordered); 246 254 247 255 // TODO: re-enable this 248 256 // const colorterm = std.posix.getenv("COLORTERM") orelse ""; ··· 263 271 ctlseqs.decrqm_unicode ++ 264 272 ctlseqs.decrqm_color_scheme ++ 265 273 ctlseqs.in_band_resize_set ++ 274 + 275 + // Explicit width query. We send the cursor home, then do an explicit width command, then 276 + // query the position. If the parsed value is an F3 with shift, we support explicit width. 277 + // The returned response will be something like \x1b[1;2R...which when parsed as a Key is a 278 + // shift + F3 (the row is ignored). We only care if the column has moved from 1->2, which is 279 + // why we see a Shift modifier 280 + ctlseqs.home ++ 281 + ctlseqs.explicit_width_query ++ 282 + ctlseqs.cursor_position_request ++ 266 283 ctlseqs.xtversion ++ 267 284 ctlseqs.csi_u_query ++ 268 285 ctlseqs.kitty_graphics_query ++ ··· 302 319 if (self.caps.kitty_keyboard) { 303 320 try self.enableKittyKeyboard(tty, self.opts.kitty_keyboard_flags); 304 321 } 305 - if (self.caps.unicode == .unicode) { 322 + // Only enable mode 2027 if we don't have explicit width 323 + if (self.caps.unicode == .unicode and !self.caps.explicit_width) { 306 324 try tty.writeAll(ctlseqs.unicode_set); 307 325 } 308 326 }, ··· 611 629 } 612 630 try tty.print(ctlseqs.osc8, .{ ps, cell.link.uri }); 613 631 } 614 - try tty.writeAll(cell.char.grapheme); 632 + 633 + // If we have explicit width and our width is greater than 1, let's use it 634 + if (self.caps.explicit_width and w > 1) { 635 + try tty.print(ctlseqs.explicit_width, .{ w, cell.char.grapheme }); 636 + } else { 637 + try tty.writeAll(cell.char.grapheme); 638 + } 615 639 cursor_pos.col = col + w; 616 640 cursor_pos.row = row; 617 641 }
+3
src/ctlseqs.zig
··· 11 11 pub const csi_u_query = "\x1b[?u"; 12 12 pub const kitty_graphics_query = "\x1b_Gi=1,a=q\x1b\\"; 13 13 pub const sixel_geometry_query = "\x1b[?2;1;0S"; 14 + pub const cursor_position_request = "\x1b[6n"; 15 + pub const explicit_width_query = "\x1b]66;w=1; \x1b\\"; 14 16 15 17 // mouse. We try for button motion and any motion. terminals will enable the 16 18 // last one we tried (any motion). This was added because zellij doesn't ··· 31 33 // unicode 32 34 pub const unicode_set = "\x1b[?2027h"; 33 35 pub const unicode_reset = "\x1b[?2027l"; 36 + pub const explicit_width = "\x1b]66;w={d};{s}\x1b\\"; 34 37 35 38 // bracketed paste 36 39 pub const bp_set = "\x1b[?2004h";