示例#1
0
        public StartupHandler(IScreens screensInfo, IVirtualDesktopCollection virtualDesktops, IContainerNodeCreater containerNodeCreator, IVirtualDesktopCreater virtualDesktopCreator, IScreenNodeCreater screenNodeCreator, ISignalHandler signal, IKeyHandler keyHandler, ICommandHelper commandHelper, IWindowTracker windowTracker, IPInvokeHandler pinvokeHandler)
        {
            this.screensInfo          = screensInfo;
            this.desktops             = virtualDesktops;
            this.containerNodeCreator = containerNodeCreator;
            this.signal          = signal;
            this.keyHandler      = keyHandler;
            this.commandHelper   = commandHelper;
            this.windowTracker   = windowTracker;
            this.pinvokeHandler  = pinvokeHandler;
            this.HandlesToIgnore = new List <IntPtr>();

            var result = screensInfo.AllScreens.GetOrderRect();

            _screens = result.rect.ToArray();

            for (var i = 0; i < desktops.Count; i++)
            {
                var screensToAdd = _screens.Select((rect, i) => screenNodeCreator.Create("Screen" + i, rect, dir: result.direction)).ToArray();
                desktops[i] = virtualDesktopCreator.Create(i, rect: _screens.TotalRect(), dir: result.direction, childs: screensToAdd);
                desktops[i].Hide();
            }

            desktops.Index = 0;
            desktops.ActiveDesktop.Show();
        }
示例#2
0
 public ContainerNodeCreater(IFocusHandler focusHandler, IWindowEventHandler windowHandler, IWindowTracker windowTracker, IPInvokeHandler pinvokeHandler)
 {
     this.focusHandler   = focusHandler;
     this.windowHandler  = windowHandler;
     this.windowTracker  = windowTracker;
     this.pinvokeHandler = pinvokeHandler;
 }
示例#3
0
 public ContainerNode(IRenderer renderer, IContainerNodeCreater containerNodeCreator, IWindowTracker windowTracker, RECT rect, Direction direction = Direction.Horizontal, Node parent = null) : base(rect, direction, parent)
 {
     this.Renderer             = renderer;
     this.containerNodeCreator = containerNodeCreator;
     this.windowTracker        = windowTracker;
     Childs = new Collection <Node>();
     _ignoreChildsOnUpdateRect = new List <int>();
 }
示例#4
0
 public WindowNodeCreater(IDragHandler dragHandler, IFocusHandler focusHandler, ISignalHandler signalHandler, IWindowEventHandler windowHandler, IWindowTracker windowTracker, IPInvokeHandler pinvokeHandler)
 {
     this.dragHandler    = dragHandler;
     this.focusHandler   = focusHandler;
     this.signalHandler  = signalHandler;
     this.windowHandler  = windowHandler;
     this.windowTracker  = windowTracker;
     this.pinvokeHandler = pinvokeHandler;
 }
示例#5
0
        public HotkeyIsActiveTrigger(
            [NotNull] IHotkeyConverter hotkeyConverter,
            [NotNull] IKeyboardEventsSource eventSource,
            [NotNull][Dependency(WellKnownWindows.MainWindow)] IWindowTracker mainWindowTracker,
            [NotNull][Dependency(WellKnownSchedulers.UI)] IScheduler uiScheduler)
        {
            this.hotkeyConverter = hotkeyConverter;
            Disposable
            .Create(() => Log.Debug($"Disposing HotkeyTrigger, gesture: {Hotkey} (mode: {HotkeyMode})"))
            .AddTo(Anchors);
            IsActive = true;
            this.RaiseWhenSourceValue(x => x.Properties, this, x => x.IsActive).AddTo(Anchors);

            this.WhenAnyValue(x => x.Hotkey)
            .Select(hotkey => hotkey == null ? Observable.Empty <HotkeyData>() : BuildHotkeySubscription(eventSource))
            .Switch()
            .DistinctUntilChanged(x => new { x.Hotkey, x.KeyDown })
            .Where(
                hotkeyData =>
            {
                /*
                 * This method MUST be executed on the same thread which emitted Key/Mouse event
                 * otherwise .Handled value will be ignored due to obvious concurrency reasons
                 */
                if (mainWindowTracker.ActiveProcessId != CurrentProcessId)
                {
                    Log.Debug($"Application is NOT active, processing hotkey {hotkeyData.Hotkey} (isDown: {hotkeyData.KeyDown}, suppressKey: {suppressKey},  configuredKey: {Hotkey}, mode: {HotkeyMode})");
                    if (suppressKey)
                    {
                        hotkeyData.MarkAsHandled();
                    }
                    return(true);
                }

                Log.Debug($"Application is active, skipping hotkey {hotkeyData.Hotkey} (isDown: {hotkeyData.KeyDown}, suppressKey: {suppressKey},  configuredKey: {Hotkey}, mode: {HotkeyMode})");
                return(false);
            })
            .Subscribe(
                hotkeyData =>
            {
                Log.Debug($"Hotkey {hotkeyData.Hotkey} pressed, state: {(hotkeyData.KeyDown ? "down" : "up")}, suppressed: {suppressKey}");

                if (HotkeyMode == HotkeyMode.Click)
                {
                    if (hotkeyData.KeyDown)
                    {
                        IsActive = !IsActive;
                    }
                }
                else
                {
                    IsActive = !IsActive;
                }
            },
                Log.HandleUiException)
            .AddTo(Anchors);
        }
