Page 1 of 1

Programming a Timer

Posted: Sat Mar 16, 2024 3:59 pm
by Hessi1971
Hallo together,

I am very new in Purebasic and also Mac OS.

I want to program a simple timer. For example the timer should count from 45 sec to 0 sec in 1 sec steps
After this wait 15 sec and then count again from 45 sec to 0 sec.

And this for 10 replais /times. I think the the 10 replais I can to with a for next operation.

But I do not know how to code a imple timer.

Thank you very much for any solution or tipps.

Re: Programming a Timer

Posted: Sat Mar 16, 2024 4:05 pm
by mk-soft
I write a small example. Need short time

Re: Programming a Timer

Posted: Sat Mar 16, 2024 4:07 pm
by Hessi1971
Thanks a lot

Re: Programming a Timer

Posted: Sat Mar 16, 2024 4:19 pm
by infratec
Faster :wink:

Code: Select all

EnableExplicit

#XCounterDefaultValue = 10
#LongTimerDefaultValue = 45
#ShortTimerDefaultValue = 15


Define.i LongTimerCounter, ShortTimerCounter, Event, XCounter

OpenWindow(0, 0, 0, 200, 100, "Timer", #PB_Window_SystemMenu)
CreateStatusBar(0, WindowID(0))
AddStatusBarField(60)
AddStatusBarField(60)
AddStatusBarField(60)

XCounter = #XCounterDefaultValue
LongTimerCounter = #LongTimerDefaultValue

AddWindowTimer(0, 0, 1000)

StatusBarText(0, 0, Str(XCounter) + "-" + Str(#XCounterDefaultValue), #PB_StatusBar_Center)
StatusBarText(0, 1, Str(LongTimerCounter) + "-" + Str(#LongTimerDefaultValue), #PB_StatusBar_Center)
StatusBarText(0, 2, Str(ShortTimerCounter) + "-" + Str(#ShortTimerDefaultValue), #PB_StatusBar_Center)

Repeat
  
  Event = WaitWindowEvent()
  Select Event
    Case #PB_Event_Timer
      Select EventTimer()
        Case 0
          If LongTimerCounter > 0
            LongTimerCounter - 1
            Debug LongTimerCounter
            If LongTimerCounter = 0
              ShortTimerCounter = #ShortTimerDefaultValue
            EndIf
          Else
            ShortTimerCounter - 1
            Debug ShortTimerCounter
            If ShortTimerCounter = 0
              XCounter - 1
              If XCounter = 0
                Debug "End"
                RemoveWindowTimer(0, 0)
              Else
                LongTimerCounter = #LongTimerDefaultValue
              EndIf
            EndIf
          EndIf
          StatusBarText(0, 0, Str(XCounter) + "-" + Str(#XCounterDefaultValue), #PB_StatusBar_Center)
          StatusBarText(0, 1, Str(LongTimerCounter) + "-" + Str(#LongTimerDefaultValue), #PB_StatusBar_Center)
          StatusBarText(0, 2, Str(ShortTimerCounter) + "-" + Str(#ShortTimerDefaultValue), #PB_StatusBar_Center)
          
      EndSelect
      
  EndSelect
  
Until Event = #PB_Event_CloseWindow

Re: Programming a Timer

Posted: Sat Mar 16, 2024 4:41 pm
by mk-soft
Wellcome :!:

Infratec code is fine and simple ;)

Here I have to apologise for my complex example.

But should be as Basic to be the right entry into programming under macOS.
- Structured event processing.
- Time-controlled background processing (thread)
- Rules for passing to the GUI (gadget must not be changed from thread)
- Threads must be terminated before the programme is terminated.

Code: Select all

;-TOP

#ProgramTitle = "Main Window"
#ProgramVersion = "v1.01.2"

Enumeration Windows
  #Main
EndEnumeration

Enumeration MenuBar
  #MainMenu
EndEnumeration

Enumeration MenuItems
  #MainMenuAbout
  #MainMenuExit
EndEnumeration

Enumeration Gadgets
  #MainProgress
EndEnumeration

Enumeration StatusBar
  #MainStatusBar
EndEnumeration

; ----

Enumeration CustomEvent #PB_Event_FirstCustomValue
  #MyEvent_Status
  #MyEvent_Timer
EndEnumeration

; ----

Structure udtThreadData
  ThreadId.i
  Exit.i
  Window.i
  Gadget.i
EndStructure

Global thData.udtThreadData

Procedure thTimer(*data.udtThreadData)
  Protected loop, time
  With *data
    For loop = 1 To 10
      PostEvent(#MyEvent_Status, \Window, 0, 0, loop)
      If \Exit
        Break
      EndIf
      For time = 1 To 60
        If \Exit
          Break 2
        EndIf
        If time <= 45
          PostEvent(#MyEvent_Timer, \Window, 0, 0, time)
        EndIf
        Delay(1000)
      Next
    Next
  EndWith
EndProcedure

; ----

Procedure UpdateWindow()
  Protected dx, dy
  dx = WindowWidth(#Main)
  dy = WindowHeight(#Main) - StatusBarHeight(#MainStatusBar) - MenuHeight()
  ; Resize gadgets
  ResizeGadget(#MainProgress, 10, 10, dx - 20, 30)
EndProcedure

Procedure Main()
  Protected dx, dy
  
  #MainStyle = #PB_Window_SystemMenu | #PB_Window_SizeGadget | #PB_Window_MaximizeGadget | #PB_Window_MinimizeGadget
  
  If OpenWindow(#Main, #PB_Ignore, #PB_Ignore, 800, 600, #ProgramTitle , #MainStyle)
    ; Menu
    CreateMenu(#MainMenu, WindowID(#Main))
    MenuTitle("&File")
    CompilerIf #PB_Compiler_OS = #PB_OS_MacOS
      MenuItem(#PB_Menu_About, "")
    CompilerElse
      MenuItem(#MainMenuAbout, "About")
    CompilerEndIf
    ; Menu File Items
    
    CompilerIf Not #PB_Compiler_OS = #PB_OS_MacOS
      MenuBar()
      MenuItem(#MainMenuExit, "E&xit")
    CompilerEndIf
    
    ; StatusBar
    CreateStatusBar(#MainStatusBar, WindowID(#Main))
    AddStatusBarField(200)
    AddStatusBarField(#PB_Ignore)
    
    ; Gadgets
    dx = WindowWidth(#Main)
    dy = WindowHeight(#Main) - StatusBarHeight(#MainStatusBar) - MenuHeight()
    ProgressBarGadget(#MainProgress, 10, 10, dx - 20, 30, 0, 59)
    
    ; Bind Events
    BindEvent(#PB_Event_SizeWindow, @UpdateWindow(), #Main)
    
    ; Init Thread
    thData\Window = #Main
    thData\ThreadId = CreateThread(@thTimer(), @thData)
    
    ; Event Loop
    Repeat
      Select WaitWindowEvent()
        Case #PB_Event_CloseWindow
          Select EventWindow()
            Case #Main
              If IsThread(thData\ThreadId)
                thData\Exit = #True
                If Not WaitThread(thData\ThreadId, 2000)
                  KillThread(thData\ThreadId)
                EndIf
              EndIf
              Break
              
          EndSelect
          
        Case #PB_Event_Menu
          Select EventMenu()
            CompilerIf #PB_Compiler_OS = #PB_OS_MacOS   
              Case #PB_Menu_About
                PostEvent(#PB_Event_Menu, #Main, #MainMenuAbout)
                
              Case #PB_Menu_Preferences
                
              Case #PB_Menu_Quit
                PostEvent(#PB_Event_CloseWindow, #Main, #Null)
                
            CompilerEndIf
            
          Case #MainMenuAbout
            MessageRequester("About", #ProgramTitle + #LF$ + #ProgramVersion, #PB_MessageRequester_Info)
              
          Case #MainMenuExit
            PostEvent(#PB_Event_CloseWindow, #Main, #Null)
            
          EndSelect
          
        Case #PB_Event_Gadget
          Select EventGadget()
              
          EndSelect
          
        Case #MyEvent_Status
          StatusBarText(#MainStatusBar, 0, " Loop " + EventData())
          
        Case #MyEvent_Timer
          SetGadgetState(#MainProgress, EventData())
          StatusBarText(#MainStatusBar, 1, " Time " + EventData())
          
      EndSelect
    ForEver
    
  EndIf
  
EndProcedure : Main()

Re: Programming a Timer

Posted: Sat Mar 16, 2024 5:45 pm
by RASHAD
infratec fine code with BindEvent()

Code: Select all

;EnableExplicit

#XCounterDefaultValue = 10
#LongTimerDefaultValue = 45
#ShortTimerDefaultValue = 15
Global LongTimerCounter,ShortTimerCounter,XCounter

Procedure timerCB()
  If LongTimerCounter > 0
    LongTimerCounter - 1
    Debug LongTimerCounter
    If LongTimerCounter = 0
      ShortTimerCounter = #ShortTimerDefaultValue
    EndIf
  Else
    ShortTimerCounter - 1
    Debug ShortTimerCounter
    If ShortTimerCounter = 0
      XCounter - 1
      If XCounter = 0
        Debug "End"
        RemoveWindowTimer(0, 0)
      Else
        LongTimerCounter = #LongTimerDefaultValue
      EndIf
    EndIf
  EndIf
  StatusBarText(0, 0, Str(XCounter) + "-" + Str(#XCounterDefaultValue), #PB_StatusBar_Center)
  StatusBarText(0, 1, Str(LongTimerCounter) + "-" + Str(#LongTimerDefaultValue), #PB_StatusBar_Center)
  StatusBarText(0, 2, Str(ShortTimerCounter) + "-" + Str(#ShortTimerDefaultValue), #PB_StatusBar_Center)
EndProcedure

;Define.i LongTimerCounter, ShortTimerCounter, Event, XCounter

OpenWindow(0, 0, 0, 200, 100, "Timer", #PB_Window_SystemMenu)
CreateStatusBar(0, WindowID(0))
AddStatusBarField(60)
AddStatusBarField(60)
AddStatusBarField(60)

XCounter = #XCounterDefaultValue
LongTimerCounter = #LongTimerDefaultValue

AddWindowTimer(0, 0, 1000)

BindEvent(#PB_Event_Timer,@timerCB())

StatusBarText(0, 0, Str(XCounter) + "-" + Str(#XCounterDefaultValue), #PB_StatusBar_Center)
StatusBarText(0, 1, Str(LongTimerCounter) + "-" + Str(#LongTimerDefaultValue), #PB_StatusBar_Center)
StatusBarText(0, 2, Str(ShortTimerCounter) + "-" + Str(#ShortTimerDefaultValue), #PB_StatusBar_Center)

Repeat
  
  Event = WaitWindowEvent()
  Select Event
      
  EndSelect
  
Until Event = #PB_Event_CloseWindow


Re: Programming a Timer

Posted: Sat Mar 16, 2024 5:53 pm
by mk-soft
I think we all have too much time 8)

Re: Programming a Timer

Posted: Sat Mar 16, 2024 5:56 pm
by RASHAD
:mrgreen:

Re: Programming a Timer

Posted: Sat Mar 16, 2024 7:49 pm
by infratec
Why is BindEvent() better?

My version catches the event in the main program event loop.
This loop executes all events one after an other.
If you move heavily the mose, for example, then there are many events to process.
This can result in a short delay until the timer event is processed.
But the fault is not additional, since the 1 second timer generates the next event independend.

If you use BindEvent() you get a bit more precise timing.

But I don't know if this is relevant.

Re: Programming a Timer

Posted: Sat Mar 16, 2024 8:18 pm
by Fred
Some actions actually locks the event loop (like resizing a window on Windows) but binded events are still dispatched.

Re: Programming a Timer

Posted: Sat Mar 16, 2024 10:48 pm
by skywalk
Interesting, I asked about the order of event execution in another thread.

Is it true the main event loop responds after bind events and custom post events?

Re: Programming a Timer

Posted: Sun Mar 17, 2024 6:48 am
by RASHAD
Better use of BindEvent()
Exactly it's not perfect because if it stopped and started again the continuation of the timer cycle is messed up

Code: Select all

;EnableExplicit

#XCounterDefaultValue = 10
#LongTimerDefaultValue = 45
#ShortTimerDefaultValue = 15
Global LongTimerCounter,ShortTimerCounter,XCounter

Procedure timerCB()
  If LongTimerCounter > 0
    LongTimerCounter - 1
    Debug LongTimerCounter
    If LongTimerCounter = 0
      ShortTimerCounter = #ShortTimerDefaultValue
    EndIf
  Else
    ShortTimerCounter - 1
    Debug ShortTimerCounter
    If ShortTimerCounter = 0
      XCounter - 1
      If XCounter = 0
        Debug "End"
        RemoveWindowTimer(0, 0)
      Else
        LongTimerCounter = #LongTimerDefaultValue
      EndIf
    EndIf
  EndIf
  StatusBarText(0, 0, Str(XCounter) + "-" + Str(#XCounterDefaultValue), #PB_StatusBar_Center)
  StatusBarText(0, 1, Str(LongTimerCounter) + "-" + Str(#LongTimerDefaultValue), #PB_StatusBar_Center)
  StatusBarText(0, 2, Str(ShortTimerCounter) + "-" + Str(#ShortTimerDefaultValue), #PB_StatusBar_Center)
EndProcedure

OpenWindow(0, 0, 0, 200, 100, "Timer", #PB_Window_SystemMenu|#PB_Window_ScreenCentered)
ButtonGadget(0,10,10,50,20,"Start")
ButtonGadget(1,65,10,50,20,"Stop")
ButtonGadget(2,120,10,50,20,"END")
CreateStatusBar(0, WindowID(0))
AddStatusBarField(60)
AddStatusBarField(60)
AddStatusBarField(60)

XCounter = #XCounterDefaultValue
LongTimerCounter = #LongTimerDefaultValue

AddWindowTimer(0, 0, 1000)

StatusBarText(0, 0, Str(XCounter) + "-" + Str(#XCounterDefaultValue), #PB_StatusBar_Center)
StatusBarText(0, 1, Str(LongTimerCounter) + "-" + Str(#LongTimerDefaultValue), #PB_StatusBar_Center)
StatusBarText(0, 2, Str(ShortTimerCounter) + "-" + Str(#ShortTimerDefaultValue), #PB_StatusBar_Center)

Repeat  
  Select WaitWindowEvent(1)
    Case #PB_Event_CloseWindow
      If start = 1
        UnbindEvent(#PB_Event_Timer,@timerCB())
      EndIf
      Quit = 1
      
    Case #PB_Event_Gadget
      Select EventGadget()
        Case 0
          start = 1
          BindEvent(#PB_Event_Timer,@timerCB())
                    
        Case 1
          start = 0
          UnbindEvent(#PB_Event_Timer,@timerCB())
          
        Case 2
          If start = 1
            UnbindEvent(#PB_Event_Timer,@timerCB())
          EndIf
          Quit = 1
          
      EndSelect      
  EndSelect
  
Until Quit = 1


Re: Programming a Timer

Posted: Sun Mar 17, 2024 3:12 pm
by Michael Vogel
Not a solution for you (windows) but maybe an inspiration (also for creating more console apps)... :wink:

Code: Select all

; Define 500 lines of code

	EnableExplicit

	#Q=#DOUBLEQUOTE$
	#Shades=4
	#ConsoleTitle="Console Clock by Michael Vogel"

	Global Dim Pattern(#Shades)

	Structure CharType
		Width.i
		Height.i
		Space.i
		DotWidth.i
		Bytes.i
		OffsetX.i[5]
		OffsetY.i
		Dot.i
		TimeDot.i
		Time.i[5]
		OldTime.i[5]
		Transition.i[2]
		Phase.i[2]
		ColorMode.i
		Foreground.i
		Background.i
		ConsoleColors.i
		Delay.i
		Running.i
		Refresh.i
		FontFace.s
		FontType.i
		FontRatio.i
		ClearOnExit.i
		Countdown.i
		StopAtZero.i
		TimerDone.i
		FullChar.i
		MidChar.i
		HalfChar.i
		TimeFormat.s
		TimeOffset.i
		CpuTime.i
	EndStructure

	Structure ConsoleType
		Handle.i
		Width.i
		Height.i
		Cursor.i
		Background.i
		Window.i
	EndStructure

	Global Dim Matrix.b(11,10)

	Global Char.CharType
	Global Con.ConsoleType

	Structure Console_COORD
		StructureUnion
			coord.COORD
			long.l
		EndStructureUnion
	EndStructure

; EndDefine
Procedure Max(a,b)
	If a>b
		ProcedureReturn a
	Else
		ProcedureReturn b
	EndIf
EndProcedure
Procedure Limit(value,min,max)
	If value>max
		ProcedureReturn max
	ElseIf value<min
		ProcedureReturn min
	Else
		ProcedureReturn value
	EndIf
EndProcedure
Procedure Check(check,yes,no)
	If check
		ProcedureReturn yes
	Else
		ProcedureReturn no
	EndIf
EndProcedure
Procedure ConsoleBufferLocate(x,y)
	Protected Coordinates.Console_COORD
	Coordinates\coord\x=x
	Coordinates\coord\y=y
	SetConsoleCursorPosition_(Con\Handle,Coordinates\long)
EndProcedure
Procedure ConsoleClear()
	Protected Coordinates.Console_COORD
	Protected NumberOfCharsWritten
	Coordinates\coord\x=0
	Coordinates\coord\y=0
	FillConsoleOutputCharacter_(Con\Handle,' ',Con\Height*Con\Width,Coordinates\long,@NumberOfCharsWritten)
	FillConsoleOutputAttribute_(Con\Handle,Con\Background+Char\Foreground,Con\Height*Con\Width,Coordinates\long,@NumberOfCharsWritten)
	ConsoleBufferLocate(0,0)
EndProcedure
Procedure ConsoleBackground(color)
	ProcedureReturn (#BACKGROUND_BLUE*Bool(color&1)) | (#BACKGROUND_GREEN*Bool(color&2)) | (#BACKGROUND_RED*Bool(color&4)) | (#BACKGROUND_INTENSITY*Bool(color&8))
EndProcedure
Procedure CreateFont()

	Protected.f cw,ch,fw,fh,mx,my,ox,oy,px,py
	Protected.i i,xmin,xmax,ymin,ymax,z
	Protected.i n,w,o,ix,iy
	Protected.s s

	fw=200
	fh=200
	xmin=9999
	ymin=9999

	For i=0 To 10
		s=Chr('0'+i)
		CreateImage(i,fw+50,fh,32,#White)
		StartVectorDrawing(ImageVectorOutput(i))
		VectorFont(FontID(0),100)

		If i=0
			fw=VectorTextWidth(s,#PB_VectorText_Default)
			fh=VectorTextHeight(s,#PB_VectorText_Default)
		EndIf

		cw=VectorTextWidth(s,#PB_VectorText_Visible)
		ch=VectorTextHeight(s,#PB_VectorText_Visible)
		ox=VectorTextWidth(s,#PB_VectorText_Visible|#PB_VectorText_Offset)
		oy=VectorTextHeight(s,#PB_VectorText_Visible|#PB_VectorText_Offset)
		mx=(fw-cw)/2
		my=(fh-ch)/2

		z=Round(mx,#PB_Round_Down) :	If xmin>z : xmin=z : EndIf :
		z=Round(mx+cw,#PB_Round_Up) :	If xmax<z : xmax=z : EndIf :
		z=Round(my,#PB_Round_Down) :	If ymin>z : ymin=z : EndIf :
		z=Round(my+ch,#PB_Round_Up) :	If ymax<z : ymax=z : EndIf :

		MovePathCursor(25+mx-ox,my-oy)
		DrawVectorText(s)

		StopVectorDrawing()
		If i=0
			GrabImage(0,0,0,0,fw+50,fh)
		EndIf
	Next i

	For i=0 To 10
		GrabImage(i,i,25+xmin,ymin,xmax-xmin,ymax-ymin)
		ResizeImage(i,Char\Width,Char\Height,#PB_Image_Smooth)
	Next i

	Char\DotWidth=cw*Char\Width/(xmax-xmin)
	Char\Bytes=Char\Width*Char\Height

	ReDim Matrix(11,Char\Bytes)

	w=Char\Width

	For i=0 To 10
		If i=10
			w=Char\DotWidth
			o=(Char\Width-w)/2
		EndIf
		StartDrawing(ImageOutput(i))
		n=0
		iy=0
		While iy<Char\Height
			ix=0
			While ix<w
				z=($FF-(Point(o+ix,iy)&$FF))*#Shades/255
				Matrix(i,n)=Round(z,#PB_Round_Nearest)
				n+1
				ix+1
			Wend
			iy+1
		Wend
		StopDrawing()
		FreeImage(i)
	Next i

EndProcedure
Procedure DrawChar(index)

	Protected i,j,n,w,c,o,p
	Protected x,y,pt,pc,po

	x=Char\OffsetX[index]
	y=Char\OffsetY
	c=Char\Time[index]
	o=Char\OldTime[index]
	i=Bool(index)
	pt=Char\Transition[i]
	po=Char\Phase[i]
	pc=pt-po


	While j<Char\Height
		ConsoleBufferLocate(x,y+j)
		i=0
		If c>=10
			w=Char\DotWidth
		Else
			w=Char\Width
		EndIf

		While i<w
			If pc And pt And c<>o
				p=(Matrix(c,n)*pc+Matrix(o,n)*po)/pt
				p=Pattern(p)
			Else
				p=Pattern(Matrix(c,n))
			EndIf
			If Char\ColorMode
				Print(Chr(p))
			Else
				ConsoleColor(#Null,p)
				Print(" ")
			EndIf
			n+1
			i+1
		Wend
		j+1
	Wend

EndProcedure
Procedure Main()

	Protected i,j,e
	Protected.s s,t
	Protected ConsoleBufferInfo.CONSOLE_SCREEN_BUFFER_INFO

	With Char

		\FontFace=	"Bahnschrift"
		\FontType=	#PB_Font_HighQuality
		\FontRatio=	5
		\Foreground=	7
		\Background=	0
		\HalfChar=	'o'
		\MidChar=		'@'
		\FullChar=	'@'
		\Transition[0]= 0
		\Transition[1]= 0
		\Delay=95
		\TimeFormat="%hh%ii"


		j=CountProgramParameters()
		While i<j And e=0
			s=ProgramParameter(i)
			i+1
			t=Trim(Mid(s,3+Bool(Mid(s,3,1)=":")),#Q)
			If Left(s,1)="/"
				Select PeekC(@s+SizeOf(Character))|$20
				Case 'f'
					\FontFace=t
				Case 't'
					t=LCase(t)
					If FindString(t,"b") : \FontType|#PB_Font_Bold : EndIf
					If FindString(t,"i") : \FontType|#PB_Font_Italic : EndIf
				Case 'c'
					\Foreground=Limit(Val(t),0,15)
					\Background=Limit(Val(StringField(t,2,",")),0,15)
				Case 'a'
					\Transition[1]=Limit(Val(t),0,10)
					t=StringField(t,2,",")
					If t
						\Transition[0]=Limit(Val(t),0,10)
					Else
						\Transition[0]=\Transition[1]
					EndIf
				Case 'd'
					\Delay=Limit(Val(t),1,10)*10-5
				Case 'h'
					\FontRatio=Limit(Val(t),0,10)
				Case 'z'
					\ClearOnExit=#True
				Case 'm'
					\TimeFormat="%ii%ss"
					If t
						i=Val(t)
						If i<0
							i=-i
							\StopAtZero=#True
						EndIf
						\TimeOffset=Date()+i;*60
						\Countdown=Bool(i)
					EndIf
				Case 's'
					If t=""
						\FullChar='®'
						\MidChar='o'
						\HalfChar='·'
					Else
						\FullChar=Asc(t)
						\HalfChar=Asc(StringField(t,2,","))
						\MidChar=Asc(StringField(t,3,","))
						If \HalfChar=0 : \HalfChar=\FullChar : EndIf
						If \MidChar=0 : \MidChar=\FullChar : EndIf
					EndIf
				Case '?'
					e=#True
				EndSelect
			Else
				e=#True<<#True
			EndIf
		Wend

		If e
			OpenConsole()
			PrintN("")
			PrintN(#ConsoleTitle)
			PrintN("------------------------------")
			If e=1
				PrintN("/f:font         font name")
				PrintN("/h:0-10         font height")
				PrintN("/t:[b][i]       font type")
				PrintN("/c:0-15[,0-15]  color mode")
				PrintN("/a:0-10[,0-10]  animation")
				PrintN("/d:0-10         delay")
				PrintN("/s:char[,...]   text symbols")
				PrintN("/m[:time]       timer mode")
				PrintN("/z              clear screen")
				;Input()
			Else
				PrintN("Illegal parameter '"+s+"'.")
			EndIf
			End
		EndIf

		LoadFont(0,\FontFace,100,\FontType)

		Con\Background=ConsoleBackground(\Background)
		\ColorMode=Bool(\Foreground<>\Background)

		OpenConsole(#ConsoleTitle)
		Con\Handle=GetStdHandle_(#STD_OUTPUT_HANDLE)

		ConsoleClear()

		If \ColorMode
			Pattern(0)=' '
			Pattern(1)=\HalfChar
			Pattern(2)=\MidChar
			Pattern(3)=\FullChar
			Pattern(4)=\FullChar
		Else
			Pattern(0)=\Background
			i=Bool(\Background<8)
			If Bool(\Background%15)
				j=\Background+(i<<4)-8
				Pattern(1)=j
				Pattern(2)=j
			Else
				Pattern(1)=check(i,8,7)
				Pattern(2)=15-Pattern(1)
			EndIf
			Pattern(3)=check(i,15,0)
			Pattern(4)=Pattern(3)
		EndIf

		ConsoleCursor(0)
		ConsoleColor(\Foreground,\Background)
		\Refresh=#True


		; ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~

		CompilerIf #PB_Compiler_Debugger

			Debug "Compile as console app"
			Debug "Try 'clock /m:45 /a:10'..."

			\Transition[0]=8
			\Transition[1]=10
			\TimeOffset=Date()+45;*60
			\TimeFormat="%ii%ss%ss"
			\StopAtZero=#True
			\Countdown=#True

		CompilerEndIf

		; ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~ ~


		Repeat
			\CpuTime=ElapsedMilliseconds()
			GetConsoleScreenBufferInfo_(Con\Handle,@ConsoleBufferInfo)
			i=Max(ConsoleBufferInfo\srWindow\right-ConsoleBufferInfo\srWindow\left+1,10)
			j=Max(ConsoleBufferInfo\srWindow\bottom-ConsoleBufferInfo\srWindow\top+1,4)
			If i<>Con\Width Or j<>Con\Height
				Con\Width=i
				Con\Height=j

				\Space=Round(Con\Width*0.02,#PB_Round_Up)
				\Width=(Con\Width-\Space*6)*0.23
				\Height=Con\Height*(10+\FontRatio)/20

				CreateFont()
				\OffsetX[1]=Char\Space
				\OffsetX[2]=Char\Width+Char\Space*2
				\OffsetX[0]=\OffsetX[2]+Char\Width+Char\Space
				\OffsetX[3]=\OffsetX[0]+Char\DotWidth+Char\Space
				\OffsetX[4]=\OffsetX[3]+Char\Width+Char\Space
				\OffsetY=(Con\Height-\Height)/2

				j=(Con\Width-\OffsetX[4]-\Width-\Space)/2
				If j
					For i=0 To 4
						\OffsetX[i]+j
					Next i
				EndIf

				\Refresh=#True
				ConsoleClear()
			EndIf


			i=Date()
			j=i-\TimeOffset
			If \Countdown
				If j=0
					\TimerDone|#True
				ElseIf j<0
					j=-j
				ElseIf \StopAtZero
					j=0
				EndIf
			EndIf

			s=FormatDate(\TimeFormat,j)+FormatDate("%ss",i)
			If Char\Running
				If Bool(i%10<>\TimeDot)
					\TimeDot=i%10
					Char\OldTime[0]=Char\Time[0]
					Char\Time[0]=i&1+10
					Char\Phase[0]=Char\Transition[0]
					\Refresh=#True
					If \TimerDone=1
						\TimerDone|2
						ConsoleColor(12,\Background)
					EndIf
				EndIf
				i=Asc(Mid(s,4,1))-'0'
				If i<>Char\Time[4] Or Char\Running=1
					Char\Running=2
					For i=1 To 4
						Char\OldTime[i]=Char\Time[i]
						Char\Time[i]=Asc(Mid(s,i,1))-'0'
					Next
					Char\Phase[1]=Char\Transition[1]
				EndIf
				If Char\Phase[0] Or \Refresh
					Char\Phase[0]=Max(Char\Phase[0]-1,0)
					DrawChar(0)
				EndIf
				If Char\Phase[1] Or \Refresh
					Char\Phase[1]=Max(Char\Phase[1]-1,0)
					For i=1 To 4
						DrawChar(i)
					Next i
				EndIf
				\Refresh=#Null
			Else
				Char\Time[0]=(Asc(Mid(s,6,1))&1)+10
				Char\OldTime[0]=Char\Time[0]!1
				For i=1 To 4
					Char\Time[i]=11
				Next
				Char\Running=#True
			EndIf
			Delay(\Delay+Max(\CpuTime-ElapsedMilliseconds(),1-\Delay))

		Until Inkey()<>""

		ConsoleColor(7,0)
		ConsoleCursor(1)

		If \ClearOnExit
			Con\Background=ConsoleBackground(\Background)
			ConsoleClear()
		Else
			PrintN("")
		EndIf

	EndWith

EndProcedure
Main()

Re: Programming a Timer

Posted: Sun Mar 17, 2024 3:27 pm
by mk-soft
Not that we scare off Hessi1971 ;)

Re: Programming a Timer

Posted: Sun Mar 17, 2024 4:25 pm
by Hessi1971
Thanks a lot for all your code and solutions. They are all working well for me.

Great community!!!