commit
de3182744b
12 changed files with 548 additions and 0 deletions
@ -0,0 +1,2 @@ |
|||||||
|
/zig-cache/ |
||||||
|
/zig-out/ |
@ -0,0 +1,12 @@ |
|||||||
|
{ |
||||||
|
"version": "0.2.0", |
||||||
|
"configurations": [{ |
||||||
|
"name": "Debug", |
||||||
|
"type": "lldb", |
||||||
|
"preLaunchTask": "build", |
||||||
|
"request": "launch", |
||||||
|
"cwd": "${workspaceFolder}", |
||||||
|
"program": "${workspaceFolder}/zig-out/bin/gridstep", |
||||||
|
"args": [], |
||||||
|
}] |
||||||
|
} |
@ -0,0 +1,16 @@ |
|||||||
|
{ |
||||||
|
"version": "2.0.0", |
||||||
|
"tasks": [{ |
||||||
|
"label": "build", |
||||||
|
"group": { "kind": "build", "isDefault": true }, |
||||||
|
"type": "shell", |
||||||
|
"command": "zig build", |
||||||
|
"problemMatcher": [] |
||||||
|
},{ |
||||||
|
"label": "test", |
||||||
|
"group": { "kind": "test", "isDefault": true }, |
||||||
|
"type": "shell", |
||||||
|
"command": "zig build test", |
||||||
|
"problemMatcher": [] |
||||||
|
}] |
||||||
|
} |
@ -0,0 +1,38 @@ |
|||||||
|
const std = @import("std"); |
||||||
|
const sdl = @import("sdl"); |
||||||
|
|
||||||
|
pub fn build(b: *std.Build) void { |
||||||
|
const target = b.standardTargetOptions(.{}); |
||||||
|
const optimize = b.standardOptimizeOption(.{}); |
||||||
|
|
||||||
|
const sdl_sdk = sdl.init(b, null); |
||||||
|
|
||||||
|
const exe = b.addExecutable(.{ |
||||||
|
.name = "gridstep", |
||||||
|
.root_source_file = .{ .path = "src/main.zig" }, |
||||||
|
.target = target, |
||||||
|
.optimize = optimize, |
||||||
|
}); |
||||||
|
|
||||||
|
sdl_sdk.link(exe, .dynamic); |
||||||
|
exe.linkSystemLibrary("sdl2_image"); |
||||||
|
exe.linkSystemLibrary("libpng"); |
||||||
|
|
||||||
|
exe.root_module.addImport("sdl", sdl_sdk.getNativeModule()); |
||||||
|
b.installArtifact(exe); |
||||||
|
|
||||||
|
const run_cmd = b.addRunArtifact(exe); |
||||||
|
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 unit_tests = b.addTest(.{ |
||||||
|
.root_source_file = .{ .path = "src/main.zig" }, |
||||||
|
.target = target, |
||||||
|
.optimize = optimize, |
||||||
|
}); |
||||||
|
const run_unit_tests = b.addRunArtifact(unit_tests); |
||||||
|
const test_step = b.step("test", "Run unit tests"); |
||||||
|
test_step.dependOn(&run_unit_tests.step); |
||||||
|
} |
@ -0,0 +1,18 @@ |
|||||||
|
.{ |
||||||
|
.name = "gridstep", |
||||||
|
.version = "0.0.0", |
||||||
|
.minimum_zig_version = "0.11.0", |
||||||
|
|
||||||
|
.dependencies = .{ |
||||||
|
.sdl = .{ |
||||||
|
.url = "https://github.com/MasterQ32/SDL.zig/archive/39fb8355cccb45a241a891c4848ab925af20fee4.tar.gz", |
||||||
|
.hash = "12203537fc1357c4efce1c7a5ae3b407b9070490d83acd69aa3363b6b8e8a2b27870", |
||||||
|
}, |
||||||
|
}, |
||||||
|
|
||||||
|
.paths = .{ |
||||||
|
"src", |
||||||
|
"build.zig", |
||||||
|
"build.zig.zon", |
||||||
|
}, |
||||||
|
} |
After Width: | Height: | Size: 3.1 KiB |
Binary file not shown.
@ -0,0 +1,102 @@ |
|||||||
|
const std = @import("std"); |
||||||
|
const Allocator = std.mem.Allocator; |
||||||
|
|
||||||
|
const sdl = @import("sdl"); |
||||||
|
const Window = @import("./window.zig"); |
||||||
|
const makeSdlError = Window.makeSdlError; |
||||||
|
|
||||||
|
const Canvas = @This(); |
||||||
|
|
||||||
|
allocator: Allocator, |
||||||
|
surface: *sdl.SDL_Surface, |
||||||
|
pixels: []Pixel, |
||||||
|
width: usize, |
||||||
|
height: usize, |
||||||
|
|
||||||
|
pub fn init(allocator: Allocator, width: usize, height: usize) !*Canvas { |
||||||
|
const surface = sdl.SDL_CreateRGBSurfaceWithFormat( |
||||||
|
0, |
||||||
|
@intCast(width), |
||||||
|
@intCast(height), |
||||||
|
0, |
||||||
|
sdl.SDL_PIXELFORMAT_ABGR8888, |
||||||
|
) orelse |
||||||
|
return makeSdlError(); |
||||||
|
|
||||||
|
const pixels_ptr: [*]Pixel = @alignCast(@ptrCast(surface.pixels)); |
||||||
|
const pixels_len = width * height; |
||||||
|
const pixels = pixels_ptr[0..pixels_len]; |
||||||
|
|
||||||
|
const result = try allocator.create(Canvas); |
||||||
|
result.* = .{ |
||||||
|
.allocator = allocator, |
||||||
|
.surface = surface, |
||||||
|
.pixels = pixels, |
||||||
|
.width = width, |
||||||
|
.height = height, |
||||||
|
}; |
||||||
|
return result; |
||||||
|
} |
||||||
|
|
||||||
|
pub fn deinit(self: *Canvas) void { |
||||||
|
sdl.SDL_FreeSurface(self.surface); |
||||||
|
self.allocator.destroy(self); |
||||||
|
} |
||||||
|
|
||||||
|
pub fn getUnsafe(self: *Canvas, x: usize, y: usize) *Pixel { |
||||||
|
const index = x + y * self.width; |
||||||
|
return &self.pixels[index]; |
||||||
|
} |
||||||
|
|
||||||
|
pub fn get(self: *Canvas, x: usize, y: usize) *Pixel { |
||||||
|
std.debug.assert(x < self.width); |
||||||
|
std.debug.assert(y < self.height); |
||||||
|
return self.getUnsafe(x, y); |
||||||
|
} |
||||||
|
|
||||||
|
pub fn put(self: *Canvas, x: usize, y: usize, value: Pixel) void { |
||||||
|
self.get(x, y).* = value; |
||||||
|
} |
||||||
|
|
||||||
|
pub fn putRect(self: *Canvas, x: usize, y: usize, w: usize, h: usize, value: Pixel) void { |
||||||
|
std.debug.assert(x + w <= self.width); |
||||||
|
std.debug.assert(y + h <= self.height); |
||||||
|
const start_index = x + y * self.width; |
||||||
|
for (0..h) |yo| { |
||||||
|
const row_index = start_index + yo * self.width; |
||||||
|
@memset(self.pixels[row_index..][0..w], value); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
pub fn putSprite(self: *Canvas, x: usize, y: usize, sprite: *const Sprite) void { |
||||||
|
std.debug.assert(x + 16 <= self.width); |
||||||
|
std.debug.assert(y + 16 <= self.height); |
||||||
|
for (0..16) |yo| |
||||||
|
for (0..16) |xo| { |
||||||
|
const value = sprite[yo][xo]; |
||||||
|
if (value.a == 0x00) continue; |
||||||
|
|
||||||
|
const dest = self.getUnsafe(x + xo, y + yo); |
||||||
|
if (value.a == 0xFF) |
||||||
|
dest.* = value |
||||||
|
else |
||||||
|
@panic("Not supported"); |
||||||
|
}; |
||||||
|
} |
||||||
|
|
||||||
|
pub fn clear(self: *Canvas, value: Pixel) void { |
||||||
|
@memset(self.pixels, value); |
||||||
|
} |
||||||
|
|
||||||
|
pub const Pixel = packed struct { |
||||||
|
r: u8, |
||||||
|
g: u8, |
||||||
|
b: u8, |
||||||
|
a: u8, |
||||||
|
|
||||||
|
pub const transparent = Pixel{ .r = 0x00, .g = 0x00, .b = 0x00, .a = 0x00 }; |
||||||
|
pub const black = Pixel{ .r = 0x00, .g = 0x00, .b = 0x00, .a = 0xFF }; |
||||||
|
pub const white = Pixel{ .r = 0xFF, .g = 0xFF, .b = 0xFF, .a = 0xFF }; |
||||||
|
}; |
||||||
|
|
||||||
|
pub const Sprite = [16][16]Pixel; |
@ -0,0 +1,51 @@ |
|||||||
|
const std = @import("std"); |
||||||
|
|
||||||
|
const Grid = @This(); |
||||||
|
|
||||||
|
pub const size = 16; |
||||||
|
|
||||||
|
array: [size][size][size]Tile = .{.{.{0} ** size} ** size} ** size, |
||||||
|
|
||||||
|
pub fn get(self: *Grid, pos: Pos) Ref { |
||||||
|
return .{ .grid = self, .pos = pos }; |
||||||
|
} |
||||||
|
|
||||||
|
pub fn layerAs(self: *Grid, z: i4, comptime T: type) []T { |
||||||
|
const bytes = std.mem.sliceAsBytes(&self.array[@as(u4, @bitCast(z))]); |
||||||
|
return std.mem.bytesAsSlice(T, bytes); |
||||||
|
} |
||||||
|
|
||||||
|
pub const Tile = u8; |
||||||
|
|
||||||
|
pub const Pos = packed struct { |
||||||
|
x: i4, |
||||||
|
y: i4, |
||||||
|
z: i4, |
||||||
|
|
||||||
|
pub fn init(x: i4, y: i4, z: i4) Pos { |
||||||
|
return .{ .x = x, .y = y, .z = z }; |
||||||
|
} |
||||||
|
|
||||||
|
pub fn initTruncate(x: anytype, y: anytype, z: anytype) Pos { |
||||||
|
const sign = @typeInfo(@TypeOf(x, y, z)).Int.signedness; |
||||||
|
const T = @Type(.{ .Int = .{ .signedness = sign, .bits = 4 } }); |
||||||
|
return .{ |
||||||
|
.x = @bitCast(@as(T, @truncate(x))), |
||||||
|
.y = @bitCast(@as(T, @truncate(y))), |
||||||
|
.z = @bitCast(@as(T, @truncate(z))), |
||||||
|
}; |
||||||
|
} |
||||||
|
}; |
||||||
|
|
||||||
|
pub const Ref = struct { |
||||||
|
grid: *Grid, |
||||||
|
pos: Pos, |
||||||
|
|
||||||
|
pub fn as(self: Ref, comptime T: type) *T { |
||||||
|
std.debug.assert(@sizeOf(T) == @sizeOf(Tile)); |
||||||
|
const x: usize = @as(u4, @bitCast(self.pos.x)); |
||||||
|
const y: usize = @as(u4, @bitCast(self.pos.y)); |
||||||
|
const z: usize = @as(u4, @bitCast(self.pos.z)); |
||||||
|
return @ptrCast(&self.grid.array[z][y][x]); |
||||||
|
} |
||||||
|
}; |
@ -0,0 +1,112 @@ |
|||||||
|
const std = @import("std"); |
||||||
|
const sdl = @import("sdl"); |
||||||
|
|
||||||
|
pub const PhysicalKey = enum(u8) { |
||||||
|
// zig fmt: off |
||||||
|
backspace = 0x08, |
||||||
|
tab = '\t', |
||||||
|
enter = '\r', |
||||||
|
escape = 0x1B, |
||||||
|
|
||||||
|
// Numbers |
||||||
|
_0 = '0', _1 = '1', _2 = '2', _3 = '3', _4 = '4', |
||||||
|
_5 = '5', _6 = '6', _7 = '7', _8 = '8', _9 = '9', |
||||||
|
|
||||||
|
// Letters |
||||||
|
a = 'A', b = 'B', c = 'C', d = 'D', e = 'E', f = 'F', g = 'G', |
||||||
|
h = 'H', i = 'I', j = 'J', k = 'K', l = 'L', m = 'M', n = 'N', |
||||||
|
o = 'O', p = 'P', q = 'Q', r = 'R', s = 'S', t = 'T', u = 'U', |
||||||
|
v = 'V', w = 'W', x = 'X', y = 'Y', z = 'Z', |
||||||
|
|
||||||
|
// Visual |
||||||
|
space = ' ', |
||||||
|
grave = '`', |
||||||
|
minus = '-', |
||||||
|
equals = '=', |
||||||
|
left_bracket = '[', |
||||||
|
right_bracket = ']', |
||||||
|
semicolon = ';', |
||||||
|
apostrophe = '\'', |
||||||
|
backslash = '\\', |
||||||
|
comma = ',', |
||||||
|
period = '.', |
||||||
|
slash = '/', |
||||||
|
|
||||||
|
// Nagivation |
||||||
|
insert = 0x8D, |
||||||
|
delete = 0x7F, |
||||||
|
home = 0x8E, |
||||||
|
end = 0x9E, |
||||||
|
page_up = 0x8F, |
||||||
|
page_down = 0x9F, |
||||||
|
|
||||||
|
up = 0x8C, |
||||||
|
down = 0x9C, |
||||||
|
left = 0x9B, |
||||||
|
right = 0x9D, |
||||||
|
|
||||||
|
// Modifiers |
||||||
|
ctrl = 0x88, |
||||||
|
alt = 0x89, |
||||||
|
shift = 0x8A, |
||||||
|
caps = 0x8B, |
||||||
|
|
||||||
|
_, |
||||||
|
// zig fmt: on |
||||||
|
}; |
||||||
|
|
||||||
|
pub const lookup = blk: { |
||||||
|
const size = sdl.SDL_NUM_SCANCODES; |
||||||
|
var result: [size]?PhysicalKey = .{null} ** size; |
||||||
|
|
||||||
|
result[sdl.SDL_SCANCODE_BACKSPACE] = PhysicalKey.backspace; |
||||||
|
result[sdl.SDL_SCANCODE_TAB] = PhysicalKey.tab; |
||||||
|
result[sdl.SDL_SCANCODE_RETURN] = PhysicalKey.enter; |
||||||
|
result[sdl.SDL_SCANCODE_ESCAPE] = PhysicalKey.escape; |
||||||
|
|
||||||
|
for ('0'..'9' + 1) |i| { |
||||||
|
const scancode = @field(sdl, "SDL_SCANCODE_" ++ .{i}); |
||||||
|
const physical_key = @field(PhysicalKey, "_" ++ .{i}); |
||||||
|
result[scancode] = physical_key; |
||||||
|
} |
||||||
|
|
||||||
|
for ('A'..'Z' + 1) |i| { |
||||||
|
const scancode = @field(sdl, "SDL_SCANCODE_" ++ .{i}); |
||||||
|
const physical_key = @field(PhysicalKey, &.{std.ascii.toLower(i)}); |
||||||
|
result[scancode] = physical_key; |
||||||
|
} |
||||||
|
|
||||||
|
result[sdl.SDL_SCANCODE_GRAVE] = PhysicalKey.grave; |
||||||
|
result[sdl.SDL_SCANCODE_MINUS] = PhysicalKey.minus; |
||||||
|
result[sdl.SDL_SCANCODE_EQUALS] = PhysicalKey.equals; |
||||||
|
result[sdl.SDL_SCANCODE_LEFTBRACKET] = PhysicalKey.left_bracket; |
||||||
|
result[sdl.SDL_SCANCODE_RIGHTBRACKET] = PhysicalKey.right_bracket; |
||||||
|
result[sdl.SDL_SCANCODE_SEMICOLON] = PhysicalKey.semicolon; |
||||||
|
result[sdl.SDL_SCANCODE_APOSTROPHE] = PhysicalKey.apostrophe; |
||||||
|
result[sdl.SDL_SCANCODE_BACKSLASH] = PhysicalKey.backslash; |
||||||
|
result[sdl.SDL_SCANCODE_COMMA] = PhysicalKey.comma; |
||||||
|
result[sdl.SDL_SCANCODE_PERIOD] = PhysicalKey.period; |
||||||
|
result[sdl.SDL_SCANCODE_SLASH] = PhysicalKey.slash; |
||||||
|
result[sdl.SDL_SCANCODE_SPACE] = PhysicalKey.space; |
||||||
|
|
||||||
|
result[sdl.SDL_SCANCODE_INSERT] = PhysicalKey.insert; |
||||||
|
result[sdl.SDL_SCANCODE_DELETE] = PhysicalKey.delete; |
||||||
|
result[sdl.SDL_SCANCODE_HOME] = PhysicalKey.home; |
||||||
|
result[sdl.SDL_SCANCODE_END] = PhysicalKey.end; |
||||||
|
result[sdl.SDL_SCANCODE_PAGEUP] = PhysicalKey.page_up; |
||||||
|
result[sdl.SDL_SCANCODE_PAGEDOWN] = PhysicalKey.page_down; |
||||||
|
result[sdl.SDL_SCANCODE_UP] = PhysicalKey.up; |
||||||
|
result[sdl.SDL_SCANCODE_DOWN] = PhysicalKey.down; |
||||||
|
result[sdl.SDL_SCANCODE_LEFT] = PhysicalKey.left; |
||||||
|
result[sdl.SDL_SCANCODE_RIGHT] = PhysicalKey.right; |
||||||
|
|
||||||
|
result[sdl.SDL_SCANCODE_LCTRL] = PhysicalKey.ctrl; |
||||||
|
result[sdl.SDL_SCANCODE_RCTRL] = PhysicalKey.ctrl; |
||||||
|
result[sdl.SDL_SCANCODE_LALT] = PhysicalKey.alt; |
||||||
|
result[sdl.SDL_SCANCODE_RALT] = PhysicalKey.alt; |
||||||
|
result[sdl.SDL_SCANCODE_LSHIFT] = PhysicalKey.shift; |
||||||
|
result[sdl.SDL_SCANCODE_RSHIFT] = PhysicalKey.shift; |
||||||
|
result[sdl.SDL_SCANCODE_CAPSLOCK] = PhysicalKey.caps; |
||||||
|
|
||||||
|
break :blk result; |
||||||
|
}; |
@ -0,0 +1,92 @@ |
|||||||
|
const std = @import("std"); |
||||||
|
const Allocator = std.mem.Allocator; |
||||||
|
|
||||||
|
const sdl = @import("sdl"); |
||||||
|
const Window = @import("./window.zig"); |
||||||
|
const makeSdlError = Window.makeSdlError; |
||||||
|
|
||||||
|
const Canvas = @import("./canvas.zig"); |
||||||
|
const Pixel = Canvas.Pixel; |
||||||
|
const Sprite = Canvas.Sprite; |
||||||
|
|
||||||
|
const Grid = @import("./grid.zig"); |
||||||
|
|
||||||
|
pub fn main() !void { |
||||||
|
var gpa = std.heap.GeneralPurposeAllocator(.{}){}; |
||||||
|
const allocator = gpa.allocator(); |
||||||
|
var prng = std.Random.DefaultPrng.init(0); |
||||||
|
const random = prng.random(); |
||||||
|
|
||||||
|
var grid = Grid{}; |
||||||
|
const key_layer = grid.layerAs(0, u8); |
||||||
|
|
||||||
|
var sprite_lookup: [256]Sprite = undefined; |
||||||
|
try loadSprites(&sprite_lookup); |
||||||
|
|
||||||
|
const size = Grid.size * Grid.size; |
||||||
|
|
||||||
|
const scale = 3; |
||||||
|
const window = try Window.init(allocator, size * scale, size * scale, key_layer); |
||||||
|
defer window.deinit(); |
||||||
|
|
||||||
|
const canvas = try Canvas.init(allocator, size, size); |
||||||
|
defer canvas.deinit(); |
||||||
|
|
||||||
|
while (window.running) { |
||||||
|
window.pollEvents(); |
||||||
|
|
||||||
|
grid.get(Grid.Pos.init(-5, -4, 0)).as(u8).* +%= 1; |
||||||
|
grid.get(Grid.Pos.init(-4, -4, 0)).as(u8).* = random.int(u8); |
||||||
|
|
||||||
|
render(&grid, canvas, &sprite_lookup); |
||||||
|
try window.updateSurface(canvas); |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
fn render(grid: *Grid, canvas: *Canvas, sprite_lookup: *[256]Sprite) void { |
||||||
|
canvas.clear(Pixel.black); |
||||||
|
|
||||||
|
for (0..Grid.size) |vx| |
||||||
|
for (0..Grid.size) |vy| |
||||||
|
for (0..Grid.size) |vz| { |
||||||
|
const x = @as(isize, @intCast(vx)) - Grid.size / 2; |
||||||
|
const y = @as(isize, @intCast(vy)) - Grid.size / 2; |
||||||
|
const z = @as(isize, @intCast(vz)) - Grid.size / 2; |
||||||
|
|
||||||
|
const pos = Grid.Pos.initTruncate(x, y, z); |
||||||
|
const value = grid.get(pos).as(u8).*; |
||||||
|
|
||||||
|
const sprite = &sprite_lookup[value]; |
||||||
|
canvas.putSprite(vx * Grid.size, vy * Grid.size, sprite); |
||||||
|
}; |
||||||
|
} |
||||||
|
|
||||||
|
fn loadSprites(sprite_lookup: *[256]Sprite) !void { |
||||||
|
const png_surface = sdl.IMG_Load("./gfx/sprite_map.png") orelse |
||||||
|
return makeSdlError(); |
||||||
|
defer sdl.SDL_FreeSurface(png_surface); |
||||||
|
|
||||||
|
std.debug.assert(png_surface.w == 256); |
||||||
|
std.debug.assert(png_surface.h == 256); |
||||||
|
|
||||||
|
const pixels_surface = sdl.SDL_ConvertSurfaceFormat( |
||||||
|
png_surface, |
||||||
|
sdl.SDL_PIXELFORMAT_ABGR8888, |
||||||
|
0, |
||||||
|
) orelse |
||||||
|
return makeSdlError(); |
||||||
|
defer sdl.SDL_FreeSurface(pixels_surface); |
||||||
|
|
||||||
|
const pixels_ptr: [*]Pixel = @alignCast(@ptrCast(pixels_surface.pixels)); |
||||||
|
const pixels_len: usize = @intCast(pixels_surface.w * pixels_surface.h); |
||||||
|
const pixels = pixels_ptr[0..pixels_len]; |
||||||
|
|
||||||
|
for (sprite_lookup, 0..) |*sprite, i| { |
||||||
|
const x = i % 16; |
||||||
|
const y = i / 16; |
||||||
|
for (0..16) |yo| { |
||||||
|
const dst_index = x * 16 + (y * 16 + yo) * 256; |
||||||
|
@memcpy(&sprite[yo], pixels[dst_index..][0..16]); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
@ -0,0 +1,105 @@ |
|||||||
|
const std = @import("std"); |
||||||
|
const Allocator = std.mem.Allocator; |
||||||
|
|
||||||
|
const sdl = @import("sdl"); |
||||||
|
const keys = @import("./keys.zig"); |
||||||
|
const PhysicalKey = keys.PhysicalKey; |
||||||
|
const Canvas = @import("./canvas.zig"); |
||||||
|
|
||||||
|
const Window = @This(); |
||||||
|
|
||||||
|
allocator: Allocator, |
||||||
|
|
||||||
|
handle: *sdl.SDL_Window, |
||||||
|
surface: ?*sdl.SDL_Surface = null, |
||||||
|
key_status: []u8, |
||||||
|
running: bool = true, |
||||||
|
|
||||||
|
pub fn init(allocator: Allocator, width: usize, height: usize, key_status: []u8) !*Window { |
||||||
|
if (sdl.SDL_Init(sdl.SDL_INIT_AUDIO | sdl.SDL_INIT_VIDEO | sdl.SDL_INIT_EVENTS) < 0) |
||||||
|
return makeSdlError(); |
||||||
|
|
||||||
|
const window = sdl.SDL_CreateWindow( |
||||||
|
"Gridstep", |
||||||
|
sdl.SDL_WINDOWPOS_UNDEFINED, |
||||||
|
sdl.SDL_WINDOWPOS_UNDEFINED, |
||||||
|
@intCast(width), |
||||||
|
@intCast(height), |
||||||
|
sdl.SDL_WINDOW_SHOWN | sdl.SDL_WINDOW_RESIZABLE, |
||||||
|
) orelse |
||||||
|
return makeSdlError(); |
||||||
|
|
||||||
|
const result = try allocator.create(Window); |
||||||
|
result.* = .{ |
||||||
|
.allocator = allocator, |
||||||
|
.handle = window, |
||||||
|
.key_status = key_status, |
||||||
|
}; |
||||||
|
return result; |
||||||
|
} |
||||||
|
|
||||||
|
pub fn deinit(self: *Window) void { |
||||||
|
sdl.SDL_DestroyWindow(self.handle); |
||||||
|
sdl.SDL_Quit(); |
||||||
|
self.allocator.destroy(self); |
||||||
|
} |
||||||
|
|
||||||
|
pub fn pollEvents(self: *Window) void { |
||||||
|
{ |
||||||
|
// Special handling for the `.caps` key. |
||||||
|
const i = @intFromEnum(PhysicalKey.caps); |
||||||
|
const on = (sdl.SDL_GetModState() & sdl.KMOD_CAPS) != 0; |
||||||
|
self.key_status[i] = if (on) i else 0; |
||||||
|
} |
||||||
|
|
||||||
|
var ev = std.mem.zeroes(sdl.SDL_Event); |
||||||
|
while (sdl.SDL_PollEvent(&ev) != 0) { |
||||||
|
switch (ev.type) { |
||||||
|
sdl.SDL_QUIT => { |
||||||
|
self.running = false; |
||||||
|
}, |
||||||
|
sdl.SDL_WINDOWEVENT => { |
||||||
|
switch (ev.window.event) { |
||||||
|
sdl.SDL_WINDOWEVENT_RESIZED => { |
||||||
|
// Invalidate surface when window is resized. |
||||||
|
self.surface = null; |
||||||
|
}, |
||||||
|
else => {}, |
||||||
|
} |
||||||
|
}, |
||||||
|
sdl.SDL_KEYDOWN, sdl.SDL_KEYUP => { |
||||||
|
const key = ev.key.keysym; |
||||||
|
const down = (ev.type == sdl.SDL_KEYDOWN); |
||||||
|
if (keys.lookup[key.scancode]) |k| { |
||||||
|
if (k == .caps) break; |
||||||
|
const i = @intFromEnum(k); |
||||||
|
self.key_status[i] = if (down) i else 0; |
||||||
|
} |
||||||
|
}, |
||||||
|
else => {}, |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
pub fn updateSurface(self: *Window, canvas: *Canvas) !void { |
||||||
|
// If surface has not yet been created or invalidated due |
||||||
|
// to window being resized, reaquire the window's surface. |
||||||
|
if (self.surface == null) |
||||||
|
self.surface = sdl.SDL_GetWindowSurface(self.handle) orelse |
||||||
|
return makeSdlError(); |
||||||
|
|
||||||
|
const w: c_int = @intCast(canvas.width); |
||||||
|
const h: c_int = @intCast(canvas.height); |
||||||
|
const src_rect = sdl.SDL_Rect{ .x = 0, .y = 0, .w = w, .h = h }; |
||||||
|
var dest_rect = sdl.SDL_Rect{ .x = 0, .y = 0, .w = self.surface.?.w, .h = self.surface.?.h }; |
||||||
|
if (sdl.SDL_BlitScaled(canvas.surface, &src_rect, self.surface, &dest_rect) < 0) |
||||||
|
return makeSdlError(); |
||||||
|
|
||||||
|
if (sdl.SDL_UpdateWindowSurface(self.handle) < 0) |
||||||
|
return makeSdlError(); |
||||||
|
} |
||||||
|
|
||||||
|
pub fn makeSdlError() error{SdlError} { |
||||||
|
std.debug.print("{s}\n", .{sdl.SDL_GetError()}); |
||||||
|
return error.SdlError; |
||||||
|
} |
Loading…
Reference in new issue