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 io: Io,29 gpa: mem.Allocator,30 config: Config,31 wayland: Wayland,32 loop: Loop,33};3435pub var state: State = undefined;3637fn parseColor(str: []const u8) !pixman.Color {38 // Color string needs to contain a base prefix.39 // For example: 0xRRGGBB.40 const val = try fmt.parseInt(u24, str, 0);4142 const r: u8 = @truncate(val >> 16);43 const g: u8 = @truncate(val >> 8);44 const b: u8 = @truncate(val);4546 return pixman.Color{47 .red = @as(u16, r) << 8 | 0xff,48 .green = @as(u16, g) << 8 | 0xff,49 .blue = @as(u16, b) << 8 | 0xff,50 .alpha = 0xffff,51 };52}5354fn parseColorFlag(flg: ?[]const u8, def: []const u8) !pixman.Color {55 if (flg) |raw| {56 return parseColor(raw);57 } else {58 return parseColor(def);59 }60}6162fn parseFlags(args: []const [:0]const u8) !Config {63 const result = flags.parser([:0]const u8, &.{64 .{ .name = "hg", .kind = .arg }, // height65 .{ .name = "fn", .kind = .arg }, // font name66 .{ .name = "nf", .kind = .arg }, // normal foreground67 .{ .name = "nb", .kind = .arg }, // normal background68 .{ .name = "ff", .kind = .arg }, // focused foreground69 .{ .name = "fb", .kind = .arg }, // focused background70 }).parse(args) catch {71 usage();72 };7374 var font_names = if (result.flags.@"fn") |raw| blk: {75 break :blk [_][*:0]const u8{raw};76 } else blk: {77 break :blk [_][*:0]const u8{"monospace:size=10"};78 };7980 const font = try fcft.Font.fromName(&font_names, null);81 const height: u16 = if (result.flags.hg) |raw| blk: {82 break :blk try fmt.parseUnsigned(u16, raw, 10);83 } else blk: {84 break :blk @intFromFloat(@as(f32, @floatFromInt(font.height)) * 1.5);85 };8687 return Config{88 .font = font,89 .height = @intCast(height),90 .normalFgColor = try parseColorFlag(result.flags.nf, "0xb8b8b8"),91 .normalBgColor = try parseColorFlag(result.flags.nb, "0x282828"),92 .focusFgColor = try parseColorFlag(result.flags.ff, "0x181818"),93 .focusBgColor = try parseColorFlag(result.flags.fb, "0x7cafc2"),94 };95}9697pub fn usage() noreturn {98 const desc =99 \\usage: creek [-hg HEIGHT] [-fn FONT] [-nf COLOR] [-nb COLOR]100 \\ [-ff COLOR] [-fb COLOR]101 \\102 ;103104 var buffer: [1024]u8 = undefined;105 var serr = Io.File.stderr().writer(state.io, &buffer);106 serr.interface.writeAll(desc) catch |err| {107 std.debug.panic("{s}", .{@errorName(err)});108 };109 serr.end() catch |err| {110 std.debug.panic("{s}", .{@errorName(err)});111 };112113 process.exit(1);114}115116pub fn main(init: process.Init) anyerror!void {117 _ = fcft.init(.auto, false, .warning);118 if (fcft.capabilities() & fcft.Capabilities.text_run_shaping == 0) {119 @panic("Support for text run shaping required in fcft and not present");120 }121122 state.io = init.io;123 state.gpa = init.gpa;124 state.wayland = try Wayland.init();125 state.loop = try Loop.init();126 var args = try init.minimal.args.toSlice(state.gpa);127 state.config = parseFlags(args[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}