Skip to content

Commit

Permalink
initial
Browse files Browse the repository at this point in the history
  • Loading branch information
jhickner committed Sep 19, 2024
0 parents commit 42bf2c9
Show file tree
Hide file tree
Showing 14 changed files with 2,000 additions and 0 deletions.
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
.zig-cache/
zig-out/
*.out
*.o
21 changes: 21 additions & 0 deletions LICENSE
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
MIT License

Copyright (c) 2019 Jason Hickner

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
97 changes: 97 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,97 @@
# zerminal

A terminal library for zig.

- [X] set/get cursor position
- [X] styled printing
- [X] events: keyboard, mouse, focus, resize
- [X] misc terminal ops: alternate screen, synchronized updates, save/restore, etc.
- [X] [kitty protocol](https://sw.kovidgoyal.net/kitty/keyboard-protocol/) support
- [ ] windows support

WIP, needs docs.

## including zerminal in your project
```zig
// build.zig.zon
.dependencies = .{
.zerminal = .{
.url = "https://github.com/jhickner/zerminal/archive/master.tar.gz",
// You can this blank initially, then plug in the hash you get from
// the `zig build` error.
.hash = "",
},
// build.zig
const zerminal = b.dependency("zerminal", .{ .target = target, .optimize = optimize });
exe.root_module.addImport("zerminal", zerminal.module("zerminal"));
```

## basic usage
```zig
const std = @import("std");
const z = @import("zerminal");
pub fn main() !void {
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
defer _ = gpa.deinit();
const allocator = gpa.allocator();
const writer = std.io.getStdOut().writer();
// save terminal state
const orig = try z.getTermios();
// enable individual key events
try z.enableRawMode(.BLOCKING);
// enable resize events
try z.enableResizeEvents();
// enable terminal focus change events
try z.enableFocusChange(writer);
// enable kitty protocol events (if supported)
try z.pushKeyboardEnhancementFlags(writer, .{
.report_all_keys_as_escape_codes = true,
.report_event_types = true,
});
// enable mouse events
try z.enableMouseCapture(writer);
// create an event reader
var reader = try z.EventReader.init(allocator);
defer reader.deinit();
// loop printing events until ESC is pressed
while (try reader.readStdin(100)) |oevt| {
if (oevt) |evt| {
switch (evt) {
.key => |key| switch (key.id) {
.esc => break,
else => std.debug.print("{}\n", .{evt}),
},
else => std.debug.print("{}\n", .{evt}),
}
}
} else |err| {
// handle errors
}
// disable terminal flags we enabled
try z.popKeyboardEnhancementFlags(writer);
try z.disableMouseCapture(writer);
try z.disableFocusChange(writer);
// restore original terminal state
try z.setTermios(orig);
}
```

## Acknowledgements
- incorporates format and styling code originally from
[ansi-term](https://github.com/ziglibs/ansi-term) (MIT license)
- Inspired by rust's [crossterm](https://github.com/crossterm-rs/crossterm) library
(although this library has no windows support)
39 changes: 39 additions & 0 deletions build.zig
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
const std = @import("std");

pub fn build(b: *std.Build) void {
const target = b.standardTargetOptions(.{});
const optimize = b.standardOptimizeOption(.{});

_ = b.addModule("zerminal", .{
.root_source_file = b.path("src/zerminal.zig"),
});

const lib = b.addStaticLibrary(.{
.name = "zerminal",
.root_source_file = b.path("src/zerminal.zig"),
.target = target,
.optimize = optimize,
});

b.installArtifact(lib);

const lib_unit_tests = b.addTest(.{
.root_source_file = b.path("src/zerminal.zig"),
.target = target,
.optimize = optimize,
});

const run_lib_unit_tests = b.addRunArtifact(lib_unit_tests);

const test_step = b.step("test", "Run unit tests");
test_step.dependOn(&run_lib_unit_tests.step);

const install_docs = b.addInstallDirectory(.{
.source_dir = lib.getEmittedDocs(),
.install_dir = .prefix,
.install_subdir = "docs",
});

const docs_step = b.step("docs", "Generate documentation");
docs_step.dependOn(&install_docs.step);
}
9 changes: 9 additions & 0 deletions build.zig.zon
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
.{
.name = "zerminal",
.version = "0.0.0",
.paths = .{
"build.zig",
"build.zig.zon",
"src/",
},
}
28 changes: 28 additions & 0 deletions src/clear.zig
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
const std = @import("std");

const esc = "\x1B";
const csi = esc ++ "[";

pub fn clearCurrentLine(writer: anytype) !void {
try writer.writeAll(csi ++ "2K");
}

pub fn clearFromCursorToLineBeginning(writer: anytype) !void {
try writer.writeAll(csi ++ "1K");
}

pub fn clearFromCursorToLineEnd(writer: anytype) !void {
try writer.writeAll(csi ++ "K");
}

pub fn clearScreen(writer: anytype) !void {
try writer.writeAll(csi ++ "2J");
}

pub fn clearFromCursorToScreenBeginning(writer: anytype) !void {
try writer.writeAll(csi ++ "1J");
}

pub fn clearFromCursorToScreenEnd(writer: anytype) !void {
try writer.writeAll(csi ++ "J");
}
106 changes: 106 additions & 0 deletions src/cursor.zig
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
const std = @import("std");
const testing = std.testing;
const fixedBufferStream = std.io.fixedBufferStream;
const csi = "\x1B[";

pub const CursorMode = enum(u8) {
blinking_block = 1,
block,
blinking_underscore,
underscore,
blinking_I_beam,
I_beam,
};

pub fn setCursorMode(writer: anytype, mode: CursorMode) !void {
const modeNumber = @intFromEnum(mode);
try writer.print(csi ++ "{d} q", .{modeNumber});
}

pub fn hideCursor(writer: anytype) !void {
try writer.writeAll(csi ++ "?25l");
}

pub fn showCursor(writer: anytype) !void {
try writer.writeAll(csi ++ "?25h");
}

pub fn saveCursor(writer: anytype) !void {
try writer.writeAll(csi ++ "s");
}

pub fn restoreCursor(writer: anytype) !void {
try writer.writeAll(csi ++ "u");
}

pub fn setCursor(writer: anytype, x: usize, y: usize) !void {
try writer.print(csi ++ "{};{}H", .{ y + 1, x + 1 });
}

pub fn setCursorRow(writer: anytype, row: usize) !void {
try writer.print(csi ++ "{}H", .{row + 1});
}

pub fn setCursorColumn(writer: anytype, column: usize) !void {
try writer.print(csi ++ "{}G", .{column + 1});
}

pub fn cursorUp(writer: anytype, lines: usize) !void {
try writer.print(csi ++ "{}A", .{lines});
}

pub fn cursorDown(writer: anytype, lines: usize) !void {
try writer.print(csi ++ "{}B", .{lines});
}

pub fn cursorForward(writer: anytype, columns: usize) !void {
try writer.print(csi ++ "{}C", .{columns});
}

pub fn cursorBackward(writer: anytype, columns: usize) !void {
try writer.print(csi ++ "{}D", .{columns});
}

pub fn cursorNextLine(writer: anytype, lines: usize) !void {
try writer.print(csi ++ "{}E", .{lines});
}

pub fn cursorPreviousLine(writer: anytype, lines: usize) !void {
try writer.print(csi ++ "{}F", .{lines});
}

pub fn scrollUp(writer: anytype, lines: usize) !void {
try writer.print(csi ++ "{}S", .{lines});
}

pub fn scrollDown(writer: anytype, lines: usize) !void {
try writer.print(csi ++ "{}T", .{lines});
}

pub fn getCursorPos(writer: anytype) !void {
try writer.writeAll(csi ++ "6n");
}

test "test cursor mode BLINKING_UNDERSCORE" {
var buf: [1024]u8 = undefined;
var fixed_buf_stream = fixedBufferStream(&buf);

try setCursorMode(fixed_buf_stream.writer(), .blinking_underscore);
// the space is needed
const expected = csi ++ "3 q";
const actual = fixed_buf_stream.getWritten();

try testing.expectEqualSlices(u8, expected, actual);
}

test "test cursor mode BLINKING_I_BEAM" {
var buf: [1024]u8 = undefined;
var fixed_buf_stream = fixedBufferStream(&buf);

try setCursorMode(fixed_buf_stream.writer(), .blinking_I_beam);
// the space is needed
const expected = csi ++ "5 q";
const actual = fixed_buf_stream.getWritten();

try testing.expectEqualSlices(u8, expected, actual);
}
Loading

0 comments on commit 42bf2c9

Please sign in to comment.