Posts: 1,041
Threads: 140
Joined: Apr 2022
Reputation:
23
04-12-2025, 06:32 AM
(This post was last modified: 04-12-2025, 06:33 AM by madscijr.)
Recently I started using a big old HDTV as a monitor and wanted my PC to do some fun oldschool screensavers... did a small dive to find out where the starfield and moire and flying toasters went... apparently available from archive.org: Windows XP And 98 Screensavers ( 1) and can still work in newer Windows ( How to Use Classic Screen Savers in Windows 11, How to Add Classic XP Screensavers to Windows 11).
Reading up on them, are these SCR files just EXEs with a different file extension?
If so, is there anything different about them than a regular program other than the file extension?
Do they need to be full-screen, or have certain hooks that allow you to modify settings from the Windows control panel?
Because I would love to make my own screen savers, or use some of the excellent programs you guys have posted here, like
Posts: 125
Threads: 13
Joined: Apr 2022
Reputation:
59
(04-12-2025, 06:32 AM)madscijr Wrote: Reading up on them, are these SCR files just EXEs with a different file extension?
Yes, just rename it to .scr, then rightclick and choose "Install" from the contex menu. At least it worked that way until Win10, don't know if Win11 is pickier here.
Also look here: https://qb64phoenix.com/forum/showthread.php?tid=91
Posts: 1,041
Threads: 140
Joined: Apr 2022
Reputation:
23
Thanks m, @RhoSigma!
A question I have is: if you recall, in the control panel where you select your screensaver, there was often a [Settings] button you could click to get to usually some options, specific to the screensaver. A popup form would open with dropdowns, slider controls, checkboxes, buttons, etc. for that specific screensaver's options. This was displayed at the OS level but different for every screensaver (see screnshot for what you would see for the Ribbons screensaver). What I'm wondering is, how does Windows know what settings to display input controls for & what control types & options to show, and how does it send those options to the SCR file? My guess is that either the parameter names / types, control types & layout, and any enumerated values, are either stored in a seperate config file in some standard format the OS recognizes, or somewhere in the SCR file (maybe as binary or ascii data appended to the end of the EXE?), and sends the parameters to the SCR as command line options. Does anyone know?
Posts: 12
Threads: 2
Joined: Mar 2025
Reputation:
0
(04-12-2025, 04:49 PM)madscijr Wrote: Thanks m, @RhoSigma!
A question I have is: if you recall, in the control panel where you select your screensaver, there was often a [Settings] button you could click to get to usually some options, specific to the screensaver. A popup form would open with dropdowns, slider controls, checkboxes, buttons, etc. for that specific screensaver's options. This was displayed at the OS level but different for every screensaver (see screnshot for what you would see for the Ribbons screensaver). What I'm wondering is, how does Windows know what settings to display input controls for & what control types & options to show, and how does it send those options to the SCR file? My guess is that either the parameter names / types, control types & layout, and any enumerated values, are either stored in a seperate config file in some standard format the OS recognizes, or somewhere in the SCR file (maybe as binary or ascii data appended to the end of the EXE?), and sends the parameters to the SCR as command line options. Does anyone know?
Screensavers use their own dialog functions from what I gather. See here https://learn.microsoft.com/en-us/window.../scrnsave/
ScreenSaverConfigureDialog handles the configuration you talk about and is down to the programmer to define its function/purpose.
Posts: 1,041
Threads: 140
Joined: Apr 2022
Reputation:
23
Thanks @Tantalus, that sounds like precisely what I was looking for. I'll have to give it a look when I have some downtime (not on a Monday morning! LoL)
Posts: 3,016
Threads: 357
Joined: Apr 2022
Reputation:
282
From my personal experience, a screen saver is nothing more than an EXE file renamed as SCR. The OS treats it basically like any other EXE file, though I do think I read somewhere once that there can only ever be one instance of a SCR file running at a time. The OS has a few quirks for *full* SCR compatability so that it works 100% with settings and preview and all, but those are *optional* interfaces to work with.
For example, let's go with this little set of code:
Code: (Select All)
Randomize Timer
Screen _NewImage(320, 240, 32)
_FullScreen
_Delay .5 'give the screen time to initialize fully so we can associate mouse input with it and such
t# = Timer(0.001)
While _MouseInput: Wend: oldX = _MouseX: oldy = _MouseY 'initial mouse placement
Do
If Timer(0.001) > t# Then 'change background every 3 seconds
Cls , _RGB32(Rnd * 256, Rnd * 256, Rnd * 256)
t# = Timer(0.001) + 3
End If
While _MouseInput: Wend
If oldX <> _MouseX Or oldy <> _MouseY Then Exit Do 'if the mouse moved, quit
_Limit 10
Loop Until _KeyHit Or _MouseButton(1) Or _MouseButton(2) 'keyhit or mouse button hit, quit
System
Copy that and save it as blanker.bas.
Then compiler it to blanker.exe.
The go to whereever that exe was saved at and rename it to blanker.scr.
Now, right click on blanker.scr. In the pop-up interaction dialog, there should be an INSTALL option. Click it
Now if you go into your screen savers on your system, it should be there.
After the set time on your setting, it'll trigger automagically, and then blank your screen with different colors every 3 seconds until you move the mouse or touch a key.
That's it! All you need to do, in a nutshell.
Posts: 1,041
Threads: 140
Joined: Apr 2022
Reputation:
23
Hey, that worked! Thanks.
In fact, I liked it so much that I modified it to smoothly transition between random colors:
Code: (Select All) Dim c~&
Dim r1&, g1&, b1&
Dim dr!, dg!, db!
Screen _NewImage(320, 240, 32)
_FullScreen
_Delay .5 'give the screen time to initialize fully so we can associate mouse input with it and such
'c~& = Point(iX, iY)
Randomize Timer
c~& = _RGB32(Rnd * 256, Rnd * 256, Rnd * 256)
r1& = _Red32(c~&): g1& = _Green32(c~&): b1& = _Blue32(c~&)
Cls , c~&
GetNextColor c~&, dr!, dg!, db!
t# = Timer(0.001)
While _MouseInput: Wend: oldX = _MouseX: oldy = _MouseY 'initial mouse placement
Do
' Every 5 seconds, pick a new color and setup to transition to that in 5 seconds
If Timer(0.001) > t# Then 'change background every 3 seconds
'Cls , _RGB32(Rnd * 256, Rnd * 256, Rnd * 256)
't# = Timer(0.001) + 3
GetNextColor c~&, dr!, dg!, db!
'Cls , c~&
'r1& = _Red32(c~&): g1& = _Green32(c~&): b1& = _Blue32(c~&)
'c~& = _RGB32(r2&, g2&, b2&)
t# = Timer(0.001) + 5
End If
While _MouseInput: Wend
If oldX <> _MouseX Or oldy <> _MouseY Then Exit Do 'if the mouse moved, quit
' Change r/g/b values to move towards next color
r1& = _Red32(c~&): g1& = _Green32(c~&): b1& = _Blue32(c~&)
r1& = r1& + dr!
g1& = g1& + dg!
b1& = b1& + db!
c~& = _RGB32(r1&, g1&, b1&)
Cls , c~&
_Limit 51
Loop Until _KeyHit Or _MouseButton(1) Or _MouseButton(2) 'keyhit or mouse button hit, quit
System
' /////////////////////////////////////////////////////////////////////////////
' c~& = current color
' dr!, dg!, db! = amount to change each color channel per frame to get to next color in 5 seconds
Sub GetNextColor (c~&, dr!, dg!, db!)
Dim r1&, g1&, b1& ' current color
Dim r2&, g2&, b2& ' new color
Dim rDiff&, gDiff&, bDiff& ' diff between each color channel for old/new colors
r1& = _Red32(c~&): g1& = _Green32(c~&): b1& = _Blue32(c~&)
' choose a random new color
Do
r2& = Rnd * 256: g2& = Rnd * 256: b2& = Rnd * 256
If r2& <> r1& Or g2& <> g1& Or b2& <> b1& Then Exit Do
Loop
'c~& = _RGB32(r2&, g2&, b2&)
' get differences for each color channel
rDiff& = r1& - r2&
gDiff& = g1& - g2&
bDiff& = b1& - b2&
' determine how much to change each color channel each frame
' to evenly arrive at new color when time is up
If Abs(rDiff&) <= Abs(gDiff&) And Abs(rDiff&) <= Abs(bDiff&) Then
dr! = 1 * _IIf(r1& < r2&, 1, -1)
dg! = Abs(rDiff&) / Abs(gDiff&) * _IIf(g1& < g2&, 1, -1)
db! = Abs(rDiff&) / Abs(bDiff&) * _IIf(b1& < b2&, 1, -1)
ElseIf Abs(gDiff&) <= Abs(rDiff&) And Abs(gDiff&) <= Abs(bDiff&) Then
dr! = Abs(gDiff&) / Abs(rDiff&) * _IIf(r1& < r2&, 1, -1)
dg! = 1 * _IIf(g1& < g2&, 1, -1)
db! = Abs(gDiff&) / Abs(bDiff&) * _IIf(b1& < b2&, 1, -1)
Else
dr! = Abs(bDiff&) / Abs(rDiff&) * _IIf(r1& < r2&, 1, -1)
dg! = Abs(bDiff&) / Abs(gDiff&) * _IIf(g1& < g2&, 1, -1)
db! = 1 * _IIf(b1& < b2&, 1, -1)
End If
' max diff = 255
' change smallest 1 every cycle
' 255/5 = 51
' total cycles = {fps} * {seconds} = 255
' 51 * 5 = 255
End Sub ' GetNextColor
Posts: 1,041
Threads: 140
Joined: Apr 2022
Reputation:
23
04-14-2025, 05:40 PM
(This post was last modified: 04-14-2025, 05:55 PM by madscijr.)
I looked up the link @tantalus sent, and I don't think we need that.
What I did do was modify Steve's "blanker.bas" to display the command line args the program receives, then compiled and renamed .scr and installed it.
Here's what I found out:
When scr is first installed and runs, or when user changes the wait time, we get:
Command$(1) = "/p"
Command$(2) = "4591630"
When screensaver runs normally or in preview mode, we get:
Command$(1) = "/s"
When [Settings] button is clicked, we get:
Command$(1) = "/c:13504396"
So if we want to support the [Settings] button, all we need to do is check lcase$(left$(Command$(1), 2)).
If value = "/p", we might check the registry to see if the keys are present, and if not, initialize.
Or alternately look for a config file wherever we want to keep it, maybe in the same folder that the .scr resides in, something like:
Code: (Select All) Dim Shared ProgramPath$: ProgramPath$ = Left$(Command$(0), _InStrRev(Command$(0), "\"))
Dim Shared ProgramName$: ProgramName$ = Mid$(Command$(0), _InStrRev(Command$(0), "\") + 1)
Dim Shared ConfigFile$: ConfigFile$ = ProgramPath$ + ProgramName$ + ".cfg"
If _FileExists(ConfigFile$) = _FALSE Then
'(create config file)
'etc. etc.
ElseIf value = "/c", then display settings dialog, and show whatever settings values we have from the registry (or config file), and let the user change them & write back to the registry.
Else just run the screensaver normally using the current registry or config settings.
(Actually in all cases, we'd want to check that the registry keys or config file are present, and if not, initialize.)
I haven't tried reading/writing from/to the Windows registry, but I assume the example on the wiki page should work...
Code: (Select All) ' Let's have the screen saver check for command line arguments,
' to see if that's how Windows tells it what "mode" to open in,
' i.e., normal screensaver mode, or settings mode
' We observed the following 3 variations in command line arguments:
' When running normally or in preview mode, we get:
' Command$(1) = "/s"
' When [Settings] button is clicked, we get:
' Command$(1) = "/c:13504396"
' When scr is first installed and runs, we get:
' Command$(1) = "/p"
' Command$(2) = "4591630"
Dim i%
Dim c2$
Dim m$
Dim MsgRows%, MsgCols%
Dim ScreenRows%, ScreenCols%
Dim RowNum%, ColNum%, NextRow%
Dim r&, g&, b&
Dim bg~&
Dim fg~&
ReDim arrLines$(0)
MsgRows% = 0: MsgCols% = 0
m$ = "_CommandCount = " + _Trim$(Str$(_CommandCount)) + Chr$(13)
MsgRows% = 1: MsgCols% = Len(m$)
If _CommandCount > 0 Then
For i% = 1 To _CommandCount
m$ = m$ + Chr$(13): MsgRows% = MsgRows% + 1
m$ = m$ + "Command$(" + _Trim$(Str$(i%)) + ") = " + Chr$(34) + Command$(i%) + Chr$(34)
c2$ = LCase$(Left$(Command$(i%), 2)) ' look at first 2 chars
If c2$ = "/s" Then
m$ = m$ + ", /s received when running normally or in preview mode"
ElseIf c2$ = "/c" Then
m$ = m$ + ", /c received when Settings is opened"
' If we detect this, instead of running the screensaver
' we can display a Settings dialog
' load the values from the Registry
' let the user change values
' and save changes back to the Registry
' for how to do that, see:
' https://qb64phoenix.com/qb64wiki/index.php/Windows_Environment
ElseIf c2$ = "/p" Then
m$ = m$ + ", /p received when installing or wait time changed"
End If
If Len(m$) > MsgCols% Then MsgCols% = Len(m$)
Next i%
End If
Randomize Timer
Screen _NewImage(1024, 768, 32)
ScreenRows% = _Height / _FontHeight
ScreenCols% = _Width / _FontWidth
_FullScreen
_Delay .5 'give the screen time to initialize fully so we can associate mouse input with it and such
t# = Timer(0.001)
While _MouseInput: Wend: oldX = _MouseX: oldy = _MouseY 'initial mouse placement
Do
If Timer(0.001) > t# Then 'change background every 3 seconds
r& = Rnd * 256: g& = Rnd * 256: b& = Rnd * 256
bg~& = _RGB32(r&, g&, b&)
fg~& = _RGB32(255 - r&, 255 - g&, 255 - b&)
Cls , bg~&
RowNum% = RandomNumber%(2, (ScreenRows% - MsgRows%) - 1)
ColNum% = RandomNumber%(2, (ScreenCols% - MsgCols%) - 1)
Color fg~&, bg~&
split m$, Chr$(13), arrLines$()
NextRow% = RowNum%
For i% = LBound(arrLines$) To UBound(arrLines$)
Locate NextRow%, ColNum%
Print arrLines$(i%);
NextRow% = NextRow% + 1
Next i%
t# = Timer(0.001) + 3
End If
While _MouseInput: Wend
If oldX <> _MouseX Or oldy <> _MouseY Then Exit Do 'if the mouse moved, quit
_Limit 10
Loop Until _KeyHit Or _MouseButton(1) Or _MouseButton(2) 'keyhit or mouse button hit, quit
System
' /////////////////////////////////////////////////////////////////////////////
Function RandomNumber% (Min%, Max%)
Dim NumSpread%
NumSpread% = (Max% - Min%) + 1
RandomNumber% = Int(Rnd * NumSpread%) + Min% ' GET RANDOM # BETWEEN Max% AND Min%
End Function ' RandomNumber%
' /////////////////////////////////////////////////////////////////////////////
' Split and join strings
' https://www.qb64.org/forum/index.php?topic=1073.0
'
' FROM luke, QB64 Developer
' Date: February 15, 2019, 04:11:07 AM
'
' Given a string of words separated by spaces (or any other character),
' splits it into an array of the words. I've no doubt many people have
' written a version of this over the years and no doubt there's a million
' ways to do it, but I thought I'd put mine here so we have at least one
' version. There's also a join function that does the opposite
' array -> single string.
'
' Code is hopefully reasonably self explanatory with comments and a little demo.
' Note, this is akin to Python/JavaScript split/join, PHP explode/implode.
'Split in$ into pieces, chopping at every occurrence of delimiter$. Multiple consecutive occurrences
'of delimiter$ are treated as a single instance. The chopped pieces are stored in result$().
'
'delimiter$ must be one character long.
'result$() must have been REDIMmed previously.
' Modified to handle multi-character delimiters
Sub split (in$, delimiter$, result$())
Dim start As Integer
Dim finish As Integer
Dim iDelimLen As Integer
ReDim result$(-1)
iDelimLen = Len(delimiter$)
start = 1
Do
'While Mid$(in$, start, 1) = delimiter$
While Mid$(in$, start, iDelimLen) = delimiter$
'start = start + 1
start = start + iDelimLen
If start > Len(in$) Then
Exit Sub
End If
Wend
finish = InStr(start, in$, delimiter$)
If finish = 0 Then
finish = Len(in$) + 1
End If
ReDim _Preserve result$(0 To UBound(result$) + 1)
result$(UBound(result$)) = Mid$(in$, start, finish - start)
start = finish + 1
Loop While start <= Len(in$)
End Sub ' split
Posts: 3,016
Threads: 357
Joined: Apr 2022
Reputation:
282
Note: Instead of a screensaver, I tend to use a desktop flipper instead. See my post here: https://qb64phoenix.com/forum/showthread.php?tid=3616
I thought I'd update this old routine and share it for you, in case you either didn't see it ages ago or forgot about it.
Posts: 1,041
Threads: 140
Joined: Apr 2022
Reputation:
23
04-15-2025, 04:35 PM
(This post was last modified: 04-15-2025, 04:35 PM by madscijr.)
Neat - thanks for sharing that, it might come in handy!
|