~jamii/focus

d8f770500bcfcc4e3642af99fe39bed39d4a1ab6 — Jamie Brandon 1 year, 7 days ago ad7c52a
Daemonize properly.
3 files changed, 72 insertions(+), 17 deletions(-)

M bin/focus.zig
M lib/focus.zig
M lib/focus/common.zig
M bin/focus.zig => bin/focus.zig +36 -12
@@ 10,38 10,62 @@ pub var gpa = if (builtin.mode == .Debug)
else
    null;

const Action = union(enum) {
    Angel,
    Request: focus.Request,
};

pub fn main() void {
    const allocator = if (builtin.mode == .Debug) &gpa.allocator else std.heap.c_allocator;

    const args = std.process.argsAlloc(allocator) catch unreachable;
    var request: focus.Request = .CreateEmptyWindow;
    var be_angel = false;
    var action: Action = .{ .Request = .CreateEmptyWindow };
    for (args[1..]) |c_arg| {
        const arg: []const u8 = c_arg;
        if (std.mem.startsWith(u8, arg, "--")) {
            if (focus.meta.deepEqual(arg, "--launcher")) {
                request = .CreateLauncherWindow;
            if (focus.meta.deepEqual(arg, "--angel")) {
                action = .Angel;
            } else if (focus.meta.deepEqual(arg, "--launcher")) {
                action = .{ .Request = .CreateLauncherWindow };
            } else {
                focus.common.panic("Unrecognized arg: {}", .{arg});
            }
        } else {
            // TODO this leaks but it's annoying to clean up
            const absolute_filename = std.fs.path.resolve(allocator, &[_][]const u8{arg}) catch focus.common.oom();
            request = .{ .CreateEditorWindow = absolute_filename };
            action = .{ .Request = .{ .CreateEditorWindow = absolute_filename } };
        }
    }
    std.process.argsFree(allocator, args);

    const server_socket = focus.createServerSocket();
    switch (server_socket.state) {
        .Bound => {
            // if we successfully bound the socket then we're the main process
            // TODO daemonize a server and send it a request instead of running directly
            focus.run(allocator, server_socket, request);

    switch (action) {
        .Angel => {
            // no daemon (we're probably in a debugger)
            if (server_socket.state != .Bound)
                focus.common.panic("Couldn't bind server socket", .{});
            focus.run(allocator, server_socket);
        },
        .Unbound => {
            // if the socket is in use then there is already a main process
        .Request => |request| {
            // if we successfully bound the socket then we need to create the daemon
            if (server_socket.state == .Bound) {
                if (focus.daemonize() == .Child) {
                    focus.run(allocator, server_socket);
                    // run doesn't return
                    unreachable;
                }
            }

            // ask the main process to do something
            const client_socket = focus.createClientSocket();
            focus.sendRequest(client_socket, server_socket, request);
            switch (request) {
                .CreateEmptyWindow, .CreateLauncherWindow => {},
                .CreateEditorWindow => |filename| allocator.free(filename),
            }

            // wait until it's done
            const exit_code = focus.waitReply(client_socket);
            std.os.exit(exit_code);
        },

M lib/focus.zig => lib/focus.zig +31 -5
@@ 44,6 44,31 @@ pub const ServerSocket = struct {
    };
};

pub fn daemonize() enum { Parent, Child } {
    // https://stackoverflow.com/questions/17954432/creating-a-daemon-in-linux/17955149#17955149
    if (std.os.fork()) |pid| {
        if (pid != 0) return .Parent;
    } else |err| {
        panic("Failed to fork: {}", .{err});
    }
    {
        const err = c.setsid();
        if (err < 0) panic("Failed to setsid: {}", .{err});
    }
    // TODO
    //c.signal(c.SIGCHLD, std.os.linux.SIG_IGN);
    //c.signal(c.SIGHUP, std.os.linux.SIG_IGN);
    if (std.os.fork()) |pid| {
        if (pid != 0) std.os.exit(0);
    } else |err| {
        panic("Failed to fork: {}", .{err});
    }
    const old_umask = c.umask(0);
    if (std.os.linux.chdir("/home/jamie/") < 0)
        panic("Failed to chdir", .{});
    return .Child;
}

pub fn createServerSocket() ServerSocket {
    const socket_path = if (builtin.mode == .Debug)
        "#focus-debug"


@@ 136,9 161,8 @@ pub fn waitReply(client_socket: std.os.socket_t) u8 {

const ns_per_frame = @divTrunc(1_000_000_000, 60);

pub fn run(allocator: *Allocator, server_socket: ServerSocket, request: Request) void {
pub fn run(allocator: *Allocator, server_socket: ServerSocket) void {
    var app = App.init(allocator, server_socket);
    app.handleRequest(request, null);
    var timer = std.time.Timer.start() catch panic("Couldn't start timer", .{});
    while (true) {
        _ = timer.lap();


@@ 244,13 268,15 @@ pub const App = struct {
        self.allocator.destroy(window);
    }

    pub fn handleRequest(self: *App, request: Request, client_address_o: ?Address) void {
    pub fn handleRequest(self: *App, request: Request, client_address: Address) void {
        switch (request) {
            .CreateEmptyWindow => {
                const new_window = self.registerWindow(Window.init(self, .NotFloating));
                new_window.client_address_o = client_address;
            },
            .CreateLauncherWindow => {
                const new_window = self.registerWindow(Window.init(self, .Floating));
                new_window.client_address_o = client_address;
                // TODO this is a hack - it seems like windows can't receive focus until after their first frame?
                // without this, keypresses sometimes get sent to the current window instead of the new window
                new_window.frame(&[0]c.SDL_Event{});


@@ 258,8 284,8 @@ pub const App = struct {
                new_window.pushView(launcher);
            },
            .CreateEditorWindow => |filename| {
                const new_window = self.registerWindow(Window.init(self, .Floating));
                new_window.client_address_o = client_address_o;
                const new_window = self.registerWindow(Window.init(self, .NotFloating));
                new_window.client_address_o = client_address;
                const new_buffer = self.getBufferFromAbsoluteFilename(filename);
                const new_editor = Editor.init(self, new_buffer, true, true);
                new_window.pushView(new_editor);

M lib/focus/common.zig => lib/focus/common.zig +5 -0
@@ 6,6 6,11 @@ pub const c = @cImport({
    @cInclude("SDL2/SDL_syswm.h");
    @cInclude("SDL2/SDL_opengl.h");
    @cInclude("SDL2/SDL_ttf.h");
    @cInclude("unistd.h");
    @cInclude("signal.h");
    @cInclude("sys/types.h");
    @cInclude("sys/stat.h");
    @cInclude("syslog.h");
});

pub const builtin = @import("builtin");