Skip to content
Merged
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
1 change: 1 addition & 0 deletions claudius.opam
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ depends: [
"giflib" {>= "1.0.3"}
"imagelib" {>= "20221222"}
"crunch" {>= "4.0.0"}
"hsluv" {>= "0.1.0"}
"ocamlformat" {>= "0.27.0" & with-dev-setup}
]
build: [
Expand Down
2 changes: 1 addition & 1 deletion dune-project
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@
(name claudius)
(synopsis "A retro-style graphics library")
(description "A functional style retro-graphics library for OCaml for building generative art, demos, and games.")
(depends (ocaml (>= 5.1)) dune (tsdl (>= 1.1.0)) (ounit2 :with-test) (odoc :with-doc) (giflib (>= 1.0.3)) (imagelib (>= 20221222)) (crunch (>= 4.0.0)) (ocamlformat (and (>= 0.27.0) :with-dev-setup )))
(depends (ocaml (>= 5.1)) dune (tsdl (>= 1.1.0)) (ounit2 :with-test) (odoc :with-doc) (giflib (>= 1.0.3)) (imagelib (>= 20221222)) (crunch (>= 4.0.0)) (hsluv (>= 0.1.0)) (ocamlformat (and (>= 0.27.0) :with-dev-setup )))
(tags
(graphics rendering paletted)))

Expand Down
16 changes: 10 additions & 6 deletions src/animation.ml
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,17 @@ type recording_state_t = {
let max_frames_default = 500

let start_recording ?(max_frames = max_frames_default) (n : int) :
recording_state_t =
(recording_state_t, string) result =
if max_frames <= 0 then failwith "Number of frames must be positive";
if n <= 0 then failwith "Number of frames must be positive";
if n > max_frames then
failwith (Printf.sprintf "Maximum %d frames allowed" max_frames_default);
Printf.printf "Started recording %d frames\n%!" n;
{ frames = []; frames_to_record = n; current_frame = 0 }
match n <= 0 with
| true -> Result.Error "Number of frames must be positive"
| false -> (
match n > max_frames with
| true ->
Result.Error
(Printf.sprintf "Maximum %d frames allowed" max_frames_default)
| false ->
Result.Ok { frames = []; frames_to_record = n; current_frame = 0 })

let stop_recording (recording_state : recording_state_t) : unit =
let frames = List.rev recording_state.frames in
Expand Down
5 changes: 3 additions & 2 deletions src/animation.mli
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,10 @@ type recording_state_t = {
current_frame : int;
}

val start_recording : ?max_frames:int -> int -> recording_state_t
val start_recording :
?max_frames:int -> int -> (recording_state_t, string) result
(** [start_recording ?max_frames n] returns a new animation recording state that
will record [n] frames. Raises [Failure] if n is non-positive or if
will record [n] frames, or an error result if n is non-positive or if
exceeding [max_frames]. *)

val stop_recording : recording_state_t -> unit
Expand Down
122 changes: 86 additions & 36 deletions src/base.ml
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,11 @@ end)
module PlatformKey = Keysdl
module PlatformMouse = Mousesdl

let show_stats = ref false
let recording_state : Animation.recording_state_t option ref = ref None
type t = {
show_stats : bool;
recording_state : Animation.recording_state_t option;
status : Stats.t;
}

