zcatgui/examples/macro_demo.zig
R.Eugenio f9179b4e9a build: Migrar a Zig 0.16
- Crear src/utils/time.zig para centralizar helpers de tiempo (timestamp, milliTimestamp, nanoTimestamp).
- Reemplazar std.io.fixedBufferStream por std.Io.Writer.fixed.
- Migrar de std.fs.cwd() a std.Io.Dir.cwd() y actualizar firmas de lectura/escritura.
- Reemplazar std.Thread.sleep por std.posix.system.nanosleep.
- Corregir capturas descartadas en switches (text_input => {}).
- Actualizar tests y ejemplos para compatibilidad con std.Io y nuevos helpers.
- Actualizar build.zig.zon a 0.16.0.

Co-Authored-By: Gemini <noreply@google.com>
2026-01-18 02:01:04 +01:00

177 lines
6.2 KiB
Zig

//! 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(std.Options.debug_io, "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(std.Options.debug_io, "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
const ts = std.posix.timespec{ .sec = 0, .nsec = 16 * std.time.ns_per_ms };
_ = std.posix.system.nanosleep(&ts, null);
}
std.debug.print("Final status: {s}\n", .{status_message});
}