diff --git a/src/terminal/stream.zig b/src/terminal/stream.zig
index 6940be082..ecc599a98 100644
--- a/src/terminal/stream.zig
+++ b/src/terminal/stream.zig
@@ -432,6 +432,18 @@ pub fn Stream(comptime Handler: type) type {
},
) else log.warn("unimplemented CSI callback: {}", .{action}),
+ // HPR - Cursor Horizontal Position Relative
+ 'a' => if (@hasDecl(T, "setCursorColRelative")) try self.handler.setCursorColRelative(
+ switch (action.params.len) {
+ 0 => 1,
+ 1 => action.params[0],
+ else => {
+ log.warn("invalid HPR command: {}", .{action});
+ return;
+ },
+ },
+ ) else log.warn("unimplemented CSI callback: {}", .{action}),
+
// Repeat Previous Char (REP)
'b' => if (@hasDecl(T, "printRepeat")) try self.handler.printRepeat(
switch (action.params.len) {
@@ -474,6 +486,18 @@ pub fn Stream(comptime Handler: type) type {
},
) else log.warn("unimplemented CSI callback: {}", .{action}),
+ // VPR - Cursor Vertical Position Relative
+ 'e' => if (@hasDecl(T, "setCursorRowRelative")) try self.handler.setCursorRowRelative(
+ switch (action.params.len) {
+ 0 => 1,
+ 1 => action.params[0],
+ else => {
+ log.warn("invalid VPR command: {}", .{action});
+ return;
+ },
+ },
+ ) else log.warn("unimplemented CSI callback: {}", .{action}),
+
// TBC - Tab Clear
// TODO: test
'g' => if (@hasDecl(T, "tabClear")) try self.handler.tabClear(
diff --git a/src/termio/Exec.zig b/src/termio/Exec.zig
index 87d6600ac..cf6b1ab7c 100644
--- a/src/termio/Exec.zig
+++ b/src/termio/Exec.zig
@@ -1316,10 +1316,24 @@ const StreamHandler = struct {
self.terminal.setCursorPos(self.terminal.screen.cursor.y + 1, col);
}
+ pub fn setCursorColRelative(self: *StreamHandler, offset: u16) !void {
+ self.terminal.setCursorPos(
+ self.terminal.screen.cursor.y + 1,
+ self.terminal.screen.cursor.x + 1 + offset,
+ );
+ }
+
pub fn setCursorRow(self: *StreamHandler, row: u16) !void {
self.terminal.setCursorPos(row, self.terminal.screen.cursor.x + 1);
}
+ pub fn setCursorRowRelative(self: *StreamHandler, offset: u16) !void {
+ self.terminal.setCursorPos(
+ self.terminal.screen.cursor.y + 1 + offset,
+ self.terminal.screen.cursor.x + 1,
+ );
+ }
+
pub fn setCursorPos(self: *StreamHandler, row: u16, col: u16) !void {
self.terminal.setCursorPos(row, col);
}
diff --git a/website/app/vt/hpa/page.mdx b/website/app/vt/hpa/page.mdx
index 67ab76d51..f0cd13278 100644
--- a/website/app/vt/hpa/page.mdx
+++ b/website/app/vt/hpa/page.mdx
@@ -7,3 +7,8 @@ import VTSequence from "@/components/VTSequence";
This sequence performs [cursor position (CUP)](/vt/cup) with `x` set
to the parameterized value and `y` set to the current cursor position.
There is no additional or different behavior for using `HPA`.
+
+Because this invokes `CUP`, the cursor row (`x`) can change if it is
+outside the bounds of the `CUP` operation. For example, if
+[origin mode](#TODO) is set and the current cursor position is outside
+of the scroll region, the row will be adjusted.
diff --git a/website/app/vt/hpr/page.mdx b/website/app/vt/hpr/page.mdx
new file mode 100644
index 000000000..fbc865b52
--- /dev/null
+++ b/website/app/vt/hpr/page.mdx
@@ -0,0 +1,17 @@
+import VTSequence from "@/components/VTSequence";
+
+# Horizontal Position Relative (HPR)
+
+
+
+This sequence performs [cursor position (CUP)](/vt/cup) with `x` set
+to the current cursor column plus `x` and `y` set to the current cursor row.
+There is no additional or different behavior for using `HPR`.
+
+The parameter `x` must be an integer greater than or equal to 1. If `x` is less than
+or equal to 0, adjust `x` to be 1. If `x` is omitted, `x` defaults to 1.
+
+Because this invokes `CUP`, the cursor row (`y`) can change if it is
+outside the bounds of the `CUP` operation. For example, if
+[origin mode](#TODO) is set and the current cursor position is outside
+of the scroll region, the row will be adjusted.
diff --git a/website/app/vt/vpa/page.mdx b/website/app/vt/vpa/page.mdx
index e663b1bbd..f25c745e1 100644
--- a/website/app/vt/vpa/page.mdx
+++ b/website/app/vt/vpa/page.mdx
@@ -7,3 +7,8 @@ import VTSequence from "@/components/VTSequence";
This sequence performs [cursor position (CUP)](/vt/cup) with `y` set
to the parameterized value and `x` set to the current cursor position.
There is no additional or different behavior for using `VPA`.
+
+Because this invokes `CUP`, the cursor column (`y`) can change if it is
+outside the bounds of the `CUP` operation. For example, if
+[origin mode](#TODO) is set and the current cursor position is outside
+of the scroll region, the column will be adjusted.
diff --git a/website/app/vt/vpr/page.mdx b/website/app/vt/vpr/page.mdx
new file mode 100644
index 000000000..990b3185a
--- /dev/null
+++ b/website/app/vt/vpr/page.mdx
@@ -0,0 +1,17 @@
+import VTSequence from "@/components/VTSequence";
+
+# Vertical Position Relative (VPR)
+
+
+
+This sequence performs [cursor position (CUP)](/vt/cup) with `y` set
+to the current cursor row plus `y` and `x` set to the current cursor column.
+There is no additional or different behavior for using `VPR`.
+
+The parameter `y` must be an integer greater than or equal to 1. If `y` is less than
+or equal to 0, adjust `y` to be 1. If `y` is omitted, `y` defaults to 1.
+
+Because this invokes `CUP`, the cursor column (`x`) can change if it is
+outside the bounds of the `CUP` operation. For example, if
+[origin mode](#TODO) is set and the current cursor position is outside
+of the scroll region, the column will be adjusted.