pluto/src/kernel/tty.zig

110 lines
3.6 KiB
Zig
Raw Normal View History

const std = @import("std");
const fmt = std.fmt;
2020-06-12 12:05:50 +01:00
const Allocator = std.mem.Allocator;
const build_options = @import("build_options");
2020-06-12 12:05:50 +01:00
const arch = @import("arch.zig").internals;
const log = @import("log.zig");
const panic = @import("panic.zig").panic;
/// The OutStream for the format function
2020-06-12 12:05:50 +01:00
const OutStream = std.io.OutStream(void, anyerror, printCallback);
pub const TTY = struct {
/// Print a already-formatted string
print: fn ([]const u8) anyerror!void,
/// Set the TTY cursor position to a row and column
setCursor: fn (u8, u8) void,
/// Clear the screen and set the cursor to top left. The default implementation will be used if null
clear: ?fn () void,
/// The number of character rows supported
rows: u8,
/// The number of character columns supported
cols: u8,
};
2020-06-12 12:05:50 +01:00
/// The current tty stream
var tty: TTY = undefined;
var allocator: *Allocator = undefined;
///
2020-06-12 12:05:50 +01:00
/// A call back function for use in the formation of a string. This calls the architecture's print function.
///
/// Arguments:
/// IN ctx: void - The context of the printing. This will be empty.
/// IN str: []const u8 - The string to print.
///
2020-06-12 12:05:50 +01:00
/// Return: usize
/// The number of characters printed
///
2020-06-12 12:05:50 +01:00
fn printCallback(ctx: void, str: []const u8) !usize {
tty.print(str) catch |e| panic(@errorReturnTrace(), "Failed to print to tty: {}\n", .{e});
return str.len;
}
///
/// Print a formatted string to the terminal in the current colour. This used the standard zig
/// formatting.
///
/// Arguments:
/// IN comptime format: []const u8 - The format string to print
2020-07-12 18:06:54 +01:00
/// IN args: anytype - The arguments to be used in the formatted string
///
2020-07-12 18:06:54 +01:00
pub fn print(comptime format: []const u8, args: anytype) void {
// Printing can't error because of the scrolling, if it does, we have a big problem
fmt.format(OutStream{ .context = {} }, format, args) catch |e| {
2020-01-01 19:12:36 +00:00
log.logError("TTY: Error printing. Error: {}\n", .{e});
};
}
///
2020-06-12 12:05:50 +01:00
/// Clear the screen by printing a space at each cursor position. Sets the cursor to the top left (0, 0)
///
2020-06-12 12:05:50 +01:00
pub fn clear() void {
if (tty.clear) |clr| {
clr();
} else {
// Try to allocate the number of spaces for a whole row to avoid calling print too many times
var spaces = allocator.alloc(u8, tty.cols + 1) catch |e| switch (e) {
Allocator.Error.OutOfMemory => {
var row: u8 = 0;
// If we can't allocate the spaces then try the unoptimised way instead
while (row < tty.rows) : (row += 1) {
var col: u8 = 0;
while (col < tty.cols) : (col += 1) {
print(" ", .{});
}
print("\n", .{});
}
tty.setCursor(0, 0);
return;
},
};
2020-06-12 12:05:50 +01:00
defer allocator.free(spaces);
2020-06-12 12:05:50 +01:00
var col: u8 = 0;
while (col < tty.cols) : (col += 1) {
spaces[col] = " "[0];
}
2020-06-12 12:05:50 +01:00
spaces[col] = "\n"[0];
var row: u8 = 0;
while (row < tty.rows) : (row += 1) {
print("{}", .{spaces});
}
2020-06-12 12:05:50 +01:00
tty.setCursor(0, 0);
}
}
///
2020-06-12 12:05:50 +01:00
/// Initialise the TTY. The details of which are up to the architecture
///
/// Arguments:
2020-06-12 12:05:50 +01:00
/// IN alloc: *std.mem.Allocator - The allocator to use when requiring memory
/// IN boot_payload: arch.BootPayload - The payload passed to the kernel on boot
///
2020-06-12 12:05:50 +01:00
pub fn init(alloc: *Allocator, boot_payload: arch.BootPayload) void {
2020-01-01 19:12:36 +00:00
log.logInfo("Init tty\n", .{});
defer log.logInfo("Done tty\n", .{});
2020-06-12 12:05:50 +01:00
tty = arch.initTTY(boot_payload);
allocator = alloc;
}