Pretty Windows 11 Alarm CLOCK

Share your advanced PureBasic knowledge/code with the community.
Randy Walker
Addict
Addict
Posts: 1059
Joined: Sun Jul 25, 2004 4:21 pm
Location: USoA

Pretty Windows 11 Alarm CLOCK

Post by Randy Walker »

[NOTE] -- Before you proceed, your Windows 11 display setting MUST have scaling set to 125% for the clock face to display correctly. No idea why that is and sorry for the inconvenience.

Thanks to Danilo over in the German forum for providing the ground work. I threw in some color and added some boarder work to dress up the appearance. Also added an alarm feature sure to grab your attention, asleep or awake. Flashing the whole desktop was a trick I learned back in my GFA Basic days. And here it is:

Code: Select all

; Original Reference, written as "Clock_Timer.pb":
; German forum: http://www.purebasic.fr/german/archive/viewtopic.php?t=3528&highlight=
; THAT IS A BAD LINK AND I THINK MOVED TO HERE:
; http://www.purearea.net/pb/CodeArchiv/Time+Date/Clock/Clock_Timer.pb
; Author: Danilo (updated for PB 4.00 by Andre)
; Date: 23. January 2004
; OS: Windows
; Demo: No

; Adaptation by Randy Walker as "3amAlarm.pb" (Jan 5, 2011 for PB ver 4.20)
; New features include "window dressing", "borderless draggable window" and "STUNNING" alarm.
; (not to mention lots and lots of program line comments)
; Resized to make it readable in high rez screens and compatibilized for PB ver 6.xx (Jan 24, 2025)
If InitMouse() = 0
  End
EndIf

Global lippi.LASTINPUTINFO ; Wow! Who understands this stuff??!
lippi\cbSize = SizeOf(LASTINPUTINFO) ; Compensating for "dumb" API I guess?? 

Global mPntr.Point ; DO NOT REUSE ; Used to restore mouse position
Global CursorPosition.Point ; ok to reuse
Global wSpec.Rect, t.q ; ok to reuse
Global alarm.l, alarm$, al_h.l, al_m.l, al_s.l, secs.l, HOUR.l, minute.l, SECOND.l, reminder.l, SkypeLimit.l, activated
Global dsktp.l ,quit.l, Disabled.l, dumbSoundCard.l, wasMinimized.l, stump.l, keepout.l,alarmTime$
Global patch, active.w, targetWindow$, hWin.l, HWNDlogo.w ; ALSO SEE: Globals() procedure

Global hTooltip.l 

#TTS_BUBBLE = $40

