From b64d5befe3203dd882a67a8303b27606c3baa1b3 Mon Sep 17 00:00:00 2001 From: LordMZTE Date: Wed, 16 Nov 2022 22:55:11 +0100 Subject: [PATCH] add openbrowser script --- justfile | 1 + scripts/openbrowser/.gitignore | 5 ++ .../openbrowser/assets/openbrowser.desktop | 7 ++ scripts/openbrowser/build.zig | 40 +++++++++++ scripts/openbrowser/src/ProcessInfo.zig | 67 +++++++++++++++++++ scripts/openbrowser/src/main.zig | 54 +++++++++++++++ 6 files changed, 174 insertions(+) create mode 100644 scripts/openbrowser/.gitignore create mode 100644 scripts/openbrowser/assets/openbrowser.desktop create mode 100644 scripts/openbrowser/build.zig create mode 100644 scripts/openbrowser/src/ProcessInfo.zig create mode 100644 scripts/openbrowser/src/main.zig diff --git a/justfile b/justfile index ffeeb25..fb5e7fb 100644 --- a/justfile +++ b/justfile @@ -21,6 +21,7 @@ install-scripts target=(`echo $HOME` + "/.local"): cd scripts/randomwallpaper && zig build -Drelease-fast -p {{target}} cd scripts/vinput && zig build -Drelease-fast -p {{target}} cd scripts/playtwitch && zig build -Drelease-fast -p {{target}} + cd scripts/openbrowser && zig build -Drelease-fast -p {{target}} cd scripts/prompt && gyro build -Drelease-fast -p {{target}} cd scripts/mzteinit && gyro build -Drelease-fast -p {{target}} diff --git a/scripts/openbrowser/.gitignore b/scripts/openbrowser/.gitignore new file mode 100644 index 0000000..1ae1b10 --- /dev/null +++ b/scripts/openbrowser/.gitignore @@ -0,0 +1,5 @@ +zig-cache/ +zig-out/ +deps.zig +gyro.lock +.gyro diff --git a/scripts/openbrowser/assets/openbrowser.desktop b/scripts/openbrowser/assets/openbrowser.desktop new file mode 100644 index 0000000..50603aa --- /dev/null +++ b/scripts/openbrowser/assets/openbrowser.desktop @@ -0,0 +1,7 @@ +[Desktop Entry] +Name=Open Browser +Comment=Opens the best browser given a link +Exec=openbrowser %u +Type=Application +Categories=Network +MimeType=text/html;text/xml;application/xhtml+xml;x-scheme-handler/http;x-scheme-handler/https; diff --git a/scripts/openbrowser/build.zig b/scripts/openbrowser/build.zig new file mode 100644 index 0000000..6766985 --- /dev/null +++ b/scripts/openbrowser/build.zig @@ -0,0 +1,40 @@ +const std = @import("std"); + +pub fn build(b: *std.build.Builder) void { + // Standard target options allows the person running `zig build` to choose + // what target to build for. Here we do not override the defaults, which + // means any target is allowed, and the default is native. Other options + // for restricting supported target set are available. + const target = b.standardTargetOptions(.{}); + + // Standard release options allow the person running `zig build` to select + // between Debug, ReleaseSafe, ReleaseFast, and ReleaseSmall. + const mode = b.standardReleaseOptions(); + + const exe = b.addExecutable("openbrowser", "src/main.zig"); + exe.setTarget(target); + exe.setBuildMode(mode); + exe.install(); + + const desktop_install_step = b.addInstallFile( + .{ .path = "assets/openbrowser.desktop" }, + "share/applications/openbrowser.desktop", + ); + b.getInstallStep().dependOn(&desktop_install_step.step); + + const run_cmd = exe.run(); + run_cmd.step.dependOn(b.getInstallStep()); + if (b.args) |args| { + run_cmd.addArgs(args); + } + + const run_step = b.step("run", "Run the app"); + run_step.dependOn(&run_cmd.step); + + const exe_tests = b.addTest("src/main.zig"); + exe_tests.setTarget(target); + exe_tests.setBuildMode(mode); + + const test_step = b.step("test", "Run unit tests"); + test_step.dependOn(&exe_tests.step); +} diff --git a/scripts/openbrowser/src/ProcessInfo.zig b/scripts/openbrowser/src/ProcessInfo.zig new file mode 100644 index 0000000..6eedd87 --- /dev/null +++ b/scripts/openbrowser/src/ProcessInfo.zig @@ -0,0 +1,67 @@ +const std = @import("std"); + +running: bool, +exepath: ?[]const u8, + +const Self = @This(); + +/// Frees the data of this ProcessInfo. +/// `alloc` must be the same allocator that was supplied to `get`! +pub fn deinit(self: *Self, alloc: std.mem.Allocator) void { + if (self.exepath) |exepath| + alloc.free(exepath); + self.* = undefined; +} + +/// Gets information about a process given it's name +pub fn get( + name: []const u8, + alloc: std.mem.Allocator, +) !Self { + var proc_dir = try std.fs.openIterableDirAbsolute("/proc", .{}); + defer proc_dir.close(); + + var proc_iter = proc_dir.iterate(); + procs: while (try proc_iter.next()) |proc| { + // Filter directories that aren't PIDs + for (std.fs.path.basename(proc.name)) |letter| + if (!std.ascii.isDigit(letter)) + continue :procs; + + var buf: [512]u8 = undefined; + const cmdline_f = std.fs.openFileAbsolute( + try std.fmt.bufPrint(&buf, "/proc/{s}/cmdline", .{proc.name}), + .{}, + ) catch |e| { + // This just happens when we're dealing with another user's process. + if (e == error.AccessDenied) + continue; + + return e; + }; + + defer cmdline_f.close(); + + const cmdline_data = try cmdline_f.readToEndAlloc(alloc, std.math.maxInt(usize)); + defer alloc.free(cmdline_data); + + var cmdline_splits = std.mem.split(u8, cmdline_data, &.{0}); + const exepath = cmdline_splits.next() orelse return error.InvalidCmdline; + + // this is a startsWith instead of an eql because the arguments in the + // cmdline file are sometimes (and only sometimes!) separated by spaces + // and not null bytes. + if (!std.mem.startsWith(u8, std.fs.path.basename(exepath), name)) + continue; + + return .{ + .running = true, + .exepath = try alloc.dupe(u8, exepath), + }; + } + + return .{ + .running = false, + .exepath = null, + }; +} diff --git a/scripts/openbrowser/src/main.zig b/scripts/openbrowser/src/main.zig new file mode 100644 index 0000000..bc45887 --- /dev/null +++ b/scripts/openbrowser/src/main.zig @@ -0,0 +1,54 @@ +const std = @import("std"); +const ProcessInfo = @import("ProcessInfo.zig"); + +pub const log_level = .debug; + +const browsers = &[_][]const u8{ + "luakit", + "waterfox-g4", + "firefox", + "chromium", +}; + +pub fn main() !void { + if (std.os.argv.len < 2) { + std.log.err("need >=1 argument", .{}); + return error.WrongArgs; + } + + var gpa = std.heap.GeneralPurposeAllocator(.{}){}; + defer _ = gpa.deinit(); + const alloc = gpa.allocator(); + + for (browsers) |browser| { + var info = try ProcessInfo.get(browser, alloc); + defer info.deinit(alloc); + + if (!info.running) + continue; + + std.log.info("found running browser {s}", .{info.exepath.?}); + + try start(browser, alloc); + return; + } + + std.log.info("no running browser, using first choice", .{}); + try start(browsers[0], alloc); +} + +fn start(browser: []const u8, alloc: std.mem.Allocator) !void { + // args to browser will be same length as argv + const argv = try alloc.alloc([]const u8, std.os.argv.len); + defer alloc.free(argv); + argv[0] = browser; + + for (std.os.argv[1..]) |arg, i| { + argv[i + 1] = std.mem.span(arg); + } + + std.log.info("child argv: {s}", .{argv}); + + var child = std.ChildProcess.init(argv, alloc); + _ = try child.spawnAndWait(); +}