Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
# SleepStrap 6.9.3
# SleepStrap 6.9.4

![SleepStrap icon](SleepStrap/SleepStrap.png)

SleepStrap is a purple Windows launcher for Roblox with skyboxes, texture presets, visual modes, fonts, and Rivals display tools.

[Download SleepStrap 6.9.3](https://github.com/SleepyDebug/SleepStrap/releases/tag/sleepstrap-6.9.3) · [Discord](https://discord.gg/W3CMjx8C7s) · [Report an issue](https://github.com/SleepyDebug/SleepStrap/issues)
[Download SleepStrap 6.9.4](https://github.com/SleepyDebug/SleepStrap/releases/tag/sleepstrap-6.9.4) · [Discord](https://discord.gg/W3CMjx8C7s) · [Report an issue](https://github.com/SleepyDebug/SleepStrap/issues)

## Repository layout

Expand Down
1 change: 1 addition & 0 deletions SleepStrap/Models/Persistable/Settings.cs
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,7 @@ public class Settings
public string MacroQuickLoadoutUtilityWeapon { get; set; } = "Grappler";
public int MacroQuickLoadoutHotkeyModifiers { get; set; } = 3; // Ctrl + Alt
public int MacroQuickLoadoutHotkeyVirtualKey { get; set; } = 0x51; // Q
public bool MacroQuickLoadoutEnabled { get; set; } = true;
public bool MacroQuickRespawn { get; set; } = false;
public bool MacroAutoUtility { get; set; } = false;
public bool MacroAutoInspect { get; set; } = false;
Expand Down
96 changes: 95 additions & 1 deletion SleepStrap/Services/MacroAutomationService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,14 @@ public static class MacroAutomationService
private const int RecordedScreenTop = 0;
private const int RecordedScreenWidth = 1920;
private const int RecordedScreenHeight = 1080;
private const int TargetClientWidth = 1920;
private const int TargetClientHeight = 1080;
private const int SwRestore = 9;
private const uint SwpNoZOrder = 0x0004;
private const uint SwpNoActivate = 0x0010;
private const uint MonitorDefaultToNearest = 0x00000002;
private const int GwlStyle = -16;
private const int GwlExStyle = -20;

private static readonly object AutomaticActionsLock = new();
private static Process? _automaticActionsProcess;
Expand Down Expand Up @@ -79,10 +87,12 @@ public static async Task RunLoadoutAsync(
if (robloxWindow == IntPtr.Zero)
throw new InvalidOperationException("Roblox is not running.");

await NormalizeRobloxWindowAsync(robloxWindow, cancellationToken);

// Do not send SW_RESTORE to a fullscreen or maximized Roblox window. Windows can
// interpret that as a request to leave its current presentation state.
if (IsIconic(robloxWindow))
ShowWindowAsync(robloxWindow, 9);
ShowWindowAsync(robloxWindow, SwRestore);
ActivateWindow(robloxWindow);

DateTime activationDeadline = DateTime.UtcNow.AddSeconds(3);
Expand Down Expand Up @@ -423,6 +433,66 @@ private static MacroPoint MapRecordedPointToWindow(MacroPoint recordedPoint, Int
return new MacroPoint(mappedX, mappedY);
}

private static async Task NormalizeRobloxWindowAsync(IntPtr window, CancellationToken cancellationToken)
{
if (!GetClientRect(window, out RECT currentClient))
return;

int currentWidth = currentClient.Right - currentClient.Left;
int currentHeight = currentClient.Bottom - currentClient.Top;
if (currentWidth == TargetClientWidth && currentHeight == TargetClientHeight)
return;

// Restore down rather than minimize to the taskbar: a minimized window cannot
// receive the selector hotkey or clicks. The coordinate mapper below still
// scales to the final client size if the monitor cannot fit full 1080p.
ShowWindowAsync(window, SwRestore);
await Task.Delay(150, cancellationToken);

IntPtr monitor = MonitorFromWindow(window, MonitorDefaultToNearest);
MONITORINFO monitorInfo = new() { Size = Marshal.SizeOf<MONITORINFO>() };
if (monitor == IntPtr.Zero || !GetMonitorInfo(monitor, ref monitorInfo))
return;

int style = GetWindowLong(window, GwlStyle);
int extendedStyle = GetWindowLong(window, GwlExStyle);
RECT targetFrame = new() { Right = TargetClientWidth, Bottom = TargetClientHeight };
if (!AdjustWindowRectEx(ref targetFrame, style, false, extendedStyle))
return;

int chromeWidth = (targetFrame.Right - targetFrame.Left) - TargetClientWidth;
int chromeHeight = (targetFrame.Bottom - targetFrame.Top) - TargetClientHeight;
int workWidth = monitorInfo.Work.Right - monitorInfo.Work.Left;
int workHeight = monitorInfo.Work.Bottom - monitorInfo.Work.Top;
int availableClientWidth = Math.Max(640, workWidth - Math.Max(0, chromeWidth));
int availableClientHeight = Math.Max(360, workHeight - Math.Max(0, chromeHeight));
double scale = Math.Min(
1d,
Math.Min(availableClientWidth / (double)TargetClientWidth, availableClientHeight / (double)TargetClientHeight));
int targetClientWidth = Math.Max(640, (int)Math.Floor(TargetClientWidth * scale / 2d) * 2);
int targetClientHeight = Math.Max(360, (int)Math.Floor(TargetClientHeight * scale / 2d) * 2);

targetFrame = new RECT { Right = targetClientWidth, Bottom = targetClientHeight };
if (!AdjustWindowRectEx(ref targetFrame, style, false, extendedStyle))
return;

int outerWidth = targetFrame.Right - targetFrame.Left;
int outerHeight = targetFrame.Bottom - targetFrame.Top;
int x = monitorInfo.Work.Left + Math.Max(0, (workWidth - outerWidth) / 2);
int y = monitorInfo.Work.Top + Math.Max(0, (workHeight - outerHeight) / 2);
if (!SetWindowPos(window, IntPtr.Zero, x, y, outerWidth, outerHeight, SwpNoZOrder | SwpNoActivate))
return;

await Task.Delay(150, cancellationToken);
if (GetClientRect(window, out RECT resizedClient))
{
App.Logger.WriteLine(
"MacroAutomationService",
$"Restored and scaled Roblox client from {currentWidth}x{currentHeight} to " +
$"{resizedClient.Right - resizedClient.Left}x{resizedClient.Bottom - resizedClient.Top}");
}
}

private static string? FindAutoHotkeyV2()
{
string programFiles = Environment.GetFolderPath(Environment.SpecialFolder.ProgramFiles);
Expand Down Expand Up @@ -555,6 +625,15 @@ private struct RECT
public int Bottom;
}

[StructLayout(LayoutKind.Sequential)]
private struct MONITORINFO
{
public int Size;
public RECT Monitor;
public RECT Work;
public uint Flags;
}

[StructLayout(LayoutKind.Sequential)]
private struct INPUT
{
Expand Down Expand Up @@ -619,6 +698,21 @@ private struct MOUSEINPUT
[DllImport("user32.dll", SetLastError = true)]
private static extern bool ClientToScreen(IntPtr window, ref POINT point);

[DllImport("user32.dll", SetLastError = true)]
private static extern bool SetWindowPos(IntPtr window, IntPtr insertAfter, int x, int y, int width, int height, uint flags);

[DllImport("user32.dll", SetLastError = true)]
private static extern bool AdjustWindowRectEx(ref RECT rectangle, int style, bool hasMenu, int extendedStyle);

[DllImport("user32.dll", SetLastError = true)]
private static extern int GetWindowLong(IntPtr window, int index);

[DllImport("user32.dll")]
private static extern IntPtr MonitorFromWindow(IntPtr window, uint flags);

[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
private static extern bool GetMonitorInfo(IntPtr monitor, ref MONITORINFO monitorInfo);

[DllImport("user32.dll", SetLastError = true)]
private static extern uint SendInput(uint inputCount, INPUT[] inputs, int inputSize);

Expand Down
4 changes: 2 additions & 2 deletions SleepStrap/SleepStrap.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,8 @@
<UseWPF>true</UseWPF>
<UseWindowsForms>True</UseWindowsForms>
<ApplicationIcon>SleepStrap.ico</ApplicationIcon>
<Version>6.9.3.0</Version>
<FileVersion>6.9.3.0</FileVersion>
<Version>6.9.4.0</Version>
<FileVersion>6.9.4.0</FileVersion>
<ApplicationManifest>app.manifest</ApplicationManifest>
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
<Platforms>x64</Platforms>
Expand Down
12 changes: 10 additions & 2 deletions SleepStrap/UI/Elements/Settings/Pages/MacroPage.xaml
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,13 @@
<TextBlock Text="Loadout macro" FontSize="17" FontWeight="SemiBold" />
</ui:CardExpander.Header>
<StackPanel>
<UniformGrid Columns="2">
<Grid Margin="0,0,0,14">
<Grid.ColumnDefinitions><ColumnDefinition Width="*" /><ColumnDefinition Width="Auto" /></Grid.ColumnDefinitions>
<TextBlock Text="Enable Quick Loadout" FontWeight="SemiBold" VerticalAlignment="Center" />
<ui:ToggleSwitch Grid.Column="1" IsChecked="{Binding QuickLoadoutEnabled, Mode=TwoWay}" />
</Grid>

<UniformGrid Columns="2" IsEnabled="{Binding QuickLoadoutEnabled}">
<StackPanel Margin="0,0,8,12">
<TextBlock Text="Primary" Margin="0,0,0,5" FontWeight="SemiBold" />
<ComboBox ItemsSource="{Binding AvailablePrimary}" SelectedItem="{Binding SelectedPrimary, Mode=TwoWay}"
Expand Down Expand Up @@ -175,8 +181,10 @@
</StackPanel>
</UniformGrid>

<TextBlock Text="Quick loadout hotkey" Margin="0,2,0,5" FontWeight="SemiBold" />
<TextBlock Text="Quick loadout hotkey" Margin="0,2,0,5" FontWeight="SemiBold"
IsEnabled="{Binding QuickLoadoutEnabled}" />
<TextBox x:Name="QuickHotkeyBox" Height="38" IsReadOnly="True" Cursor="Hand"
IsEnabled="{Binding QuickLoadoutEnabled}"
HorizontalContentAlignment="Center" VerticalContentAlignment="Center"
FontWeight="SemiBold" Text="{Binding QuickLoadoutHotkeyDisplay, Mode=OneWay}"
PreviewMouseLeftButtonDown="QuickHotkeyBox_PreviewMouseLeftButtonDown"
Expand Down
12 changes: 11 additions & 1 deletion SleepStrap/UI/ViewModels/Settings/MacroViewModel.cs
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,16 @@ public WeaponOption(string name, MacroWeaponCategory category, int originalIndex
App.Settings.Prop.MacroQuickLoadoutHotkeyModifiers,
App.Settings.Prop.MacroQuickLoadoutHotkeyVirtualKey);

public bool QuickLoadoutEnabled
{
get => App.Settings.Prop.MacroQuickLoadoutEnabled;
set
{
App.Settings.Prop.MacroQuickLoadoutEnabled = value;
SaveAndNotify(nameof(QuickLoadoutEnabled));
}
}

public bool IsRunning
{
get => _isRunning;
Expand Down Expand Up @@ -515,7 +525,7 @@ private void AutomationTimer_Tick(object? sender, EventArgs e)

private bool IsQuickLoadoutHotkeyDown()
{
if (_quickHotkeyCaptureActive)
if (!QuickLoadoutEnabled || _quickHotkeyCaptureActive)
return false;

int key = App.Settings.Prop.MacroQuickLoadoutHotkeyVirtualKey;
Expand Down
Loading