Page 1 of 1

Message Timer for games and apps, splash window example!

Posted: Mon Sep 05, 2005 2:18 pm
by Rescator
Code updated for 5.20+

The Splash() procedure is (or should be) fully self contained,
no need to provide window id constants or gadget constant etc etc.
(it's an amazing example of how easy PB is really :)
The Splash accept the timeout in the form of seconds.

Now for the more advanced stuff (or not),
the MessageTimer procedures, normally you would just really use one here,
namely the MessageTimer() one, hopefully it's arguments are self explanatory.

It should be fully thread safe except for one little quirk,
the messagetimer structure do not get the threadid value set
until AFTER the thread has been created, so do not check the threadid in
the structure immediately after you started the timer,
it will take a few cpu cycles before it get set.

Anything else should be fully thread safe as it handles it's own memory and so on.
As you may also notice there is a bunch of supporting functions,
these are simple wrappers for the various Thread functions in PureBasic,
and simply saves you the trouble of making these yourself, oh how nice I am :P

Note! MessageTimer() will fail if delay is 0 this is on purpose to avoid
wasting resources since there is no point in a 0ms timer!

If the loop argument is set as 0 then the timer will loop forever,
this kind of timer is maybe of more use to certain apps, or games.
The only way to stop and cleanup the timer if loop is 0 is to use the MessageTimer_Kill() function,
so make sure you keep track of the memory pointer that MessageTimer() returns in that case.
(it is a pointer to the memory structure)

You can also use the priority function to set the system priority of a timer,
mostly of interest to time critical programs I guess.

There is also pause and resume functions,
now these may be very interesting to game makers
as you could pause the timers as the player uses a game menu etc.
Or you could apparently pause/freeze time.

Please note that this is all based on the Delay() function,
so the timer resolution is the same as that.

I would not advice the use of this timer for graphics and time critical AI and so on,
as there is some overhead and lack of timer precision.
(consider this a normal resolution timer + message overhead)
However, MessageTimer() should be perfect for splash screens,
events, triggers, auto closing of apps/windows (like the splash example),
periodical checking, program update checks, etc. etc.
the max value of the delay is the same as the Delay() function,
so you can easily set a messagetimer to trigger once each 24 days if you wanted. hehe!

Code: Select all

;by Rescator
;EmSai OpenSource Licence http://emsai.net/esos/


Structure struct_messagetimer
  delay.l
  loop.l
  hwnd.l
  message.l
  wparam.l
  lparam.l
  threadid.l
  die.l
EndStructure

Procedure MessageTimer_Threadloop(*mem.struct_messagetimer)
  If *mem
    If *mem\loop=0
      Repeat
        If *mem\die
          Break
        Else
          Delay(*mem\delay)
          PostMessage_(*mem\hwnd,*mem\message,*mem\wparam,*mem\lparam)
        EndIf
      ForEver
    Else
      i=0
      Repeat
        Delay(*mem\delay)
        PostMessage_(*mem\hwnd,*mem\message,*mem\wparam,*mem\lparam)
        i+1
      Until i=*mem\loop
    EndIf
    FreeMemory(*mem)
  EndIf
EndProcedure

Procedure MessageTimer(windowid.l,delay.l,loop.l,message.l,wparam.l,lparam.l)
  ;if loop is 0 the timer never end, but must be killed instead
  Protected *mem.struct_messagetimer
  If delay<>0 And delay<>#INFINITE And loop>#INFINITE
    *mem=AllocateMemory(SizeOf(struct_messagetimer))
    If *mem
      *mem\hwnd=WindowID(windowid)
      *mem\delay=delay
      *mem\message=message
      *mem\wparam=wparam
      *mem\lparam=lparam
      *mem\loop=loop
      *mem\die=#False
      *mem\threadid=CreateThread(@MessageTimer_Threadloop(),*mem)
      If *mem\threadid
        ProcedureReturn *mem
      Else
        FreeMemory(*mem)
      EndIf
    EndIf
  EndIf
  ProcedureReturn #False
EndProcedure

Procedure MessageTimer_Kill(*mem.struct_messagetimer)
  If *mem
    *mem\die=#True
  EndIf
EndProcedure

Procedure MessageTimer_Pause(*mem.struct_messagetimer)
  If *mem
    PauseThread(*mem\threadid)
  EndIf
EndProcedure

Procedure MessageTimer_Resume(*mem.struct_messagetimer)
  If *mem
    ResumeThread(*mem\threadid)
  EndIf
EndProcedure

Procedure MessageTimer_Priority(*mem.struct_messagetimer,priority.l)
  If *mem
    ProcedureReturn ThreadPriority(*mem\threadid,priority)
  EndIf
  ProcedureReturn #False
EndProcedure

Procedure Splash(splashimage$,splashtitle$,timeout,parentwindow)
  image_splash=LoadImage(#PB_Any,splashimage$)
  If image_splash
    flags=#WS_POPUP|#PB_Window_ScreenCentered|#PB_Window_Invisible
    window_splash=OpenWindow(#PB_Any,0,0,ImageWidth(image_splash),ImageHeight(image_splash),splashtitle$,flags,parentwindow)
    If window_splash
      AddKeyboardShortcut(window_splash,#PB_Shortcut_Escape,window_splash)
      AddKeyboardShortcut(window_splash,#PB_Shortcut_Space,window_splash)
      If ImageGadget(0,0,0,ImageWidth(image_splash),ImageHeight(image_splash),ImageID(image_splash))
        Delay(1)
        HideWindow(window_splash,0)
        timer1=MessageTimer(window_splash,timeout*1000,1,#PB_Event_CloseWindow,0,0)
        If timer1
          Debug "Success"
        Else
          Debug "Failed"
        EndIf
        Repeat
          Event=WindowEvent()
          If Event=#PB_Event_Menu Or Event=#PB_Event_Gadget
            Event=#PB_Event_CloseWindow
          Else
            Delay(1)
          EndIf
        Until Event=#PB_Event_CloseWindow
        FreeGadget(0)
      EndIf
    EndIf
    CloseWindow(window_splash)
    FreeImage(image_splash)
  EndIf
EndProcedure

UsePNGImageDecoder()
Splash("splash.png","splash test",10,0)

Posted: Mon Sep 05, 2005 2:34 pm
by Dare2
Thank you.
:)

Posted: Mon Sep 05, 2005 2:42 pm
by Rescator
Did a small improvement!
Changed the way the MessageTimer_Kill() function works,
now instead of killing the thread, it tells the thread to die,
and let the thread free up the allocated memory itself,
and exit gracefully.

Posted: Mon Sep 05, 2005 2:48 pm
by Dare2
I have another suggestion for an improvement.

First line:

Code: Select all

; by Rescator
That way we can remember where this came from. I have heaps of code from the boards. Sometimes I use some of this in code and can't recall/find where it came from. Then can't give credit.

Posted: Mon Sep 05, 2005 2:56 pm
by Rescator
*laughs* Ok, done :)

Posted: Mon Sep 05, 2005 8:18 pm
by griz
Nice, thanks for sharing Rescator!