ScreenSaver - Template (Uses Graphic Control)

Category: Screensaver Tutor Series

Date: 02-16-2022

Return to Index


 
'Most of us enjoy watching screensavers and have often wondered how to
'convert one of our graphics routines to a screensaver. This snippet
'show how it's done.
 
'In general, a screesaver is just an *.EXE program whose window takes
'up the entire computer screen, including covering the Taskbar at the
'bottom of the screen.  The EXE file must be renamed as *.SCR and placed
'in the \Windows\System32 directory in order for it to be available to
'the Windows "Display Properties" application.
 
 
'Primary Code:
'For brevity, the primary code is presented only in the compilable example
'below. However, here is a list of the key code features:
 
'0. Fills entire screen/no border/covers task bar
'1. Settings dialog
'2. Always on top
'3. Respond to Mode commands from "Display Properties" application
'   "/s"        : Normal Mode
'   "/p <hWnd>" : Preview Mode
'   "/c <hWnd>" : Configuration Mode
'4. Remove cursor from screensaver, show cursor when screensaver closes
'5. Save Settings in INI file
'6. Capture ENTER or ESC in settings dialog'
'7. Capture any keyboard press
'8. Capture any mouse movement/click
 
'Limitations
'There are a few things this snippet does not handle.
'1. Passwords - no password entry, does not prevent use of Ctl-Alt-Del to bypass password
'2. Preview - does not display in the small preview window of the "Display Properties" app
 
'Notes:
'Graphics Canvas
'This snippet includes a dialog and a Graphic Control the same size
'as the dialog. All drawing takes place on the Graphic Control.
 
'ScreenSaver Sizing
'Screen dimensions are explicitly used to size the screensaver dialog.
'The %WS_Maximize dialog style could also have been used.
 
'Window Border/Windows Taskbar
'The WS_Popup style creates a borderless dialog and covers the Windows taskbar.
 
'Display Properties App
'This snippet responds to the mode commands /s, /p, and /c. It does not support
'the /a password setting mode. Mode commands are simply command switches used
'when the screensaver is started.  To get to the Display Properties application,
'right-mouse click on the desktop and select Properties from the context menu.
 
'Multiple /s Commands
'Also, I've read, but have not seen it to be the case, that the /s mode
'command may be sent multiple times by Windows, so that the screensaver
'must recognize when it is already running.  This snippet does not check
'to see if it is already running.
 
 
'Compilable Example:  (Jose Includes)
'After compilation of this snippet to an EXE, you must manually change the
'extension to SCR and place the file in the \windows\system32 folder. It will
'then be visible from the Windows Display Properties application.
#Compiler PBWin 9, PBWin 10
#Compile EXE
#Dim All
%Unicode=1
#Include "win32api.inc"
 
''========================================================================
''Change these as needed - specific to the screensaver
''========================================================================
 
Type mtxMatrixStruct
   X As Long
   Y As Long
   CharsDistance As Long
   BlackColorMix As Byte
   PrintChar As String * 1
   CurrentFlag As Long
End Type
 
   Global mtxMatrix() As mtxMatrixStruct, mtxMatrixMax As Long
   $Main_Title = "SCRNSAVE: Matrix:"
   $Setting_Title = "Matrix Settings"
   $INIFileName = "screensaver.ini"
   ''========================================================================
   ''End of change section
   ''========================================================================
 
   Global hDlg As DWord, hGraphic as DWord, hSettings as DWord
   Global w,h,TimerInterval, OldProc As Long
   %ID_Graphic = 600 : %ID_Timer = 700
 
Function PBMain() As Long
   Settings_INI "get"
   Select Case Left$(LCase$(Command$(1)),2)
      Case "/p" : DisplayInPreviewWindow  'preview window, then quit
      Case "/a" : DisplayPassWordDialog   'password, then quit
      Case "/c" : DisplaySettingsDialog   'settings, then quit
      Case "/s" : DisplayScreenSaver      'start normally
      Case Else : DisplayScreenSaver      'start normally
   End Select
End Function
 
