From 2945e7d478ed5775be64534e660a2eb5d14c3d2d Mon Sep 17 00:00:00 2001 From: zapashcanon Date: Thu, 12 Jan 2023 04:19:40 +0100 Subject: [PATCH] better keyboard handling --- src/island_client.ml | 99 +++++++++++++++++++++++++++++++++++++------- 1 file changed, 84 insertions(+), 15 deletions(-) diff --git a/src/island_client.ml b/src/island_client.ml index 8497116..33ef546 100644 --- a/src/island_client.ml +++ b/src/island_client.ml @@ -101,21 +101,82 @@ let send_action state action = Log.debug "sending action %a to server@\n" State.pp_action action; Ws_client.send (Network.Action_msg action) -let keydown_handler ev = +module Kb : sig + (* this keeps an ordered sequence of unique values, + it's the responsability of the caller to make sure + the same element is not added twice ! *) + + type t = string + + val add : t -> unit + + val rm : t -> unit + + val get_last : unit -> t option +end = struct + type t = string + + let last = ref [] + + let add k = last := k :: !last + + let rm k = last := List.filter (( <> ) k) !last + + let get_last () = match !last with [] -> None | key :: _keys -> Some key +end + +let keypress_handler = + (* be careful to add in the correct array ! *) + let codes = Hashtbl.create 512 in + Array.iter + (fun code -> Hashtbl.add codes code ()) + [| "ArrowDown" + ; "ArrowLeft" + ; "ArrowRight" + ; "ArrowUp" + ; "KeyA" + ; "KeyD" + ; "KeyS" + ; "KeyW" + |]; + let keys = Hashtbl.create 512 in + Array.iter (fun key -> Hashtbl.add keys key ()) [| "m" |]; + (* TODO: I'm not sure the Hashtbl business is worth it. + Before, we were matching on values instead of calling Hashtbl.mem. + It should be better with Hashtbl but it wasn't benchmarked. *) + fun ~down -> + let f = if down then Kb.add else Kb.rm in + fun ev -> + let ev = Ev.as_type ev in + (* repeat is true if and only if an event as already been sent since the key has been pressed + in this case, it's already in the sequence so we just skip it, we know it'll eventually be + released on keydown before it can appears again *) + if not @@ Ev.Keyboard.repeat ev then + let code = Ev.Keyboard.code ev |> Jstr.to_string in + if Hashtbl.mem codes code then f code + else + let key = Ev.Keyboard.key ev |> Jstr.to_string in + if Hashtbl.mem keys key then f key + +let apply_last_key () = let open State in - let ev = Ev.as_type ev in - let act = - match Ev.Keyboard.code ev |> Jstr.to_string with - | "KeyW" | "ArrowUp" -> Move Up - | "KeyA" | "ArrowLeft" -> Move Left - | "KeyS" | "ArrowDown" -> Move Down - | "KeyD" | "ArrowRight" -> Move Right - | _code -> ( - match Ev.Keyboard.key ev |> Jstr.to_string with - | "m" -> Meditate - | _key -> Do_nothing ) - in - Queue.add act input_queue + Kb.get_last () + |> Option.iter (fun code_or_key -> + let act = + (* when you add something here, don't forget to add the corresponding case in `keypress_handler` *) + match code_or_key with + | "KeyW" | "ArrowUp" -> Move Up + | "KeyA" | "ArrowLeft" -> Move Left + | "KeyS" | "ArrowDown" -> Move Down + | "KeyD" | "ArrowRight" -> Move Right + | "m" -> Meditate + | _ -> + (* if this happen, it means we're adding + bad values in `keypress_handler` + and that should be fixed *) + assert false + in + Queue.add act input_queue ) let render state = draw_canvas state; @@ -131,6 +192,7 @@ let rec game_loop state last_auto_update timestamp = let last_auto_update = if should_auto_update then timestamp else last_auto_update in + apply_last_key (); let state = (* apply queue of actions *) let state = Queue.fold State.perform_action state to_apply_queue in @@ -167,7 +229,14 @@ let () = (* bind keys *) let _e : Ev.listener = - Ev.listen Ev.keydown keydown_handler (Window.as_target G.window) + Ev.listen Ev.keydown + (keypress_handler ~down:true) + (Window.as_target G.window) + in + let _e : Ev.listener = + Ev.listen Ev.keyup + (keypress_handler ~down:false) + (Window.as_target G.window) in (* bind buttons *)