(*---------------------------------------------------------------------------
   Copyright (c) 2013 The tsdl programmers. All rights reserved.
   SPDX-License-Identifier: ISC
  ---------------------------------------------------------------------------*)

let unsafe_get = Array.unsafe_get

open Ctypes
open Foreign

module Sdl = struct

(* Enum cases and #ifdef'd constants, see support/ in the distribution *)

open Tsdl_consts

(* Formatting with continuation. *)

let kpp k fmt =
  let k fmt = k (Format.flush_str_formatter ()) in
  Format.kfprintf k Format.str_formatter fmt

(* Invalid_argument strings *)

let str = Printf.sprintf
let err_length_mul l mul = str "invalid length: %d not a multiple of %d" l mul
let err_read_field = "cannot read field"
let err_bigarray_pitch pitch ba_el_size =
  str "invalid bigarray kind: pitch (%d bytes) not a multiple of bigarray \
       element byte size (%d)" pitch ba_el_size

let err_bigarray_data len ba_el_size =
  str "invalid bigarray kind: data (%d bytes) not a multiple of bigarray \
       element byte size (%d)" len ba_el_size

let err_array_to_short ~exp ~fnd =
  str "array too short exp:%d bytes found:%d bytes" exp fnd

(* ctypes views *)

let write_never _ = assert false

let bool =
  view ~read:((<>)0) ~write:(fun b -> compare b false) int;;

let int_as_uint8_t =
  view ~read:Unsigned.UInt8.to_int ~write:Unsigned.UInt8.of_int uint8_t

let int_as_uint16_t =
  view ~read:Unsigned.UInt16.to_int ~write:Unsigned.UInt16.of_int uint16_t

let int_as_uint32_t =
  view ~read:Unsigned.UInt32.to_int ~write:Unsigned.UInt32.of_int uint32_t

let int_as_int32_t =
  view ~read:Signed.Int32.to_int ~write:Signed.Int32.of_int int32_t

let int32_as_uint32_t =
  view ~read:Unsigned.UInt32.to_int32 ~write:Unsigned.UInt32.of_int32 uint32_t

let string_as_char_array n = (* FIXME: drop this if ctypes proposes better *)
  let n_array = array n char in
  let read a =
    let len = CArray.length a in
    let b = Buffer.create len in
    try
      for i = 0 to len - 1 do
        let c = CArray.get a i in
        if c = '\000' then raise Exit else Buffer.add_char b c
      done;
      Buffer.contents b
    with Exit -> Buffer.contents b
  in
  let write s =
    let a = CArray.make char n in
    let len = min (CArray.length a) (String.length s) in
    for i = 0 to len - 1 do CArray.set a i (s.[i]) done;
    a
  in
  view ~read ~write n_array

let get_error =
  foreign "SDL_GetError" (void @-> returning string)

(* SDL results *)

type nonrec 'a result = ( 'a, [ `Msg of string ] ) result

let error () = Error (`Msg (get_error ()))

let zero_to_ok =
  let read = function 0 -> Ok () | err -> error () in
  view ~read ~write:write_never int

let one_to_ok =
  let read = function 1 -> Ok () | err -> error () in
  view ~read ~write:write_never int

let bool_to_ok =
  let read = function 0 -> Ok false | 1 -> Ok true | _ -> error () in
  view ~read ~write:write_never int

let nat_to_ok =
  let read = function n when n < 0 -> error () | n -> Ok n in
  view ~read ~write:write_never int

let some_to_ok t =
  let read = function Some v -> Ok v | None -> error () in
  view ~read ~write:write_never t

let sdl_free = foreign "SDL_free" (ptr void @-> returning void)

(* Since we never let SDL redefine our main make sure this is always
   called. *)

let () =
  let set_main_ready = foreign "SDL_SetMainReady" (void @-> returning void) in
  set_main_ready ()

let stub = true


(* Integer types and maps *)

type uint8 = int
type uint16 = int
type int16 = int
type uint32 = int32
type uint64 = int64

module Int = struct type t = int let compare : int -> int -> int = compare end
module Imap = Map.Make(Int)

(* Bigarrays *)

type ('a, 'b) bigarray = ('a, 'b, Bigarray.c_layout) Bigarray.Array1.t

let ba_create k len = Bigarray.Array1.create k Bigarray.c_layout len
let ba_kind_byte_size :  ('a, 'b) Bigarray.kind -> int = fun k ->
  let open Bigarray in
  (* FIXME: see http://caml.inria.fr/mantis/view.php?id=6263 *)
  match Obj.magic k with
  | k when k = char || k = int8_signed || k = int8_unsigned -> 1
  | k when k = int16_signed || k = int16_unsigned -> 2
  | k when k = int32 || k = float32 -> 4
  | k when k = float64 || k = int64 || k = complex32 -> 8
  | k when k = complex64 -> 16
  | k when k = int || k = nativeint -> Sys.word_size / 8
  | k -> assert false

let access_ptr_typ_of_ba_kind : ('a, 'b) Bigarray.kind -> 'a ptr typ = fun k ->
  let open Bigarray in
  (* FIXME: use typ_of_bigarray_kind when ctypes support it. *)
  match Obj.magic k with
  | k when k = float32 -> Obj.magic (ptr Ctypes.float)
  | k when k = float64 -> Obj.magic (ptr Ctypes.double)
  | k when k = complex32 -> Obj.magic (ptr Ctypes.complex32)
  | k when k = complex64 -> Obj.magic (ptr Ctypes.complex64)
  | k when k = int8_signed -> Obj.magic (ptr Ctypes.int8_t)
  | k when k = int8_unsigned -> Obj.magic (ptr Ctypes.uint8_t)
  | k when k = int16_signed -> Obj.magic (ptr Ctypes.int16_t)
  | k when k = int16_unsigned -> Obj.magic (ptr Ctypes.uint16_t)
  | k when k = int -> Obj.magic (ptr Ctypes.camlint)
  | k when k = int32 -> Obj.magic (ptr Ctypes.int32_t)
  | k when k = int64 -> Obj.magic (ptr Ctypes.int64_t)
  | k when k = nativeint -> Obj.magic (ptr Ctypes.nativeint)
  | k when k = char -> Obj.magic (ptr Ctypes.char)
  | _ -> assert false

(* Basics *)

(* Initialization and shutdown *)

module Init = struct
  type t = Unsigned.uint32
  let i = Unsigned.UInt32.of_int
  let ( + ) = Unsigned.UInt32.logor
  let ( - ) f f' = Unsigned.UInt32.(logand f (lognot f'))
  let test f m = Unsigned.UInt32.(compare (logand f m) zero <> 0)
  let eq f f' = Unsigned.UInt32.(compare f f' = 0)
  let nothing = i 0
  let timer = i sdl_init_timer
  let audio = i sdl_init_audio
  let video = i sdl_init_video
  let joystick = i sdl_init_joystick
  let haptic = i sdl_init_haptic
  let gamecontroller = i sdl_init_gamecontroller
  let events = i sdl_init_events
  let everything = i sdl_init_everything
  let noparachute = i sdl_init_noparachute
end

let init =
  foreign "SDL_Init" (uint32_t @-> returning zero_to_ok)

let init_sub_system =
  foreign "SDL_InitSubSystem" (uint32_t @-> returning zero_to_ok)

let quit =
  foreign "SDL_Quit" (void @-> returning void)

let quit_sub_system =
  foreign "SDL_QuitSubSystem" (uint32_t @-> returning void)

let was_init =
  foreign "SDL_WasInit" (uint32_t @-> returning uint32_t)

let was_init = function
| None -> was_init (Unsigned.UInt32.of_int 0)
| Some m -> was_init m

(* Hints *)

module Hint = struct
  type t = string
  let audio_resampling_mode = sdl_hint_audio_resampling_mode
  let framebuffer_acceleration = sdl_hint_framebuffer_acceleration
  let idle_timer_disabled = sdl_hint_idle_timer_disabled
  let orientations = sdl_hint_orientations
  let mouse_focus_clickthrough = sdl_hint_mouse_focus_clickthrough
  let mouse_normal_speed_scale = sdl_hint_mouse_normal_speed_scale
  let mouse_relative_speed_scale = sdl_hint_mouse_relative_speed_scale
  let render_driver = sdl_hint_render_driver
  let render_logical_size_mode = sdl_hint_render_logical_size_mode
  let render_opengl_shaders = sdl_hint_render_opengl_shaders
  let render_scale_quality = sdl_hint_render_scale_quality
  let render_vsync = sdl_hint_render_vsync
  let no_signal_handlers = sdl_hint_no_signal_handlers
  let thread_stack_size = sdl_hint_thread_stack_size
  let touch_mouse_events = sdl_hint_touch_mouse_events
  let mouse_touch_events = sdl_hint_mouse_touch_events
  let window_frame_usable_while_cursor_hidden =
    sdl_hint_window_frame_usable_while_cursor_hidden

  type priority = int
  let default = sdl_hint_default
  let normal = sdl_hint_normal
  let override = sdl_hint_override
end

let clear_hints =
  foreign "SDL_ClearHints" (void @-> returning void)

let get_hint =
  foreign "SDL_GetHint" (string @-> returning string_opt)

let get_hint_boolean =
  foreign "SDL_GetHintBoolean" (string @-> bool @-> returning bool)

let set_hint =
  foreign "SDL_SetHint" (string @-> string @-> returning bool)

let set_hint_with_priority =
  foreign "SDL_SetHintWithPriority"
    (string @-> string @-> int @-> returning bool)

(* Errors *)

let clear_error =
  foreign "SDL_ClearError" (void @-> returning void)

let set_error =
  foreign "SDL_SetError" (string @-> returning int)

let set_error fmt =
  kpp (fun s -> ignore (set_error s)) fmt

(* Log *)

module Log = struct
  type category = int
  let category_application = sdl_log_category_application
  let category_error = sdl_log_category_error
  let category_system = sdl_log_category_system
  let category_audio = sdl_log_category_audio
  let category_video = sdl_log_category_video
  let category_render = sdl_log_category_render
  let category_input = sdl_log_category_input
  let category_custom = sdl_log_category_custom

  type priority = int
  let priority_compare : int -> int -> int = compare
  let priority_verbose = sdl_log_priority_verbose
  let priority_debug = sdl_log_priority_debug
  let priority_info = sdl_log_priority_info
  let priority_warn = sdl_log_priority_warn
  let priority_error = sdl_log_priority_error
  let priority_critical = sdl_log_priority_critical
end

external log_message : int -> int -> string -> unit = "ocaml_tsdl_log_message"
let log_message c p fmt = kpp (fun s -> log_message c p s) fmt

let log fmt = log_message Log.category_application Log.priority_info fmt
let log_critical c fmt = log_message c Log.priority_critical fmt
let log_debug c fmt = log_message c Log.priority_debug fmt
let log_info c fmt = log_message c Log.priority_info fmt
let log_error c fmt = log_message c Log.priority_error fmt
let log_verbose c fmt = log_message c Log.priority_verbose fmt
let log_warn c fmt = log_message c Log.priority_warn fmt

let log_get_priority =
  foreign "SDL_LogGetPriority" (int @-> returning int)

let log_reset_priorities =
  foreign "SDL_LogResetPriorities" (void @-> returning void)

let log_set_all_priority =
  foreign "SDL_LogSetAllPriority" (int @-> returning void)

let log_set_priority =
  foreign "SDL_LogSetPriority" (int @-> int @-> returning void)

(* Version *)

let version = structure "SDL_version"
let version_major = field version "major" uint8_t
let version_minor = field version "minor" uint8_t
let version_patch = field version "patch" uint8_t
let () = seal version

let get_version =
  foreign "SDL_GetVersion" (ptr version @-> returning void)

let get_version () =
  let get v f = Unsigned.UInt8.to_int (getf v f) in
  let v = make version in
  get_version (addr v);
  (get v version_major), (get v version_minor), (get v version_patch)

let get_revision =
  foreign "SDL_GetRevision" (void @-> returning string)

let get_revision_number =
  foreign "SDL_GetRevisionNumber" (void @-> returning int)

(* IO absraction *)

type _rw_ops
let rw_ops_struct : _rw_ops structure typ = structure "SDL_RWops"
let rw_ops : _rw_ops structure ptr typ = ptr rw_ops_struct
let rw_ops_opt : _rw_ops structure ptr option typ = ptr_opt rw_ops_struct

let rw_ops_size = field rw_ops_struct "size"
    (funptr (rw_ops @-> returning int64_t))
let rw_ops_seek = field rw_ops_struct "seek"
    (funptr (rw_ops @-> int64_t @-> int @-> returning int64_t))
let rw_ops_read = field rw_ops_struct "read"
    (funptr (rw_ops @-> ptr void @-> size_t @-> size_t @-> returning size_t))
let rw_ops_write = field rw_ops_struct "write"
    (funptr (rw_ops @-> ptr void @-> size_t @-> size_t @-> returning size_t))
let rw_ops_close = field rw_ops_struct "close"
    (funptr (rw_ops @-> returning int))
let _ = field rw_ops_struct "type" uint32_t
(* ... #ifdef'd union follows, we don't care we don't use Ctypes.make *)
let () = seal rw_ops_struct

type rw_ops = _rw_ops structure ptr

let load_file_rw =
  foreign "SDL_LoadFile_RW"
    (rw_ops @-> ptr int @-> bool @-> returning (some_to_ok string_opt))

let load_file_rw rw_ops close =
  load_file_rw rw_ops (coerce (ptr void) (ptr int) null) close

let rw_from_file =
  foreign "SDL_RWFromFile"
    (string @-> string @-> returning (some_to_ok rw_ops_opt))

let rw_from_const_mem =
  foreign "SDL_RWFromConstMem"
    (ocaml_string @-> int @-> returning (some_to_ok rw_ops_opt))

let rw_from_const_mem str = rw_from_const_mem
  (ocaml_string_start str) (String.length str)

let rw_from_mem =
  foreign "SDL_RWFromMem"
    (ocaml_bytes @-> int @-> returning (some_to_ok rw_ops_opt))

let rw_from_mem b = rw_from_mem (ocaml_bytes_start b) (Bytes.length b)

let load_file filename = (* defined as a macro in SDL_rwops.h *)
  match rw_from_file filename "rb" with
  | Error _ as e -> e
  | Ok rw -> load_file_rw rw true

let rw_close =
  foreign "SDL_RWclose" (rw_ops @-> returning int)

let rw_close ops =
  if rw_close ops = 0 then Ok () else (error ())

let unsafe_rw_ops_of_ptr addr : rw_ops =
  from_voidp rw_ops_struct (ptr_of_raw_address addr)
let unsafe_ptr_of_rw_ops rw_ops =
  raw_address_of_ptr (to_voidp rw_ops)

(* File system paths *)

let get_base_path =
  foreign "SDL_GetBasePath" (void @-> returning (ptr char))

let get_base_path () =
  let p = get_base_path () in
  let path = coerce (ptr char) (some_to_ok string_opt) p in
  sdl_free (coerce (ptr char) (ptr void) p);
  path

let get_pref_path =
  foreign "SDL_GetPrefPath" (string @-> string @-> returning (ptr char))

let get_pref_path ~org ~app =
  let p = get_pref_path org app in
  let path = coerce (ptr char) (some_to_ok string_opt) p in
  sdl_free (coerce (ptr char) (ptr void) p);
  path

(* Video *)

type window = unit ptr
let window : window typ = ptr void
let window_opt : window option typ = ptr_opt void

let unsafe_window_of_ptr addr : window =
  ptr_of_raw_address addr
let unsafe_ptr_of_window window =
  raw_address_of_ptr (to_voidp window)

(* Colors *)

type _color
type color = _color structure
let color : color typ = structure "SDL_Color"
let color_r = field color "r" uint8_t
let color_g = field color "g" uint8_t
let color_b = field color "b" uint8_t
let color_a = field color "a" uint8_t
let () = seal color

module Color = struct
  let create ~r ~g ~b ~a =
    let c = make color in
    setf c color_r (Unsigned.UInt8.of_int r);
    setf c color_g (Unsigned.UInt8.of_int g);
    setf c color_b (Unsigned.UInt8.of_int b);
    setf c color_a (Unsigned.UInt8.of_int a);
    c

  let r c = Unsigned.UInt8.to_int (getf c color_r)
  let g c = Unsigned.UInt8.to_int (getf c color_g)
  let b c = Unsigned.UInt8.to_int (getf c color_b)
  let a c = Unsigned.UInt8.to_int (getf c color_a)

  let set_r c r = setf c color_r (Unsigned.UInt8.of_int r)
  let set_g c g = setf c color_g (Unsigned.UInt8.of_int g)
  let set_b c b = setf c color_b (Unsigned.UInt8.of_int b)
  let set_a c a = setf c color_a (Unsigned.UInt8.of_int a)
end

(* Points *)

type _point
type point = _point structure
let point : point typ = structure "SDL_Point"
let point_x = field point "x" int
let point_y = field point "y" int
let () = seal point

module Point = struct
  let create ~x ~y =
    let p = make point in
    setf p point_x x;
    setf p point_y y;
    p

  let x p = getf p point_x
  let y p = getf p point_y

  let set_x p x = setf p point_x x
  let set_y p y = setf p point_y y

  let opt_addr = function
  | None -> coerce (ptr void) (ptr point) null
  | Some v -> addr v
end

(* Float Points *)

type _fpoint
type fpoint = _fpoint structure
let fpoint : fpoint typ = structure "SDL_FPoint"
let fpoint_x = field fpoint "x" float
let fpoint_y = field fpoint "y" float
let () = seal fpoint

module Fpoint = struct
  let create ~x ~y =
    let p = make fpoint in
    setf p fpoint_x x;
    setf p fpoint_y y;
    p

  let x p = getf p fpoint_x
  let y p = getf p fpoint_y

  let set_x p x = setf p fpoint_x x
  let set_y p y = setf p fpoint_y y
end

(* Vertices *)

type _vertex
type vertex = _vertex structure
let vertex : vertex typ = structure "SDL_Vertex"
let vertex_position = field vertex "position" fpoint
let vertex_color = field vertex "color" color
let vertex_tex_coord = field vertex "tex_coord" fpoint
let () = seal vertex

module Vertex = struct
  let create ~position ~color ~tex_coord =
    let v = make vertex in
    setf v vertex_position position;
    setf v vertex_color color;
    setf v vertex_tex_coord tex_coord;
    v

  let position v = getf v vertex_position
  let color v = getf v vertex_color
  let tex_coord v = getf v vertex_tex_coord

  let set_position v position = setf v vertex_position position
  let set_color v color = setf v vertex_color color
  let set_tex_coord v tex_coord = setf v vertex_tex_coord tex_coord

  let opt_addr = function
  | None -> coerce (ptr void) (ptr vertex) null
  | Some v -> addr v
end

(* Rectangle *)

type _rect
type rect = _rect structure
let rect : rect typ = structure "SDL_Rect"
let rect_x = field rect "x" int
let rect_y = field rect "y" int
let rect_w = field rect "w" int
let rect_h = field rect "h" int
let () = seal rect

module Rect = struct
  let create ~x ~y ~w ~h =
    let r = make rect in
    setf r rect_x x;
    setf r rect_y y;
    setf r rect_w w;
    setf r rect_h h;
    r

  let x r = getf r rect_x
  let y r = getf r rect_y
  let w r = getf r rect_w
  let h r = getf r rect_h

  let set_x r x = setf r rect_x x
  let set_y r y = setf r rect_y y
  let set_w r w = setf r rect_w w
  let set_h r h = setf r rect_h h

  let opt_addr = function
  | None -> coerce (ptr void) (ptr rect) null
  | Some v -> addr v
end

(* Float Rectangle *)

type _frect
type frect = _frect structure
let frect : frect typ = structure "SDL_FRect"
let frect_x = field frect "x" float
let frect_y = field frect "y" float
let frect_w = field frect "w" float
let frect_h = field frect "h" float
let () = seal frect

module Frect = struct
  let create ~x ~y ~w ~h =
    let r = make frect in
    setf r frect_x x;
    setf r frect_y y;
    setf r frect_w w;
    setf r frect_h h;
    r

  let x r = getf r frect_x
  let y r = getf r frect_y
  let w r = getf r frect_w
  let h r = getf r frect_h

  let set_x r x = setf r frect_x x
  let set_y r y = setf r frect_y y
  let set_w r w = setf r frect_w w
  let set_h r h = setf r frect_h h
end

let enclose_points =
  foreign "SDL_EnclosePoints"
    (ptr void @-> int @-> ptr rect @-> ptr rect @-> returning bool)

let enclose_points_ba ?clip ps =
  let len = Bigarray.Array1.dim ps in
  if len mod 2 <> 0 then invalid_arg (err_length_mul len 2) else
  let count = len / 2 in
  let ps = to_voidp (bigarray_start array1 ps) in
  let res = make rect in
  if enclose_points ps count (Rect.opt_addr clip) (addr res)
  then Some res
  else None

let enclose_points ?clip ps =
  let a = CArray.of_list point ps in
  let ps = to_voidp (CArray.start a) in
  let res = make rect in
  if enclose_points ps (CArray.length a) (Rect.opt_addr clip) (addr res)
  then Some res
  else None

let has_intersection =
  foreign "SDL_HasIntersection"
    (ptr rect @-> ptr rect @-> returning bool)

let has_intersection a b =
  has_intersection (addr a) (addr b)

let intersect_rect =
  foreign "SDL_IntersectRect"
    (ptr rect @-> ptr rect @-> ptr rect @-> returning bool)

let intersect_rect a b =
  let res = make rect in
  if intersect_rect (addr a) (addr b) (addr res) then Some res else None

let intersect_rect_and_line =
  foreign "SDL_IntersectRectAndLine"
    (ptr rect @-> ptr int @-> ptr int @-> ptr int @-> ptr int @->
     returning bool)

let intersect_rect_and_line r x1 y1 x2 y2 =
  let alloc v = allocate int v in
  let x1, y1 = alloc x1, alloc y1 in
  let x2, y2 = alloc x2, alloc y2 in
  if intersect_rect_and_line (addr r) x1 y1 x2 y2
  then Some ((!@x1, !@y1), (!@x2, !@y2))
  else None

let point_in_rect p r =
  (* SDL_FORCE_INLINE *)
  let px = Point.x p in
  let py = Point.y p in
  let rx = Rect.x r in
  let ry = Rect.y r in
  px >= rx && px < rx + Rect.w r && py >= ry && py < ry + Rect.h r

let rect_empty r =
  (* symbol doesn't exist: SDL_FORCE_INLINE directive
     foreign "SDL_RectEmpty" (ptr rect @-> returning bool) *)
  Rect.w r <= 0 || Rect.h r <= 0

let rect_equals a b =
  (* symbol doesn't exist: SDL_FORCE_INLINE directive
    foreign "SDL_RectEquals" (ptr rect @-> ptr rect @-> returning bool) *)
  (Rect.x a = Rect.x b) && (Rect.y a = Rect.y b) &&
  (Rect.w a = Rect.w b) && (Rect.h a = Rect.h b)

let union_rect =
  foreign "SDL_UnionRect"
    (ptr rect @-> ptr rect @-> ptr rect @-> returning void)

let union_rect a b =
  let res = make rect in
  union_rect (addr a) (addr b) (addr res);
  res

(* Palettes *)

type _palette
type palette_struct = _palette structure
let palette_struct : palette_struct typ = structure "SDL_Palette"
let palette_ncolors = field palette_struct "ncolors" int
let palette_colors = field palette_struct "colors" (ptr color)
let _ = field palette_struct "version" uint32_t
let _ = field palette_struct "refcount" int
let () = seal palette_struct

type palette = palette_struct ptr
let palette : palette typ = ptr palette_struct
let palette_opt : palette option typ = ptr_opt palette_struct

let unsafe_palette_of_ptr addr : palette =
  from_voidp palette_struct (ptr_of_raw_address addr)
let unsafe_ptr_of_palette palette =
  raw_address_of_ptr (to_voidp palette)

let alloc_palette =
  foreign "SDL_AllocPalette"
    (int @-> returning (some_to_ok palette_opt))

let free_palette =
  foreign "SDL_FreePalette" (palette @-> returning void)

let get_palette_ncolors p =
  getf (!@ p) palette_ncolors

let get_palette_colors p =
  let ps = !@ p in
  CArray.to_list
    (CArray.from_ptr (getf ps palette_colors) (getf ps palette_ncolors))

let get_palette_colors_ba p =
  let ps = !@ p in
  (* FIXME: ctypes should have a CArray.copy function *)
  let n = getf ps palette_ncolors in
  let ba = Bigarray.(Array1.create int8_unsigned c_layout (n * 4)) in
  let ba_ptr =
    CArray.from_ptr (coerce (ptr int) (ptr color) (bigarray_start array1 ba)) n
  in
  let ca = CArray.from_ptr (getf ps palette_colors) n in
  for i = 0 to n - 1 do CArray.set ba_ptr i (CArray.get ca i) done;
  ba

let set_palette_colors =
  foreign "SDL_SetPaletteColors"
    (palette @-> ptr void @-> int @-> int @-> returning zero_to_ok)

let set_palette_colors_ba p cs ~fst =
  let len = Bigarray.Array1.dim cs in
  if len mod 4 <> 0 then invalid_arg (err_length_mul len 4) else
  let count = len / 4 in
  let cs = to_voidp (bigarray_start array1 cs) in
  set_palette_colors p cs fst count

let set_palette_colors p cs ~fst =
  let a = CArray.of_list color cs in
  set_palette_colors p (to_voidp (CArray.start a)) fst (CArray.length a)

(* Pixel formats *)

type gamma_ramp = (int, Bigarray.int16_unsigned_elt) bigarray

let calculate_gamma_ramp =
  foreign "SDL_CalculateGammaRamp"
    (float @-> ptr void @-> returning void)

let calculate_gamma_ramp g =
  let ba = Bigarray.(Array1.create int16_unsigned c_layout 256) in
  calculate_gamma_ramp g (to_voidp (bigarray_start array1 ba));
  ba

module Blend = struct
  type mode = Unsigned.UInt.t
  let mode_none = Unsigned.UInt.of_int sdl_blendmode_none
  let mode_blend = Unsigned.UInt.of_int sdl_blendmode_blend
  let mode_add = Unsigned.UInt.of_int sdl_blendmode_add
  let mode_mod = Unsigned.UInt.of_int sdl_blendmode_mod

  type operation = int
  let add = sdl_blendoperation_add
  let subtract = sdl_blendoperation_subtract
  let rev_subtract = sdl_blendoperation_rev_subtract
  let minimum = sdl_blendoperation_minimum
  let maximum = sdl_blendoperation_maximum

  type factor = int
  let zero = sdl_blendfactor_zero
  let one = sdl_blendfactor_one
  let src_color = sdl_blendfactor_src_color
  let one_minus_src_color = sdl_blendfactor_one_minus_src_color
  let src_alpha = sdl_blendfactor_src_alpha
  let one_minus_src_alpha = sdl_blendfactor_one_minus_src_alpha
  let dst_color = sdl_blendfactor_dst_color
  let one_minus_dst_color = sdl_blendfactor_one_minus_dst_color
  let dst_alpha = sdl_blendfactor_dst_alpha
  let one_minus_dst_alpha = sdl_blendfactor_one_minus_dst_alpha

end

let compose_custom_blend_mode =
  foreign "SDL_ComposeCustomBlendMode"
    (int @-> int @-> int @-> int @-> int @-> int @-> returning uint)

module Pixel = struct
  type format_enum = Unsigned.UInt32.t
  let i = Unsigned.UInt32.of_int32
  let to_uint32 = Unsigned.UInt32.to_int32
  let eq f f' = Unsigned.UInt32.(compare f f' = 0)
  let format_unknown = i sdl_pixelformat_unknown
  let format_index1lsb = i sdl_pixelformat_index1lsb
  let format_index1msb = i sdl_pixelformat_index1msb
  let format_index4lsb = i sdl_pixelformat_index4lsb
  let format_index4msb = i sdl_pixelformat_index4msb
  let format_index8 = i sdl_pixelformat_index8
  let format_rgb332 = i sdl_pixelformat_rgb332
  let format_rgb444 = i sdl_pixelformat_rgb444
  let format_rgb555 = i sdl_pixelformat_rgb555
  let format_bgr555 = i sdl_pixelformat_bgr555
  let format_argb4444 = i sdl_pixelformat_argb4444
  let format_rgba4444 = i sdl_pixelformat_rgba4444
  let format_abgr4444 = i sdl_pixelformat_abgr4444
  let format_bgra4444 = i sdl_pixelformat_bgra4444
  let format_argb1555 = i sdl_pixelformat_argb1555
  let format_rgba5551 = i sdl_pixelformat_rgba5551
  let format_abgr1555 = i sdl_pixelformat_abgr1555
  let format_bgra5551 = i sdl_pixelformat_bgra5551
  let format_rgb565 = i sdl_pixelformat_rgb565
  let format_bgr565 = i sdl_pixelformat_bgr565
  let format_rgb24 = i sdl_pixelformat_rgb24
  let format_bgr24 = i sdl_pixelformat_bgr24
  let format_rgb888 = i sdl_pixelformat_rgb888
  let format_rgbx8888 = i sdl_pixelformat_rgbx8888
  let format_bgr888 = i sdl_pixelformat_bgr888
  let format_bgrx8888 = i sdl_pixelformat_bgrx8888
  let format_argb8888 = i sdl_pixelformat_argb8888
  let format_rgba8888 = i sdl_pixelformat_rgba8888
  let format_abgr8888 = i sdl_pixelformat_abgr8888
  let format_bgra8888 = i sdl_pixelformat_bgra8888
  let format_argb2101010 = i sdl_pixelformat_argb2101010
  let format_yv12 = i sdl_pixelformat_yv12
  let format_iyuv = i sdl_pixelformat_iyuv
  let format_yuy2 = i sdl_pixelformat_yuy2
  let format_uyvy = i sdl_pixelformat_uyvy
  let format_yvyu = i sdl_pixelformat_yvyu
