Screensaver Preview framework

BlitzMax Forums/BlitzMax Programming/Screensaver Preview framework

Eikon(Posted 2005) [#1]
I have seen a lot of interest in this recently, so I decided to try and convert my original Blitz+ work on this subject over to BlitzMax. The results so far are promising, but I have run into a few issues I'm hoping the community can help me solve. First, the code:

Strict

' // Framework & Modules //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%//
Framework BRL.GLMax2D 
Import BRL.Basic
Import BRL.System
Import BRL.Retro

Import "-lshell32"

' // Win32 API //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%//
Extern "win32"
	Function FindWindow(className:Byte Ptr, titleName:Byte Ptr) = "FindWindowA@8"
	Function SetWindowPos(hWnd, after, x, y, w, h, flags)
	Function GetWindowLong(hwnd, nIndex) = "GetWindowLongA@8"
	Function SetWindowLong(hwnd, index, nIndex) = "SetWindowLongA@12"
	Function SetParent(hWnd, parenthWnd)
	Function FindWindowEx(hWnd1, hWnd2, lpsz1:Byte Ptr, lpsz2:Byte Ptr) = "FindWindowExA@16"
End Extern

Local a$, bPre = False
For a$ = EachIn AppArgs
	If Instr(a$, "/p", 1) > 0 Then bPre = True; Exit
Next

If bPre = False Then End
Graphics 152, 112, 0', -1
Local bmx_hWnd = FindWindow("BlitzGL Window Class", "BlitzGL Window")
If bmx_hWnd = 0 Then Notify "Blitz Window Not Found!"; End
Local bmx_Style = GetWindowLong(bmx_hWnd, -16)
bmx_Style = bmx_Style And $40000000 Or $800000 And $10000000
SetWindowLong bmx_hWnd, -16, bmx_Style; Delay 10

Local pre_hWnd = FindWindow("#32770", "Display Properties")
If pre_hWnd = 0 Then Notify "Display Properties Window Not Found!"; End
pre_hWnd = FindWindowEx(pre_hWnd, 0, "#32770", "Screen Saver")
If pre_hWnd = 0 Then Notify "Screen Saver Window Not Found!"; End
Local static_hWnd = FindWindowEx(pre_hWnd, 0, "Static", "")
If static_hWnd = 0 Then Notify "Static Window Not Found!"; End
pre_hWnd = FindWindowEx(static_hWnd, 0, "SSDemoParent", "")
If pre_hWnd = 0 Then Notify "Preview Window Not Found!"; End

SetParent bmx_hWnd, pre_hWnd; Delay 10
SetWindowLong bmx_hWnd, -8, pre_hWnd; Delay 10
SetWindowPos bmx_hWnd, -2, 0, 0, 152, 112, $4 Or $10 Or $40 

SetClsColor 0, 0, 128
Local X = -75
Repeat
Cls

DrawText "Testing...", X, 52
If X < 152 Then X:+1 Else X = -75
pre_hWnd = FindWindow(Null, "Display Properties")

FlushMem; Flip
Until pre_hWnd = 0
Compile as a non GUI app and rename to .scr, then place it in your System/32 directory to test the preview.

Current problems:
- If built in GUI mode, there is a long delay before the preview becomes visible
- Preview stays visible when switching to other screensavers.
- Doesn't close if rundll32.exe persists. Why rundll32 sometimes stays open even after the display window has closed is unknown.

I'm thinking these problems are being caused by something in the BMax window style. Hopefully we can sort this out and make an open source solution for everyone - thank you.


Grisu(Posted 2005) [#2]
GREAT JOB EIKON! as always... :)


netmaestro(Posted 2005) [#3]
Hello Eikon,

I am netmaestro and I'm pleased to meet you. I'm very pleased to see this work of yours as I am finding it quite instructive. I am replying because I recently wrote a dll in Delphi for gamemaker programmers to use which serves this purpose. I also ran into the "refusing to quit" behavior and solved it quite effectively by making a call to the win32 dll in every step and returning true or false depending upon what IsWindowVisible(<PreviewWnd>) returns. If it comes back false, your saver should leap upon its sword without further ado.
Incorporate that and I'm pretty sure most of your problems are solved. btw, if you are into gamemaker at all you can find my dll with a sample program and docs at http://www.networkmaestro.com/gm_sslib2.zip


netmaestro(Posted 2005) [#4]
Hi, Eikon, me again.
I just noticed that in preview mode you are issuing the command graphics 152, 112, ... etc. You don't need to do this. The SetWindowPos call instructs Windows to give your program 152 x 112 and Windows will automatically scale your 800 x600 (or whatever) window to run in this space. You don't have to worry about it. I've seen programmers write savers in two complete versions, one to run in the prev and one to run normal, and that's just a whole lot of work for nothing.


netmaestro(Posted 2005) [#5]
Sorry to keep posting to this, but there is one other thing you have to do: You must create either a mutex object or a semaphore on entry and end if the creation fails. That is to prevent a second instance of your program from starting if another is already running. Tell you what, why don't I show you the full source code of my gm_sslib2.dll (which works perfectly btw):

library gm_sslib2;

There is really nothing to it.

uses
SysUtils,
Classes,
Windows;

function SetProcessLock(SemIn: String): Double; cdecl;
var MySem: THandle; SemTest: PChar;
begin
result := 1;
SemTest := PChar(SemIn);
MySem := CreateSemaphore(nil,0,1,SemTest);
if ((MySem <> 0) and (GetLastError = ERROR_ALREADY_EXISTS)) then
begin
CloseHandle(MySem);
Result := -4;
end;
end;

function WndAssign(wndprev: string; wndgame: string): double; cdecl;
var gamewnd, prevwnd: hwnd; myrect: Trect;
begin
gamewnd := strtoint(wndgame);
prevwnd := strtoint(wndprev);
setparent(gamewnd,prevwnd);
getwindowrect(prevwnd, myrect);
movewindow(gamewnd,0,0,myrect.Right-myrect.Left,myrect.Bottom-myrect.Top,true);
result := 0;
end;

function CheckVisible(wndprev: string): Double; cdecl;
var prevwnd: hwnd;
begin
prevwnd := strtoint(wndprev);
if not iswindowvisible(prevwnd) then result := -4 else result := 1;
end;

exports SetProcessLock, WndAssign, CheckVisible;

begin
end.