From a6de27c5de7d8172a7c03856a605e6a4e0f310dd Mon Sep 17 00:00:00 2001 From: lunarcleint <88809950+lunarcleint@users.noreply.github.com> Date: Fri, 21 Apr 2023 17:55:24 -0700 Subject: [PATCH] ummm new dark mode code --- project.xml | 3 + source/funkin/backend/system/Main.hx | 2 - source/funkin/backend/utils/NativeAPI.hx | 7 +- source/funkin/backend/utils/native/Windows.hx | 10 +- .../_internal/backend/native/NativeWindow.hx | 708 ++++++++++++++++++ 5 files changed, 720 insertions(+), 10 deletions(-) create mode 100644 source/lime/_internal/backend/native/NativeWindow.hx diff --git a/project.xml b/project.xml index 619b8369..18a29b90 100644 --- a/project.xml +++ b/project.xml @@ -150,6 +150,9 @@ + + + diff --git a/source/funkin/backend/system/Main.hx b/source/funkin/backend/system/Main.hx index f363232d..b04addd5 100644 --- a/source/funkin/backend/system/Main.hx +++ b/source/funkin/backend/system/Main.hx @@ -63,8 +63,6 @@ class Main extends Sprite public function new() { super(); - #if windows NativeAPI.setDarkMode(true); #end - addChild(game = new FunkinGame(gameWidth, gameHeight, MainState, Options.framerate, Options.framerate, skipSplash, startFullscreen)); #if !mobile diff --git a/source/funkin/backend/utils/NativeAPI.hx b/source/funkin/backend/utils/NativeAPI.hx index aa088fd1..3ca986fb 100644 --- a/source/funkin/backend/utils/NativeAPI.hx +++ b/source/funkin/backend/utils/NativeAPI.hx @@ -29,12 +29,9 @@ class NativeAPI { #end } - /** - * Sets the window titlebar to dark mode (Windows 10 only) - */ - public static function setDarkMode(enable:Bool) { + public static function setDarkMode(handle:Dynamic, enable:Bool) { #if windows - Windows.setDarkMode(enable); + Windows.setDarkMode(handle, enable); #end } diff --git a/source/funkin/backend/utils/native/Windows.hx b/source/funkin/backend/utils/native/Windows.hx index ad38b385..1efb4d33 100644 --- a/source/funkin/backend/utils/native/Windows.hx +++ b/source/funkin/backend/utils/native/Windows.hx @@ -129,12 +129,16 @@ class Windows { @:functionCode(' int darkMode = enable ? 1 : 0; - HWND window = GetActiveWindow(); - if (S_OK != DwmSetWindowAttribute(window, 19, &darkMode, sizeof(darkMode))) { + + HWND window = FindWindowA(NULL, title.c_str()); + // Look for child windows if top level aint found + if (window == NULL) window = FindWindowExA(GetActiveWindow(), NULL, NULL, title.c_str()); + + if (window != NULL && S_OK != DwmSetWindowAttribute(window, 19, &darkMode, sizeof(darkMode))) { DwmSetWindowAttribute(window, 20, &darkMode, sizeof(darkMode)); } ') - public static function setDarkMode(enable:Bool) {} + public static function setDarkMode(title:String, enable:Bool) {} @:functionCode(' // https://stackoverflow.com/questions/15543571/allocconsole-not-displaying-cout diff --git a/source/lime/_internal/backend/native/NativeWindow.hx b/source/lime/_internal/backend/native/NativeWindow.hx new file mode 100644 index 00000000..a56c808d --- /dev/null +++ b/source/lime/_internal/backend/native/NativeWindow.hx @@ -0,0 +1,708 @@ +package lime._internal.backend.native; + +import haxe.io.Bytes; +import lime._internal.backend.native.NativeCFFI; +import lime.app.Application; +import lime.graphics.cairo.Cairo; +import lime.graphics.cairo.CairoFormat; +import lime.graphics.cairo.CairoImageSurface; +import lime.graphics.cairo.CairoSurface; +import lime.graphics.opengl.GL; +import lime.graphics.CairoRenderContext; +import lime.graphics.Image; +import lime.graphics.ImageBuffer; +import lime.graphics.OpenGLRenderContext; +import lime.graphics.RenderContext; +import lime.math.Rectangle; +import lime.math.Vector2; +import lime.system.Display; +import lime.system.DisplayMode; +import lime.system.JNI; +import lime.system.System; +import lime.ui.MouseCursor; +import lime.ui.Window; +import lime.utils.UInt8Array; + +#if !lime_debug +@:fileXml('tags="haxe,release"') +@:noDebug +#end +@:access(lime._internal.backend.native.NativeCFFI) +@:access(lime._internal.backend.native.NativeOpenGLRenderContext) +@:access(lime.app.Application) +@:access(lime.graphics.cairo.Cairo) +@:access(lime.graphics.opengl.GL) +@:access(lime.graphics.OpenGLRenderContext) +@:access(lime.graphics.RenderContext) +@:access(lime.system.DisplayMode) +@:access(lime.ui.Window) +class NativeWindow +{ + public var handle:Dynamic; + + private var closing:Bool; + private var cursor:MouseCursor; + private var displayMode:DisplayMode; + private var frameRate:Float; + private var mouseLock:Bool; + private var parent:Window; + private var useHardware:Bool; + #if lime_cairo + private var cacheLock:Dynamic; + private var cairo:Cairo; + private var primarySurface:CairoSurface; + #end + + public function new(parent:Window) + { + this.parent = parent; + + cursor = DEFAULT; + displayMode = new DisplayMode(0, 0, 0, 0); + + var attributes = parent.__attributes; + var contextAttributes = Reflect.hasField(attributes, "context") ? attributes.context : {}; + var title = Reflect.hasField(attributes, "title") ? attributes.title : "Lime Application"; + var flags = 0; + + if (!Reflect.hasField(contextAttributes, "antialiasing")) contextAttributes.antialiasing = 0; + if (!Reflect.hasField(contextAttributes, "background")) contextAttributes.background = 0; + if (!Reflect.hasField(contextAttributes, "colorDepth")) contextAttributes.colorDepth = 24; + if (!Reflect.hasField(contextAttributes, "depth")) contextAttributes.depth = true; + if (!Reflect.hasField(contextAttributes, "hardware")) contextAttributes.hardware = true; + if (!Reflect.hasField(contextAttributes, "stencil")) contextAttributes.stencil = true; + if (!Reflect.hasField(contextAttributes, "vsync")) contextAttributes.vsync = false; + + #if (cairo || (!lime_opengl && !lime_opengles)) + contextAttributes.type = CAIRO; + #end + if (Reflect.hasField(contextAttributes, "type") && contextAttributes.type == CAIRO) contextAttributes.hardware = false; + + if (Reflect.hasField(attributes, "allowHighDPI") && attributes.allowHighDPI) flags |= cast WindowFlags.WINDOW_FLAG_ALLOW_HIGHDPI; + if (Reflect.hasField(attributes, "alwaysOnTop") && attributes.alwaysOnTop) flags |= cast WindowFlags.WINDOW_FLAG_ALWAYS_ON_TOP; + if (Reflect.hasField(attributes, "borderless") && attributes.borderless) flags |= cast WindowFlags.WINDOW_FLAG_BORDERLESS; + if (Reflect.hasField(attributes, "fullscreen") && attributes.fullscreen) flags |= cast WindowFlags.WINDOW_FLAG_FULLSCREEN; + if (Reflect.hasField(attributes, "hidden") && attributes.hidden) flags |= cast WindowFlags.WINDOW_FLAG_HIDDEN; + if (Reflect.hasField(attributes, "maximized") && attributes.maximized) flags |= cast WindowFlags.WINDOW_FLAG_MAXIMIZED; + if (Reflect.hasField(attributes, "minimized") && attributes.minimized) flags |= cast WindowFlags.WINDOW_FLAG_MINIMIZED; + if (Reflect.hasField(attributes, "resizable") && attributes.resizable) flags |= cast WindowFlags.WINDOW_FLAG_RESIZABLE; + + if (contextAttributes.antialiasing >= 4) + { + flags |= cast WindowFlags.WINDOW_FLAG_HW_AA_HIRES; + } + else if (contextAttributes.antialiasing >= 2) + { + flags |= cast WindowFlags.WINDOW_FLAG_HW_AA; + } + + if (contextAttributes.colorDepth == 32) flags |= cast WindowFlags.WINDOW_FLAG_COLOR_DEPTH_32_BIT; + if (contextAttributes.depth) flags |= cast WindowFlags.WINDOW_FLAG_DEPTH_BUFFER; + if (contextAttributes.hardware) flags |= cast WindowFlags.WINDOW_FLAG_HARDWARE; + if (contextAttributes.stencil) flags |= cast WindowFlags.WINDOW_FLAG_STENCIL_BUFFER; + if (contextAttributes.vsync) flags |= cast WindowFlags.WINDOW_FLAG_VSYNC; + + var width = Reflect.hasField(attributes, "width") ? attributes.width : #if desktop 800 #else 0 #end; + var height = Reflect.hasField(attributes, "height") ? attributes.height : #if desktop 600 #else 0 #end; + + #if (!macro && lime_cffi) + handle = NativeCFFI.lime_window_create(parent.application.__backend.handle, width, height, flags, title); + + #if DARK_MODE_WINDOW + funkin.backend.utils.NativeAPI.setDarkMode(title, true); + #end + + if (handle != null) + { + parent.__width = NativeCFFI.lime_window_get_width(handle); + parent.__height = NativeCFFI.lime_window_get_height(handle); + parent.__x = NativeCFFI.lime_window_get_x(handle); + parent.__y = NativeCFFI.lime_window_get_y(handle); + parent.__hidden = (Reflect.hasField(attributes, "hidden") && attributes.hidden); + parent.id = NativeCFFI.lime_window_get_id(handle); + } + + parent.__scale = NativeCFFI.lime_window_get_scale(handle); + + var context = new RenderContext(); + context.window = parent; + + #if hl + var contextType = @:privateAccess String.fromUTF8(NativeCFFI.lime_window_get_context_type(handle)); + #else + var contextType:String = NativeCFFI.lime_window_get_context_type(handle); + #end + + switch (contextType) + { + case "opengl": + var gl = new NativeOpenGLRenderContext(); + + useHardware = true; + + #if lime_opengl + context.gl = gl; + #end + + context.gles2 = gl; + context.webgl = gl; + context.type = gl.type; + context.version = Std.string(gl.version); + + if (gl.type == OPENGLES && gl.version >= 3) + { + context.gles3 = gl; + context.webgl2 = gl; + } + + if (GL.context == null) + { + GL.context = gl; + } + + default: + useHardware = false; + + #if lime_cairo + context.cairo = cairo; + context.type = CAIRO; + context.version = ""; + + parent.context = context; + render(); + #end + context.type = CAIRO; + } + + contextAttributes.type = context.type; + context.attributes = contextAttributes; + parent.context = context; + + setFrameRate(Reflect.hasField(attributes, "frameRate") ? attributes.frameRate : 60); + #end + } + + public function alert(message:String, title:String):Void + { + if (handle != null) + { + #if (!macro && lime_cffi) + NativeCFFI.lime_window_alert(handle, message, title); + #end + } + } + + public function close():Void + { + if (!closing) + { + closing = true; + parent.onClose.dispatch(); + + if (!parent.onClose.canceled) + { + if (handle != null) + { + #if (!macro && lime_cffi) + NativeCFFI.lime_window_close(handle); + #end + handle = null; + } + } + else + { + closing = false; + } + } + } + + public function contextFlip():Void + { + #if (!macro && lime_cffi) + if (!useHardware) + { + #if lime_cairo + if (cairo != null) + { + primarySurface.flush(); + } + #end + NativeCFFI.lime_window_context_unlock(handle); + } + + NativeCFFI.lime_window_context_flip(handle); + #end + } + + public function focus():Void + { + if (handle != null) + { + #if (!macro && lime_cffi) + NativeCFFI.lime_window_focus(handle); + #end + } + } + + public function getCursor():MouseCursor + { + return cursor; + } + + public function getDisplay():Display + { + if (handle != null) + { + #if (!macro && lime_cffi) + var index = NativeCFFI.lime_window_get_display(handle); + + if (index > -1) + { + return System.getDisplay(index); + } + #end + } + + return null; + } + + public function getDisplayMode():DisplayMode + { + if (handle != null) + { + #if (!macro && lime_cffi) + #if hl + NativeCFFI.lime_window_get_display_mode(handle, displayMode); + #else + var data:Dynamic = NativeCFFI.lime_window_get_display_mode(handle); + displayMode.width = data.width; + displayMode.height = data.height; + displayMode.pixelFormat = data.pixelFormat; + displayMode.refreshRate = data.refreshRate; + #end + #end + } + + return displayMode; + } + + public function getFrameRate():Float + { + return frameRate; + } + + public function getMouseLock():Bool + { + if (handle != null) + { + #if (!macro && lime_cffi) + mouseLock = NativeCFFI.lime_window_get_mouse_lock(handle); + #end + } + + return mouseLock; + } + + public function getTextInputEnabled():Bool + { + if (handle != null) + { + #if (!macro && lime_cffi) + return NativeCFFI.lime_window_get_text_input_enabled(handle); + #end + } + + return false; + } + + public function move(x:Int, y:Int):Void + { + if (handle != null) + { + #if (!macro && lime_cffi) + NativeCFFI.lime_window_move(handle, x, y); + #end + } + } + + public function readPixels(rect:Rectangle):Image + { + var imageBuffer:ImageBuffer = null; + + switch (parent.context.type) + { + case OPENGL, OPENGLES, WEBGL: + var gl = parent.context.webgl; + var windowWidth = Std.int(parent.__width * parent.__scale); + var windowHeight = Std.int(parent.__height * parent.__scale); + + var x, y, width, height; + + if (rect != null) + { + x = Std.int(rect.x); + y = Std.int((windowHeight - rect.y) - rect.height); + width = Std.int(rect.width); + height = Std.int(rect.height); + } + else + { + x = 0; + y = 0; + width = windowWidth; + height = windowHeight; + } + + var data = new UInt8Array(width * height * 4); + + gl.readPixels(x, y, width, height, gl.RGBA, gl.UNSIGNED_BYTE, data); + + #if !js // TODO + + var rowLength = width * 4; + var srcPosition = (height - 1) * rowLength; + var destPosition = 0; + + var temp = Bytes.alloc(rowLength); + var buffer = data.buffer; + var rows = Std.int(height / 2); + + while (rows-- > 0) + { + temp.blit(0, buffer, destPosition, rowLength); + buffer.blit(destPosition, buffer, srcPosition, rowLength); + buffer.blit(srcPosition, temp, 0, rowLength); + + destPosition += rowLength; + srcPosition -= rowLength; + } + #end + + imageBuffer = new ImageBuffer(data, width, height, 32, RGBA32); + + default: + #if (!macro && lime_cffi) + #if !cs + imageBuffer = NativeCFFI.lime_window_read_pixels(handle, rect, new ImageBuffer(new UInt8Array(Bytes.alloc(0)))); + #else + var data:Dynamic = NativeCFFI.lime_window_read_pixels(handle, rect, null); + if (data != null) + { + imageBuffer = new ImageBuffer(new UInt8Array(@:privateAccess new Bytes(data.data.length, data.data.b)), data.width, data.height, + data.bitsPerPixel); + } + #end + #end + + if (imageBuffer != null) + { + imageBuffer.format = RGBA32; + } + } + + if (imageBuffer != null) + { + return new Image(imageBuffer); + } + + return null; + } + + public function render():Void + { + #if (!macro && lime_cffi) + NativeCFFI.lime_window_context_make_current(handle); + + if (!useHardware) + { + #if lime_cairo + var lock:Dynamic = NativeCFFI.lime_window_context_lock(handle); + + if (lock != null + && (cacheLock == null || cacheLock.pixels != lock.pixels || cacheLock.width != lock.width || cacheLock.height != lock.height)) + { + primarySurface = CairoImageSurface.create(lock.pixels, CairoFormat.ARGB32, lock.width, lock.height, lock.pitch); + + if (cairo != null) + { + cairo.recreate(primarySurface); + } + else + { + cairo = new Cairo(primarySurface); + } + + parent.context.cairo = cairo; + } + + cacheLock = lock; + #else + parent.context = null; + #end + } + #end + } + + public function resize(width:Int, height:Int):Void + { + if (handle != null) + { + #if (!macro && lime_cffi) + NativeCFFI.lime_window_resize(handle, width, height); + #end + } + } + + public function setBorderless(value:Bool):Bool + { + if (handle != null) + { + #if (!macro && lime_cffi) + NativeCFFI.lime_window_set_borderless(handle, value); + #end + } + + return value; + } + + public function setCursor(value:MouseCursor):MouseCursor + { + if (cursor != value) + { + if (value == null) + { + #if (!macro && lime_cffi) + NativeCFFI.lime_window_set_cursor(handle, 0); + #end + } + else + { + var type:MouseCursorType = switch (value) + { + case ARROW: ARROW; + case CROSSHAIR: CROSSHAIR; + case MOVE: MOVE; + case POINTER: POINTER; + case RESIZE_NESW: RESIZE_NESW; + case RESIZE_NS: RESIZE_NS; + case RESIZE_NWSE: RESIZE_NWSE; + case RESIZE_WE: RESIZE_WE; + case TEXT: TEXT; + case WAIT: WAIT; + case WAIT_ARROW: WAIT_ARROW; + default: DEFAULT; + } + + #if (!macro && lime_cffi) + NativeCFFI.lime_window_set_cursor(handle, type); + #end + } + + cursor = value; + } + + return cursor; + } + + public function setDisplayMode(value:DisplayMode):DisplayMode + { + if (handle != null) + { + #if (!macro && lime_cffi) + #if hl + NativeCFFI.lime_window_set_display_mode(handle, value, displayMode); + #else + var data:Dynamic = NativeCFFI.lime_window_set_display_mode(handle, value); + displayMode.width = data.width; + displayMode.height = data.height; + displayMode.pixelFormat = data.pixelFormat; + displayMode.refreshRate = data.refreshRate; + #end + #end + } + + return displayMode; + } + + public function setMouseLock(value:Bool):Bool + { + if (mouseLock != value) + { + #if (!macro && lime_cffi) + NativeCFFI.lime_window_set_mouse_lock(handle, value); + #end + + mouseLock = value; + } + + return mouseLock; + } + + public function setTextInputEnabled(value:Bool):Bool + { + if (handle != null) + { + #if (!macro && lime_cffi) + NativeCFFI.lime_window_set_text_input_enabled(handle, value); + #end + } + + return value; + } + + public function setTextInputRect(value:Rectangle):Rectangle + { + if (handle != null) + { + #if (!macro && lime_cffi) + NativeCFFI.lime_window_set_text_input_rect(handle, value); + #end + } + + return value; + } + + public function setFrameRate(value:Float):Float + { + // TODO: Support multiple independent frame rates per window + + if (handle != null) + { + #if (!macro && lime_cffi) + NativeCFFI.lime_application_set_frame_rate(parent.application.__backend.handle, value); + #end + } + + return frameRate = value; + } + + public function setFullscreen(value:Bool):Bool + { + if (handle != null) + { + #if (!macro && lime_cffi) + value = NativeCFFI.lime_window_set_fullscreen(handle, value); + + parent.__width = NativeCFFI.lime_window_get_width(handle); + parent.__height = NativeCFFI.lime_window_get_height(handle); + parent.__x = NativeCFFI.lime_window_get_x(handle); + parent.__y = NativeCFFI.lime_window_get_y(handle); + #end + + if (value) + { + parent.onFullscreen.dispatch(); + } + } + + return value; + } + + public function setIcon(image:Image):Void + { + if (handle != null) + { + #if (!macro && lime_cffi) + NativeCFFI.lime_window_set_icon(handle, image.buffer); + #end + } + } + + public function setMaximized(value:Bool):Bool + { + if (handle != null) + { + #if (!macro && lime_cffi) + return NativeCFFI.lime_window_set_maximized(handle, value); + #end + } + + return value; + } + + public function setMinimized(value:Bool):Bool + { + if (handle != null) + { + #if (!macro && lime_cffi) + return NativeCFFI.lime_window_set_minimized(handle, value); + #end + } + + return value; + } + + public function setResizable(value:Bool):Bool + { + if (handle != null) + { + #if (!macro && lime_cffi) + NativeCFFI.lime_window_set_resizable(handle, value); + + // TODO: remove need for workaround + + NativeCFFI.lime_window_set_borderless(handle, !parent.__borderless); + NativeCFFI.lime_window_set_borderless(handle, parent.__borderless); + #end + } + + return value; + } + + public function setTitle(value:String):String + { + if (handle != null) + { + #if (!macro && lime_cffi) + return NativeCFFI.lime_window_set_title(handle, value); + #end + } + + return value; + } + + public function warpMouse(x:Int, y:Int):Void + { + #if (!macro && lime_cffi) + NativeCFFI.lime_window_warp_mouse(handle, x, y); + #end + } +} + +@:enum private abstract MouseCursorType(Int) from Int to Int +{ + var HIDDEN = 0; + var ARROW = 1; + var CROSSHAIR = 2; + var DEFAULT = 3; + var MOVE = 4; + var POINTER = 5; + var RESIZE_NESW = 6; + var RESIZE_NS = 7; + var RESIZE_NWSE = 8; + var RESIZE_WE = 9; + var TEXT = 10; + var WAIT = 11; + var WAIT_ARROW = 12; +} + +@:enum private abstract WindowFlags(Int) +{ + var WINDOW_FLAG_FULLSCREEN = 0x00000001; + var WINDOW_FLAG_BORDERLESS = 0x00000002; + var WINDOW_FLAG_RESIZABLE = 0x00000004; + var WINDOW_FLAG_HARDWARE = 0x00000008; + var WINDOW_FLAG_VSYNC = 0x00000010; + var WINDOW_FLAG_HW_AA = 0x00000020; + var WINDOW_FLAG_HW_AA_HIRES = 0x00000060; + var WINDOW_FLAG_ALLOW_SHADERS = 0x00000080; + var WINDOW_FLAG_REQUIRE_SHADERS = 0x00000100; + var WINDOW_FLAG_DEPTH_BUFFER = 0x00000200; + var WINDOW_FLAG_STENCIL_BUFFER = 0x00000400; + var WINDOW_FLAG_ALLOW_HIGHDPI = 0x00000800; + var WINDOW_FLAG_HIDDEN = 0x00001000; + var WINDOW_FLAG_MINIMIZED = 0x00002000; + var WINDOW_FLAG_MAXIMIZED = 0x00004000; + var WINDOW_FLAG_ALWAYS_ON_TOP = 0x00008000; + var WINDOW_FLAG_COLOR_DEPTH_32_BIT = 0x00010000; +} \ No newline at end of file