示例#6
0
 public VirtualDesktopCreater(IServiceProvider service)// IFocusTracker focusTracker, IFocusHandler focusHandler, IWindowEventHandler windowHandler, IWindowTracker windowTracker, IPInvokeHandler pinvokeHandler, IContainerNodeCreater containerCreater)
 {
     this.service          = service;
     this.focusHandler     = service.GetRequiredService <IFocusHandler>();
     this.windowHandler    = service.GetRequiredService <IWindowEventHandler>();
     this.windowTracker    = service.GetRequiredService <IWindowTracker>();
     this.pinvokeHandler   = service.GetRequiredService <IPInvokeHandler>();
     this.containerCreater = service.GetRequiredService <IContainerNodeCreater>();
     this.screenCreater    = service.GetRequiredService <IScreenNodeCreater>();
     this.signalHandler    = service.GetRequiredService <ISignalHandler>();
 }
示例#7
0
        public MainWindowViewModel(
            [NotNull] IAppArguments appArguments,
            [NotNull] IFactory <IStartupManager, StartupManagerArgs> startupManagerFactory,
            [NotNull] IMicrophoneControllerEx microphoneController,
            [NotNull] IMicSwitchOverlayViewModel overlay,
            [NotNull] IAudioNotificationsManager audioNotificationsManager,
            [NotNull] IFactory <IAudioNotificationSelectorViewModel> audioSelectorFactory,
            [NotNull] IApplicationUpdaterViewModel appUpdater,
            [NotNull][Dependency(WellKnownWindows.MainWindow)] IWindowTracker mainWindowTracker,
            [NotNull] IConfigProvider <MicSwitchConfig> configProvider,
            [NotNull] IComplexHotkeyTracker hotkeyTracker,
            [NotNull] IMicrophoneProvider microphoneProvider,
            [NotNull] IImageProvider imageProvider,
            [NotNull] IViewController viewController,
            [NotNull][Dependency(WellKnownSchedulers.UI)] IScheduler uiScheduler)
        {
            var startupManagerArgs = new StartupManagerArgs
            {
                UniqueAppName   = $"{appArguments.AppName}{(appArguments.IsDebugMode ? "-debug" : string.Empty)}",
                ExecutablePath  = appUpdater.GetLatestExecutable().FullName,
                CommandLineArgs = appArguments.StartupArgs,
                AutostartFlag   = appArguments.AutostartFlag
            };

            this.startupManager = startupManagerFactory.Create(startupManagerArgs);

            this.appArguments         = appArguments;
            this.microphoneController = microphoneController;

            ApplicationUpdater     = appUpdater;
            this.mainWindowTracker = mainWindowTracker;
            this.configProvider    = configProvider;
            this.imageProvider     = imageProvider;
            this.RaiseWhenSourceValue(x => x.IsActive, mainWindowTracker, x => x.IsActive).AddTo(Anchors);

            AudioSelectorWhenMuted   = audioSelectorFactory.Create();
            AudioSelectorWhenUnmuted = audioSelectorFactory.Create();

            Observable.Merge(
                AudioSelectorWhenMuted.ObservableForProperty(x => x.SelectedValue, skipInitial: true),
                AudioSelectorWhenUnmuted.ObservableForProperty(x => x.SelectedValue, skipInitial: true))
            .Subscribe(() => this.RaisePropertyChanged(nameof(AudioNotification)), Log.HandleException)
            .AddTo(Anchors);

            configProvider.ListenTo(x => x.Notification)
            .ObserveOn(uiScheduler)
            .Subscribe(cfg =>
            {
                Log.Debug($"Applying new notification configuration: {cfg.DumpToTextRaw()} (current: {AudioNotification.DumpToTextRaw()})");
                AudioNotification = cfg;
            }, Log.HandleException)
            .AddTo(Anchors);

            configProvider.ListenTo(x => x.IsPushToTalkMode)
            .ObserveOn(uiScheduler)
            .Subscribe(x =>
            {
                IsPushToTalkMode = x;
                if (isPushToTalkMode)
                {
                    MuteMicrophoneCommand.Execute(true);
                }
            }, Log.HandleException)
            .AddTo(Anchors);

            configProvider.ListenTo(x => x.SuppressHotkey)
            .ObserveOn(uiScheduler)
            .Subscribe(x => SuppressHotkey = x, Log.HandleException)
            .AddTo(Anchors);

            Observable.Merge(configProvider.ListenTo(x => x.MicrophoneHotkey), configProvider.ListenTo(x => x.MicrophoneHotkeyAlt))
            .Select(x => new
            {
                Hotkey    = (HotkeyGesture) new HotkeyConverter().ConvertFrom(configProvider.ActualConfig.MicrophoneHotkey ?? string.Empty),
                HotkeyAlt = (HotkeyGesture) new HotkeyConverter().ConvertFrom(configProvider.ActualConfig.MicrophoneHotkeyAlt ?? string.Empty),
            })
            .ObserveOn(uiScheduler)
            .Subscribe(cfg =>
            {
                Log.Debug($"Setting new hotkeys configuration: {cfg.DumpToTextRaw()} (current: {hotkey}, alt: {hotkeyAlt})");
                Hotkey    = cfg.Hotkey;
                HotkeyAlt = cfg.HotkeyAlt;
            }, Log.HandleException)
            .AddTo(Anchors);

            Overlay = overlay;

            this.RaiseWhenSourceValue(x => x.RunAtLogin, startupManager, x => x.IsRegistered, uiScheduler).AddTo(Anchors);
            this.RaiseWhenSourceValue(x => x.MicrophoneVolume, microphoneController, x => x.VolumePercent, uiScheduler).AddTo(Anchors);
            this.RaiseWhenSourceValue(x => x.MicrophoneMuted, microphoneController, x => x.Mute, uiScheduler).AddTo(Anchors);
            ImageProvider = imageProvider;

            microphoneProvider.Microphones
            .ToObservableChangeSet()
            .ObserveOn(uiScheduler)
            .Bind(out var microphones)
            .Subscribe()
            .AddTo(Anchors);
            Microphones = microphones;

            this.ObservableForProperty(x => x.MicrophoneMuted, skipInitial: true)
            .DistinctUntilChanged()
            .Where(x => !MicrophoneLine.IsEmpty)
            .Skip(1)     // skip initial setup
            .Subscribe(x =>
            {
                var cfg = configProvider.ActualConfig.Notification;
                var notificationToPlay = x.Value ? cfg.On : cfg.Off;
                Log.Debug($"Playing notification {notificationToPlay} (cfg: {cfg.DumpToTextRaw()})");
                audioNotificationsManager.PlayNotification(notificationToPlay);
            }, Log.HandleUiException)
            .AddTo(Anchors);

            this.WhenAnyValue(x => x.MicrophoneLine)
            .DistinctUntilChanged()
            .Subscribe(x => microphoneController.LineId = x, Log.HandleUiException)
            .AddTo(Anchors);

            Observable.Merge(
                configProvider.ListenTo(x => x.MicrophoneLineId).ToUnit(),
                Microphones.ToObservableChangeSet().ToUnit())
            .Select(_ => configProvider.ActualConfig.MicrophoneLineId)
            .ObserveOn(uiScheduler)
            .Subscribe(configLineId =>
            {
                Log.Debug($"Microphone line configuration changed, lineId: {configLineId}, known lines: {Microphones.DumpToTextRaw()}");

                var micLine = Microphones.FirstOrDefault(line => line.Equals(configLineId));
                if (micLine.IsEmpty)
                {
                    Log.Debug($"Selecting first one of available microphone lines, known lines: {Microphones.DumpToTextRaw()}");
                    micLine = Microphones.FirstOrDefault();
                }
                MicrophoneLine = micLine;
                MuteMicrophoneCommand.ResetError();
            }, Log.HandleUiException)
            .AddTo(Anchors);

            hotkeyTracker
            .WhenAnyValue(x => x.IsActive)
            .ObserveOn(uiScheduler)
            .Subscribe(async isActive =>
            {
                if (isPushToTalkMode)
                {
                    await MuteMicrophoneCommandExecuted(!isActive);
                }
                else
                {
                    await MuteMicrophoneCommandExecuted(!MicrophoneMuted);
                }
            }, Log.HandleUiException)
            .AddTo(Anchors);

            ToggleOverlayLockCommand = CommandWrapper.Create(
                () =>
            {
                if (overlay.IsLocked && overlay.UnlockWindowCommand.CanExecute(null))
                {
                    overlay.UnlockWindowCommand.Execute(null);
                }
                else if (!overlay.IsLocked && overlay.LockWindowCommand.CanExecute(null))
                {
                    overlay.LockWindowCommand.Execute(null);
                }
            });

            ExitAppCommand = CommandWrapper.Create(
                () =>
            {
                Log.Debug("Closing application");
                configProvider.Save(configProvider.ActualConfig);
                Application.Current.Shutdown();
            });

            this.WhenAnyValue(x => x.WindowState)
            .Subscribe(x => ShowInTaskbar = x != WindowState.Minimized, Log.HandleUiException)
            .AddTo(Anchors);

            ShowAppCommand = CommandWrapper.Create(
                () =>
            {
                if (Visibility != Visibility.Visible)
                {
                    viewController.Show();
                }
                else
                {
                    viewController.Hide();
                }
            });

            OpenAppDataDirectoryCommand = CommandWrapper.Create(OpenAppDataDirectory);

            ResetOverlayPositionCommand = CommandWrapper.Create(ResetOverlayPositionCommandExecuted);

            RunAtLoginToggleCommand          = CommandWrapper.Create <bool>(RunAtLoginCommandExecuted);
            MuteMicrophoneCommand            = CommandWrapper.Create <bool>(MuteMicrophoneCommandExecuted);
            SelectMicrophoneIconCommand      = CommandWrapper.Create(SelectMicrophoneIconCommandExecuted);
            SelectMutedMicrophoneIconCommand = CommandWrapper.Create(SelectMutedMicrophoneIconCommandExecuted);
            ResetMicrophoneIconsCommand      = CommandWrapper.Create(ResetMicrophoneIconsCommandExecuted);

            var executingAssemblyName = Assembly.GetExecutingAssembly().GetName();

            Title = $"{(appArguments.IsDebugMode ? "[D]" : "")} {executingAssemblyName.Name} v{executingAssemblyName.Version}";

            WindowState = WindowState.Minimized;
            viewController
            .WhenLoaded
            .Take(1)
            .Select(() => configProvider.ListenTo(y => y.StartMinimized))
            .Switch()
            .Take(1)
            .ObserveOn(uiScheduler)
            .Subscribe(
                x =>
            {
                if (x)
                {
                    Log.Debug($"StartMinimized option is active - minimizing window, current state: {WindowState}");
                    StartMinimized = true;
                    viewController.Hide();
                }
                else
                {
                    Log.Debug($"StartMinimized option is not active - showing window as Normal, current state: {WindowState}");
                    StartMinimized = false;
                    viewController.Show();
                }
            }, Log.HandleUiException)
            .AddTo(Anchors);

            // config processing
            Observable.Merge(
                this.ObservableForProperty(x => x.MicrophoneLine, skipInitial: true).ToUnit(),
                this.ObservableForProperty(x => x.IsPushToTalkMode, skipInitial: true).ToUnit(),
                this.ObservableForProperty(x => x.AudioNotification, skipInitial: true).ToUnit(),
                this.ObservableForProperty(x => x.HotkeyAlt, skipInitial: true).ToUnit(),
                this.ObservableForProperty(x => x.Hotkey, skipInitial: true).ToUnit(),
                this.ObservableForProperty(x => x.SuppressHotkey, skipInitial: true).ToUnit(),
                this.ObservableForProperty(x => x.StartMinimized, skipInitial: true).ToUnit())
            .Throttle(ConfigThrottlingTimeout)
            .ObserveOn(uiScheduler)
            .Subscribe(() =>
            {
                var config = configProvider.ActualConfig.CloneJson();
                config.IsPushToTalkMode    = IsPushToTalkMode;
                config.MicrophoneHotkey    = (Hotkey ?? new HotkeyGesture()).ToString();
                config.MicrophoneHotkeyAlt = (HotkeyAlt ?? new HotkeyGesture()).ToString();
                config.MicrophoneLineId    = MicrophoneLine;
                config.Notification        = AudioNotification;
                config.SuppressHotkey      = SuppressHotkey;
                config.StartMinimized      = StartMinimized;
                configProvider.Save(config);
            }, Log.HandleUiException)
            .AddTo(Anchors);
        }
