Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
38 changes: 38 additions & 0 deletions samples/22_timing.cr
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
require "../src/sdl"
require "../src/image"
require "../src/ttf"

SDL.init(SDL::Init::VIDEO); at_exit { SDL.quit }
SDL::TTF.init; at_exit { SDL::TTF.quit }

window = SDL::Window.new("SDL tutorial", 640, 480)
renderer = SDL::Renderer.new(window, SDL::Renderer::Flags::ACCELERATED | SDL::Renderer::Flags::PRESENTVSYNC)

font = SDL::TTF::Font.new(File.join(__DIR__, "data", "lazy.ttf"), 28)
font_color = SDL::Color[0, 0, 0, 255]

renderer.draw_color = SDL::Color[255, 255, 255, 255]
prompt_surface = font.render_shaded("Press Enter to Reset Start Time.", font_color, renderer.draw_color)

start_time = 0

loop do
case event = SDL::Event.poll
when SDL::Event::Quit
break
when SDL::Event::Keyboard
if event.keydown? && event.sym.return?
start_time = SDL::Timer.ticks
end
end
renderer.clear
prompt_x = (window.width - prompt_surface.width) / 2
renderer.copy(prompt_surface, dstrect: SDL::Rect[prompt_x, 0, prompt_surface.width, prompt_surface.height])

time_text_surface = font.render_shaded("Milliseconds since start time #{SDL::Timer.ticks - start_time}", font_color, renderer.draw_color)
time_text_x = (window.width - time_text_surface.width) / 2
time_text_y = (window.height - time_text_surface.height) / 2
renderer.copy(time_text_surface, dstrect: SDL::Rect[time_text_x, time_text_y, time_text_surface.width, time_text_surface.height])

renderer.present
end
117 changes: 117 additions & 0 deletions samples/23_advanced_timers.cr
Original file line number Diff line number Diff line change
@@ -0,0 +1,117 @@
require "../src/sdl"
require "../src/image"
require "../src/ttf"

SDL.init(SDL::Init::VIDEO); at_exit { SDL.quit }
SDL::TTF.init; at_exit { SDL::TTF.quit }

window = SDL::Window.new("SDL tutorial", 640, 480)
renderer = SDL::Renderer.new(window, SDL::Renderer::Flags::ACCELERATED | SDL::Renderer::Flags::PRESENTVSYNC)

font = SDL::TTF::Font.new(File.join(__DIR__, "data", "lazy.ttf"), 28)
font_color = SDL::Color[0, 0, 0, 255]

renderer.draw_color = SDL::Color[255, 255, 255, 255]
bg_color = renderer.draw_color
start_prompt_surface = font.render_shaded("Press S to Start or Stop the Timer", font_color, bg_color)
pause_prompt_surface = font.render_shaded("Press P to Pause or Unpause the Timer", font_color, bg_color)

class SampleTimer
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This sample timer is very nice, but can be achieved using Time.monotonic and doesn't need SDL_GetTicks. For example:

ticks = Time.monotonic.total_milliseconds

property started : Bool = false
property paused : Bool = false
property start_ticks : UInt32 = 0_u32
property paused_ticks : UInt32 = 0_u32

def ticks
time = 0
if started?
if paused?
time = @paused_ticks
else
time = SDL::Timer.ticks - @start_ticks
end
end
time
end

def start
@started = true
@paused = false
@start_ticks = SDL::Timer.ticks
@paused_ticks = 0_u32
end

def stop
@started = false
@paused = false
@start_ticks = 0_u32
@paused_ticks = 0_u32
end

def started?
@started
end

def toggle_on_state
started? ? stop : start
end

def pause
unless paused?
@paused = true
@paused_ticks = SDL::Timer.ticks - @start_ticks
@start_ticks = 0_u32
end
end

def unpause
if paused?
@paused = false
@start_ticks = SDL::Timer.ticks - @paused_ticks
@paused_ticks = 0_u32
end
end

def paused?
@paused && @started
end

