void UpdateStatusInEditMode()
        {
            switch (_oscOut.mode)
            {
            case OscSendMode.UnicastToSelf:
                _statusInEditMode = OscRemoteStatus.Connected;
                _pingEnumerator   = null;
                break;

            case OscSendMode.Unicast:
                _statusInEditMode = OscRemoteStatus.Unknown;
                if (!Application.isPlaying)
                {
                    _pingEnumerator = OscHelper.StartCoroutineInEditMode(PingCoroutine(), ref _lastPingTime);
                }
                break;

            case OscSendMode.Multicast:
                _statusInEditMode = OscRemoteStatus.Unknown;
                _pingEnumerator   = null;
                break;

            case OscSendMode.Broadcast:
                _statusInEditMode = OscRemoteStatus.Unknown;
                _pingEnumerator   = null;
                break;
            }
        }
Пример #2
0
        void OnEnable()
        {
            oscOut = target as OscOut;
            if (messageBuffer == null)
            {
                messageBuffer = new Queue <OscMessage>(messageBufferCapacity);
            }

            // Get serialized properties.
            _openOnAwake                = serializedObject.FindProperty("_openOnAwake");
            _ipAddress                  = serializedObject.FindProperty("_ipAddress");
            _port                       = serializedObject.FindProperty("_port");
            _multicastLoopback          = serializedObject.FindProperty("_multicastLoopback");
            _bundleMessagesOnEndOfFrame = serializedObject.FindProperty("_bundleMessagesOnEndOfFrame");
            _settingsFoldout            = serializedObject.FindProperty("_settingsFoldout");
            _messagesFoldout            = serializedObject.FindProperty("_messagesFoldout");

            // Store socket info for change check workaround.
            tempIPAddress = _ipAddress.stringValue;
            tempPort      = _port.intValue;

            // Ensure that OscOut scripts will be executed early, so that if Open On Awake is enabled the socket will open before other scripts are called.
            MonoScript script = MonoScript.FromMonoBehaviour(target as MonoBehaviour);

            if (MonoImporter.GetExecutionOrder(script) != executionOrderNum)
            {
                MonoImporter.SetExecutionOrder(script, executionOrderNum);
            }

            // When object is selected in Edit Mode then we start listening.
            if (oscOut.enabled && !Application.isPlaying && !oscOut.isOpen)
            {
                oscOut.Open(oscOut.port, oscOut.ipAddress);
                statusInEditMode = oscOut.mode == OscSendMode.UnicastToSelf ? OscRemoteStatus.Connected : OscRemoteStatus.Unknown;
            }

            // Subscribe to OSC messages
            oscOut.onAnyMessage.AddListener(OnOSCMessage);

            // If in Edit Mode, then start a coroutine that will update the connection status. Unity can't start coroutines in Runtime.
            if (!Application.isPlaying && oscOut.mode == OscSendMode.Unicast)
            {
                pingEnumerator = OscHelper.StartCoroutineInEditMode(PingCoroutine(), ref lastPingTime);
            }
        }
