# The contents of this file are subject to the Common Public Attribution License # Version 1.0 (the “License”); you may not use this file except in compliance # with the License. You may obtain a copy of the License at # https://myou.dev/licenses/LICENSE-CPAL. The License is based on the Mozilla # Public License Version 1.1 but Sections 14 and 15 have been added to cover use # of software over a computer network and provide for limited attribution for # the Original Developer. In addition, Exhibit A has been modified to be # consistent with Exhibit B. # # Software distributed under the License is distributed on an “AS IS” basis, # WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License for # the specific language governing rights and limitations under the License. # # The Original Code is Myou Engine. # # the Original Developer is the Initial Developer. # # The Initial Developer of the Original Code is the Myou Engine developers. # All portions of the code written by the Myou Engine developers are Copyright # (c) 2024. All Rights Reserved. # # Alternatively, the contents of this file may be used under the terms of the # GNU Affero General Public License version 3 (the [AGPL-3] License), in which # case the provisions of [AGPL-3] License are applicable instead of those above. # # If you wish to allow use of your version of this file only under the terms of # the [AGPL-3] License and not to allow others to use your version of this file # under the CPAL, indicate your decision by deleting the provisions above and # replace them with the notice and other provisions required by the [AGPL-3] # License. If you do not delete the provisions above, a recipient may use your # version of this file under either the CPAL or the [AGPL-3] License. import ../types import vmath except Quat, quat import std/strutils type Window* = ref object of RootObj import std/math import std/strformat import std/strutils import std/unicode import ./gl import ../screen import ../util import ../graphics/render import ../input var global_screen: Screen proc screen*(window: Window): Screen {.inline.} = cast[Screen](window) proc `screen=`*(window: Window, screen1: Screen) {.inline.} = global_screen = screen1 type MyouCallbacks = object getdir: proc(app: pointer, buf: ptr char, len: int32, which: int8): int32 {.cdecl.} getbundlefile: proc(app: pointer, buf: ptr char, len: int32, path: cstring): int32 {.cdecl,gcsafe.} closeapp: proc(app: pointer) {.cdecl.} {.emit:"void *global_myou_app_pointer, *global_myou_app_callbacks;".} var app_pointer {.importc:"global_myou_app_pointer".}: pointer var callbacks0 {.importc:"global_myou_app_callbacks".}: pointer template callbacks: untyped = cast[ptr MyouCallbacks](callbacks0) proc NimMain() {.importc.} proc myouEngineCreate(app: pointer, cbs: ptr MyouCallbacks) {.exportc,cdecl,stackTrace:off.} = app_pointer = app callbacks0 = cbs try: NimMain() echo "nimmain end" except Exception as e: for line in e.getStackTrace.split '\n': debugEcho line debugEcho getCurrentExceptionMsg() proc myouEngineDestroy() {.exportc,cdecl,noreturn.} = # TODO discard proc myouEngineOnPause() {.exportc,cdecl.} = discard proc myouEngineOnResume() {.exportc,cdecl.} = discard proc myouEngineOnSurfaceCreated(w,h,r: int32, scale: float32, top,right,bottom,left: float32) {.exportc,cdecl.} = assert global_screen != nil if global_screen != nil: global_screen.resize(w,h,r.int8) global_screen.display_scale = scale when not defined(myouUseFakeFrameInset): global_screen.frame_inset = FrameInset(top: top*scale, right: right*scale, bottom: bottom*scale, left: left*scale) global_screen.engine.renderer.initialize() proc myouEngineOnSurfaceChanged(w,h,r: int32, scale: float32, top,right,bottom,left: float32) {.exportc,cdecl.} = if global_screen != nil: global_screen.resize(w,h,r.int8) global_screen.display_scale = scale when not defined(myouUseFakeFrameInset): global_screen.frame_inset = FrameInset(top: top*scale, right: right*scale, bottom: bottom*scale, left: left*scale) proc make_window*(width, height: int32, title: string): Window = nil proc set_vsync*(window: Window, vsync: bool) = discard var max_messages = 0 proc init_graphics*(engine: MyouEngine, width, height: int32, title: string, opengl_version = 330, opengl_es = false, samples = 1, ) = assert samples == 1, "Samples != 1 not supported on this platform yet" let major = opengl_version div 100 let minor = opengl_version mod 100 div 10 let rev = opengl_version mod 10 assert major >= 3 if not gladLoadGLES2(nil): echo "Could not initialize OpenGL" quit -1 engine.renderer.enqueue proc()= when not defined(release) and not defined(emscripten): proc f(source: GLenum, etype: GLenum, id: GLuint, severity: GLenum, length: GLsizei, message: cstring, userParam: pointer) {.stdcall.} = if id == 131185: # buffer usage hints return # if message == "GL_INVALID_OPERATION error generated. Target buffer must be bound.": # return if max_messages == 0: return # dump (source, etype, id, severity, length) echo "OpenGL error: ", message max_messages -= 1 if max_messages == 0: echo "No more OpenGL messages will be shown" glEnable(GL_DEBUG_OUTPUT) glDebugMessageCallback cast[GLdebugProc](f), nil glEnable(GL_DEBUG_OUTPUT_SYNCHRONOUS) max_messages = 100 # glEnable(GL_TEXTURE_CUBE_MAP_SEAMLESS) # glHint(GL_GENERATE_MIPMAP_HINT, GL_FASTEST) # glHint(GL_GENERATE_MIPMAP_HINT, GL_NICEST) proc glfm_breakpoint() {.exportc.} = # Add "b glfm_breakpoint" to your debugger startup commands discard var engine: MyouEngine var main_loop: proc(self: MyouEngine) proc start_platform_main_loop*(engine1: MyouEngine, main_loop1: proc(self: MyouEngine)) = engine = engine1 main_loop = main_loop1 type MyouEngineFrameResult = object needsKeyboard: uint8 var frame_result: MyouEngineFrameResult proc myouEngineOnFrame(): MyouEngineFrameResult {.exportc,cdecl.} = if engine == nil: return try: engine.main_loop() return frame_result except Exception as e: for line in e.getStackTrace.split '\n': echo line echo getCurrentExceptionMsg() glfm_breakpoint() proc myouOnTouch(touch: int32, ending: char, x, y: float32) {.exportc,cdecl.} = let ending = ending.bool let screen = global_screen if screen == nil: return screen.emulateMouseWithTouch(touch, ending, x, y) proc platform_switch_screen*(screen: Screen): bool {.inline.} = discard proc myouSetKeyboardVisible*(show: bool) = frame_result.needsKeyboard = show.uint8 proc myouGetDocumentsDir*(): string = result.setLen 512 let l = callbacks.getdir(app_pointer, result[0].addr, result.len.int32, 0) result.setLen l if result.startswith "file://": result = result[7 .. ^1] if result.endswith "/": result.setlen(result.len - 1) proc myouGetCacheDir*(): string = result.setLen 512 let l = callbacks.getdir(app_pointer, result[0].addr, result.len.int32, 1) result.setLen l if result.startswith "file://": result = result[7 .. ^1] if result.endswith "/": result.setlen(result.len - 1) proc myouGetTmpDir*(): string = result.setLen 512 let l = callbacks.getdir(app_pointer, result[0].addr, result.len.int32, 2) result.setLen l if result.startswith "file://": result = result[7 .. ^1] if result.endswith "/": result.setlen(result.len - 1) proc myouGetBundledFilePath*(path: string): string {.gcsafe.} = result.setLen 512 let l = callbacks.getbundlefile(app_pointer, result[0].addr, result.len.int32, path.cstring) result.setLen l if result.startswith "file://": result = result[7 .. ^1] proc myouOnKeyEvent(keyCode: int32, mods: int32, pressed: int32) {.exportc,cdecl.} = let key = cast[KeyCode](keyCode) let shiftKey = (mods and (1 shl 17)).bool let metaKey = (mods and (2 shl 17)).bool let altKey = (mods and (4 shl 17)).bool let ctrlKey = (mods and (8 shl 17)).bool for cb in global_screen.key_callbacks: cb(KeyEvent( pressed: pressed != 0, repeat: false, shiftKey: shiftKey, ctrlKey: ctrlKey, altKey: altKey, metaKey: metaKey, key: key, )) if global_screen.break_current_callbacks: global_screen.break_current_callbacks = false break proc myouOnCharEvent(str: cstring) {.exportc,cdecl.} = for rune in ($str).toRunes: let codepoint = cast[uint32](rune) for cb in global_screen.char_callbacks: cb(codepoint) if global_screen.break_current_callbacks: global_screen.break_current_callbacks = false break proc myouCloseMobileApp*() = callbacks.closeapp(app_pointer)