示例#8
0
 public FixedContainerNode(IRenderer renderer, IContainerNodeCreater containerNodeCreator, IWindowTracker windowTracker, RECT rect, Direction direction = Direction.Horizontal, Node parent = null)
     : base(renderer, containerNodeCreator, windowTracker, rect, direction, parent)
 {
 }
示例#9
0
        public MainWindowViewModel(
            IAppArguments appArguments,
            IApplicationAccessor applicationAccessor,
            IFactory <IStartupManager, StartupManagerArgs> startupManagerFactory,
            IMicSwitchOverlayViewModel overlay,
            IMicrophoneControllerViewModel microphoneControllerViewModel,
            IOverlayWindowController overlayWindowController,
            IWaveOutDeviceSelectorViewModel waveOutDeviceSelector,
            IAudioNotificationsManager audioNotificationsManager,
            IFactory <IAudioNotificationSelectorViewModel> audioSelectorFactory,
            IApplicationUpdaterViewModel appUpdater,
            [Dependency(WellKnownWindows.MainWindow)] IWindowTracker mainWindowTracker,
            IConfigProvider <MicSwitchConfig> configProvider,
            IConfigProvider <MicSwitchOverlayConfig> overlayConfigProvider,
            IImageProvider imageProvider,
            IErrorMonitorViewModel errorMonitor,
            IAudioNotificationsManager notificationsManager,
            IWindowViewController viewController,
            [Dependency(WellKnownSchedulers.UI)] IScheduler uiScheduler)
        {
            Title = $"{(appArguments.IsDebugMode ? "[D]" : "")} {appArguments.AppName} v{appArguments.Version}";

            this.appArguments          = appArguments;
            this.applicationAccessor   = applicationAccessor;
            this.MicrophoneController  = microphoneControllerViewModel.AddTo(Anchors);
            this.mainWindowTracker     = mainWindowTracker;
            this.configProvider        = configProvider;
            this.overlayConfigProvider = overlayConfigProvider;
            this.notificationsManager  = notificationsManager;
            this.viewController        = viewController;
            ApplicationUpdater         = appUpdater.AddTo(Anchors);
            WaveOutDeviceSelector      = waveOutDeviceSelector;
            ImageProvider            = imageProvider;
            ErrorMonitor             = errorMonitor;
            AudioSelectorWhenMuted   = audioSelectorFactory.Create().AddTo(Anchors);
            AudioSelectorWhenUnmuted = audioSelectorFactory.Create().AddTo(Anchors);
            WindowState = WindowState.Minimized;
            Overlay     = overlay.AddTo(Anchors);

            try
            {
                var startupManagerArgs = new StartupManagerArgs
                {
                    UniqueAppName   = $"{appArguments.AppName}{(appArguments.IsDebugMode ? "-debug" : string.Empty)}",
                    ExecutablePath  = appUpdater.LauncherExecutable.FullName,
                    CommandLineArgs = appArguments.StartupArgs,
                    AutostartFlag   = appArguments.AutostartFlag
                };
                this.startupManager     = startupManagerFactory.Create(startupManagerArgs);
                RunAtLoginToggleCommand = CommandWrapper.Create <bool>(RunAtLoginCommandExecuted, Observable.Return(startupManager?.IsReady ?? false));
            }
            catch (Exception e)
            {
                Log.Warn("Failed to initialize startup manager", e);
            }

            this.RaiseWhenSourceValue(x => x.IsActive, mainWindowTracker, x => x.IsActive, uiScheduler).AddTo(Anchors);
            this.RaiseWhenSourceValue(x => x.RunAtLogin, startupManager, x => x.IsRegistered, uiScheduler).AddTo(Anchors);
            this.RaiseWhenSourceValue(x => x.ShowOverlaySettings, Overlay, x => x.OverlayVisibilityMode).AddTo(Anchors);

            audioNotificationSource = Observable.Merge(
                AudioSelectorWhenMuted.ObservableForProperty(x => x.SelectedValue, skipInitial: true),
                AudioSelectorWhenUnmuted.ObservableForProperty(x => x.SelectedValue, skipInitial: true))
                                      .Select(x => new TwoStateNotification
            {
                On  = AudioSelectorWhenUnmuted.SelectedValue,
                Off = AudioSelectorWhenMuted.SelectedValue
            })
                                      .ToProperty(this, x => x.AudioNotification)
                                      .AddTo(Anchors);

            this.WhenAnyValue(x => x.AudioNotificationVolume)
            .Subscribe(x =>
            {
                AudioSelectorWhenUnmuted.Volume = AudioSelectorWhenMuted.Volume = x;
            })
            .AddTo(Anchors);

            MicrophoneController.ObservableForProperty(x => x.MicrophoneMuted, skipInitial: true)
            .DistinctUntilChanged()
            .Where(x => !MicrophoneController.MicrophoneLine.IsEmpty)
            .Select(isMuted => (isMuted.Value ? AudioNotification.Off : AudioNotification.On) ?? default(AudioNotificationType).ToString())
            .Where(notificationToPlay => !string.IsNullOrEmpty(notificationToPlay))
            .Select(notificationToPlay => Observable.FromAsync(async token =>
            {
                Log.Debug($"Playing notification {notificationToPlay}, volume: {audioNotificationVolume}");
                try
                {
                    await audioNotificationsManager.PlayNotification(notificationToPlay, audioNotificationVolume, waveOutDeviceSelector.SelectedItem, token);
                    Log.Debug($"Played notification {notificationToPlay}");
                }
                catch (Exception ex)
                {
                    Log.Debug($"Failed to play notification {notificationToPlay}", ex);
                }
            }))
            .Switch()
            .SubscribeToErrors(Log.HandleUiException)
            .AddTo(Anchors);

            this.WhenAnyValue(x => x.WindowState)
            .SubscribeSafe(x => ShowInTaskbar = x != WindowState.Minimized, Log.HandleUiException)
            .AddTo(Anchors);

            viewController
            .WhenClosing
            .SubscribeSafe(x => HandleWindowClosing(viewController, x), Log.HandleUiException)
            .AddTo(Anchors);

            ToggleOverlayLockCommand         = CommandWrapper.Create(ToggleOverlayCommandExecuted);
            ExitAppCommand                   = CommandWrapper.Create(ExitAppCommandExecuted);
            ShowAppCommand                   = CommandWrapper.Create(ShowAppCommandExecuted);
            OpenAppDataDirectoryCommand      = CommandWrapper.Create(OpenAppDataDirectory);
            ResetOverlayPositionCommand      = CommandWrapper.Create(ResetOverlayPositionCommandExecuted);
            RunAtLoginToggleCommand          = CommandWrapper.Create <bool>(RunAtLoginCommandExecuted, startupManager.WhenAnyValue(x => x.IsReady));
            SelectMicrophoneIconCommand      = CommandWrapper.Create(SelectMicrophoneIconCommandExecuted);
            SelectMutedMicrophoneIconCommand = CommandWrapper.Create(SelectMutedMicrophoneIconCommandExecuted);
            ResetMicrophoneIconsCommand      = CommandWrapper.Create(ResetMicrophoneIconsCommandExecuted);
            AddSoundCommand                  = CommandWrapper.Create(AddSoundCommandExecuted);

            Observable.Merge(configProvider.ListenTo(x => x.Notifications).ToUnit(), configProvider.ListenTo(x => x.NotificationVolume).ToUnit())
            .Select(_ => new { configProvider.ActualConfig.Notifications, configProvider.ActualConfig.NotificationVolume })
            .ObserveOn(uiScheduler)
            .SubscribeSafe(cfg =>
            {
                Log.Debug($"Applying new notification configuration: {cfg.DumpToTextRaw()} (current: {AudioNotification.DumpToTextRaw()}, volume: {AudioNotificationVolume})");
                AudioSelectorWhenMuted.SelectedValue   = cfg.Notifications.Off;
                AudioSelectorWhenUnmuted.SelectedValue = cfg.Notifications.On;
                AudioNotificationVolume = cfg.NotificationVolume;
            }, Log.HandleException)
            .AddTo(Anchors);

            configProvider.ListenTo(x => x.MinimizeOnClose)
            .ObserveOn(uiScheduler)
            .SubscribeSafe(x => MinimizeOnClose = x, Log.HandleException)
            .AddTo(Anchors);

            configProvider.ListenTo(x => x.OutputDeviceId)
            .ObserveOn(uiScheduler)
            .SubscribeSafe(x => WaveOutDeviceSelector.SelectById(x), Log.HandleException)
            .AddTo(Anchors);

            viewController
            .WhenLoaded
            .Take(1)
            .Select(_ => configProvider.ListenTo(y => y.StartMinimized))
            .Switch()
            .Take(1)
            .ObserveOn(uiScheduler)
            .SubscribeSafe(
                x =>
            {
                if (x)
                {
                    Log.Debug($"StartMinimized option is active - minimizing window, current state: {WindowState}");
                    StartMinimized = true;
                    viewController.Hide();
                }
                else
                {
                    Log.Debug($"StartMinimized option is not active - showing window as Normal, current state: {WindowState}");
                    StartMinimized = false;
                    viewController.Show();
                }
            }, Log.HandleUiException)
            .AddTo(Anchors);

            Observable.Merge(
                microphoneControllerViewModel.ObservableForProperty(x => x.MuteMode, skipInitial: true).ToUnit(),
                waveOutDeviceSelector.ObservableForProperty(x => x.SelectedItem, skipInitial: true).ToUnit(),
                this.ObservableForProperty(x => x.AudioNotification, skipInitial: true).ToUnit(),
                this.ObservableForProperty(x => x.MinimizeOnClose, skipInitial: true).ToUnit(),
                this.ObservableForProperty(x => x.Width, skipInitial: true).ToUnit(),
                this.ObservableForProperty(x => x.Height, skipInitial: true).ToUnit(),
                this.ObservableForProperty(x => x.Top, skipInitial: true).ToUnit(),
                this.ObservableForProperty(x => x.Left, skipInitial: true).ToUnit(),
                this.ObservableForProperty(x => x.AudioNotificationVolume, skipInitial: true).ToUnit(),
                this.ObservableForProperty(x => x.StartMinimized, skipInitial: true).ToUnit())
            .Throttle(ConfigThrottlingTimeout)
            .ObserveOn(uiScheduler)
            .SubscribeSafe(() =>
            {
                var config                = configProvider.ActualConfig.CloneJson();
                config.Notifications      = AudioNotification;
                config.NotificationVolume = AudioNotificationVolume;
                config.StartMinimized     = StartMinimized;
                config.MinimizeOnClose    = MinimizeOnClose;
                config.OutputDeviceId     = waveOutDeviceSelector.SelectedItem?.Id;
                config.MainWindowBounds   = new Rect(Left, Top, Width, Height);
                configProvider.Save(config);
            }, Log.HandleUiException)
            .AddTo(Anchors);

            viewController.WhenLoaded
            .SubscribeSafe(() =>
            {
                Log.Debug($"Main window loaded - loading overlay, current process({CurrentProcess.ProcessName} 0x{CurrentProcess.Id:x8}) main window: {CurrentProcess.MainWindowHandle} ({CurrentProcess.MainWindowTitle})");
                overlayWindowController.RegisterChild(Overlay).AddTo(Anchors);
                Log.Debug("Overlay loaded successfully");
            }, Log.HandleUiException)
            .AddTo(Anchors);

            configProvider.ListenTo(x => x.MainWindowBounds)
            .WithPrevious()
            .ObserveOn(uiScheduler)
            .SubscribeSafe(x =>
            {
                Log.Debug($"Main window config bounds updated: {x}");

                Rect bounds;
                if (x.Current == null)
                {
                    var monitorBounds = UnsafeNative.GetMonitorBounds(Rectangle.Empty).ScaleToWpf();
                    var monitorCenter = monitorBounds.Center();
                    bounds            = new Rect(
                        monitorCenter.X - DefaultSize.Width / 2f,
                        monitorCenter.Y - DefaultSize.Height / 2f,
                        DefaultSize.Width,
                        DefaultSize.Height);
                }
                else
                {
                    bounds = x.Current.Value;
                }

                Left   = bounds.Left;
                Top    = bounds.Top;
                Width  = bounds.Width;
                Height = bounds.Height;
            }, Log.HandleUiException)
            .AddTo(Anchors);

            var theme = Theme.Create(
                Theme.Light,
                primary: SwatchHelper.Lookup[(MaterialDesignColor)PrimaryColor.BlueGrey],
                accent: SwatchHelper.Lookup[(MaterialDesignColor)SecondaryColor.LightBlue]);
            var paletteHelper = new PaletteHelper();

            paletteHelper.SetTheme(theme);
        }