Пример #3
0
        void OnEnable()
        {
            _oscOut = target as OscOut;

            // Get serialized properties.
            _openOnAwake                        = serializedObject.FindProperty("_openOnAwake");
            _remoteIpAddress                    = serializedObject.FindProperty("_remoteIpAddress");
            _port                               = serializedObject.FindProperty("_port");
            _multicastLoopback                  = serializedObject.FindProperty("_multicastLoopback");
            _bundleMessagesOnEndOfFrame         = serializedObject.FindProperty("_bundleMessagesOnEndOfFrame");
            _splitBundlesAvoidingBufferOverflow = serializedObject.FindProperty("_splitBundlesAvoidingBufferOverflow");
            _udpBufferSize                      = serializedObject.FindProperty("_udpBufferSize");
            _settingsFoldout                    = serializedObject.FindProperty("_settingsFoldout");
            _messagesFoldout                    = serializedObject.FindProperty("_messagesFoldout");

            // Store socket info for change check workaround.
            _tempIPAddress = _remoteIpAddress.stringValue;
            _tempPort      = _port.intValue;

            // Ensure that OscOut scripts will be executed early, so that if Open On Awake is enabled the socket will open before other scripts are called.
            MonoScript script = MonoScript.FromMonoBehaviour(target as MonoBehaviour);

            if (MonoImporter.GetExecutionOrder(script) != executionOrderNum)
            {
                MonoImporter.SetExecutionOrder(script, executionOrderNum);
            }

            // When object is selected in Edit Mode then we start listening.
            if (_oscOut.enabled && !Application.isPlaying && !_oscOut.isOpen)
            {
                _oscOut.Open(_oscOut.port, _oscOut.remoteIpAddress);
                _statusInEditMode = _oscOut.mode == OscSendMode.UnicastToSelf ? OscRemoteStatus.Connected : OscRemoteStatus.Unknown;
            }

            // Subscribe to OSC messages
            OscEditorUI.AddInspectorMessageListener(_oscOut, OnOSCMessage, ref _inspectorMessageEventObject);

            // If in Edit Mode, then start a coroutine that will update the connection status. Unity can't start coroutines in Runtime.
            if (!Application.isPlaying && _oscOut.mode == OscSendMode.Unicast)
            {
                _pingEnumerator = OscHelper.StartCoroutineInEditMode(PingCoroutine(), ref _lastPingTime);
            }
        }
        public override void OnInspectorGUI()
        {
            string currentControlName;
            bool   deselect;

            // Check for key down before drawing any fields because they might consume the event.
            bool enterKeyDown = Event.current.type == EventType.KeyDown && Event.current.keyCode == KeyCode.Return;

            // Load serialized object.
            serializedObject.Update();

            // Port field.
            EditorGUI.BeginChangeCheck();
            GUI.SetNextControlName(portControlName);
            int newPort = EditorGUILayout.IntField(_portLabel, _oscOut.port);

            if (EditorGUI.EndChangeCheck())
            {
                _portProp.intValue = newPort;
                if (_oscOut.isOpen)
                {
                    _oscOut.Close();                                  // Close socket while editing
                }
            }
            currentControlName = GUI.GetNameOfFocusedControl();
            bool enterKeyDownPort = enterKeyDown && currentControlName == portControlName;

            if (enterKeyDownPort)
            {
                UnfocusAndUpdateUI();
            }
            deselect = _prevControlName == portControlName && currentControlName != portControlName;
            if ((deselect || enterKeyDownPort) && !_oscOut.isOpen)
            {
                if (_oscOut.Open(_portProp.intValue, _remoteIpAddressProp.stringValue))
                {
                    _tempPort = _portProp.intValue;
                }
                else
                {
                    _portProp.intValue = _tempPort;                     // Undo
                    _oscOut.Open(_portProp.intValue, _remoteIpAddressProp.stringValue);
                }
            }

            // Mode field.
            EditorGUI.BeginChangeCheck();
            OscSendMode newMode = (OscSendMode)EditorGUILayout.EnumPopup(_modeLabel, _oscOut.mode);

            if (EditorGUI.EndChangeCheck() && newMode != _oscOut.mode)
            {
                switch (newMode)
                {
                case OscSendMode.UnicastToSelf:         _oscOut.Open(_oscOut.port); break;

                case OscSendMode.Unicast:                       _oscOut.Open(_oscOut.port, OscConst.unicastAddressDefault); break;

                case OscSendMode.Multicast:                     _oscOut.Open(_oscOut.port, OscConst.multicastAddressDefault); break;

                case OscSendMode.Broadcast:                     _oscOut.Open(_oscOut.port, IPAddress.Broadcast.ToString()); break;
                }
                UpdateStatusInEditMode();
            }

            // IP Address field.
            EditorGUI.BeginChangeCheck();
            GUI.SetNextControlName(ipAddressControlName);
            EditorGUILayout.BeginHorizontal();
            EditorGUILayout.PrefixLabel(_remoteIpAddressLabel);
            string newIp = EditorGUILayout.TextField(_oscOut.remoteIpAddress);               // Field

            if (EditorGUI.EndChangeCheck())
            {
                IPAddress ip;
                if (IPAddress.TryParse(newIp, out ip))
                {
                    _remoteIpAddressProp.stringValue = newIp;                                                       // Accept only valid ip addresses
                }
                if (_oscOut.isOpen)
                {
                    _oscOut.Close();                                  // Close socket while editing
                }
                if (!Application.isPlaying)
                {
                    if (_pingEnumerator != null)
                    {
                        _pingEnumerator = null;                                               // Don't update ping coroutine while editing
                    }
                    if (_statusInEditMode != OscRemoteStatus.Unknown)
                    {
                        _statusInEditMode = OscRemoteStatus.Unknown;
                    }
                }
            }
            GUILayout.FlexibleSpace();
            Rect rect = GUILayoutUtility.GetRect(16, 5);

            rect.width = 5;
            rect.x    += 3;
            rect.y    += 7;
            OscRemoteStatus status = Application.isPlaying ? _oscOut.remoteStatus : _statusInEditMode;

            EditorGUI.DrawRect(rect, StatusToColor(status));
            EditorGUILayout.EndHorizontal();
            GUILayoutUtility.GetRect(1, 2);               // vertical spacing
            currentControlName = GUI.GetNameOfFocusedControl();
            bool enterKeyDownIp = enterKeyDown && currentControlName == ipAddressControlName;

            if (enterKeyDownIp)
            {
                UnfocusAndUpdateUI();
            }
            deselect = _prevControlName == ipAddressControlName && currentControlName != ipAddressControlName;
            if ((deselect || enterKeyDownIp) && !_oscOut.isOpen)                 // All this mess to check for end edit, OMG!!! Not cool.
            {
                if (_oscOut.Open(_portProp.intValue, _remoteIpAddressProp.stringValue))
                {
                    _tempIPAddress = _remoteIpAddressProp.stringValue;
                    UpdateStatusInEditMode();
                }
                else
                {
                    _remoteIpAddressProp.stringValue = _tempIPAddress;                     // Undo
                }
            }

            // Is Open field.
            EditorGUI.BeginDisabledGroup(true);
            EditorGUILayout.Toggle(_isOpenLabel, _oscOut.isOpen);
            EditorGUI.EndDisabledGroup();

            // Open On Awake field.
            EditorGUI.BeginDisabledGroup(Application.isPlaying);
            EditorGUILayout.PropertyField(_openOnAwakeProp, _openOnAwakeLabel);
            EditorGUI.EndDisabledGroup();

            // Settings ...
            _settingsFoldoutProp.boolValue = EditorGUILayout.Foldout(_settingsFoldoutProp.boolValue, _settingsFoldLabel, true);
            if (_settingsFoldoutProp.boolValue)
            {
                EditorGUI.indentLevel++;

                // Udp Buffer Size field.
                EditorGUI.BeginChangeCheck();
                int newBufferSize = EditorGUILayout.IntField(_udpBufferSizeLabel, _udpBufferSizeProp.intValue);
                if (EditorGUI.EndChangeCheck())
                {
                    if (newBufferSize >= OscConst.udpBufferSizeMin)
                    {
                        _udpBufferSizeProp.intValue = newBufferSize;
                        _oscOut.udpBufferSize       = newBufferSize;                   // This will reopen OscOut
                    }
                }

                // Bundle messages automatically.
                EditorGUILayout.BeginHorizontal();
                EditorGUILayout.LabelField(_bundleMessagesAutomaticallyLabel, GUILayout.Width(220));
                GUILayout.FlexibleSpace();
                _bundleMessagesAutomaticallyProp.boolValue = EditorGUILayout.Toggle(_bundleMessagesAutomaticallyProp.boolValue, GUILayout.Width(30));
                EditorGUILayout.EndHorizontal();
                if (!_bundleMessagesAutomaticallyProp.boolValue)
                {
                    EditorGUILayout.HelpBox("Unbundled messages that are send successively are prone to be lost. Only disable this setting if your receiving end does not support OSC bundles.", MessageType.Warning);
                }

                EditorGUI.indentLevel--;
            }

            // Monitor ...
            EditorGUI.BeginDisabledGroup(!_oscOut.isOpen);
            _sb.Clear();
            _sb.Append("Messages (").Append(_oscOut.messageCountSendLastFrame).Append(',').Append(OscDebug.GetPrettyByteSize(_oscOut.byteCountSendLastFrame)).Append(')');
            GUIContent messagesFoldContent = new GUIContent(_sb.ToString(), "Messages received last update");

            _messagesFoldoutProp.boolValue = EditorGUILayout.Foldout(_messagesFoldoutProp.boolValue, messagesFoldContent, true);
            if (_messagesFoldoutProp.boolValue)
            {
                EditorGUI.indentLevel++;
                if (_messageMonitorText.Length > 0)
                {
                    _messageMonitorText.Remove(_messageMonitorText.Length - 1, 1);                                                  // Trim last new line temporarily.
                }
                EditorGUILayout.HelpBox(_messageMonitorText.ToString(), MessageType.None);
                if (_messageMonitorText.Length > 0)
                {
                    _messageMonitorText.Append('\n');
                }
                EditorGUI.indentLevel--;
            }
            EditorGUI.EndDisabledGroup();


            // Apply
            serializedObject.ApplyModifiedProperties();

            // Request OnInspectorGUI to be called every frame as long as inspector is active
            EditorUtility.SetDirty(target);

            // Update ping coroutine manually in Edit Mode. (Unity does not run coroutines in Edit Mode)
            if (!Application.isPlaying && _pingEnumerator != null)
            {
                OscHelper.UpdateCoroutineInEditMode(_pingEnumerator, ref _lastPingTime);
            }

            // Store name of focused control to detect unfocus events
            _prevControlName = GUI.GetNameOfFocusedControl();
        }
