Zig uncooked terminal library.

feat(repo): First commit.

+509
+2
.gitignore
··· 1 + zig-cache/ 2 + zig-out/
+121
LICENSE
··· 1 + Creative Commons Legal Code 2 + 3 + CC0 1.0 Universal 4 + 5 + CREATIVE COMMONS CORPORATION IS NOT A LAW FIRM AND DOES NOT PROVIDE 6 + LEGAL SERVICES. DISTRIBUTION OF THIS DOCUMENT DOES NOT CREATE AN 7 + ATTORNEY-CLIENT RELATIONSHIP. CREATIVE COMMONS PROVIDES THIS 8 + INFORMATION ON AN "AS-IS" BASIS. CREATIVE COMMONS MAKES NO WARRANTIES 9 + REGARDING THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS 10 + PROVIDED HEREUNDER, AND DISCLAIMS LIABILITY FOR DAMAGES RESULTING FROM 11 + THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS PROVIDED 12 + HEREUNDER. 13 + 14 + Statement of Purpose 15 + 16 + The laws of most jurisdictions throughout the world automatically confer 17 + exclusive Copyright and Related Rights (defined below) upon the creator 18 + and subsequent owner(s) (each and all, an "owner") of an original work of 19 + authorship and/or a database (each, a "Work"). 20 + 21 + Certain owners wish to permanently relinquish those rights to a Work for 22 + the purpose of contributing to a commons of creative, cultural and 23 + scientific works ("Commons") that the public can reliably and without fear 24 + of later claims of infringement build upon, modify, incorporate in other 25 + works, reuse and redistribute as freely as possible in any form whatsoever 26 + and for any purposes, including without limitation commercial purposes. 27 + These owners may contribute to the Commons to promote the ideal of a free 28 + culture and the further production of creative, cultural and scientific 29 + works, or to gain reputation or greater distribution for their Work in 30 + part through the use and efforts of others. 31 + 32 + For these and/or other purposes and motivations, and without any 33 + expectation of additional consideration or compensation, the person 34 + associating CC0 with a Work (the "Affirmer"), to the extent that he or she 35 + is an owner of Copyright and Related Rights in the Work, voluntarily 36 + elects to apply CC0 to the Work and publicly distribute the Work under its 37 + terms, with knowledge of his or her Copyright and Related Rights in the 38 + Work and the meaning and intended legal effect of CC0 on those rights. 39 + 40 + 1. Copyright and Related Rights. A Work made available under CC0 may be 41 + protected by copyright and related or neighboring rights ("Copyright and 42 + Related Rights"). Copyright and Related Rights include, but are not 43 + limited to, the following: 44 + 45 + i. the right to reproduce, adapt, distribute, perform, display, 46 + communicate, and translate a Work; 47 + ii. moral rights retained by the original author(s) and/or performer(s); 48 + iii. publicity and privacy rights pertaining to a person's image or 49 + likeness depicted in a Work; 50 + iv. rights protecting against unfair competition in regards to a Work, 51 + subject to the limitations in paragraph 4(a), below; 52 + v. rights protecting the extraction, dissemination, use and reuse of data 53 + in a Work; 54 + vi. database rights (such as those arising under Directive 96/9/EC of the 55 + European Parliament and of the Council of 11 March 1996 on the legal 56 + protection of databases, and under any national implementation 57 + thereof, including any amended or successor version of such 58 + directive); and 59 + vii. other similar, equivalent or corresponding rights throughout the 60 + world based on applicable law or treaty, and any national 61 + implementations thereof. 62 + 63 + 2. Waiver. To the greatest extent permitted by, but not in contravention 64 + of, applicable law, Affirmer hereby overtly, fully, permanently, 65 + irrevocably and unconditionally waives, abandons, and surrenders all of 66 + Affirmer's Copyright and Related Rights and associated claims and causes 67 + of action, whether now known or unknown (including existing as well as 68 + future claims and causes of action), in the Work (i) in all territories 69 + worldwide, (ii) for the maximum duration provided by applicable law or 70 + treaty (including future time extensions), (iii) in any current or future 71 + medium and for any number of copies, and (iv) for any purpose whatsoever, 72 + including without limitation commercial, advertising or promotional 73 + purposes (the "Waiver"). Affirmer makes the Waiver for the benefit of each 74 + member of the public at large and to the detriment of Affirmer's heirs and 75 + successors, fully intending that such Waiver shall not be subject to 76 + revocation, rescission, cancellation, termination, or any other legal or 77 + equitable action to disrupt the quiet enjoyment of the Work by the public 78 + as contemplated by Affirmer's express Statement of Purpose. 79 + 80 + 3. Public License Fallback. Should any part of the Waiver for any reason 81 + be judged legally invalid or ineffective under applicable law, then the 82 + Waiver shall be preserved to the maximum extent permitted taking into 83 + account Affirmer's express Statement of Purpose. In addition, to the 84 + extent the Waiver is so judged Affirmer hereby grants to each affected 85 + person a royalty-free, non transferable, non sublicensable, non exclusive, 86 + irrevocable and unconditional license to exercise Affirmer's Copyright and 87 + Related Rights in the Work (i) in all territories worldwide, (ii) for the 88 + maximum duration provided by applicable law or treaty (including future 89 + time extensions), (iii) in any current or future medium and for any number 90 + of copies, and (iv) for any purpose whatsoever, including without 91 + limitation commercial, advertising or promotional purposes (the 92 + "License"). The License shall be deemed effective as of the date CC0 was 93 + applied by Affirmer to the Work. Should any part of the License for any 94 + reason be judged legally invalid or ineffective under applicable law, such 95 + partial invalidity or ineffectiveness shall not invalidate the remainder 96 + of the License, and in such case Affirmer hereby affirms that he or she 97 + will not (i) exercise any of his or her remaining Copyright and Related 98 + Rights in the Work or (ii) assert any associated claims and causes of 99 + action with respect to the Work, in either case contrary to Affirmer's 100 + express Statement of Purpose. 101 + 102 + 4. Limitations and Disclaimers. 103 + 104 + a. No trademark or patent rights held by Affirmer are waived, abandoned, 105 + surrendered, licensed or otherwise affected by this document. 106 + b. Affirmer offers the Work as-is and makes no representations or 107 + warranties of any kind concerning the Work, express, implied, 108 + statutory or otherwise, including without limitation warranties of 109 + title, merchantability, fitness for a particular purpose, non 110 + infringement, or the absence of latent or other defects, accuracy, or 111 + the present or absence of errors, whether or not discoverable, all to 112 + the greatest extent permissible under applicable law. 113 + c. Affirmer disclaims responsibility for clearing rights of other persons 114 + that may apply to the Work or any use thereof, including without 115 + limitation any person's Copyright and Related Rights in the Work. 116 + Further, Affirmer disclaims responsibility for obtaining any necessary 117 + consents, permissions or other rights required for any use of the 118 + Work. 119 + d. Affirmer understands and acknowledges that Creative Commons is not a 120 + party to this document and has no duty or obligation with respect to 121 + this CC0 or use of the Work.
+4
README.md
··· 1 + # ZUT 2 + The Zig Uncooked Terminal library. 3 + 4 + Right now it only supplies a `zut.get_key()` for retrieving single key presses.
+70
build.zig
··· 1 + const std = @import("std"); 2 + 3 + // Although this function looks imperative, note that its job is to 4 + // declaratively construct a build graph that will be executed by an external 5 + // runner. 6 + pub fn build(b: *std.Build) void { 7 + // Standard target options allows the person running `zig build` to choose 8 + // what target to build for. Here we do not override the defaults, which 9 + // means any target is allowed, and the default is native. Other options 10 + // for restricting supported target set are available. 11 + const target = b.standardTargetOptions(.{}); 12 + 13 + // Standard optimization options allow the person running `zig build` to select 14 + // between Debug, ReleaseSafe, ReleaseFast, and ReleaseSmall. Here we do not 15 + // set a preferred release mode, allowing the user to decide how to optimize. 16 + const optimize = b.standardOptimizeOption(.{}); 17 + 18 + const exe = b.addExecutable(.{ 19 + .name = "io", 20 + // In this case the main source file is merely a path, however, in more 21 + // complicated build scripts, this could be a generated file. 22 + .root_source_file = .{ .path = "src/main.zig" }, 23 + .target = target, 24 + .optimize = optimize, 25 + }); 26 + 27 + // This declares intent for the executable to be installed into the 28 + // standard location when the user invokes the "install" step (the default 29 + // step when running `zig build`). 30 + b.installArtifact(exe); 31 + 32 + // This *creates* a Run step in the build graph, to be executed when another 33 + // step is evaluated that depends on it. The next line below will establish 34 + // such a dependency. 35 + const run_cmd = b.addRunArtifact(exe); 36 + 37 + // By making the run step depend on the install step, it will be run from the 38 + // installation directory rather than directly from within the cache directory. 39 + // This is not necessary, however, if the application depends on other installed 40 + // files, this ensures they will be present and in the expected location. 41 + run_cmd.step.dependOn(b.getInstallStep()); 42 + 43 + // This allows the user to pass arguments to the application in the build 44 + // command itself, like this: `zig build run -- arg1 arg2 etc` 45 + if (b.args) |args| { 46 + run_cmd.addArgs(args); 47 + } 48 + 49 + // This creates a build step. It will be visible in the `zig build --help` menu, 50 + // and can be selected like this: `zig build run` 51 + // This will evaluate the `run` step rather than the default, which is "install". 52 + const run_step = b.step("run", "Run the app"); 53 + run_step.dependOn(&run_cmd.step); 54 + 55 + // Creates a step for unit testing. This only builds the test executable 56 + // but does not run it. 57 + const unit_tests = b.addTest(.{ 58 + .root_source_file = .{ .path = "src/main.zig" }, 59 + .target = target, 60 + .optimize = optimize, 61 + }); 62 + 63 + const run_unit_tests = b.addRunArtifact(unit_tests); 64 + 65 + // Similar to creating the run step earlier, this exposes a `test` step to 66 + // the `zig build --help` menu, providing a way for the user to request 67 + // running the unit tests. 68 + const test_step = b.step("test", "Run unit tests"); 69 + test_step.dependOn(&run_unit_tests.step); 70 + }
+15
src/main.zig
··· 1 + const std = @import("std"); 2 + const zut = @import("zut.zig"); 3 + const io = std.io; 4 + 5 + const stdout = io.getStdOut().writer(); 6 + 7 + pub fn main() !void { 8 + const key = try zut.get_key(); 9 + 10 + switch (key) { 11 + .a => try stdout.print("a", .{}), 12 + .b => try stdout.print("b", .{}), 13 + else => try stdout.print("Not supported: {}", .{key}), 14 + } 15 + }
+297
src/zut.zig
··· 1 + const std = @import("std"); 2 + const os = std.os; 3 + const fs = std.fs; 4 + 5 + // termios setup: https://zig.news/lhp/want-to-create-a-tui-application-the-basics-of-uncooked-terminal-io-17gm 6 + const Key = enum { 7 + NUL, 8 + SOH, 9 + STX, 10 + ETX, 11 + EOT, 12 + ENQ, 13 + ACK, 14 + BEL, 15 + BS, 16 + TAB, 17 + LF, 18 + VT, 19 + FF, 20 + CR, 21 + SO, 22 + SI, 23 + DLE, 24 + DC1, 25 + DC2, 26 + DC3, 27 + DC4, 28 + NAK, 29 + SYN, 30 + ETB, 31 + CAN, 32 + EM, 33 + SUB, 34 + ESC, 35 + FS, 36 + GS, 37 + RS, 38 + US, 39 + Space, 40 + exclamation_point, 41 + double_quote, 42 + octothorpe, 43 + dollar_sign, 44 + percent_sign, 45 + ampersand, 46 + quote, 47 + l_paren, 48 + r_paren, 49 + star, 50 + plus, 51 + comma, 52 + minus, 53 + period, 54 + slash, 55 + zero, 56 + one, 57 + two, 58 + three, 59 + four, 60 + five, 61 + six, 62 + seven, 63 + eight, 64 + nine, 65 + colon, 66 + semicolon, 67 + lt, 68 + eq, 69 + gt, 70 + question_mark, 71 + at, 72 + A, 73 + B, 74 + C, 75 + D, 76 + E, 77 + F, 78 + G, 79 + H, 80 + I, 81 + J, 82 + K, 83 + L, 84 + M, 85 + N, 86 + O, 87 + P, 88 + Q, 89 + R, 90 + S, 91 + T, 92 + U, 93 + V, 94 + W, 95 + X, 96 + Y, 97 + Z, 98 + l_bracket, 99 + backslash, 100 + r_bracket, 101 + exp, 102 + underscore, 103 + backtick, 104 + a, 105 + b, 106 + c, 107 + d, 108 + e, 109 + f, 110 + g, 111 + h, 112 + i, 113 + j, 114 + k, 115 + l, 116 + m, 117 + n, 118 + o, 119 + p, 120 + q, 121 + r, 122 + s, 123 + t, 124 + u, 125 + v, 126 + w, 127 + x, 128 + y, 129 + z, 130 + l_brace, 131 + pipe, 132 + r_brace, 133 + simmetric, 134 + }; 135 + 136 + pub fn get_key() !Key { 137 + var tty: fs.File = try fs.cwd().openFile("/dev/tty", .{ .mode = fs.File.OpenMode.read_write }); 138 + defer tty.close(); 139 + 140 + const original_termios = try os.tcgetattr(tty.handle); 141 + var raw = original_termios; 142 + 143 + raw.lflag &= ~@as(os.system.tcflag_t, os.system.ECHO | os.system.ICANON | os.system.ISIG | os.system.IEXTEN); 144 + raw.iflag &= ~@as(os.system.tcflag_t, os.system.IXON | os.system.ICRNL | os.system.BRKINT | os.system.INPCK | os.system.ISTRIP); 145 + raw.oflag &= ~@as(os.system.tcflag_t, os.system.OPOST); 146 + raw.cflag |= os.system.CS8; 147 + 148 + raw.cc[os.system.V.TIME] = 0; 149 + raw.cc[os.system.V.MIN] = 1; 150 + 151 + try os.tcsetattr(tty.handle, .FLUSH, raw); 152 + defer os.tcsetattr(tty.handle, .FLUSH, original_termios) catch {}; 153 + 154 + try tty.writeAll("\x1B[s"); // Save cursor position. 155 + try tty.writeAll("\x1B[?25l"); // Hide the cursor. 156 + defer tty.writeAll("\x1B[u") catch {}; // Restore cursor position. 157 + 158 + // try tty.writeAll("\x1B[?47h"); // Save screen. 159 + // defer tty.writeAll("\x1B[?47l") catch {}; // Restore screen. 160 + 161 + // try tty.writeAll("\x1B[?1049h"); // Enable alternative buffer. 162 + // defer tty.writeAll("\x1B[?1049l") catch {}; // Disable alternative buffer. 163 + 164 + var buffer: [1]u8 = undefined; 165 + _ = try tty.read(&buffer); 166 + 167 + return switch (buffer[0]) { 168 + 0 => .NUL, 169 + 1 => .SOH, 170 + 2 => .STX, 171 + 3 => .ETX, 172 + 4 => .EOT, 173 + 5 => .ENQ, 174 + 6 => .ACK, 175 + 7 => .BEL, 176 + 8 => .BS, 177 + 9 => .TAB, 178 + 10 => .LF, 179 + 11 => .VT, 180 + 12 => .FF, 181 + 13 => .CR, 182 + 14 => .SO, 183 + 15 => .SI, 184 + 16 => .DLE, 185 + 17 => .DC1, 186 + 18 => .DC2, 187 + 19 => .DC3, 188 + 20 => .DC4, 189 + 21 => .NAK, 190 + 22 => .SYN, 191 + 23 => .ETB, 192 + 24 => .CAN, 193 + 25 => .EM, 194 + 26 => .SUB, 195 + 27 => .ESC, 196 + 28 => .FS, 197 + 29 => .GS, 198 + 30 => .RS, 199 + 31 => .US, 200 + 32 => .Space, 201 + 33 => .exclamation_point, 202 + 34 => .double_quote, 203 + 35 => .octothorpe, 204 + 36 => .dollar_sign, 205 + 37 => .percent_sign, 206 + 38 => .ampersand, 207 + 39 => .quote, 208 + 40 => .l_paren, 209 + 41 => .r_paren, 210 + 42 => .star, 211 + 43 => .plus, 212 + 44 => .comma, 213 + 45 => .minus, 214 + 46 => .period, 215 + 47 => .slash, 216 + 48 => .zero, 217 + 49 => .one, 218 + 50 => .two, 219 + 51 => .three, 220 + 52 => .four, 221 + 53 => .five, 222 + 54 => .six, 223 + 55 => .seven, 224 + 56 => .eight, 225 + 57 => .nine, 226 + 58 => .colon, 227 + 59 => .semicolon, 228 + 60 => .lt, 229 + 61 => .eq, 230 + 62 => .gt, 231 + 63 => .question_mark, 232 + 64 => .at, 233 + 65 => .A, 234 + 66 => .B, 235 + 67 => .C, 236 + 68 => .D, 237 + 69 => .E, 238 + 70 => .F, 239 + 71 => .G, 240 + 72 => .H, 241 + 73 => .I, 242 + 74 => .J, 243 + 75 => .K, 244 + 76 => .L, 245 + 77 => .M, 246 + 78 => .N, 247 + 79 => .O, 248 + 80 => .P, 249 + 81 => .Q, 250 + 82 => .R, 251 + 83 => .S, 252 + 84 => .T, 253 + 85 => .U, 254 + 86 => .V, 255 + 87 => .W, 256 + 88 => .X, 257 + 89 => .Y, 258 + 90 => .Z, 259 + 91 => .l_bracket, 260 + 92 => .backslash, 261 + 93 => .r_bracket, 262 + 94 => .exp, 263 + 95 => .underscore, 264 + 96 => .backtick, 265 + 97 => .a, 266 + 98 => .b, 267 + 99 => .c, 268 + 100 => .d, 269 + 101 => .e, 270 + 102 => .f, 271 + 103 => .g, 272 + 104 => .h, 273 + 105 => .i, 274 + 106 => .j, 275 + 107 => .k, 276 + 108 => .l, 277 + 109 => .m, 278 + 110 => .n, 279 + 111 => .o, 280 + 112 => .p, 281 + 113 => .q, 282 + 114 => .r, 283 + 115 => .s, 284 + 116 => .t, 285 + 117 => .u, 286 + 118 => .v, 287 + 119 => .w, 288 + 120 => .x, 289 + 121 => .y, 290 + 122 => .z, 291 + 123 => .l_brace, 292 + 124 => .pipe, 293 + 125 => .r_brace, 294 + 126 => .simmetric, 295 + else => .NUL, 296 + }; 297 + }