Compare commits

...

4 Commits

Author SHA1 Message Date
gnisman
dde208b480 UI: Expose games build ID for cheat management (#4340)
* Ava UI: Expose games build ID for cheat management

* Fix bad merge

* Change integrity check level to error on invalid

* Add support for GDK

* Remove whitespace

* Add BID identifier

* PR Comments fix

* Restore title id in cheats GTK window

* use halign center instead of margin_left

* Merge

* fix after merge

* PR comments fix - design AVA

* PR fix - Move GetApplicationBuildId to ApplicationData class

* PR comment fix - Add empty line before method

* Align with PR #4755

* PR comments fix

* Change BuildId label to support translation

* Comments fix

* Remove unused BuildIdLabel property
2023-05-07 14:36:44 +00:00
Nico
4c3d2d5d75 UI: Add progress bar for re-packaging shaders (#4805)
* feat: introduce new shader loading state for progress tracking when writing shaders to disk

* fix: move translation to bottom of locale file

* fix: change back to foreach and add requested spacing between lines

* style: fix formatting

Co-authored-by: gdkchan <gab.dark.100@gmail.com>

---------

Co-authored-by: gdkchan <gab.dark.100@gmail.com>
2023-05-06 15:35:46 +02:00
Ac_K
fab11ba3f1 AM: Stub some service calls (#4825)
* AM: Stub some service call

Some IPC I have stubbed during private testing and I don't want to deal with them anymore. Nothing more.

* ICommonStateGetter disposable
2023-05-06 03:33:50 +02:00
riperiperi
332891b5ff Use correct offset for storage constant buffer elimination (#4821) 2023-05-05 23:59:36 +02:00
17 changed files with 293 additions and 29 deletions

View File

@@ -590,6 +590,7 @@
"DlcWindowTitle": "Manage Downloadable Content for {0} ({1})",
"UpdateWindowTitle": "Title Update Manager",
"CheatWindowHeading": "Cheats Available for {0} [{1}]",
"BuildId": "BuildId:",
"DlcWindowHeading": "{0} Downloadable Content(s)",
"UserProfilesEditProfile": "Edit Selected",
"Cancel": "Cancel",
@@ -644,5 +645,6 @@
"UserEditorTitleCreate" : "Create User",
"SettingsTabNetworkInterface": "Network Interface:",
"NetworkInterfaceTooltip": "The network interface used for LAN features",
"NetworkInterfaceDefault": "Default"
"NetworkInterfaceDefault": "Default",
"PackagingShaders": "Packaging Shaders"
}

View File

@@ -10,6 +10,7 @@ using Ryujinx.Ava.UI.Helpers;
using Ryujinx.Ava.UI.ViewModels;
using Ryujinx.Ava.UI.Windows;
using Ryujinx.Common.Configuration;
using Ryujinx.Ui.App.Common;
using Ryujinx.HLE.HOS;
using Ryujinx.Ui.Common.Helper;
using System;
@@ -118,7 +119,11 @@ namespace Ryujinx.Ava.UI.Controls
if (viewModel?.SelectedApplication != null)
{
await new CheatWindow(viewModel.VirtualFileSystem, viewModel.SelectedApplication.TitleId, viewModel.SelectedApplication.TitleName).ShowDialog(viewModel.TopLevel as Window);
await new CheatWindow(
viewModel.VirtualFileSystem,
viewModel.SelectedApplication.TitleId,
viewModel.SelectedApplication.TitleName,
viewModel.SelectedApplication.Path).ShowDialog(viewModel.TopLevel as Window);
}
}

View File

@@ -1099,6 +1099,10 @@ namespace Ryujinx.Ava.UI.ViewModels
LoadHeading = LocaleManager.Instance[LocaleKeys.CompilingShaders];
IsLoadingIndeterminate = false;
break;
case ShaderCacheLoadingState.Packaging:
LoadHeading = LocaleManager.Instance[LocaleKeys.PackagingShaders];
IsLoadingIndeterminate = false;
break;
case ShaderCacheLoadingState.Loaded:
LoadHeading = LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.LoadingHeading, TitleName);
IsLoadingIndeterminate = true;

View File

@@ -11,6 +11,7 @@ using Ryujinx.Common;
using Ryujinx.Common.Utilities;
using Ryujinx.HLE.HOS;
using Ryujinx.Modules;
using Ryujinx.Ui.App.Common;
using Ryujinx.Ui.Common;
using Ryujinx.Ui.Common.Configuration;
using Ryujinx.Ui.Common.Helper;
@@ -176,7 +177,11 @@ namespace Ryujinx.Ava.UI.Views.Main
string name = ViewModel.AppHost.Device.Processes.ActiveApplication.ApplicationControlProperties.Title[(int)ViewModel.AppHost.Device.System.State.DesiredTitleLanguage].NameString.ToString();
await new CheatWindow(Window.VirtualFileSystem, ViewModel.AppHost.Device.Processes.ActiveApplication.ProgramIdText, name).ShowDialog(Window);
await new CheatWindow(
Window.VirtualFileSystem,
ViewModel.AppHost.Device.Processes.ActiveApplication.ProgramIdText,
name,
Window.ViewModel.SelectedApplication.Path).ShowDialog(Window);
ViewModel.AppHost.Device.EnableCheats();
}

