public IUnoHostControl Create(
            AbsoluteFilePath assemblyPath,
            Command onFocused,
            Menu contextMenu,
            AbsoluteFilePath userDataPath,
            Action <IUnoHostControl> initialize,
            Action <OpenGlVersion> gotVersion,
            params string[] arguments)
        {
            UnoHostProcess.Application = ExternalApplication.FromNativeExe(typeof(UnoHostControlFactory).Assembly.GetCodeBaseFilePath());

            var dispatcher = Fusion.Application.MainThread;

            var messagesToHost = new Subject <IBinaryMessage>();

            var unoHost = UnoHostProcess.Spawn(assemblyPath, messagesToHost, userDataPath, /*TODO*/ new List <string>());

            unoHost.Process.Subscribe(process => new Job().AddProcess(process.Id));

            var windowCreated = unoHost.Receieve(WindowCreatedMessage.TryParse).Replay(1);

            windowCreated.Connect();

            var control = new UnoHostControlImplementation()
            {
                _disposable = unoHost,
                Messages    = unoHost.Messages,
                MessagesTo  = messagesToHost,
                Process     = unoHost.Process,
            };

            control.Control =
                Fusion.Control.Create(location =>
            {
                var dummyControl = Shapes.Rectangle();
                dummyControl.Mount(location);
                var dummyElement = (FrameworkElement)dummyControl.NativeHandle;

                location.IsRooted
                .ObserveOn(dispatcher)
                .SubscribeUsing(rooted => rooted
                                                        ? ContextMenuImplementation.AddMenuTemporarily(dummyElement, contextMenu, dispatcher)
                                                        : Disposable.Empty);

                var mainWindow = DataBinding
                                 .ObservableFromNativeEvent <object>(dummyElement, "LayoutUpdated")
                                 .StartWith(new object())
                                 .Select(_ =>
                {
                    var hwndSource1 = PresentationSource.FromVisual(dummyElement);
                    if (hwndSource1 == null)
                    {
                        return(Optional.None <System.Windows.Window>());
                    }

                    var window = hwndSource1.RootVisual as System.Windows.Window;
                    if (window == null)
                    {
                        return(Optional.None <System.Windows.Window>());
                    }

                    return(Optional.Some(window));
                })
                                 .DistinctUntilChanged()
                                 .NotNone()
                                 .Replay(1).RefCount();

                var mainWindowHandle = mainWindow.Select(currentWindow =>
                {
                    var hwndSource = (HwndSource)PresentationSource.FromVisual(currentWindow);
                    if (hwndSource == null)
                    {
                        return(Optional.None());
                    }

                    return(Optional.Some(hwndSource.Handle));
                })
                                       .NotNone()
                                       .Replay(1);

                mainWindowHandle.Connect();

                var focused = unoHost.Receieve(WindowFocusMessage.TryParse)
                              .Where(focusState => focusState == FocusState.Focused);

                focused.CombineLatest(mainWindowHandle, (a, b) => b)
                .ObserveOn(dispatcher)
                .Subscribe(t => WinApi.ShowWindow(t, WinApi.ShowWindowEnum.ShowNoActivate));

                unoHost.Receieve(WindowContextMenuMessage.TryParse)
                .ObserveOn(dispatcher)
                .Subscribe(t => dummyElement.ContextMenu.IsOpen = t);

                unoHost.Receieve(WindowMouseScrollMessage.TryParse)
                .ObserveOn(dispatcher)
                .Subscribe(deltaWheel =>
                {
                    var delta       = deltaWheel;
                    var routedEvent = Mouse.MouseWheelEvent;

                    dummyElement.RaiseEvent(
                        new MouseWheelEventArgs(
                            Mouse.PrimaryDevice,
                            Environment.TickCount, delta)
                    {
                        RoutedEvent = routedEvent,
                        Source      = dummyElement
                    });
                });


                // This is a fix for a commit that "fixed" handling the shortcuts, but broke just
                // plain typing in a TextInput.
                // See https://github.com/fusetools/Fuse/issues/3342
                // and https://github.com/fusetools/Fuse/issues/3887

                // Workaround to fix a problem with handling shortcut keys in the application while
                // the viewport has focus and the user is typing in an TextInput in the app.
                // We have give the main window focus to handle the shortcut keys, but then
                // the viewport will lose focus. Current work around is to only do this when
                // the user presses down Ctrl.

                unoHost.Receieve(WindowKeyDown.TryParse)
                .WithLatestFromBuffered(mainWindow, (t, w) => new { Keys = t, Window = w })
                .ObserveOn(dispatcher)
                .Subscribe(t =>
                {
                    var modifiers = System.Windows.Input.Keyboard.PrimaryDevice.Modifiers;
                    var alt       = modifiers.HasFlag(System.Windows.Input.ModifierKeys.Alt);

                    // Activate the main window if the Ctrl-key was pressed.
                    // Ctrl + Alt means AltGr which we want to keep in the Uno host e.g. for the '@' key on Nordic keyboards.
                    if (t.Keys == Keys.ControlKey && !alt)
                    {
                        t.Window.Activate();
                    }
                });

                focused
                .WithLatestFromBuffered(onFocused.Execute.ConnectWhile(dummyControl.IsRooted), (_, c) => c)
                .Subscribe(c => c());

                var overlayForm = new OverlayForm();

                Observable
                .CombineLatest(windowCreated, mainWindowHandle,
                               (viewportHwnd, mainHwnd) => new
                {
                    ViewportHwnd = viewportHwnd,
                    MainHwnd     = mainHwnd,
                })
                .ObserveOn(dispatcher)
                .SubscribeUsing(t =>
                                overlayForm.BindTo(t.ViewportHwnd, t.MainHwnd, dummyControl, dummyElement.GetDpi()));

                unoHost.Messages
                .SelectMany(m => m.TryParse(Ready.MessageType, Ready.ReadDataFrom))
                .Take(1)
                .Subscribe(_ => initialize(control));

                unoHost.Messages
                .SelectSome(OpenGlVersionMessage.TryParse)
                .Subscribe(gotVersion);

                unoHost.Messages.Connect();


                return(dummyElement);
            });

            return(control);
        }
        public IUnoHostControl Create(
            AbsoluteFilePath assemblyPath,
            Command onFocused,
            Menu contextMenu,
            AbsoluteFilePath userDataPath,
            Action <IUnoHostControl> initialize,
            Action <OpenGlVersion> gotVersion,
            params string[] arguments)
        {
            var currentExeDir = typeof(Application).Assembly.GetCodeBaseFilePath().ContainingDirectory;
            var contentsDir   = currentExeDir / ".." / ".." / "..";
            var unoHostBundle = contentsDir / "UnoHost.app";

            UnoHostProcess.Application = ExternalApplication.FromAppBundle(unoHostBundle);

            var unoHostExe = unoHostBundle / "Contents" / "MacOS" / new FileName("UnoHost");

            UnoHostProcess.Application = ExternalApplication.FromNativeExe(unoHostExe);

            var dispatcher = Fusion.Application.MainThread;

            var messagesToHost = new Subject <IBinaryMessage>();

            var unoHost = UnoHostProcess.Spawn(assemblyPath, messagesToHost, userDataPath, /*TODO*/ new List <string>());

            var control = new UnoHostControlImplementation()
            {
                Disposables = Disposable.Combine(unoHost),
                Messages    = unoHost.Messages,
                MessagesTo  = messagesToHost,
                Process     = unoHost.Process,
            };

            control.Control =
                Fusion.Control.Create(self =>
            {
                var view = UnoHostViewFactory.Create(unoHost.Messages, messagesToHost);

                self.BindNativeDefaults(view.View, dispatcher);

                view.Focus
                .Where(foc => foc == FocusState.Focused)
                .WithLatestFromBuffered(onFocused.Execute.ConnectWhile(self.IsRooted), (_, c) => c)
                .Subscribe(c => c());

                unoHost.Messages
                .SelectMany(m => m.TryParse(Ready.MessageType, Ready.ReadDataFrom))
                .Take(1)
                .Subscribe(_ => initialize(control));

                unoHost.Messages
                .SelectSome(OpenGlVersionMessage.TryParse)
                .Subscribe(gotVersion);

                unoHost.Messages.Connect();

                return(view.View);
            }).SetContextMenu(contextMenu);

            return(control);
        }