Sub DisplayScreenSaver
   Desktop Get Size To w,h
   Dialog New Pixels, 0, $Main_Title,0,0,w,h, %WS_Popup To hDlg
   Control Add Graphic, hDlg, %ID_Graphic, "", 0,0,w,h,%WS_Visible
   Control Handle hDlg, %ID_Graphic to hGraphic
   Graphic Attach hDlg, %ID_Graphic, Redraw
   Graphic Color %Green, %Black
   Graphic Font "MS Serif", 28, 1
   Graphic Clear
   Dialog Show Modal hDlg Call DlgProc
End Sub
 
CallBack Function DlgProc() As Long
   Select Case CB.Msg
      Case %WM_InitDialog
         SetWindowPos(hDlg, %HWND_TOPMOST, 0, 0, 0, 0, %SWP_NoMove Or %SWP_NoSize)  'on Top
         InitializeGraphics
         OldProc = SetWindowLong(hGraphic, %GWL_WndProc, CodePTR(NewGraphicProc))  'subclass
         ShowCursor(%False)
         SetTimer(CB.Hndl, %ID_Timer, TimerInterval, ByVal %NULL)   'uses callback messages
         Dialog Post CB.Hndl, %WM_Timer, %ID_TIMER, 0  ' optional - forces an initial %WM_TIMER "event"
      Case %WM_SetCursor
         Static iCount as Long
         Incr iCount
         If iCount > 5 Then Dialog End hDlg
      Case %WM_Timer
         DisplayGraphics
      Case %WM_Destroy
         SetWindowLong hGraphic, %GWL_WNDPROC, OldProc   'un-subclass
         ShowCursor(%True)
         KillTimer CB.Hndl, %ID_Timer
         Settings_INI "save"
   End Select
End Function
 