def toggle_pause_state
paused? ? unpause : pause
end
end

timer = SampleTimer.new

loop do
case event = SDL::Event.poll
when SDL::Event::Quit
break
when SDL::Event::Keyboard
if event.keydown?
case event.sym
when .s?
timer.toggle_on_state
when .p?
timer.toggle_pause_state
end
end
end
renderer.clear
# Render the start prompt text
start_prompt_x = (window.width - start_prompt_surface.width) / 2
renderer.copy(start_prompt_surface, dstrect: SDL::Rect[start_prompt_x, 0, start_prompt_surface.width, start_prompt_surface.height])

# Render the pause prompt text
pause_prompt_x = (window.width - pause_prompt_surface.width) / 2
renderer.copy(pause_prompt_surface, dstrect: SDL::Rect[pause_prompt_x, start_prompt_surface.height, pause_prompt_surface.width, pause_prompt_surface.height])

# Render the action text
time_text_surface = font.render_shaded("Seconds since start time #{timer.ticks / 1000.0}", font_color, bg_color)
time_text_x = (window.width - time_text_surface.width) / 2
time_text_y = (window.height - time_text_surface.height) / 2
renderer.copy(time_text_surface, dstrect: SDL::Rect[time_text_x, time_text_y, time_text_surface.width, time_text_surface.height])

renderer.present
end

1 change: 1 addition & 0 deletions src/lib_sdl.cr
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ require "./lib_sdl/render"
require "./lib_sdl/rwops"
require "./lib_sdl/shape"
#require "./lib_sdl/system"
require "./lib_sdl/timer"
require "./lib_sdl/video"

@[Link("SDL2")]
Expand Down
13 changes: 13 additions & 0 deletions src/lib_sdl/timer.cr
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
lib LibSDL
# (interval, params)
alias TimerCallback = (UInt32, Void*) -> Void
alias TimerID = LibC::Int

fun add_timer = SDL_AddTimer(interval : UInt32, callback : TimerCallback, param : Void*) : TimerID
fun delay = SDL_Delay(ms : UInt32) : Void
fun get_performance_counter = SDL_GetPerformanceCounter() : UInt64
fun get_performance_frequency = SDL_GetPerformanceFrequency() : UInt64
fun get_ticks = SDL_GetTicks() : UInt32
fun remove_timer = SDL_RemoveTimer(id : TimerID) : Bool
fun ticks_passed = SDL_TICKS_PASSED(a : UInt32, b : UInt32) : Bool
end
1 change: 1 addition & 0 deletions src/sdl.cr
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ require "./renderer"
require "./window"
require "./screensaver"
require "./color"
require "./timer"

module SDL
class Error < Exception
Expand Down
28 changes: 28 additions & 0 deletions src/timer.cr
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
module SDL
module Timer

Copy link

@ghost ghost Mar 10, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why not just on this line extend self instead of writing self. at every single method?

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I prefer explicit self. definitions over extend self, which only has value if the module is meant to be used as an includable module and a namespace for global methods at the same time —it may be valid here.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ditto ^ 😂

def self.add(interval, &callback : UInt32 -> Void)
LibSDL.add_timer(interval, callback)
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm afraid callback will be run from an unknown context, possibly another thread (unknown to crystal, thus unknown to the garbage collector, and incapable to access the event loop).

It also relies on a captured block passed to a C function, which will make it impossible to rely on closure variables, and there may be GC issues (e.g. callback trying to access collected variables) making it complex to handle, over a simple crystal function, such as spawn { sleep(seconds), yield } for example.

end

def self.delay(ms : UInt32)
LibSDL.delay(ms)
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

SDL_Delay() duplicates sleep() and AFAIK block the current thread, thus block the crystal event loop.

end

def self.performance_counter
LibSDL.get_performance_counter
end

def self.performance_frequency
LibSDL.get_performance_frequency
end

def self.ticks
LibSDL.get_ticks
end

def self.remove(timer_id)
LibSDL.remove_timer(timer_id)
end
end
end