View File

@@ -21,23 +21,52 @@
</Window.Styles>
<Grid Name="CheatGrid" Margin="15">
<Grid.RowDefinitions>
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="Auto" />
<RowDefinition Height="*" />
<RowDefinition Height="Auto" />
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*" />
<ColumnDefinition Width="*" />
</Grid.ColumnDefinitions>
<TextBlock
Grid.Row="1"
Grid.Column="0"
Grid.ColumnSpan="2"
MaxWidth="500"
Margin="20,15,20,20"
Margin="20,15,20,5"
HorizontalAlignment="Center"
VerticalAlignment="Center"
LineHeight="18"
Text="{Binding Heading}"
TextAlignment="Center"
TextWrapping="Wrap" />
<Border
<TextBlock
Grid.Row="2"
Grid.Column="0"
MaxWidth="500"
Margin="140,15,20,5"
HorizontalAlignment="Center"
VerticalAlignment="Center"
LineHeight="30"
Text="{locale:Locale BuildId}"
TextAlignment="Center"
TextWrapping="Wrap" />
<TextBox
Grid.Row="2"
Grid.Column="1"
Margin="0,5,110,5"
MinWidth="160"
HorizontalAlignment="Center"
VerticalAlignment="Center"
Text="{Binding BuildId}"
IsReadOnly="True" />
<Border
Grid.Row="3"
Grid.Column="0"
Grid.ColumnSpan="2"
Margin="5"
HorizontalAlignment="Stretch"
VerticalAlignment="Stretch"
@@ -81,7 +110,9 @@
</TreeView>
</Border>
<DockPanel
Grid.Row="3"
Grid.Row="4"
Grid.Column="0"
Grid.ColumnSpan="2"
Margin="0"
HorizontalAlignment="Stretch">
<DockPanel Margin="0" HorizontalAlignment="Right">

View File

@@ -1,8 +1,10 @@
using Avalonia.Collections;
using Avalonia;
using Avalonia.Collections;
using Ryujinx.Ava.Common.Locale;
using Ryujinx.Ava.UI.Models;
using Ryujinx.HLE.FileSystem;
using Ryujinx.HLE.HOS;
using Ryujinx.Ui.App.Common;
using System.Collections.Generic;
using System.IO;
using System.Linq;
@@ -17,6 +19,7 @@ namespace Ryujinx.Ava.UI.Windows
private AvaloniaList<CheatsList> LoadedCheats { get; }
public string Heading { get; }
public string BuildId { get; }
public CheatWindow()
{
@@ -27,12 +30,13 @@ namespace Ryujinx.Ava.UI.Windows
Title = $"Ryujinx {Program.Version} - " + LocaleManager.Instance[LocaleKeys.CheatWindowTitle];
}
public CheatWindow(VirtualFileSystem virtualFileSystem, string titleId, string titleName)
public CheatWindow(VirtualFileSystem virtualFileSystem, string titleId, string titleName, string titlePath)
{
LoadedCheats = new AvaloniaList<CheatsList>();
Heading = LocaleManager.Instance.UpdateAndGetDynamicValue(LocaleKeys.CheatWindowHeading, titleName, titleId.ToUpper());
BuildId = ApplicationData.GetApplicationBuildId(virtualFileSystem, titlePath);
InitializeComponent();
string modsBasePath = ModLoader.GetModsBasePath();

View File

@@ -22,7 +22,7 @@ namespace Ryujinx.Graphics.Gpu.Shader.DiskCache
private const ushort FileFormatVersionMajor = 1;
private const ushort FileFormatVersionMinor = 2;
private const uint FileFormatVersionPacked = ((uint)FileFormatVersionMajor << 16) | FileFormatVersionMinor;
private const uint CodeGenVersion = 2237;
private const uint CodeGenVersion = 4821;
private const string SharedTocFileName = "shared.toc";
private const string SharedDataFileName = "shared.data";

View File

@@ -299,10 +299,13 @@ namespace Ryujinx.Graphics.Gpu.Shader.DiskCache
if (_programList.Count != 0)
{
_stateChangeCallback(ShaderCacheState.Packaging, 0, _programList.Count);
Logger.Info?.Print(LogClass.Gpu, $"Rebuilding {_programList.Count} shaders...");
using var streams = _hostStorage.GetOutputStreams(_context);
int packagedShaders = 0;
foreach (var kv in _programList)
{
if (!Active)
@@ -311,7 +314,10 @@ namespace Ryujinx.Graphics.Gpu.Shader.DiskCache
}
(CachedShaderProgram program, byte[] binaryCode) = kv.Value;
_hostStorage.AddShader(_context, program, binaryCode, streams);
_stateChangeCallback(ShaderCacheState.Packaging, ++packagedShaders, _programList.Count);
}
Logger.Info?.Print(LogClass.Gpu, $"Rebuilt {_programList.Count} shaders successfully.");

View File

@@ -7,6 +7,8 @@ namespace Ryujinx.Graphics.Gpu.Shader
Start,
/// <summary>Shader cache is loading</summary>
Loading,
/// <summary>Shader cache is written to disk</summary>
Packaging,
/// <summary>Shader cache finished loading</summary>
Loaded
}

View File

@@ -161,12 +161,12 @@ namespace Ryujinx.Graphics.Shader.Translation.Optimizations
Operand addrLow,
bool isStg16Or8)
{
int baseAddressCbOffset = GetStorageCbOffset(config.Stage, storageIndex);
(int sbCbSlot, int sbCbOffset) = config.GetSbCbInfo(storageIndex);
bool storageAligned = !(config.GpuAccessor.QueryHasUnalignedStorageBuffer() || config.GpuAccessor.QueryHostStorageBufferOffsetAlignment() > Constants.StorageAlignment);
(Operand byteOffset, int constantOffset) = storageAligned ?
GetStorageOffset(block, Utils.FindLastOperation(addrLow, block), baseAddressCbOffset) :
GetStorageOffset(block, Utils.FindLastOperation(addrLow, block), sbCbSlot, sbCbOffset) :
(null, 0);
if (byteOffset != null)
@@ -176,8 +176,6 @@ namespace Ryujinx.Graphics.Shader.Translation.Optimizations
if (byteOffset == null)
{
(int sbCbSlot, int sbCbOffset) = config.GetSbCbInfo(storageIndex);
Operand baseAddrLow = Cbuf(sbCbSlot, sbCbOffset);
Operand baseAddrTrunc = Local();
@@ -217,9 +215,9 @@ namespace Ryujinx.Graphics.Shader.Translation.Optimizations
return wordOffset;
}
private static bool IsCb0Offset(Operand operand, int offset)
private static bool IsCbOffset(Operand operand, int slot, int offset)
{
return operand.Type == OperandType.ConstantBuffer && operand.GetCbufSlot() == 0 && operand.GetCbufOffset() == offset;
return operand.Type == OperandType.ConstantBuffer && operand.GetCbufSlot() == slot && operand.GetCbufOffset() == offset;
}
private static void ReplaceAddressAlignment(LinkedList<INode> list, Operand address, Operand byteOffset, int constantOffset)
@@ -263,9 +261,9 @@ namespace Ryujinx.Graphics.Shader.Translation.Optimizations
}
}
private static (Operand, int) GetStorageOffset(BasicBlock block, Operand address, int baseAddressCbOffset)
private static (Operand, int) GetStorageOffset(BasicBlock block, Operand address, int cbSlot, int baseAddressCbOffset)
{
if (IsCb0Offset(address, baseAddressCbOffset))
if (IsCbOffset(address, cbSlot, baseAddressCbOffset))
{
// Direct offset: zero.
return (Const(0), 0);
@@ -275,7 +273,7 @@ namespace Ryujinx.Graphics.Shader.Translation.Optimizations
address = Utils.FindLastOperation(address, block);
if (IsCb0Offset(address, baseAddressCbOffset))
if (IsCbOffset(address, cbSlot, baseAddressCbOffset))
{
// Only constant offset
return (Const(0), constantOffset);
@@ -289,11 +287,11 @@ namespace Ryujinx.Graphics.Shader.Translation.Optimizations
Operand src1 = offsetAdd.GetSource(0);
Operand src2 = Utils.FindLastOperation(offsetAdd.GetSource(1), block);
if (IsCb0Offset(src2, baseAddressCbOffset))
if (IsCbOffset(src2, cbSlot, baseAddressCbOffset))
{
return (src1, constantOffset);
}
else if (IsCb0Offset(src1, baseAddressCbOffset))
else if (IsCbOffset(src1, cbSlot, baseAddressCbOffset))
{
return (src2, constantOffset);
}

View File

@@ -92,6 +92,15 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletAE.AllSystemAppletProxiesService
return ResultCode.Success;
}
[CommandCmif(23)]
// GetAppletCommonFunctions() -> object<nn::am::service::IAppletCommonFunctions>
public ResultCode GetAppletCommonFunctions(ServiceCtx context)
{
MakeObject(context, new IAppletCommonFunctions());
return ResultCode.Success;
}
[CommandCmif(1000)]
// GetDebugFunctions() -> object<nn::am::service::IDebugFunctions>
public ResultCode GetDebugFunctions(ServiceCtx context)

View File

@@ -9,8 +9,10 @@ using System;
namespace Ryujinx.HLE.HOS.Services.Am.AppletAE.AllSystemAppletProxiesService.SystemAppletProxy
{
class ICommonStateGetter : IpcService
class ICommonStateGetter : DisposableIpcService
{
private readonly ServiceCtx _context;
private Apm.ManagerServer _apmManagerServer;
private Apm.SystemManagerServer _apmSystemManagerServer;
private Lbl.LblControllerServer _lblControllerServer;
@@ -23,11 +25,18 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletAE.AllSystemAppletProxiesService.Sys
private int _messageEventHandle;
private int _displayResolutionChangedEventHandle;
private KEvent _acquiredSleepLockEvent;
private int _acquiredSleepLockEventHandle;
public ICommonStateGetter(ServiceCtx context)
{
_context = context;
_apmManagerServer = new Apm.ManagerServer(context);
_apmSystemManagerServer = new Apm.SystemManagerServer(context);
_lblControllerServer = new Lbl.LblControllerServer(context);
_acquiredSleepLockEvent = new KEvent(context.Device.System.KernelContext);
}
[CommandCmif(0)]
@@ -117,6 +126,34 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletAE.AllSystemAppletProxiesService.Sys
return ResultCode.Success;
}
[CommandCmif(10)]
// RequestToAcquireSleepLock()
public ResultCode RequestToAcquireSleepLock(ServiceCtx context)
{
Logger.Stub?.PrintStub(LogClass.ServiceAm);
return ResultCode.Success;
}
[CommandCmif(13)]
// GetAcquiredSleepLockEvent() -> handle<copy>
public ResultCode GetAcquiredSleepLockEvent(ServiceCtx context)
{
if (_acquiredSleepLockEventHandle == 0)
{
if (context.Process.HandleTable.GenerateHandle(_acquiredSleepLockEvent.ReadableEvent, out _acquiredSleepLockEventHandle) != Result.Success)
{
throw new InvalidOperationException("Out of handles!");
}
}
context.Response.HandleDesc = IpcHandleDesc.MakeCopy(_acquiredSleepLockEventHandle);
Logger.Stub?.PrintStub(LogClass.ServiceAm);
return ResultCode.Success;
}
[CommandCmif(50)] // 3.0.0+
// IsVrModeEnabled() -> b8
public ResultCode IsVrModeEnabled(ServiceCtx context)
@@ -281,5 +318,17 @@ namespace Ryujinx.HLE.HOS.Services.Am.AppletAE.AllSystemAppletProxiesService.Sys
return ResultCode.Success;
}
protected override void Dispose(bool isDisposing)
{
if (isDisposing)
{
if (_acquiredSleepLockEventHandle != 0)
{
_context.Process.HandleTable.CloseHandle(_acquiredSleepLockEventHandle);
_acquiredSleepLockEventHandle = 0;
}
}
}
}
}