Global Dim Tool.TOOLINFO(0) 
Define x, success
Define ini_Signal$ = "C:\accessories\alarm.wav"
Global IsSound = 0
#RingTone = 0
If Asc(ini_Signal$) And FileSize(ini_Signal$) > 0 And InitSound()
	If LoadSound(#RingTone, ini_Signal$)
		IsSound = #True ;SoundLength(#RingTone, #PB_Sound_Millisecond)
	EndIf
EndIf

Procedure Sound(*Value)
	If PeekI(*Value) = 1 And IsSound
		PlaySound(#RingTone)
		Delay(200);IsSound)
	EndIf
EndProcedure

Procedure AddTip(Handle,Text.s) 
Tool(0)\cbSize = SizeOf(TOOLINFO) 
Tool(0)\uFlags = #TTF_SUBCLASS|#TTF_IDISHWND 
Tool(0)\hwnd = Handle 
Tool(0)\uId = Handle 
Tool(0)\hInst = GetModuleHandle_(0) 
Tool(0)\lpszText = @Text 
SendMessage_(hTooltip,#TTM_ADDTOOL,0,@Tool(0)); 
EndProcedure 




Procedure ListWindows(Window, Parameter) ; used inside Gt_Prog()
  ;  WindowClass.s = Space(255) 
  WindowTitle.s = Space(255) 
  ; e.l = GetWindowLong_(Window,#GWL_ID) ; PB Window_# (0,1,2,3, etc)
  ; GetClassName_(Window, WindowClass, 255) 
  GetWindowText_(Window, WindowTitle, 255)
  If FindString(WindowTitle,targetWindow$,1)
    active.w = 1
    hWin.l = Window
  EndIf 
  ProcedureReturn #True  
EndProcedure
Procedure Gt_Prog(dmy$)   ;   input : 11 char PROCESS name  ,  active.w = true if running : hWin.l holds Window handle
  targetWindow$ = dmy$
  active.w = 0
  hWin.l = 0
  EnumWindows_(@ListWindows(), 0) ; Windows CallBack operation.
  ProcedureReturn active.w
EndProcedure

If FileSize("alarm.wav") > 0
  If InitSound() <> 0
    If LoadSound(0,"alarm.wav")
     ; dumbSoundCard.l = #True
    EndIf
  EndIf
EndIf
  ;
dsktp.l = GetDesktopWindow_()
Disabled = GetSysColor_(#COLOR_INACTIVECAPTIONTEXT)

Procedure.f DSin(angle_in_degree.f)
  ; returns Sinus of 'angle in degree (and bannishes any flu symptoms you may have)
  ProcedureReturn Sin(angle_in_degree*0.01745329)
EndProcedure

Procedure.f DCos(angle_in_degree.f)
  ; returns CoSinus of 'angle in degree (and bannishes any flu symptoms your partner may have)
  ProcedureReturn Cos(angle_in_degree*0.01745329)
EndProcedure

Procedure Lin(HDC,X,Y,x1,y1,width,Color)
  ; by einander, english forum
  pen=CreatePen_(#PS_SOLID,width,Color)  ; You can also change the style with #Ps_dash, #Ps_dot, #Ps_dashdotdot, but only when the pen width equals 1.
  hPenOld=SelectObject_(hDC,pen)
  MoveToEx_(hDC,x,y,0):LineTo_(hDC,x1,y1)
  DeleteObject_(SelectObject_(hDC,hPenOld))
EndProcedure

Procedure UpdateDisplay()
  date = reminder & $F
  reminder >> 4
  reminder + (date << 20)
  date$ = FormatDate("%mm/%dd",Date())
  date = Date()
  SECOND = Second(date)
  minute = Minute(date)
  HOUR = Hour(date)
  sec.f = (180-SECOND*6)
  min.f = (180-minute*6)-second/10
  ;std.f = (180-Hour(date)*30)-minute/2
  std.f = (180-Hour*30)-minute/2
  ;Debug std
  HDC = StartDrawing(ImageOutput(1))
  If HDC
    If reminder
      Circle(181,159,111,reminder)
    Else
      Circle(181,159,111,$DEEFEF)
    EndIf
    If secs
      time$ = FormatDate("%hh:%ii:%ss",Date())
    Else
      time$ = FormatDate("   %hh:%ii",Date())
    EndIf
    FrontColor(RGB(Red(ForeGround),Green(ForeGround),Blue(ForeGround)))
    DrawingMode(1)
    DrawingFont(FontID(1))
    DrawImage(ImageID(42),80,315)
    DrawText(120,200,date$)
    DrawText(80,315,time$)
    
    Lin(HDC,180,159,180+DSin(std)*100,159+DCos(std)*90,10,0)
    Lin(HDC,180,159,180+DSin(std)*48,159+DCos(std)*48,9,0) 
    Lin(HDC,180,159,180+DSin(std)*48,159+DCos(std)*48,6,0) 
    Circle(180+DSin(std)*70,159+DCos(std)*70,9,$0) 
    Lin(HDC,180,159,180+DSin(min)*99,159+DCos(min)*99, 6,$FF2222) 
    Circle(180+DSin(min)*81,159+DCos(min)*81,9,$FF2222) 
    If secs
      Circle(180,159,6,$4444FF) 
      LineXY( 180,159,180+DSin(sec)*109,159+DCos(sec)*109, $4444FF) 
    EndIf
    If alarm
      If alarm_flash
        Circle(299,57,12,$00BB00)
        alarm_flash = #False
      Else
        Circle(299,57,12,$FF0000)
        alarm_flash = #True
      EndIf
      ForeGround = GetSysColor_(#COLOR_BTNTEXT)
      FrontColor(RGB(Red(ForeGround),Green(ForeGround),Blue(ForeGround)))
      DrawingMode(1)
      DrawingFont(FontID(1))
      DrawText(285,32,Chr(164))
      ;Box(293,51,12,12,$00FF00) 
      Circle(299,56,5,$00FF00) 
    Else
      Box(282,44,33,33,$A0E4F0) ;LED ERASER
    EndIf
    StopDrawing()
  EndIf
  SetGadgetState(1,ImageID(1))
  s$ = Right("0"+Str(HOUR),2) + ":" + Right("0"+Str(minute),2)
  If secs
    s$ = s$ + ":" + Right("0"+Str(SECOND),2)
  EndIf
  If alarm
    s$ = s$ + " *" + alarm$
  EndIf
  SetWindowTitle(1,s$)
  Delay(800)
EndProcedure

Procedure ReallySetForegroundWindow(MSwnd)
  
  hWnd = MSwnd
  
  ; If the window is in a minimized state, maximize now
  
  If GetWindowLong_(hWnd, #GWL_STYLE) & #WS_MINIMIZE
    ShowWindow_(hWnd, #SW_MAXIMIZE)
    UpdateWindow_(hWnd)
  EndIf
  
  ; Check To see If we are the foreground thread
  
  foregroundThreadID = GetWindowThreadProcessId_(GetForegroundWindow_(), 0)
  ourThreadID = GetCurrentThreadId_()
  ; If not, attach our thread's 'input' to the foreground thread's
  
  If (foregroundThreadID <> ourThreadID)
    AttachThreadInput_(foregroundThreadID, ourThreadID, #True);
  EndIf
  
  ; Bring our window To the foreground
  SetForegroundWindow_(hWnd)
  
  ; If we attached our thread, detach it now
  If (foregroundThreadID <> ourThreadID)
    AttachThreadInput_(foregroundThreadID, ourThreadID, #False)
  EndIf 
  
  ; Force our window To redraw
  InvalidateRect_(hWnd, #Null, #True)
EndProcedure 
Procedure flash(wndw.l) ;/ REQUIRES EVEN NUMBER PASSES to return to normal screen ! !  !   !    !
  ; irritate video sensory organs
  GetClientRect_(wndw, wSpec)
  InvertRect_(GetWindowDC_(wndw), wSpec)
  ; irritate audio sensory organs
  Debug "need sound"
  If 1=0;IsSound = #True
    PlaySound(#RingTone)
  Else
    Beep_(800,100) 
    Beep_(900,100) 
    Beep_(1070,100) 
  EndIf
EndProcedure
Procedure testAlarm()
  If keepout = #False
    If alarm.l ; Then beep and flash 8 times
      Debug Str(HOUR)+" "+Str(al_h)
      Debug Str(minute)+" "+Str(al_m)
      Debug Str(SECOND)+" "+Str(al_s)
      If (al_s.l = SECOND) And (al_h.l=HOUR) And (al_m.l=minute)
        Debug " Hit Alarm set time"
        keepout = #True ; prevent multiple instances of #timer activations
        ShowWindow_(WindowID(1),#SW_MINIMIZE)
        ;BringWindowToTop_(WindowID(1))
        Fcount = 1
        Repeat
          Select WaitWindowEvent()
            Case #PB_Event_Timer
              flash(dsktp) ;/ <<< REQUIRES EVEN NUMBER PASSES to return to normal screen ! !  !   !    !
              UpdateDisplay()
              Fcount + 1
          EndSelect
        Until (Fcount & 1) And (Fcount > 8) ;- << ADJUST NUMBER OF FALSHES AND BEEPS YOU WANT HERE <<
        ShowWindow_(WindowID(1),#SW_RESTORE)
        alarm.l = #False
        reminder = $4B6D8F
        keepout = #False
      EndIf
      Debug ""
    EndIf
  EndIf
EndProcedure

Procedure InitImage()  ; DRAW MAIN WINDOW DRESSINGS
  img = CreateImage(1,372,390)
  If img = 0
    MessageRequester("ERROR","Cant create image!",#MB_ICONERROR):End
  EndIf
  LoadFont(1,"Verdana",27,#PB_Font_Italic | #PB_Font_Bold)
  If StartDrawing(ImageOutput(1))
      Box(3,3,357,366,$80BBDF)
      Box(6,8,348,348,$A0E4F0)
      Box(9,13,339,330,$80BBDF)
      Box(12,18,330,312,$A0E4F0)
      Box(15,23,321,294,$80BBDF)
      Box(18,28,312,276,$A0E4F0)
      Box(21,33,303,258,$80BBDF)
      Box(24,38,294,240,$A0E4F0)
      LineXY( 3,373,357,373, $666666)
      Circle(181,159,111,$999999)
      ForeGround = GetSysColor_(#COLOR_BTNTEXT)
      FrontColor(RGB(Red(ForeGround),Green(ForeGround),Blue(ForeGround)))
      DrawingMode(1)
      DrawingFont(FontID(1))
      DrawText(181-TextWidth("12")/2,-3,"12")
      DrawText(30,135,"9")
      DrawText(305,135,"3")
      DrawText(181-TextWidth("6")/2,275,"6")
      For a = 0 To 360 Step 6
        sin.f = DSin(a)
        cos.f = DCos(a)
        If a % 5
          Circle(180+sin*118,159+cos*118,1,0)
        Else
          Circle(180+sin*118,159+cos*118,2,0)
        EndIf
        If a % 10 = 0
          For b = 2 To 0 Step -1
            Plot(180+sin*(111-b),159+cos*(111-b),0)
          Next b
        EndIf
      Next a
      StopDrawing() 
      patch = GrabImage(1,42,80,315,210,45)
  EndIf
  ProcedureReturn img
EndProcedure

Procedure configure()
  Static secflag.l, alarmh$, alarmm$, alarms$, trump$, t.q
  SM_CXscreen = GetSystemMetrics_(#SM_CXSCREEN)
  SM_CYscreen = GetSystemMetrics_(#SM_CYSCREEN)
  GetCursorPos_(@CursorPosition.Point)
  wx.l = CursorPosition\X-40  ; offset here because used to determine window placement.
  wy.l = CursorPosition\Y-124 ; offset here because used to determine window placement.
  If wx < 2
    wx = 2
  EndIf
  If wx > SM_CXscreen - 115
    wx = SM_CXscreen - 115
  EndIf
  If wy < 30
    wy = 30
  EndIf
  If wy > SM_CYscreen - 130
    wy = SM_CYscreen - 130
  EndIf
  reminder = #False
  trump$ = Right("00"+Str(SkypeLimit),2)
  If OpenWindow(0,wx,wy,100,160,"3amAlarm*",#WS_DLGFRAME)
    ButtonGadget   (100, 20, 109, 30, 24, "OK")
    TextGadget     (101, 26, 5, 55, 17, "     :     :", #PB_Text_Border) ; **MILITARY** TIME ! ! !
    ;
    If Hour(Date()) < 12
      day = Day(Date())
    Else
      day = Day(Date())+1
    EndIf
    year = Year(Date())
    month = Month(Date())
    If t = 0
      t = Date(year,month,day,3,0,0)
    EndIf
    ;t = 1737784800
    DateGadget(102, 30, 7, 90, 25, "", 0, #PB_Date_UpDown)
    SetGadgetState(102, t)
    SetWindowLong_(GadgetID(102), #GWL_STYLE, GetWindowLong_(GadgetID(102), #GWL_STYLE) | #DTS_TIMEFORMAT)
    alarmTime$ = GetGadgetText(102)
    CheckBoxGadget (105,   30, 27, 60, 18, "Alarm")
    CheckBoxGadget (106,   12, 47, 70, 18,"Show secs")
;    StringGadget   (107,   85, 67, 14, 13,trump$, #PB_String_Numeric|#PB_String_BorderLess|#ES_MULTILINE|#ES_AUTOVSCROLL|$10000000)
    ButtonGadget   (108,   54, 109, 36, 24, "Kill")
    SetGadgetColor(102, #PB_Gadget_BackColor, $DDF3FF)
    SendMessage_(GadgetID(102),#EM_SETLIMITTEXT,2,0)
 ;   SendMessage_(GadgetID(107),#EM_SETLIMITTEXT,2,0)
  EndIf
  If Len(alarm$) = 0     ; **MILITARY** TIME ! ! !
    secflag = 1
    alarm = 1
    ; alarmh$="03:00:00"    ; Default is enabled at 3:00 AM -- hence the name "3amAlarm"
    alarmm$="00"    ; Default is enabled at 3:00 AM -- hence the name "3amAlarm"
    alarms$="00"    ; Default is enabled at 3:00 AM -- hence the name "3amAlarm"
  EndIf
  If alarm
    SetGadgetColor(101, #PB_Gadget_BackColor, $DDF3FF)
  Else
    SetGadgetColor(101, #PB_Gadget_BackColor, Disabled)
  EndIf
  SetGadgetState(106, secflag)
  SetGadgetState(105, alarm)
  DisableGadget(102,alarm-1)
  HideWindow(0,0)
  SetActiveGadget(100)
  q.l=#False
  Repeat
    oldgadget = GetActiveGadget()
    Select WaitWindowEvent()
      Case #PB_Event_Gadget
        Gadget  = EventGadget()
        Select Gadget
          Case 105  ; AlarmCheck
            alarm.l=GetGadgetState(105)
            DisableGadget(102,alarm-1)
            If alarm
              SetGadgetColor(101, #PB_Gadget_BackColor, $DDF3FF)
              SetWindowTitle(0,"     3amAlarm*")
            Else
              SetGadgetColor(101, #PB_Gadget_BackColor, Disabled)
              SetWindowTitle(0,"     3amAlarm")
            EndIf
            SetActiveGadget(100)
          Case 106
            SetActiveGadget(100)
          Case 102, 103, 104, 107  ; Hr|Mn|Sc
            If oldgadget <> Gadget ; Catch Caret relocation
              PostMessage_(GadgetID(Gadget),#EM_SETSEL,2,0)
            EndIf
            ; Case 110  ; Stump Skype - closewindow on it
          Case 100  ; OK
            q =100
          Case 108  ; Kill
            quit = #True
            q =108
        EndSelect
      Case #PB_Event_Timer
        UpdateDisplay()
        testAlarm()
      Case #WM_KEYDOWN
        Select EventwParam()
          Case #VK_RETURN ; watch for Tab refocus
            s$=GetGadgetText(oldgadget)
            k.l=FindString(s$,Chr(13)+Chr(10),1) ; Stop CR/LF Ding
            If k.l<>0  ; Was Enter pressed on the StringGadget?
              s$ = ReplaceString(s$,Chr(13)+Chr(10),"")
              SetGadgetText(oldgadget,s$) ; Yes, so remove CR+LF.
              ;PostMessage_(GadgetID(oldgadget), #EM_SETSEL, k.l-1, k.l-1)  ; Set cursor position back.
              WaitWindowEvent()
              Repeat : _mess = WindowEvent() : Until _mess = 0
            EndIf
            k.l=FindString(s$,Chr(13),1) ; Stop CR/LF Ding
            If k.l<>0  ; Was Enter pressed on the StringGadget?
              s$ = ReplaceString(s$,Chr(13),"")
              SetGadgetText(oldgadget,s$) ; Yes, so remove CR+LF.
              ;PostMessage_(GadgetID(oldgadget), #EM_SETSEL, k.l-1, k.l-1)  ; Set cursor position back.
              WaitWindowEvent()
              Repeat : _mess = WindowEvent() : Until _mess = 0
            EndIf
            SetGadgetText(oldgadget,s$) ; Yes, so remove CR+LF.
            Select oldgadget
              Case 102, 103
                PostMessage_(GadgetID(oldgadget),#EM_SETSEL,2,0)
              Case 104, 107
                oldgadget = 99
              Case 100
                q =100
            EndSelect
            SetActiveGadget(oldgadget)
            Debug s$
            Debug Len(s$)
        EndSelect
    EndSelect
  Until q.l
  q = Val(StringField(GetGadgetText(102),1,":"))
  Minute = Val(StringField(GetGadgetText(102),2,":"))
  Second = Val(StringField(GetGadgetText(102),3,":"))
  If Hour(Date()) > q
    day = Day(Date())+1
  Else
    day = Day(Date())
  EndIf
  year = Year(Date())
  month = Month(Date())
  t = Date(year,month,day,q,Minute,Second)
  secflag.l=GetGadgetState(106)
  alarm.l=GetGadgetState(105)     ;    **MILITARY** TIME ! ! !
  alarmh$=StringField(GetGadgetText(102),1,":")
  alarmm$=StringField(GetGadgetText(102),2,":")
  alarms$=StringField(GetGadgetText(102),3,":")
  alarm$=alarmh$ + ":" + alarmm$ + ":" + alarms$
  al_h.l=Val(Left(alarm$,2))      ; This could use error proofing
  If FindString(GetGadgetText(102),"PM")
    al_h.l + 12
  EndIf
  al_m.l=Val(Mid(alarm$,4,2))     ; if user is too lazy or dumb to
  al_s.l=Val(Mid(alarm$,7,2))     ; edit using proper military time. 
  secs.l=GetGadgetState(106)
  If stump.l
    SkypeLimit = 0
  EndIf
  CloseWindow(0)
EndProcedure

Procedure WinCallback(hWnd, uMsg, wParam, lParam) 
  ; Windows fills the parameter automatically, which we will use in the callback...
  If hWnd = WindowID(1)
    If keepout = #False
      Select uMsg 
        Case #WM_SIZE     ; Only effective if window 1 style excludes BorderLess|DLGFRAME.
          Select wParam
            Case #SIZE_MINIMIZED    ;/ Track Landing Pad window status in case user minimizes manually.
              wasMinimized = #True  ;/ CRITICAL PART OF HONORING WINDOW QUEUE MANAGEMENT!!!!
            Case #SIZE_RESTORED
              wasMinimized = #False ;/ CRITICAL PART OF HONORING WINDOW QUEUE MANAGEMENT!!!!
          EndSelect
      EndSelect
    EndIf
  EndIf
  ProcedureReturn #PB_ProcessPureBasicEvents 
EndProcedure 

SkypeLimit = 0
  
configure()

;/  PLEASE READ -- PLEASE READ -- PLEASE READ -- PLEASE READ -- PLEASE READ
; **Consider, this is an "ALARM CLOCK" so you do not want to kill it accidentally.
;   That is why it is not as "convenient" as possible to exit the program.
;/  /\  /\  /\  /\  /\  /\  /\  /\  /\  /\  /\  /\  /\  /\  /\  /\  /\  /\  /\
;/               TRY BOTH OPENWINDOW STYLES FOR YOUR PREFERENCE
;/  \/  \/  \/  \/  \/  \/  \/  \/  \/  \/  \/  \/  \/  \/  \/  \/  \/  \/  \/
;If OpenWindow(1,3,3,123,129,"3amAlarm") ; << enables title bar menu options <<
If OpenWindow(1,3,3,368,380,"3amAlarm",#PB_Window_BorderLess|#WS_DLGFRAME)
  ImageGadget(1,1,1,372,390,InitImage())
  ;
  ;
  hTooltip = CreateWindowEx_(#WS_EX_TOPMOST, "tooltips_class32", 0,#TTS_ALWAYSTIP|#TTS_NOPREFIX|#WS_POPUP|#TTS_BUBBLE,#CW_USEDEFAULT,#CW_USEDEFAULT, #CW_USEDEFAULT,#CW_USEDEFAULT,WindowID(1), 0, GetModuleHandle_(0), 0); 
  SetWindowTheme_(hTooltip, @null.w, @null.w)
  
  SendMessage_(hTooltip,#TTM_SETDELAYTIME,#TTDT_INITIAL,0) 
  
  SendMessage_(hTooltip,#TTM_SETTIPTEXTCOLOR,$0002FF,0)                  ;TextColor Tooltip 
  SendMessage_(hTooltip,#TTM_SETTIPBKCOLOR,$D1FFFF,0)                    ;BackColor Tooltip 
  
  SendMessage_(hTooltip,#TTM_SETMAXTIPWIDTH,0,160)                       ;Max tip width
  
  r.RECT 
  SetRect_(r,10,10,10,10)                                                ;Tip Margins
  SendMessage_(hTooltip,#TTM_SETMARGIN,0,r)
  
  SendMessage_(hTooltip,#TTM_ACTIVATE,#False,0)                          ;DeActivate
  
  SendMessage_(hTooltip,#TTM_ACTIVATE,#True,0)                           ;Activate
  
  LoadFont(0, "Tahoma",11,#PB_Font_HighQuality) 
  SendMessage_(hTooltip,#WM_SETFONT,FontID(0),0) 
  
  AddTip(WindowID(1),"Right Click for Options.  Double Click to Minimize. Click hold and drag.     One click stop alarm") 
  ;
  ; Next 3 lines are only useful if window style excludes #PB_Window_BorderLess|#WS_DLGFRAME.
  menubar.l=GetSystemMenu_(WindowID(1),0)
  ModifyMenu_(menubar.l,#SC_CLOSE,#MF_BYCOMMAND,#SC_CLOSE,"Hide 3amAlarm") ; ,"Close 3amAlarm")
  AppendMenu_(menubar.l,#MF_STRING,100,"Settings...")                      ; used in main loop under WM_SYSCOMMAND
  SetWindowCallback(@WinCallback())                                        ; activate the callback to monitor system tray icon
  
  ; no idea how or why the following 3 lines allow window mouse dragging (but I like it :-)
  SetWindowLong_(GadgetID(1), #GWL_STYLE, GetWindowLong_(GadgetID(1), #GWL_STYLE) &~#SS_NOTIFY)
  #BS_FLAT=$8000
  SetWindowLong_(GadgetID(1),#GWL_STYLE,GetWindowLong_(GadgetID(1),#GWL_STYLE) |#BS_FLAT)
  
  UpdateDisplay()                 ; Get first update before entering main loop.
  ;SetTimer_(WindowID(1),0,1000,0) ; Timer message triggers at 1000 milliseconds (1 second interval)
  AddWindowTimer(1,0,1000)  ; Timer message triggers at 1000 milliseconds (1 second interval)
  
  Repeat
    evnt = WaitWindowEvent(3000)
    Select evnt
      Case #WM_RBUTTONDOWN        ; Opens the options window for settings or exit.
        configure()
        If quit
          Break
        EndIf
      Case #WM_LBUTTONDBLCLK      ; Gets the "stupid thing" out of the way!! !  !   !    !
        wasMinimized = #True      ;/ CRITICAL PART OF HONORING WINDOW QUEUE MANAGEMENT!!!!
        ShowWindow_(WindowID(1),#SW_MINIMIZE)
      Case #WM_LBUTTONDOWN        ; Hold and drag to move the clock on desktop.
        reminder = #False
        SendMessage_(WindowID(1),#WM_NCLBUTTONDOWN, #HTCAPTION,0)
      Case #PB_Event_Timer              ; "tick" second hand and test trigger-setting for alarm.
        UpdateDisplay()
        testAlarm()
        If stump.l
          If Gt_Prog("Skype")   ; Prevent Skype windows from dominating taskbar.
            SendMessage_(hWin.l, #WM_CLOSE,0,0)
            While WindowEvent() <> 0 : Wend
            If Gt_Prog("Skype")   ; Prevent Skype windows from dominating taskbar.
              SendMessage_(hWin.l, #WM_CLOSE,0,0)
              While WindowEvent() <> 0 : Wend
            EndIf 
          EndIf 
        EndIf
      Case #WM_SYSCOMMAND         ; Only effective if window style excludes BorderLess|DLGFRAME.
        _wParam=EventwParam()
        If _wParam=100            ; 100 is reference to value inside the AppendMenu_ line seen above.
          configure()
        EndIf
      Default
        If activated
          GetLastInputInfo_(@lippi)
          timeNow.l = ElapsedMilliseconds() ; get current timestamp value for comparison calculations
          If timeNow.l - lippi\dwTime < 4
            SetForegroundWindow_(activated)
            activated = 0
            SetCursorPos_(mPntr\x,mPntr\y)
            ShowCursor_(1)
            If wasMinimized
              SetWindowState(1,#PB_Window_Minimize)
            EndIf
          EndIf
        EndIf
    EndSelect
  Until evnt = #PB_Event_CloseWindow
  If quit
    RemoveWindowTimer(1,0)
    CloseWindow(1)
  EndIf
EndIf
End
THANK YOU Danilo <<<<<<<<<<<<<<

[EDIT] Applied a few minor tweaks to code above to improve:
* left and bottom shadow borders were way too big
* Alarm LED
* Minute hand too long was leaving remnants around inner region of the seconds marks
* Extended the second hand to more practical length for readability
Last edited by Randy Walker on Wed Jun 04, 2025 9:18 pm, edited 2 times in total.
- - - - - - - - - - - - - - - -
Randy
I *never* claimed to be a programmer.
Axolotl
Addict
Addict
Posts: 832
Joined: Wed Dec 31, 2008 3:36 pm

Re: Pretty Windows Alarm CLOCK

Post by Axolotl »

HI Randy Walker,
thanks for sharing.
I have briefly read the code.
Some variable names indicate that they would be better declared as integer e.g. hwnd.l

My tip: Always use integer variables, only within API functions or API structures the LONG is necessary (or if DWORD is specified).
Just because it worked doesn't mean it works.
PureBasic 6.04 (x86) and <latest stable version and current alpha/beta> (x64) on Windows 11 Home. Now started with Linux (VM: Ubuntu 22.04).
User avatar
SPH
Enthusiast
Enthusiast
Posts: 566
Joined: Tue Jan 04, 2011 6:21 pm

Re: Pretty Windows Alarm CLOCK

Post by SPH »

DPI Destructor ! :(

!i!i!i!i!i!i!i!i!i!
!i!i!i!i!i!i!
!i!i!i!
//// Informations ////
Portable LENOVO ideapad 110-17ACL 64 bits
Version de PB : 6.12LTS - 64 bits
User avatar
skywalk
Addict
Addict
Posts: 4215
Joined: Wed Dec 23, 2009 10:14 pm
Location: Boston, MA

Re: Pretty Windows Alarm CLOCK

Post by skywalk »

Does not display correctly on Windows 11. Window clips analog clock contents.
Is NOT pretty, since no anti-aliased drawing.
Use the vector lib on canvasgadget to draw with smooth lines and edges.
The nice thing about standards is there are so many to choose from. ~ Andrew Tanenbaum
Randy Walker
Addict
Addict
Posts: 1059
Joined: Sun Jul 25, 2004 4:21 pm
Location: USoA

Re: Pretty Windows Alarm CLOCK

Post by Randy Walker »

skywalk wrote: Sun Jan 26, 2025 7:12 pm Does not display correctly on Windows 11. Window clips analog clock contents.
Uhhh, But this was revised by me and run on my 64 bit Windows 11 Pro so i don't understand how it can be that it fails on your machine. Looks and runs great here. Uhh, Also compiled using PB ver 6.12 so maybe there is a difference there.
Is NOT pretty, since no anti-aliased drawing.
Use the vector lib on canvasgadget to draw with smooth lines and edges.
Sorry. Talking over my head. Have example(s)?
- - - - - - - - - - - - - - - -
Randy
I *never* claimed to be a programmer.
Randy Walker
Addict
Addict
Posts: 1059
Joined: Sun Jul 25, 2004 4:21 pm
Location: USoA

Re: Pretty Windows Alarm CLOCK

Post by Randy Walker »

skywalk wrote: Sun Jan 26, 2025 7:12 pm Does not display correctly on Windows 11. Window clips analog clock contents.
Is NOT pretty, since no anti-aliased drawing.
Use the vector lib on canvasgadget to draw with smooth lines and edges.
I think I might know what happened. I tested on my Win 7 laptop with 1024x800 display and it clipped the clock terribly, but looks fine on my laptop with 1092x1024 display, so I figure Windows itself is clipping the window to allow the app to run and seems to be running ok, just clipped down and ugly as hell because of it. But, no. That doesn't seem to be the case either. I tested on my 1600x1200 Win 7 desktop and it also clipped, running same PB ver 5.40 as tested on my 1092x1024 laptop that also clipped. So I tested again on my 1092x1024 laptop using PB ver 5.62 and it also clipped so evidently the clock can only compile and run properly on PB ver 6.xx and posssibly only on ver 6.12. Wouldn't that be a shame -- so much incompatibility.
- - - - - - - - - - - - - - - -
Randy
I *never* claimed to be a programmer.
User avatar
skywalk
Addict
Addict
Posts: 4215
Joined: Wed Dec 23, 2009 10:14 pm
Location: Boston, MA

Re: Pretty Windows Alarm CLOCK

Post by skywalk »

Randy Walker wrote: Sun Jan 26, 2025 9:23 pm Sorry. Talking over my head. Have example(s)?
Yes, search the forum for anti-aliased drawing.
The nice thing about standards is there are so many to choose from. ~ Andrew Tanenbaum
Randy Walker
Addict
Addict
Posts: 1059
Joined: Sun Jul 25, 2004 4:21 pm
Location: USoA

Re: Pretty Windows Alarm CLOCK

Post by Randy Walker »

Axolotl wrote: Sun Jan 26, 2025 5:26 pm HI Randy Walker,
thanks for sharing.
I have briefly read the code.
Some variable names indicate that they would be better declared as integer e.g. hwnd.l
I guess what I need is "sed" for windows so I can replace all occurrences of .l with .i in all my PB code files. That way everyone would not have to keep reminding me.
- - - - - - - - - - - - - - - -
Randy
I *never* claimed to be a programmer.
Randy Walker
Addict
Addict
Posts: 1059
Joined: Sun Jul 25, 2004 4:21 pm
Location: USoA

Re: Pretty Windows Alarm CLOCK

Post by Randy Walker »

Did anybody notice, I did did not create the original code as mentioned in the top of the file:
;Original Reference, written as "Clock_Timer.pb":
; German forum: http://www.purebasic.fr/german/archive/ ... highlight=
; THAT IS A BAD LINK AND I THINK MOVED TO HERE:
; http://www.purearea.net/pb/CodeArchiv/T ... k_Timer.pb
; Author: Danilo
So maybe you need to straighten Danilo out about that anti-aliasing stuff whatever that is.
I don't know why it works perfectly here and not for you. Maybe an issue with your graphics cards.
- - - - - - - - - - - - - - - -
Randy
I *never* claimed to be a programmer.
AZJIO
Addict
Addict
Posts: 2183
Joined: Sun May 14, 2017 1:48 am

Re: Pretty Windows Alarm CLOCK

Post by AZJIO »

Try to set scaling 100%, 125%, 150%. I think the problem is this.

Code: Select all

If OpenWindow(1,3,3,243,251,"3amAlarm",#PB_Window_BorderLess|#WS_DLGFRAME)
  ImageGadget(1,1,1,372,390,InitImage())
Why is the window width 243, and the Imagegadget width is 372?
If you use the canvas, you will receive a program that also works on Linux.
The VectorDrawing library has smoothed lines.
Randy Walker
Addict
Addict
Posts: 1059
Joined: Sun Jul 25, 2004 4:21 pm
Location: USoA

Re: Pretty Windows Alarm CLOCK

Post by Randy Walker »

AZJIO wrote: Mon Jan 27, 2025 11:12 pm Try to set scaling 100%, 125%, 150%. I think the problem is this.
Oh wow! good catch. I do have my scaling set to 150%.
Is that the key to the clipping issue? Hmmm. That would suck.
- - - - - - - - - - - - - - - -
Randy
I *never* claimed to be a programmer.
Randy Walker
Addict
Addict
Posts: 1059
Joined: Sun Jul 25, 2004 4:21 pm
Location: USoA

Re: Pretty Windows Alarm CLOCK

Post by Randy Walker »

Sure enough. I changed my scaling to 100% and got clipped clock face same as my little win 7 laptop. So this does suck. Will make note of that in my OP.
- - - - - - - - - - - - - - - -
Randy
I *never* claimed to be a programmer.
Marc56us
Addict
Addict
Posts: 1600
Joined: Sat Feb 08, 2014 3:26 pm

Re: Pretty Windows 11 Alarm CLOCK

Post by Marc56us »

Use the vector lib on canvasgadget to draw with smooth lines and edges.
[..]
Sorry. Talking over my head. Have example(s)?
Simple 3D look clock - only vectors by l1marik
Post by l1marik » Fri Jul 24, 2020 12:53 pm

Code: Select all

; Simple 3D clock by Lukas. Enjoy it :-)

Procedure Draw_3D_Clock(gid)
  
  If StartVectorDrawing(CanvasVectorOutput(gid))
    ScaleCoordinates(DesktopResolutionX(), DesktopResolutionY())
    
    AddPathBox(0, 0, GadgetWidth(gid), GadgetWidth(gid))
    VectorSourceColor(RGBA(255, 255, 255, 255))
    VectorFont(FontID(0), GadgetHeight(gid)/40)
    FillPath()
    
    BeginVectorLayer(255)
    
    ; Dial
    VectorSourceCircularGradient(GadgetWidth(gid)/2, GadgetHeight(gid)/2, GadgetWidth(gid)/2, -GadgetWidth(gid)/2, -GadgetHeight(gid)/2.5)
    VectorSourceGradientColor(RGBA(255, 255, 255, 255), 0.2)
    VectorSourceGradientColor(RGBA(223, 159, 159, 150), 1.0)
    AddPathCircle(GadgetWidth(gid)/2,GadgetHeight(gid)/2,GadgetWidth(gid)/3)
    FillPath()
    ClosePath()
    Text$= "PureBasic Clock"
    VectorSourceColor(RGBA(153, 51, 51, 150))
    MovePathCursor(GadgetHeight(gid)/2 - VectorTextWidth(Text$)/2, GadgetHeight(gid)/2.5) 
    DrawVectorText(Text$)

    ; Shadow
    VectorSourceCircularGradient(GadgetWidth(gid)/2 + GadgetHeight(gid)/100 ,GadgetHeight(gid)/2 + GadgetHeight(gid)/100,GadgetWidth(gid)/2.5)
    VectorSourceGradientColor(RGBA(200, 200, 200, 0), 0.7)
    VectorSourceGradientColor(RGBA(100, 100, 100, 100), 0.8)
    VectorSourceGradientColor(RGBA(200, 200, 200, 0), 0.9)
    AddPathCircle(GadgetWidth(gid)/2 + GadgetHeight(gid)/100,GadgetHeight(gid)/2 + GadgetHeight(gid)/100,GadgetWidth(gid)/3.1)
    StrokePath(GadgetWidth(gid)/15,#PB_Path_RoundEnd )
    
    ; Frame
    VectorSourceCircularGradient(GadgetWidth(gid)/2,GadgetHeight(gid)/2,GadgetWidth(gid)/2.5)
    VectorSourceGradientColor(RGBA(0, 0, 0, 250), 0.65)
    VectorSourceGradientColor(RGBA(153, 51, 51, 255), 0.8)
    VectorSourceGradientColor(RGBA(0, 0, 0, 250), 0.95)
    AddPathCircle(GadgetWidth(gid)/2,GadgetHeight(gid)/2,GadgetWidth(gid)/3.1)
    StrokePath(GadgetWidth(gid)/15,#PB_Path_RoundEnd )
    
    EndVectorLayer()
    
    BeginVectorLayer(180)
    
    VectorSourceColor(RGBA(0,0,0,255))
    loop_step.f = 0 
    While loop_step < 360
      ResetCoordinates()
      MovePathCursor(GadgetWidth(gid)/2, GadgetWidth(gid)/2) 
      RotateCoordinates(GadgetWidth(gid)/2, GadgetHeight(gid)/2, loop_step)
      If Mod(loop_step, 15) = 0
        MovePathCursor(GadgetWidth(gid)/2, GadgetWidth(gid)/3.8) 
        AddPathLine(GadgetWidth(gid)/2, GadgetWidth(gid)/4.5)
        StrokePath(GadgetWidth(gid)/100,#PB_Path_RoundEnd)
        ;add numbers ???
      Else
        MovePathCursor(GadgetWidth(gid)/2, GadgetWidth(gid)/4) 
        AddPathLine(GadgetWidth(gid)/2, GadgetWidth(gid)/4.5)
        StrokePath(GadgetWidth(gid)/400,#PB_Path_RoundEnd)
      EndIf
      loop_step = loop_step + 6
    Wend
    
    EndVectorLayer()
    
    BeginVectorLayer(250)
    
    hours_angle.f = 0
    minutes_angle.f = 0
    hours.f = Hour(Date())
    minutes.f = Minute(Date())
    seconds.f = Second(Date())
    
    If (hours >= 12) 
      hours = hours - 12 
    EndIf
    
    seconds_angle = seconds * 6  
    minutes_angle = minutes * 6  + (seconds_angle/60)
    hours_angle = hours * 30 + (minutes/60) * 30

    ; Minutes
    VectorSourceColor(RGBA(0,0,0,255))
    ResetCoordinates()
    RotateCoordinates(GadgetWidth(gid)/2, GadgetHeight(gid)/2,minutes_angle)
    MovePathCursor(GadgetWidth(gid)/2 + GadgetWidth(gid)/80, GadgetHeight(gid)/2)
    AddPathLine(GadgetWidth(gid)/2 + GadgetWidth(gid)/200, GadgetHeight(gid)/4.1) 
    AddPathLine(GadgetWidth(gid)/2 - GadgetWidth(gid)/200, GadgetHeight(gid)/4.1) 
    AddPathLine(GadgetWidth(gid)/2 - GadgetWidth(gid)/80, GadgetHeight(gid)/2) 
    FillPath(#PB_Path_Winding)
    AddPathCircle(GadgetWidth(gid)/2, GadgetHeight(gid)/2, GadgetWidth(gid)/80)
    FillPath(#PB_Path_Winding)
    
    ; Hours
    VectorSourceColor(RGBA(0,0,0,255))
    ResetCoordinates()
    RotateCoordinates(GadgetWidth(gid)/2, GadgetHeight(gid)/2,hours_angle)
    MovePathCursor(GadgetWidth(gid)/2 + GadgetWidth(gid)/80, GadgetHeight(gid)/2)
    AddPathLine(GadgetWidth(gid)/2 + GadgetWidth(gid)/200, GadgetHeight(gid)/3.5) 
    AddPathLine(GadgetWidth(gid)/2 - GadgetWidth(gid)/200, GadgetHeight(gid)/3.5) 
    AddPathLine(GadgetWidth(gid)/2 - GadgetWidth(gid)/80, GadgetHeight(gid)/2) 
    FillPath(#PB_Path_Winding)
    AddPathCircle(GadgetWidth(gid)/2, GadgetHeight(gid)/2, GadgetWidth(gid)/80)
    FillPath(#PB_Path_Winding)
    
    ; Seconds
    VectorSourceColor(RGBA(150,0,0,255))
    ResetCoordinates()
    RotateCoordinates(GadgetWidth(gid)/2, GadgetHeight(gid)/2,seconds_angle)
    MovePathCursor(GadgetWidth(gid)/2 + GadgetWidth(gid)/100, GadgetHeight(gid)/2)
    AddPathLine(GadgetWidth(gid)/2, GadgetHeight(gid)/4.3) 
    AddPathLine(GadgetWidth(gid)/2 - GadgetWidth(gid)/100, GadgetHeight(gid)/2) 
    FillPath()
    AddPathCircle(GadgetWidth(gid)/2, GadgetHeight(gid)/2, GadgetWidth(gid)/100)
    FillPath()
    
    EndVectorLayer()
    
    StopVectorDrawing()
  EndIf
  
EndProcedure



If OpenWindow(0, 0, 0, 300, 300, "Simple 3D Clock", #PB_Window_SystemMenu | #PB_Window_ScreenCentered| #PB_Window_MaximizeGadget | #PB_Window_SizeGadget)
  
  AddWindowTimer(0, 123,1000)
  CanvasGadget(10, 0, 0, 300, 300)
  LoadFont(0, "Verdana", 10,  #PB_Font_HighQuality)
  Draw_3D_Clock(10)
  
  Repeat 
    
    If Event = #PB_Event_Timer And EventTimer() = 123
      Draw_3D_Clock(10)
    EndIf
    
    If event = #PB_Event_SizeWindow
      ResizeGadget(10, 0, 0, WindowHeight(0), WindowHeight(0))
      Draw_3D_Clock(10)
    EndIf
    
    Event = WaitWindowEvent()
    
  Until Event = #PB_Event_CloseWindow
EndIf
Only PB functions, no API, 147 lines.

And there are plenty of others (including how to make a transparent background), just search the forum.
Take the most recent and shortest codes, not one from 2004 then 2011.
PB evolves fast. Functionalities that used to require direct API calls are now done directly: more stable, more durable, easier to maintain, anti-aliasing, often compatible with several OS.

:wink:
User avatar
SPH
Enthusiast
Enthusiast
Posts: 566
Joined: Tue Jan 04, 2011 6:21 pm

Re: Pretty Windows 11 Alarm CLOCK

Post by SPH »

@Randy

1/4 of your clock display is cut off (window too small) :idea:

!i!i!i!i!i!i!i!i!i!
!i!i!i!i!i!i!
!i!i!i!
//// Informations ////
Portable LENOVO ideapad 110-17ACL 64 bits
Version de PB : 6.12LTS - 64 bits
Randy Walker
Addict
Addict
Posts: 1059
Joined: Sun Jul 25, 2004 4:21 pm
Location: USoA

Re: Pretty Windows 11 Alarm CLOCK

Post by Randy Walker »

Marc56us wrote: Tue Jan 28, 2025 3:07 pm
Use the vector lib on canvasgadget to draw with smooth lines and edges.
[..]
Sorry. Talking over my head. Have example(s)?
Simple 3D look clock - only vectors by l1marik
Post by l1marik » Fri Jul 24, 2020 12:53 pm

Simple 3D clock by Lukas. Enjoy it :-)

Only PB functions, no API, 147 lines.

And there are plenty of others (including how to make a transparent background), just search the forum.
Take the most recent and shortest codes, not one from 2004 then 2011.
PB evolves fast. Functionalities that used to require direct API calls are now done directly: more stable, more durable, easier to maintain, anti-aliasing, often compatible with several OS.

:wink:
Thanks. I saved the code as "3DclockDraft" Because it is so bad it's actually worse than mine when not using 150% scaling. maybe one day I will try to figure out how to align the pieces to look like a clock.
- - - - - - - - - - - - - - - -
Randy
I *never* claimed to be a programmer.
Post Reply