end

(* Note. Giving direct access to the palette field of SDL_PixelFormat
   is problematic. We can't ensure the pointer won't become invalid at
   a certain point. *)

type _pixel_format
type pixel_format_struct = _pixel_format structure
let pixel_format_struct : pixel_format_struct typ = structure "SDL_PixelFormat"
let pf_format = field pixel_format_struct "format" uint32_t
let pf_palette = field pixel_format_struct "palette" palette
let pf_bits_per_pixel = field pixel_format_struct "BitsPerPixel" uint8_t
let pf_bytes_per_pixel = field pixel_format_struct "BytesPerPixel" uint8_t
let _ = field pixel_format_struct "padding" uint16_t
let _ = field pixel_format_struct "Rmask" uint32_t
let _ = field pixel_format_struct "Gmask" uint32_t
let _ = field pixel_format_struct "Bmask" uint32_t
let _ = field pixel_format_struct "Amask" uint32_t
let _ = field pixel_format_struct "Rloss" uint8_t
let _ = field pixel_format_struct "Gloss" uint8_t
let _ = field pixel_format_struct "Bloss" uint8_t
let _ = field pixel_format_struct "Aloss" uint8_t
let _ = field pixel_format_struct "Rshift" uint8_t
let _ = field pixel_format_struct "Gshift" uint8_t
let _ = field pixel_format_struct "Bshift" uint8_t
let _ = field pixel_format_struct "Ashift" uint8_t
let _ = field pixel_format_struct "refcount" int
let _ = field pixel_format_struct "next" (ptr pixel_format_struct)
let () = seal pixel_format_struct

type pixel_format = pixel_format_struct ptr
let pixel_format : pixel_format typ = ptr pixel_format_struct
let pixel_format_opt : pixel_format option typ = ptr_opt pixel_format_struct

let unsafe_pixel_format_of_ptr addr : pixel_format =
  from_voidp pixel_format_struct (ptr_of_raw_address addr)
let unsafe_ptr_of_pixel_format pixel_format =
  raw_address_of_ptr (to_voidp pixel_format)

let alloc_format =
  foreign "SDL_AllocFormat"
    (uint32_t @-> returning (some_to_ok pixel_format_opt))

let free_format =
  foreign "SDL_FreeFormat" (pixel_format @-> returning void)

let get_pixel_format_name =
  foreign "SDL_GetPixelFormatName" (uint32_t @-> returning string)

let get_pixel_format_format pf =
  getf (!@ pf) pf_format

let get_pixel_format_bits_pp pf =
  Unsigned.UInt8.to_int (getf (!@ pf) pf_bits_per_pixel)

let get_pixel_format_bytes_pp pf =
  Unsigned.UInt8.to_int (getf (!@ pf) pf_bytes_per_pixel)

let get_rgb =
  foreign "SDL_GetRGB"
    (int32_as_uint32_t @-> pixel_format @-> ptr uint8_t @->
     ptr uint8_t @-> ptr uint8_t @-> returning void)

let get_rgb pf p =
  let alloc () = allocate uint8_t Unsigned.UInt8.zero in
  let to_int = Unsigned.UInt8.to_int in
  let r, g, b = alloc (), alloc (), alloc () in
  get_rgb p pf r g b;
   to_int (!@ r), to_int (!@ g), to_int (!@ b)

let get_rgba =
  foreign "SDL_GetRGBA"
    (int32_as_uint32_t @-> pixel_format @-> ptr uint8_t @->
     ptr uint8_t @-> ptr uint8_t @-> ptr uint8_t @-> returning void)

let get_rgba pf p =
  let alloc () = allocate uint8_t Unsigned.UInt8.zero in
  let to_int = Unsigned.UInt8.to_int in
  let r, g, b, a = alloc (), alloc (), alloc (), alloc () in
  get_rgba p pf r g b a;
   to_int (!@ r), to_int (!@ g), to_int (!@ b), to_int (!@ a)

let map_rgb =
  foreign "SDL_MapRGB"
    (pixel_format @-> int_as_uint8_t @-> int_as_uint8_t @-> int_as_uint8_t @->
     returning int32_as_uint32_t)

let map_rgba =
  foreign "SDL_MapRGBA"
    (pixel_format @-> int_as_uint8_t @-> int_as_uint8_t @-> int_as_uint8_t @->
     int_as_uint8_t @-> returning int32_as_uint32_t)

let masks_to_pixel_format_enum =
  foreign "SDL_MasksToPixelFormatEnum"
    (int @-> int32_as_uint32_t @-> int32_as_uint32_t @-> int32_as_uint32_t @->
     int32_as_uint32_t @-> returning uint32_t)

let pixel_format_enum_to_masks =
  foreign "SDL_PixelFormatEnumToMasks"
    (uint32_t @-> ptr int @->
     ptr uint32_t @-> ptr uint32_t @-> ptr uint32_t @-> ptr uint32_t @->
     returning bool)

let pixel_format_enum_to_masks pf =
  let ui () = allocate uint32_t (Unsigned.UInt32.of_int 0) in
  let get iptr = Unsigned.UInt32.to_int32 (!@ iptr) in
  let bpp = allocate int 0 in
  let rm, gm, bm, am = ui (), ui (), ui (), ui () in
  if not (pixel_format_enum_to_masks pf bpp rm gm bm am) then error () else
  Ok (!@ bpp, get rm, get gm, get bm, get am)

let set_pixel_format_palette =
  foreign "SDL_SetPixelFormatPalette"
    (pixel_format @-> palette @-> returning zero_to_ok)

(* Surface *)

type _surface
type surface_struct = _surface structure
let surface_struct : surface_struct typ = structure "SDL_Surface"
let _ = field surface_struct "flags" uint32_t
let surface_format = field surface_struct "format" pixel_format
let surface_w = field surface_struct "w" int
let surface_h = field surface_struct "h" int
let surface_pitch = field surface_struct "pitch" int
let surface_pixels = field surface_struct "pixels" (ptr void)
let _ = field surface_struct "userdata" (ptr void)
let _ = field surface_struct "locked" int
let _ = field surface_struct "list_blitmap" (ptr void)
let _ = field surface_struct "clip_rect" rect
let _ = field surface_struct "map" (ptr void)
let _ = field surface_struct "refcount" int
let () = seal surface_struct

type surface = surface_struct ptr
let surface : surface typ = ptr surface_struct
let surface_opt : surface option typ = ptr_opt surface_struct

let unsafe_surface_of_ptr addr : surface =
  from_voidp surface_struct (ptr_of_raw_address addr)
let unsafe_ptr_of_surface surface =
  raw_address_of_ptr (to_voidp surface)

let blit_scaled =
  (* SDL_BlitScaled is #ifdef'd to SDL_UpperBlitScaled *)
  foreign "SDL_UpperBlitScaled"
    (surface @-> ptr rect @-> surface @-> ptr rect @-> returning zero_to_ok)

let blit_scaled ~src sr ~dst dr =
  blit_scaled src (Rect.opt_addr sr) dst (Rect.opt_addr dr)

let blit_surface =
  (* SDL_BlitSurface is #ifdef'd to SDL_UpperBlit *)
  foreign "SDL_UpperBlit"
    (surface @-> ptr rect @-> surface @-> ptr rect @-> returning zero_to_ok)

let blit_surface ~src sr ~dst dr =
  blit_surface src (Rect.opt_addr sr) dst (Rect.opt_addr dr)

let convert_pixels =
  foreign "SDL_ConvertPixels"
    (int @-> int @-> uint32_t @-> ptr void @-> int @-> uint32_t @->
     ptr void @-> int @-> returning zero_to_ok)

let convert_pixels ~w ~h ~src sp spitch ~dst dp dpitch =
  (* FIXME: we could try check bounds. *)
  let spitch = ba_kind_byte_size (Bigarray.Array1.kind sp) * spitch in
  let dpitch = ba_kind_byte_size (Bigarray.Array1.kind dp) * dpitch in
  let sp = to_voidp (bigarray_start array1 sp) in
  let dp = to_voidp (bigarray_start array1 dp) in
  convert_pixels w h src sp spitch dst dp dpitch

let convert_surface =
  foreign "SDL_ConvertSurface"
    (surface @-> pixel_format @-> uint32_t @->
     returning (some_to_ok surface_opt))

let convert_surface s pf =
  convert_surface s pf Unsigned.UInt32.zero

let convert_surface_format =
  foreign "SDL_ConvertSurfaceFormat"
    (surface @-> uint32_t @-> uint32_t @-> returning (some_to_ok surface_opt))

let convert_surface_format s pf =
  convert_surface_format s pf Unsigned.UInt32.zero

let create_rgb_surface =
  foreign "SDL_CreateRGBSurface"
    (uint32_t @-> int @-> int @-> int @-> int32_as_uint32_t @->
     int32_as_uint32_t @-> int32_as_uint32_t @-> int32_as_uint32_t @->
     returning (some_to_ok surface_opt))

let create_rgb_surface ~w ~h ~depth rmask gmask bmask amask =
  create_rgb_surface Unsigned.UInt32.zero w h depth rmask gmask bmask amask

let create_rgb_surface_from =
  foreign "SDL_CreateRGBSurfaceFrom"
    (ptr void @-> int @-> int @-> int @-> int @-> int32_as_uint32_t @->
     int32_as_uint32_t @-> int32_as_uint32_t @-> int32_as_uint32_t @->
     returning (some_to_ok surface_opt))

let create_rgb_surface_from p ~w ~h ~depth ~pitch rmask gmask bmask amask =
  (* FIXME: we could try check bounds. *)
  let pitch = ba_kind_byte_size (Bigarray.Array1.kind p) * pitch in
  let p = to_voidp (bigarray_start array1 p) in
  create_rgb_surface_from p w h depth pitch rmask gmask bmask amask

let create_rgb_surface_with_format =
  foreign "SDL_CreateRGBSurfaceWithFormat"
    (uint32_t @-> int @-> int @-> int @-> uint32_t @->
     returning (some_to_ok surface_opt))

let create_rgb_surface_with_format ~w ~h ~depth format =
  create_rgb_surface_with_format Unsigned.UInt32.zero w h depth format

let create_rgb_surface_with_format_from =
  foreign "SDL_CreateRGBSurfaceWithFormatFrom"
    (ptr void @-> int @-> int @-> int @-> int @-> uint32_t @->
     returning (some_to_ok surface_opt))

let create_rgb_surface_with_format_from p ~w ~h ~depth ~pitch format =
  (* FIXME: check bounds? *)
  let pitch = ba_kind_byte_size (Bigarray.Array1.kind p) * pitch in
  let p = to_voidp (bigarray_start array1 p) in
  create_rgb_surface_with_format_from p w h depth pitch format

let duplicate_surface =
  foreign "SDL_DuplicateSurface" (surface @-> returning surface)

let fill_rect =
  foreign "SDL_FillRect"
    (surface @-> ptr rect @-> int32_as_uint32_t @-> returning zero_to_ok)

let fill_rect s r c =
  fill_rect s (Rect.opt_addr r) c

let fill_rects =
  foreign "SDL_FillRects"
    (surface @-> ptr void @-> int @-> int32_as_uint32_t @->
     returning zero_to_ok)

let fill_rects_ba s rs col =
  let len = Bigarray.Array1.dim rs in
  if len mod 4 <> 0 then invalid_arg (err_length_mul len 4) else
  let count = len / 4 in
  let rs = to_voidp (bigarray_start array1 rs) in
  fill_rects s rs count col

let fill_rects s rs col =
  let a = CArray.of_list rect rs in
  fill_rects s (to_voidp (CArray.start a)) (CArray.length a) col

let free_surface =
  foreign "SDL_FreeSurface" (surface @-> returning void)

let get_clip_rect =
  foreign "SDL_GetClipRect" (surface @-> ptr rect @-> returning void)

let get_clip_rect s =
  let r = make rect in
  (get_clip_rect s (addr r); r)

let get_color_key =
  foreign "SDL_GetColorKey"
    (surface @-> ptr uint32_t @-> returning zero_to_ok)

let get_color_key s =
  let key = allocate uint32_t Unsigned.UInt32.zero in
  match get_color_key s key with
  | Ok () -> Ok (Unsigned.UInt32.to_int32 (!@ key)) | Error _ as e -> e

let get_surface_alpha_mod =
  foreign "SDL_GetSurfaceAlphaMod"
    (surface @-> ptr uint8_t @-> returning zero_to_ok)

let get_surface_alpha_mod s =
  let alpha = allocate uint8_t Unsigned.UInt8.zero in
  match get_surface_alpha_mod s alpha with
  | Ok () -> Ok (Unsigned.UInt8.to_int (!@ alpha)) | Error _ as e -> e

let get_surface_blend_mode =
  foreign "SDL_GetSurfaceBlendMode"
    (surface @-> ptr uint @-> returning zero_to_ok)

let get_surface_blend_mode s =
  let mode = allocate uint Unsigned.UInt.zero in
  match get_surface_blend_mode s mode with
  Ok () -> Ok (!@ mode) | Error _ as e -> e

let get_surface_color_mod =
  foreign "SDL_GetSurfaceColorMod"
    (surface @-> ptr uint8_t @-> ptr uint8_t @-> ptr uint8_t @->
     returning zero_to_ok)

let get_surface_color_mod s =
  let alloc () = allocate uint8_t Unsigned.UInt8.zero in
  let get v = Unsigned.UInt8.to_int (!@ v) in
  let r, g, b = alloc (), alloc (), alloc () in
  match get_surface_color_mod s r g b with
  | Ok () -> Ok (get r, get g, get b) | Error _ as e -> e

let get_surface_format_enum s =
  (* We don't give direct access to the format field. This prevents
     memory ownership problems. *)
  get_pixel_format_format (getf (!@ s) surface_format)

let get_surface_pitch s =
  getf (!@ s) surface_pitch

let get_surface_pixels s kind =
  let pitch = get_surface_pitch s in
  let kind_size = ba_kind_byte_size kind in
  if pitch mod kind_size <> 0
  then invalid_arg (err_bigarray_pitch pitch kind_size)
  else
  let h = getf (!@ s) surface_h in
  let ba_size = (pitch * h) / kind_size in
  let pixels = getf (!@ s) surface_pixels in
  let pixels = coerce (ptr void) (access_ptr_typ_of_ba_kind kind) pixels in
  bigarray_of_ptr array1 ba_size kind pixels

let get_surface_size s =
  getf (!@ s) surface_w, getf (!@ s) surface_h

let load_bmp_rw =
  foreign "SDL_LoadBMP_RW"
    (rw_ops @-> bool @-> returning (some_to_ok surface_opt))

let load_bmp_rw rw ~close =
  load_bmp_rw rw close

let load_bmp file =
  (* SDL_LoadBMP is cpp based *)
  match rw_from_file file "rb" with
  | Error _ as e -> e
  | Ok rw -> load_bmp_rw rw ~close:true

let lock_surface =
  foreign "SDL_LockSurface" (surface @-> returning zero_to_ok)

let lower_blit =
  foreign "SDL_LowerBlit"
    (surface @-> ptr rect @-> surface @-> ptr rect @-> returning zero_to_ok)

let lower_blit ~src sr ~dst dr =
  lower_blit src (addr sr) dst (addr dr)

let lower_blit_scaled =
  foreign "SDL_LowerBlitScaled"
    (surface @-> ptr rect @-> surface @-> ptr rect @-> returning zero_to_ok)

let lower_blit_scaled ~src sr ~dst dr =
  lower_blit_scaled src (addr sr) dst (addr dr)

let save_bmp_rw =
  foreign "SDL_SaveBMP_RW"
    (surface @-> rw_ops @-> bool @-> returning zero_to_ok)

let save_bmp_rw s rw ~close =
  save_bmp_rw s rw close

let save_bmp s file =
  (* SDL_SaveBMP is cpp based *)
  match rw_from_file file "wb" with
  | Error _ as e -> e
  | Ok rw -> save_bmp_rw s rw ~close:true

let set_clip_rect =
  foreign "SDL_SetClipRect" (surface @-> ptr rect @-> returning bool)

let set_clip_rect s r =
  set_clip_rect s (addr r)

let set_color_key =
  foreign "SDL_SetColorKey"
    (surface @-> bool @-> int32_as_uint32_t @-> returning zero_to_ok)

let set_surface_alpha_mod =
  foreign "SDL_SetSurfaceAlphaMod"
    (surface @-> int_as_uint8_t @-> returning zero_to_ok)

let set_surface_blend_mode =
  foreign "SDL_SetSurfaceBlendMode"
    (surface @-> uint @-> returning zero_to_ok)

let set_surface_color_mod =
  foreign "SDL_SetSurfaceColorMod"
    (surface @-> int_as_uint8_t @-> int_as_uint8_t @-> int_as_uint8_t @->
     returning zero_to_ok)

let set_surface_palette =
  foreign "SDL_SetSurfacePalette"
    (surface @-> palette @-> returning zero_to_ok)

let set_surface_rle =
  foreign "SDL_SetSurfaceRLE" (surface @-> bool @-> returning zero_to_ok)

let unlock_surface =
  foreign "SDL_UnlockSurface" (surface @-> returning void)

(* Renderers *)

type flip = int
module Flip = struct
  let ( + ) = ( lor )
  let none = sdl_flip_none
  let horizontal = sdl_flip_horizontal
  let vertical = sdl_flip_vertical
end

type texture = unit ptr
let texture : texture typ = ptr void
let texture_opt : texture option typ = ptr_opt void

let unsafe_texture_of_ptr addr : texture =
  ptr_of_raw_address addr
let unsafe_ptr_of_texture texture =
  raw_address_of_ptr (to_voidp texture)

type renderer = unit ptr
let renderer : renderer typ = ptr void
let renderer_opt : renderer option typ = ptr_opt void

let unsafe_renderer_of_ptr addr : renderer =
  ptr_of_raw_address addr
let unsafe_ptr_of_renderer renderer =
  raw_address_of_ptr (to_voidp renderer)

module Renderer = struct
  type flags = Unsigned.uint32
  let i = Unsigned.UInt32.of_int
  let ( + ) = Unsigned.UInt32.logor
  let ( - ) f f' = Unsigned.UInt32.(logand f (lognot f'))
  let test f m = Unsigned.UInt32.(compare (logand f m) zero <> 0)
  let eq f f' = Unsigned.UInt32.(compare f f' = 0)
  let software = i sdl_renderer_software
  let accelerated = i sdl_renderer_accelerated
  let presentvsync = i sdl_renderer_presentvsync
  let targettexture = i sdl_renderer_targettexture
end

type renderer_info =
  { ri_name : string;
    ri_flags : Renderer.flags;
    ri_texture_formats : Pixel.format_enum list;
    ri_max_texture_width : int;
    ri_max_texture_height : int; }

let renderer_info = structure "SDL_RendererInfo"
let ri_name = field renderer_info "name" string
let ri_flags = field renderer_info "flags" uint32_t
let ri_num_tf = field renderer_info "num_texture_formats" uint32_t
let ri_tfs = field renderer_info "texture_formats" (array 16 uint32_t)
let ri_max_texture_width = field renderer_info "max_texture_width" int
let ri_max_texture_height = field renderer_info "max_texture_height" int
let () = seal renderer_info

let renderer_info_of_c c =
  let ri_name = getf c ri_name in
  let ri_flags = getf c ri_flags in
  let num_tf = Unsigned.UInt32.to_int (getf c ri_num_tf) in
  let tfs = getf c ri_tfs in
  let ri_texture_formats =
    let acc = ref [] in
    for i = 0 to num_tf - 1 do acc := (CArray.get tfs i) :: !acc done;
    List.rev !acc
  in
  let ri_max_texture_width = getf c ri_max_texture_width in
  let ri_max_texture_height = getf c ri_max_texture_height in
  { ri_name; ri_flags; ri_texture_formats; ri_max_texture_width;
    ri_max_texture_height }

let create_renderer =
  foreign "SDL_CreateRenderer"
    (window @-> int @-> uint32_t @-> returning (some_to_ok renderer_opt))

let create_renderer ?(index = -1) ?(flags = Unsigned.UInt32.zero) w =
  create_renderer w index flags

let create_software_renderer =
  foreign "SDL_CreateSoftwareRenderer"
    (surface @-> returning (some_to_ok renderer_opt))

let destroy_renderer =
  foreign "SDL_DestroyRenderer" (renderer @-> returning void)

let get_num_render_drivers =
  foreign "SDL_GetNumRenderDrivers" (void @-> returning nat_to_ok)

let get_render_draw_blend_mode =
  foreign "SDL_GetRenderDrawBlendMode"
    (renderer @-> ptr uint @-> returning zero_to_ok)

let get_render_draw_blend_mode r =
  let m = allocate uint Unsigned.UInt.zero in
  match get_render_draw_blend_mode r m with
  | Ok () -> Ok !@m | Error _ as e -> e

let get_render_draw_color =
  foreign "SDL_GetRenderDrawColor"
    (renderer @-> ptr uint8_t @-> ptr uint8_t @-> ptr uint8_t @->
     ptr uint8_t @-> returning zero_to_ok)

let get_render_draw_color rend =
  let alloc () = allocate uint8_t Unsigned.UInt8.zero in
  let get v = Unsigned.UInt8.to_int (!@ v) in
  let r, g, b, a = alloc (), alloc (), alloc (), alloc () in
  match get_render_draw_color rend r g b a with
  | Ok () -> Ok (get r, get g, get b, get a) | Error _ as e -> e

