Page 1 of 1

Screensaver Preview

Posted: Sat Dec 27, 2003 6:06 am
by eddy
Code updated For 5.20+

Whenever possible, I use PB command instead of API (threre are only two API commands)
What do you think about it.

(code updated scroll down)

Code: Select all

;-------------------
;-Preview Destructor
;-------------------

Procedure DestroyPreview(hnd)
   Repeat
      Delay(200)
   Until  IsWindow_(hnd)=0  
   End  
EndProcedure 

;-------------------
;-Preview
;-------------------

Procedure Preview(previewWindow)
   ; ------------------------
   ; wait end of preview 
   ; ------------------------
   ThreadPriority(CreateThread(@DestroyPreview(),previewWindow),4) 

   Repeat
      WaitWindowEvent()
   ForEver
EndProcedure 

Posted: Sat Dec 27, 2003 12:57 pm
by Seldon
I think it's the best "trick" for this kind of stuff, thank you for sharing!

Posted: Sat Dec 27, 2003 3:38 pm
by Kale
Remember that parsing the command line arg should be done a little more thoroughly, this is taken from a screensaver guide i used when writing screensavers:
When parsing command-line options, note that the letters may appear as lower-case or upper-case, and that there might be either a forward slash or a hyphen prefixing the letter, and that there may be either a space or a colon after the letter. Thus, you should respond to /p #### and -P:#### and all options in between.
I always use WinAPI for displaying stuff in the preview window, i don't think it can be done purely with native commands :( anyway here is how i do it (scroll to the bottom):

viewtopic.php?t=6010

Posted: Sun Dec 28, 2003 5:45 am
by eddy
@Kale
/p #### or -P:####
Thanks for your information about commandline.

@Seldom
Ok I've updated my code.

Did you test it ?
I hope that it works for you :)

Posted: Sun Dec 28, 2003 11:14 am
by Seldon
I just tested (1st version) and it works well under Win98SE (I have here). I said it's the best because you add a gadget not a child window that needs to have a certain class name, etc.. Maybe the way suggested by Kale is the standard, but your method is simple and it works (if you only need a static image in the preview).

Posted: Sun Dec 28, 2003 8:44 pm
by eddy
@Kale
Hmm it's true. We can't code a screensaver preview without WinAPI.
We always need a child window to receive some CLOSE messages.

Code: Select all

#WM_CLOSE_OLD_PREVIEW = 2000
#PREVIEW_TITLE  = "My Saver Preview"

;------------------- 
;-Preview Destructor 
;------------------- 

Procedure PreviewDestructor(window, message, wParam, lParam) 
    Result = #PB_ProcessPureBasicEvents 
    Select message 
    Case #WM_CLOSE
      DestroyWindow_(window)
      End 
    Case #WM_CLOSE_OLD_PREVIEW
        GlobalString.l = wParam 
        GlobalString$ = Space(1204) 
        GlobalGetAtomName_(GlobalString,GlobalString$,1024) 
        If GlobalString$=#PREVIEW_TITLE
           GlobalDeleteAtom_(GlobalString) 
           End
        EndIf            
    EndSelect 
    ProcedureReturn Result 
EndProcedure 

;------------------- 
;-Preview 
;------------------- 

