Compare commits
35 Commits
memes
...
6948a65a97
| Author | SHA1 | Date | |
|---|---|---|---|
| 6948a65a97 | |||
| bba26b9c0f | |||
| fcf528275b | |||
| d53f6687e5 | |||
| d46c1de49d | |||
| f0c5556450 | |||
| 97730899c6 | |||
| 18daf83c1c | |||
| dfef26fcf5 | |||
| 048ea0eab4 | |||
| 3509930195 | |||
| b1ac36ce3e | |||
| a12db025e0 | |||
| 5c10f3860a | |||
| a64fcbb010 | |||
| 7baa6f3648 | |||
| af92f03706 | |||
| cb263b5758 | |||
| 44879eb947 | |||
| 49bddb6365 | |||
| b5d846b35d | |||
| 60c83c608a | |||
| 58ec73972b | |||
| b705c598ff | |||
| 9d1ccb93b5 | |||
| 3fc8125d42 | |||
| 3b09bb1c11 | |||
| 281351371d | |||
| fec4249d9f | |||
| 65aa7ff901 | |||
| 39193ff253 | |||
| 399280d9c4 | |||
| 6a484c3a06 | |||
| 7460b8f793 | |||
| c40e725978 |
@ -1 +0,0 @@
|
||||
profile = compact
|
||||
205
backend.ml
Normal file
205
backend.ml
Normal file
@ -0,0 +1,205 @@
|
||||
module F = Fmt
|
||||
|
||||
module Key = struct
|
||||
type special =
|
||||
[ `Enter
|
||||
| `Escape
|
||||
| `Tab
|
||||
| `Arrow of [`Up | `Down | `Left | `Right]
|
||||
| `Function of int
|
||||
| `Page of [`Up | `Down]
|
||||
| `Home
|
||||
| `End
|
||||
| `Insert
|
||||
| `Delete
|
||||
| `Backspace ]
|
||||
|
||||
(** Type of key code. *)
|
||||
type code =
|
||||
[`Uchar of Uchar.t (** A unicode character. *) | special]
|
||||
|
||||
type keystate =
|
||||
{ctrl: bool; meta: bool; shift: bool; super: bool; code: code}
|
||||
|
||||
module KeyS = struct
|
||||
type t = keystate
|
||||
|
||||
let compare = compare
|
||||
end
|
||||
|
||||
module Bind = struct
|
||||
(* parts stolen from lambda-term/src/lTerm_{edit,key}.ml{,i} *)
|
||||
module S = Zed_input.Make (KeyS)
|
||||
|
||||
type 'a t = 'a list S.t
|
||||
type 'a resolver = 'a list S.resolver
|
||||
type 'a result = 'a list S.result
|
||||
|
||||
type 'a state =
|
||||
{ mutable bindings: 'a t
|
||||
; mutable state: 'a result
|
||||
; mutable last_keyseq: keystate list
|
||||
; mutable last_actions: 'a list }
|
||||
|
||||
type mods = Ctrl | Meta | Super | Shift
|
||||
type key = C of char | U of code
|
||||
|
||||
let keystate_of_mods ks m =
|
||||
List.fold_left
|
||||
(fun ks m ->
|
||||
match m with
|
||||
| Meta -> {ks with meta= true}
|
||||
| Ctrl -> {ks with ctrl= true}
|
||||
| Super -> {ks with super= true}
|
||||
| Shift -> {ks with shift= true} )
|
||||
ks m
|
||||
|
||||
let add events action bindings =
|
||||
let events =
|
||||
List.map
|
||||
(fun (m, k) ->
|
||||
keystate_of_mods
|
||||
{ meta= false
|
||||
; ctrl= false
|
||||
; super= false
|
||||
; shift= false
|
||||
; code=
|
||||
( match k with
|
||||
| C c -> `Uchar (Uchar.of_char c)
|
||||
| U c -> c ) }
|
||||
m )
|
||||
events in
|
||||
S.add events action bindings
|
||||
|
||||
let default_resolver b = S.resolver [S.pack (fun x -> x) b]
|
||||
|
||||
let get_resolver result default =
|
||||
match result with S.Continue r -> r | _ -> default
|
||||
|
||||
let init bindings =
|
||||
{bindings; state= S.Rejected; last_keyseq= []; last_actions= []}
|
||||
|
||||
let resolve = S.resolve
|
||||
let empty = S.empty
|
||||
|
||||
type action =
|
||||
| Custom of (unit -> unit)
|
||||
| CustomLwt of (unit -> unit Lwt.t)
|
||||
| Zed of Zed_edit.action
|
||||
|
||||
let resolve_events (state : 'a state) events =
|
||||
List.flatten
|
||||
(List.filter_map
|
||||
(fun e ->
|
||||
match e with
|
||||
| `Key (`Press, (k : keystate)) -> (
|
||||
( match state.state with
|
||||
| Continue _ -> ()
|
||||
| _ -> state.last_keyseq <- [] ) ;
|
||||
state.state <-
|
||||
resolve k
|
||||
(get_resolver state.state
|
||||
(default_resolver state.bindings) ) ;
|
||||
state.last_keyseq <- k :: state.last_keyseq ;
|
||||
match state.state with
|
||||
| Accepted a ->
|
||||
state.last_actions <- a ;
|
||||
Some a
|
||||
| Rejected ->
|
||||
state.last_actions <- [] ;
|
||||
None
|
||||
| _ -> None )
|
||||
| _ -> None )
|
||||
events )
|
||||
|
||||
let actions_of_events (state : action state) events =
|
||||
List.flatten
|
||||
(List.filter_map
|
||||
(fun e ->
|
||||
match e with
|
||||
| `Key (`Press, (k : keystate)) -> (
|
||||
( match state.state with
|
||||
| Continue _ -> ()
|
||||
| _ -> state.last_keyseq <- [] ) ;
|
||||
state.state <-
|
||||
resolve k
|
||||
(get_resolver state.state
|
||||
(default_resolver state.bindings) ) ;
|
||||
state.last_keyseq <- k :: state.last_keyseq ;
|
||||
match state.state with
|
||||
| Accepted a ->
|
||||
state.last_actions <- a ;
|
||||
Some a
|
||||
| Rejected ->
|
||||
state.last_actions <- [] ;
|
||||
None
|
||||
| _ -> None )
|
||||
| _ -> None )
|
||||
events )
|
||||
|
||||
let process bindstate events =
|
||||
Lwt_list.iter_s
|
||||
(function
|
||||
| Custom f -> Lwt.return (f ())
|
||||
| CustomLwt f -> f ()
|
||||
| Zed _ -> Lwt.return_unit )
|
||||
(actions_of_events bindstate events)
|
||||
end
|
||||
|
||||
(* stolen from lambda-term/src/lTerm_{edit,key}.ml{,i} *)
|
||||
let string_of_code = function
|
||||
| `Uchar ch ->
|
||||
if Uchar.is_char ch then F.str "Char '%c'" (Uchar.to_char ch)
|
||||
else F.str "Char 0x%02x" (Uchar.to_int ch)
|
||||
| `Enter -> "Enter"
|
||||
| `Escape -> "Escape"
|
||||
| `Tab -> "Tab"
|
||||
| `Arrow `Up -> "Up"
|
||||
| `Arrow `Down -> "Down"
|
||||
| `Arrow `Left -> "Left"
|
||||
| `Arrow `Right -> "Right"
|
||||
| `Function i -> F.str "F%d" i
|
||||
| `Page `Up -> "Page Up"
|
||||
| `Page `Down -> "Page Down"
|
||||
| `Home -> "Home"
|
||||
| `End -> "End"
|
||||
| `Insert -> "Insert"
|
||||
| `Delete -> "Delete"
|
||||
| `Backspace -> "Backspace"
|
||||
|
||||
let to_string key =
|
||||
Printf.sprintf
|
||||
"{ control = %B; meta = %B; shift = %B; super = %B; code = %s }"
|
||||
key.ctrl key.meta key.shift key.super
|
||||
(string_of_code key.code)
|
||||
|
||||
let to_string_compact key =
|
||||
let buffer = Buffer.create 32 in
|
||||
if key.ctrl then Buffer.add_string buffer "Ctrl-" ;
|
||||
if key.meta then Buffer.add_string buffer "Meta-" ;
|
||||
if key.shift then Buffer.add_string buffer "Shift-" ;
|
||||
if key.super then Buffer.add_string buffer "Super-" ;
|
||||
( match key.code with
|
||||
| `Uchar ch ->
|
||||
let code = Uchar.to_int ch in
|
||||
if Uchar.is_char ch then
|
||||
match Uchar.to_char ch with
|
||||
| ( 'a' .. 'z'
|
||||
| 'A' .. 'Z'
|
||||
| '0' .. '9'
|
||||
| '_' | '(' | ')' | '[' | ']' | '{' | '}' | '#' | '~'
|
||||
| '&' | '$' | '*' | '%' | '!' | '?' | ',' | ';' | ':'
|
||||
| '/' | '\\' | '.' | '@' | '=' | '+' | '-' ) as ch ->
|
||||
Buffer.add_char buffer ch
|
||||
| ' ' -> Buffer.add_string buffer "space"
|
||||
| _ -> Printf.bprintf buffer "U+%02x" code
|
||||
else if code <= 0xffff then
|
||||
Printf.bprintf buffer "U+%04x" code
|
||||
else Printf.bprintf buffer "U+%06x" code
|
||||
| `Page `Down -> Buffer.add_string buffer "pgup"
|
||||
| `Page `Up -> Buffer.add_string buffer "pgdn"
|
||||
| code ->
|
||||
Buffer.add_string buffer
|
||||
(String.lowercase_ascii (string_of_code code)) ) ;
|
||||
Buffer.contents buffer
|
||||
end
|
||||
7
backend_js.ml
Normal file
7
backend_js.ml
Normal file
@ -0,0 +1,7 @@
|
||||
include Backend
|
||||
|
||||
module Keycode = struct
|
||||
open Js_of_ocaml
|
||||
|
||||
type t = Dom_html.Keyboard_code.t
|
||||
end
|
||||
438
backend_sdl.ml
Normal file
438
backend_sdl.ml
Normal file
@ -0,0 +1,438 @@
|
||||
module Key = struct
|
||||
let sdlkey_map = Hashtbl.create 1024
|
||||
|
||||
let () =
|
||||
let aa (x : int) (y : Key.code) = Hashtbl.add sdlkey_map x y in
|
||||
aa return `Enter ;
|
||||
aa escape `Escape ;
|
||||
aa backspace `Backspace ;
|
||||
aa tab `Tab ;
|
||||
aa f1 (`Function 1) ;
|
||||
aa f2 (`Function 2) ;
|
||||
aa f3 (`Function 3) ;
|
||||
aa f4 (`Function 4) ;
|
||||
aa f5 (`Function 5) ;
|
||||
aa f6 (`Function 6) ;
|
||||
aa f7 (`Function 7) ;
|
||||
aa f8 (`Function 8) ;
|
||||
aa f9 (`Function 9) ;
|
||||
aa f10 (`Function 10) ;
|
||||
aa f11 (`Function 11) ;
|
||||
aa f12 (`Function 12) ;
|
||||
aa insert `Insert ;
|
||||
aa delete `Delete ;
|
||||
aa home `Home ;
|
||||
aa kend `End ;
|
||||
aa pageup (`Page `Up) ;
|
||||
aa pagedown (`Page `Down) ;
|
||||
aa right (`Arrow `Right) ;
|
||||
aa left (`Arrow `Left) ;
|
||||
aa down (`Arrow `Down) ;
|
||||
aa up (`Arrow `Up)
|
||||
|
||||
let key_of_sdlkey ev =
|
||||
let (kc : Sdl.keycode) =
|
||||
Sdl.Event.get ev Sdl.Event.keyboard_keycode
|
||||
land lnot Sdl.K.scancode_mask in
|
||||
match (Hashtbl.find_opt sdlkey_map kc, Uchar.is_valid kc) with
|
||||
| Some s, _ -> Some s
|
||||
| None, true -> Some (`Uchar (Uchar.of_int kc))
|
||||
| None, false -> None
|
||||
|
||||
let event_of_sdlevent ev : t option =
|
||||
match Sdl.Event.enum (Sdl.Event.get ev Sdl.Event.typ) with
|
||||
| (`Key_down | `Key_up) as d -> (
|
||||
match key_of_sdlkey ev with
|
||||
| None -> None
|
||||
| Some code ->
|
||||
let km = Sdl.Event.get ev Sdl.Event.keyboard_keymod in
|
||||
Some
|
||||
(`Key
|
||||
( ( match d with
|
||||
| _
|
||||
when Sdl.Event.get ev Sdl.Event.keyboard_repeat > 1
|
||||
->
|
||||
`Repeat
|
||||
| `Key_up -> `Release
|
||||
| _ -> `Press )
|
||||
, { code
|
||||
; ctrl= km land Sdl.Kmod.ctrl > 0
|
||||
; meta= km land Sdl.Kmod.alt > 0
|
||||
; super= km land Sdl.Kmod.gui > 0
|
||||
; shift= km land Sdl.Kmod.shift > 0 } ) ) )
|
||||
| `Mouse_motion ->
|
||||
let x, y = snd (Tsdl.Sdl.get_mouse_state ()) in
|
||||
Some (`Mouse (V2.v (float x) (float y)))
|
||||
| `Quit -> Some `Quit
|
||||
(* Unhandled events *)
|
||||
| `Text_editing -> Some (`Unknown "`Text_editing")
|
||||
| `Text_input -> Some (`Unknown "`Text_input")
|
||||
| `App_did_enter_background ->
|
||||
Some (`Unknown "`App_did_enter_background")
|
||||
| `App_did_enter_foreground ->
|
||||
Some (`Unknown "`App_did_enter_foreground ")
|
||||
| `App_low_memory -> Some (`Unknown "`App_low_memory ")
|
||||
| `App_terminating -> Some (`Unknown "`App_terminating ")
|
||||
| `App_will_enter_background ->
|
||||
Some (`Unknown "`App_will_enter_background ")
|
||||
| `App_will_enter_foreground ->
|
||||
Some (`Unknown "`App_will_enter_foreground ")
|
||||
| `Clipboard_update -> Some (`Unknown "`Clipboard_update ")
|
||||
| `Controller_axis_motion ->
|
||||
Some (`Unknown "`Controller_axis_motion ")
|
||||
| `Controller_button_down ->
|
||||
Some (`Unknown "`Controller_button_down ")
|
||||
| `Controller_button_up -> Some (`Unknown "`Controller_button_up ")
|
||||
| `Controller_device_added ->
|
||||
Some (`Unknown "`Controller_device_added ")
|
||||
| `Controller_device_remapped ->
|
||||
Some (`Unknown "`Controller_device_remapped ")
|
||||
| `Controller_device_removed ->
|
||||
Some (`Unknown "`Controller_device_removed ")
|
||||
| `Dollar_gesture -> Some (`Unknown "`Dollar_gesture ")
|
||||
| `Dollar_record -> Some (`Unknown "`Dollar_record ")
|
||||
| `Drop_file -> Some (`Unknown "`Drop_file ")
|
||||
| `Finger_down -> Some (`Unknown "`Finger_down")
|
||||
| `Finger_motion -> Some (`Unknown "`Finger_motion ")
|
||||
| `Finger_up -> Some (`Unknown "`Finger_up ")
|
||||
| `Joy_axis_motion -> Some (`Unknown "`Joy_axis_motion ")
|
||||
| `Joy_ball_motion -> Some (`Unknown "`Joy_ball_motion ")
|
||||
| `Joy_button_down -> Some (`Unknown "`Joy_button_down ")
|
||||
| `Joy_button_up -> Some (`Unknown "`Joy_button_up ")
|
||||
| `Joy_device_added -> Some (`Unknown "`Joy_device_added ")
|
||||
| `Joy_device_removed -> Some (`Unknown "`Joy_device_removed ")
|
||||
| `Joy_hat_motion -> Some (`Unknown "`Joy_hat_motion ")
|
||||
| `Mouse_button_down -> Some (`Unknown "`Mouse_button_down ")
|
||||
| `Mouse_button_up -> Some (`Unknown "`Mouse_button_up")
|
||||
| `Mouse_wheel -> Some (`Unknown "`Mouse_wheel ")
|
||||
| `Multi_gesture -> Some (`Unknown "`Multi_gesture")
|
||||
| `Sys_wm_event -> Some (`Unknown "`Sys_wm_event ")
|
||||
| `Unknown e -> Some (`Unknown (Format.sprintf "`Unknown %d" e))
|
||||
| `User_event -> Some (`Unknown "`User_event ")
|
||||
| `Display_event -> Some (`Unknown "`Display_event ")
|
||||
| `Sensor_update -> Some (`Unknown "`Sensor_update ")
|
||||
| `Window_event -> Some (`Unknown "`Window_event ")
|
||||
|
||||
let key_up : Sdl.keycode = 0x40000052
|
||||
let key_down : Sdl.keycode = 0x40000051
|
||||
let key_left : Sdl.keycode = 0x40000050
|
||||
let key_right : Sdl.keycode = 0x4000004f
|
||||
end
|
||||
|
||||
module Display = struct
|
||||
open Tgles2
|
||||
open Tsdl
|
||||
open Gg
|
||||
open Wall
|
||||
module I = Image
|
||||
module P = Path
|
||||
|
||||
let ( >>>= ) x f =
|
||||
match x with Ok a -> f a | Error _ as result -> result
|
||||
|
||||
let get_result = function
|
||||
| Ok x -> x
|
||||
| Error (`Msg msg) -> failwith msg
|
||||
|
||||
(* current window state to be passed to window renderer *)
|
||||
type state =
|
||||
{ box: box2
|
||||
(* This is cannonically box within which the next element should draw *)
|
||||
; time: float
|
||||
; wall: Wall.renderer }
|
||||
|
||||
(* the box2 here is cannonically the place the returner drew
|
||||
(the Wall.image extents) *)
|
||||
type image = box2 * Wall.image
|
||||
type pane = state -> state * image
|
||||
type actor = (Event.events -> pane Lwt.t) ref
|
||||
|
||||
let pane_empty s =
|
||||
(s, (Box2.of_pts (Box2.o s.box) (Box2.o s.box), Image.empty))
|
||||
|
||||
type frame =
|
||||
{ sdl_win: Sdl.window
|
||||
; gl: Sdl.gl_context
|
||||
; wall: Wall.renderer
|
||||
; mutable last_pane: pane
|
||||
; mutable quit: bool
|
||||
; mutable fullscreen: bool }
|
||||
|
||||
let ticks () = Int32.to_float (Sdl.get_ticks ()) /. 1000.
|
||||
|
||||
let on_failure ~cleanup result =
|
||||
(match result with Ok _ -> () | Error _ -> cleanup ()) ;
|
||||
result
|
||||
|
||||
let video_initialized = lazy (Sdl.init Sdl.Init.video)
|
||||
|
||||
let make_frame ?(title = "komm") ~w ~h () =
|
||||
Lazy.force video_initialized
|
||||
>>>= fun () ->
|
||||
Sdl.create_window ~w ~h title
|
||||
Sdl.Window.(
|
||||
opengl + allow_highdpi + resizable (*+ input_grabbed*))
|
||||
>>>= fun sdl_win ->
|
||||
Sdl.set_window_title sdl_win title ;
|
||||
ignore (Sdl.gl_set_swap_interval (-1)) ;
|
||||
ignore (Sdl.gl_set_attribute Sdl.Gl.stencil_size 1) ;
|
||||
on_failure
|
||||
( Sdl.gl_create_context sdl_win
|
||||
>>>= fun gl ->
|
||||
let wall =
|
||||
Wall.Renderer.create ~antialias:true ~stencil_strokes:true ()
|
||||
in
|
||||
Ok
|
||||
{ sdl_win
|
||||
; gl
|
||||
; wall
|
||||
; quit= false
|
||||
; fullscreen= false
|
||||
; last_pane= pane_empty } )
|
||||
~cleanup:(fun () -> Sdl.destroy_window sdl_win)
|
||||
|
||||
let handle_frame_events frame events =
|
||||
List.iter
|
||||
(fun (e : Event.t) ->
|
||||
match e with
|
||||
| `Quit -> frame.quit <- true
|
||||
| `Fullscreen a ->
|
||||
frame.fullscreen <- a ;
|
||||
ignore (Sdl.show_cursor (not frame.fullscreen) : _ result) ;
|
||||
ignore
|
||||
( Sdl.set_window_fullscreen frame.sdl_win
|
||||
( if frame.fullscreen then
|
||||
Sdl.Window.fullscreen_desktop
|
||||
else Sdl.Window.windowed )
|
||||
: _ result )
|
||||
| _ -> () )
|
||||
events
|
||||
|
||||
let draw_pane frame pane =
|
||||
let width, height = Sdl.gl_get_drawable_size frame.sdl_win in
|
||||
let _, (_, image) =
|
||||
pane
|
||||
{ box= Box2.v (P2.v 0. 0.) (P2.v (float width) (float height))
|
||||
; time= ticks ()
|
||||
; wall= frame.wall } in
|
||||
Sdl.gl_make_current frame.sdl_win frame.gl
|
||||
>>>= fun () ->
|
||||
Gl.viewport 0 0 width height ;
|
||||
Gl.clear_color 0.0 0.0 0.0 1.0 ;
|
||||
Gl.(
|
||||
clear
|
||||
(color_buffer_bit lor depth_buffer_bit lor stencil_buffer_bit)) ;
|
||||
Gl.enable Gl.blend ;
|
||||
Gl.blend_func_separate Gl.one Gl.src_alpha Gl.one
|
||||
Gl.one_minus_src_alpha ;
|
||||
Gl.enable Gl.cull_face_enum ;
|
||||
Gl.disable Gl.depth_test ;
|
||||
let width = float width and height = float height in
|
||||
Wall.Renderer.render frame.wall ~width ~height image ;
|
||||
Sdl.gl_swap_window frame.sdl_win ;
|
||||
Ok ()
|
||||
|
||||
let rec get_events () : Event.t list =
|
||||
(* create and fill event list *)
|
||||
let ev = Sdl.Event.create () in
|
||||
if Sdl.poll_event (Some ev) then
|
||||
match Event.event_of_sdlevent ev with
|
||||
| Some e -> get_events () @ [e]
|
||||
| None -> get_events ()
|
||||
else []
|
||||
|
||||
let successful_actor = ref (fun _ -> Lwt.return pane_empty)
|
||||
|
||||
let display_frame frame (actor : actor) =
|
||||
let events = get_events () in
|
||||
handle_frame_events frame events ;
|
||||
if List.length events > 0 then (
|
||||
(* recompute the actor definition with the new events to return a new pane *)
|
||||
( try
|
||||
!actor events
|
||||
>|= fun p ->
|
||||
successful_actor := !actor ;
|
||||
p
|
||||
with e ->
|
||||
F.epr
|
||||
"Display.display_frame (!actor events) failed with:@. %s \
|
||||
@."
|
||||
(Printexc.to_string e) ;
|
||||
actor := !successful_actor ;
|
||||
!actor events )
|
||||
>>= fun p ->
|
||||
frame.last_pane <- p ;
|
||||
(* call draw_pane because we should redraw now that we have updated *)
|
||||
ignore (draw_pane frame frame.last_pane) ;
|
||||
Lwt.return_unit )
|
||||
else Lwt.return_unit
|
||||
|
||||
let run frame actor () =
|
||||
let frame = get_result frame in
|
||||
Sdl.show_window frame.sdl_win ;
|
||||
let rec loop () =
|
||||
Lwt.pause () (* seems required for the irc connection to work *)
|
||||
>>= fun () ->
|
||||
Lwt_unix.sleep 0.030
|
||||
>>= fun () ->
|
||||
display_frame frame actor
|
||||
>>= fun () ->
|
||||
if not frame.quit then loop () else Lwt.return_unit in
|
||||
Lwt_main.run (loop ()) ;
|
||||
print_endline "quit" ;
|
||||
Sdl.hide_window frame.sdl_win ;
|
||||
Sdl.gl_delete_context frame.gl ;
|
||||
Sdl.destroy_window frame.sdl_win ;
|
||||
Sdl.quit () ;
|
||||
()
|
||||
|
||||
let gray ?(a = 1.0) v = Color.v v v v a
|
||||
|
||||
module FontCache = Map.Make (String)
|
||||
|
||||
let font_cache = ref FontCache.empty
|
||||
|
||||
let load_font name =
|
||||
match FontCache.find_opt name !font_cache with
|
||||
| Some font -> font
|
||||
| None -> (
|
||||
let ic = open_in_bin name in
|
||||
let dim = in_channel_length ic in
|
||||
let fd = Unix.descr_of_in_channel ic in
|
||||
let buffer =
|
||||
Unix.map_file fd Bigarray.int8_unsigned Bigarray.c_layout
|
||||
false [|dim|]
|
||||
|> Bigarray.array1_of_genarray in
|
||||
let offset = List.hd (Stb_truetype.enum buffer) in
|
||||
match Stb_truetype.init buffer offset with
|
||||
| None -> assert false
|
||||
| Some font ->
|
||||
font_cache := FontCache.add name font !font_cache ;
|
||||
font )
|
||||
|
||||
let font_icons = lazy (load_font "fonts/entypo.ttf")
|
||||
let font_sans = lazy (load_font "fonts/Roboto-Regular.ttf")
|
||||
let font_sans_bold = lazy (load_font "fonts/Roboto-Bold.ttf")
|
||||
let font_sans_light = lazy (load_font "fonts/Roboto-Light.ttf")
|
||||
let font_sans_italic = lazy (load_font "fonts/Roboto-Italic.ttf")
|
||||
|
||||
let font_sans_bold_italic =
|
||||
lazy (load_font "fonts/Roboto-BoldItalic.ttf")
|
||||
|
||||
let font_serif =
|
||||
lazy (load_font "fonts/ScheherazadeNew-Regular.ttf")
|
||||
|
||||
let font_serif_bold =
|
||||
lazy (load_font "fonts/ScheherazadeNew-Bold.ttf")
|
||||
|
||||
let font_mono = lazy (load_font "fonts/static/RobotoMono-Regular")
|
||||
|
||||
let font_mono_bold =
|
||||
lazy (load_font "fonts/static/RobotoMono-Regular")
|
||||
|
||||
let font_mono_light =
|
||||
lazy (load_font "fonts/static/RobotoMono-Regular")
|
||||
|
||||
let font_emoji = lazy (load_font "fonts/NotoEmoji-Regular.ttf")
|
||||
|
||||
let str_of_box b =
|
||||
Printf.sprintf "(ox:%0.1f oy:%0.1f ex%0.1f ey%0.1f)" (Box2.ox b)
|
||||
(Box2.oy b) (Box2.maxx b) (Box2.maxy b)
|
||||
|
||||
let draw_label text b =
|
||||
let f =
|
||||
Wall_text.Font.make ~size:(Box2.h b) (Lazy.force font_sans)
|
||||
in
|
||||
( Box2.v (Box2.o b)
|
||||
(P2.v (Wall_text.Font.text_width f text) (Box2.h b))
|
||||
, I.paint
|
||||
(Paint.color (gray ~a:0.5 1.0))
|
||||
Wall_text.(
|
||||
simple_text f ~valign:`BASELINE ~halign:`LEFT ~x:(Box2.ox b)
|
||||
~y:(Box2.oy b +. (Box2.h b *. 0.75))
|
||||
text) )
|
||||
|
||||
let fill_box c b =
|
||||
( b
|
||||
, I.paint (Paint.color c)
|
||||
( I.fill_path
|
||||
@@ fun t ->
|
||||
P.rect t ~x:(Box2.ox b) ~y:(Box2.oy b) ~w:(Box2.w b)
|
||||
~h:(Box2.h b) ) )
|
||||
|
||||
let draw_filled_box c (s : state) = (s, fill_box c s.box)
|
||||
|
||||
let path_box c b (s : state) =
|
||||
( s
|
||||
, ( b
|
||||
, I.paint (Paint.color c)
|
||||
( I.stroke_path (Outline.make ())
|
||||
@@ fun t ->
|
||||
P.rect t ~x:(Box2.ox b) ~y:(Box2.oy b) ~w:(Box2.w b)
|
||||
~h:(Box2.h b) ) ) )
|
||||
|
||||
let path_circle c b (s : state) =
|
||||
( s
|
||||
, ( b
|
||||
, I.paint (Paint.color c)
|
||||
( I.stroke_path (Outline.make ())
|
||||
@@ fun t ->
|
||||
P.circle t ~cx:(Box2.midx b) ~cy:(Box2.midy b)
|
||||
~r:(Box2.w b /. 2.) ) ) )
|
||||
|
||||
(** Display.state.box as supplied to a widget defines the allowed drawing area for the widget.
|
||||
This way basic widgets will just expand to the full area of a box, while other widgets can have
|
||||
the express purpose of limiting the size of an object in a larger system of limitations.
|
||||
|
||||
Panes return a tuple: (state, (box, image))
|
||||
state is the updated state, where state.box is always
|
||||
- the top left corner of the box the pane drew in, and
|
||||
- the bottom right corner of the state.box that was passed in
|
||||
box is the area the widget actually drew in (or wants to sort of "use")
|
||||
image is the Wall.image to compose with other panes and draw to the display
|
||||
*)
|
||||
|
||||
let simple_text f text (s : state) =
|
||||
let fm = Wall_text.Font.font_metrics f in
|
||||
let font_height = fm.ascent -. fm.descent +. fm.line_gap in
|
||||
let tm = Wall_text.Font.text_measure f text in
|
||||
let br_pt =
|
||||
P2.v (Box2.ox s.box +. tm.width) (Box2.oy s.box +. font_height)
|
||||
in
|
||||
let bextent = Box2.of_pts (Box2.o s.box) br_pt in
|
||||
(* let _, (_, redbox) = path_box Color.red bextent s in*)
|
||||
( {s with box= Box2.of_pts (Box2.br_pt bextent) (Box2.max s.box)}
|
||||
, ( bextent
|
||||
, (* I.stack redbox *)
|
||||
I.paint
|
||||
(Paint.color (gray ~a:0.5 1.0))
|
||||
Wall_text.(
|
||||
simple_text f ~valign:`BASELINE ~halign:`LEFT
|
||||
~x:(Box2.ox s.box)
|
||||
~y:(Box2.oy s.box +. fm.ascent)
|
||||
text) ) )
|
||||
|
||||
let pane_box next_point_func (subpanes : pane list) (so : state) =
|
||||
let sr, (br, ir) =
|
||||
List.fold_left
|
||||
(fun (sp, (bp, ip)) (pane : pane) ->
|
||||
(* uses br to hold max extent of boxes *)
|
||||
let sr, (br, ir) = pane sp in
|
||||
(* draw the pane *)
|
||||
let _, (_, irb) = path_box Color.blue br sr in
|
||||
(* draw the box around the pane *)
|
||||
( { sr with
|
||||
box= Box2.of_pts (next_point_func br) (Box2.max sp.box)
|
||||
}
|
||||
, ( Box2.of_pts (Box2.o bp)
|
||||
(P2.v
|
||||
(max (Box2.maxx br) (Box2.maxx bp))
|
||||
(max (Box2.maxy br) (Box2.maxy bp)) )
|
||||
, Image.seq [ip; irb; ir] ) ) )
|
||||
( so
|
||||
, (Box2.of_pts (Box2.o so.box) (Box2.o so.box), Image.empty)
|
||||
)
|
||||
subpanes in
|
||||
let _, (_, redbox) = path_box Color.red br sr in
|
||||
(sr, (br, Image.stack redbox ir))
|
||||
end
|
||||
122
boot_js.ml
Normal file
122
boot_js.ml
Normal file
@ -0,0 +1,122 @@
|
||||
open Js_of_ocaml
|
||||
open Lwt.Infix
|
||||
module NVG = Graphv_webgl
|
||||
|
||||
let _ =
|
||||
Logs.set_reporter (Human.Logs_reporter.console_reporter ());
|
||||
Logs.set_level (Some Debug)
|
||||
|
||||
module Log = (val Logs.src_log Logs.default : Logs.LOG)
|
||||
|
||||
(* This scales the canvas to match the DPI of the window,
|
||||
it prevents blurriness when rendering to the canvas *)
|
||||
let scale_canvas (canvas : Dom_html.canvasElement Js.t) =
|
||||
let dpr = Dom_html.window##.devicePixelRatio in
|
||||
let rect = canvas##getBoundingClientRect in
|
||||
let width = rect##.right -. rect##.left in
|
||||
let height = rect##.bottom -. rect##.top in
|
||||
canvas##.width := width *. dpr |> int_of_float;
|
||||
canvas##.height := height *. dpr |> int_of_float;
|
||||
let width =
|
||||
Printf.sprintf "%dpx" (int_of_float width) |> Js.string
|
||||
in
|
||||
let height =
|
||||
Printf.sprintf "%dpx" (int_of_float height) |> Js.string
|
||||
in
|
||||
canvas##.style##.width := width;
|
||||
canvas##.style##.height := height
|
||||
|
||||
let webgl_initialize canvas =
|
||||
scale_canvas canvas;
|
||||
(* Graphv requires a stencil buffer to work properly *)
|
||||
let attrs = WebGL.defaultContextAttributes in
|
||||
attrs##.stencil := Js._true;
|
||||
match
|
||||
WebGL.getContextWithAttributes canvas attrs |> Js.Opt.to_option
|
||||
with
|
||||
| None ->
|
||||
print_endline "Sorry your browser does not support WebGL";
|
||||
raise Exit
|
||||
| Some ctx -> ctx
|
||||
|
||||
let graphv_initialize webgl_ctx =
|
||||
let open NVG in
|
||||
let vg =
|
||||
create
|
||||
~flags:CreateFlags.(antialias lor stencil_strokes)
|
||||
webgl_ctx
|
||||
in
|
||||
(* File in this case is actually the CSS font name *)
|
||||
Text.create vg ~name:"sans" ~file:"sans" |> ignore;
|
||||
webgl_ctx##clearColor 0.3 0.3 0.32 1.;
|
||||
vg
|
||||
|
||||
let request_animation_frame () =
|
||||
let t, s = Lwt.wait () in
|
||||
let (_ : Dom_html.animation_frame_request_id) =
|
||||
Dom_html.window##requestAnimationFrame
|
||||
(Js.wrap_callback (fun (time : float) -> Lwt.wakeup s time))
|
||||
in
|
||||
t
|
||||
|
||||
let render_stream canvas webgl_ctx vg
|
||||
(render : NVG.t -> ?time:float -> Gg.p2 -> Human.I.t -> unit) :
|
||||
Human.I.t Lwt_stream.t -> unit Lwt.t =
|
||||
Lwt_stream.iter_n (fun i ->
|
||||
request_animation_frame () >>= fun time ->
|
||||
webgl_ctx##clear
|
||||
(webgl_ctx##._COLOR_BUFFER_BIT_
|
||||
lor webgl_ctx##._DEPTH_BUFFER_BIT_
|
||||
lor webgl_ctx##._STENCIL_BUFFER_BIT_);
|
||||
let device_ratio = Dom_html.window##.devicePixelRatio in
|
||||
NVG.begin_frame vg ~width:canvas##.width ~height:canvas##.height
|
||||
~device_ratio;
|
||||
NVG.Transform.scale vg ~x:device_ratio ~y:device_ratio;
|
||||
render vg ~time Gg.P2.o i;
|
||||
NVG.end_frame vg;
|
||||
Lwt.return_unit)
|
||||
|
||||
open Human
|
||||
|
||||
let _ =
|
||||
let canvas =
|
||||
Js.Unsafe.coerce (Dom_html.getElementById_exn "canvas")
|
||||
in
|
||||
let webgl_ctx = webgl_initialize canvas in
|
||||
let vg = graphv_initialize webgl_ctx in
|
||||
let open Js_of_ocaml_lwt.Lwt_js_events in
|
||||
let open Nottui in
|
||||
let gravity_pad = Gravity.make ~h:`Negative ~v:`Negative in
|
||||
let gravity_crop = Gravity.make ~h:`Positive ~v:`Negative in
|
||||
let body = Lwd.var (Lwd.pure Ui.empty) in
|
||||
let wm = Widgets.window_manager (Lwd.join (Lwd.get body)) in
|
||||
Nav.test_pull () >>= fun test_store ->
|
||||
let ui = Widgets.(h_node_area (test_store, [ [] ])) in
|
||||
let root =
|
||||
Lwd.set body
|
||||
(Lwd.map ~f:(Ui.resize ~pad:gravity_pad ~crop:gravity_crop) ui);
|
||||
Widgets.window_manager_view wm
|
||||
in
|
||||
|
||||
let events, push_event = Lwt_stream.create () in
|
||||
let images =
|
||||
Human.Nottui_lwt.render vg
|
||||
~size:(Gg.P2.v canvas##.width canvas##.height)
|
||||
events root
|
||||
in
|
||||
async (fun () ->
|
||||
render_stream canvas webgl_ctx vg
|
||||
(fun vg ?(time = 0.) p i ->
|
||||
let _ = time in
|
||||
Log.debug (fun m ->
|
||||
m "Drawing image: p=%a n=%a" Gg.V2.pp p
|
||||
(I.Draw.pp ~attr:A.dark)
|
||||
i);
|
||||
let p' = I.Draw.node vg A.dark p i in
|
||||
Logs.debug (fun m ->
|
||||
m "Drawing finished: p'=%a" Gg.V2.pp p'))
|
||||
images);
|
||||
buffered_loop (make_event Dom_html.Event.keydown) Dom_html.document
|
||||
(fun ev _ ->
|
||||
Lwt.return
|
||||
@@ push_event (Some (`Key (Event_js.evt_of_jskey ev))))
|
||||
7
cors_proxy.sh
Executable file
7
cors_proxy.sh
Executable file
@ -0,0 +1,7 @@
|
||||
#!/bin/bash
|
||||
if [ ! -f /tmp/key.pem ]; then
|
||||
echo Creating key
|
||||
openssl req -newkey rsa:2048 -new -nodes -x509 -days 3650 -keyout /tmp/key.pem -out /tmp/cert.pem -batch
|
||||
fi
|
||||
|
||||
npx http-server --cors -S -P https://gitea.departmentofinter.net --log-ip -c-1 -C /tmp/cert.pem -K /tmp/key.pem
|
||||
94
dune
94
dune
@ -1,78 +1,30 @@
|
||||
(env
|
||||
(dev
|
||||
(flags (:standard -warn-error -A))))
|
||||
|
||||
(library
|
||||
(name human)
|
||||
(modes byte)
|
||||
(modules human)
|
||||
(libraries
|
||||
topinf
|
||||
lwt_ppx
|
||||
tsdl
|
||||
tgls.tgles2
|
||||
wall
|
||||
zed
|
||||
lambda-term
|
||||
irmin-unix
|
||||
nottui
|
||||
nottui-pretty
|
||||
uuseg.string
|
||||
uutf
|
||||
uucp
|
||||
ocaml-compiler-libs.common
|
||||
ocaml-compiler-libs.bytecomp
|
||||
ocaml-compiler-libs.toplevel))
|
||||
|
||||
(dev (flags (:standard -warn-error -A))
|
||||
(js_of_ocaml (flags --no-inline --pretty --source-map-inline --debug-info)
|
||||
(build_runtime_flags --no-inline --pretty --source-map-inline --debug-info)
|
||||
(link_flags --source-map-inline))))
|
||||
|
||||
(executable
|
||||
(name irc)
|
||||
(modes byte)
|
||||
(modules irc)
|
||||
(name boot_js)
|
||||
(modes byte js)
|
||||
(preprocess (pps js_of_ocaml-ppx))
|
||||
|
||||
(modules boot_js human)
|
||||
(libraries
|
||||
human
|
||||
lwt
|
||||
fmt
|
||||
topinf
|
||||
lwt_ppx
|
||||
irc-client
|
||||
irc-client-lwt
|
||||
irc-client-unix
|
||||
irc-client-tls
|
||||
nottui-lwt
|
||||
nottui-pretty
|
||||
logs
|
||||
graphv_webgl
|
||||
js_of_ocaml-lwt
|
||||
digestif.ocaml
|
||||
checkseum.ocaml
|
||||
irmin.mem
|
||||
git
|
||||
irmin-git
|
||||
cohttp-lwt-jsoo
|
||||
mimic
|
||||
uri
|
||||
zed
|
||||
gg
|
||||
lwd
|
||||
))
|
||||
|
||||
(executable
|
||||
(name boot)
|
||||
(modes byte)
|
||||
(modules boot)
|
||||
(link_flags (-linkall))
|
||||
(libraries
|
||||
lwt_ppx
|
||||
lambda-term
|
||||
topinf))
|
||||
|
||||
(library
|
||||
(name topinf)
|
||||
(modes byte)
|
||||
(modules topinf)
|
||||
(libraries
|
||||
fmt
|
||||
tsdl
|
||||
tgls.tgles2
|
||||
wall
|
||||
zed
|
||||
lambda-term
|
||||
irmin-unix
|
||||
nottui
|
||||
nottui-pretty
|
||||
nottui-lwt
|
||||
uuseg
|
||||
irc-client
|
||||
irc-client-lwt
|
||||
irc-client-unix
|
||||
irc-client-tls
|
||||
ocaml-compiler-libs.common
|
||||
ocaml-compiler-libs.bytecomp
|
||||
ocaml-compiler-libs.toplevel))
|
||||
|
||||
@ -1,3 +1,2 @@
|
||||
(lang dune 2.8)
|
||||
(name komm)
|
||||
(wrapped_executables false)
|
||||
(lang dune 3.4)
|
||||
(name boot)
|
||||
|
||||
35
index.html
Normal file
35
index.html
Normal file
@ -0,0 +1,35 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<style>
|
||||
html, body {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
overflow: hidden;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
div {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
canvas {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div>
|
||||
<canvas id='canvas'></canvas>
|
||||
</div>
|
||||
</body>
|
||||
<script
|
||||
type='text/javascript'
|
||||
defer
|
||||
src='_build/default/boot_js.bc.js'>
|
||||
</script>
|
||||
</html>
|
||||
44
notes.org
Normal file
44
notes.org
Normal file
@ -0,0 +1,44 @@
|
||||
* mvp todo
|
||||
|
||||
|
||||
** toplevel repl in js_of_ocaml
|
||||
** git pull from gitea.departmentofinter.net/console/boot
|
||||
** git push to gitea.departmentofinter.net/console/boot
|
||||
** execute a git file execution in top level
|
||||
** display arbitrary git file from pulled repo
|
||||
** edit arbitrary file with common emacs bindings
|
||||
*** move left and right by character
|
||||
*** move up and down by line
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
* other todo
|
||||
*** yank (to clipboard) next char
|
||||
*** move left and right by word and sentance
|
||||
*** region select
|
||||
|
||||
|
||||
* principles?
|
||||
an "anywhere" programming environment
|
||||
|
||||
* 221211
|
||||
ok you got the scroll box mostly working so next:
|
||||
** fix the scroll jump bugs
|
||||
** setup better keybindings
|
||||
** fix cursor and active focus indicators
|
||||
|
||||
|
||||
|
||||
* 221210 -
|
||||
** need to resolve the issue with the ui.t Resize type.
|
||||
this is an issue with the direction of the determination of the .height and .width fields of Ui.t
|
||||
|
||||
currently you were planning to combine update_sensors and update_size
|
||||
|
||||
in the original nottui.ml library, are Ui.t.w and Ui.t.h determined from the top down orbottom up?
|
||||
the bottom up, becahse they are chars
|
||||
|
||||
|
||||
37
unicom.org
Normal file
37
unicom.org
Normal file
@ -0,0 +1,37 @@
|
||||
UNICOM
|
||||
|
||||
unifying tools for thought and commmunication
|
||||
|
||||
At the top level is a kind of "trace" that connects all the tools together
|
||||
and records the exact use of all the tools in a session. Traces are the only
|
||||
kind of document in the system, and are basically written by a user when
|
||||
they interact with the system. Users can store, retrieve, view, and modify
|
||||
traces as a way of controlling system state, retrieving history, and sharing
|
||||
information?
|
||||
|
||||
|
||||
tools for thought:
|
||||
- todo lists
|
||||
- personal journal
|
||||
- calendar
|
||||
- calculator/spreadsheet
|
||||
- health, fitness, finance data tracking, analysis
|
||||
- Integrated Development Environment [for development/configuration of all these tools]
|
||||
|
||||
|
||||
tools for communication:
|
||||
- collaborative documents
|
||||
video / .txt file
|
||||
- content discovery,display,labeling?
|
||||
twitter / netflix / books
|
||||
- direct messages
|
||||
sms / email
|
||||
- group messages
|
||||
private (chat) / private (irc,twitter)
|
||||
- audio/music production
|
||||
sound driver / DAW
|
||||
- cad
|
||||
KiCad / OpenSCAD
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user