let get_render_driver_info =
  foreign "SDL_GetRenderDriverInfo"
    (int @-> ptr renderer_info @-> returning zero_to_ok)

let get_render_driver_info i =
  let info = make renderer_info in
  match get_render_driver_info i (addr info) with
  | Ok () -> Ok (renderer_info_of_c info) | Error _ as e -> e

let get_render_target =
  foreign "SDL_GetRenderTarget" (renderer @-> returning texture_opt)

let get_renderer =
  foreign "SDL_GetRenderer"
    (window @-> returning (some_to_ok renderer_opt))

let get_renderer_info =
  foreign "SDL_GetRendererInfo"
    (renderer @-> ptr renderer_info @-> returning zero_to_ok)

let get_renderer_info r =
  let info = make renderer_info in
  match get_renderer_info r (addr info) with
  | Ok () -> Ok (renderer_info_of_c info) | Error _ as e -> e

let get_renderer_output_size =
  foreign "SDL_GetRendererOutputSize"
    (renderer @-> ptr int @-> ptr int @-> returning zero_to_ok)

let get_renderer_output_size r =
  let w = allocate int 0 in
  let h = allocate int 0 in
  match get_renderer_output_size r w h with
  | Ok () -> Ok (!@ w, !@ h) | Error _ as e -> e

let render_clear =
  foreign "SDL_RenderClear" (renderer @-> returning zero_to_ok)

let render_copy =
  foreign "SDL_RenderCopy"
    (renderer @-> texture @-> ptr rect @-> ptr rect @->
     returning zero_to_ok)

let render_copy ?src ?dst r t =
  render_copy r t (Rect.opt_addr src) (Rect.opt_addr dst)

let render_copy_ex =
  foreign "SDL_RenderCopyEx"
    (renderer @-> texture @-> ptr rect @-> ptr rect @-> double @->
     ptr point @-> int @-> returning zero_to_ok)

let render_copy_ex ?src ?dst r t angle c flip =
  render_copy_ex r t (Rect.opt_addr src) (Rect.opt_addr dst) angle
    (Point.opt_addr c) flip

let render_draw_line =
  foreign "SDL_RenderDrawLine"
    (renderer @-> int @-> int @-> int @-> int @-> returning zero_to_ok)

let render_draw_line_f =
  foreign "SDL_RenderDrawLineF"
    (renderer @-> float @-> float @-> float @-> float @-> returning zero_to_ok)

let render_draw_lines =
  foreign "SDL_RenderDrawLines"
    (renderer @-> ptr void @-> int @-> returning zero_to_ok)

let render_draw_lines_ba r ps =
  let len = Bigarray.Array1.dim ps in
  if len mod 2 <> 0 then invalid_arg (err_length_mul len 2) else
  let count = len / 2 in
  let ps = to_voidp (bigarray_start array1 ps) in
  render_draw_lines r ps count

let render_draw_lines r ps =
  let a = CArray.of_list point ps in
  render_draw_lines r (to_voidp (CArray.start a)) (CArray.length a)

let render_draw_point =
  foreign "SDL_RenderDrawPoint"
    (renderer @-> int @-> int @-> returning zero_to_ok)

let render_draw_points =
  foreign "SDL_RenderDrawPoints"
    (renderer @-> ptr void @-> int @-> returning zero_to_ok)

let render_draw_points_ba r ps =
  let len = Bigarray.Array1.dim ps in
  if len mod 2 <> 0 then invalid_arg (err_length_mul len 2) else
  let count = len / 2 in
  let ps = to_voidp (bigarray_start array1 ps) in
  render_draw_points r ps count

let render_draw_points r ps =
  let a = CArray.of_list point ps in
  render_draw_points r (to_voidp (CArray.start a)) (CArray.length a)

let render_draw_point_f =
  foreign "SDL_RenderDrawPointF"
    (renderer @-> float @-> float @-> returning zero_to_ok)

let render_draw_points_f =
  foreign "SDL_RenderDrawPointsF"
    (renderer @-> ptr void @-> int @-> returning zero_to_ok)

let render_draw_points_f_ba r ps =
  let len = Bigarray.Array1.dim ps in
  if len mod 2 <> 0 then invalid_arg (err_length_mul len 2) else
  let count = len / 2 in
  let ps = to_voidp (bigarray_start array1 ps) in
  render_draw_points_f r ps count

let render_draw_points_f r ps =
  let a = CArray.of_list fpoint ps in
  render_draw_points_f r (to_voidp (CArray.start a)) (CArray.length a)

let render_draw_rect =
  foreign "SDL_RenderDrawRect"
    (renderer @-> ptr rect @-> returning zero_to_ok)

let render_draw_rect rend r =
  render_draw_rect rend (Rect.opt_addr r)

let render_draw_rects =
  foreign "SDL_RenderDrawRects"
    (renderer @-> ptr void @-> int @-> returning zero_to_ok)

let render_draw_rects_ba r rs =
  let len = Bigarray.Array1.dim rs in
  if len mod 4 <> 0 then invalid_arg (err_length_mul len 4) else
  let count = len / 4 in
  let rs = to_voidp (bigarray_start array1 rs) in
  render_draw_rects r rs count

let render_draw_rects r rs =
  let a = CArray.of_list rect rs in
  render_draw_rects r (to_voidp (CArray.start a)) (CArray.length a)

let render_fill_rect =
  foreign "SDL_RenderFillRect"
    (renderer @-> ptr rect @-> returning zero_to_ok)

let render_fill_rect rend r =
  render_fill_rect rend (Rect.opt_addr r)

let render_fill_rects =
  foreign "SDL_RenderFillRects"
    (renderer @-> ptr void @-> int @-> returning zero_to_ok)

let render_fill_rects_ba r rs =
  let len = Bigarray.Array1.dim rs in
  if len mod 4 <> 0 then invalid_arg (err_length_mul len 4) else
  let count = len / 4 in
  let rs = to_voidp (bigarray_start array1 rs) in
  render_fill_rects r rs count

let render_fill_rects r rs =
  let a = CArray.of_list rect rs in
  render_fill_rects r (to_voidp (CArray.start a)) (CArray.length a)

let render_geometry =
  foreign "SDL_RenderGeometry"
    (renderer @-> texture @-> ptr void @-> int @-> ptr void @-> int @->
     returning zero_to_ok)

let render_geometry ?indices ?texture r vertices =
  let a1 = CArray.of_list vertex vertices in
  let t = match texture with
  | None -> null | Some texture -> texture
  in
  let a2_ptr, a2_len = match indices with
  | None -> (null, 0)
  | Some is ->
      let a2 = CArray.of_list int is in
      (to_voidp (CArray.start a2), CArray.length a2)
  in
  render_geometry
    r t (to_voidp (CArray.start a1)) (CArray.length a1) a2_ptr a2_len

let render_geometry_raw =
  foreign "SDL_RenderGeometryRaw"
    (renderer @-> texture @->
     ptr void @-> int @->
     ptr void @-> int @->
     ptr void @-> int @->
     int @-> ptr void @-> int @-> int @-> returning zero_to_ok)

let render_geometry_raw
    ?indices ?texture r ~xy ?(xy_stride = 8) ~color ?(color_stride = 4)
    ~uv ?(uv_stride = 8) ~num_vertices ()
  =
  let t = match texture with
  | None -> null | Some texture -> texture
  in
  let i_ptr, i_len = match indices with
  | None -> null, 0
  | Some is -> to_voidp (bigarray_start array1 is), Bigarray.Array1.dim is
  in
  let i_stride = 4 in (* indices are assumed to be 4-byte integers *)
  let xy_ptr = to_voidp (bigarray_start array1 xy) in
  let xy_len_bytes = Bigarray.Array1.dim xy * 4 in
  let xy_exp_bytes = num_vertices * xy_stride - (xy_stride - 8) in
  if xy_len_bytes < xy_exp_bytes then begin
    let msg = "xy " ^ err_array_to_short ~exp:xy_exp_bytes ~fnd:xy_len_bytes in
    invalid_arg msg
  end;
  let color_ptr = to_voidp (bigarray_start array1 color) in
  let color_len_bytes = Bigarray.Array1.dim color in
  let color_exp_bytes = num_vertices * color_stride - (color_stride - 4) in
  if color_len_bytes < color_exp_bytes then begin
    let msg =
      "color " ^ err_array_to_short ~exp:color_exp_bytes ~fnd:color_len_bytes
    in
    invalid_arg msg
  end;
  let uv_ptr = to_voidp (bigarray_start array1 uv) in
  let uv_len_bytes = Bigarray.Array1.dim uv * 4 in
  let uv_exp_bytes = num_vertices * uv_stride - (uv_stride - 8) in
  if uv_len_bytes < uv_exp_bytes then begin
    let msg =
      "uv " ^ err_array_to_short ~exp:uv_exp_bytes ~fnd:uv_len_bytes
    in
    invalid_arg msg
  end;
  render_geometry_raw
    r t xy_ptr xy_stride color_ptr color_stride uv_ptr uv_stride num_vertices
    i_ptr i_len i_stride

let render_get_clip_rect =
  foreign "SDL_RenderGetClipRect"
    (renderer @-> ptr rect @-> returning void)

let render_get_clip_rect rend =
  let r = make rect in
  render_get_clip_rect rend (addr r);
  r

let render_is_clip_enabled =
  foreign "SDL_RenderIsClipEnabled" (renderer @-> returning bool)

let render_get_integer_scale =
 foreign "SDL_RenderGetIntegerScale"
    (renderer @-> returning bool)

let render_get_logical_size =
  foreign "SDL_RenderGetLogicalSize"
    (renderer @-> ptr int @-> ptr int @-> returning void)

let render_get_logical_size r =
  let w = allocate int 0 in
  let h = allocate int 0 in
  render_get_logical_size r w h;
  !@ w, !@ h

let render_get_scale =
  foreign "SDL_RenderGetScale"
    (renderer @-> ptr float @-> ptr float @-> returning void)

let render_get_scale r =
  let x = allocate float 0. in
  let y = allocate float 0. in
  render_get_scale r x y;
  !@ x, !@ y

let render_get_viewport =
  foreign "SDL_RenderGetViewport"
    (renderer @-> ptr rect @-> returning void)

let render_get_viewport rend =
  let r = make rect in
  render_get_viewport rend (addr r);
  r

let render_present =
  foreign ~release_runtime_lock:true "SDL_RenderPresent"
    (renderer @-> returning void)

let render_read_pixels =
  foreign "SDL_RenderReadPixels"
    (renderer @-> ptr rect @-> uint32_t @-> ptr void @-> int @->
     returning zero_to_ok)

let render_read_pixels r rect format pixels pitch =
  let format = match format with None -> Unsigned.UInt32.zero | Some f -> f in
  let pixels = to_voidp (bigarray_start array1 pixels) in
  render_read_pixels r (Rect.opt_addr rect) format pixels pitch

let render_set_clip_rect =
  foreign "SDL_RenderSetClipRect"
    (renderer @-> ptr rect @-> returning zero_to_ok)

let render_set_clip_rect rend r =
  render_set_clip_rect rend (Rect.opt_addr r)

let render_set_integer_scale =
  foreign "SDL_RenderSetIntegerScale"
    (renderer @-> bool @-> returning zero_to_ok)

let render_set_logical_size =
  foreign "SDL_RenderSetLogicalSize"
    (renderer @-> int @-> int @-> returning zero_to_ok)

let render_set_scale =
  foreign "SDL_RenderSetScale"
    (renderer @-> float @-> float @-> returning zero_to_ok)

let render_set_viewport =
  foreign "SDL_RenderSetViewport"
    (renderer @-> ptr rect @-> returning zero_to_ok)

let render_set_viewport rend r =
  render_set_viewport rend (Rect.opt_addr r)

let render_target_supported =
  foreign "SDL_RenderTargetSupported" (renderer @-> returning bool)

let set_render_draw_blend_mode =
  foreign "SDL_SetRenderDrawBlendMode"
    (renderer @-> uint @-> returning zero_to_ok)

let set_render_draw_color =
  foreign "SDL_SetRenderDrawColor"
    (renderer @-> int_as_uint8_t @-> int_as_uint8_t @-> int_as_uint8_t @->
     int_as_uint8_t @-> returning zero_to_ok)

let set_render_target =
  foreign "SDL_SetRenderTarget"
    (renderer @-> texture @-> returning zero_to_ok)

let set_render_target r t =
  let t = match t with None -> null | Some t -> t in
  set_render_target r t

(* Textures *)

module Texture = struct
  type access = int
  let access_static = sdl_textureaccess_static
  let access_streaming = sdl_textureaccess_streaming
  let access_target = sdl_textureaccess_target

  let i = Unsigned.UInt32.of_int
  type modulate = Unsigned.uint32
  let modulate_none = i sdl_texturemodulate_none
  let modulate_color = i sdl_texturemodulate_color
  let modulate_alpha = i sdl_texturemodulate_alpha
end

let create_texture =
  foreign "SDL_CreateTexture"
    (renderer @-> uint32_t @-> int @-> int @-> int @->
     returning (some_to_ok texture_opt))

let create_texture r pf access ~w ~h =
  create_texture r pf access w h

let create_texture_from_surface =
  foreign "SDL_CreateTextureFromSurface"
    (renderer @-> surface @-> returning (some_to_ok texture_opt))

let destroy_texture =
  foreign "SDL_DestroyTexture" (texture @-> returning void)

let get_texture_alpha_mod =
  foreign "SDL_GetTextureAlphaMod"
    (texture @-> ptr uint8_t @-> returning zero_to_ok)

let get_texture_alpha_mod t =
  let alpha = allocate uint8_t Unsigned.UInt8.zero in
  match get_texture_alpha_mod t alpha with
  | Ok () -> Ok (Unsigned.UInt8.to_int (!@ alpha)) | Error _ as e -> e

let get_texture_blend_mode =
  foreign "SDL_GetTextureBlendMode"
    (texture @-> ptr uint @-> returning zero_to_ok)

let get_texture_blend_mode t =
  let m = allocate uint Unsigned.UInt.zero in
  match get_texture_blend_mode t m with
  | Ok () -> Ok (!@ m) | Error _ as e -> e

let get_texture_color_mod =
  foreign "SDL_GetTextureColorMod"
    (texture @-> ptr uint8_t @-> ptr uint8_t @-> ptr uint8_t @->
     returning zero_to_ok)

let get_texture_color_mod t =
  let alloc () = allocate uint8_t Unsigned.UInt8.zero in
  let get v = Unsigned.UInt8.to_int (!@ v) in
  let r, g, b = alloc (), alloc (), alloc () in
  match get_texture_color_mod t r g b with
  | Ok () -> Ok (get r, get g, get b) | Error _ as e -> e

let query_texture =
  foreign "SDL_QueryTexture"
    (texture @-> ptr uint32_t @-> ptr int @-> ptr int @-> ptr int @->
     returning zero_to_ok)

let _texture_height t =
  let h = allocate int 0 in
  let unull = coerce (ptr void) (ptr uint32_t) null in
  let inull = coerce (ptr void) (ptr int) null in
  match query_texture t unull inull inull h with
  | Ok () -> Ok (!@ h) | Error _ as e -> e

let lock_texture =
  foreign "SDL_LockTexture"
    (texture @-> ptr rect @-> ptr (ptr void) @-> ptr int @->
     returning zero_to_ok)

let lock_texture t r kind =
  match (match r with None -> _texture_height t | Some r -> Ok (Rect.h r)) with
  | Error _ as e -> e
  | Ok h ->
      let pitch = allocate int 0 in
      let p = allocate (ptr void) null in
      match lock_texture t (Rect.opt_addr r) p pitch with
      | Error _ as e -> e
      | Ok () ->
          let p = !@ p in
          let pitch = !@ pitch in
          let kind_size = ba_kind_byte_size kind in
          if pitch mod kind_size <> 0
          then invalid_arg (err_bigarray_pitch pitch kind_size)
          else
          let ba_size = (pitch * h) / kind_size in
          let pixels = coerce (ptr void) (access_ptr_typ_of_ba_kind kind) p in
          Ok (bigarray_of_ptr array1 ba_size kind pixels, pitch / kind_size)

let query_texture t =
  let pf = allocate uint32_t Unsigned.UInt32.zero in
  let access = allocate int 0 in
  let w = allocate int 0 in
  let h = allocate int 0 in
  match query_texture t pf access w h with
  | Ok () -> Ok (!@ pf, !@ access, (!@ w, !@ h)) | Error _ as e -> e

let set_texture_alpha_mod =
  foreign "SDL_SetTextureAlphaMod"
    (texture @-> int_as_uint8_t @-> returning zero_to_ok)

let set_texture_blend_mode =
  foreign "SDL_SetTextureBlendMode"
    (texture @-> uint @-> returning zero_to_ok)

let set_texture_color_mod =
  foreign "SDL_SetTextureColorMod"
    (texture @-> int_as_uint8_t @-> int_as_uint8_t @-> int_as_uint8_t @->
     returning zero_to_ok)

let unlock_texture =
  foreign "SDL_UnlockTexture" (texture @-> returning void)

let update_texture =
  foreign "SDL_UpdateTexture"
    (texture @-> ptr rect @-> ptr void @-> int @-> returning zero_to_ok)

let update_texture t rect pixels pitch =
  let pitch = pitch * (ba_kind_byte_size (Bigarray.Array1.kind pixels)) in
  let pixels = to_voidp (bigarray_start array1 pixels) in
  update_texture t (Rect.opt_addr rect) pixels pitch

let update_yuv_texture =
  foreign "SDL_UpdateYUVTexture"
    (texture @-> ptr rect @->
     ptr void @-> int @-> ptr void @-> int @-> ptr void @-> int @->
     returning zero_to_ok)

let update_yuv_texture r rect ~y ypitch ~u upitch ~v vpitch =
  let yp = to_voidp (bigarray_start array1 y) in
  let up = to_voidp (bigarray_start array1 u) in
  let vp = to_voidp (bigarray_start array1 v) in
  update_yuv_texture r (Rect.opt_addr rect) yp ypitch up upitch vp vpitch

(* Video drivers *)

let get_current_video_driver =
  foreign "SDL_GetCurrentVideoDriver" (void @-> returning string_opt)

let get_num_video_drivers =
  foreign "SDL_GetNumVideoDrivers" (void @-> returning nat_to_ok)

let get_video_driver =
  foreign "SDL_GetVideoDriver" (int @-> returning (some_to_ok string_opt))

let video_init =
  foreign "SDL_VideoInit" (string_opt @-> returning zero_to_ok)

let video_quit =
  foreign "SDL_VideoQuit" (void @-> returning void)

(* Displays *)

type driverdata = unit ptr
let driverdata = ptr_opt void

type display_mode =
  { dm_format : Pixel.format_enum;
    dm_w : int;
    dm_h : int;
    dm_refresh_rate : int option;
    dm_driverdata : driverdata option }

type _display_mode
let display_mode : _display_mode structure typ = structure "SDL_DisplayMode"
let dm_format = field display_mode "format" uint32_t
let dm_w = field display_mode "w" int
let dm_h = field display_mode "h" int
let dm_refresh_rate = field display_mode "refresh_rate" int
let dm_driverdata = field display_mode "driverdata" driverdata
let () = seal display_mode

let display_mode_to_c o =
  let c = make display_mode in
  let rate = match o.dm_refresh_rate with None -> 0 | Some r -> r in
  setf c dm_format o.dm_format;
  setf c dm_w o.dm_w;
  setf c dm_h o.dm_h;
  setf c dm_refresh_rate rate;
  setf c dm_driverdata o.dm_driverdata;
  c

let display_mode_of_c c =
  let dm_format = getf c dm_format in
  let dm_w = getf c dm_w in
  let dm_h = getf c dm_h in
  let dm_refresh_rate = match getf c dm_refresh_rate with
  | 0 -> None | r -> Some r
  in
  let dm_driverdata = getf c dm_driverdata in
  { dm_format; dm_w; dm_h; dm_refresh_rate; dm_driverdata }

let get_closest_display_mode =
  foreign "SDL_GetClosestDisplayMode"
    (int @-> ptr display_mode @-> ptr display_mode @->
       returning (ptr_opt void))

let get_closest_display_mode i m =
  let mode = display_mode_to_c m in
  let closest = make display_mode in
  match get_closest_display_mode i (addr mode) (addr closest) with
  | None -> None
  | Some _ -> Some (display_mode_of_c closest)

let get_current_display_mode =
  foreign "SDL_GetCurrentDisplayMode"
    (int @-> ptr display_mode @-> returning zero_to_ok)

let get_current_display_mode i =
  let mode = make display_mode in
  match get_current_display_mode i (addr mode) with
  | Ok () -> Ok (display_mode_of_c mode) | Error _ as e -> e

let get_desktop_display_mode =
  foreign "SDL_GetDesktopDisplayMode"
    (int @-> ptr display_mode @-> returning zero_to_ok)

let get_desktop_display_mode i =
  let mode = make display_mode in
  match get_desktop_display_mode i (addr mode) with
  | Ok () -> Ok (display_mode_of_c mode) | Error _ as e -> e

let get_display_bounds =
  foreign "SDL_GetDisplayBounds"
    (int @-> ptr rect @-> returning zero_to_ok)

let get_display_bounds i =
  let r = make rect in
  match get_display_bounds i (addr r) with
  | Ok () -> Ok r | Error _ as e -> e

let get_display_dpi =
  foreign "SDL_GetDisplayDPI"
    (int @-> ptr float @-> ptr float @-> ptr float @-> returning zero_to_ok)

let get_display_dpi display =
  let diagonal = allocate float 0. in
  let horizontal = allocate float 0. in
  let vertical = allocate float 0. in
  match get_display_dpi display diagonal horizontal vertical with
  | Ok () -> Ok (!@diagonal,!@horizontal,!@vertical)
  | Error _ as err -> err

let get_display_mode =
  foreign "SDL_GetDisplayMode"
    (int @-> int @-> ptr display_mode @-> returning zero_to_ok)

let get_display_mode d i =
  let mode = make display_mode in
  match get_display_mode d i (addr mode) with
  | Ok () -> Ok (display_mode_of_c mode) | Error _ as e -> e

let get_display_usable_bounds =
  foreign "SDL_GetDisplayUsableBounds"
    (int @-> ptr rect @-> returning zero_to_ok)

let get_display_usable_bounds i =
  let r = make rect in
  match get_display_usable_bounds i (addr r) with
  | Ok () -> Ok r | Error _ as e -> e

let get_num_display_modes =
  foreign "SDL_GetNumDisplayModes" (int @-> returning nat_to_ok)

let get_display_name =
  foreign "SDL_GetDisplayName" (int @-> returning (some_to_ok string_opt))

let get_num_video_displays =
  foreign "SDL_GetNumVideoDisplays" (void @-> returning nat_to_ok)

(* Windows *)

module Window = struct
  let pos_undefined = sdl_windowpos_undefined
  let pos_centered = sdl_windowpos_centered

  type flags = Unsigned.uint32
  let i = Unsigned.UInt32.of_int
  let ( + ) = Unsigned.UInt32.logor
  let ( - ) f f' = Unsigned.UInt32.(logand f (lognot f'))
  let test f m = Unsigned.UInt32.(compare (logand f m) zero <> 0)
  let eq f f' = Unsigned.UInt32.(compare f f' = 0)
  let windowed = i 0
  let fullscreen = i sdl_window_fullscreen
  let fullscreen_desktop = i sdl_window_fullscreen_desktop
  let opengl = i sdl_window_opengl
  let shown = i sdl_window_shown
  let hidden = i sdl_window_hidden
  let borderless = i sdl_window_borderless
  let resizable = i sdl_window_resizable
  let minimized = i sdl_window_minimized
  let maximized = i sdl_window_maximized
  let input_grabbed = i sdl_window_input_grabbed
  let input_focus = i sdl_window_input_focus
  let mouse_focus = i sdl_window_mouse_focus
  let foreign = i sdl_window_foreign
  let allow_highdpi = i sdl_window_allow_highdpi
  let mouse_capture = i sdl_window_mouse_capture
  let always_on_top = i sdl_window_always_on_top
  let skip_taskbar = i sdl_window_skip_taskbar
  let utility = i sdl_window_utility
  let popup_menu = i sdl_window_popup_menu
  let vulkan = i sdl_window_vulkan
end

let create_window =
  foreign "SDL_CreateWindow"
    (string @-> int @-> int @-> int @-> int @-> uint32_t @->
     returning (some_to_ok window_opt))

let create_window t ?(x = Window.pos_undefined) ?(y = Window.pos_undefined)
    ~w ~h flags = create_window t x y w h flags

let create_window_and_renderer =
  foreign "SDL_CreateWindowAndRenderer"
    (int @-> int @-> uint32_t @-> ptr window @-> ptr renderer @->
     (returning zero_to_ok))

let create_window_and_renderer ~w ~h flags =
  let win = allocate window null in
  let r = allocate renderer null in
  match create_window_and_renderer w h flags win r with
  | Ok () -> Ok (!@ win, !@ r) | Error _ as e -> e

let destroy_window =
  foreign "SDL_DestroyWindow" (window @-> returning void)

let get_window_brightness =
  foreign "SDL_GetWindowBrightness" (window @-> returning float)

let get_window_borders_size =
  foreign "SDL_GetWindowBordersSize"
    (window @-> ptr int @-> ptr int @-> ptr int @-> ptr int @->
     returning zero_to_ok)

