Skip to content

Instantly share code, notes, and snippets.

@doccaico
Created November 20, 2020 10:58
Show Gist options
  • Select an option

  • Save doccaico/a9a27ec452ff4a09e4b6e399171e8b77 to your computer and use it in GitHub Desktop.

Select an option

Save doccaico/a9a27ec452ff4a09e4b6e399171e8b77 to your computer and use it in GitHub Desktop.
Resource embedding in Nim (sdl2_nim)
import
sdl2/sdl,
sdl2/sdl_ttf as ttf
const
Title = "SDL2 App"
ScreenW = 640 # Window width
ScreenH = 480 # Window height
WindowFlags = 0
RendererFlags = sdl.RendererAccelerated or sdl.RendererPresentVsync
white = sdl.Color(r: 0xFF, g: 0xFF, b: 0xFF)
black = sdl.Color(r: 0x00, g: 0x00, b: 0x00)
type
App = ref AppObj
AppObj = object
window*: sdl.Window # Window pointer
renderer*: sdl.Renderer # Rendering state pointer
FpsManager = ref FpsManagerObj
FpsManagerObj = object
counter, fps: int
timer: sdl.TimerID
##############
# FPSMANAGER #
##############
# FPS timer
# param is FpsManager casted to pointer
proc fpsTimer(interval: uint32, param: pointer): uint32 {.cdecl.} =
let obj = cast[FpsManager](param)
obj.fps = obj.counter
obj.counter = 0
return interval
proc newFpsManager(): FpsManager = FpsManager(counter: 0, fps: 0, timer: 0)
proc free(obj: FpsManager) =
discard sdl.removeTimer(obj.timer)
obj.timer = 0
proc start(obj: FpsManager) =
obj.timer = sdl.addTimer(1000, fpsTimer, cast[pointer](obj))
proc count(obj: FpsManager) {.inline.} = inc(obj.counter)
##########
# COMMON #
##########
# Initialization sequence
proc init(app: App): bool =
# Init SDL
if sdl.init(sdl.InitVideo or sdl.InitTimer) != 0:
sdl.logCritical(sdl.LogCategoryError,
"Can't initialize SDL: %s",
sdl.getError())
return false
# Init SDL_TTF
if ttf.init() != 0:
sdl.logCritical(sdl.LogCategoryError,
"Can't initialize SDL_TTF: %s",
ttf.getError())
# Create window
app.window = sdl.createWindow(
Title,
sdl.WindowPosUndefined,
sdl.WindowPosUndefined,
ScreenW,
ScreenH,
WindowFlags)
if app.window == nil:
sdl.logCritical(sdl.LogCategoryError,
"Can't create window: %s",
sdl.getError())
return false
# Create renderer
app.renderer = sdl.createRenderer(app.window, -1, RendererFlags)
if app.renderer == nil:
sdl.logCritical(sdl.LogCategoryError,
"Can't create renderer: %s",
sdl.getError())
return false
# Set draw color
if app.renderer.setRenderDrawColor(0x00, 0x00, 0x00, 0xFF) != 0:
sdl.logWarn(sdl.LogCategoryVideo,
"Can't set draw color: %s",
sdl.getError())
return false
sdl.logInfo(sdl.LogCategoryApplication, "SDL initialized successfully")
return true
# Shutdown sequence
proc exit(app: App) =
app.renderer.destroyRenderer()
app.window.destroyWindow()
ttf.quit()
# img.quit()
sdl.logInfo(sdl.LogCategoryApplication, "SDL shutdown completed")
sdl.quit()
# Render surface
proc render(renderer: sdl.Renderer,
surface: sdl.Surface, x, y: int): bool =
result = true
var rect = sdl.Rect(x: x, y: y, w: surface.w, h: surface.h)
# Convert to texture
var texture = sdl.createTextureFromSurface(renderer, surface)
if texture == nil:
return false
# Render texture
if renderer.renderCopy(texture, nil, addr(rect)) != 0:
result = false
# Clean
destroyTexture(texture)
# Event handling
# Return true on app shutdown request, otherwise return false
proc events(pressed: var seq[sdl.Keycode]): bool =
result = false
var e: sdl.Event
if pressed.len > 0:
pressed = @[]
while sdl.pollEvent(addr(e)) != 0:
# Quit requested
if e.kind == sdl.Quit:
return true
# Key pressed
elif e.kind == sdl.KeyDown:
# Add pressed key to sequence
pressed.add(e.key.keysym.sym)
# Exit on Escape key press
if e.key.keysym.sym == sdl.K_Escape:
return true
########
# MAIN #
########
var
app = App(window: nil, renderer: nil)
done = false # Main loop exit condition
pressed: seq[sdl.Keycode] = @[] # Pressed keys
if init(app):
var font: ttf.Font
# embedded at compile-time
const f = staticRead"fnt/PixelMplus12-Regular.ttf"
font = ttf.openFontRW(rwFromConstMem(cast[ptr byte](f), f.len), 0, 8*2)
# open at runtime
# font = ttf.openFont("fnt/PixelMplus12-Regular.ttf", 8*2)
if font == nil:
sdl.logCritical(sdl.LogCategoryError,
"Can't load font: %s",
ttf.getError())
done = true
# Init FPS manager
var
fpsMgr = newFpsManager()
delta = 0.0 # Time passed since last frame in seconds
ticks: uint64 # Ticks counter
freq = sdl.getPerformanceFrequency() # Get counter frequency
fpsMgr.start()
ticks = getPerformanceCounter()
# Main loop
while not done:
# Clear screen with draw color
discard app.renderer.setRenderDrawColor(0x00, 0x00, 0x00, 0xFF)
if app.renderer.renderClear() != 0:
sdl.logWarn(sdl.LogCategoryVideo,
"Can't clear screen: %s",
sdl.getError())
var fps = font.renderUTF8_Shaded("FPS: " & $fpsMgr.fps, white, black)
if not app.renderer.render(fps, 10, 10):
sdl.logWarn(sdl.LogCategoryVideo,
"Can't render text: %s",
sdl.getError())
sdl.freeSurface(fps)
var s = font.renderUTF8_Shaded("吾輩は猫である。名前はまだ無い。どこで生れたかとんと見当がつかぬ。", white, black)
if not app.renderer.render(s, 10, 10+8*2+5):
sdl.logWarn(sdl.LogCategoryVideo,
"Can't render text: %s",
sdl.getError())
sdl.freeSurface(s)
# Update renderer
app.renderer.renderPresent()
# Event handling
done = events(pressed)
# Count frame
fpsMgr.count()
# Get frame duration
delta = (sdl.getPerformanceCounter() - ticks).float / freq.float
ticks = sdl.getPerformanceCounter()
free(fpsMgr)
# Shutdown
exit(app)
@doccaico
Copy link
Copy Markdown
Author

a

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment