예제 #1
0
        /// <summary>
        /// attempt to write the monitor setup file to all configured Saved Games folders
        /// and also persist our viewports information
        /// </summary>
        /// <param name="callbacks"></param>
        /// <returns></returns>
        public override InstallationResult Install(IInstallationCallbacks callbacks)
        {
            try
            {
                if (!_parent.CheckMonitorsValid)
                {
                    throw new Exception(
                              "UI should have disabled monitor setup writing without up to date monitors; implementation error");
                }

                if (string.IsNullOrWhiteSpace(_parent.Profile.Path))
                {
                    throw new Exception(
                              "UI should have disabled monitor setup without a profile name; implementation error");
                }

                // create template for combined monitor setup
                MonitorSetupTemplate combinedTemplate = CreateCombinedTemplate();

                // gather all the results into a list to enumerate the yield returns
                List <StatusReportItem> results =
                    new List <StatusReportItem>(EnumerateMonitorSetupFiles(InstallFile, combinedTemplate));
                if (!_parent.GenerateCombined)
                {
                    // add the same tests for separate monitor setup
                    results.AddRange(EnumerateMonitorSetupFiles(InstallFile, CreateSeparateTemplate()));
                }

                // now scan results
                foreach (StatusReportItem item in results)
                {
                    if (item.Severity >= StatusReportItem.SeverityCode.Error)
                    {
                        callbacks.Failure(
                            "Monitor setup file generation has failed",
                            "Some files may have been generated before the failure and these files were not removed.",
                            results);
                        return(InstallationResult.Fatal);
                    }

                    // REVISIT we should have simulated first to gather warnings and errors so we can show a danger prompt
                }

                _parent.Combined.Save(combinedTemplate.ProfileName, _localViewports);

                callbacks.Success(
                    "Monitor setup generation successful",
                    "Monitor setup files have been installed into DCS.",
                    results);
                _parent.InvalidateStatusReport();
                return(InstallationResult.Success);
            }
            catch (Exception ex)
            {
                ConfigManager.LogManager.LogError("failed to install monitor setup", ex);
                callbacks.Failure("Failed to install monitor setup", ex.Message, new List <StatusReportItem>());
                _parent.InvalidateStatusReport();
                return(InstallationResult.Fatal);
            }
        }
예제 #2
0
        private IEnumerable <StatusReportItem> GatherViewports(MonitorSetupTemplate template)
        {
            foreach (StatusReportItem item in UpdateLocalViewports(template))
            {
                yield return(item);
            }

            if (!template.Combined)
            {
                _allViewports = _localViewports;
            }
            else
            {
                _allViewports = new ViewportSetupFile
                {
                    MonitorLayoutKey = _parent.MonitorLayoutKey
                };
                foreach (string name in _parent.Combined.CalculateCombinedSetupNames())
                {
                    if (name == template.ProfileName)
                    {
                        // this is the current profile so we take the data from where we generate it instead
                        // of from a file
                        foreach (StatusReportItem item in _allViewports.Merge(name, _localViewports))
                        {
                            yield return(item);
                        }

                        continue;
                    }

                    ViewportSetupFile generated = _parent.Combined.Load(name);
                    if (null == generated)
                    {
                        yield return(new StatusReportItem
                        {
                            Status =
                                $"Could not include the viewports for Helios profile '{name}' because no generated viewport data was found",
                            Recommendation =
                                $"Configure DCS Monitor Setup for Helios profile '{name}', then configure DCS Monitor Setup for current Helios profile",
                            Severity = StatusReportItem.SeverityCode.Warning,
                            Link = StatusReportItem.ProfileEditor
                        });

                        continue;
                    }

                    foreach (StatusReportItem item in _allViewports.Merge(name, generated))
                    {
                        yield return(item);
                    }
                }
            }
        }
예제 #3
0
        private IEnumerable <StatusReportItem> UpdateLocalViewports(MonitorSetupTemplate template)
        {
            _localViewports = new ViewportSetupFile
            {
                MonitorLayoutKey = _parent.MonitorLayoutKey
            };
            foreach (ShadowVisual shadow in _parent.Viewports)
            {
                string name = shadow.Viewport.ViewportName;
                if (_localViewports.Viewports.ContainsKey(name))
                {
                    yield return(new StatusReportItem
                    {
                        Status =
                            $"The viewport '{name}' exists more than once in this profile.  Each viewport must have a unique name.",
                        Recommendation = $"Rename one of the duplicated viewports with name '{name}' in this profile",
                        Severity = StatusReportItem.SeverityCode.Warning,
                        Flags = StatusReportItem.StatusFlags.ConfigurationUpToDate
                    });

                    continue;
                }

                Rect rect = MonitorSetup.VisualToRect(shadow.Visual);
                rect.Offset(shadow.Monitor.Left, shadow.Monitor.Top);
                _localViewports.Viewports.Add(name, rect);
            }

            // now check against our saved state, which we also have to update
            ViewportSetupFile saved = _parent.Combined.Load(template.ProfileName);

            if (null == saved)
            {
                yield return(new StatusReportItem
                {
                    Status = "The viewport data for this profile does not exist",
                    Severity = StatusReportItem.SeverityCode.Error,
                    Recommendation = $"Configure {_parent.Name}"
                });
            }
            else if (saved.MonitorLayoutKey != _localViewports.MonitorLayoutKey ||
                     !saved.Viewports.OrderBy(e => e.Key)
                     .SequenceEqual(_localViewports.Viewports.OrderBy(e => e.Key)))
            {
                // monitor layout key and the viewport rectangles in order must be equal
                yield return(new StatusReportItem
                {
                    Status = "The viewport data for this profile is out of date",
                    Severity = StatusReportItem.SeverityCode.Error,
                    Recommendation = $"Configure {_parent.Name}"
                });
            }
        }
예제 #4
0
        private static List <FormattableString> CreateHeader(MonitorSetupTemplate template)
        {
            List <FormattableString> lines = new List <FormattableString>
            {
                // NOTE: why do we need to run this string through a local function?  does this create a ref somehow or prevent string interning?
                $"_  = function(p) return p end",
                $"name = _('{template.MonitorSetupName}')",
                $"description = 'Generated from {template.SourcesList}'"
            };

            return(lines);
        }
예제 #5
0
        private IEnumerable <StatusReportItem> EnumerateMonitorSetupFiles(ProcessMonitorSetupFile handler,
                                                                          MonitorSetupTemplate template)
        {
            string           fileName = template.FileName;
            HashSet <string> done     = new HashSet <string>(StringComparer.CurrentCultureIgnoreCase);

            foreach (InstallationLocation location in InstallationLocations.Singleton.Active)
            {
                if (done.Contains(location.SavedGamesName))
                {
                    // could have defaulted or configured the same variant twice
                    continue;
                }

                string monitorSetupsDirectory = GenerateSavedGamesPath(location.SavedGamesName);
                string monitorSetupPath       = Path.Combine(monitorSetupsDirectory, fileName);
                string correctContents        = template.Combined ? _combinedMonitorSetup : _monitorSetup;
                yield return(handler(location, fileName, monitorSetupsDirectory, monitorSetupPath, correctContents));
            }
        }
예제 #6
0
        /// <summary>
        /// generate the monitor setup file but do not write it out yet
        /// </summary>
        /// <returns></returns>
        private IEnumerable <StatusReportItem> UpdateMonitorSetup(MonitorSetupTemplate template, bool verbose)
        {
            List <FormattableString> lines = CreateHeader(template);

            foreach (StatusReportItem item in GatherViewports(template))
            {
                yield return(item);
            }

            // emit in sorted canonical order so we can compare files later
            foreach (KeyValuePair <string, Rect> viewport in _allViewports.Viewports.OrderBy(p => p.Key))
            {
                if (TryCreateViewport(lines, viewport, out FormattableString code))
                {
                    yield return(new StatusReportItem
                    {
                        Status = $"{template.MonitorSetupFileBaseName}: {code}",
                        Flags = INFORMATIONAL
                    });
                }
                else
                {
                    yield return(new StatusReportItem
                    {
                        Status =
                            $"viewport '{viewport.Key}' was not included in monitor setup because it is not entirely contained in rendered resolution",
                        Severity = StatusReportItem.SeverityCode.Warning,
                        Recommendation = "adjust the viewport location or the monitor layout",
                        Link = StatusReportItem.ProfileEditor,
                        Flags = StatusReportItem.StatusFlags.ConfigurationUpToDate
                    });
                }
            }

            // find main and ui view extents
            Rect mainView = Rect.Empty;
            Rect uiView   = Rect.Empty;

            foreach (ShadowMonitor monitor in _parent.Monitors)
            {
                string monitorDescription =
                    $"Monitor at Windows coordinates ({monitor.Monitor.Left},{monitor.Monitor.Top}) of size {monitor.Monitor.Width}x{monitor.Monitor.Height}";
                if (!monitor.Included)
                {
                    if (verbose)
                    {
                        yield return new StatusReportItem
                               {
                                   Status = $"{monitorDescription} is not included in monitor setup",
                                   Flags  = INFORMATIONAL
                               }
                    }
                    ;
                    continue;
                }

                Rect rect = MonitorSetup.VisualToRect(monitor.Monitor);
                if (monitor.Main)
                {
                    if (_parent.MonitorLayoutMode == MonitorLayoutMode.TopLeftQuarter)
                    {
                        // special
                        Rect scaled = rect;

                        scaled.Scale(0.5d, 0.5d);
                        mainView.Union(scaled);
                    }
                    else
                    {
                        mainView.Union(rect);
                    }
                    if (verbose)
                    {
                        yield return new StatusReportItem
                               {
                                   Status = $"{monitorDescription} is used for main view",
                                   Flags  = INFORMATIONAL
                               }
                    }
                    ;
                }

                if (monitor.UserInterface)
                {
                    uiView.Union(rect);
                    if (verbose)
                    {
                        yield return new StatusReportItem
                               {
                                   Status = $"{monitorDescription} is used to display the DCS user interface",
                                   Flags  = INFORMATIONAL
                               }
                    }
                    ;
                }

                if (monitor.ViewportCount > 0)
                {
                    string plural = monitor.ViewportCount > 1 ? "s" : "";
                    if (verbose)
                    {
                        yield return new StatusReportItem
                               {
                                   Status = $"{monitorDescription} has {monitor.ViewportCount} viewport{plural} from this profile",
                                   Flags  = INFORMATIONAL
                               }
                    }
                    ;
                }
            }

            // trim to rendered area in case of partial screens
            mainView.Intersect(_parent.Rendered);
            uiView.Intersect(_parent.Rendered);

            // change to DCS coordinates (0,0 at top left of rect, physical pixels)
            ConvertToDCS(ref mainView);
            ConvertToDCS(ref uiView);

            CreateMainView(lines, mainView);
            if (verbose)
            {
                yield return new StatusReportItem
                       {
                           Status =
                               $"MAIN = {{ x = {mainView.Left}, y = {mainView.Top}, width = {mainView.Width}, height = {mainView.Height} }}",
                           Flags = INFORMATIONAL
                       }
            }
            ;

            // check for separate UI view
            StatusReportItem uiStatus = CreateUserInterfaceViewIfRequired(lines, mainView, uiView, out string uiViewName);

            if (verbose)
            {
                yield return(uiStatus);
            }

            // set up required names for viewports (well-known to DCS)
            lines.Add($"UIMainView = {uiViewName}");
            lines.Add($"GU_MAIN_VIEWPORT = Viewports.Center");

            foreach (FormattableString line in lines)
            {
                ConfigManager.LogManager.LogDebug(FormattableString.Invariant(line));
            }

            if (template.Combined)
            {
                _combinedMonitorSetup = Render(lines);
            }
            else
            {
                _monitorSetup = Render(lines);
            }
        }
예제 #7
0
        public override IEnumerable <StatusReportItem> PerformReadyCheck()
        {
            // while we do not have the ability to measure the DPI setting for each screen, we just report the system dpi and an advisory message
            // we always include this advisory because it is affecting basically all our widescreen users right now
            int dpi = ConfigManager.DisplayManager.DPI;

            yield return(new StatusReportItem
            {
                Status = $"Windows reports a scaling value of {Math.Round(dpi / 0.96d)}% ({dpi} dpi)",
                Recommendation = "This version of Helios does not support using different display scaling (DPI) on different monitors.  Make sure you use the same scaling value for all displays.",
                Flags = StatusReportItem.StatusFlags.ConfigurationUpToDate | StatusReportItem.StatusFlags.Verbose
            });

            if (!_parent.CheckMonitorsValid)
            {
                yield return(new StatusReportItem
                {
                    Status = "Monitor configuration in profile does not match this computer",
                    Recommendation = "Perform 'Reset Monitors' function from the 'Profile' menu",
                    Link = StatusReportItem.ProfileEditor,
                    Severity = StatusReportItem.SeverityCode.Error
                });

                yield break;
            }

            if (string.IsNullOrWhiteSpace(_parent.Profile.Path))
            {
                yield return(new StatusReportItem
                {
                    Status =
                        "You must save the profile before you can use Monitor Setup, because it needs to know the profile name",
                    Recommendation = "Save the profile at least once before configuring Monitor Setup",
                    Link = StatusReportItem.ProfileEditor,
                    Severity = StatusReportItem.SeverityCode.Error
                });

                yield break;
            }

            // check if DCS install folders are configured
            InstallationLocations locations = InstallationLocations.Singleton;

            if (!locations.Active.Any())
            {
                yield return(new StatusReportItem
                {
                    Status = "No DCS installation locations are configured for monitor setup",
                    Recommendation = "Configure any DCS installation location you use",
                    Link = StatusReportItem.ProfileEditor,
                    Severity = StatusReportItem.SeverityCode.Error
                });
            }

            // gather viewport providers from this profile
            List <IViewportProvider> viewportProviders =
                _parent.Profile.Interfaces.OfType <IViewportProvider>().ToList();

            if (!_parent.UsingViewportProvider)
            {
                foreach (HeliosInterface viewportProvider in viewportProviders.OfType <HeliosInterface>())
                {
                    yield return(new StatusReportItem
                    {
                        Status = $"interface '{viewportProvider.Name}' is providing additional viewport patches but this profile uses a third-party solution for those",
                        Severity = StatusReportItem.SeverityCode.Warning,
                        Recommendation = $"Remove the '{viewportProvider.Name}' interface since this profile is configured to expect a third-party solution to provide viewport modifications"
                    });
                }
            }

            // check if any referenced viewports require patches to work
            foreach (IViewportExtent viewport in _parent.Viewports
                     .Select(shadow => shadow.Viewport)
                     .Where(v => v.RequiresPatches))
            {
                if (!_parent.UsingViewportProvider)
                {
                    yield return(new StatusReportItem
                    {
                        Status =
                            $"viewport '{viewport.ViewportName}' must be provided by third-party solution for additional viewports",
                        Recommendation =
                            "Verify that your third-party viewport modifications match the viewport names for this profile",
                        Flags = StatusReportItem.StatusFlags.Verbose |
                                StatusReportItem.StatusFlags.ConfigurationUpToDate
                    });

                    continue;
                }
                bool found = false;
                foreach (IViewportProvider provider in viewportProviders)
                {
                    if (provider.IsViewportAvailable(viewport.ViewportName))
                    {
                        yield return(new StatusReportItem
                        {
                            Status =
                                $"viewport '{viewport.ViewportName}' is provided by '{((HeliosInterface) provider).Name}'",
                            Flags = StatusReportItem.StatusFlags.Verbose |
                                    StatusReportItem.StatusFlags.ConfigurationUpToDate
                        });

                        found = true;
                        break;
                    }
                }

                if (!found)
                {
                    yield return(new StatusReportItem
                    {
                        Status = $"viewport '{viewport.ViewportName}' requires patches to be installed",
                        Recommendation =
                            "Add an Additional Viewports interface or configure the viewport extent not to require patches",
                        Link = StatusReportItem.ProfileEditor,
                        Severity = StatusReportItem.SeverityCode.Error,
                        Flags = StatusReportItem.StatusFlags.ConfigurationUpToDate
                    });
                }
            }

            bool updated = true;
            bool hasFile = false;

            // calculate shared monitor config
            // and see if it is up to date in all locations
            MonitorSetupTemplate combinedTemplate = CreateCombinedTemplate();
            string monitorSetupName = combinedTemplate.MonitorSetupName;

            foreach (StatusReportItem item in UpdateMonitorSetup(combinedTemplate, true))
            {
                yield return(item);
            }

            foreach (StatusReportItem item in EnumerateMonitorSetupFiles(CheckSetupFile, combinedTemplate))
            {
                if (!item.Flags.HasFlag(StatusReportItem.StatusFlags.ConfigurationUpToDate))
                {
                    updated = false;
                }

                hasFile = true;
                yield return(item);
            }

            // also calculate separate config, if applicable
            // and see if it is up to date in all locations
            if (!_parent.GenerateCombined)
            {
                MonitorSetupTemplate separateTemplate = CreateSeparateTemplate();
                monitorSetupName = separateTemplate.MonitorSetupName;
                foreach (StatusReportItem item in UpdateMonitorSetup(separateTemplate, false))
                {
                    yield return(item);
                }

                foreach (StatusReportItem item in EnumerateMonitorSetupFiles(CheckSetupFile, separateTemplate))
                {
                    if (!item.Flags.HasFlag(StatusReportItem.StatusFlags.ConfigurationUpToDate))
                    {
                        updated = false;
                    }

                    hasFile = true;
                    yield return(item);
                }
            }

            foreach (InstallationLocation location in locations.Active)
            {
                if (DCSOptions.TryReadOptions(location, out DCSOptions options))
                {
                    // check if correct monitor resolution selected in DCS
                    yield return(ReportResolutionSelected(location, options));

                    // don't tell the user to do this yet if the file isn't done
                    if (hasFile && updated)
                    {
                        // check if monitor setup selected in DCS
                        yield return(ReportMonitorSetupSelected(location, options, monitorSetupName));
                    }
                }
                else
                {
                    // report that the user has to check this themselves (we may not have access and that's ok)
                    string qualifier = _parent.MonitorLayoutMode == MonitorLayoutMode.FromTopLeftCorner ? "at least " : "";
                    yield return(new StatusReportItem
                    {
                        Status = $"Helios was unable to check the DCS settings stored in {location.DescribeOptionsPath}",
                        Recommendation =
                            $"Using DCS, please make sure the 'Resolution' in the 'System' options is set to {qualifier}{_parent.Rendered.Width}x{_parent.Rendered.Height}",
                        Severity = StatusReportItem.SeverityCode.Info,
                        Flags = StatusReportItem.StatusFlags.ConfigurationUpToDate
                    });

                    // don't tell the user to do this yet if the file isn't done
                    if (hasFile && updated)
                    {
                        yield return(new StatusReportItem
                        {
                            Status = $"Helios was unable to check the DCS monitor setup selection stored in {location.DescribeOptionsPath}",
                            Recommendation =
                                $"Using DCS, please make sure 'Monitors' in the 'System' options is set to '{monitorSetupName}'",
                            Severity = StatusReportItem.SeverityCode.Info,
                            Flags = StatusReportItem.StatusFlags.ConfigurationUpToDate
                        });
                    }
                }
            }

            if (_parent.GenerateCombined)
            {
                yield break;
            }

            yield return(new StatusReportItem
            {
                Status = "This profile requires a specific monitor setup file",
                Recommendation =
                    "You will need to switch 'Monitors' in DCS when you switch Helios Profile",
                Severity = StatusReportItem.SeverityCode.Info,
                Flags = StatusReportItem.StatusFlags.ConfigurationUpToDate
            });
        }