View File

@@ -1,5 +1,16 @@
using LibHac.Common;
using LibHac.Ns;
using LibHac.Fs;
using LibHac.Fs.Fsa;
using LibHac.FsSystem;
using LibHac.Loader;
using LibHac.Tools.Fs;
using LibHac.Tools.FsSystem;
using LibHac.Tools.FsSystem.NcaUtils;
using Ryujinx.Common.Logging;
using Ryujinx.HLE.FileSystem;
using System;
using System.IO;
namespace Ryujinx.Ui.App.Common
{
@@ -19,5 +30,122 @@ namespace Ryujinx.Ui.App.Common
public double FileSizeBytes { get; set; }
public string Path { get; set; }
public BlitStruct<ApplicationControlProperty> ControlHolder { get; set; }
public static string GetApplicationBuildId(VirtualFileSystem virtualFileSystem, string titleFilePath)
{
using FileStream file = new(titleFilePath, FileMode.Open, FileAccess.Read);
Nca mainNca = null;
Nca patchNca = null;
if (!System.IO.Path.Exists(titleFilePath))
{
Logger.Error?.Print(LogClass.Application, $"File does not exists. {titleFilePath}");
return string.Empty;
}
string extension = System.IO.Path.GetExtension(titleFilePath).ToLower();
if (extension is ".nsp" or ".xci")
{
PartitionFileSystem pfs;
if (extension == ".xci")
{
Xci xci = new(virtualFileSystem.KeySet, file.AsStorage());
pfs = xci.OpenPartition(XciPartitionType.Secure);
}
else
{
pfs = new PartitionFileSystem(file.AsStorage());
}
foreach (DirectoryEntryEx fileEntry in pfs.EnumerateEntries("/", "*.nca"))
{
using var ncaFile = new UniqueRef<IFile>();
pfs.OpenFile(ref ncaFile.Ref, fileEntry.FullPath.ToU8Span(), OpenMode.Read).ThrowIfFailure();
Nca nca = new(virtualFileSystem.KeySet, ncaFile.Get.AsStorage());
if (nca.Header.ContentType != NcaContentType.Program)
{
continue;
}
int dataIndex = Nca.GetSectionIndexFromType(NcaSectionType.Data, NcaContentType.Program);
if (nca.Header.GetFsHeader(dataIndex).IsPatchSection())
{
patchNca = nca;
}
else
{
mainNca = nca;
}
}
}
else if (extension == ".nca")
{
mainNca = new Nca(virtualFileSystem.KeySet, file.AsStorage());
}
if (mainNca == null)
{
Logger.Error?.Print(LogClass.Application, "Extraction failure. The main NCA was not present in the selected file");
return string.Empty;
}
(Nca updatePatchNca, _) = ApplicationLibrary.GetGameUpdateData(virtualFileSystem, mainNca.Header.TitleId.ToString("x16"), 0, out _);
if (updatePatchNca != null)
{
patchNca = updatePatchNca;
}
IFileSystem codeFs = null;
if (patchNca == null)
{
if (mainNca.CanOpenSection(NcaSectionType.Code))
{
codeFs = mainNca.OpenFileSystem(NcaSectionType.Code, IntegrityCheckLevel.ErrorOnInvalid);
}
}
else
{
if (patchNca.CanOpenSection(NcaSectionType.Code))
{
codeFs = mainNca.OpenFileSystemWithPatch(patchNca, NcaSectionType.Code, IntegrityCheckLevel.ErrorOnInvalid);
}
}
if (codeFs == null)
{
Logger.Error?.Print(LogClass.Loader, "No ExeFS found in NCA");
return string.Empty;
}
const string mainExeFs = "main";
if (!codeFs.FileExists($"/{mainExeFs}"))
{
Logger.Error?.Print(LogClass.Loader, "No main binary ExeFS found in ExeFS");
return string.Empty;
}
using var nsoFile = new UniqueRef<IFile>();
codeFs.OpenFile(ref nsoFile.Ref, $"/{mainExeFs}".ToU8Span(), OpenMode.Read).ThrowIfFailure();
NsoReader reader = new NsoReader();
reader.Initialize(nsoFile.Release().AsStorage().AsFile(OpenMode.Read)).ThrowIfFailure();
return BitConverter.ToString(reader.Header.ModuleId.ItemsRo.ToArray()).Replace("-", "").ToUpper()[..16];
}
}
}