Esempio n. 3
0
        public static IFuse Initialize(string programName, List <string> args)
        {
            var systemId  = SystemGuidLoader.LoadOrCreateOrEmpty();
            var sessionId = Guid.NewGuid();

            var report = ReportFactory.GetReporter(systemId, sessionId, programName);

            AppDomain.CurrentDomain.ReportUnhandledExceptions(report);
            report.Info("Initializing with arguments '" + args.Join(" ") + "'");

            // Report running mono version
            Type type = Type.GetType("Mono.Runtime");

            if (type != null)
            {
                MethodInfo displayName = type.GetMethod("GetDisplayName", BindingFlags.NonPublic | BindingFlags.Static);
                if (displayName != null)
                {
                    report.Info("Running with Mono " + displayName.Invoke(null, null));
                }
            }

            EnsureSpecialFoldersExist();

            var fuseExeOverride = args
                                  .TryParse("override-fuse-exe")
                                  .SelectMany(AbsoluteFilePath.TryParse);

            var rootDirSetByArgs = Optional.None <AbsoluteDirectoryPath>();            /*args
                                                                                        * .FirstOrNone(a => a.StartsWith("fuse-root"))
                                                                                        * .Select(a => a.Substring(a.IndexOf("=", StringComparison.InvariantCulture) + 1, a.Length - 1 - a.IndexOf("=", StringComparison.InvariantCulture)))
                                                                                        * .Select(AbsoluteDirectoryPath.Parse);*/

            var os = Platform.OperatingSystem;

            var fuseRoot = rootDirSetByArgs
                           .Or(() => GetFuseRoot(os))
                           .OrThrow(new FuseRootDirectoryWasNotFound());

            if (os != OS.Mac && os != OS.Windows)
            {
                throw new UnsupportedPlatformException(os);
            }

            var isMac = os == OS.Mac;


            var mono = isMac ? Optional.Some(FindMonoExe(fuseRoot)) : Optional.None();

            var codeAssistanceService = new FileName("Fuse.CodeAssistanceService.exe");

            var fuseExe = fuseExeOverride.Or(isMac
                                ? fuseRoot / "MacOS" / new FileName("Fuse")
                                : fuseRoot / new FileName("Fuse.exe"));

            var unoExe = FindUnoExe(fuseRoot);

            var impl = new FuseImpl
            {
                FuseRoot = fuseRoot,
                Version  = SystemInfoFactory.GetBuildVersion(),

                UnoExe  = unoExe,
                FuseExe = fuseExe,
                MonoExe = mono,

                Fuse = ExternalApplication.FromNativeExe(fuseExe),

                // Tools

                Designer = isMac
                                        ? ExternalApplication.FromAppBundle(fuseRoot / new FileName("Fuse Studio.app"))
                                        : ExternalApplication.FromNativeExe(fuseRoot / new FileName("Fuse Studio.exe")),

                // Services

                CodeAssistance = isMac
                                        ? ExternalApplication.FromMonoExe(fuseRoot / "MonoBundle" / codeAssistanceService, mono)
                                        : ExternalApplication.FromNativeExe(fuseRoot / codeAssistanceService),

                Tray = isMac
                                        ? ExternalApplication.FromNativeExe(fuseRoot / "Fuse Tray.app" / "Contents" / "MacOS" / new FileName("Fuse Tray"))
                                        : ExternalApplication.FromNativeExe(fuseRoot / new FileName("Fuse-Tray.exe")),

                LogServer = isMac
                                        ? ExternalApplication.FromNativeExe(fuseRoot / new FileName("fuse-logserver"))
                                        : null,

                // Uno

                Uno = isMac
                                        ? ExternalApplication.FromMonoExe(unoExe, mono)
                                        : ExternalApplication.FromNativeExe(unoExe),

                // System paths

                UserDataDir = isMac
                                        ? AbsoluteDirectoryPath.Parse(Environment.GetFolderPath(Environment.SpecialFolder.Personal)) / ".fuse"
                                        : AbsoluteDirectoryPath.Parse(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData)) /
                              "Fusetools" / "Fuse",

                ProjectsDir =
                    AbsoluteDirectoryPath.Parse(Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments)) / "Fuse",

                SystemId  = systemId,
                SessionId = sessionId,
                Report    = report
            };

            var partial = args.Remove("--partial") | args.Remove("-p");

            if (!partial)
            {
                impl.CheckCompleteness();
            }

            // Set assembly configuration for .unoconfig, to be able to find correct bin/$(Configuration) directories.
#if DEBUG
            UnoConfigFile.Constants["Configuration"] = "Debug";
#else
            UnoConfigFile.Constants["Configuration"] = "Release";
#endif
            return(impl);
        }