1const std = @import("std");2const heap = std.heap;3const io = std.io;4const log = std.log;5const mem = std.mem;6const posix = std.posix;7const os = std.os;8const fmt = std.fmt;9const process = std.process;1011const fcft = @import("fcft");12const pixman = @import("pixman");1314const flags = @import("flags.zig");15const Loop = @import("Loop.zig");16const Wayland = @import("Wayland.zig");1718pub const Config = struct {19 height: u16,20 normalFgColor: pixman.Color,21 normalBgColor: pixman.Color,22 focusFgColor: pixman.Color,23 focusBgColor: pixman.Color,24 font: *fcft.Font,25};2627pub const State = struct {28 gpa: mem.Allocator,29 config: Config,30 wayland: Wayland,31 loop: Loop,32};3334pub var state: State = undefined;3536fn parseColor(str: []const u8) !pixman.Color {37 // Color string needs to contain a base prefix.38 // For example: 0xRRGGBB.39 const val = try fmt.parseInt(u24, str, 0);4041 const r: u8 = @truncate(val >> 16);42 const g: u8 = @truncate(val >> 8);43 const b: u8 = @truncate(val);4445 return pixman.Color{46 .red = @as(u16, r) << 8 | 0xff,47 .green = @as(u16, g) << 8 | 0xff,48 .blue = @as(u16, b) << 8 | 0xff,49 .alpha = 0xffff,50 };51}5253fn parseColorFlag(flg: ?[]const u8, def: []const u8) !pixman.Color {54 if (flg) |raw| {55 return parseColor(raw);56 } else {57 return parseColor(def);58 }59}6061fn parseFlags(args: [][*:0]u8) !Config {62 const result = flags.parser([*:0]const u8, &.{63 .{ .name = "hg", .kind = .arg }, // height64 .{ .name = "fn", .kind = .arg }, // font name65 .{ .name = "nf", .kind = .arg }, // normal foreground66 .{ .name = "nb", .kind = .arg }, // normal background67 .{ .name = "ff", .kind = .arg }, // focused foreground68 .{ .name = "fb", .kind = .arg }, // focused background69 }).parse(args) catch {70 usage();71 };7273 var font_names = if (result.flags.@"fn") |raw| blk: {74 break :blk [_][*:0]const u8{raw};75 } else blk: {76 break :blk [_][*:0]const u8{"monospace:size=10"};77 };7879 const font = try fcft.Font.fromName(&font_names, null);80 const height: u16 = if (result.flags.hg) |raw| blk: {81 break :blk try fmt.parseUnsigned(u16, raw, 10);82 } else blk: {83 break :blk @intFromFloat(@as(f32, @floatFromInt(font.height)) * 1.5);84 };8586 return Config{87 .font = font,88 .height = @intCast(height),89 .normalFgColor = try parseColorFlag(result.flags.nf, "0xb8b8b8"),90 .normalBgColor = try parseColorFlag(result.flags.nb, "0x282828"),91 .focusFgColor = try parseColorFlag(result.flags.ff, "0x181818"),92 .focusBgColor = try parseColorFlag(result.flags.fb, "0x7cafc2"),93 };94}9596pub fn usage() noreturn {97 const desc =98 \\usage: creek [-hg HEIGHT] [-fn FONT] [-nf COLOR] [-nb COLOR]99 \\ [-ff COLOR] [-fb COLOR]100 \\101 ;102103 var buffer: [1024]u8 = undefined;104 var serr = std.fs.File.stderr().writer(&buffer);105 serr.interface.writeAll(desc) catch |err| {106 std.debug.panic("{s}", .{@errorName(err)});107 };108 serr.end() catch |err| {109 std.debug.panic("{s}", .{@errorName(err)});110 };111112 process.exit(1);113}114115pub fn main() anyerror!void {116 var gpa: heap.GeneralPurposeAllocator(.{}) = .{};117 defer _ = gpa.deinit();118119 _ = fcft.init(.auto, false, .warning);120 if (fcft.capabilities() & fcft.Capabilities.text_run_shaping == 0) {121 @panic("Support for text run shaping required in fcft and not present");122 }123124 state.gpa = gpa.allocator();125 state.wayland = try Wayland.init();126 state.loop = try Loop.init();127 state.config = parseFlags(os.argv[1..]) catch |err| {128 log.err("Option parsing failed with: {s}", .{@errorName(err)});129 usage();130 };131132 defer {133 state.wayland.deinit();134 }135136 try state.wayland.registerGlobals();137 try state.loop.run();138}