View File

@@ -1626,9 +1626,12 @@ namespace Ryujinx.Ui
private void ManageCheats_Pressed(object sender, EventArgs args)
{
var window = new CheatWindow(_virtualFileSystem,
_emulationContext.Processes.ActiveApplication.ProgramId,
_emulationContext.Processes.ActiveApplication.ApplicationControlProperties.Title[(int)_emulationContext.System.State.DesiredTitleLanguage].NameString.ToString());
var window = new CheatWindow(
_virtualFileSystem,
_emulationContext.Processes.ActiveApplication.ProgramId,
_emulationContext.Processes.ActiveApplication.ApplicationControlProperties
.Title[(int)_emulationContext.System.State.DesiredTitleLanguage].NameString.ToString(),
_currentEmulatedGamePath);
window.Destroyed += CheatWindow_Destroyed;
window.Show();

View File

@@ -461,7 +461,7 @@ namespace Ryujinx.Ui.Widgets
private void ManageCheats_Clicked(object sender, EventArgs args)
{
new CheatWindow(_virtualFileSystem, _titleId, _titleName).Show();
new CheatWindow(_virtualFileSystem, _titleId, _titleName, _titleFilePath).Show();
}
private void OpenTitleModDir_Clicked(object sender, EventArgs args)

View File

@@ -1,6 +1,7 @@
using Gtk;
using Ryujinx.HLE.FileSystem;
using Ryujinx.HLE.HOS;
using Ryujinx.Ui.App.Common;
using System;
using System.Collections.Generic;
using System.IO;
@@ -17,16 +18,18 @@ namespace Ryujinx.Ui.Windows
#pragma warning disable CS0649, IDE0044
[GUI] Label _baseTitleInfoLabel;
[GUI] TextView _buildIdTextView;
[GUI] TreeView _cheatTreeView;
[GUI] Button _saveButton;
#pragma warning restore CS0649, IDE0044
public CheatWindow(VirtualFileSystem virtualFileSystem, ulong titleId, string titleName) : this(new Builder("Ryujinx.Ui.Windows.CheatWindow.glade"), virtualFileSystem, titleId, titleName) { }
public CheatWindow(VirtualFileSystem virtualFileSystem, ulong titleId, string titleName, string titlePath) : this(new Builder("Ryujinx.Ui.Windows.CheatWindow.glade"), virtualFileSystem, titleId, titleName, titlePath) { }
private CheatWindow(Builder builder, VirtualFileSystem virtualFileSystem, ulong titleId, string titleName) : base(builder.GetRawOwnedObject("_cheatWindow"))
private CheatWindow(Builder builder, VirtualFileSystem virtualFileSystem, ulong titleId, string titleName, string titlePath) : base(builder.GetRawOwnedObject("_cheatWindow"))
{
builder.Autoconnect(this);
_baseTitleInfoLabel.Text = $"Cheats Available for {titleName} [{titleId:X16}]";
_buildIdTextView.Buffer.Text = $"BuildId: {ApplicationData.GetApplicationBuildId(virtualFileSystem, titlePath)}";
string modsBasePath = ModLoader.GetModsBasePath();
string titleModsPath = ModLoader.GetTitleDir(modsBasePath, titleId.ToString("X16"));

View File

@@ -31,6 +31,21 @@
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkTextView" id="_buildIdTextView">
<property name="visible">True</property>
<property name="margin_top">10</property>
<property name="halign">center</property>
<property name="margin_bottom">10</property>
<property name="editable">False</property>
<property name="cursor_visible">False</property>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">1</property>
</packing>
</child>
<child>
<object class="GtkScrolledWindow">
<property name="visible">True</property>
@@ -57,7 +72,7 @@
<packing>
<property name="expand">True</property>
<property name="fill">True</property>
<property name="position">1</property>
<property name="position">2</property>
</packing>
</child>
</object>