let get_window_borders_size w =
  let top = allocate int 0 in
  let left = allocate int 0 in
  let bottom = allocate int 0 in
  let right = allocate int 0 in
  match get_window_borders_size w top bottom left right with
  | Ok () -> Ok (!@ top, !@ left, !@ bottom, !@ right)
  | Error _ as err -> err

let get_window_display_index =
  foreign "SDL_GetWindowDisplayIndex" (window @-> returning nat_to_ok)

let get_window_display_mode =
  foreign "SDL_GetWindowDisplayMode"
    (window @-> (ptr display_mode) @-> returning int)

let get_window_display_mode w =
  let mode = make display_mode in
  match get_window_display_mode w (addr mode) with
  | 0 -> Ok (display_mode_of_c mode) | err -> error ()

let get_window_flags =
  foreign "SDL_GetWindowFlags" (window @-> returning uint32_t)

let get_window_from_id =
  foreign "SDL_GetWindowFromID"
    (int_as_uint32_t @-> returning (some_to_ok window_opt))

let get_window_gamma_ramp =
  foreign "SDL_GetWindowGammaRamp"
    (window @-> ptr void @-> ptr void @-> ptr void @-> returning zero_to_ok)

let get_window_gamma_ramp w =
  let create_ramp () = ba_create Bigarray.int16_unsigned 256 in
  let r, g, b = create_ramp (), create_ramp (), create_ramp () in
  let ramp_ptr r = to_voidp (bigarray_start array1 r) in
  match get_window_gamma_ramp w (ramp_ptr r) (ramp_ptr g) (ramp_ptr b) with
  | Ok () -> Ok (r, g, b) | Error _ as e -> e

let get_window_grab =
  foreign "SDL_GetWindowGrab" (window @-> returning bool)

let get_grabbed_window =
  foreign "SDL_GetGrabbedWindow" (void @-> returning window)

let get_window_id =
  foreign "SDL_GetWindowID" (window @-> returning int_as_uint32_t)

let get_window_maximum_size =
  foreign "SDL_GetWindowMaximumSize"
    (window @-> (ptr int) @-> (ptr int) @-> returning void)

let get_window_maximum_size win =
  let w = allocate int 0 in
  let h = allocate int 0 in
  get_window_maximum_size win w h;
  !@ w, !@ h

let get_window_minimum_size =
  foreign "SDL_GetWindowMinimumSize"
    (window @-> (ptr int) @-> (ptr int) @-> returning void)

let get_window_minimum_size win =
  let w = allocate int 0 in
  let h = allocate int 0 in
  get_window_minimum_size win w h;
  !@ w, !@ h

let get_window_opacity =
  foreign "SDL_GetWindowOpacity"
    (window @-> (ptr float) @-> returning zero_to_ok)

let get_window_opacity win =
  let x = allocate float 0. in
  match get_window_opacity win x with
  | Ok () -> Ok !@x
  | Error _ as e -> e

let get_window_pixel_format =
  foreign "SDL_GetWindowPixelFormat" (window @-> returning uint32_t)

let get_window_position =
  foreign "SDL_GetWindowPosition"
    (window @-> (ptr int) @-> (ptr int) @-> returning void)

let get_window_position win =
  let x = allocate int 0 in
  let y = allocate int 0 in
  get_window_position win x y;
  !@ x, !@ y

let get_window_size =
  foreign "SDL_GetWindowSize"
    (window @-> (ptr int) @-> (ptr int) @-> returning void)

let get_window_size win =
  let w = allocate int 0 in
  let h = allocate int 0 in
  get_window_size win w h;
  !@ w, !@ h

let get_window_surface =
  foreign "SDL_GetWindowSurface"
    (window @-> returning (some_to_ok surface_opt))

let get_window_title =
  foreign "SDL_GetWindowTitle" (window @-> returning string)

let hide_window =
  foreign "SDL_HideWindow" (window @-> returning void)

let maximize_window =
  foreign "SDL_MaximizeWindow" (window @-> returning void)

let minimize_window =
  foreign "SDL_MinimizeWindow" (window @-> returning void)

let raise_window =
  foreign "SDL_RaiseWindow" (window @-> returning void)

let restore_window =
  foreign "SDL_RestoreWindow" (window @-> returning void)

let set_window_bordered =
  foreign "SDL_SetWindowBordered" (window @-> bool @-> returning void)

let set_window_brightness =
  foreign "SDL_SetWindowBrightness"
    (window @-> float @-> returning zero_to_ok)

let set_window_display_mode =
  foreign "SDL_SetWindowDisplayMode"
    (window @-> (ptr display_mode) @-> returning zero_to_ok)

let set_window_display_mode w m =
  let mode = display_mode_to_c m in
  set_window_display_mode w (addr mode)

let set_window_fullscreen =
  foreign "SDL_SetWindowFullscreen"
    (window @-> uint32_t @-> returning zero_to_ok)

let set_window_gamma_ramp =
  foreign "SDL_SetWindowGammaRamp"
    (window @-> ptr void @-> ptr void @-> ptr void @->
     returning zero_to_ok)

let set_window_gamma_ramp w r g b =
  let ramp_ptr r = to_voidp (bigarray_start array1 r) in
  set_window_gamma_ramp w (ramp_ptr r) (ramp_ptr g) (ramp_ptr b)

let set_window_grab =
  foreign "SDL_SetWindowGrab" (window @-> bool @-> returning void)

let set_window_icon =
  foreign "SDL_SetWindowIcon" (window @-> surface @-> returning void)

let set_window_input_focus =
  foreign "SDL_SetWindowInputFocus" (window @-> returning zero_to_ok)

let set_window_maximum_size =
  foreign "SDL_SetWindowMaximumSize"
    (window @-> int @-> int @-> returning void)

let set_window_maximum_size win ~w ~h =
  set_window_maximum_size win w h

let set_window_minimum_size =
  foreign "SDL_SetWindowMinimumSize"
    (window @-> int @-> int @-> returning void)

let set_window_minimum_size win ~w ~h =
  set_window_minimum_size win w h

let set_window_modal_for =
  foreign "SDL_SetWindowModalFor" ( window @-> window @-> returning zero_to_ok)

let set_window_modal_for ~modal ~parent = set_window_modal_for modal parent

let set_window_opacity =
  foreign "SDL_SetWindowOpacity" ( window @-> float @-> returning zero_to_ok)

let set_window_position =
  foreign "SDL_SetWindowPosition"
    (window @-> int @-> int @-> returning void)

let set_window_position win ~x ~y =
  set_window_position win x y

let set_window_resizable =
  foreign "SDL_SetWindowResizable" (window @-> bool @-> returning void)

let set_window_size =
  foreign "SDL_SetWindowSize" (window @-> int @-> int @-> returning void)

let set_window_size win ~w ~h =
  set_window_size win w h

let set_window_title =
  foreign "SDL_SetWindowTitle" (window @-> string @-> returning void)

let show_window =
  foreign "SDL_ShowWindow" (window @-> returning void)

let update_window_surface =
  foreign "SDL_UpdateWindowSurface" (window @-> returning zero_to_ok)

let update_window_surface_rects =
  foreign "SDL_UpdateWindowSurfaceRects"
    (window @-> ptr void @-> int @-> returning zero_to_ok)

let update_window_surface_rects_ba w rs =
  let len = Bigarray.Array1.dim rs in
  if len mod 4 <> 0 then invalid_arg (err_length_mul len 4) else
  let count = len / 4 in
  let rs = to_voidp (bigarray_start array1 rs) in
  update_window_surface_rects w rs count

let update_window_surface_rects w rs =
  let a = CArray.of_list rect rs in
  let rs = to_voidp (CArray.start a) in
  update_window_surface_rects w rs (CArray.length a)

(* OpenGL contexts *)

type gl_context = unit ptr
let gl_context : unit ptr typ = ptr void
let gl_context_opt : unit ptr option typ = ptr_opt void

let unsafe_gl_context_of_ptr addr : gl_context =
  ptr_of_raw_address addr
let unsafe_ptr_of_gl_context gl_context =
  raw_address_of_ptr (to_voidp gl_context)

module Gl = struct
  type context_flags = int
  let context_debug_flag = sdl_gl_context_debug_flag
  let context_forward_compatible_flag = sdl_gl_context_forward_compatible_flag
  let context_robust_access_flag = sdl_gl_context_robust_access_flag
  let context_reset_isolation_flag = sdl_gl_context_reset_isolation_flag

  type profile = int
  let context_profile_core = sdl_gl_context_profile_core
  let context_profile_compatibility = sdl_gl_context_profile_compatibility
  let context_profile_es = sdl_gl_context_profile_es

  type attr = int
  let red_size = sdl_gl_red_size
  let green_size = sdl_gl_green_size
  let blue_size = sdl_gl_blue_size
  let alpha_size = sdl_gl_alpha_size
  let buffer_size = sdl_gl_buffer_size
  let doublebuffer = sdl_gl_doublebuffer
  let depth_size = sdl_gl_depth_size
  let stencil_size = sdl_gl_stencil_size
  let accum_red_size = sdl_gl_accum_red_size
  let accum_green_size = sdl_gl_accum_green_size
  let accum_blue_size = sdl_gl_accum_blue_size
  let accum_alpha_size = sdl_gl_accum_alpha_size
  let stereo = sdl_gl_stereo
  let multisamplebuffers = sdl_gl_multisamplebuffers
  let multisamplesamples = sdl_gl_multisamplesamples
  let accelerated_visual = sdl_gl_accelerated_visual
  let context_major_version = sdl_gl_context_major_version
  let context_minor_version = sdl_gl_context_minor_version
  let context_egl = sdl_gl_context_egl
  let context_flags = sdl_gl_context_flags
  let context_profile_mask = sdl_gl_context_profile_mask
  let context_release_behavior = sdl_gl_context_release_behavior
  let share_with_current_context = sdl_gl_share_with_current_context
  let framebuffer_srgb_capable = sdl_gl_framebuffer_srgb_capable
end

let gl_bind_texture =
  foreign "SDL_GL_BindTexture"
    (texture @-> ptr float @-> ptr float @-> returning zero_to_ok)

let gl_bind_texture t =
  let w = allocate float 0. in
  let h = allocate float 0. in
  match gl_bind_texture t w h with
  | Ok () -> Ok (!@ w, !@ h) | Error _ as e -> e

let gl_create_context =
  foreign "SDL_GL_CreateContext"
    (window @-> returning (some_to_ok gl_context_opt))

let gl_delete_context =
  foreign "SDL_GL_DeleteContext" (gl_context @-> returning void)

let gl_extension_supported =
  foreign "SDL_GL_ExtensionSupported" (string @-> returning bool)

let gl_get_attribute =
  foreign "SDL_GL_GetAttribute" (int @-> (ptr int) @-> returning int)

let gl_get_attribute att =
  let value = allocate int 0 in
  match gl_get_attribute att value with
  | 0 -> Ok (!@ value) | err -> error ()

let gl_get_current_context =
  foreign "SDL_GL_GetCurrentContext"
    (void @-> returning (some_to_ok gl_context_opt))

let gl_get_drawable_size =
  foreign "SDL_GL_GetDrawableSize"
    (window @-> ptr int @-> ptr int @-> returning void)

let gl_get_drawable_size win =
  let w = allocate int 0 in
  let h = allocate int 0 in
  gl_get_drawable_size win w h;
  (!@ w, !@ h)

let int_to_ok =
  let read n = Ok n in
  view ~read ~write:write_never int

let gl_get_swap_interval =
  foreign "SDL_GL_GetSwapInterval" (void @-> returning int_to_ok)

let gl_make_current =
  foreign "SDL_GL_MakeCurrent"
    (window @-> gl_context @-> returning zero_to_ok)

let gl_reset_attributes =
  foreign "SDL_GL_ResetAttributes" ~stub (void @-> returning void)

let gl_set_attribute =
  foreign "SDL_GL_SetAttribute" (int @-> int @-> returning zero_to_ok)

let gl_set_swap_interval =
  foreign "SDL_GL_SetSwapInterval" (int @-> returning zero_to_ok)

let gl_swap_window =
  foreign "SDL_GL_SwapWindow" (window @-> returning void)

let gl_unbind_texture =
  foreign "SDL_GL_UnbindTexture" (texture @-> returning zero_to_ok)

(* Vulkan *)

module Vulkan = struct

  type instance = unit ptr
  let instance = ptr void
  let unsafe_ptr_of_instance = raw_address_of_ptr
  let unsafe_instance_of_ptr x = ptr_of_raw_address x

  type surface = uint64
  let surface = int64_t
  let unsafe_uint64_of_surface x = x
  let unsafe_surface_of_uint64 x = x

  let load_library =
    foreign "SDL_Vulkan_LoadLibrary" (string_opt @-> returning zero_to_ok)

  let unload_library =
    foreign "SDL_Vulkan_UnloadLibrary" (void @-> returning void)

  let get_instance_extensions =
    foreign "SDL_Vulkan_GetInstanceExtensions"
      (window @-> ptr int @-> ptr string @-> returning bool)

  let get_instance_extensions window =
    let n = allocate int 0 in
    match get_instance_extensions window n
            (Ctypes.coerce (ptr void) (ptr string) null) with
    | false -> None
    | true ->
        let exts = allocate_n string ~count:(!@n) in
        match get_instance_extensions window n exts with
        | false -> None
        | true -> Some CArray.(to_list @@ from_ptr exts (!@n))

  let create_surface =
    foreign "SDL_Vulkan_CreateSurface"
      (window @-> instance @-> ptr surface @-> returning bool)

  let create_surface window instance =
    let s = allocate_n surface ~count:1 in
    if create_surface window instance s then
      Some !@s
    else
    None

  let get_drawable_size =
    foreign "SDL_Vulkan_GetDrawableSize"
      (window @-> ptr int @-> ptr int @-> returning void)

  let get_drawable_size window =
    let w = allocate int 0 in
    let h = allocate int 0 in
    get_drawable_size window w h;
    !@w, !@h
end

(* Screen saver *)

let disable_screen_saver =
  foreign "SDL_DisableScreenSaver" (void @-> returning void)

let enable_screen_saver =
  foreign "SDL_EnableScreenSaver" (void @-> returning void)

let is_screen_saver_enabled =
  foreign "SDL_IsScreenSaverEnabled" (void @-> returning bool)

(* Message boxes *)

module Message_box = struct
  let i = Unsigned.UInt32.of_int

  type button_flags = Unsigned.uint32
  let button_no_default = i 0
  let button_returnkey_default = i sdl_messagebox_button_returnkey_default
  let button_escapekey_default = i sdl_messagebox_button_escapekey_default

  type button_data =
    { button_flags : button_flags;
      button_id : int;
      button_text : string }

  let button_data = structure "SDL_MessageBoxButtonData"
  let button_flags = field button_data "flags" uint32_t
  let button_buttonid = field button_data "buttonid" int
  let button_text = field button_data "text" string
  let () = seal button_data

  type flags = Unsigned.uint32
  let error = i sdl_messagebox_error
  let warning = i sdl_messagebox_warning
  let information = i sdl_messagebox_information

  type color = int * int * int
  let color = structure "SDL_MessageBoxColor"
  let color_r = field color "r" uint8_t
  let color_g = field color "g" uint8_t
  let color_b = field color "b" uint8_t
  let () = seal color

  let color_background = sdl_messagebox_color_background
  let color_text = sdl_messagebox_color_text
  let color_button_border = sdl_messagebox_color_button_border
  let color_button_background = sdl_messagebox_color_button_background
  let color_button_selected = sdl_messagebox_color_button_selected
  let color_button_max = sdl_messagebox_color_max

  type color_scheme =
    { color_background : color;
      color_text : color;
      color_button_border : color;
      color_button_background : color;
      color_button_selected : color; }

  let color_scheme = structure "SDL_MessageBoxColorScheme"
  let colors = field color_scheme "colors" (array color_button_max color)
  let () = seal color_scheme

  type data =
    { flags : flags;
      window : window option;
      title : string;
      message : string;
      buttons : button_data list;
      color_scheme : color_scheme option }

  let data = structure "SDL_MessageBoxData"
  let d_flags = field data "flags" uint32_t
  let d_window = field data "window" window
  let d_title = field data "title" string
  let d_message = field data "message" string
  let d_numbuttons = field data "numbuttons" int
  let d_buttons = field data "buttons" (ptr button_data)
  let d_color_scheme = field data "colorScheme" (ptr color_scheme)
  let () = seal data

  let buttons_to_c bl =
    let button_data_to_c b =
      let bt = make button_data in
      setf bt button_flags b.button_flags;
      setf bt button_buttonid b.button_id;
      setf bt button_text b.button_text;
      bt
    in
    CArray.start (CArray.of_list button_data (List.map button_data_to_c bl))

  let color_scheme_to_c s =
    let st = make color_scheme in
    let colors = getf st colors in
    let set i (rv, gv, bv) =
      let ct = CArray.get colors i in
      setf ct color_r (Unsigned.UInt8.of_int rv);
      setf ct color_g (Unsigned.UInt8.of_int gv);
      setf ct color_b (Unsigned.UInt8.of_int bv);
    in
    set color_background s.color_background;
    set color_text s.color_text;
    set color_button_border s.color_button_border;
    set color_button_background s.color_button_background;
    set color_button_selected s.color_button_selected;
    st

  let data_to_c d =
    let dt = make data in
    setf dt d_flags d.flags;
    setf dt d_window (match d.window with None -> null | Some w -> w);
    setf dt d_title d.title;
    setf dt d_message d.message;
    setf dt d_numbuttons (List.length d.buttons);
    setf dt d_buttons (buttons_to_c d.buttons);
    setf dt d_color_scheme
      begin match d.color_scheme with
      | None -> coerce (ptr void) (ptr color_scheme) null
      | Some s -> addr (color_scheme_to_c s)
      end;
    dt
end

let show_message_box =
  foreign "SDL_ShowMessageBox"
    (ptr Message_box.data @-> ptr int @-> returning zero_to_ok)

let show_message_box d =
  let d = addr (Message_box.data_to_c d) in
  let ret = allocate int 0 in
  match show_message_box d ret with
  | Ok () -> Ok (!@ ret) | Error _ as e -> e

let show_simple_message_box =
  foreign "SDL_ShowSimpleMessageBox"
    (uint32_t @-> string @-> string @-> window_opt @-> returning zero_to_ok)

let show_simple_message_box t ~title msg w =
  show_simple_message_box t title msg w

(* Clipboard *)

let get_clipboard_text =
  foreign "SDL_GetClipboardText" (void @-> returning (ptr char))

let get_clipboard_text () =
  let p = get_clipboard_text () in
  if (to_voidp p) = null then error () else
  let b = Buffer.create 255 in
  let ptr = ref p in
  while (!@ !ptr) <> '\000' do
    Buffer.add_char b (!@ !ptr);
    ptr := !ptr +@ 1;
  done;
  sdl_free (to_voidp p);
  Ok (Buffer.contents b)

let has_clipboard_text =
  foreign "SDL_HasClipboardText" (void @-> returning bool)

let set_clipboard_text =
  foreign "SDL_SetClipboardText" (string @-> returning zero_to_ok)

(* Input *)

type button_state = uint8
let pressed = sdl_pressed
let released = sdl_released

type toggle_state = uint8
let disable = sdl_disable
let enable = sdl_enable

(* Keyboard *)

type scancode = int
let scancode = int

module Scancode = struct
  let num_scancodes = sdl_num_scancodes
  let unknown = sdl_scancode_unknown
  let a = sdl_scancode_a
  let b = sdl_scancode_b
  let c = sdl_scancode_c
  let d = sdl_scancode_d
  let e = sdl_scancode_e
  let f = sdl_scancode_f
  let g = sdl_scancode_g
  let h = sdl_scancode_h
  let i = sdl_scancode_i
  let j = sdl_scancode_j
  let k = sdl_scancode_k
  let l = sdl_scancode_l
  let m = sdl_scancode_m
  let n = sdl_scancode_n
  let o = sdl_scancode_o
  let p = sdl_scancode_p
  let q = sdl_scancode_q
  let r = sdl_scancode_r
  let s = sdl_scancode_s
  let t = sdl_scancode_t
  let u = sdl_scancode_u
  let v = sdl_scancode_v
  let w = sdl_scancode_w
  let x = sdl_scancode_x
  let y = sdl_scancode_y
  let z = sdl_scancode_z
  let k1 = sdl_scancode_1
  let k2 = sdl_scancode_2
  let k3 = sdl_scancode_3
  let k4 = sdl_scancode_4
  let k5 = sdl_scancode_5
  let k6 = sdl_scancode_6
  let k7 = sdl_scancode_7
  let k8 = sdl_scancode_8
  let k9 = sdl_scancode_9
  let k0 = sdl_scancode_0
  let return = sdl_scancode_return
  let escape = sdl_scancode_escape
  let backspace = sdl_scancode_backspace
  let tab = sdl_scancode_tab
  let space = sdl_scancode_space
  let minus = sdl_scancode_minus
  let equals = sdl_scancode_equals
  let leftbracket = sdl_scancode_leftbracket
  let rightbracket = sdl_scancode_rightbracket
  let backslash = sdl_scancode_backslash
  let nonushash = sdl_scancode_nonushash
  let semicolon = sdl_scancode_semicolon
  let apostrophe = sdl_scancode_apostrophe
  let grave = sdl_scancode_grave
  let comma = sdl_scancode_comma
  let period = sdl_scancode_period
  let slash = sdl_scancode_slash
  let capslock = sdl_scancode_capslock
  let f1 = sdl_scancode_f1
  let f2 = sdl_scancode_f2
  let f3 = sdl_scancode_f3
  let f4 = sdl_scancode_f4
  let f5 = sdl_scancode_f5
  let f6 = sdl_scancode_f6
  let f7 = sdl_scancode_f7
  let f8 = sdl_scancode_f8
  let f9 = sdl_scancode_f9
  let f10 = sdl_scancode_f10
  let f11 = sdl_scancode_f11
  let f12 = sdl_scancode_f12
  let printscreen = sdl_scancode_printscreen
  let scrolllock = sdl_scancode_scrolllock
  let pause = sdl_scancode_pause
  let insert = sdl_scancode_insert
  let home = sdl_scancode_home
  let pageup = sdl_scancode_pageup
  let delete = sdl_scancode_delete
  let kend = sdl_scancode_end
  let pagedown = sdl_scancode_pagedown
  let right = sdl_scancode_right
  let left = sdl_scancode_left
  let down = sdl_scancode_down
  let up = sdl_scancode_up
  let numlockclear = sdl_scancode_numlockclear
  let kp_divide = sdl_scancode_kp_divide
  let kp_multiply = sdl_scancode_kp_multiply
  let kp_minus = sdl_scancode_kp_minus
  let kp_plus = sdl_scancode_kp_plus
  let kp_enter = sdl_scancode_kp_enter
  let kp_1 = sdl_scancode_kp_1
  let kp_2 = sdl_scancode_kp_2
  let kp_3 = sdl_scancode_kp_3
  let kp_4 = sdl_scancode_kp_4
  let kp_5 = sdl_scancode_kp_5
  let kp_6 = sdl_scancode_kp_6
  let kp_7 = sdl_scancode_kp_7
  let kp_8 = sdl_scancode_kp_8
  let kp_9 = sdl_scancode_kp_9
  let kp_0 = sdl_scancode_kp_0
  let kp_period = sdl_scancode_kp_period
  let nonusbackslash = sdl_scancode_nonusbackslash
  let application = sdl_scancode_application
  let kp_equals = sdl_scancode_kp_equals
  let f13 = sdl_scancode_f13
  let f14 = sdl_scancode_f14
  let f15 = sdl_scancode_f15
  let f16 = sdl_scancode_f16
  let f17 = sdl_scancode_f17
  let f18 = sdl_scancode_f18
  let f19 = sdl_scancode_f19
  let f20 = sdl_scancode_f20
  let f21 = sdl_scancode_f21
  let f22 = sdl_scancode_f22
  let f23 = sdl_scancode_f23
  let f24 = sdl_scancode_f24
  let execute = sdl_scancode_execute
  let help = sdl_scancode_help
  let menu = sdl_scancode_menu
  let select = sdl_scancode_select
  let stop = sdl_scancode_stop
  let again = sdl_scancode_again
  let undo = sdl_scancode_undo
  let cut = sdl_scancode_cut
  let copy = sdl_scancode_copy
  let paste = sdl_scancode_paste
  let find = sdl_scancode_find
  let mute = sdl_scancode_mute
  let volumeup = sdl_scancode_volumeup
  let volumedown = sdl_scancode_volumedown
  let kp_comma = sdl_scancode_kp_comma
  let kp_equalsas400 = sdl_scancode_kp_equalsas400
  let international1 = sdl_scancode_international1
  let international2 = sdl_scancode_international2
  let international3 = sdl_scancode_international3
  let international4 = sdl_scancode_international4
  let international5 = sdl_scancode_international5
  let international6 = sdl_scancode_international6
  let international7 = sdl_scancode_international7
  let international8 = sdl_scancode_international8
  let international9 = sdl_scancode_international9
  let lang1 = sdl_scancode_lang1
  let lang2 = sdl_scancode_lang2
  let lang3 = sdl_scancode_lang3
  let lang4 = sdl_scancode_lang4
  let lang5 = sdl_scancode_lang5
  let lang6 = sdl_scancode_lang6
  let lang7 = sdl_scancode_lang7
  let lang8 = sdl_scancode_lang8
  let lang9 = sdl_scancode_lang9
  let alterase = sdl_scancode_alterase
  let sysreq = sdl_scancode_sysreq
  let cancel = sdl_scancode_cancel
  let clear = sdl_scancode_clear
  let prior = sdl_scancode_prior
  let return2 = sdl_scancode_return2
  let separator = sdl_scancode_separator
  let out = sdl_scancode_out
  let oper = sdl_scancode_oper
  let clearagain = sdl_scancode_clearagain
  let crsel = sdl_scancode_crsel
  let exsel = sdl_scancode_exsel
  let kp_00 = sdl_scancode_kp_00
  let kp_000 = sdl_scancode_kp_000
  let thousandsseparator = sdl_scancode_thousandsseparator
  let decimalseparator = sdl_scancode_decimalseparator
  let currencyunit = sdl_scancode_currencyunit
  let currencysubunit = sdl_scancode_currencysubunit
  let kp_leftparen = sdl_scancode_kp_leftparen
  let kp_rightparen = sdl_scancode_kp_rightparen
  let kp_leftbrace = sdl_scancode_kp_leftbrace
  let kp_rightbrace = sdl_scancode_kp_rightbrace
  let kp_tab = sdl_scancode_kp_tab
  let kp_backspace = sdl_scancode_kp_backspace
  let kp_a = sdl_scancode_kp_a
  let kp_b = sdl_scancode_kp_b
  let kp_c = sdl_scancode_kp_c
  let kp_d = sdl_scancode_kp_d
  let kp_e = sdl_scancode_kp_e
  let kp_f = sdl_scancode_kp_f
  let kp_xor = sdl_scancode_kp_xor
  let kp_power = sdl_scancode_kp_power
  let kp_percent = sdl_scancode_kp_percent
  let kp_less = sdl_scancode_kp_less
  let kp_greater = sdl_scancode_kp_greater
  let kp_ampersand = sdl_scancode_kp_ampersand
  let kp_dblampersand = sdl_scancode_kp_dblampersand
  let kp_verticalbar = sdl_scancode_kp_verticalbar
  let kp_dblverticalbar = sdl_scancode_kp_dblverticalbar
  let kp_colon = sdl_scancode_kp_colon
  let kp_hash = sdl_scancode_kp_hash
  let kp_space = sdl_scancode_kp_space
  let kp_at = sdl_scancode_kp_at
  let kp_exclam = sdl_scancode_kp_exclam
  let kp_memstore = sdl_scancode_kp_memstore
  let kp_memrecall = sdl_scancode_kp_memrecall
  let kp_memclear = sdl_scancode_kp_memclear
  let kp_memadd = sdl_scancode_kp_memadd
  let kp_memsubtract = sdl_scancode_kp_memsubtract
  let kp_memmultiply = sdl_scancode_kp_memmultiply
  let kp_memdivide = sdl_scancode_kp_memdivide
  let kp_plusminus = sdl_scancode_kp_plusminus
  let kp_clear = sdl_scancode_kp_clear
  let kp_clearentry = sdl_scancode_kp_clearentry
  let kp_binary = sdl_scancode_kp_binary
  let kp_octal = sdl_scancode_kp_octal
  let kp_decimal = sdl_scancode_kp_decimal
  let kp_hexadecimal = sdl_scancode_kp_hexadecimal
  let lctrl = sdl_scancode_lctrl
  let lshift = sdl_scancode_lshift
  let lalt = sdl_scancode_lalt
  let lgui = sdl_scancode_lgui
  let rctrl = sdl_scancode_rctrl
  let rshift = sdl_scancode_rshift
  let ralt = sdl_scancode_ralt
  let rgui = sdl_scancode_rgui
  let mode = sdl_scancode_mode
  let audionext = sdl_scancode_audionext
  let audioprev = sdl_scancode_audioprev
  let audiostop = sdl_scancode_audiostop
  let audioplay = sdl_scancode_audioplay
  let audiomute = sdl_scancode_audiomute
  let mediaselect = sdl_scancode_mediaselect
  let www = sdl_scancode_www
  let mail = sdl_scancode_mail
  let calculator = sdl_scancode_calculator
  let computer = sdl_scancode_computer
  let ac_search = sdl_scancode_ac_search
  let ac_home = sdl_scancode_ac_home
  let ac_back = sdl_scancode_ac_back
  let ac_forward = sdl_scancode_ac_forward
  let ac_stop = sdl_scancode_ac_stop
  let ac_refresh = sdl_scancode_ac_refresh
  let ac_bookmarks = sdl_scancode_ac_bookmarks
  let brightnessdown = sdl_scancode_brightnessdown
  let brightnessup = sdl_scancode_brightnessup
  let displayswitch = sdl_scancode_displayswitch
  let kbdillumtoggle = sdl_scancode_kbdillumtoggle
  let kbdillumdown = sdl_scancode_kbdillumdown
  let kbdillumup = sdl_scancode_kbdillumup
  let eject = sdl_scancode_eject
  let sleep = sdl_scancode_sleep
  let app1 = sdl_scancode_app1
  let app2 = sdl_scancode_app2

  let enum_of_scancode = [|
    `Unknown; `Unknown; `Unknown; `Unknown; `A; `B; `C; `D; `E; `F;
    `G; `H; `I; `J; `K; `L; `M; `N; `O; `P; `Q; `R; `S; `T; `U; `V;
    `W; `X; `Y; `Z; `K1; `K2; `K3; `K4; `K5; `K6; `K7; `K8; `K9; `K0;
    `Return; `Escape; `Backspace; `Tab; `Space; `Minus; `Equals;
    `Leftbracket; `Rightbracket; `Backslash; `Nonushash; `Semicolon;
    `Apostrophe; `Grave; `Comma; `Period; `Slash; `Capslock; `F1; `F2;
    `F3; `F4; `F5; `F6; `F7; `F8; `F9; `F10; `F11; `F12; `Printscreen;
    `Scrolllock; `Pause; `Insert; `Home; `Pageup; `Delete; `End;
    `Pagedown; `Right; `Left; `Down; `Up; `Numlockclear; `Kp_divide;
    `Kp_multiply; `Kp_minus; `Kp_plus; `Kp_enter; `Kp_1; `Kp_2; `Kp_3;
    `Kp_4; `Kp_5; `Kp_6; `Kp_7; `Kp_8; `Kp_9; `Kp_0; `Kp_period;
    `Nonusbackslash; `Application; `Power; `Kp_equals; `F13; `F14;
    `F15; `F16; `F17; `F18; `F19; `F20; `F21; `F22; `F23; `F24;
    `Execute; `Help; `Menu; `Select; `Stop; `Again; `Undo; `Cut;
    `Copy; `Paste; `Find; `Mute; `Volumeup; `Volumedown; `Unknown;
    `Unknown; `Unknown; `Kp_comma; `Kp_equalsas400; `International1;
    `International2; `International3; `International4;
    `International5; `International6; `International7;
    `International8; `International9; `Lang1; `Lang2; `Lang3; `Lang4;
    `Lang5; `Lang6; `Lang7; `Lang8; `Lang9; `Alterase; `Sysreq;
    `Cancel; `Clear; `Prior; `Return2; `Separator; `Out; `Oper;
    `Clearagain; `Crsel; `Exsel; `Unknown; `Unknown; `Unknown;
    `Unknown; `Unknown; `Unknown; `Unknown; `Unknown; `Unknown;
    `Unknown; `Unknown; `Kp_00; `Kp_000; `Thousandsseparator;
    `Decimalseparator; `Currencyunit; `Currencysubunit; `Kp_leftparen;
    `Kp_rightparen; `Kp_leftbrace; `Kp_rightbrace; `Kp_tab;
    `Kp_backspace; `Kp_a; `Kp_b; `Kp_c; `Kp_d; `Kp_e; `Kp_f; `Kp_xor;
    `Kp_power; `Kp_percent; `Kp_less; `Kp_greater; `Kp_ampersand;
    `Kp_dblampersand; `Kp_verticalbar; `Kp_dblverticalbar; `Kp_colon;
    `Kp_hash; `Kp_space; `Kp_at; `Kp_exclam; `Kp_memstore;
    `Kp_memrecall; `Kp_memclear; `Kp_memadd; `Kp_memsubtract;
    `Kp_memmultiply; `Kp_memdivide; `Kp_plusminus; `Kp_clear;
    `Kp_clearentry; `Kp_binary; `Kp_octal; `Kp_decimal;
    `Kp_hexadecimal; `Unknown; `Unknown; `Lctrl; `Lshift; `Lalt;
    `Lgui; `Rctrl; `Rshift; `Ralt; `Rgui; `Unknown; `Unknown;
    `Unknown; `Unknown; `Unknown; `Unknown; `Unknown; `Unknown;
    `Unknown; `Unknown; `Unknown; `Unknown; `Unknown; `Unknown;
    `Unknown; `Unknown; `Unknown; `Unknown; `Unknown; `Unknown;
    `Unknown; `Unknown; `Unknown; `Unknown; `Unknown; `Mode;
    `Audionext; `Audioprev; `Audiostop; `Audioplay; `Audiomute;
    `Mediaselect; `Www; `Mail; `Calculator; `Computer; `Ac_search;
    `Ac_home; `Ac_back; `Ac_forward; `Ac_stop; `Ac_refresh;
    `Ac_bookmarks; `Brightnessdown; `Brightnessup; `Displayswitch;
    `Kbdillumtoggle; `Kbdillumdown; `Kbdillumup; `Eject; `Sleep;
    `App1; `App2; |]

  let enum s =
    if 0 <= s && s <= app2 then unsafe_get enum_of_scancode s else
    `Unknown
end

type keycode = int
let keycode = int

module K = struct
  let scancode_mask = sdlk_scancode_mask
  let unknown = sdlk_unknown
  let return = sdlk_return
  let escape = sdlk_escape
  let backspace = sdlk_backspace
  let tab = sdlk_tab
  let space = sdlk_space
  let exclaim = sdlk_exclaim
  let quotedbl = sdlk_quotedbl
  let hash = sdlk_hash
  let percent = sdlk_percent
  let dollar = sdlk_dollar
  let ampersand = sdlk_ampersand
  let quote = sdlk_quote
  let leftparen = sdlk_leftparen
  let rightparen = sdlk_rightparen
  let asterisk = sdlk_asterisk
  let plus = sdlk_plus
  let comma = sdlk_comma
  let minus = sdlk_minus
  let period = sdlk_period
  let slash = sdlk_slash
  let k0 = sdlk_0
  let k1 = sdlk_1
  let k2 = sdlk_2
  let k3 = sdlk_3
  let k4 = sdlk_4
  let k5 = sdlk_5
  let k6 = sdlk_6
  let k7 = sdlk_7
  let k8 = sdlk_8
  let k9 = sdlk_9
  let colon = sdlk_colon
  let semicolon = sdlk_semicolon
  let less = sdlk_less
  let equals = sdlk_equals
  let greater = sdlk_greater
  let question = sdlk_question
  let at = sdlk_at
  let leftbracket = sdlk_leftbracket
  let backslash = sdlk_backslash
  let rightbracket = sdlk_rightbracket
  let caret = sdlk_caret
  let underscore = sdlk_underscore
  let backquote = sdlk_backquote
  let a = sdlk_a
  let b = sdlk_b
  let c = sdlk_c
  let d = sdlk_d
  let e = sdlk_e
  let f = sdlk_f
  let g = sdlk_g
  let h = sdlk_h
  let i = sdlk_i
  let j = sdlk_j
  let k = sdlk_k
  let l = sdlk_l
  let m = sdlk_m
  let n = sdlk_n
  let o = sdlk_o
  let p = sdlk_p
  let q = sdlk_q
  let r = sdlk_r
  let s = sdlk_s
  let t = sdlk_t
  let u = sdlk_u
  let v = sdlk_v
  let w = sdlk_w
  let x = sdlk_x
  let y = sdlk_y
  let z = sdlk_z
  let capslock = sdlk_capslock
  let f1 = sdlk_f1
  let f2 = sdlk_f2
  let f3 = sdlk_f3
  let f4 = sdlk_f4
  let f5 = sdlk_f5
  let f6 = sdlk_f6
  let f7 = sdlk_f7
  let f8 = sdlk_f8
  let f9 = sdlk_f9
  let f10 = sdlk_f10
  let f11 = sdlk_f11
  let f12 = sdlk_f12
  let printscreen = sdlk_printscreen
  let scrolllock = sdlk_scrolllock
  let pause = sdlk_pause
  let insert = sdlk_insert
  let home = sdlk_home
  let pageup = sdlk_pageup
  let delete = sdlk_delete
  let kend = sdlk_end
  let pagedown = sdlk_pagedown
  let right = sdlk_right
  let left = sdlk_left
  let down = sdlk_down
  let up = sdlk_up
  let numlockclear = sdlk_numlockclear
  let kp_divide = sdlk_kp_divide
  let kp_multiply = sdlk_kp_multiply
  let kp_minus = sdlk_kp_minus
  let kp_plus = sdlk_kp_plus
  let kp_enter = sdlk_kp_enter
  let kp_1 = sdlk_kp_1
  let kp_2 = sdlk_kp_2
  let kp_3 = sdlk_kp_3
  let kp_4 = sdlk_kp_4
  let kp_5 = sdlk_kp_5
  let kp_6 = sdlk_kp_6
  let kp_7 = sdlk_kp_7
  let kp_8 = sdlk_kp_8
  let kp_9 = sdlk_kp_9
  let kp_0 = sdlk_kp_0
  let kp_period = sdlk_kp_period
  let application = sdlk_application
  let power = sdlk_power
  let kp_equals = sdlk_kp_equals
  let f13 = sdlk_f13
  let f14 = sdlk_f14
  let f15 = sdlk_f15
  let f16 = sdlk_f16
  let f17 = sdlk_f17
  let f18 = sdlk_f18
  let f19 = sdlk_f19
  let f20 = sdlk_f20
  let f21 = sdlk_f21
  let f22 = sdlk_f22
  let f23 = sdlk_f23
  let f24 = sdlk_f24
  let execute = sdlk_execute
  let help = sdlk_help
  let menu = sdlk_menu
  let select = sdlk_select
  let stop = sdlk_stop
  let again = sdlk_again
  let undo = sdlk_undo
  let cut = sdlk_cut
  let copy = sdlk_copy
  let paste = sdlk_paste
  let find = sdlk_find
  let mute = sdlk_mute
  let volumeup = sdlk_volumeup
  let volumedown = sdlk_volumedown
  let kp_comma = sdlk_kp_comma
  let kp_equalsas400 = sdlk_kp_equalsas400
  let alterase = sdlk_alterase
  let sysreq = sdlk_sysreq
  let cancel = sdlk_cancel
  let clear = sdlk_clear
  let prior = sdlk_prior
  let return2 = sdlk_return2
  let separator = sdlk_separator
  let out = sdlk_out
  let oper = sdlk_oper
  let clearagain = sdlk_clearagain
  let crsel = sdlk_crsel
  let exsel = sdlk_exsel
  let kp_00 = sdlk_kp_00
  let kp_000 = sdlk_kp_000
  let thousandsseparator = sdlk_thousandsseparator
  let decimalseparator = sdlk_decimalseparator
  let currencyunit = sdlk_currencyunit
  let currencysubunit = sdlk_currencysubunit
  let kp_leftparen = sdlk_kp_leftparen
  let kp_rightparen = sdlk_kp_rightparen
  let kp_leftbrace = sdlk_kp_leftbrace
  let kp_rightbrace = sdlk_kp_rightbrace
  let kp_tab = sdlk_kp_tab
  let kp_backspace = sdlk_kp_backspace
  let kp_a = sdlk_kp_a
  let kp_b = sdlk_kp_b
  let kp_c = sdlk_kp_c
  let kp_d = sdlk_kp_d
  let kp_e = sdlk_kp_e
  let kp_f = sdlk_kp_f
  let kp_xor = sdlk_kp_xor
  let kp_power = sdlk_kp_power
  let kp_percent = sdlk_kp_percent
  let kp_less = sdlk_kp_less
  let kp_greater = sdlk_kp_greater
  let kp_ampersand = sdlk_kp_ampersand
  let kp_dblampersand = sdlk_kp_dblampersand
  let kp_verticalbar = sdlk_kp_verticalbar
  let kp_dblverticalbar = sdlk_kp_dblverticalbar
  let kp_colon = sdlk_kp_colon
  let kp_hash = sdlk_kp_hash
  let kp_space = sdlk_kp_space
  let kp_at = sdlk_kp_at
  let kp_exclam = sdlk_kp_exclam
  let kp_memstore = sdlk_kp_memstore
  let kp_memrecall = sdlk_kp_memrecall
  let kp_memclear = sdlk_kp_memclear
  let kp_memadd = sdlk_kp_memadd
  let kp_memsubtract = sdlk_kp_memsubtract
  let kp_memmultiply = sdlk_kp_memmultiply
  let kp_memdivide = sdlk_kp_memdivide
  let kp_plusminus = sdlk_kp_plusminus
  let kp_clear = sdlk_kp_clear
  let kp_clearentry = sdlk_kp_clearentry
  let kp_binary = sdlk_kp_binary
  let kp_octal = sdlk_kp_octal
  let kp_decimal = sdlk_kp_decimal
  let kp_hexadecimal = sdlk_kp_hexadecimal
  let lctrl = sdlk_lctrl
  let lshift = sdlk_lshift
  let lalt = sdlk_lalt
  let lgui = sdlk_lgui
  let rctrl = sdlk_rctrl
  let rshift = sdlk_rshift
  let ralt = sdlk_ralt
  let rgui = sdlk_rgui
  let mode = sdlk_mode
  let audionext = sdlk_audionext
  let audioprev = sdlk_audioprev
  let audiostop = sdlk_audiostop
  let audioplay = sdlk_audioplay
  let audiomute = sdlk_audiomute
  let mediaselect = sdlk_mediaselect
  let www = sdlk_www
  let mail = sdlk_mail
  let calculator = sdlk_calculator
  let computer = sdlk_computer
  let ac_search = sdlk_ac_search
  let ac_home = sdlk_ac_home
  let ac_back = sdlk_ac_back
  let ac_forward = sdlk_ac_forward
  let ac_stop = sdlk_ac_stop
  let ac_refresh = sdlk_ac_refresh
  let ac_bookmarks = sdlk_ac_bookmarks
  let brightnessdown = sdlk_brightnessdown
  let brightnessup = sdlk_brightnessup
  let displayswitch = sdlk_displayswitch
  let kbdillumtoggle = sdlk_kbdillumtoggle
  let kbdillumdown = sdlk_kbdillumdown
  let kbdillumup = sdlk_kbdillumup
  let eject = sdlk_eject
  let sleep = sdlk_sleep
end

type keymod = int
let keymod = int_as_uint16_t

module Kmod = struct
  let none = kmod_none
  let lshift = kmod_lshift
  let rshift = kmod_rshift
  let lctrl = kmod_lctrl
  let rctrl = kmod_rctrl
  let lalt = kmod_lalt
  let ralt = kmod_ralt
  let lgui = kmod_lgui
  let rgui = kmod_rgui
  let num = kmod_num
  let caps = kmod_caps
  let mode = kmod_mode
  let reserved = kmod_reserved
  let ctrl = kmod_ctrl
  let shift = kmod_shift
  let alt = kmod_alt
  let gui = kmod_gui
end

let get_keyboard_focus =
  foreign "SDL_GetKeyboardFocus" (void @-> returning window_opt)

let get_keyboard_state =
  foreign "SDL_GetKeyboardState" (ptr int @-> returning (ptr int))

let get_keyboard_state () =
  let count = allocate int 0 in
  let p = get_keyboard_state count in
  bigarray_of_ptr array1 (!@ count) Bigarray.int8_unsigned p

let get_key_from_name =
  foreign "SDL_GetKeyFromName" (string @-> returning keycode)

let get_key_from_scancode =
  foreign "SDL_GetKeyFromScancode" (scancode @-> returning keycode)

let get_key_name =
  foreign "SDL_GetKeyName" (keycode @-> returning string)

let get_mod_state =
  foreign "SDL_GetModState" (void @-> returning keymod)

let get_scancode_from_key =
  foreign "SDL_GetScancodeFromKey" (keycode @-> returning scancode)

let get_scancode_from_name =
  foreign "SDL_GetScancodeFromName" (string @-> returning scancode)

let get_scancode_name =
  foreign "SDL_GetScancodeName" (scancode @-> returning string)

let has_screen_keyboard_support =
  foreign "SDL_HasScreenKeyboardSupport" (void @-> returning bool)

let is_screen_keyboard_shown =
  foreign "SDL_IsScreenKeyboardShown" (window @-> returning bool)

let is_text_input_active =
  foreign "SDL_IsTextInputActive" (void @-> returning bool)

let set_mod_state =
  foreign "SDL_SetModState" (keymod @-> returning void)

let set_text_input_rect =
  foreign "SDL_SetTextInputRect" (ptr rect @-> returning void)

let set_text_input_rect r =
  set_text_input_rect (Rect.opt_addr r)

let start_text_input =
  foreign "SDL_StartTextInput" (void @-> returning void)

let stop_text_input =
  foreign "SDL_StopTextInput" (void @-> returning void)

(* Mouse *)

type cursor = unit ptr
let cursor : cursor typ = ptr void
let cursor_opt : cursor option typ = ptr_opt void

let unsafe_cursor_of_ptr addr : cursor =
  ptr_of_raw_address addr
let unsafe_ptr_of_cursor cursor =
  raw_address_of_ptr (to_voidp cursor)

module System_cursor = struct
  type t = int
  let arrow = sdl_system_cursor_arrow
  let ibeam = sdl_system_cursor_ibeam
  let wait = sdl_system_cursor_wait
  let crosshair = sdl_system_cursor_crosshair
  let waitarrow = sdl_system_cursor_waitarrow
  let size_nw_se = sdl_system_cursor_sizenwse
  let size_ne_sw = sdl_system_cursor_sizenesw
  let size_we = sdl_system_cursor_sizewe
  let size_ns = sdl_system_cursor_sizens
  let size_all = sdl_system_cursor_sizeall
  let no = sdl_system_cursor_no
  let hand = sdl_system_cursor_hand
end

module Button = struct
  let left = sdl_button_left
  let right = sdl_button_right
  let middle = sdl_button_middle
  let x1 = sdl_button_x1
  let x2 = sdl_button_x2

  let i = Int32.of_int
  let lmask = i sdl_button_lmask
  let mmask = i sdl_button_mmask
  let rmask = i sdl_button_rmask
  let x1mask = i sdl_button_x1mask
  let x2mask = i sdl_button_x2mask
end

let capture_mouse =
  foreign "SDL_CaptureMouse" (bool @-> returning zero_to_ok)

let create_color_cursor =
  foreign "SDL_CreateColorCursor"
    (surface @-> int @-> int @-> returning (some_to_ok cursor_opt))

let create_color_cursor s ~hot_x ~hot_y =
  create_color_cursor s hot_x hot_y

let create_cursor =
  foreign "SDL_CreateCursor"
    (ptr void @-> ptr void @-> int @-> int @-> int @-> int @->
     returning (some_to_ok cursor_opt))

let create_cursor d m ~w ~h ~hot_x ~hot_y =
  (* FIXME: we could try to check bounds *)
  let d = to_voidp (bigarray_start array1 d) in
  let m = to_voidp (bigarray_start array1 m) in
  create_cursor d m w h hot_x hot_y

let create_system_cursor =
  foreign "SDL_CreateSystemCursor"
    (int @-> returning (some_to_ok cursor_opt))

let free_cursor =
  foreign "SDL_FreeCursor" (cursor @-> returning void)

let get_cursor =
  foreign "SDL_GetCursor" (void @-> returning cursor_opt)

let get_default_cursor =
  foreign "SDL_GetDefaultCursor" (void @-> returning cursor_opt)

let get_global_mouse_state =
  foreign "SDL_GetGlobalMouseState"
    (ptr int @-> ptr int @-> returning int32_as_uint32_t)

let get_global_mouse_state () =
  let x = allocate int 0 in
  let y = allocate int 0 in
  let s = get_global_mouse_state x y in
  s, (!@ x, !@ y)

let get_mouse_focus =
  foreign "SDL_GetMouseFocus" (void @-> returning window_opt)

let get_mouse_state =
  foreign "SDL_GetMouseState"
    (ptr int @-> ptr int @-> returning int32_as_uint32_t)

let get_mouse_state () =
  let x = allocate int 0 in
  let y = allocate int 0 in
  let s = get_mouse_state x y in
  s, (!@ x, !@ y)

let get_relative_mouse_mode =
  foreign "SDL_GetRelativeMouseMode" (void @-> returning bool)

let get_relative_mouse_state =
  foreign "SDL_GetRelativeMouseState"
    (ptr int @-> ptr int @-> returning int32_as_uint32_t)

let get_relative_mouse_state () =
  let x = allocate int 0 in
  let y = allocate int 0 in
  let s = get_relative_mouse_state x y in
  s, (!@ x, !@ y)

let show_cursor =
  foreign "SDL_ShowCursor" (int @-> returning bool_to_ok)

let get_cursor_shown () =
  show_cursor (-1)

let set_cursor =
  foreign "SDL_SetCursor" (cursor_opt @-> returning void)

let set_relative_mouse_mode =
  foreign "SDL_SetRelativeMouseMode" (bool @-> returning zero_to_ok)

let show_cursor b =
  show_cursor (if b then 1 else 0)

let warp_mouse_in_window =
  foreign "SDL_WarpMouseInWindow"
    (window_opt @-> int @-> int @-> returning void)

let warp_mouse_in_window w ~x ~y =
  warp_mouse_in_window w x y

let warp_mouse_global=
  foreign "SDL_WarpMouseGlobal" (int @-> int @-> returning zero_to_ok)

let warp_mouse_global ~x ~y =
  warp_mouse_global x y

(* Touch *)

type touch_id = int64
let touch_id = int64_t
let touch_mouse_id = Int64.of_int32 (sdl_touch_mouseid)

type gesture_id = int64
let gesture_id = int64_t

type finger_id = int64
let finger_id = int64_t

type _finger
type finger = _finger structure
let finger : finger typ = structure "SDL_Finger"
let finger_finger_id = field finger "id" finger_id
let finger_x = field finger "x" float
let finger_y = field finger "y" float
let finger_pressure = field finger "pressure" float
let () = seal finger

module Finger = struct
  let id f = getf f finger_finger_id
  let x f = getf f finger_x
  let y f = getf f finger_y
  let pressure f = getf f finger_pressure
end

let get_num_touch_devices =
  foreign "SDL_GetNumTouchDevices" (void @-> returning int)

let get_num_touch_fingers =
  foreign "SDL_GetNumTouchFingers" (touch_id @-> returning int)

let get_touch_device =
  foreign "SDL_GetTouchDevice" (int @-> returning touch_id)

let get_touch_device i =
  match get_touch_device i with
  | 0L -> error () | id -> Ok id

let get_touch_finger =
  foreign "SDL_GetTouchFinger"
    (touch_id @-> int @-> returning (ptr_opt finger))

let get_touch_finger id i =
  match get_touch_finger id i with
  | None -> None | Some p -> Some (!@ p)

let load_dollar_templates =
  foreign "SDL_LoadDollarTemplates"
    (touch_id @-> rw_ops @-> returning zero_to_ok)

let record_gesture =
  foreign "SDL_RecordGesture" (touch_id @-> returning one_to_ok)

let save_dollar_template =
  foreign "SDL_SaveDollarTemplate"
    (gesture_id @-> rw_ops @-> returning zero_to_ok)

let save_all_dollar_templates =
  foreign "SDL_SaveAllDollarTemplates" (rw_ops @-> returning zero_to_ok)

(* Joystick *)

type _joystick_guid
type joystick_guid = _joystick_guid structure
let joystick_guid : joystick_guid typ = structure "SDL_JoystickGUID"
(* FIXME: No array here, see
   https://github.com/ocamllabs/ocaml-ctypes/issues/113 *)
(* let _= field joystick_guid "data" (array 16 uint8_t) *)
let _= field joystick_guid "data0" uint8_t
let _= field joystick_guid "data1" uint8_t
let _= field joystick_guid "data2" uint8_t
let _= field joystick_guid "data3" uint8_t
let _= field joystick_guid "data4" uint8_t
let _= field joystick_guid "data5" uint8_t
let _= field joystick_guid "data6" uint8_t
let _= field joystick_guid "data7" uint8_t
let _= field joystick_guid "data8" uint8_t
let _= field joystick_guid "data9" uint8_t
let _= field joystick_guid "data10" uint8_t
let _= field joystick_guid "data11" uint8_t
let _= field joystick_guid "data12" uint8_t
let _= field joystick_guid "data13" uint8_t
let _= field joystick_guid "data14" uint8_t
let _= field joystick_guid "data15" uint8_t
let () = seal joystick_guid

type joystick_id = int32
let joystick_id = int32_t

type joystick = unit ptr
let joystick : joystick typ = ptr void
let joystick_opt : joystick option typ = ptr_opt void

let unsafe_joystick_of_ptr addr : joystick =
  ptr_of_raw_address addr
let unsafe_ptr_of_joystick joystick =
  raw_address_of_ptr (to_voidp joystick)

module Hat = struct
  type t = int
  let centered = sdl_hat_centered
  let up = sdl_hat_up
  let right = sdl_hat_right
  let down = sdl_hat_down
  let left = sdl_hat_left
  let rightup = sdl_hat_rightup
  let rightdown = sdl_hat_rightdown
  let leftup = sdl_hat_leftup
  let leftdown = sdl_hat_leftdown
end

module Joystick_power_level = struct
  type t = int
  let unknown = sdl_joystick_power_unknown
  let low = sdl_joystick_power_low
  let medium = sdl_joystick_power_medium
  let full = sdl_joystick_power_full
  let wired = sdl_joystick_power_wired
  let max = sdl_joystick_power_max
end

module Joystick_type = struct
  type t = int
  let unknown        = sdl_joystick_type_unknown
  let gamecontroller = sdl_joystick_type_gamecontroller
  let wheel          = sdl_joystick_type_wheel
  let arcade_stick   = sdl_joystick_type_arcade_stick
  let flight_stick   = sdl_joystick_type_flight_stick
  let dance_pad      = sdl_joystick_type_dance_pad
  let guitar         = sdl_joystick_type_guitar
  let drum_kit       = sdl_joystick_type_drum_kit
  let arcade_pad     = sdl_joystick_type_arcade_pad
  let throttle      = sdl_joystick_type_throttle
end

let joystick_close =
  foreign "SDL_JoystickClose" (joystick @-> returning void)

let joystick_current_power_level =
  foreign "SDL_JoystickCurrentPowerLevel"
    (joystick @-> returning int)

let joystick_event_state =
  foreign "SDL_JoystickEventState" (int @-> returning nat_to_ok)

let joystick_from_instance_id =
  foreign "SDL_JoystickFromInstanceID" (joystick_id @-> returning joystick)

let joystick_get_event_state () =
  joystick_event_state sdl_query

let joystick_set_event_state s =
  joystick_event_state s

let joystick_get_attached =
  foreign "SDL_JoystickGetAttached" (joystick @-> returning bool)

let joystick_get_axis =
  foreign "SDL_JoystickGetAxis" (joystick @-> int @-> returning int16_t)

let joystick_get_axis_initial_state =
  foreign "SDL_JoystickGetAxisInitialState"
    (joystick @-> int @-> returning int16_t)

let joystick_get_ball =
  foreign "SDL_JoystickGetBall"
    (joystick @-> int @-> (ptr int) @-> (ptr int) @-> returning int)

let joystick_get_ball j i =
  let x = allocate int 0 in
  let y = allocate int 0 in
  match joystick_get_ball j i x y with
  | 0 -> Ok (!@ x, !@ y) | _ -> error ()

let joystick_get_button =
  foreign "SDL_JoystickGetButton"
    (joystick @-> int @-> returning int_as_uint8_t)

let joystick_get_device_guid =
  foreign "SDL_JoystickGetDeviceGUID" (int @-> returning joystick_guid)

let joystick_get_device_product =
  foreign "SDL_JoystickGetDeviceProduct" (int @-> returning int_as_uint16_t)

let joystick_get_device_product_version =
  foreign "SDL_JoystickGetDeviceProductVersion"
    (int @-> returning int_as_uint16_t)

let joystick_get_device_type =
  foreign "SDL_JoystickGetDeviceType" (int @-> returning int)

let joystick_get_device_instance_id =
  foreign "SDL_JoystickGetDeviceInstanceID" (int @-> returning joystick_id)

let joystick_get_device_vendor =
  foreign "SDL_JoystickGetDeviceVendor" (int @-> returning int_as_uint16_t)

let joystick_get_guid =
  foreign "SDL_JoystickGetGUID" (joystick @-> returning joystick_guid)

let joystick_get_guid_from_string =
  foreign "SDL_JoystickGetGUIDFromString" (string @-> returning joystick_guid)

let joystick_get_guid_string =
  foreign "SDL_JoystickGetGUIDString"
    (joystick_guid @-> ptr char @-> int @-> returning void)

let joystick_get_guid_string guid =
  let len = 33 in
  let s = CArray.start (CArray.make char 33) in
  joystick_get_guid_string guid s len;
  coerce (ptr char) string s

let joystick_get_hat =
  foreign "SDL_JoystickGetHat" (joystick @-> int @-> returning int_as_uint8_t)

let joystick_get_product =
  foreign "SDL_JoystickGetProduct" (joystick @-> returning int_as_uint16_t)

let joystick_get_product_version =
  foreign "SDL_JoystickGetProductVersion"
    (joystick @-> returning int_as_uint16_t)

let joystick_get_type =
  foreign "SDL_JoystickGetType" (joystick @-> returning int)

let joystick_get_vendor =
  foreign "SDL_JoystickGetVendor" (joystick @-> returning int_as_uint16_t)

let joystick_instance_id =
  foreign "SDL_JoystickInstanceID" (joystick @-> returning joystick_id)

let joystick_instance_id j =
  match joystick_instance_id j with
  | n when n < 0l -> error () | n -> Ok n

let joystick_name =
  foreign "SDL_JoystickName" (joystick @-> returning (some_to_ok string_opt))

let joystick_name_for_index =
  foreign "SDL_JoystickNameForIndex" (int @-> returning (some_to_ok string_opt))

let joystick_num_axes =
  foreign "SDL_JoystickNumAxes" (joystick @-> returning nat_to_ok)

let joystick_num_balls =
  foreign "SDL_JoystickNumBalls" (joystick @-> returning nat_to_ok)

let joystick_num_buttons =
  foreign "SDL_JoystickNumButtons" (joystick @-> returning nat_to_ok)

let joystick_num_hats =
  foreign "SDL_JoystickNumHats" (joystick @-> returning nat_to_ok)

let joystick_open =
  foreign "SDL_JoystickOpen" (int @-> returning (some_to_ok joystick_opt))

let joystick_update =
  foreign "SDL_JoystickUpdate" (void @-> returning void)

let num_joysticks =
  foreign "SDL_NumJoysticks" (void @-> returning nat_to_ok)

(* Game controller *)

type game_controller = unit ptr
let game_controller : game_controller typ = ptr void
let game_controller_opt : game_controller option typ = ptr_opt void

let unsafe_game_controller_of_ptr addr : game_controller =
  ptr_of_raw_address addr
let unsafe_ptr_of_game_controller game_controller =
  raw_address_of_ptr (to_voidp game_controller)

type _button_bind
let button_bind : _button_bind structure typ =
  structure "SDL_GameControllerBindType"
let button_bind_bind_type = field button_bind "bindType" int
let button_bind_value1 = field button_bind "value1" int  (* simplified enum *)
let button_bind_value2 = field button_bind "value2" int
let () = seal button_bind

module Controller = struct
  type bind_type = int
  let bind_type_none = sdl_controller_bindtype_none
  let bind_type_button = sdl_controller_bindtype_button
  let bind_type_axis = sdl_controller_bindtype_axis
  let bind_type_hat = sdl_controller_bindtype_hat

  type axis = int
  let axis_invalid = sdl_controller_axis_invalid
  let axis_left_x = sdl_controller_axis_leftx
  let axis_left_y = sdl_controller_axis_lefty
  let axis_right_x = sdl_controller_axis_rightx
  let axis_right_y = sdl_controller_axis_righty
  let axis_trigger_left = sdl_controller_axis_triggerleft
  let axis_trigger_right = sdl_controller_axis_triggerright
  let axis_max = sdl_controller_axis_max

  type button = int
  let button_invalid = sdl_controller_button_invalid
  let button_a = sdl_controller_button_a
  let button_b = sdl_controller_button_b
  let button_x = sdl_controller_button_x
  let button_y = sdl_controller_button_y
  let button_back = sdl_controller_button_back
  let button_guide = sdl_controller_button_guide
  let button_start = sdl_controller_button_start
  let button_left_stick = sdl_controller_button_leftstick
  let button_right_stick = sdl_controller_button_rightstick
  let button_left_shoulder = sdl_controller_button_leftshoulder
  let button_right_shoulder = sdl_controller_button_rightshoulder
  let button_dpad_up = sdl_controller_button_dpad_up
  let button_dpad_down = sdl_controller_button_dpad_down
  let button_dpad_left = sdl_controller_button_dpad_left
  let button_dpad_right = sdl_controller_button_dpad_right
  let button_max = sdl_controller_button_max

  type button_bind = _button_bind structure
  let bind_type v = getf v button_bind_bind_type
  let bind_button_value v = getf v button_bind_value1
  let bind_axis_value v = getf v button_bind_value1
  let bind_hat_value v = getf v button_bind_value1, getf v button_bind_value2
end

let game_controller_add_mapping =
  foreign "SDL_GameControllerAddMapping" (string @-> returning bool_to_ok)

let game_controller_add_mapping_from_rw =
  foreign "SDL_GameControllerAddMappingsFromRW"
    ~stub (rw_ops @-> bool @-> returning nat_to_ok)

let game_controller_close =
  foreign "SDL_GameControllerClose" (game_controller @-> returning void)

let game_controller_event_state =
  foreign "SDL_GameControllerEventState" (int @-> returning nat_to_ok)

let game_controller_from_instance_id =
  foreign "SDL_GameControllerFromInstanceID"
    (joystick_id @-> returning game_controller)

let game_controller_get_event_state () =
  game_controller_event_state sdl_query

let game_controller_set_event_state t =
  game_controller_event_state t

let game_controller_get_attached =
  foreign "SDL_GameControllerGetAttached" (game_controller @-> returning bool)

let game_controller_get_axis =
  foreign "SDL_GameControllerGetAxis"
    (game_controller @-> int @-> returning int16_t)

let game_controller_get_axis_from_string =
  foreign "SDL_GameControllerGetAxisFromString"
    (string @-> returning int)

let game_controller_get_bind_for_axis =
  foreign "SDL_GameControllerGetBindForAxis"
    (game_controller @-> int @-> returning button_bind)

let game_controller_get_bind_for_button =
  foreign "SDL_GameControllerGetBindForButton"
    (game_controller @-> int @-> returning button_bind)

let game_controller_get_button =
  foreign "SDL_GameControllerGetButton"
    (game_controller @-> int @-> returning int_as_uint8_t)

let game_controller_get_button_from_string =
  foreign "SDL_GameControllerGetButtonFromString" (string @-> returning int)

let game_controller_get_joystick =
  foreign "SDL_GameControllerGetJoystick"
    (game_controller @-> returning (some_to_ok joystick_opt))

let game_controller_get_product =
  foreign "SDL_GameControllerGetProduct"
    (game_controller @-> returning int_as_uint16_t)

let game_controller_get_product_version =
  foreign "SDL_GameControllerGetProductVersion"
    (game_controller @-> returning int_as_uint16_t)

let game_controller_get_string_for_axis =
  foreign "SDL_GameControllerGetStringForAxis" (int @-> returning string_opt)

let game_controller_get_string_for_button =
  foreign "SDL_GameControllerGetStringForButton" (int @-> returning string_opt)

let game_controller_get_vendor =
  foreign "SDL_GameControllerGetVendor"
    (game_controller @-> returning int_as_uint16_t)

let game_controller_mapping =
  foreign "SDL_GameControllerMapping"
    (game_controller @-> returning (some_to_ok string_opt))

let game_controller_mapping_for_index =
  foreign "SDL_GameControllerMappingForIndex"
    (int @-> returning (some_to_ok string_opt))

let game_controller_mapping_for_guid =
  foreign "SDL_GameControllerMappingForGUID"
    (joystick_guid @-> returning (some_to_ok string_opt))

let game_controller_name =
  foreign "SDL_GameControllerName"
    (game_controller @-> returning (some_to_ok string_opt))

let game_controller_name_for_index =
  foreign "SDL_GameControllerNameForIndex"
    (int @-> returning (some_to_ok string_opt))

let game_controller_num_mappings =
  foreign "SDL_GameControllerNumMappings" (void @-> returning int)

let game_controller_open =
  foreign "SDL_GameControllerOpen"
    (int @-> returning (some_to_ok game_controller_opt))

let game_controller_update =
  foreign "SDL_GameControllerUpdate" (void @-> returning void)

let is_game_controller =
  foreign "SDL_IsGameController" (int @-> returning bool)

(* Events *)

type event_type = int
let event_type : event_type typ = int_as_uint32_t

module Event = struct

  (* Event structures *)

  module Common = struct
    type t
    let t : t structure typ = structure "SDL_CommonEvent"
    let typ = field t "type" int_as_uint32_t
    let timestamp = field t "timestamp" int32_as_uint32_t
    let () = seal t
  end

  module Controller_axis_event = struct
    type t
    let t : t structure typ = structure "SDL_ControllerAxisEvent"
    let _ = field t "type" int_as_uint32_t
    let _ = field t "timestamp" int32_as_uint32_t
    let which = field t "which" joystick_id
    let axis = field t "axis" int_as_uint8_t
    let _ = field t "padding1" uint8_t
    let _ = field t "padding2" uint8_t
    let _ = field t "padding3" uint8_t
    let value = field t "value" int16_t
    let _ = field t "padding4" uint16_t
    let () = seal t
  end

  module Controller_button_event = struct
    type t
    let t : t structure typ = structure "SDL_ControllerButtonEvent"
    let _ = field t "type" int_as_uint32_t
    let _ = field t "timestamp" int32_as_uint32_t
    let which = field t "which" joystick_id
    let button = field t "button" int_as_uint8_t
    let state = field t "state" int_as_uint8_t
    let _ = field t "padding1" uint8_t
    let _ = field t "padding2" uint8_t
    let () = seal t
  end

  module Controller_device_event = struct
    type t
    let t : t structure typ = structure "SDL_ControllerDeviceEvent"
    let _ = field t "type" int_as_uint32_t
    let _ = field t "timestamp" int32_as_uint32_t
    let which = field t "which" joystick_id
    let () = seal t
  end

  module Dollar_gesture_event = struct
    type t
    let t : t structure typ = structure "SDL_DollarGestureEvent"
    let _ = field t "type" int_as_uint32_t
    let _ = field t "timestamp" int32_as_uint32_t
    let touch_id = field t "touchId" touch_id
    let gesture_id = field t "gestureId" gesture_id
    let num_fingers = field t "numFingers" int_as_uint32_t
    let error = field t "error" float
    let x = field t "x" float
    let y = field t "y" float
    let () = seal t
  end

  module Drop_event = struct
    type t
    let t : t structure typ = structure "SDL_DropEvent"
    let _ = field t "type" int_as_uint32_t
    let _ = field t "timestamp" int32_as_uint32_t
    let file = field t "file" (ptr char)
    let window_id = field t "windowID" int_as_uint32_t
    let () = seal t
  end

  module Keyboard_event = struct
    type t
    let t : t structure typ = structure "SDL_KeyboardEvent"
    let _ = field t "type" int_as_uint32_t
    let _ = field t "timestamp" int32_as_uint32_t
    let window_id = field t "windowID" int_as_uint32_t
    let state = field t "state" int_as_uint8_t
    let repeat = field t "repeat" int_as_uint8_t
    let padding2 = field t "padding2" uint8_t
    let padding3 = field t "padding3" uint8_t
    (* We inline the definition of SDL_Keysym *)
    let scancode = field t "scancode" scancode
    let keycode = field t "sym" keycode
    let keymod = field t "mod" keymod
    let unused = field t "unused" uint32_t
    let () = seal t
  end

  module Joy_axis_event = struct
    type t
    let t : t structure typ = structure "SDL_JoyAxisEvent"
    let _ = field t "type" int_as_uint32_t
    let _ = field t "timestamp" int32_as_uint32_t
    let which = field t "which" joystick_id
    let axis = field t "axis" int_as_uint8_t
    let _ = field t "padding1" uint8_t
    let _ = field t "padding2" uint8_t
    let _ = field t "padding3" uint8_t
    let value = field t "value" int16_t
    let _ = field t "padding4" uint16_t
    let () = seal t
  end

  module Joy_ball_event = struct
    type t
    let t : t structure typ = structure "SDL_JoyBallEvent"
    let _ = field t "type" int_as_uint32_t
    let _ = field t "timestamp" int32_as_uint32_t
    let which = field t "which" joystick_id
    let ball = field t "ball" int_as_uint8_t
    let _ = field t "padding1" uint8_t
    let _ = field t "padding2" uint8_t
    let _ = field t "padding3" uint8_t
    let xrel = field t "xrel" int16_t
    let yrel = field t "yrel" int16_t
    let () = seal t
  end

  module Joy_button_event = struct
    type t
    let t : t structure typ = structure "SDL_JoyButtonEvent"
    let _ = field t "type" int_as_uint32_t
    let _ = field t "timestamp" int32_as_uint32_t
    let which = field t "which" joystick_id
    let button = field t "button" int_as_uint8_t
    let state = field t "state" int_as_uint8_t
    let _ = field t "padding1" uint8_t
    let _ = field t "padding2" uint8_t
    let () = seal t
  end

  module Joy_device_event = struct
    type t
    let t : t structure typ = structure "SDL_JoyDeviceEvent"
    let _ = field t "type" int_as_uint32_t
    let _ = field t "timestamp" int32_as_uint32_t
    let which = field t "which" joystick_id
    let () = seal t
  end

  module Joy_hat_event = struct
    type t
    let t : t structure typ = structure "SDL_JoyHatEvent"
    let _ = field t "type" int_as_uint32_t
    let _ = field t "timestamp" int32_as_uint32_t
    let which = field t "which" joystick_id
    let hat = field t "hat" int_as_uint8_t
    let value = field t "value" int_as_uint8_t
    let _ = field t "padding1" uint8_t
    let _ = field t "padding2" uint8_t
    let () = seal t
  end

  module Mouse_button_event = struct
    type t
    let t : t structure typ = structure "SDL_MouseButtonEvent"
    let _ = field t "type" int_as_uint32_t
    let _ = field t "timestamp" int32_as_uint32_t
    let window_id = field t "windowID" int_as_uint32_t
    let which = field t "which" int32_as_uint32_t
    let button = field t "button" int_as_uint8_t
    let state = field t "state" int_as_uint8_t
    let clicks = field t "clicks" int_as_uint8_t
    let _ = field t "padding1" int_as_uint8_t
    let x = field t "x" int_as_int32_t
    let y = field t "y" int_as_int32_t
    let () = seal t
  end

  module Mouse_motion_event = struct
    type t
    let t : t structure typ = structure "SDL_MouseMotionEvent"
    let _ = field t "type" int_as_uint32_t
    let _ = field t "timestamp" int32_as_uint32_t
    let window_id = field t "windowID" int_as_uint32_t
    let which = field t "which" int32_as_uint32_t
    let state = field t "state" int32_as_uint32_t
    let x = field t "x" int_as_int32_t
    let y = field t "y" int_as_int32_t
    let xrel = field t "xrel" int_as_int32_t
    let yrel = field t "yrel" int_as_int32_t
    let () = seal t
  end

    type mouse_wheel_direction = int
    let mouse_wheel_normal = sdl_mousewheel_normal
    let mouse_wheel_flipped = sdl_mousewheel_flipped

  module Mouse_wheel_event = struct
    type t
    let t : t structure typ = structure "SDL_MouseWheelEvent"
    let _ = field t "type" int_as_uint32_t
    let _ = field t "timestamp" int32_as_uint32_t
    let window_id = field t "windowID" int_as_uint32_t
    let which = field t "which" int32_as_uint32_t
    let x = field t "x" int_as_int32_t
    let y = field t "y" int_as_int32_t
    let direction = field t "direction" int_as_uint32_t
    let () = seal t
  end

  module Multi_gesture_event = struct
    type t
    let t : t structure typ = structure "SDL_MultiGestureEvent"
    let _ = field t "type" int_as_uint32_t
    let _ = field t "timestamp" int32_as_uint32_t
    let touch_id = field t "touchId" touch_id
    let dtheta = field t "dTheta" float
    let ddist = field t "ddist" float
    let x = field t "x" float
    let y = field t "y" float
    let num_fingers = field t "numFingers" int_as_uint16_t
    let _ = field t "padding" uint16_t
    let () = seal t
  end

  module Sensor_event = struct
    type t
    let t : t structure typ = structure "SDL_SensorEvent"
    let _ = field t "type" int_as_uint32_t
    let _ = field t "timestamp" int32_as_uint32_t
    let which = field t "which" int32_as_uint32_t
    (* FIXME: No array here, see
       https://github.com/ocamllabs/ocaml-ctypes/issues/113 *)
    let data0 = field t "data0" float
    let data1 = field t "data1" float
    let data2 = field t "data2" float
    let data3 = field t "data3" float
    let data4 = field t "data4" float
    let data5 = field t "data5" float
    let () = seal t
  end

  module Quit_event = struct
    type t
    let t : t structure typ = structure "SDL_QuitEvent"
    let _ = field t "type" int_as_uint32_t
    let _ = field t "timestamp" int32_as_uint32_t
    let () = seal t
  end

  module Sys_wm_event = struct
    type t
    let t : t structure typ = structure "SDL_SysWMEvent"
    let _ = field t "type" int_as_uint32_t
    let _ = field t "timestamp" int32_as_uint32_t
    let _ = field t "msg" (ptr void)
    let () = seal t
  end

  module Text_editing_event = struct
    type t
    let t : t structure typ = structure "SDL_TextEditingEvent"
    let _ = field t "type" int_as_uint32_t
    let _ = field t "timestamp" int32_as_uint32_t
    let window_id = field t "windowID" int_as_uint32_t
    let text = field t "text" (string_as_char_array
                                 sdl_texteditingevent_text_size)
    let start = field t "start" int_as_int32_t
    let length = field t "end" int_as_int32_t
    let () = seal t
  end

  module Text_input_event = struct
    type t
    let t : t structure typ = structure "SDL_TextIfmtsnputEvent"
    let _ = field t "type" int_as_uint32_t
    let _ = field t "timestamp" int32_as_uint32_t
    let window_id = field t "windowID" int_as_uint32_t
    let text = field t "text" (string_as_char_array
                                 sdl_textinputevent_text_size)
    let () = seal t
  end

  module Touch_finger_event = struct
    type t
    let t : t structure typ = structure "SDL_TouchFingerEvent"
    let _ = field t "type" int_as_uint32_t
    let _ = field t "timestamp" int32_as_uint32_t
    let touch_id = field t "touchId" touch_id
    let finger_id = field t "fingerId" finger_id
    let x = field t "x" float
    let y = field t "y" float
    let dx = field t "dx" float
    let dy = field t "dy" float
    let pressure = field t "pressure" float
    let () = seal t
  end

  module User_event = struct
    type t
    let t : t structure typ = structure "SDL_UserEvent"
    let _ = field t "type" int_as_uint32_t
    let _ = field t "timestamp" int32_as_uint32_t
    let window_id = field t "windowID" int_as_uint32_t
    let code = field t "code" int_as_int32_t
    let _ = field t "data1" (ptr void)
    let _ = field t "data2" (ptr void)
    let () = seal t
  end

  module Window_event = struct
    type t
    let t : t structure typ = structure "SDL_WindowEvent"
    let _ = field t "type" int_as_uint32_t
    let _ = field t "timestamp" int32_as_uint32_t
    let window_id = field t "windowID" int_as_uint32_t
    let event = field t "event" int_as_uint8_t
    let padding1 = field t "padding1" uint8_t
    let padding2 = field t "padding2" uint8_t
    let padding3 = field t "padding3" uint8_t
    let data1 = field t "data1" int32_t
    let data2 = field t "data2" int32_t
    let () = seal t
  end

  module Display_event = struct
    type t
    let t : t structure typ = structure "SDL_DisplayEvent"
    let _ = field t "type" int_as_uint32_t
    let _ = field t "timestamp" int32_as_uint32_t
    let display = field t "display" int32_as_uint32_t
    let event = field t "event" int_as_uint8_t
    let padding1 = field t "padding1" uint8_t
    let padding2 = field t "padding2" uint8_t
    let padding3 = field t "padding3" uint8_t
    let data1 = field t "data1" int32_t
    let () = seal t
  end

  module Audio_device_event = struct
    type t
    let t : t structure typ = structure "SDL_AudioDevice"
    let _ = field t "type" int_as_uint32_t
    let timestamp = field t "timestamp" int32_as_uint32_t
    let which = field t "which" int32_as_uint32_t
    let iscapture = field t "iscapture" int_as_uint8_t
    let () = seal t
  end

  type t
  let t : t union typ = union "SDL_Event"
  let typ = field t "type" int_as_uint32_t
  let audio_device_event = field t "adevice" Audio_device_event.t
  let common = field t "common" Common.t
  let controller_axis_event = field t "caxis" Controller_axis_event.t
  let controller_button_event = field t "cbutton" Controller_button_event.t
  let controller_device_event = field t "cdevice" Controller_device_event.t
  let dollar_gesture_event = field t "dgesture" Dollar_gesture_event.t
  let drop_event = field t "drop" Drop_event.t
  let joy_axis_event = field t "jaxis" Joy_axis_event.t
  let joy_ball_event = field t "jball" Joy_ball_event.t
  let joy_button_event = field t "jbutton" Joy_button_event.t
  let joy_device_event = field t "jdevice" Joy_device_event.t
  let joy_hat_event = field t "jhat" Joy_hat_event.t
  let keyboard_event = field t "key" Keyboard_event.t
  let mouse_button_event = field t "button" Mouse_button_event.t
  let mouse_motion_event = field t "motion" Mouse_motion_event.t
  let mouse_wheel_event = field t "wheel" Mouse_wheel_event.t
  let multi_gesture_event = field t "mgesture" Multi_gesture_event.t
  let quit_event = field t "quit" Quit_event.t
  let sys_wm_event = field t "syswm" Sys_wm_event.t
  let text_editing_event = field t "edit" Text_editing_event.t
  let text_input_event = field t "text" Text_input_event.t
  let touch_finger_event = field t "tfinger" Touch_finger_event.t
  let user_event = field t "user" User_event.t
  let window_event = field t "window" Window_event.t
  let display_event = field t "display" Display_event.t
  let sensor_event = field t "sensor" Sensor_event.t
  let padding = field t "padding"
      (abstract ~name:"padding" ~size:tsdl_sdl_event_size ~alignment:1)
  let () = seal t

  let create () = make t
  let opt_addr = function
  | None -> coerce (ptr void) (ptr t) null
  | Some v -> addr v

  type _ field =
      F : (* existential to hide the 'a structure *)
        (('a structure, t union) Ctypes.field *
         ('b, 'a structure) Ctypes.field) -> 'b field

  let get e (F (s, f)) = getf (getf e s) f
  let set e (F (s, f)) v = setf (getf e s) f v

  (* Aliases *)

  let first_event = sdl_firstevent
  let last_event = sdl_lastevent

  (* Common *)

  let typ  = F (common, Common.typ)
  let timestamp = F (common, Common.timestamp)

  (* Application events. *)

  let app_terminating = sdl_app_terminating
  let app_low_memory = sdl_app_lowmemory
  let app_will_enter_background = sdl_app_willenterbackground
  let app_did_enter_background = sdl_app_didenterbackground
  let app_will_enter_foreground = sdl_app_willenterforeground
  let app_did_enter_foreground = sdl_app_didenterforeground

  (* Clipboard events *)

  let clipboard_update = sdl_clipboardupdate

  (* Controller events *)

  let controller_axis_motion = sdl_controlleraxismotion
  let controller_button_down = sdl_controllerbuttondown
  let controller_button_up = sdl_controllerbuttonup
  let controller_device_added = sdl_controllerdeviceadded
  let controller_device_remapped = sdl_controllerdeviceremapped
  let controller_device_removed = sdl_controllerdeviceremoved

  let controller_axis_which =
    F (controller_axis_event, Controller_axis_event.which)
  let controller_axis_axis =
    F (controller_axis_event, Controller_axis_event.axis)
  let controller_axis_value =
    F (controller_axis_event, Controller_axis_event.value)

  let controller_button_which =
    F (controller_button_event, Controller_button_event.which)
  let controller_button_button =
    F (controller_button_event, Controller_button_event.button)
  let controller_button_state =
    F (controller_button_event, Controller_button_event.state)

  let controller_device_which =
    F (controller_device_event, Controller_device_event.which)

  (* Dollar gesture events *)

  let dollar_gesture = sdl_dollargesture
  let dollar_record = sdl_dollarrecord

  let dollar_gesture_touch_id =
    F (dollar_gesture_event, Dollar_gesture_event.touch_id)
  let dollar_gesture_gesture_id =
    F (dollar_gesture_event, Dollar_gesture_event.gesture_id)
  let dollar_gesture_num_fingers =
    F (dollar_gesture_event, Dollar_gesture_event.num_fingers)
  let dollar_gesture_error =
    F (dollar_gesture_event, Dollar_gesture_event.error)
  let dollar_gesture_x = F (dollar_gesture_event, Dollar_gesture_event.x)
  let dollar_gesture_y = F (dollar_gesture_event, Dollar_gesture_event.y)

  (* Drop file event *)

  let drop_file = sdl_dropfile
  let drop_text = sdl_droptext
  let drop_begin = sdl_dropbegin
  let drop_complete = sdl_dropcomplete

  let drop_file_file = F (drop_event, Drop_event.file)
  let drop_window_id = F (drop_event, Drop_event.window_id)

  let drop_file_free e =
    let sp = to_voidp (get e drop_file_file) in
    if is_null sp then () else sdl_free sp

  let drop_file_file e =
    let sp = get e drop_file_file in
    if is_null sp then None else Some (coerce (ptr char) string sp)

  (* Touch events *)

  let finger_down = sdl_fingerdown
  let finger_motion = sdl_fingermotion
  let finger_up = sdl_fingerup

  let touch_finger_touch_id = F (touch_finger_event,Touch_finger_event.touch_id)
  let touch_finger_finger_id =
    F (touch_finger_event, Touch_finger_event.finger_id)
  let touch_finger_x = F (touch_finger_event, Touch_finger_event.x)
  let touch_finger_y = F (touch_finger_event, Touch_finger_event.y)
  let touch_finger_dx = F (touch_finger_event, Touch_finger_event.dx)
  let touch_finger_dy = F (touch_finger_event, Touch_finger_event.dy)
  let touch_finger_pressure =
    F (touch_finger_event, Touch_finger_event.pressure)

  (* Joystick events. *)

  let joy_axis_motion = sdl_joyaxismotion
  let joy_ball_motion = sdl_joyballmotion
  let joy_button_down = sdl_joybuttondown
  let joy_button_up = sdl_joybuttonup
  let joy_device_added = sdl_joydeviceadded
  let joy_device_removed = sdl_joydeviceremoved
  let joy_hat_motion = sdl_joyhatmotion

  let joy_axis_which = F (joy_axis_event, Joy_axis_event.which)
  let joy_axis_axis = F (joy_axis_event, Joy_axis_event.axis)
  let joy_axis_value = F (joy_axis_event, Joy_axis_event.value)

  let joy_ball_which = F (joy_ball_event, Joy_ball_event.which)
  let joy_ball_ball = F (joy_ball_event, Joy_ball_event.ball)
  let joy_ball_xrel = F (joy_ball_event, Joy_ball_event.xrel)
  let joy_ball_yrel = F (joy_ball_event, Joy_ball_event.yrel)

  let joy_button_which = F (joy_button_event, Joy_button_event.which)
  let joy_button_button = F (joy_button_event, Joy_button_event.button)
  let joy_button_state = F (joy_button_event, Joy_button_event.state)

  let joy_device_which = F (joy_device_event, Joy_device_event.which)

  let joy_hat_which = F (joy_hat_event, Joy_hat_event.which)
  let joy_hat_hat = F (joy_hat_event, Joy_hat_event.hat)
  let joy_hat_value = F (joy_hat_event, Joy_hat_event.value)

  (* Keyboard events *)

  let key_down = sdl_keydown
  let key_up = sdl_keyup
  let keymap_changed = sdl_keymapchanged

  let keyboard_window_id = F (keyboard_event, Keyboard_event.window_id)
  let keyboard_repeat = F (keyboard_event, Keyboard_event.repeat)
  let keyboard_state = F (keyboard_event, Keyboard_event.state)
  let keyboard_scancode = F (keyboard_event, Keyboard_event.scancode)
  let keyboard_keycode = F (keyboard_event, Keyboard_event.keycode)
  let keyboard_keymod = F (keyboard_event, Keyboard_event.keymod)

  (* Mouse events *)

  let mouse_button_down = sdl_mousebuttondown
  let mouse_button_up = sdl_mousebuttonup
  let mouse_motion = sdl_mousemotion
  let mouse_wheel = sdl_mousewheel

  let mouse_button_window_id =
    F (mouse_button_event, Mouse_button_event.window_id)
  let mouse_button_which = F (mouse_button_event, Mouse_button_event.which)
  let mouse_button_state = F (mouse_button_event, Mouse_button_event.state)
  let mouse_button_button = F (mouse_button_event, Mouse_button_event.button)
  let mouse_button_clicks = F (mouse_button_event, Mouse_button_event.clicks)
  let mouse_button_x = F (mouse_button_event, Mouse_button_event.x)
  let mouse_button_y = F (mouse_button_event, Mouse_button_event.y)

  let mouse_motion_window_id =
    F (mouse_motion_event, Mouse_motion_event.window_id)
  let mouse_motion_which = F (mouse_motion_event, Mouse_motion_event.which)
  let mouse_motion_state = F (mouse_motion_event, Mouse_motion_event.state)
  let mouse_motion_x = F (mouse_motion_event, Mouse_motion_event.x)
  let mouse_motion_y = F (mouse_motion_event, Mouse_motion_event.y)
  let mouse_motion_xrel = F (mouse_motion_event, Mouse_motion_event.xrel)
  let mouse_motion_yrel = F (mouse_motion_event, Mouse_motion_event.yrel)

  let mouse_wheel_window_id = F (mouse_wheel_event, Mouse_wheel_event.window_id)
  let mouse_wheel_which = F (mouse_wheel_event, Mouse_wheel_event.which)
  let mouse_wheel_x = F (mouse_wheel_event, Mouse_wheel_event.x)
  let mouse_wheel_y = F (mouse_wheel_event, Mouse_wheel_event.y)
  let mouse_wheel_direction = F(mouse_wheel_event, Mouse_wheel_event.direction)

  (* Multi gesture events *)

  let multi_gesture = sdl_multigesture

  let multi_gesture_touch_id =
    F (multi_gesture_event, Multi_gesture_event.touch_id)
  let multi_gesture_dtheta = F (multi_gesture_event, Multi_gesture_event.dtheta)
  let multi_gesture_ddist = F (multi_gesture_event, Multi_gesture_event.ddist)
  let multi_gesture_x = F (multi_gesture_event, Multi_gesture_event.x)
  let multi_gesture_y = F (multi_gesture_event, Multi_gesture_event.y)
  let multi_gesture_num_fingers =
    F (multi_gesture_event, Multi_gesture_event.num_fingers)

  (* Quit events *)

  let quit = sdl_quit

  (* System window manager events *)

  let sys_wm_event = sdl_syswmevent

  (* Text events *)

  let text_editing = sdl_textediting
  let text_input = sdl_textinput

  let text_editing_window_id =
    F (text_editing_event, Text_editing_event.window_id)
  let text_editing_text = F (text_editing_event, Text_editing_event.text)
  let text_editing_start = F (text_editing_event, Text_editing_event.start)
  let text_editing_length = F (text_editing_event, Text_editing_event.length)

  let text_input_window_id = F (text_input_event, Text_input_event.window_id)
  let text_input_text = F (text_input_event, Text_input_event.text)

  (* User events *)

  let user_window_id = F (user_event, User_event.window_id)
  let user_code = F (user_event, User_event.code)
  let user_event = sdl_userevent

  (* Window events *)

  type window_event_id = int
  let window_event_shown = sdl_windowevent_shown
  let window_event_hidden = sdl_windowevent_hidden
  let window_event_exposed = sdl_windowevent_exposed
  let window_event_moved = sdl_windowevent_moved
  let window_event_resized = sdl_windowevent_resized
  let window_event_size_changed = sdl_windowevent_size_changed
  let window_event_minimized = sdl_windowevent_minimized
  let window_event_maximized = sdl_windowevent_maximized
  let window_event_restored = sdl_windowevent_restored
  let window_event_enter = sdl_windowevent_enter
  let window_event_leave = sdl_windowevent_leave
  let window_event_focus_gained = sdl_windowevent_focus_gained
  let window_event_focus_lost = sdl_windowevent_focus_lost
  let window_event_close = sdl_windowevent_close
  let window_event_take_focus = sdl_windowevent_take_focus
  let window_event_hit_test = sdl_windowevent_hit_test

  let window_window_id = F (window_event, Window_event.window_id)
  let window_event_id = F (window_event, Window_event.event)
  let window_data1 = F (window_event, Window_event.data1)
  let window_data2 = F (window_event, Window_event.data2)

  let window_event = sdl_windowevent

  (* Window event id enum *)

  type window_event_enum =
    [ `Close | `Enter | `Exposed | `Focus_gained | `Focus_lost | `Hidden
    | `Hit_test | `Leave | `Maximized | `Minimized | `Moved | `Resized
    | `Restored | `Shown | `Size_changed | `Take_focus
    | `Unknown of window_event_id ]

  let enum_of_window_event_id =
    let add acc (k, v) = Imap.add k v acc in
    let enums = [
      window_event_shown, `Shown;
      window_event_hidden, `Hidden;
      window_event_exposed, `Exposed;
      window_event_moved, `Moved;
      window_event_resized, `Resized;
      window_event_size_changed, `Size_changed;
      window_event_minimized, `Minimized;
      window_event_maximized, `Maximized;
      window_event_restored, `Restored;
      window_event_enter, `Enter;
      window_event_leave, `Leave;
      window_event_focus_gained, `Focus_gained;
      window_event_focus_lost, `Focus_lost;
      window_event_close, `Close;
      window_event_take_focus, `Take_focus;
      window_event_hit_test, `Hit_test; ]
    in
    List.fold_left add Imap.empty enums

  let window_event_enum id =
    try Imap.find id enum_of_window_event_id with Not_found -> `Unknown id

  (* Display event *)

  let display_display =
    F (display_event, Display_event.display)

  let display_event_id =
    F (display_event, Display_event.event)

  let display_data1 =
    F (display_event, Display_event.data1)

  let display_event = sdl_displayevent

  (* Sensor event *)

  let sensor_which =
    F (sensor_event, Sensor_event.which)

  let sensor_data0 =
    F (sensor_event, Sensor_event.data0)

  let sensor_data1 =
    F (sensor_event, Sensor_event.data1)

  let sensor_data2 =
    F (sensor_event, Sensor_event.data2)

  let sensor_data3 =
    F (sensor_event, Sensor_event.data3)

  let sensor_data4 =
    F (sensor_event, Sensor_event.data4)

  let sensor_data5 =
    F (sensor_event, Sensor_event.data5)

  let sensor_update = sdl_sensorupdate

  (* Render events *)

  let render_targets_reset = sdl_render_targets_reset
  let render_device_reset = sdl_render_device_reset

  (* Audio device event *)

  let audio_device_added = sdl_audiodeviceadded
  let audio_device_removed = sdl_audiodeviceremoved

  let audio_device_timestamp =
    F (audio_device_event, Audio_device_event.timestamp)

  let audio_device_which =
    F (audio_device_event, Audio_device_event.which)

  let audio_device_is_capture =
    F (audio_device_event, Audio_device_event.iscapture)

  (* Event type enum *)

  type enum =
  [ `App_did_enter_background | `App_did_enter_foreground
  | `App_low_memory | `App_terminating | `App_will_enter_background
  | `App_will_enter_foreground
  | `Audio_device_added | `Audio_device_removed
  | `Clipboard_update | `Controller_axis_motion | `Controller_button_down
  | `Controller_button_up | `Controller_device_added
  | `Controller_device_remapped | `Controller_device_removed
  | `Dollar_gesture | `Dollar_record
  | `Drop_begin | `Drop_complete | `Drop_file | `Drop_text
  | `Finger_down | `Finger_motion | `Finger_up
  | `Keymap_changed
  | `Joy_axis_motion | `Joy_ball_motion
  | `Joy_button_down | `Joy_button_up | `Joy_device_added
  | `Joy_device_removed | `Joy_hat_motion | `Key_down | `Key_up
  | `Mouse_button_down | `Mouse_button_up | `Mouse_motion
  | `Mouse_wheel | `Multi_gesture | `Quit
  | `Render_targets_reset | `Render_device_reset
  | `Sys_wm_event
  | `Text_editing | `Text_input | `Unknown of int | `User_event
  | `Window_event | `Display_event | `Sensor_update ]

  let enum_of_event_type =
    let add acc (k, v) = Imap.add k v acc in
    let enums = [ app_terminating, `App_terminating;
                  app_low_memory, `App_low_memory;
                  app_will_enter_background, `App_will_enter_background;
                  app_did_enter_background, `App_did_enter_background;
                  app_will_enter_foreground, `App_will_enter_foreground;
                  app_did_enter_foreground, `App_did_enter_foreground;
                  audio_device_added, `Audio_device_added;
                  audio_device_removed, `Audio_device_removed;
                  clipboard_update, `Clipboard_update;
                  controller_axis_motion, `Controller_axis_motion;
                  controller_button_down, `Controller_button_down;
                  controller_button_up, `Controller_button_up;
                  controller_device_added, `Controller_device_added;
                  controller_device_remapped, `Controller_device_remapped;
                  controller_device_removed, `Controller_device_removed;
                  dollar_gesture, `Dollar_gesture;
                  dollar_record, `Dollar_record;
                  drop_begin, `Drop_begin;
                  drop_complete, `Drop_complete;
                  drop_file, `Drop_file;
                  drop_text, `Drop_text;
                  finger_down, `Finger_down;
                  finger_motion, `Finger_motion;
                  finger_up, `Finger_up;
                  keymap_changed, `Keymap_changed;
                  joy_axis_motion, `Joy_axis_motion;
                  joy_ball_motion, `Joy_ball_motion;
                  joy_button_down, `Joy_button_down;
                  joy_button_up, `Joy_button_up;
                  joy_device_added, `Joy_device_added;
                  joy_device_removed, `Joy_device_removed;
                  joy_hat_motion, `Joy_hat_motion;
                  key_down, `Key_down;
                  key_up, `Key_up;
                  mouse_button_down, `Mouse_button_down;
                  mouse_button_up, `Mouse_button_up;
                  mouse_motion, `Mouse_motion;
                  mouse_wheel, `Mouse_wheel;
                  multi_gesture, `Multi_gesture;
                  render_targets_reset, `Render_targets_reset;
                  render_device_reset, `Render_device_reset;
                  sys_wm_event, `Sys_wm_event;
                  text_editing, `Text_editing;
                  text_input, `Text_input;
                  user_event, `User_event;
                  quit, `Quit;
                  window_event, `Window_event;
                  display_event, `Display_event;
                  sensor_update, `Sensor_update; ]
    in
    List.fold_left add Imap.empty enums

  let enum t = try Imap.find t enum_of_event_type with Not_found -> `Unknown t

end

type event = Event.t union

let event_state =
  foreign "SDL_EventState" (event_type @-> int @-> returning int_as_uint8_t)

let get_event_state e =
  event_state e sdl_query

let set_event_state e s =
  ignore (event_state e s)

let flush_event =
  foreign "SDL_FlushEvent" (event_type @-> returning void)

let flush_events =
  foreign "SDL_FlushEvents" (event_type @-> event_type @-> returning void)

let has_event =
  foreign "SDL_HasEvent" (event_type @-> returning bool)

let has_events =
  foreign "SDL_HasEvents" (event_type @-> event_type @-> returning bool)

let poll_event =
  foreign "SDL_PollEvent" (ptr Event.t @-> returning bool)

let poll_event e =
  poll_event (Event.opt_addr e)

let pump_events =
  foreign "SDL_PumpEvents" (void @-> returning void)

let push_event =
  foreign "SDL_PushEvent" (ptr Event.t @-> returning bool_to_ok)

let push_event e =
  push_event (addr e)

let register_events =
  foreign "SDL_RegisterEvents" (int @-> returning uint32_t)

let register_event () = match Unsigned.UInt32.to_int32 (register_events 1) with
| -1l -> None | t -> Some (Int32.to_int t)

let wait_event =
  foreign ~release_runtime_lock:true
    "SDL_WaitEvent" (ptr Event.t @-> returning int)

let wait_event e = match wait_event (Event.opt_addr e) with
| 1 -> Ok () | _ -> error ()

let wait_event_timeout =
  foreign "SDL_WaitEventTimeout" ~release_runtime_lock:true
    (ptr Event.t @-> int @-> returning bool)

let wait_event_timeout e t =
  wait_event_timeout (Event.opt_addr e) t

(* Force feedback *)

type haptic = unit ptr
let haptic : haptic typ = ptr void
let haptic_opt : haptic option typ = ptr_opt void

module Haptic = struct
  let infinity = -1l

  (* Features *)

  type feature = int
  let gain = sdl_haptic_gain
  let autocenter = sdl_haptic_autocenter
  let status = sdl_haptic_status
  let pause = sdl_haptic_pause

  (* Directions *)

  type direction_type = int
  let polar = sdl_haptic_polar
  let cartesian = sdl_haptic_cartesian
  let spherical = sdl_haptic_spherical

  module Direction = struct
    type _t
    type t = _t structure
    let t : _t structure typ = structure "SDL_HapticDirection"
    let typ = field t "type" int_as_uint8_t
    let dir_0 = field t "dir0" int32_t
    let dir_1 = field t "dir1" int32_t
    let dir_2 = field t "dir2" int32_t
    let () = seal t

    let create typv d0 d1 d2 =
      let d = make t in
      setf d typ typv;
      setf d dir_0 d0;
      setf d dir_1 d1;
      setf d dir_2 d2;
      d

    let typ d = getf d typ
    let dir_0 d = getf d dir_0
    let dir_1 d = getf d dir_1
    let dir_2 d = getf d dir_2
  end

  (* Effects *)

  module Constant = struct
    type t
    let t : t structure typ = structure "SDL_HapticConstant"
    let typ = field t "type" int_as_uint16_t
    let direction = field t "direction" Direction.t
    let length = field t "length" int32_as_uint32_t
    let delay = field t "delay" int_as_uint16_t
    let button = field t "button" int_as_uint16_t
    let interval = field t "interval" int_as_uint16_t

    let level = field t "level" int16_t
    let attack_length = field t "attack_length" int_as_uint16_t
    let attack_level = field t "attack_level" int_as_uint16_t
    let fade_length = field t "fade_length" int_as_uint16_t
    let fade_level = field t "fade_level" int_as_uint16_t
    let () = seal t
  end

  module Periodic = struct
    type t
    let t : t structure typ = structure "SDL_HapticPeriodic"
    let typ = field t "type" int_as_uint16_t
    let direction = field t "direction" Direction.t
    let length = field t "length" int32_as_uint32_t
    let delay = field t "delay" int_as_uint16_t
    let button = field t "button" int_as_uint16_t
    let interval = field t "interval" int_as_uint16_t

    let period = field t "period" int_as_uint16_t
    let magnitude = field t "magnitude" int16_t
    let offset = field t "offset" int16_t
    let phase = field t "phase" int_as_uint16_t
    let attack_length = field t "attack_length" int_as_uint16_t
    let attack_level = field t "attack_level" int_as_uint16_t
    let fade_length = field t "fade_length" int_as_uint16_t
    let fade_level = field t "fade_level" int_as_uint16_t
    let () = seal t
  end

  module Condition = struct
    type t
    let t : t structure typ = structure "SDL_HapticCondition"
    let typ = field t "type" int_as_uint16_t
    let direction = field t "direction" Direction.t
    let length = field t "length" int32_as_uint32_t
    let delay = field t "delay" int_as_uint16_t
    let button = field t "button" int_as_uint16_t
    let interval = field t "interval" int_as_uint16_t

    let right_sat_0 = field t "right_sat[0]" int_as_uint16_t
    let right_sat_1 = field t "right_sat[1]" int_as_uint16_t
    let right_sat_2 = field t "right_sat[2]" int_as_uint16_t
    let left_sat_0 = field t "left_sat[0]" int_as_uint16_t
    let left_sat_1 = field t "left_sat[1]" int_as_uint16_t
    let left_sat_2 = field t "left_sat[2]" int_as_uint16_t
    let right_coeff_0 = field t "right_coeff[0]" int16_t
    let right_coeff_1 = field t "right_coeff[1]" int16_t
    let right_coeff_2 = field t "right_coeff[2]" int16_t
    let left_coeff_0 = field t "left_coeff[0]" int16_t
    let left_coeff_1 = field t "left_coeff[1]" int16_t
    let left_coeff_2 = field t "left_coeff[2]" int16_t
    let deadband_0 = field t "deadband[0]" int_as_uint16_t
    let deadband_1 = field t "deadband[1]" int_as_uint16_t
    let deadband_2 = field t "deadband[2]" int_as_uint16_t
    let center_0 = field t "center[0]" int16_t
    let center_1 = field t "center[1]" int16_t
    let center_2 = field t "center[2]" int16_t
    let () = seal t
  end

  module Ramp = struct
    type t
    let t : t structure typ = structure "SDL_HapticRamp"
    let typ = field t "type" int_as_uint16_t
    let direction = field t "direction" Direction.t
    let length = field t "length" int32_as_uint32_t
    let delay = field t "delay" int_as_uint16_t
    let button = field t "button" int_as_uint16_t
    let interval = field t "interval" int_as_uint16_t

    let start = field t "start" int16_t
    let end_ = field t "end" int16_t
    let attack_length = field t "attack_length" int_as_uint16_t
    let attack_level = field t "attack_level" int_as_uint16_t
    let fade_length = field t "fade_length" int_as_uint16_t
    let fade_level = field t "fade_level" int_as_uint16_t
    let () = seal t
  end

  module Left_right = struct
    type t
    let t : t structure typ = structure "SDL_HapticLeftRight"
    let typ = field t "type" int_as_uint16_t
    let direction = field t "direction" Direction.t
    let length = field t "length" int32_as_uint32_t

    let large_magnitude = field t "large_magnitude" int_as_uint16_t
    let small_magnitude = field t "small_magnitude" int_as_uint16_t
    let () = seal t
  end

  module Custom = struct
    let int_list_as_uint16_t_ptr =
      let read _ = invalid_arg err_read_field in
      let write l =
        let l = List.map Unsigned.UInt16.of_int l in
        let a = CArray.of_list uint16_t l in
        CArray.start a
      in
      view ~read ~write (ptr uint16_t)

    type t
    let t : t structure typ = structure "SDL_HapticCustom"
    let typ = field t "type" int_as_uint16_t
    let direction = field t "direction" Direction.t
    let length = field t "length" int32_as_uint32_t
    let delay = field t "delay" int_as_uint16_t
    let button = field t "button" int_as_uint16_t
    let interval = field t "interval" int_as_uint16_t

    let channels = field t "channels" int_as_uint8_t
    let period = field t "period" int_as_uint16_t
    let samples = field t "samples" int_as_uint16_t
    let data = field t "data" int_list_as_uint16_t_ptr
    let attack_length = field t "attack_length" int_as_uint16_t
    let attack_level = field t "attack_level" int_as_uint16_t
    let fade_length = field t "fade_length" int_as_uint16_t
    let fade_level = field t "fade_level" int_as_uint16_t
    let () = seal t
  end

  module Effect = struct
    type t
    let t : t union typ = union "SDL_HapticEffect"
    let typ = field t "type" int_as_uint16_t
    let constant = field t "constant" Constant.t
    let periodic = field t "periodic" Periodic.t
    let condition = field t "condition" Condition.t
    let ramp = field t "ramp" Ramp.t
    let left_right = field t "condition" Left_right.t
    let custom = field t "custom" Custom.t
    let () = seal t
  end

  type effect_type = int

  let create_effect () = make Effect.t

  type _ field =
      F : (* existential to hide the 'a structure *)
        (('a structure, Effect.t union) Ctypes.field *
         ('b, 'a structure) Ctypes.field) -> 'b field

  let get e (F (s, f)) = getf (getf e s) f
  let set e (F (s, f)) v = setf (getf e s) f v
  let typ = F (Effect.constant, Constant.typ) (* same in each enum *)

  (* Constant *)
  let constant = sdl_haptic_constant

  let constant_type = F (Effect.constant, Constant.typ)
  let constant_direction = F (Effect.constant, Constant.direction)
  let constant_length = F (Effect.constant, Constant.length)
  let constant_delay = F (Effect.constant, Constant.delay)
  let constant_button = F (Effect.constant, Constant.button)
  let constant_interval = F (Effect.constant, Constant.interval)
  let constant_level = F (Effect.constant, Constant.level)
  let constant_attack_length = F (Effect.constant, Constant.attack_length)
  let constant_attack_level = F (Effect.constant, Constant.attack_level)
  let constant_fade_length = F (Effect.constant, Constant.fade_length)
  let constant_fade_level = F (Effect.constant, Constant.fade_level)

  (* Periodic *)

  let sine = sdl_haptic_sine
  let left_right = sdl_haptic_leftright
  let triangle = sdl_haptic_triangle
  let sawtooth_up = sdl_haptic_sawtoothup
  let sawtooth_down = sdl_haptic_sawtoothdown

  let periodic_type = F (Effect.periodic, Periodic.typ)
  let periodic_direction = F (Effect.periodic, Periodic.direction)
  let periodic_length = F (Effect.periodic, Periodic.length)
  let periodic_delay = F (Effect.periodic, Periodic.delay)
  let periodic_button = F (Effect.periodic, Periodic.button)
  let periodic_interval = F (Effect.periodic, Periodic.interval)
  let periodic_period = F (Effect.periodic, Periodic.period)
  let periodic_magnitude = F (Effect.periodic, Periodic.magnitude)
  let periodic_offset = F (Effect.periodic, Periodic.offset)
  let periodic_phase = F (Effect.periodic, Periodic.phase)
  let periodic_attack_length = F (Effect.periodic, Periodic.attack_length)
  let periodic_attack_level = F (Effect.periodic, Periodic.attack_level)
  let periodic_fade_length = F (Effect.periodic, Periodic.fade_length)
  let periodic_fade_level = F (Effect.periodic, Periodic.fade_level)

  (* Condition *)

  let spring = sdl_haptic_spring
  let damper = sdl_haptic_damper
  let inertia = sdl_haptic_inertia
  let friction = sdl_haptic_friction

  let condition_type = F (Effect.condition, Condition.typ)
  let condition_direction = F (Effect.condition, Condition.direction)
  let condition_length = F (Effect.condition, Condition.length)
  let condition_delay = F (Effect.condition, Condition.delay)
  let condition_button = F (Effect.condition, Condition.button)
  let condition_interval = F (Effect.condition, Condition.interval)
  let condition_right_sat_0 = F (Effect.condition, Condition.right_sat_0)
  let condition_right_sat_1 = F (Effect.condition, Condition.right_sat_1)
  let condition_right_sat_2 = F (Effect.condition, Condition.right_sat_2)
  let condition_left_sat_0 = F (Effect.condition, Condition.left_sat_0)
  let condition_left_sat_1 = F (Effect.condition, Condition.left_sat_1)
  let condition_left_sat_2 = F (Effect.condition, Condition.left_sat_2)
  let condition_right_coeff_0 = F (Effect.condition, Condition.right_coeff_0)
  let condition_right_coeff_1 = F (Effect.condition, Condition.right_coeff_1)
  let condition_right_coeff_2 = F (Effect.condition, Condition.right_coeff_2)
  let condition_left_coeff_0 = F (Effect.condition, Condition.left_coeff_0)
  let condition_left_coeff_1 = F (Effect.condition, Condition.left_coeff_1)
  let condition_left_coeff_2 = F (Effect.condition, Condition.left_coeff_2)
  let condition_deadband_0 = F (Effect.condition, Condition.deadband_0)
  let condition_deadband_1 = F (Effect.condition, Condition.deadband_1)
  let condition_deadband_2 = F (Effect.condition, Condition.deadband_2)
  let condition_center_0 = F (Effect.condition, Condition.center_0)
  let condition_center_1 = F (Effect.condition, Condition.center_1)
  let condition_center_2 = F (Effect.condition, Condition.center_2)

  (* Ramp *)

  let ramp = sdl_haptic_ramp

  let ramp_type = F (Effect.ramp, Ramp.typ)
  let ramp_direction = F (Effect.ramp, Ramp.direction)
  let ramp_length = F (Effect.ramp, Ramp.length)
  let ramp_delay = F (Effect.ramp, Ramp.delay)
  let ramp_button = F (Effect.ramp, Ramp.button)
  let ramp_interval = F (Effect.ramp, Ramp.interval)
  let ramp_start = F (Effect.ramp, Ramp.start)
  let ramp_end = F (Effect.ramp, Ramp.end_)
  let ramp_attack_length = F (Effect.ramp, Ramp.attack_length)
  let ramp_attack_level = F (Effect.ramp, Ramp.attack_level)
  let ramp_fade_length = F (Effect.ramp, Ramp.fade_length)
  let ramp_fade_level = F (Effect.ramp, Ramp.fade_level)

  (* Left right *)

  let left_right_type = F (Effect.left_right, Left_right.typ)
  let left_right_length = F (Effect.left_right, Left_right.length)
  let left_right_large_magnitude =
    F (Effect.left_right, Left_right.large_magnitude)
  let left_right_small_magnitude =
    F (Effect.left_right, Left_right.small_magnitude)

  (* Custom *)

  let custom = sdl_haptic_custom

  let custom_type = F (Effect.custom, Custom.typ)
  let custom_direction = F (Effect.custom, Custom.direction)
  let custom_length = F (Effect.custom, Custom.length)
  let custom_delay = F (Effect.custom, Custom.delay)
  let custom_button = F (Effect.custom, Custom.button)
  let custom_interval = F (Effect.custom, Custom.interval)
  let custom_channels = F (Effect.custom, Custom.channels)
  let custom_period = F (Effect.custom, Custom.period)
  let custom_samples = F (Effect.custom, Custom.samples)
  let custom_data = F (Effect.custom, Custom.data)
  let custom_attack_length = F (Effect.custom, Custom.attack_length)
  let custom_attack_level = F (Effect.custom, Custom.attack_level)
  let custom_fade_length = F (Effect.custom, Custom.fade_length)
  let custom_fade_level = F (Effect.custom, Custom.fade_level)
end

type haptic_effect = Haptic.Effect.t union

type haptic_effect_id = int
let haptic_effect_id : int typ = int

let haptic_close =
  foreign "SDL_HapticClose" (haptic @-> returning void)

let haptic_destroy_effect =
  foreign "SDL_HapticDestroyEffect"
    (haptic @-> int @-> returning void)

let haptic_effect_supported =
  foreign "SDL_HapticEffectSupported"
    (haptic @-> ptr Haptic.Effect.t @-> returning bool_to_ok)

let haptic_effect_supported h e =
  haptic_effect_supported h (addr e)

let haptic_get_effect_status =
  foreign "SDL_HapticGetEffectStatus"
    (haptic @-> haptic_effect_id @-> returning bool_to_ok)

let haptic_index =
  foreign "SDL_HapticIndex" (haptic @-> returning nat_to_ok)

let haptic_name =
  foreign "SDL_HapticName" (int @-> returning (some_to_ok string_opt))

let haptic_new_effect =
  foreign "SDL_HapticNewEffect"
    (haptic @-> ptr Haptic.Effect.t @-> returning nat_to_ok)

let haptic_new_effect h e =
  haptic_new_effect h (addr e)

let haptic_num_axes =
  foreign "SDL_HapticNumAxes" (haptic @-> returning nat_to_ok)

let haptic_num_effects =
  foreign "SDL_HapticNumEffects" (haptic @-> returning nat_to_ok)

let haptic_num_effects_playing =
  foreign "SDL_HapticNumEffectsPlaying" (haptic @-> returning nat_to_ok)

let haptic_open =
  foreign "SDL_HapticOpen" (int @-> returning (some_to_ok haptic_opt))

let haptic_open_from_joystick =
  foreign "SDL_HapticOpenFromJoystick"
  (joystick @-> returning (some_to_ok haptic_opt))

let haptic_open_from_mouse =
  foreign "SDL_HapticOpenFromMouse"
    (void @-> returning (some_to_ok haptic_opt))

let haptic_opened =
  foreign "SDL_HapticOpened" (int @-> returning int)

let haptic_opened i = match haptic_opened i with
| 0 -> false | 1 -> true | _ -> assert false

let haptic_pause =
  foreign "SDL_HapticPause" (haptic @-> returning zero_to_ok)

let haptic_query =
  foreign "SDL_HapticQuery" (haptic @-> returning int)

let haptic_rumble_init =
  foreign "SDL_HapticRumbleInit" (haptic @-> returning zero_to_ok)

let haptic_rumble_play =
  foreign "SDL_HapticRumblePlay"
    (haptic @-> float @-> int32_t @-> returning zero_to_ok)

let haptic_rumble_stop =
  foreign "SDL_HapticRumbleStop" (haptic @-> returning zero_to_ok)

let haptic_rumble_supported =
  foreign "SDL_HapticRumbleSupported" (haptic @-> returning bool_to_ok)

let haptic_run_effect =
  foreign "SDL_HapticRunEffect"
    (haptic @-> haptic_effect_id  @-> int32_t @-> returning zero_to_ok)

let haptic_set_autocenter =
  foreign "SDL_HapticSetAutocenter" (haptic @-> int @-> returning zero_to_ok)

let haptic_set_gain =
  foreign "SDL_HapticSetGain" (haptic @-> int @-> returning zero_to_ok)

let haptic_stop_all =
  foreign "SDL_HapticStopAll" (haptic @-> returning zero_to_ok)

let haptic_stop_effect =
  foreign "SDL_HapticStopEffect"
    (haptic @-> haptic_effect_id @-> returning zero_to_ok)

let haptic_unpause =
  foreign "SDL_HapticUnpause" (haptic @-> returning zero_to_ok)

let haptic_update_effect =
  foreign "SDL_HapticUpdateEffect"
    (haptic @-> haptic_effect_id @-> ptr Haptic.Effect.t @->
     returning zero_to_ok)

let haptic_update_effect h id e =
  haptic_update_effect h id (addr e)

let joystick_is_haptic =
  foreign "SDL_JoystickIsHaptic"
    (joystick @-> returning bool_to_ok)

let mouse_is_haptic =
  foreign "SDL_MouseIsHaptic" (void @-> returning bool_to_ok)

let num_haptics =
  foreign "SDL_NumHaptics" (void @-> returning nat_to_ok)

(* Audio *)

(* Audio drivers *)

let audio_init =
  foreign "SDL_AudioInit" (string_opt @-> returning zero_to_ok)

let audio_quit =
  foreign "SDL_AudioQuit" (void @-> returning void)

let get_audio_driver =
  foreign "SDL_GetAudioDriver"
    (int @-> returning (some_to_ok string_opt))

let get_current_audio_driver =
  foreign "SDL_GetCurrentAudioDriver" (void @-> returning string_opt)

let get_num_audio_drivers =
  foreign "SDL_GetNumAudioDrivers" (void @-> returning nat_to_ok)

(* Audio devices *)

module Audio = struct
  type status = int
  let stopped = sdl_audio_stopped
  let playing = sdl_audio_playing
  let paused = sdl_audio_paused

  type format = int
  let format = int_as_uint16_t
  let s8 = audio_s8
  let u8 = audio_u8
  let s16_lsb = audio_s16lsb
  let s16_msb = audio_s16msb
  let s16_sys = audio_s16sys
  let s16 = audio_s16
  let u16_lsb = audio_u16lsb
  let u16_msb = audio_u16msb
  let u16_sys = audio_u16sys
  let u16 = audio_u16
  let s32_lsb = audio_s32lsb
  let s32_msb = audio_s32msb
  let s32_sys = audio_s32sys
  let s32 = audio_s32
  let f32_lsb = audio_f32lsb
  let f32_msb = audio_f32msb
  let f32_sys = audio_f32sys
  let f32 = audio_f32

  type allow = int
  let allow = int
  let allow_frequency_change = sdl_audio_allow_frequency_change
  let allow_format_change = sdl_audio_allow_format_change
  let allow_channels_change = sdl_audio_allow_channels_change
  let allow_any_change = sdl_audio_allow_any_change
end

type audio_device_id = int32
let audio_device_id = int32_as_uint32_t

type audio_callback =
  unit Ctypes_static.ptr -> Unsigned.uint8 Ctypes_static.ptr -> int -> unit

type audio_spec =
  { as_freq : int;
    as_format : Audio.format;
    as_channels : uint8;
    as_silence : uint8;
    as_samples : uint8;
    as_size : uint32;
    as_callback : audio_callback option; }

let audio_callback kind f =
  let kind_bytes = ba_kind_byte_size kind in
  let ba_ptr_typ = access_ptr_typ_of_ba_kind kind in
  fun _ p len ->
    let p = coerce (ptr uint8_t) ba_ptr_typ p in
    let len = len / kind_bytes in
    f (bigarray_of_ptr array1 len kind p)

let as_callback =
  (ptr void @-> ptr uint8_t @-> int @-> returning void)

type _audio_spec
let audio_spec : _audio_spec structure typ = structure "SDL_AudioSpec"
let as_freq = field audio_spec "freq" int
let as_format = field audio_spec "format" Audio.format
let as_channels = field audio_spec "channels" int_as_uint8_t
let as_silence = field audio_spec "silence" int_as_uint8_t
let as_samples = field audio_spec "samples" int_as_uint16_t
let _ = field audio_spec "padding" uint16_t
let as_size = field audio_spec "size" int32_as_uint32_t
let as_callback =
  field audio_spec "callback"
    (funptr_opt ~thread_registration:true ~runtime_lock:true as_callback)

let as_userdata = field audio_spec "userdata" (ptr void)
let () = seal audio_spec

let audio_spec_of_c c =
  let as_freq = getf c as_freq in
  let as_format = getf c as_format in
  let as_channels = getf c as_channels in
  let as_silence = getf c as_silence in
  let as_samples = getf c as_samples in
  let as_size = getf c as_size in
  let as_callback = None in
  { as_freq; as_format; as_channels; as_silence; as_samples; as_size;
    as_callback; }

let audio_spec_to_c a =
  let c = make audio_spec in
  setf c as_freq a.as_freq;
  setf c as_format a.as_format;
  setf c as_channels a.as_channels;
  setf c as_silence a.as_silence; (* irrelevant *)
  setf c as_samples a.as_samples;
  setf c as_size a.as_size;       (* irrelevant *)
  setf c as_callback a.as_callback;
  setf c as_userdata null;
  c

let close_audio_device =
  foreign "SDL_CloseAudioDevice" (audio_device_id @-> returning void)

let free_wav =
  foreign "SDL_FreeWAV" (ptr void @-> returning void)

let free_wav ba =
  free_wav (to_voidp (bigarray_start array1 ba))

let get_audio_device_name =
  foreign "SDL_GetAudioDeviceName"
    (int @-> bool @-> returning (some_to_ok string_opt))

let get_audio_device_status =
  foreign "SDL_GetAudioDeviceStatus" (audio_device_id @-> returning int)

let get_num_audio_devices =
  foreign "SDL_GetNumAudioDevices" (bool @-> returning nat_to_ok)

let load_wav_rw =
  foreign ~release_runtime_lock:true "SDL_LoadWAV_RW"
    (rw_ops @-> int @-> ptr audio_spec @-> ptr (ptr void) @-> ptr uint32_t @->
     returning (some_to_ok (ptr_opt audio_spec)))

let load_wav_rw ops spec kind =
  let d = allocate (ptr void) null in
  let len = allocate uint32_t Unsigned.UInt32.zero in
  match load_wav_rw ops 0 (addr (audio_spec_to_c spec)) d len with
  | Error _ as e -> e
  | Ok r ->
      let rspec = audio_spec_of_c (!@ r) in
      let kind_size = ba_kind_byte_size kind in
      let len = Unsigned.UInt32.to_int (!@ len) in
      if len mod kind_size <> 0
      then invalid_arg (err_bigarray_data len kind_size)
      else
      let ba_size = len / kind_size in
      let ba_ptr = access_ptr_typ_of_ba_kind kind in
      let d = coerce (ptr void)  ba_ptr (!@ d) in
      Ok (rspec, bigarray_of_ptr array1 ba_size kind d)

let lock_audio_device =
  foreign "SDL_LockAudioDevice" (audio_device_id @-> returning void)

let open_audio_device =
  foreign "SDL_OpenAudioDevice"
    (string_opt @-> bool @-> ptr audio_spec @-> ptr audio_spec @->
     Audio.allow @-> returning int32_as_uint32_t)

let open_audio_device dev capture desired allow =
  let desiredc = audio_spec_to_c desired in
  let obtained = make audio_spec in
  match open_audio_device dev capture (addr desiredc) (addr obtained) allow
  with
  | id when id = Int32.zero -> error ()
  | id -> Ok (id,  audio_spec_of_c obtained)

let pause_audio_device =
  foreign "SDL_PauseAudioDevice" (audio_device_id @-> bool @-> returning void)

let unlock_audio_device =
  foreign "SDL_UnlockAudioDevice" (audio_device_id @-> returning void)

let queue_audio =
  foreign "SDL_QueueAudio"
    (audio_device_id @-> ptr void @-> int_as_uint32_t @-> returning zero_to_ok)

let queue_audio dev ba =
  let len = Bigarray.Array1.dim ba in
  let kind_size = ba_kind_byte_size (Bigarray.Array1.kind ba) in
  queue_audio dev (to_voidp (bigarray_start array1 ba)) (len * kind_size)

let dequeue_audio =
  foreign "SDL_DequeueAudio"
    (audio_device_id @-> ptr void @-> int @-> returning int_as_uint32_t)

let dequeue_audio dev ba =
  let len = Bigarray.Array1.dim ba in
  let kind_size = ba_kind_byte_size (Bigarray.Array1.kind ba) in
  dequeue_audio dev (to_voidp (bigarray_start array1 ba)) (len * kind_size)

let get_queued_audio_size =
  foreign "SDL_GetQueuedAudioSize"
    (audio_device_id @-> returning int_as_uint32_t)

let clear_queued_audio =
  foreign "SDL_ClearQueuedAudio" (audio_device_id @-> returning void)

(* Timer *)

let delay =
  foreign ~release_runtime_lock:true "SDL_Delay" (int32_t @-> returning void)

let get_ticks =
  foreign "SDL_GetTicks" (void @-> returning int32_t)

let get_ticks64 =
  foreign "SDL_GetTicks64" (void @-> returning int64_t)

let get_performance_counter =
  foreign "SDL_GetPerformanceCounter" (void @-> returning int64_t)

let get_performance_frequency =
  foreign "SDL_GetPerformanceFrequency" (void @-> returning int64_t)

(* Platform and CPU information *)

let get_platform =
  foreign "SDL_GetPlatform" (void @-> returning string)

let get_cpu_cache_line_size =
  foreign "SDL_GetCPUCacheLineSize" (void @-> returning nat_to_ok)

let get_cpu_count =
  foreign "SDL_GetCPUCount" (void @-> returning int)

let get_system_ram =
  foreign "SDL_GetSystemRAM" (void @-> returning int)

let has_3d_now =
  foreign "SDL_Has3DNow" (void @-> returning bool)

let has_altivec =
  foreign "SDL_HasAltiVec" (void @-> returning bool)

let has_avx =
  foreign ~stub "SDL_HasAVX" (void @-> returning bool)

let has_avx2 =
  foreign  "SDL_HasAVX2" (void @-> returning bool)

let has_mmx =
  foreign "SDL_HasMMX" (void @-> returning bool)

let has_neon =
  foreign "SDL_HasNEON" (void @-> returning bool)

let has_rdtsc =
  foreign "SDL_HasRDTSC" (void @-> returning bool)

let has_sse =
  foreign "SDL_HasSSE" (void @-> returning bool)

let has_sse2 =
  foreign "SDL_HasSSE2" (void @-> returning bool)

let has_sse3 =
  foreign "SDL_HasSSE3" (void @-> returning bool)

let has_sse41 =
  foreign "SDL_HasSSE41" (void @-> returning bool)

let has_sse42 =
  foreign "SDL_HasSSE42" (void @-> returning bool)

(* Power management *)

type power_state =
  [ `Unknown | `On_battery | `No_battery | `Charging | `Charged ]

let power_state =
  [ sdl_powerstate_unknown, `Unknown;
    sdl_powerstate_on_battery, `On_battery;
    sdl_powerstate_no_battery, `No_battery;
    sdl_powerstate_charging, `Charging;
    sdl_powerstate_charged, `Charged; ]

type power_info =
  { pi_state : power_state;
    pi_secs : int option;
    pi_pct : int option; }

let get_power_info =
  foreign "SDL_GetPowerInfo" ((ptr int) @-> (ptr int) @-> returning int)

let get_power_info () =
  let secs = allocate int 0 in
  let pct = allocate int 0 in
  let s = get_power_info secs pct in
  let pi_state = try List.assoc s power_state with Not_found -> assert false in
  let pi_secs = match !@ secs with -1 -> None | secs -> Some secs in
  let pi_pct = match !@ pct with -1 -> None | pct -> Some pct in
  { pi_state; pi_secs; pi_pct }
end