type input_state = {
keys : KeyCodeSet.t;
Expand Down Expand Up @@ -144,8 +147,16 @@ let run title boot tick s =
let initial_input =
{ keys = KeyCodeSet.empty; events = []; mouse = Mouse.create scale }
in
let fps_stats = ref (Stats.create ()) in
let rec loop t prev_buffer input last_t =

let initial_internal_state =
{
show_stats = false;
recording_state = None;
status = Stats.create ();
}
in

let rec loop internal_state t prev_buffer input last_t =
let now = Sdl.get_ticks () in
let diff =
Int32.sub (Int32.of_int (1000 / 60)) (Int32.sub now last_t)
Expand All @@ -158,47 +169,86 @@ let run title boot tick s =
{ keys = new_keys; events = unified_events; mouse = new_mouse }
in
if exit then ()
else (
fps_stats :=
Stats.update ~now:(Unix.gettimeofday ()) ~tick:t !fps_stats;
else
let internal_state =
{
internal_state with
status =
Stats.update ~now:(Unix.gettimeofday ()) ~tick:t
internal_state.status;
}
in

show_stats :=
let internal_state =
List.fold_left
(fun acc ev ->
match ev with Event.KeyUp Key.F1 -> not acc | _ -> acc)
!show_stats input.events;

Screenshot.save_screenshot current_input.events s prev_buffer;

List.iter
(function
| Event.KeyDown Key.F3 -> (
Printf.printf
"Enter number of frames to record (default 500): %!";
try
let line = read_line () in
let n =
if String.trim line = "" then
Animation.max_frames_default
else int_of_string line
match ev with
| Event.KeyUp Key.F1 ->
{
internal_state with
show_stats = not internal_state.show_stats;
}
| Event.KeyUp Key.F2 ->
let log_message =
match Screenshot.save_screenshot s prev_buffer with
| Result.Ok path ->
Printf.sprintf "Screenshot saved as %s" path
| Result.Error msg -> msg
in
recording_state := Some (Animation.start_recording n)
with Failure _ ->
{
internal_state with
status = Stats.log internal_state.status log_message;
}
| Event.KeyUp Key.F3 -> (
Printf.printf
"Invalid input. Recording not started.\n%!")
| _ -> ())
input.events;
"Enter number of frames to record (default 500): %!";
try
let line = read_line () in
let n =
if String.trim line = "" then
Animation.max_frames_default
else int_of_string line
in
match Animation.start_recording n with
| Result.Ok recording_state ->
{
internal_state with
recording_state = Some recording_state;
}
| Result.Error msg ->
{
internal_state with
status = Stats.log internal_state.status msg;
}
with Failure _ ->
{
internal_state with
status =
Stats.log internal_state.status
"Invalid input. Recording not started.";
})
| _ -> acc)
internal_state input.events
in

let updated_buffer = tick t s prev_buffer current_input in

let stats_buffer =
Stats.render internal_state.status internal_state.show_stats t s
updated_buffer
in
let display_buffer =
if !show_stats then Stats.render !fps_stats t s updated_buffer
else updated_buffer
match stats_buffer with None -> updated_buffer | Some b -> b
in

recording_state :=
Option.bind !recording_state (fun st ->
Animation.record_frame st s display_buffer);
let internal_state =
{
internal_state with
recording_state =
Option.bind internal_state.recording_state (fun st ->
Animation.record_frame st s display_buffer);
}
in

if
display_buffer != prev_buffer
Expand All @@ -214,9 +264,9 @@ let run title boot tick s =
(match render_texture r texture s bitmap with
| Error (`Msg e) -> Sdl.log "Render error: %s" e
| Ok () -> ());
loop (t + 1) updated_buffer current_input now)
loop internal_state (t + 1) updated_buffer current_input now
in
loop 0 initial_buffer initial_input Int32.zero;
loop initial_internal_state 0 initial_buffer initial_input Int32.zero;
Sdl.destroy_texture texture;
Sdl.destroy_renderer r;
Sdl.destroy_window w;
Expand Down
3 changes: 0 additions & 3 deletions src/base.mli
Original file line number Diff line number Diff line change
Expand Up @@ -11,9 +11,6 @@ module PlatformMouse : module type of Mousesdl
(** A module that provides platform-specific mouse handling, based on the
{!Mousesdl} module. *)

val show_stats : bool ref
(** Whether stats display is currently enabled *)

type input_state = {
keys : KeyCodeSet.t;
events : Event.t list;
Expand Down
2 changes: 1 addition & 1 deletion src/dune
Original file line number Diff line number Diff line change
Expand Up @@ -7,4 +7,4 @@
(library
(name claudius)
(public_name claudius)
(libraries tsdl giflib crunch imagelib imagelib.unix))
(libraries tsdl giflib crunch imagelib imagelib.unix hsluv))
Loading