示例#10
0
 public ScreenNode(string name, IRenderer renderer, IContainerNodeCreater containerNodeCreator, IWindowTracker windowTracker, RECT rect, Direction direction = Direction.Horizontal) :
     base(renderer, containerNodeCreator, windowTracker, rect, direction, null)
 {
     Name      = name;
     FixedRect = true;
 }
示例#11
0
        public MainWindowViewModel(
            IAppArguments appArguments,
            IFactory <IStartupManager, StartupManagerArgs> startupManagerFactory,
            IMicSwitchOverlayViewModel overlay,
            IMicrophoneControllerViewModel microphoneControllerViewModel,
            IOverlayWindowController overlayWindowController,
            IAudioNotificationsManager audioNotificationsManager,
            IFactory <IAudioNotificationSelectorViewModel> audioSelectorFactory,
            IApplicationUpdaterViewModel appUpdater,
            [Dependency(WellKnownWindows.MainWindow)] IWindowTracker mainWindowTracker,
            IConfigProvider <MicSwitchConfig> configProvider,
            IConfigProvider <MicSwitchOverlayConfig> overlayConfigProvider,
            IImageProvider imageProvider,
            IAudioNotificationsManager notificationsManager,
            IWindowViewController viewController,
            [Dependency(WellKnownSchedulers.UI)] IScheduler uiScheduler)
        {
            Title = $"{(appArguments.IsDebugMode ? "[D]" : "")} {appArguments.AppName} v{appArguments.Version}";

            this.appArguments          = appArguments;
            this.MicrophoneController  = microphoneControllerViewModel.AddTo(Anchors);
            this.mainWindowTracker     = mainWindowTracker;
            this.configProvider        = configProvider;
            this.overlayConfigProvider = overlayConfigProvider;
            this.notificationsManager  = notificationsManager;
            this.viewController        = viewController;
            ApplicationUpdater         = appUpdater.AddTo(Anchors);
            ImageProvider            = imageProvider;
            AudioSelectorWhenMuted   = audioSelectorFactory.Create().AddTo(Anchors);
            AudioSelectorWhenUnmuted = audioSelectorFactory.Create().AddTo(Anchors);
            WindowState = WindowState.Minimized;
            Overlay     = overlay.AddTo(Anchors);

            var startupManagerArgs = new StartupManagerArgs
            {
                UniqueAppName   = $"{appArguments.AppName}{(appArguments.IsDebugMode ? "-debug" : string.Empty)}",
                ExecutablePath  = appUpdater.GetLatestExecutable().FullName,
                CommandLineArgs = appArguments.StartupArgs,
                AutostartFlag   = appArguments.AutostartFlag
            };

            this.startupManager = startupManagerFactory.Create(startupManagerArgs);

            this.RaiseWhenSourceValue(x => x.IsActive, mainWindowTracker, x => x.IsActive, uiScheduler).AddTo(Anchors);
            this.RaiseWhenSourceValue(x => x.RunAtLogin, startupManager, x => x.IsRegistered, uiScheduler).AddTo(Anchors);
            this.RaiseWhenSourceValue(x => x.ShowOverlaySettings, Overlay, x => x.OverlayVisibilityMode).AddTo(Anchors);

            audioNotificationSource = Observable.Merge(
                AudioSelectorWhenMuted.ObservableForProperty(x => x.SelectedValue, skipInitial: true),
                AudioSelectorWhenUnmuted.ObservableForProperty(x => x.SelectedValue, skipInitial: true))
                                      .Select(x => new TwoStateNotification
            {
                On  = AudioSelectorWhenUnmuted.SelectedValue,
                Off = AudioSelectorWhenMuted.SelectedValue
            })
                                      .ToPropertyHelper(this, x => x.AudioNotification)
                                      .AddTo(Anchors);

            this.WhenAnyValue(x => x.AudioNotificationVolume)
            .Subscribe(x =>
            {
                AudioSelectorWhenUnmuted.Volume = AudioSelectorWhenMuted.Volume = x;
            })
            .AddTo(Anchors);

            MicrophoneController.ObservableForProperty(x => x.MicrophoneMuted, skipInitial: true)
            .DistinctUntilChanged()
            .Where(x => !MicrophoneController.MicrophoneLine.IsEmpty)
            .SubscribeSafe(x =>
            {
                var notificationToPlay = (x.Value ? AudioNotification.On : AudioNotification.Off) ?? default(AudioNotificationType).ToString();
                Log.Debug($"Playing notification {notificationToPlay} (cfg: {AudioNotification.DumpToTextRaw()})");
                audioNotificationsManager.PlayNotification(notificationToPlay, audioNotificationVolume);
            }, Log.HandleUiException)
            .AddTo(Anchors);

            this.WhenAnyValue(x => x.WindowState)
            .SubscribeSafe(x => ShowInTaskbar = x != WindowState.Minimized, Log.HandleUiException)
            .AddTo(Anchors);

            viewController
            .WhenClosing
            .SubscribeSafe(x => HandleWindowClosing(viewController, x), Log.HandleUiException)
            .AddTo(Anchors);

            ToggleOverlayLockCommand         = CommandWrapper.Create(ToggleOverlayCommandExecuted);
            ExitAppCommand                   = CommandWrapper.Create(ExitAppCommandExecuted);
            ShowAppCommand                   = CommandWrapper.Create(ShowAppCommandExecuted);
            OpenAppDataDirectoryCommand      = CommandWrapper.Create(OpenAppDataDirectory);
            ResetOverlayPositionCommand      = CommandWrapper.Create(ResetOverlayPositionCommandExecuted);
            RunAtLoginToggleCommand          = CommandWrapper.Create <bool>(RunAtLoginCommandExecuted);
            SelectMicrophoneIconCommand      = CommandWrapper.Create(SelectMicrophoneIconCommandExecuted);
            SelectMutedMicrophoneIconCommand = CommandWrapper.Create(SelectMutedMicrophoneIconCommandExecuted);
            ResetMicrophoneIconsCommand      = CommandWrapper.Create(ResetMicrophoneIconsCommandExecuted);
            AddSoundCommand                  = CommandWrapper.Create(AddSoundCommandExecuted);

            Observable.Merge(configProvider.ListenTo(x => x.Notification).ToUnit(), configProvider.ListenTo(x => x.NotificationVolume).ToUnit())
            .Select(_ => new { configProvider.ActualConfig.Notification, configProvider.ActualConfig.NotificationVolume })
            .ObserveOn(uiScheduler)
            .SubscribeSafe(cfg =>
            {
                Log.Debug($"Applying new notification configuration: {cfg.DumpToTextRaw()} (current: {AudioNotification.DumpToTextRaw()}, volume: {AudioNotificationVolume})");
                AudioSelectorWhenMuted.SelectedValue   = cfg.Notification.Off;
                AudioSelectorWhenUnmuted.SelectedValue = cfg.Notification.On;
                AudioNotificationVolume = cfg.NotificationVolume;
            }, Log.HandleException)
            .AddTo(Anchors);

            configProvider.ListenTo(x => x.MinimizeOnClose)
            .ObserveOn(uiScheduler)
            .SubscribeSafe(x => MinimizeOnClose = x, Log.HandleException)
            .AddTo(Anchors);

            viewController
            .WhenLoaded
            .Take(1)
            .Select(_ => configProvider.ListenTo(y => y.StartMinimized))
            .Switch()
            .Take(1)
            .ObserveOn(uiScheduler)
            .SubscribeSafe(
                x =>
            {
                if (x)
                {
                    Log.Debug($"StartMinimized option is active - minimizing window, current state: {WindowState}");
                    StartMinimized = true;
                    viewController.Hide();
                }
                else
                {
                    Log.Debug($"StartMinimized option is not active - showing window as Normal, current state: {WindowState}");
                    StartMinimized = false;
                    viewController.Show();
                }
            }, Log.HandleUiException)
            .AddTo(Anchors);

            configProvider.WhenChanged
            .Subscribe()
            .AddTo(Anchors);

            Observable.Merge(
                microphoneControllerViewModel.ObservableForProperty(x => x.MuteMode, skipInitial: true).ToUnit(),
                this.ObservableForProperty(x => x.AudioNotification, skipInitial: true).ToUnit(),
                this.ObservableForProperty(x => x.MinimizeOnClose, skipInitial: true).ToUnit(),
                this.ObservableForProperty(x => x.AudioNotificationVolume, skipInitial: true).ToUnit(),
                this.ObservableForProperty(x => x.StartMinimized, skipInitial: true).ToUnit())
            .Throttle(ConfigThrottlingTimeout)
            .ObserveOn(uiScheduler)
            .SubscribeSafe(() =>
            {
                var config                = configProvider.ActualConfig.CloneJson();
                config.Notification       = AudioNotification;
                config.NotificationVolume = AudioNotificationVolume;
                config.StartMinimized     = StartMinimized;
                config.MinimizeOnClose    = MinimizeOnClose;
                configProvider.Save(config);
            }, Log.HandleUiException)
            .AddTo(Anchors);

            viewController.WhenLoaded
            .SubscribeSafe(() =>
            {
                Log.Debug($"Main window loaded - loading overlay, current process({CurrentProcess.ProcessName} 0x{CurrentProcess.Id:x8}) main window: {CurrentProcess.MainWindowHandle} ({CurrentProcess.MainWindowTitle})");
                overlayWindowController.RegisterChild(Overlay).AddTo(Anchors);
                Log.Debug("Overlay loaded successfully");
            }, Log.HandleUiException)
            .AddTo(Anchors);

            var theme = Theme.Create(
                Theme.Light,
                primary: SwatchHelper.Lookup[(MaterialDesignColor)PrimaryColor.BlueGrey],
                accent: SwatchHelper.Lookup[(MaterialDesignColor)SecondaryColor.LightBlue]);
            var paletteHelper = new PaletteHelper();

            paletteHelper.SetTheme(theme);
        }
