|
| 1 | +// zig build-lib -dynamic -O ReleaseFast -femit-bin=reaper_zig.so hello_world.zig |
| 2 | + |
| 3 | +const std = @import("std"); |
| 4 | +const ImGui = @import("reaper_imgui"); |
| 5 | +const reaper = struct { // @import("reaper"); |
| 6 | + pub const PLUGIN_VERSION = 0x20E; |
| 7 | + |
| 8 | + pub const HINSTANCE = *opaque {}; |
| 9 | + pub const HWND = *opaque {}; |
| 10 | + pub const KbdSectionInfo = opaque {}; |
| 11 | + |
| 12 | + pub const plugin_info_t = extern struct { |
| 13 | + caller_version: c_int, |
| 14 | + hwnd_main: HWND, |
| 15 | + register: ?@TypeOf(plugin_register), |
| 16 | + getFunc: ?@TypeOf(plugin_getapi), |
| 17 | + }; |
| 18 | + |
| 19 | + pub const custom_action_register_t = extern struct { |
| 20 | + section: c_int, |
| 21 | + id_str: [*:0]const u8, |
| 22 | + name: [*:0]const u8, |
| 23 | + extra: ?*anyopaque = null, |
| 24 | + }; |
| 25 | + |
| 26 | + pub fn init(rec: *plugin_info_t) bool { |
| 27 | + if(rec.caller_version != PLUGIN_VERSION) { |
| 28 | + std.debug.print("expected REAPER API version {x}, got {x}\n", |
| 29 | + .{ PLUGIN_VERSION, rec.caller_version }); |
| 30 | + return false; |
| 31 | + } |
| 32 | + |
| 33 | + const getFunc = rec.getFunc.?; |
| 34 | + inline for(@typeInfo(@This()).Struct.decls) |decl| { |
| 35 | + comptime var decl_type = @typeInfo(@TypeOf(@field(@This(), decl.name))); |
| 36 | + const is_optional = decl_type == .Optional; |
| 37 | + if(is_optional) |
| 38 | + decl_type = @typeInfo(decl_type.Optional.child); |
| 39 | + if(decl_type != .Pointer or @typeInfo(decl_type.Pointer.child) != .Fn) |
| 40 | + continue; |
| 41 | + if(getFunc(decl.name)) |func| |
| 42 | + @field(@This(), decl.name) = @ptrCast(func) |
| 43 | + else if(is_optional) |
| 44 | + @field(@This(), decl.name) = null |
| 45 | + else { |
| 46 | + std.debug.print("unable to import the API function '{s}'\n", .{ decl.name }); |
| 47 | + return false; |
| 48 | + } |
| 49 | + } |
| 50 | + |
| 51 | + return true; |
| 52 | + } |
| 53 | + |
| 54 | + pub var plugin_register: *fn(name: [*:0]const u8, infostruct: *anyopaque) callconv(.C) c_int = undefined; |
| 55 | + pub var plugin_getapi: *fn(name: [*:0]const u8) callconv(.C) ?*anyopaque = undefined; |
| 56 | + pub var ShowMessageBox: *fn(body: [*:0]const u8, title: [*:0]const u8, flags: c_int) callconv(.C) void = undefined; |
| 57 | +}; |
| 58 | + |
| 59 | +const plugin_name = "Hello, Zig!"; |
| 60 | +var action_id: c_int = undefined; |
| 61 | +var ctx: ImGui.ContextPtr = null; |
| 62 | +var click_count: u32 = 0; |
| 63 | +var text = std.mem.zeroes([255:0]u8); |
| 64 | + |
| 65 | +fn loop() !void { |
| 66 | + if(ctx == null) { |
| 67 | + try ImGui.init(reaper.plugin_getapi); |
| 68 | + ctx = try ImGui.CreateContext(.{ plugin_name }); |
| 69 | + } |
| 70 | + |
| 71 | + try ImGui.SetNextWindowSize(.{ ctx, 400, 80, ImGui.Cond_FirstUseEver }); |
| 72 | + |
| 73 | + var open: bool = true; |
| 74 | + if(try ImGui.Begin(.{ ctx, plugin_name, &open })) { |
| 75 | + if(try ImGui.Button(.{ ctx, "Click me!" })) |
| 76 | + click_count +%= 1; |
| 77 | + |
| 78 | + if(click_count & 1 != 0) { |
| 79 | + try ImGui.SameLine(.{ ctx }); |
| 80 | + try ImGui.Text(.{ ctx, "\\o/" }); |
| 81 | + } |
| 82 | + |
| 83 | + _ = try ImGui.InputText(.{ ctx, "text input", &text, text.len }); |
| 84 | + try ImGui.End(.{ ctx }); |
| 85 | + } |
| 86 | + |
| 87 | + if(!open) |
| 88 | + reset(); |
| 89 | +} |
| 90 | + |
| 91 | +fn init() void { |
| 92 | + _ = reaper.plugin_register("timer", @constCast(@ptrCast(&onTimer))); |
| 93 | +} |
| 94 | + |
| 95 | +fn reset() void { |
| 96 | + _ = reaper.plugin_register("-timer", @constCast(@ptrCast(&onTimer))); |
| 97 | + ctx = null; |
| 98 | +} |
| 99 | + |
| 100 | +fn onTimer() callconv(.C) void { |
| 101 | + loop() catch { |
| 102 | + reset(); |
| 103 | + reaper.ShowMessageBox(ImGui.last_error.?, plugin_name, 0); |
| 104 | + }; |
| 105 | +} |
| 106 | + |
| 107 | +fn onCommand(sec: *reaper.KbdSectionInfo, command: c_int, val: c_int, |
| 108 | + val2hw: c_int, relmode: c_int, hwnd: reaper.HWND) callconv(.C) c_char |
| 109 | +{ |
| 110 | + _ = .{ sec, val, val2hw, relmode, hwnd }; |
| 111 | + |
| 112 | + if(command == action_id) { |
| 113 | + if(ctx == null) init() else reset(); |
| 114 | + return 1; |
| 115 | + } |
| 116 | + |
| 117 | + return 0; |
| 118 | +} |
| 119 | + |
| 120 | +export fn ReaperPluginEntry(instance: reaper.HINSTANCE, rec: ?*reaper.plugin_info_t) c_int { |
| 121 | + _ = instance; |
| 122 | + |
| 123 | + if(rec == null) |
| 124 | + return 0 // cleanup here |
| 125 | + else if(!reaper.init(rec.?)) |
| 126 | + return 0; |
| 127 | + |
| 128 | + const action = reaper.custom_action_register_t |
| 129 | + { .section = 0, .id_str = "REAIMGUI_ZIG", .name = "ReaImGui Zig example" }; |
| 130 | + action_id = reaper.plugin_register("custom_action", @constCast(@ptrCast(&action))); |
| 131 | + _ = reaper.plugin_register("hookcommand2", @constCast(@ptrCast(&onCommand))); |
| 132 | + |
| 133 | + return 1; |
| 134 | +} |
0 commit comments