open Lwt.Infix module F = Fmt open Tgles2 module Gv = Graphv_gles2_native open Ogui module GLFWExtras = struct open Ctypes open Foreign let glfwSetErrorCallback : (int -> string -> unit) -> int -> string -> unit = let errorfun = int @-> string @-> returning void in foreign "glfwSetErrorCallback" (funptr errorfun @-> returning (funptr errorfun)) end let errorcb error desc = Printf.printf "GLFW error %d: %s\n%!" error desc let load_fonts vg = let _ = Gv.Text.create vg ~name:"mono" ~file:"./assets/mono.ttf" in let _ = Gv.Text.create vg ~name:"icons" ~file:"./assets/entypo.ttf" in let _ = Gv.Text.create vg ~name:"sans" ~file:"./assets/Roboto-Regular.ttf" in let _ = Gv.Text.create vg ~name:"sans-bold" ~file:"./assets/Roboto-Bold.ttf" in let _ = Gv.Text.create vg ~name:"emoji" ~file:"./assets/NotoEmoji-Regular.ttf" in Gv.Text.add_fallback vg ~name:"sans" ~fallback:"emoji"; Gv.Text.add_fallback vg ~name:"sans-bold" ~fallback:"emoji"; Gv.Text.set_font_face vg ~name:"mono" let main = GLFW.init (); at_exit GLFW.terminate; let _res = GLFWExtras.glfwSetErrorCallback errorcb in GLFW.windowHint ~hint:GLFW.ClientApi ~value:GLFW.OpenGLESApi; GLFW.windowHint ~hint:GLFW.ContextVersionMajor ~value:2; GLFW.windowHint ~hint:GLFW.ContextVersionMinor ~value:0; let window = GLFW.createWindow ~width:1000 ~height:600 ~title:"window" () in (* Make the window's context current *) GLFW.makeContextCurrent ~window:(Some window); GLFW.swapInterval ~interval:0; Gl.clear_color 0.1 0.2 0.2 1.; (*Memtrace.trace_if_requested (); *) let ctx = Gv.create ~flags:Gv.CreateFlags.(antialias lor stencil_strokes) () in let graph = Perfgraph.init Perfgraph.FPS "Frame Time" in let min_fps = ref Float.max_float in let max_fps = ref Float.min_float in (* Thread which is woken up when the main window is closed. *) let _waiter, _wakener = Lwt.wait () in let rootrepo = Store.init_default (F.str "%s/console/rootstore.git" Secrets.giturl) in let ui = Ogui.Ui.window ctx ~window Gg.(Box2.v P2.o (P2.v 500. 500.)) in load_fonts ui.gv; (* Format.safe_set_geometry ~max_indent:(500 - 1) ~margin:500; *) let event_stream, event_push = Lwt_stream.create () in Ogui.Ui.process_events ui event_stream; GLFW.setKeyCallback ~window ~f: (Some (fun _window key _int state mods -> (* ignore key releases and capslock *) match (state, key, mods) with | Release, _, _ | _, CapsLock, _ -> () | _ -> event_push (Some (`Key (state, key, mods))) (*Lwt.async (fun () -> Ogui.Ui.keycallback ui state key mods >>= fun _ -> Lwt.return_unit) *))) |> ignore; GLFW.setCharCallback ~window ~f: (Some (fun _window ch -> event_push (Some (`Char ch)) (* Lwt.async (fun () -> Ogui.Ui.chrcallback ui ch) *))) |> ignore; GLFW.setWindowSizeCallback ~window ~f: (Some Gg.( fun _window x y -> Lwd.set ui.rect (Box2.v V2.zero (V2.v (float x) (float y))))) |> ignore; F.pr "oplevel.ml: building initial page@."; let initial_path = [ ".config"; "init.ml" ] in TextBuffer.of_repo ~initial_path ~repo:rootrepo >>= fun tb_init -> TextBuffer.of_string ~repo:rootrepo ~path: (List.fold_right (fun a (acc : string list) -> match acc with | [] -> [ F.str "%s.output" a ] | a' -> a :: a') [] initial_path) (F.str "(* --- output:%s --- *)\n\n" (String.concat "/" initial_path)) |> Lwt.return >>= fun to_init -> let _out_ppf = let insert s = Lwt.async (fun () -> TextBuffer.length to_init >>= fun len -> (* TKTK if buffer is modified here during yield from >>= it could be weird *) TextBuffer.insert to_init len s) in Format.formatter_of_out_functions Format. { out_string = (fun s _ _ -> insert s); out_flush = (fun () -> ()); out_indent = (fun n -> insert (String.make (n * 2) ' ')); out_newline = (fun () -> insert "\n"); out_spaces = (fun n -> insert (String.make n ' ')); } in (*F.pr "oplevel.ml: Toploop.initialize_toplevel_env@."; Toploop.initialize_toplevel_env (); Clflags.debug := true; ignore (Toploop.use_input out_ppf (String "#use \"topfind\";;\n#list;;#require \"lwt\";;")); *) (* toplevel execution binding *) Ui.( update_bindings ui Event.( fun a -> a |> adds [ [ Key (Press, X, [ Control ]); Key (Press, E, [ Control ]); ]; ] [ Custom ( "toplevel_execute", fun () -> TextBuffer.peek tb_init >>= fun _str -> (*Toploop.use_input out_ppf (String str) |> F.epr "Toploop.use_input=%b@."; *) Lwt.return_unit ); ])); WindowManager.make ui (Lwd.var (`T ( `Y, [ (`TextEdit (TextEdit.multiline ui to_init), `Ratio 0.333); (`TextEdit (TextEdit.multiline ui tb_init), `Ratio 0.5); (`TextEdit (TextEdit.multiline ui to_init), `Ratio 1.0); ] ))) >>= fun page -> let page_root = Lwd.observe page in let bindings = ui.bindings |> Lwd.get |> Lwd.observe |> Lwd.quick_sample |> snd in F.epr "Bindings:@.%a" Ui.pp_bindings bindings; F.pr "oplevel.ml: entering drawing loop@."; let period_min = 1.0 /. 30. in let t = GLFW.getTime () |> ref in let rec draw_loop () = let now = GLFW.getTime () in let dt = now -. !t in t := now; Perfgraph.update graph dt; if now > 2. then ( let avg = 1. /. Perfgraph.average graph in min_fps := Float.min avg !min_fps; max_fps := Float.max avg !max_fps); let win_w, win_h = GLFW.getWindowSize ~window in Gl.viewport 0 0 win_w win_h; Gl.clear (Gl.color_buffer_bit lor Gl.depth_buffer_bit lor Gl.stencil_buffer_bit); Gl.enable Gl.blend; Gl.blend_func Gl.src_alpha Gl.one_minus_src_alpha; Gl.enable Gl.cull_face_enum; Gl.disable Gl.depth_test; let page = Lwd.quick_sample page_root in let win_w, win_h = GLFW.getWindowSize ~window in let width, height = (float win_w, float win_h) in let box = Gg.(Box2.v V2.zero Size2.(v width (height -. 20.))) in Gv.begin_frame ctx ~width ~height ~device_ratio:1.; Perfgraph.render graph ctx (width -. 205.) 5.; (*F.epr "Painter.layout=%a@." Gg.Box2.pp box; *) Painter.layout box ui page >>= fun _ -> (* Demo.render_demo ctx mx my win_w win_h now !blowup data; *) Gv.end_frame ctx; Gc.major_slice 0 |> ignore; GLFW.swapBuffers ~window; GLFW.pollEvents (); Lwt_unix.sleep Float.(max 0. (period_min -. GLFW.getTime () +. !t)) >>= fun () -> if not GLFW.(windowShouldClose ~window) then draw_loop () else Lwt.return_unit in (try draw_loop () with e -> F.epr "draw_loop Exception: %s@.Backtrace:@.%s@." (Printexc.to_string e) (Printexc.get_backtrace ()) |> Lwt.return) >>= fun () -> Printf.printf "MIN %.2f\n" !min_fps; Printf.printf "MAX %.2f\n%!" !max_fps; Lwt.return_unit let () = try Lwt_main.run main with e -> F.epr "Exception: %s@.Backtrace:@.%s@." (Printexc.to_string e) (Printexc.get_backtrace ())