示例#12
0
        public EyeOverlayViewModel(
            [NotNull] [Dependency(WellKnownWindows.AllWindows)] IWindowTracker mainWindowTracker,
            [NotNull] IOverlayWindowController overlayWindowController,
            [NotNull] IAuraModelController auraModelController,
            [NotNull] IWindowListProvider windowListProvider,
            [NotNull] ISelectionAdornerViewModel selectionAdorner,
            [NotNull] [Dependency(WellKnownSchedulers.UI)] IScheduler uiScheduler)
        {
            using var sw = new BenchmarkTimer("Initialization", Log, nameof(EyeOverlayViewModel));
            SelectionAdorner = selectionAdorner.AddTo(Anchors);
            activeConfigEditorAnchors.AddTo(Anchors);
            this.mainWindowTracker = mainWindowTracker;
            this.overlayWindowController = overlayWindowController;
            this.auraModelController = auraModelController;
            this.windowListProvider = windowListProvider;
            MinSize = new Size(32, 32);
            SizeToContent = SizeToContent.Manual;
            Width = 400;
            Height = 400;
            Left = 200;
            Top = 200;
            IsUnlockable = true;
            Title = "EyeAuras";
            EnableHeader = false;
            thumbnailOpacity.SetDefaultValue(DefaultThumbnailOpacity);

            ResetRegionCommandExecuted();
            sw.Step("Basic properties initialized");
            
            WhenLoaded
                .Take(1)
                .Subscribe(ApplyConfig)
                .AddTo(Anchors);
            sw.Step("WhenLoaded executed");
            
            resetRegionCommand = CommandWrapper.Create(ResetRegionCommandExecuted, ResetRegionCommandCanExecute);
            selectRegionCommand = CommandWrapper.Create(SelectRegionCommandExecuted, SelectRegionCommandCanExecute);
            closeConfigEditorCommand = CommandWrapper.Create(CloseConfigEditorCommandExecuted);
            fitOverlayCommand = CommandWrapper.Create<double?>(FitOverlayCommandExecuted);
            setAttachedWindowCommand = CommandWrapper.Create<WindowHandle>(SetAttachedWindowCommandExecuted);
            setClickThroughCommand = CommandWrapper.Create<bool?>(SetClickThroughModeExecuted);
            DisableAuraCommand = CommandWrapper.Create(() => auraModelController.IsEnabled = false);
            CloseCommand = CommandWrapper.Create(CloseCommandExecuted, auraModelController.WhenAnyValue(x => x.CloseController).Select(CloseCommandCanExecute));
            ToggleLockStateCommand = CommandWrapper.Create(
                () =>
                {
                    if (IsLocked && UnlockWindowCommand.CanExecute(null))
                    {
                        UnlockWindowCommand.Execute(null);
                    }
                    else if (!IsLocked && LockWindowCommand.CanExecute(null))
                    {
                        LockWindowCommand.Execute(null);
                    }
                    else
                    {
                        throw new ApplicationException($"Something went wrong - invalid Overlay Lock state: {new {IsLocked, IsUnlockable, CanUnlock = UnlockWindowCommand.CanExecute(null), CanLock = LockWindowCommand.CanExecute(null)  }}");
                    }
                });

            auraModelController.WhenAnyValue(x => x.Name)
                .Where(x => !string.IsNullOrEmpty(x))
                .Subscribe(x => OverlayName = x)
                .AddTo(Anchors);

            this.RaiseWhenSourceValue(x => x.ActiveThumbnailOpacity, thumbnailOpacity, x => x.Value).AddTo(Anchors);
            this.RaiseWhenSourceValue(x => x.ThumbnailOpacity, this, x => x.ActiveThumbnailOpacity).AddTo(Anchors);
            this.RaiseWhenSourceValue(x => x.SourceBounds, Region, x => x.Bounds).AddTo(Anchors);

            isInEditMode = Observable.Merge(
                    this.WhenAnyProperty(x => x.IsInSelectMode, x => x.IsLocked))
                .Select(change => IsInSelectMode || !IsLocked)
                .ToPropertyHelper(this, x => x.IsInEditMode, uiScheduler)
                .AddTo(Anchors);

            this.WhenAnyValue(x => x.IsLocked)
                .Where(x => x && isInSelectMode)
                .Subscribe(() => IsInSelectMode = false)
                .AddTo(Anchors);

            aspectRatio = this.WhenAnyProperty(x => x.Bounds, x => x.ViewModelLocation)
                .Select(change => Width >= 0 && Height >= 0
                    ? Width / Height
                    : double.PositiveInfinity)
                .ToPropertyHelper(this, x => x.AspectRatio, uiScheduler)
                .AddTo(Anchors);
            sw.ResetStep();
            configEditorSupplier = new Lazy<OverlayConfigEditor>(() => CreateConfigEditor(this));
            sw.Step("Initialized Config editor");
        }
示例#13
0
 public FocusHandler(ISignalHandler signal, IWindowTracker windowTracker)
 {
     this.signal        = signal;
     this.windowTracker = windowTracker;
     this._listeners    = new Dictionary <IntPtr, List <Tuple <Guid, Action <bool> > > >();
 }