Skip to content

Commit

Permalink
Add WinWait
Browse files Browse the repository at this point in the history
  • Loading branch information
phil294 committed Jul 20, 2023
1 parent e4d01a0 commit db32862
Show file tree
Hide file tree
Showing 5 changed files with 57 additions and 21 deletions.
8 changes: 4 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ AHK_X11 can be used completely without a terminal. You can however if you want u
<details><summary><strong>CLICK TO SEE WHICH COMMANDS ARE IMPLEMENTED AND WHICH ARE MISSING</strong>. Note however that this is not very representative. For example, no `Gui` sub command is included in the listing. For a better overview on what is already done, skim through the <a href="https://phil294.github.io/AHK_X11"><b>FULL DOCUMENTATION HERE</b></a>.</summary>

```diff
DONE ?% (115/220):
DONE ?% (116/220):
+ Else, { ... }, Break, Continue, Return, Exit, GoSub, GoTo, IfEqual, Loop, SetEnv, Sleep, FileCopy,
+ SetTimer, WinActivate, MsgBox, Gui, SendRaw, #Persistent, ExitApp,
+ EnvAdd, EnvSub, EnvMult, EnvDiv, ControlSendRaw, IfWinExist/IfWinNotExist, SetWorkingDir,
Expand All @@ -71,7 +71,7 @@ DONE ?% (115/220):
+ WinGet, Input, Loop (parse a string), ToolTip, If var [not] in/contains MatchList, ControlSetText,
+ PixelSearch, #Include, InputBox, ClipWait, EnvSet, SetKeyDelay, SetMouseDelay, MouseClickDrag,
+ #NoTrayIcon, TrayTip, Random, Shutdown, RunAs, SoundGet, SoundSet, SoundPlay, Sort,
+ StringTrimLeft, StringTrimRight, WinMinimizeAll, WinMinimizeAllUndo, WinSetTitle
+ StringTrimLeft, StringTrimRight, WinMinimizeAll, WinMinimizeAllUndo, WinSetTitle, WinWait,

NEW ?% (9/220): (not part of spec or from a more recent version)
@@ Echo, ahk_x11_print_vars, FileRead, RegExGetPos, RegExReplace, EnvGet, Click @@
Expand All @@ -86,7 +86,7 @@ REMOVED ?% (11/220):
# AutoTrim: It's always Off. It would not differentiate between %a_space% and %some_var%.
# It's possible but needs significant work.

TO DO ?% (81/220): alphabetically
TO DO ?% (80/220): alphabetically
- BlockInput, Control, ControlFocus, ControlGet, ControlGetFocus,
- ControlMove,
- DetectHiddenText, DetectHiddenWindows, Drive, DriveGet, DriveSpaceFree,
Expand All @@ -107,7 +107,7 @@ TO DO ?% (81/220): alphabetically
- SysGet, Thread, Transform, WinActivateBottom,
- WinGetActiveStats, WinGetActiveTitle,
- WinMenuSelectItem,
- WinSet, WinWait, WinWaitActive,
- WinSet, WinWaitActive,
- WinWaitClose, WinWaitNotActive, #CommentFlag, #ErrorStdOut, #EscapeChar,
- #HotkeyInterval, #HotkeyModifierTimeout, #MaxHotkeysPerInterval, #MaxMem,
- #MaxThreads, #MaxThreadsBuffer, #MaxThreadsPerHotkey, #WinActivateForce
Expand Down
7 changes: 4 additions & 3 deletions docs/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -779,7 +779,7 @@ <h2>Table of contents </h2>
<a href="#WinShow.htm">WinShow</a>
</li>
<li>
<a class="tbd" href="#WinWait.htm">WinWait</a>
<a href="#WinWait.htm">WinWait</a>
</li>
<li>
<a class="tbd" href="#WinWaitActive.htm">WinWaitActive/WinWaitNotActive</a>
Expand Down Expand Up @@ -2806,7 +2806,7 @@ <h2 class="calibre9"><span class="calibre23">The "Last Found" Window </span></h2
<td height="16" class="calibre4">Unhides a hidden window. </td>
</tr>
<tr class="calibre3">
<td height="16" class="tbd calibre4"><a href="#WinWait.htm" class="pcalibre3 pcalibre1 pcalibre calibre5 pcalibre2">WinWait</a></td>
<td height="16" class="calibre4"><a href="#WinWait.htm" class="pcalibre3 pcalibre1 pcalibre calibre5 pcalibre2">WinWait</a></td>
<td height="16" class="calibre4">Waits until the requested window exists.</td>
</tr>
<tr class="calibre3">
Expand Down Expand Up @@ -14949,7 +14949,7 @@ <h2 id="Examples">Examples</h2>
</pre>
</div>
</div>
<div class="calibreMain tbd">
<div class="calibreMain">
<div class="calibreEbookContent">
<a id="WinWait.htm" href="#WinWait.htm">#</a> <h2 class="calibre17">WinWait</h2>
<hr size="2" class="calibre24" />
Expand Down Expand Up @@ -14995,6 +14995,7 @@ <h2 id="Examples">Examples</h2>
<p class="calibre8">If a matching window comes into existence, the command will not wait for <em class="calibre21">Seconds</em> to expire. Instead, it will immediately set <a href="#ErrorLevel.htm" class="pcalibre3 pcalibre1 pcalibre calibre5 pcalibre2">ErrorLevel</a> to 0, update the <a href="#LastFoundWindow.htm" class="pcalibre3 pcalibre1 pcalibre calibre5 pcalibre2">Last Found Window</a>, and the script will continue executing.</p>
<p class="calibre8">Window titles and text are always case sensitive. Hidden windows are not detected unless <a href="#DetectHiddenWindows.htm" class="pcalibre3 pcalibre1 pcalibre calibre5 pcalibre2">DetectHiddenWindows</a> has been turned on.</p>
<p class="calibre8">While the command is in a waiting state, new <a href="#Threads.htm" class="pcalibre3 pcalibre1 pcalibre calibre5 pcalibre2">threads</a> can be launched via <a href="#Hotkeys.htm" class="pcalibre3 pcalibre1 pcalibre calibre5 pcalibre2">hotkey</a>, <a href="#Menu.htm" class="pcalibre3 pcalibre1 pcalibre calibre5 pcalibre2">custom menu item</a>, or <a href="#SetTimer.htm" class="pcalibre3 pcalibre1 pcalibre calibre5 pcalibre2">timer</a>.</p>
<p class="calibre8 x11">The seconds waited may not be very precise as the program internally merely does a loop with exponential back-off delay.</p>
<p class="calibre8"> </p>
<p class="calibre8"><strong class="calibre14">Related</strong></p>
<p class="calibre8"><a href="#WinWaitActive.htm" class="pcalibre3 pcalibre1 pcalibre calibre5 pcalibre2">WinWaitActive</a>, <a href="#WinWaitClose.htm" class="pcalibre3 pcalibre1 pcalibre calibre5 pcalibre2">WinWaitClose</a>, <a href="#IfWinExist.htm" class="pcalibre3 pcalibre1 pcalibre calibre5 pcalibre2">IfWinExist</a>, <a href="#IfWinActive.htm" class="pcalibre3 pcalibre1 pcalibre calibre5 pcalibre2">IfWinActive</a>, <a href="#Process.htm" class="pcalibre3 pcalibre1 pcalibre calibre5 pcalibre2">Process</a>, <a href="#SetTitleMatchMode.htm" class="pcalibre3 pcalibre1 pcalibre calibre5 pcalibre2">SetTitleMatchMode</a>, <a href="#DetectHiddenWindows.htm" class="pcalibre3 pcalibre1 pcalibre calibre5 pcalibre2">DetectHiddenWindows</a></p>
Expand Down
17 changes: 3 additions & 14 deletions src/cmd/misc/clip-wait.cr
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
require "../../util/exponential-back-off.cr"
# ClipWait [, SecondsToWait]
class Cmd::Misc::ClipWait < Cmd::Base
def self.min_args; 0 end
Expand All @@ -19,21 +20,9 @@ class Cmd::Misc::ClipWait < Cmd::Base
# to retrieve text, even when it's empty:
# thread.runner.display.gtk.clipboard &.wait_is_text_available
# So we need to resort to looping which you could also easily do with ahk code itself.
back_off_wait = 5.milliseconds
start = Time.monotonic
loop do
Util::ExponentialBackOff.back_off(initial_wait: 5.milliseconds, factor: 1.2, max_wait: 0.5.seconds, timeout: timeout) do
txt = gtk.clipboard &.wait_for_text || ""
if yield(txt)
break
end
if timeout
if Time.monotonic - start > timeout
return false
end
end
sleep back_off_wait
back_off_wait = ::Math.min(back_off_wait * 1.2, 0.5.seconds)
yield(txt)
end
true
end
end
20 changes: 20 additions & 0 deletions src/cmd/x11/window/win-wait.cr
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
require "../../../util/exponential-back-off.cr"
require "./win-util"
# WinWait, WinTitle, WinText, Seconds [, ExcludeTitle, ExcludeText]
class Cmd::X11::Window::WinWait < Cmd::Base
def self.min_args; 3 end
def self.max_args; 5 end
def self.sets_error_level; true end
def run(thread, args)
seconds = args[2].to_f? || 0.5
seconds = 0.5 if seconds == 0
match_conditions = args
match_conditions.delete_at(2)
match = ::Util::ExponentialBackOff.back_off(initial_wait: 20.milliseconds, factor: 1.15, max_wait: 0.8.seconds, timeout: seconds.seconds) do
Util.match(thread, match_conditions, empty_is_last_found: false, a_is_active: false) do |win|
thread.settings.last_found_window = win
end
end
match ? "0" : "1"
end
end
26 changes: 26 additions & 0 deletions src/util/exponential-back-off.cr
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
class Util::ExponentialBackOff
# Waits until the *&block* passes. Increases the sleep time in between operations
# by *factor*, but sleeps max. *max_wait* seconds. If *max_wait* is unspecified,
# the waiting gaps can get infinitely big.
# Returns `true` when the *&block* returns true or `false` when *timeout* was exceeded.
def self.back_off(*, initial_wait : Time::Span, factor : Float64, max_wait : Time::Span? = nil, timeout : Time::Span? = nil, &block : -> Bool)
back_off_wait = initial_wait
start = Time.monotonic
loop do
if yield
break
end
if timeout
if Time.monotonic - start > timeout
return false
end
end
sleep back_off_wait
back_off_wait = back_off_wait * factor
if max_wait
back_off_wait = ::Math.min(back_off_wait, max_wait)
end
end
true
end
end

0 comments on commit db32862

Please sign in to comment.