#Region ;**** Directives created by AutoIt3Wrapper_GUI **** #AutoIt3Wrapper_Icon=eye-solid-full.ico #AutoIt3Wrapper_Res_Description=EyeBreak enforces a 20s break every 20m #AutoIt3Wrapper_Res_Fileversion=2 #AutoIt3Wrapper_Res_ProductVersion=2.0 #AutoIt3Wrapper_Res_CompanyName=Daniel Sherratt #AutoIt3Wrapper_Res_Language=5129 #EndRegion ;**** Directives created by AutoIt3Wrapper_GUI **** ; ===================================================================== ; 20-20-20 Eye Break (AutoIt v3) - v3 ; --------------------------------------------------------------- ; Runs silently in the tray. Every 20 minutes of ACTIVE screen time: ; * at 19:55 a black screen + eye begins fading in (5 s fade) ; * at 20:00 a 20-second break screen shows the eye (blinking ; every 10 s) with a seconds countdown below it ; * playing media is paused for the break and resumed after ; ; v3 additions: ; * settings in Documents\EyeBreak.ini - editable any time, no ; recompile needed (created with defaults on first run) ; * copies itself into the Startup folder so it runs at login ; (AutoStart=0 removes it again) ; * idle detection - time away from keyboard/mouse doesn't count, ; so you won't get a break right after returning to your desk ; * sleep/resume detection - waking the PC restarts the timer ; * meeting-aware - if the mic is in use (e.g. on a call), the ; break is quietly deferred and retried later ; * snooze - press S during the fade to postpone 5 minutes ; * live tray tooltip showing time until the next break ; * overlay can span all monitors (AllMonitors=1) ; * countdown number can be hidden (ShowCountdown=0) ; ; Esc = skip the break S (during fade) = snooze ; ; Compile to EXE: right-click this file > "Compile Script (x64)" ; ===================================================================== #pragma compile(FileDescription, "20-20-20 Eye Break") #pragma compile(ProductName, "EyeBreak2020") #include #include #include #include Opt("TrayMenuMode", 3) _Singleton("EyeBreak2020_Mutex") ; ---------------- settings (loaded from EyeBreak.ini in Documents) ---------------- Global Const $INI = @MyDocumentsDir & "\EyeBreak.ini" ; migrate settings from the old location (next to the exe), if present If Not FileExists($INI) And FileExists(@ScriptDir & "\EyeBreak.ini") Then FileCopy(@ScriptDir & "\EyeBreak.ini", $INI) _WriteDefaultIni() ; settings are loaded (and re-loadable from the tray) by _LoadSettings() Global $WORK_SECS, $FADE_SECS, $BREAK_SECS, $BLINK_EVERY, $SNOOZE_SECS, $DEFER_SECS, $IDLE_SECS Global $PAUSE_MEDIA, $SKIP_MIC, $BREAK_IN_FS, $ALL_MONITORS, $SHOW_COUNT, $AUTO_START Global $MSG_BREAK, $MSG_FADE Global $GX, $GY, $GW, $GH, $ECX, $ECY Global $COL_IRIS = 0xFF3E7FB8 Global $PI = 3.14159265358979 _LoadSettings() _InstallStartup() Global $hDLL = DllOpen("user32.dll") Global $idNow = TrayCreateItem("Take a break now") TrayCreateItem("") Global $idOpenIni = TrayCreateItem("Open settings") Global $idReload = TrayCreateItem("Reload settings") TrayCreateItem("") Global $idExit = TrayCreateItem("Exit") TraySetState(1) _GDIPlus_Startup() OnAutoItExitRegister("_Cleanup") ; ---------------- main loop (deadline model) ---------------- Global $tApp = TimerInit() Global $fNext = TimerDiff($tApp) + ($WORK_SECS - $FADE_SECS) * 1000 ; fade-start deadline Global $fLastLoop = TimerDiff($tApp) Global $iLastTip = -1 While 1 Local $fNow = TimerDiff($tApp) Switch TrayGetMsg() Case $idNow _RunBreak() $fNext = TimerDiff($tApp) + ($WORK_SECS - $FADE_SECS) * 1000 Case $idOpenIni ShellExecute($INI) ; whatever app .ini is associated with If @error Then ShellExecute("notepad.exe", '"' & $INI & '"') Case $idReload _LoadSettings() _InstallStartup() ; applies AutoStart on/off immediately $fNext = TimerDiff($tApp) + ($WORK_SECS - $FADE_SECS) * 1000 ; restart cycle with new timings $iLastTip = -1 Case $idExit Exit EndSwitch ; sleep/resume: a big gap between loop iterations means the PC was ; suspended - restart the work timer If $fNow - $fLastLoop > 10 * 1000 Then $fNext = $fNow + ($WORK_SECS - $FADE_SECS) * 1000 $fLastLoop = $fNow ; away detection: a locked workstation ALWAYS counts as away, even if ; audio keeps playing or a fullscreen window is still up behind it. ; Otherwise "watching" = audio playing, or (with BreakDuringFullScreen=1) ; a fullscreen app in the foreground. IdleResetMinutes=0 disables all of this. If $IDLE_SECS > 0 Then If _IsLocked() Then $fNext = $fNow + ($WORK_SECS - $FADE_SECS) * 1000 ElseIf _IdleSecs() >= $IDLE_SECS Then Local $bWatching = _AudioActiveCached() If Not $bWatching And $BREAK_IN_FS Then $bWatching = _FullscreenCached() If Not $bWatching Then $fNext = $fNow + ($WORK_SECS - $FADE_SECS) * 1000 EndIf EndIf ; deadline reached? If $fNow >= $fNext Then If _BreakBlocked() Then $fNext = TimerDiff($tApp) + $DEFER_SECS * 1000 ; retry later Else Local $iResult = _RunBreak() If $iResult = 1 Then $fNext = TimerDiff($tApp) + $SNOOZE_SECS * 1000 Else $fNext = TimerDiff($tApp) + ($WORK_SECS - $FADE_SECS) * 1000 EndIf EndIf EndIf ; live tooltip - "Next break in MM:SS" Local $iLeft = Int(($fNext - TimerDiff($tApp)) / 1000) If $iLeft < 0 Then $iLeft = 0 If $iLeft <> $iLastTip Then TraySetToolTip("Eye Break - next in " & StringFormat("%02u:%02u", Int($iLeft / 60), Mod($iLeft, 60))) $iLastTip = $iLeft EndIf Sleep(200) WEnd ; ---------------- break sequence ---------------- ; returns 0 = break shown (or Esc-skipped), 1 = snoozed Func _RunBreak() Local $bPausedMedia = False If $PAUSE_MEDIA And _IsAudioPlaying() Then Send("{MEDIA_PLAY_PAUSE}") $bPausedMedia = True EndIf Local $hGUI = GUICreate("", $GW, $GH, $GX, $GY, $WS_POPUP, BitOR($WS_EX_TOPMOST, $WS_EX_TOOLWINDOW)) GUISetBkColor(0x000000, $hGUI) GUISetCursor(16, 1, $hGUI) WinSetTrans($hGUI, "", 0) GUISetState(@SW_SHOW, $hGUI) Local $hGfxWin = _GDIPlus_GraphicsCreateFromHWND($hGUI) Local $hBmp = _GDIPlus_BitmapCreateFromGraphics($GW, $GH, $hGfxWin) Local $hGfx = _GDIPlus_ImageGetGraphicsContext($hBmp) _GDIPlus_GraphicsSetSmoothingMode($hGfx, 2) _GDIPlus_GraphicsSetTextRenderingHint($hGfx, 4) Local $iOutcome = 0, $bSkip = False ; --- phase 1: fade in --- Local $t = TimerInit() While TimerDiff($t) < $FADE_SECS * 1000 WinSetTrans($hGUI, "", Int(255 * TimerDiff($t) / ($FADE_SECS * 1000))) _DrawFrame($hGfx, $hGfxWin, $hBmp, TimerDiff($t) / 1000, -1) If _IsPressed("1B", $hDLL) Then $bSkip = True ExitLoop EndIf If _IsPressed("53", $hDLL) Then ; S = snooze $bSkip = True $iOutcome = 1 ExitLoop EndIf Sleep(15) WEnd ; --- phase 2: break --- If Not $bSkip Then WinSetTrans($hGUI, "", 255) $t = TimerInit() While TimerDiff($t) < $BREAK_SECS * 1000 Local $fLeft = $BREAK_SECS - TimerDiff($t) / 1000 _DrawFrame($hGfx, $hGfxWin, $hBmp, $FADE_SECS + TimerDiff($t) / 1000, $fLeft) If _IsPressed("1B", $hDLL) Then ExitLoop Sleep(15) WEnd EndIf ; --- phase 3: fade out --- $t = TimerInit() While TimerDiff($t) < 600 WinSetTrans($hGUI, "", Int(255 * (1 - TimerDiff($t) / 600))) Sleep(15) WEnd _GDIPlus_GraphicsDispose($hGfx) _GDIPlus_BitmapDispose($hBmp) _GDIPlus_GraphicsDispose($hGfxWin) GUIDelete($hGUI) If $bPausedMedia Then Send("{MEDIA_PLAY_PAUSE}") Return $iOutcome EndFunc ; ---------------- drawing ---------------- Func _DrawFrame($hGfx, $hGfxWin, $hBmp, $fAnimT, $fSecsLeft) _GDIPlus_GraphicsClear($hGfx, 0xFF000000) Local $iEyeW = 420, $iEyeH = 210 Local $iIrisR = 48, $iPupilR = 20 Local $fBlink = 1.0 Local $fBP = Mod($fAnimT, $BLINK_EVERY) If $fBP < 0.35 Then $fBlink = Abs(Cos($fBP / 0.35 * $PI)) If $fBlink < 0.06 Then $fBlink = 0.06 _DrawEye($hGfx, $ECX, $ECY, $iEyeW, $iEyeH, $iIrisR, $iPupilR, $fBlink) Local $hFamily = _GDIPlus_FontFamilyCreate("Segoe UI") Local $hFormat = _GDIPlus_StringFormatCreate() _GDIPlus_StringFormatSetAlign($hFormat, 1) _GDIPlus_StringFormatSetLineAlign($hFormat, 1) Local $hBrushTxt = _GDIPlus_BrushCreateSolid(0xFFE8E8E0) If $fSecsLeft >= 0 Then If $SHOW_COUNT Then Local $hFontBig = _GDIPlus_FontCreate($hFamily, 64, 1) Local $tNum = _GDIPlus_RectFCreate($ECX - 200, $ECY + 160, 400, 110) _GDIPlus_GraphicsDrawStringEx($hGfx, String(Ceiling($fSecsLeft)), $hFontBig, $tNum, $hFormat, $hBrushTxt) _GDIPlus_FontDispose($hFontBig) EndIf Local $hFontMsg = _GDIPlus_FontCreate($hFamily, 24, 0) Local $tMsg = _GDIPlus_RectFCreate($ECX - 500, $ECY - 250, 1000, 60) _GDIPlus_GraphicsDrawStringEx($hGfx, $MSG_BREAK, $hFontMsg, $tMsg, $hFormat, $hBrushTxt) _GDIPlus_FontDispose($hFontMsg) Else Local $hFontMsg = _GDIPlus_FontCreate($hFamily, 22, 0) Local $tMsg = _GDIPlus_RectFCreate($ECX - 500, $ECY + 180, 1000, 60) _GDIPlus_GraphicsDrawStringEx($hGfx, $MSG_FADE, $hFontMsg, $tMsg, $hFormat, $hBrushTxt) _GDIPlus_FontDispose($hFontMsg) EndIf _GDIPlus_BrushDispose($hBrushTxt) _GDIPlus_StringFormatDispose($hFormat) _GDIPlus_FontFamilyDispose($hFamily) _GDIPlus_GraphicsDrawImageRect($hGfxWin, $hBmp, 0, 0, $GW, $GH) EndFunc Func _DrawEye($hGfx, $x, $y, $iEyeW, $iEyeH, $iIrisR, $iPupilR, $fBlink) Local $iEyeHNow = $iEyeH * $fBlink Local $hBrushWhite = _GDIPlus_BrushCreateSolid(0xFFF2F2EC) _GDIPlus_GraphicsFillEllipse($hGfx, $x - $iEyeW / 2, $y - $iEyeHNow / 2, $iEyeW, $iEyeHNow, $hBrushWhite) If $fBlink > 0.3 Then Local $hBrushIris = _GDIPlus_BrushCreateSolid($COL_IRIS) Local $hBrushPupil = _GDIPlus_BrushCreateSolid(0xFF111111) Local $hBrushGlint = _GDIPlus_BrushCreateSolid(0xFFFFFFFF) _GDIPlus_GraphicsFillEllipse($hGfx, $x - $iIrisR, $y - $iIrisR * $fBlink, $iIrisR * 2, $iIrisR * 2 * $fBlink, $hBrushIris) _GDIPlus_GraphicsFillEllipse($hGfx, $x - $iPupilR, $y - $iPupilR * $fBlink, $iPupilR * 2, $iPupilR * 2 * $fBlink, $hBrushPupil) _GDIPlus_GraphicsFillEllipse($hGfx, $x + 8, $y - 22 * $fBlink, 14, 14 * $fBlink, $hBrushGlint) _GDIPlus_BrushDispose($hBrushIris) _GDIPlus_BrushDispose($hBrushPupil) _GDIPlus_BrushDispose($hBrushGlint) EndIf Local $hPenLid = _GDIPlus_PenCreate(0xFF000000, 8) _GDIPlus_GraphicsDrawEllipse($hGfx, $x - $iEyeW / 2, $y - $iEyeHNow / 2, $iEyeW, $iEyeHNow, $hPenLid) _GDIPlus_PenDispose($hPenLid) _GDIPlus_BrushDispose($hBrushWhite) EndFunc ; ---------------- helpers ---------------- Func _SM($iIndex) ; GetSystemMetrics Local $a = DllCall("user32.dll", "int", "GetSystemMetrics", "int", $iIndex) If @error Then Return 0 Return $a[0] EndFunc ; True if any keyboard/mouse input... (see _IdleSecs). This wrapper caches ; the audio check for 5 s so the COM call isn't made 5x per second while idle. Func _AudioActiveCached() Local Static $tCache = 0, $bCache = False If $tCache = 0 Or TimerDiff($tCache) > 5000 Then $bCache = _IsAudioPlaying() $tCache = TimerInit() EndIf Return $bCache EndFunc ; cached fullscreen check (5 s) - used by the idle logic Func _FullscreenCached() Local Static $tCache = 0, $bCache = False If $tCache = 0 Or TimerDiff($tCache) > 5000 Then $bCache = _IsFullscreenActive() $tCache = TimerInit() EndIf Return $bCache EndFunc ; True if the foreground window fully covers its monitor Func _IsFullscreenActive() Local $aWnd = DllCall("user32.dll", "hwnd", "GetForegroundWindow") If @error Or Not $aWnd[0] Then Return False Local $hWnd = $aWnd[0] ; ignore the desktop/shell Local $tClass = DllStructCreate("wchar Class[64]") DllCall("user32.dll", "int", "GetClassNameW", "hwnd", $hWnd, "struct*", $tClass, "int", 64) Local $sClass = DllStructGetData($tClass, "Class") If $sClass = "Progman" Or $sClass = "WorkerW" Or $sClass = "Shell_TrayWnd" Then Return False Local $tRect = DllStructCreate("long L;long T;long R;long B") DllCall("user32.dll", "bool", "GetWindowRect", "hwnd", $hWnd, "struct*", $tRect) Local $aMon = DllCall("user32.dll", "handle", "MonitorFromWindow", "hwnd", $hWnd, "dword", 2) ; MONITOR_DEFAULTTONEAREST If @error Or Not $aMon[0] Then Return False Local $tMI = DllStructCreate("dword Size;long ML;long MT;long MR;long MB;long WL;long WT;long WR;long WB;dword Flags") DllStructSetData($tMI, "Size", DllStructGetSize($tMI)) DllCall("user32.dll", "bool", "GetMonitorInfoW", "handle", $aMon[0], "struct*", $tMI) Return DllStructGetData($tRect, "L") <= DllStructGetData($tMI, "ML") _ And DllStructGetData($tRect, "T") <= DllStructGetData($tMI, "MT") _ And DllStructGetData($tRect, "R") >= DllStructGetData($tMI, "MR") _ And DllStructGetData($tRect, "B") >= DllStructGetData($tMI, "MB") EndFunc ; True if the workstation is locked: the lock screen runs on a secure ; desktop that this process cannot open, so a failed OpenInputDesktop ; means locked Func _IsLocked() Local $aRet = DllCall("user32.dll", "handle", "OpenInputDesktop", "dword", 0, "bool", False, "dword", 0x0100) ; DESKTOP_SWITCHDESKTOP If @error Or Not $aRet[0] Then Return True DllCall("user32.dll", "bool", "CloseDesktop", "handle", $aRet[0]) Return False EndFunc ; seconds since the last keyboard/mouse input Func _IdleSecs() Local $tLII = DllStructCreate("uint cbSize;dword dwTime") DllStructSetData($tLII, "cbSize", DllStructGetSize($tLII)) DllCall("user32.dll", "bool", "GetLastInputInfo", "struct*", $tLII) Local $aTick = DllCall("kernel32.dll", "dword", "GetTickCount") If @error Then Return 0 Return ($aTick[0] - DllStructGetData($tLII, "dwTime")) / 1000 EndFunc ; True if the break should be deferred (mic in use, e.g. on a call) Func _BreakBlocked() If $SKIP_MIC And _IsMicInUse() Then Return True Return False EndFunc ; True if any app currently has the microphone open (Windows consent store: ; an in-use app has LastUsedTimeStop = 0) Func _IsMicInUse() Local $sBase = "HKCU\Software\Microsoft\Windows\CurrentVersion\CapabilityAccessManager\ConsentStore\microphone" Local $aRoots[2] = [$sBase, $sBase & "\NonPackaged"] For $r = 0 To 1 Local $i = 1 While 1 Local $sKey = RegEnumKey($aRoots[$r], $i) If @error Then ExitLoop If $sKey <> "NonPackaged" Then Local $vStop = RegRead($aRoots[$r] & "\" & $sKey, "LastUsedTimeStop") If Not @error And Number($vStop) = 0 Then Return True EndIf $i += 1 WEnd Next Return False EndFunc ; ---------------- audio detection (Core Audio) ---------------- Func _IsAudioPlaying() Local Const $sCLSID_MMDeviceEnumerator = "{BCDE0395-E52F-467C-8E3D-C4579291692E}" Local Const $sIID_IMMDeviceEnumerator = "{A95664D2-9614-4F35-A746-DE8DB63617E6}" Local Const $sTag_IMMDeviceEnumerator = _ "EnumAudioEndpoints hresult(int;dword;ptr*);" & _ "GetDefaultAudioEndpoint hresult(int;int;ptr*);" & _ "GetDevice hresult(wstr;ptr*);" & _ "RegisterEndpointNotificationCallback hresult(ptr);" & _ "UnregisterEndpointNotificationCallback hresult(ptr);" Local $oEnum = ObjCreateInterface($sCLSID_MMDeviceEnumerator, $sIID_IMMDeviceEnumerator, $sTag_IMMDeviceEnumerator) If Not IsObj($oEnum) Then Return SetError(1, 0, False) Local $pDevice = 0 $oEnum.GetDefaultAudioEndpoint(0, 1, $pDevice) ; eRender, eMultimedia If Not $pDevice Then Return SetError(2, 0, False) Local Const $sIID_IMMDevice = "{D666063F-1587-4E43-81F1-B948E807363F}" Local Const $sTag_IMMDevice = _ "Activate hresult(struct*;dword;ptr;ptr*);" & _ "OpenPropertyStore hresult(dword;ptr*);" & _ "GetId hresult(ptr*);" & _ "GetState hresult(dword*);" Local $oDevice = ObjCreateInterface($pDevice, $sIID_IMMDevice, $sTag_IMMDevice) If Not IsObj($oDevice) Then Return SetError(3, 0, False) Local Const $sIID_IAudioMeterInformation = "{C02216F6-8C67-4B5B-9D00-D008E73E0064}" Local $tIID = _GUIDFromString($sIID_IAudioMeterInformation) If @error Then Return SetError(6, 0, False) Local $pMeter = 0 $oDevice.Activate($tIID, 0x17, 0, $pMeter) ; CLSCTX_ALL If Not $pMeter Then Return SetError(4, 0, False) Local Const $sTag_IAudioMeterInformation = _ "GetPeakValue hresult(float*);" & _ "GetMeteringChannelCount hresult(dword*);" & _ "GetChannelsPeakValues hresult(dword;float*);" & _ "QueryHardwareSupport hresult(dword*);" Local $oMeter = ObjCreateInterface($pMeter, $sIID_IAudioMeterInformation, $sTag_IAudioMeterInformation) If Not IsObj($oMeter) Then Return SetError(5, 0, False) Local $fPeak = 0 For $i = 1 To 5 Local $fVal = 0 $oMeter.GetPeakValue($fVal) If $fVal > $fPeak Then $fPeak = $fVal Sleep(30) Next Return $fPeak > 0.001 EndFunc Func _GUIDFromString($sGUID) Local $tGUID = DllStructCreate("ulong Data1;ushort Data2;ushort Data3;byte Data4[8]") Local $aRet = DllCall("ole32.dll", "long", "CLSIDFromString", "wstr", $sGUID, "struct*", $tGUID) If @error Or $aRet[0] <> 0 Then Return SetError(1, 0, 0) Return $tGUID EndFunc ; ---------------- INI ---------------- Func _LoadSettings() $WORK_SECS = IniRead($INI, "Timing", "WorkMinutes", 20) * 60 $FADE_SECS = Number(IniRead($INI, "Timing", "FadeSeconds", 5)) $BREAK_SECS = Number(IniRead($INI, "Timing", "BreakSeconds", 20)) $BLINK_EVERY = Number(IniRead($INI, "Timing", "BlinkEverySeconds", 10)) $SNOOZE_SECS = IniRead($INI, "Timing", "SnoozeMinutes", 5) * 60 $DEFER_SECS = IniRead($INI, "Timing", "DeferMinutes", 2) * 60 $IDLE_SECS = IniRead($INI, "Timing", "IdleResetMinutes", 3) * 60 $PAUSE_MEDIA = Int(IniRead($INI, "Behaviour", "PauseMedia", 1)) = 1 $SKIP_MIC = Int(IniRead($INI, "Behaviour", "SkipWhenMicInUse", 1)) = 1 $BREAK_IN_FS = Int(IniRead($INI, "Behaviour", "BreakDuringFullScreen", 1)) = 1 $ALL_MONITORS = Int(IniRead($INI, "Behaviour", "AllMonitors", 1)) = 1 $SHOW_COUNT = Int(IniRead($INI, "Behaviour", "ShowCountdown", 1)) = 1 $AUTO_START = Int(IniRead($INI, "Behaviour", "AutoStart", 1)) = 1 $MSG_BREAK = IniRead($INI, "Text", "BreakMessage", "Look at something in the distance (20 ft away)") $MSG_FADE = IniRead($INI, "Text", "FadeMessage", "20s Eye Break starting...") ; screen geometry: overlay covers either the primary monitor or the ; whole virtual screen; the eye is always centred on the primary monitor $GX = 0 $GY = 0 $GW = @DesktopWidth $GH = @DesktopHeight If $ALL_MONITORS Then $GX = _SM(76) ; SM_XVIRTUALSCREEN $GY = _SM(77) ; SM_YVIRTUALSCREEN $GW = _SM(78) ; SM_CXVIRTUALSCREEN $GH = _SM(79) ; SM_CYVIRTUALSCREEN EndIf $ECX = @DesktopWidth / 2 - $GX ; eye centre in overlay coords $ECY = @DesktopHeight / 2 - $GY EndFunc ; copy the compiled exe into the user's Startup folder so it runs at login ; (AutoStart=0 removes it again). Does nothing when run as an .au3 script. Func _InstallStartup() If Not @Compiled Then Return Local $sDest = @StartupDir & "\EyeBreak2020.exe" If StringLower(@ScriptFullPath) = StringLower($sDest) Then Return ; already running from Startup If $AUTO_START Then FileCopy(@ScriptFullPath, $sDest, 1) Else FileDelete($sDest) EndIf EndFunc Func _WriteDefaultIni() If FileExists($INI) Then Return IniWrite($INI, "Timing", "WorkMinutes", "20") IniWrite($INI, "Timing", "FadeSeconds", "5") IniWrite($INI, "Timing", "BreakSeconds", "20") IniWrite($INI, "Timing", "BlinkEverySeconds", "10") IniWrite($INI, "Timing", "SnoozeMinutes", "5") IniWrite($INI, "Timing", "DeferMinutes", "2") IniWrite($INI, "Timing", "IdleResetMinutes", "3") IniWrite($INI, "Behaviour", "PauseMedia", "1") IniWrite($INI, "Behaviour", "SkipWhenMicInUse", "1") IniWrite($INI, "Behaviour", "BreakDuringFullScreen", "1") IniWrite($INI, "Behaviour", "AllMonitors", "1") IniWrite($INI, "Behaviour", "ShowCountdown", "1") IniWrite($INI, "Behaviour", "AutoStart", "1") IniWrite($INI, "Text", "BreakMessage", "Look at something in the distance (20 ft away)") IniWrite($INI, "Text", "FadeMessage", "20s Eye Break starting...") EndFunc Func _Cleanup() _GDIPlus_Shutdown() DllClose($hDLL) EndFunc