Пример #5
0
        public override void OnInspectorGUI()
        {
            string currentControlName;
            bool   deselect;

            // Check for key down before drawing any fields because they might consume the event.
            bool enterKeyDown = Event.current.type == EventType.KeyDown && Event.current.keyCode == KeyCode.Return;

            // Load serialized object.
            serializedObject.Update();

            // Port field.
            EditorGUI.BeginChangeCheck();
            GUI.SetNextControlName(portControlName);
            int newPort = EditorGUILayout.IntField(_portLabel, _oscOut.port);

            if (EditorGUI.EndChangeCheck())
            {
                _port.intValue = newPort;
                if (_oscOut.isOpen)
                {
                    _oscOut.Close();                                  // Close socket while editing
                }
            }
            currentControlName = GUI.GetNameOfFocusedControl();
            bool enterKeyDownPort = enterKeyDown && currentControlName == portControlName;

            if (enterKeyDownPort)
            {
                UnfocusAndUpdateUI();
            }
            deselect = _prevControlName == portControlName && currentControlName != portControlName;
            if ((deselect || enterKeyDownPort) && !_oscOut.isOpen)
            {
                if (_oscOut.Open(_port.intValue, _remoteIpAddress.stringValue))
                {
                    _tempPort = _port.intValue;
                }
                else
                {
                    _port.intValue = _tempPort;                     // Undo
                    _oscOut.Open(_port.intValue, _remoteIpAddress.stringValue);
                }
            }

            // Mode field.
            EditorGUI.BeginChangeCheck();
            OscSendMode newMode = (OscSendMode)EditorGUILayout.EnumPopup(_modeLabel, _oscOut.mode);

            if (EditorGUI.EndChangeCheck() && newMode != _oscOut.mode)
            {
                switch (newMode)
                {
                case OscSendMode.UnicastToSelf:         _oscOut.Open(_oscOut.port); break;

                case OscSendMode.Unicast:                       _oscOut.Open(_oscOut.port, OscConst.unicastAddressDefault); break;

                case OscSendMode.Multicast:                     _oscOut.Open(_oscOut.port, OscConst.multicastAddressDefault); break;

                case OscSendMode.Broadcast:                     _oscOut.Open(_oscOut.port, IPAddress.Broadcast.ToString()); break;
                }
                UpdateStatusInEditMode();
            }

            // IP Address field.
            EditorGUI.BeginChangeCheck();
            GUI.SetNextControlName(ipAddressControlName);
            EditorGUILayout.BeginHorizontal();
            EditorGUILayout.PrefixLabel(_remoteIpAddressLabel);
            string newIp = EditorGUILayout.TextField(_oscOut.remoteIpAddress);               // Field

            if (EditorGUI.EndChangeCheck())
            {
                IPAddress ip;
                if (IPAddress.TryParse(newIp, out ip))
                {
                    _remoteIpAddress.stringValue = newIp;                                                       // Accept only valid ip addresses
                }
                if (_oscOut.isOpen)
                {
                    _oscOut.Close();                                  // Close socket while editing
                }
                if (!Application.isPlaying)
                {
                    if (_pingEnumerator != null)
                    {
                        _pingEnumerator = null;                                               // Don't update ping coroutine while editing
                    }
                    if (_statusInEditMode != OscRemoteStatus.Unknown)
                    {
                        _statusInEditMode = OscRemoteStatus.Unknown;
                    }
                }
            }
            GUILayout.FlexibleSpace();
            Rect rect = GUILayoutUtility.GetRect(16, 5);

            rect.width = 5;
            rect.x    += 3;
            rect.y    += 7;
            OscRemoteStatus status = Application.isPlaying ? _oscOut.remoteStatus : _statusInEditMode;

            EditorGUI.DrawRect(rect, StatusToColor(status));
            EditorGUILayout.EndHorizontal();
            GUILayoutUtility.GetRect(1, 2);               // vertical spacing
            currentControlName = GUI.GetNameOfFocusedControl();
            bool enterKeyDownIp = enterKeyDown && currentControlName == ipAddressControlName;

            if (enterKeyDownIp)
            {
                UnfocusAndUpdateUI();
            }
            deselect = _prevControlName == ipAddressControlName && currentControlName != ipAddressControlName;
            if ((deselect || enterKeyDownIp) && !_oscOut.isOpen)                 // All this mess to check for end edit, OMG!!! Not cool.
            {
                if (_oscOut.Open(_port.intValue, _remoteIpAddress.stringValue))
                {
                    _tempIPAddress = _remoteIpAddress.stringValue;
                    UpdateStatusInEditMode();
                }
                else
                {
                    _remoteIpAddress.stringValue = _tempIPAddress;                     // Undo
                }
            }

            // Is Open field.
            EditorGUI.BeginDisabledGroup(true);
            EditorGUILayout.Toggle(_isOpenLabel, _oscOut.isOpen);
            EditorGUI.EndDisabledGroup();

            // Open On Awake field.
            EditorGUI.BeginDisabledGroup(Application.isPlaying);
            EditorGUILayout.PropertyField(_openOnAwake, _openOnAwakeLabel);
            EditorGUI.EndDisabledGroup();


            // Settings ...
            _settingsFoldout.boolValue = EditorGUILayout.Foldout(_settingsFoldout.boolValue, _settingsFoldLabel, true);
            if (_settingsFoldout.boolValue)
            {
                EditorGUI.indentLevel++;

                // Multicast loopback field.
                EditorGUILayout.BeginHorizontal();
                EditorGUILayout.LabelField(_multicastLoopbackLabel, GUILayout.Width(150));
                GUILayout.FlexibleSpace();
                EditorGUI.BeginChangeCheck();
                _multicastLoopback.boolValue = EditorGUILayout.Toggle(_multicastLoopback.boolValue, GUILayout.Width(30));
                if (EditorGUI.EndChangeCheck() && _oscOut.mode == OscSendMode.Multicast)
                {
                    _oscOut.multicastLoopback = _multicastLoopback.boolValue;
                }
                EditorGUILayout.EndHorizontal();

                // Bundle Messages On End Of Frame field.
                BoolSettingsField(_bundleMessagesOnEndOfFrame, _bundleMessagesOnEndOfFrameLabel);

                // Split Bundles Avoiding Buffer Overflow field.
                BoolSettingsField(_splitBundlesAvoidingBufferOverflow, _splitBundlesAvoidingBufferOverflowLabel);

                // Udp Buffer Size field. (UI horror get get a end changed event).
                GUI.SetNextControlName(bufferSizeControlName);
                EditorGUI.BeginChangeCheck();
                int newBufferSize = EditorGUILayout.IntField(_udpBufferSizeLabel, _udpBufferSize.intValue);
                if (EditorGUI.EndChangeCheck())
                {
                    _tempBufferSize = Mathf.Clamp(newBufferSize, OscConst.udpBufferSizeMin, OscConst.udpBufferSizeMax);
                }
                currentControlName = GUI.GetNameOfFocusedControl();
                bool enterKeyDownBufferSize = enterKeyDown && currentControlName == bufferSizeControlName;
                if (enterKeyDownBufferSize)
                {
                    UnfocusAndUpdateUI();
                }
                deselect = _prevControlName == bufferSizeControlName && currentControlName != bufferSizeControlName;
                if (enterKeyDownBufferSize || deselect)
                {
                    if (_tempBufferSize != _udpBufferSize.intValue)
                    {
                        _udpBufferSize.intValue = _tempBufferSize;
                        _oscOut.udpBufferSize   = _tempBufferSize;                       // This will reopen OscOut
                    }
                }

                EditorGUI.indentLevel--;
            }

            // Messages ...
            EditorGUI.BeginDisabledGroup(!_oscOut.isOpen);
            GUIContent messagesFoldContent = new GUIContent("Messages (" + _oscOut.messageCount + ")", "Messages received since last update");

            _messagesFoldout.boolValue = EditorGUILayout.Foldout(_messagesFoldout.boolValue, messagesFoldContent, true);
            if (_messagesFoldout.boolValue)
            {
                EditorGUI.indentLevel++;

                _sb.Clear();
                _messageStringQueue.CopyTo(_messageStringBuffer, 0);                   // Copy to array so we can iterate backswards.
                for (int i = _messageStringBuffer.Length - 1; i >= 0; i--)
                {
                    _sb.AppendLine(_messageStringBuffer[i]);
                }
                EditorGUILayout.HelpBox(_sb.ToString(), MessageType.None);

                EditorGUI.indentLevel--;
            }
            EditorGUI.EndDisabledGroup();


            // Apply
            serializedObject.ApplyModifiedProperties();

            // Request OnInspectorGUI to be called every frame as long as inspector is active
            EditorUtility.SetDirty(target);

            // Update ping coroutine manually in Edit Mode. (Unity does not run coroutines in Edit Mode)
            if (!Application.isPlaying && _pingEnumerator != null)
            {
                OscHelper.UpdateCoroutineInEditMode(_pingEnumerator, ref _lastPingTime);
            }

            // Store name of focused control to detect unfocus events
            _prevControlName = GUI.GetNameOfFocusedControl();
        }
