#if UNITY_EDITOR_OSX || (UNITY_STANDALONE_OSX && !UNITY_EDITOR) using System; using System.Collections; using System.Collections.Generic; using UnityEngine; namespace ZenFulcrum.EmbeddedBrowser { /// /// Manages a browser that's been opened in an OS-native window outside the game's main window. /// (Presently only used for OS X.) /// public class PopUpBrowser : MonoBehaviour, IBrowserUI { private int windowId; private int browserId; private List messages = new List(); private Browser browser; private BrowserNative.WindowCallbackFunc callbackRef;//don't let this get GC'd private Vector2 delayedResize; public static void Create(int possibleBrowserId) { var go = new GameObject("Pop up browser"); var browser = go.AddComponent(); browser.RequestNativeBrowser(possibleBrowserId); var pop = go.AddComponent(); pop.callbackRef = pop.HandleWindowMessage; pop.windowId = BrowserNative.zfb_windowCreate("", pop.callbackRef); pop.browserId = possibleBrowserId; pop.BrowserCursor = new BrowserCursor(); pop.KeyEvents = new List(); pop.browser = browser; pop.StartCoroutine(pop.FixFocus()); pop.InputSettings = new BrowserInputSettings(); browser.UIHandler = pop; browser.EnableRendering = false;//rendering is done differently, so don't run the usual code } private void HandleWindowMessage(int windowId, IntPtr data) { var msgJSON = Util.PtrToStringUTF8(data); //if (!msgJSON.Contains("mouseMove")) Debug.Log("Window message: " + msgJSON); lock (messages) messages.Add(msgJSON); } public void OnDestroy() { if (!BrowserNative.SymbolsLoaded) return; BrowserNative.zfb_windowClose(windowId); } private IEnumerator FixFocus() { //OS X: Magically, new browser windows that are focused don't think they are focused, even though we told them so. //Hack workaround. yield return null; BrowserNative.zfb_setFocused(browserId, false); yield return null; BrowserNative.zfb_setFocused(browserId, true); yield return null; BrowserNative.zfb_setFocused(browserId, KeyboardHasFocus); } public void InputUpdate() { MouseScroll = Vector2.zero; KeyEvents.Clear(); delayedResize = new Vector2(float.NaN, float.NaN); lock (messages) { for (int i = 0; i < messages.Count; i++) { HandleMessage(messages[i]); } messages.Clear(); } if (!float.IsNaN(delayedResize.x)) { browser.Resize((int)delayedResize.x, (int)delayedResize.y); } } private void HandleMessage(string message) { var msg = JSONNode.Parse(message); switch ((string)msg["type"]) { case "mouseDown": case "mouseUp": { int button = msg["button"]; MouseButton flag = 0; if (button == 0) flag = MouseButton.Left; else if (button == 1) flag = MouseButton.Right; else if (button == 2) flag = MouseButton.Middle; if (msg["type"] == "mouseDown") MouseButtons |= flag; else MouseButtons &= ~flag; break; } case "mouseMove": { var screenPos = new Vector2(msg["x"], msg["y"]); screenPos.x = screenPos.x / browser.Size.x; screenPos.y = screenPos.y / browser.Size.y; MousePosition = screenPos; //Debug.Log("mouse now at " + screenPos); break; } case "mouseFocus": MouseHasFocus = msg["focus"]; break; case "keyboardFocus": KeyboardHasFocus = msg["focus"]; break; case "mouseScroll": { const float mult = 1 / 3f; MouseScroll += new Vector2(msg["x"], msg["y"]) * mult; break; } case "keyDown": case "keyUp": { var ev = new Event(); ev.type = msg["type"] == "keyDown" ? EventType.KeyDown : EventType.KeyUp; ev.character = (char)0; ev.keyCode = KeyMappings.GetUnityKeyCode(msg["code"]); SetMods(ev); //Debug.Log("Convert wkc " + (int)msg["code"] + " to ukc " + ev.keyCode); KeyEvents.Add(ev); break; } case "keyPress": { string characters = msg["characters"]; foreach (char c in characters) { var ev = new Event(); ev.type = EventType.KeyDown; SetMods(ev); ev.character = c; ev.keyCode = 0; KeyEvents.Add(ev); } break; } case "resize": //on OS X (editor at least), resizing hangs the update loop, so we suddenly end up with a bajillion resize //messages we were unable to process. Just record it here and when we've processed everything we'll resize delayedResize.x = msg["w"]; delayedResize.y = msg["h"]; break; case "close": Destroy(gameObject); break; default: Debug.LogWarning("Unknown window event: " + msg.AsJSON); break; } } private bool shiftDown, controlDown, altDown, commandDown; private void SetMods(Event ev) { switch (ev.keyCode) { case KeyCode.LeftShift: case KeyCode.RightShift: shiftDown = ev.type == EventType.KeyDown; break; case KeyCode.LeftControl: case KeyCode.RightControl: controlDown = ev.type == EventType.KeyDown; break; case KeyCode.LeftAlt: case KeyCode.RightAlt: altDown = ev.type == EventType.KeyDown; break; case KeyCode.LeftCommand: case KeyCode.RightCommand: case KeyCode.LeftWindows: case KeyCode.RightWindows: commandDown = ev.type == EventType.KeyDown; break; } ev.shift = shiftDown; ev.control = controlDown; ev.alt = altDown; ev.command = commandDown; } public void Update() { if (!BrowserNative.SymbolsLoaded) return; BrowserNative.zfb_windowRender(windowId, browserId); } public bool MouseHasFocus { get; private set; } public Vector2 MousePosition { get; private set; } public MouseButton MouseButtons { get; private set; } public Vector2 MouseScroll { get; private set; } public bool KeyboardHasFocus { get; private set; } public List KeyEvents { get; private set; } public BrowserCursor BrowserCursor { get; private set; } public BrowserInputSettings InputSettings { get; private set; } } } #endif