Function NewGraphicProc(ByVal hWnd As LongByVal Msg As LongByVal wParam As LongByVal lParam As LongAs Long
   Select Case Msg
      Case %WM_Char
         Dialog End hDlg
   End Select
   Function = CallWindowProc(OldProc, hWnd, Msg, wParam, lParam)
End Function
 
Sub DisplayInPreviewWindow
   'not supported as this time. this Sub is a placeholder for when the
   'Display Properties calls the screensaver with the /p preview mode command.
   Dialog End hDlg  'not supported at this time
   Exit Sub
 
   '   The following code does not work. I found this in some VB6 examples,
   '   but haven't gotten it to work in PowerBASIC so far. Basically, the
   '   code is supposed to set the screensaver dialog as a child of the
   '   Preview Window so that the screensaver will play in that smaller window.
   '   I included it here in case anyone feels the urge to play with it.
 
   Local lngStyle As Long, dispHWND As Long, DispRec As RECT, temp$
   temp$ = Command$(1)
   Replace "/pWith "in temp$
   dispHWND = Val(Trim$(temp$))
   GetClientRect dispHWND, DispRec
   lngStyle = GetWindowLong(hDlg, %GWL_STYLE)
   lngStyle = lngStyle Or %WS_CHILD 'Append "WS_CHILD"
   SetWindowLong hDlg, %GWL_STYLE, lngStyle
   SetParent hDlg, dispHWND
   SetWindowLong hDlg, %GWL_HWNDPARENT, dispHWND
   SetWindowPos hDlg, %HWND_TOP, 0&, 0&, _
      DispRec.nRight, DispRec.nBottom, _
      %SWP_NOZORDER Or %SWP_NOACTIVATE Or %SWP_SHOWWINDOW
End Sub
 
   ''================================================================================
   ''Change below this line as needed - specific to the screensaver
   ''================================================================================
 
Sub Settings_INI(Task$)
   'This sub saves settings to an INI file wherever the SCR file is placed.
   'Each setting corresponds to a Global variable
   'To call, use one of these:     Settings_INI("get")  or   Setting_INI("save")
   'Examples of saving Numeric and String variable are shown
 
   Local temp As Asciiz*%Max_Path, INIFileName As Asciiz*%Max_Path
 
   'defines file name (any file name will work)
   INIFileName = Exe.Path$ + $INIFileName
 
   If Task$ = "getThen
      'get value for numeric variable
      Getprivateprofilestring "Settings", "TimerInterval", "20", temp, %Max_Path, INIFileName
      TimerInterval = Val(temp)
      'get value for string variable
      '      Getprivateprofilestring "Settings", "UnusedString", "20", temp, %Max_Path, INIFileName
      '      UnsedString = temp
   End If
 
   If Task$ = "saveThen
      'save numeric variable
      temp = Str$(TimerInterval)
      WritePrivateProfileString "Settings", "TimerInterval", temp, INIFileName
      'save string variable
      '      temp = Str$(UnusedString)
      '      WritePrivateProfileString "Settings", "UnusedString", temp, INIFileName
   End If
 
End Sub
 
Sub InitializeGraphics
   ReDim mtxMatrix(100)
   Local i As Long
   Randomize
   mtxMatrixMax = 100
   For i = 0 To mtxMatrixMax
      mtxMatrix(i).X = Rnd(1,w)                 'graphic width
      mtxMatrix(i).Y = Rnd(1,h)                 'graphic height
      mtxMatrix(i).CharsDistance = Rnd(5,15)   'Distance between chars
      mtxMatrix(i).BlackColorMix = Rnd(0,255)   'Green to black color
      mtxMatrix(i).PrintChar = Chr$(Rnd*1)
      mtxMatrix(i).CurrentFlag = 0              'Display chars or black box
   Next i
End Sub
 
Sub DisplayGraphics
   Local i As Long, k As Long
   k = 25
   For i = 0 To mtxMatrixMax    'Draw one char at each designated position
      mtxMatrix(i).Y = mtxMatrix(i).Y + mtxMatrix(i).CharsDistance
      If mtxMatrix(i).Y > h Then
         mtxMatrix(i).CurrentFlag = mtxMatrix(i).CurrentFlag XOR 1  'chars or black box
         mtxMatrix(i).Y = -1
         If mtxMatrix(i).CurrentFlag Then mtxMatrix(i).X = Rnd(0,w)  'new x location
         mtxMatrix(i).BlackColorMix = Rnd(0,255)    'New random color for the chars
      End If
 
      mtxMatrix(i).PrintChar = Chr$(Rnd(0,255))
      Graphic Set Pos (mtxMatrix(i).X, mtxMatrix(i).Y)    'Locate the cursor to print
 
      'print greenish character or black box at new XY position
      If mtxMatrix(i).CurrentFlag = 0  Then       'Display black block - to erase previously displayed chars
         Graphic Box (mtxMatrix(i).X, mtxMatrix(i).Y)-((mtxMatrix(i).X + k), (mtxMatrix(i).Y) + k), %Black, %Black
      Else          'Display characters
         Graphic Color Rgb(25, mtxMatrix(i).BlackColorMix, 25), %Black
         Graphic Print mtxMatrix(i).PrintChar
      End If
   Next i
   Graphic Redraw
End Sub
 
Sub DisplayPasswordDialog()
   'not supported
End Sub
 
Sub DisplaySettingsDialog()
   Dialog New Pixels, hDlg, $Setting_Title, (w-200)/2,(h-200)/2,200,200, %WS_SysMenu Or %WS_Caption Or %WS_ClipChildren To hSettings
   Dialog Set Icon hSettings, "aainfo"
   Control Add Label, hSettings, 800, "Timer Interval (ms):", 50, 60, 100, 20
   Control Add TextBox, hSettings, 900, "20", 50, 80, 100, 20
   Control Set Text hSettings, 900, Str$(TimerInterval)
   Dialog Show Modal hSettings Call SettingsProc
End Sub
 
CallBack Function SettingsProc() As Long
   Local temp$
   Select Case CB.Msg
      Case %WM_Command
         If CB.Ctl = %IDOK Then Dialog End hSettings
         If CB.Ctl = %IDCANCEL Then Dialog End hSettings
      Case %WM_Destroy
         Control Get Text hSettings, 900 To temp$
         TimerInterval = Val(temp$)
         If TimerInterval <=0 Then TimerInterval = 1
         Settings_INI "save"
   End Select
End Function
 
'gbs_00466


created by gbSnippets
http://www.garybeene.com/sw/gbsnippets.htm