Пример #6
0
        public override void OnInspectorGUI()
        {
            string currentControlName;
            bool   deselect;

            // Check for key down before drawing any fields because they might consume the event.
            bool enterKeyDown = Event.current.type == EventType.keyDown && Event.current.keyCode == KeyCode.Return;

            // Load serialized object.
            serializedObject.Update();

            // Port field.
            EditorGUI.BeginChangeCheck();
            GUI.SetNextControlName(portControlName);
            int newPort = EditorGUILayout.IntField(_portLabel, oscOut.port);

            if (EditorGUI.EndChangeCheck())
            {
                _port.intValue = newPort;
                if (oscOut.isOpen)
                {
                    oscOut.Close();                                 // Close socket while editing
                }
            }
            currentControlName = GUI.GetNameOfFocusedControl();
            bool enterKeyDownPort = enterKeyDown && currentControlName == portControlName;

            if (enterKeyDownPort)
            {
                UnfocusAndUpdateUI();
            }
            deselect = prevControlName == portControlName && currentControlName != portControlName;
            if ((deselect || enterKeyDownPort) && !oscOut.isOpen)
            {
                if (oscOut.Open(_port.intValue, _ipAddress.stringValue))
                {
                    tempPort = _port.intValue;
                }
                else
                {
                    _port.intValue = tempPort;                     // Undo
                    oscOut.Open(_port.intValue, _ipAddress.stringValue);
                }
            }

            // Mode field.
            EditorGUI.BeginChangeCheck();
            OscSendMode newMode = (OscSendMode)EditorGUILayout.EnumPopup(_modeLabel, oscOut.mode);

            if (EditorGUI.EndChangeCheck() && newMode != oscOut.mode)
            {
                switch (newMode)
                {
                case OscSendMode.UnicastToSelf:         oscOut.Open(oscOut.port); break;

                case OscSendMode.Unicast:                       oscOut.Open(oscOut.port, OscHelper.unicastAddressDefault); break;

                case OscSendMode.Multicast:                     oscOut.Open(oscOut.port, OscHelper.multicastAddressDefault); break;

                case OscSendMode.Broadcast:                     oscOut.Open(oscOut.port, IPAddress.Broadcast.ToString()); break;
                }
                UpdateStatusInEditMode();
            }

            // IP Address field.
            EditorGUI.BeginChangeCheck();
            GUI.SetNextControlName(ipAddressControlName);
            EditorGUILayout.BeginHorizontal();
            EditorGUILayout.PrefixLabel(_ipAdrressLabel);
            string newIp = EditorGUILayout.TextField(oscOut.ipAddress);               // Field

            if (EditorGUI.EndChangeCheck())
            {
                System.Net.IPAddress ip;
                if (System.Net.IPAddress.TryParse(newIp, out ip))
                {
                    _ipAddress.stringValue = newIp;                                                                  // Accept only valid ip addresses
                }
                if (oscOut.isOpen)
                {
                    oscOut.Close();                                 // Close socket while editing
                }
                if (!Application.isPlaying)
                {
                    if (pingEnumerator != null)
                    {
                        pingEnumerator = null;                                              // Don't update ping coroutine while editing
                    }
                    if (statusInEditMode != OscRemoteStatus.Unknown)
                    {
                        statusInEditMode = OscRemoteStatus.Unknown;
                    }
                }
            }
            GUILayout.FlexibleSpace();
            Rect rect = GUILayoutUtility.GetRect(16, 5);

            rect.width = 5;
            rect.x    += 3;
            rect.y    += 7;
            OscRemoteStatus status = Application.isPlaying ? oscOut.remoteStatus : statusInEditMode;

            EditorGUI.DrawRect(rect, StatusToColor(status));
            EditorGUILayout.EndHorizontal();
            GUILayoutUtility.GetRect(1, 2);               // vertical spacing
            currentControlName = GUI.GetNameOfFocusedControl();
            bool enterKeyDownIp = enterKeyDown && currentControlName == ipAddressControlName;

            if (enterKeyDownIp)
            {
                UnfocusAndUpdateUI();
            }
            deselect = prevControlName == ipAddressControlName && currentControlName != ipAddressControlName;

            if ((deselect || enterKeyDownIp) && !oscOut.isOpen)                 // All this mess to check for end edit, OMG!!! Not cool.
            {
                if (oscOut.Open(_port.intValue, _ipAddress.stringValue))
                {
                    tempIPAddress = _ipAddress.stringValue;
                    UpdateStatusInEditMode();
                }
                else
                {
                    _ipAddress.stringValue = tempIPAddress;                     // Undo
                }
            }

            // Is Open field.
            EditorGUI.BeginDisabledGroup(true);
            EditorGUILayout.Toggle(_isOpenLabel, oscOut.isOpen);
            EditorGUI.EndDisabledGroup();

            // Open On Awake field.
            EditorGUI.BeginDisabledGroup(Application.isPlaying);
            EditorGUILayout.PropertyField(_openOnAwake, _openOnAwakeLabel);
            EditorGUI.EndDisabledGroup();

            EditorGUI.indentLevel++;

            // Settings ...
            _settingsFoldout.boolValue = EditorGUILayout.Foldout(_settingsFoldout.boolValue, _settingsFoldLabel);
            if (_settingsFoldout.boolValue)
            {
                // Multicast loopback field.
                EditorGUILayout.BeginHorizontal();
                EditorGUILayout.LabelField(_multicastLoopbackLabel, GUILayout.Width(150));
                GUILayout.FlexibleSpace();
                EditorGUI.BeginChangeCheck();
                _multicastLoopback.boolValue = EditorGUILayout.Toggle(_multicastLoopback.boolValue, GUILayout.Width(30));
                if (EditorGUI.EndChangeCheck() && oscOut.mode == OscSendMode.Multicast)
                {
                    oscOut.multicastLoopback = _multicastLoopback.boolValue;
                }
                EditorGUILayout.EndHorizontal();

                // Bundle Messages On End Of Frame field.
                BoolSettingsField(_bundleMessagesOnEndOfFrame, _bundleMessagesOnEndOfFrameLabel);
            }

            // Messages ...
            EditorGUI.BeginDisabledGroup(!oscOut.isOpen);
            GUIContent messagesFoldContent = new GUIContent("Messages (" + oscOut.messageCount + ")", "Messages received since last update");

            _messagesFoldout.boolValue = EditorGUILayout.Foldout(_messagesFoldout.boolValue, messagesFoldContent);
            if (_messagesFoldout.boolValue)
            {
                OscMessage[]  messages     = messageBuffer.ToArray();
                StringBuilder messagesText = new StringBuilder();
                for (int m = messages.Length - 1; m >= 0; m--)
                {
                    messagesText.Append((m != messages.Length - 1 ? Environment.NewLine : "") + messages[m].ToString());
                }
                EditorGUILayout.HelpBox(messagesText.ToString(), MessageType.None);
            }
            EditorGUI.EndDisabledGroup();

            EditorGUI.indentLevel--;

            // Apply
            serializedObject.ApplyModifiedProperties();

            // Request OnInspectorGUI to be called every frame as long as inspector is active
            EditorUtility.SetDirty(target);

            // Update ping coroutine manually in Edit Mode. (Unity does not run coroutines in Edit Mode)
            if (!Application.isPlaying && pingEnumerator != null)
            {
                OscHelper.UpdateCoroutineInEditMode(pingEnumerator, ref lastPingTime);
            }

            // Store name of focused control to detect unfocus events
            prevControlName = GUI.GetNameOfFocusedControl();
        }