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); }
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); }