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;
10
11const fcft = @import("fcft");
12const pixman = @import("pixman");
13
14const flags = @import("flags.zig");
15const Loop = @import("Loop.zig");
16const Wayland = @import("Wayland.zig");
17
18pub 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};
26
27pub const State = struct {
28 gpa: mem.Allocator,
29 config: Config,
30 wayland: Wayland,
31 loop: Loop,
32};
33
34pub var state: State = undefined;
35
36fn 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);
40
41 const r: u8 = @truncate(val >> 16);
42 const g: u8 = @truncate(val >> 8);
43 const b: u8 = @truncate(val);
44
45 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}
52
53fn 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}
60
61fn parseFlags(args: [][*:0]u8) !Config {
62 const result = flags.parser([*:0]const u8, &.{
63 .{ .name = "hg", .kind = .arg }, // height
64 .{ .name = "fn", .kind = .arg }, // font name
65 .{ .name = "nf", .kind = .arg }, // normal foreground
66 .{ .name = "nb", .kind = .arg }, // normal background
67 .{ .name = "ff", .kind = .arg }, // focused foreground
68 .{ .name = "fb", .kind = .arg }, // focused background
69 }).parse(args) catch {
70 usage();
71 };
72
73 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 };
78
79 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 };
85
86 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}
95
96pub fn usage() noreturn {
97 const desc =
98 \\usage: creek [-hg HEIGHT] [-fn FONT] [-nf COLOR] [-nb COLOR]
99 \\ [-ff COLOR] [-fb COLOR]
100 \\
101 ;
102
103 io.getStdErr().writeAll(desc) catch |err| {
104 std.debug.panic("{s}", .{@errorName(err)});
105 };
106
107 process.exit(1);
108}
109
110pub fn main() anyerror!void {
111 var gpa: heap.GeneralPurposeAllocator(.{}) = .{};
112 defer _ = gpa.deinit();
113
114 _ = fcft.init(.auto, false, .warning);
115 if (fcft.capabilities() & fcft.Capabilities.text_run_shaping == 0) {
116 @panic("Support for text run shaping required in fcft and not present");
117 }
118
119 state.gpa = gpa.allocator();
120 state.wayland = try Wayland.init();
121 state.loop = try Loop.init();
122 state.config = parseFlags(os.argv[1..]) catch |err| {
123 log.err("Option parsing failed with: {s}", .{@errorName(err)});
124 usage();
125 };
126
127 defer {
128 state.wayland.deinit();
129 }
130
131 try state.wayland.registerGlobals();
132 try state.loop.run();
133}