//! Macro Demo - Demonstrates the macro recording system //! //! Press: //! - R: Start/stop recording //! - P: Play back recorded macro //! - S: Save macro to file //! - L: Load macro from file //! - ESC: Quit //! //! Type any keys while recording to capture them. const std = @import("std"); const zcatgui = @import("zcatgui"); const Framebuffer = zcatgui.render.Framebuffer; const SoftwareRenderer = zcatgui.render.SoftwareRenderer; const Sdl2Backend = zcatgui.backend.Sdl2Backend; const MacroRecorder = zcatgui.MacroRecorder; const MacroPlayer = zcatgui.MacroPlayer; const Color = zcatgui.Color; const Command = zcatgui.Command; const KeyEvent = zcatgui.KeyEvent; pub fn main() !void { var gpa = std.heap.GeneralPurposeAllocator(.{}){}; defer _ = gpa.deinit(); const allocator = gpa.allocator(); // Initialize backend var backend = try Sdl2Backend.init("zcatgui - Macro Demo", 800, 600); defer backend.deinit(); // Create framebuffer var fb = try Framebuffer.init(allocator, 800, 600); defer fb.deinit(); // Create renderer var renderer = SoftwareRenderer.init(&fb); // Create macro recorder var recorder = MacroRecorder.init(allocator); defer recorder.deinit(); // State var running = true; var last_keys: [10]KeyEvent = undefined; var last_key_count: usize = 0; var status_message: []const u8 = "Press R to start recording"; // Event injection function for playback const inject = struct { var injected_count: usize = 0; fn injectKey(key: KeyEvent) void { _ = key; injected_count += 1; std.debug.print("Injected key #{d}\n", .{injected_count}); } }.injectKey; while (running) { // Poll events while (backend.pollEvent()) |event| { switch (event) { .quit => running = false, .key => |key| { if (!key.pressed) continue; // Record if recording recorder.record(key); // Store for display if (last_key_count < last_keys.len) { last_keys[last_key_count] = key; last_key_count += 1; } else { // Shift left for (0..last_keys.len - 1) |i| { last_keys[i] = last_keys[i + 1]; } last_keys[last_keys.len - 1] = key; } // Handle special keys switch (key.key) { .escape => running = false, .r => { if (recorder.isRecording()) { _ = recorder.stop(); status_message = "Recording stopped"; std.debug.print("Stopped recording. {d} events captured.\n", .{recorder.eventCount()}); } else { recorder.start(); status_message = "Recording..."; std.debug.print("Started recording.\n", .{}); } }, .p => { if (!recorder.isRecording() and recorder.eventCount() > 0) { status_message = "Playing back..."; std.debug.print("Playing back {d} events.\n", .{recorder.eventCount()}); MacroPlayer.play(recorder.stop(), inject); status_message = "Playback complete"; } }, .s => { if (recorder.eventCount() > 0) { recorder.save("macro.zcm") catch |err| { std.debug.print("Save failed: {}\n", .{err}); }; status_message = "Saved to macro.zcm"; std.debug.print("Saved macro to macro.zcm\n", .{}); } }, .l => { recorder.load("macro.zcm") catch |err| { std.debug.print("Load failed: {}\n", .{err}); }; status_message = "Loaded from macro.zcm"; std.debug.print("Loaded macro: {d} events\n", .{recorder.eventCount()}); }, else => {}, } }, .resize => |size| { try fb.resize(size.width, size.height); }, else => {}, } } // Clear renderer.clear(Color.background); // Draw title area renderer.execute(Command.rect(0, 0, fb.width, 50, Color.rgb(40, 40, 40))); // Draw status const status_color = if (recorder.isRecording()) Color.danger else Color.foreground; renderer.execute(Command.rect(10, 60, 200, 20, status_color)); // Draw event count renderer.execute(Command.rect(10, 90, @intCast(recorder.eventCount() * 10), 20, Color.primary)); // Draw last keys as blocks for (0..last_key_count) |i| { const x: i32 = @intCast(10 + i * 50); renderer.execute(Command.rect(x, 150, 40, 40, Color.secondary)); } // Draw instructions area renderer.execute(Command.rect(10, 250, 400, 120, Color.rgb(35, 35, 35))); // Text would go here with a proper font // Recording indicator if (recorder.isRecording()) { renderer.execute(Command.rect(@as(i32, @intCast(fb.width)) - 30, 10, 20, 20, Color.danger)); } // Present backend.present(&fb); // Cap at ~60 FPS std.Thread.sleep(16 * std.time.ns_per_ms); } std.debug.print("Final status: {s}\n", .{status_message}); }