Procedure Preview(preview)      
   ; ------------------------ 
   ; preview image 
   ; ------------------------ 
   UseGadgetList(preview) 

      GetClientRect_(preview,@rc.rect)    
      CreateImage(1,rc\right,rc\bottom) 
         StartDrawing(ImageOutput()) 
            For y=0 To ImageHeight() 
               Line(0,y,ImageWidth(),1,RGB($FF*y/ImageHeight(),$CC*y/ImageHeight(),0)) 
            Next 
         StopDrawing() 
      ImageGadget(1,0,0,0,0,ImageID())  

   CloseGadgetList() 


   ; ------------------------ 
   ; close all old previews 
   ; ------------------------ 
   Sendmessage_(#HWND_BROADCAST,#WM_CLOSE_OLD_PREVIEW,GlobalAddAtom_(#PREVIEW_TITLE),0)   

   ; ------------------------ 
   ; wait end of preview (using fake window)
   ; ------------------------    
   OpenWindow(0,0,0,0,0,#PB_WINDOW_INVISIBLE,#PREVIEW_TITLE)
   SetParent_(WindowID(),preview)   
   SetWindowCallback(@PreviewDestructor())
   
   Repeat 
      WaitWindowEvent() 
   ForEver   
EndProcedure 

;------------------- 
;-Main Program 
;------------------- 
FirstParam.s    =ProgramParameter() 
command.s       =LCase(Left(ReplaceString(FirstParam,"-","/"),2)) 
ParentWindow.l  =Val(StringField(FirstParam,2,":")) | Val(ProgramParameter()) 
        
Select command      
   Case "/p" 
      Preview(ParentWindow) 
   Case "/c" 
      ;Config(ParentWindow)      
   Default 
      ;Saver() 
EndSelect 


Posted: Sun Dec 28, 2003 9:23 pm
by Kale
woah! that code is way to complicated, hidden windows??? This bit worries me:

Code: Select all

; ------------------------ 
; close all old previews 
; ------------------------ 
Sendmessage_(#HWND_BROADCAST,#WM_CLOSE_OLD_PREVIEW,GlobalAddAtom_(#PREVIEW_TITLE),0)
close all old previews? no more than one instance of a config, preview or saver should ever start. use this to only start one instance to begin with:

Code: Select all

...
ScreensaverName = "PureBasic ScreenSaver v1.0"
...
;start only one screensaver
Procedure OnlyOneSCRStart(SaverName.s)
    *MutexName = @SaverName
    Shared OnlyOneStartMutex.l
    OnlyOneStartMutex = CreateMutex_(0, 1, *MutexName)
    OnlyOneStartError.l = GetLastError_()
    If OnlyOneStartMutex <> 0 And OnlyOneStartError = 0
        ProcedureReturn 1
    Else
        CloseHandle_(OnlyOneStartMutex)
        End
    EndIf
EndProcedure

;close the screensaver
Procedure OnlyOneSCRStop()
    CloseHandle_(OnlyOneStartMutex)
EndProcedure
...
;start only one instance of the program!
OnlyOneSCRStart(ScreensaverName)

;deal with the Parameters passed to this program
Select Parameter
    Case "" ;double clicked
        ExecuteConfiguration()
    Case "A" ;check password
        End
    Case "C" ;'Settings' button clicked in the screensaver dialog
        ExecuteConfiguration()
    Case "P" ;when the preview is requested in the screensaver dialog by selecting this screensaver
        ExecutePreview()
    Case "S" ;launch the main screensaver after an interval or by pressing 'Preview' in the dialog
        ExecuteScreenSaver()
EndSelect

;stop monitoring to see if one instance is running
OnlyOneSCRStop()
End

Posted: Sun Dec 28, 2003 9:29 pm
by freak
just one thing for eddy:

you can't just send a userdefined message to #HWND_BROADCAST!
Some other application might interpret it as one of it's own messages , and
act very strangely because of it.

You have to use RegisterWindowMessage_() to get a message ID, that is
unique throughout the system.

Timo

Posted: Mon Dec 29, 2003 9:03 am
by eddy
you can't just send a userdefined message to #HWND_BROADCAST!
8O 8O Oups!!

Third version should be better :lol:
Thanks Freak

Code: Select all

;------------------- 
;-Preview Destructor 
;------------------- 

Procedure PreviewAutoDestruction(window, message, wParam, lParam) 
   Shared WM_DestroyPreview
   
   Result = #PB_ProcessPureBasicEvents 
   Select message 
      Case #WM_CLOSE 
         DestroyWindow_(window) 
         End 
      Case WM_DestroyPreview
         End 
   EndSelect 
   ProcedureReturn Result 
EndProcedure 

;------------------- 
;-Preview 
;------------------- 

Procedure Preview(preview)
      
   ; ------------------------ 
   ; preview image 
   ; ------------------------ 
   UseGadgetList(preview) 
      
      GetClientRect_(preview,@rc.rect)    
      CreateImage(1,rc\right,rc\bottom) 
         StartDrawing(ImageOutput()) 
            For y=0 To ImageHeight() 
               Line(0,y,ImageWidth(),1,RGB($FF*y/ImageHeight(),$CC*y/ImageHeight(),0)) 
            Next 
         StopDrawing() 
      ImageGadget(1,0,0,0,0,ImageID())  

   CloseGadgetList() 


   ; ------------------------ 
   ; close previous previews 
   ; ------------------------ 
   Shared WM_DestroyPreview
   WM_DestroyPreview=RegisterWindowMessage_(@"PREVIEW AUTO DESTRUCTION")
   Sendmessage_(#HWND_BROADCAST,WM_DestroyPreview,0,0)    

   ; ------------------------ 
   ; the empty child window will receive all messages 
   ; wait end of preview... 
   ; ------------------------        
   SetParent_(OpenWindow(0,0,0,0,0,#PB_WINDOW_INVISIBLE,"CHILD WINDOW"),preview)    
   SetWindowCallback(@PreviewAutoDestruction()) 
    
   Repeat 
      WaitWindowEvent() 
   ForEver    
EndProcedure 

;------------------- 
;-Main Program 
;------------------- 
FirstParam.s    =ProgramParameter() 
command.s       =LCase(Left(ReplaceString(FirstParam,"-","/"),2)) 
ParentWindow.l  =Val(StringField(FirstParam,2,":")) | Val(ProgramParameter()) 
        
Select command      
   Case "/p" 
      Preview(ParentWindow) 
   Case "/c" 
      ;Config(ParentWindow)      
   Default 
      ;Saver() 
EndSelect