Skip to content

Commit db32862

Browse files
committed
Add WinWait
1 parent e4d01a0 commit db32862

File tree

5 files changed

+57
-21
lines changed

5 files changed

+57
-21
lines changed

README.md

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ AHK_X11 can be used completely without a terminal. You can however if you want u
5555
<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>
5656

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

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

89-
TO DO ?% (81/220): alphabetically
89+
TO DO ?% (80/220): alphabetically
9090
- BlockInput, Control, ControlFocus, ControlGet, ControlGetFocus,
9191
- ControlMove,
9292
- DetectHiddenText, DetectHiddenWindows, Drive, DriveGet, DriveSpaceFree,
@@ -107,7 +107,7 @@ TO DO ?% (81/220): alphabetically
107107
- SysGet, Thread, Transform, WinActivateBottom,
108108
- WinGetActiveStats, WinGetActiveTitle,
109109
- WinMenuSelectItem,
110-
- WinSet, WinWait, WinWaitActive,
110+
- WinSet, WinWaitActive,
111111
- WinWaitClose, WinWaitNotActive, #CommentFlag, #ErrorStdOut, #EscapeChar,
112112
- #HotkeyInterval, #HotkeyModifierTimeout, #MaxHotkeysPerInterval, #MaxMem,
113113
- #MaxThreads, #MaxThreadsBuffer, #MaxThreadsPerHotkey, #WinActivateForce

docs/index.html

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -779,7 +779,7 @@ <h2>Table of contents </h2>
779779
<a href="#WinShow.htm">WinShow</a>
780780
</li>
781781
<li>
782-
<a class="tbd" href="#WinWait.htm">WinWait</a>
782+
<a href="#WinWait.htm">WinWait</a>
783783
</li>
784784
<li>
785785
<a class="tbd" href="#WinWaitActive.htm">WinWaitActive/WinWaitNotActive</a>
@@ -2806,7 +2806,7 @@ <h2 class="calibre9"><span class="calibre23">The "Last Found" Window </span></h2
28062806
<td height="16" class="calibre4">Unhides a hidden window. </td>
28072807
</tr>
28082808
<tr class="calibre3">
2809-
<td height="16" class="tbd calibre4"><a href="#WinWait.htm" class="pcalibre3 pcalibre1 pcalibre calibre5 pcalibre2">WinWait</a></td>
2809+
<td height="16" class="calibre4"><a href="#WinWait.htm" class="pcalibre3 pcalibre1 pcalibre calibre5 pcalibre2">WinWait</a></td>
28102810
<td height="16" class="calibre4">Waits until the requested window exists.</td>
28112811
</tr>
28122812
<tr class="calibre3">
@@ -14949,7 +14949,7 @@ <h2 id="Examples">Examples</h2>
1494914949
</pre>
1495014950
</div>
1495114951
</div>
14952-
<div class="calibreMain tbd">
14952+
<div class="calibreMain">
1495314953
<div class="calibreEbookContent">
1495414954
<a id="WinWait.htm" href="#WinWait.htm">#</a> <h2 class="calibre17">WinWait</h2>
1495514955
<hr size="2" class="calibre24" />
@@ -14995,6 +14995,7 @@ <h2 id="Examples">Examples</h2>
1499514995
<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>
1499614996
<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>
1499714997
<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>
14998+
<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>
1499814999
<p class="calibre8"> </p>
1499915000
<p class="calibre8"><strong class="calibre14">Related</strong></p>
1500015001
<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>

src/cmd/misc/clip-wait.cr

Lines changed: 3 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
require "../../util/exponential-back-off.cr"
12
# ClipWait [, SecondsToWait]
23
class Cmd::Misc::ClipWait < Cmd::Base
34
def self.min_args; 0 end
@@ -19,21 +20,9 @@ class Cmd::Misc::ClipWait < Cmd::Base
1920
# to retrieve text, even when it's empty:
2021
# thread.runner.display.gtk.clipboard &.wait_is_text_available
2122
# So we need to resort to looping which you could also easily do with ahk code itself.
22-
back_off_wait = 5.milliseconds
23-
start = Time.monotonic
24-
loop do
23+
Util::ExponentialBackOff.back_off(initial_wait: 5.milliseconds, factor: 1.2, max_wait: 0.5.seconds, timeout: timeout) do
2524
txt = gtk.clipboard &.wait_for_text || ""
26-
if yield(txt)
27-
break
28-
end
29-
if timeout
30-
if Time.monotonic - start > timeout
31-
return false
32-
end
33-
end
34-
sleep back_off_wait
35-
back_off_wait = ::Math.min(back_off_wait * 1.2, 0.5.seconds)
25+
yield(txt)
3626
end
37-
true
3827
end
3928
end

src/cmd/x11/window/win-wait.cr

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
require "../../../util/exponential-back-off.cr"
2+
require "./win-util"
3+
# WinWait, WinTitle, WinText, Seconds [, ExcludeTitle, ExcludeText]
4+
class Cmd::X11::Window::WinWait < Cmd::Base
5+
def self.min_args; 3 end
6+
def self.max_args; 5 end
7+
def self.sets_error_level; true end
8+
def run(thread, args)
9+
seconds = args[2].to_f? || 0.5
10+
seconds = 0.5 if seconds == 0
11+
match_conditions = args
12+
match_conditions.delete_at(2)
13+
match = ::Util::ExponentialBackOff.back_off(initial_wait: 20.milliseconds, factor: 1.15, max_wait: 0.8.seconds, timeout: seconds.seconds) do
14+
Util.match(thread, match_conditions, empty_is_last_found: false, a_is_active: false) do |win|
15+
thread.settings.last_found_window = win
16+
end
17+
end
18+
match ? "0" : "1"
19+
end
20+
end

src/util/exponential-back-off.cr

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
class Util::ExponentialBackOff
2+
# Waits until the *&block* passes. Increases the sleep time in between operations
3+
# by *factor*, but sleeps max. *max_wait* seconds. If *max_wait* is unspecified,
4+
# the waiting gaps can get infinitely big.
5+
# Returns `true` when the *&block* returns true or `false` when *timeout* was exceeded.
6+
def self.back_off(*, initial_wait : Time::Span, factor : Float64, max_wait : Time::Span? = nil, timeout : Time::Span? = nil, &block : -> Bool)
7+
back_off_wait = initial_wait
8+
start = Time.monotonic
9+
loop do
10+
if yield
11+
break
12+
end
13+
if timeout
14+
if Time.monotonic - start > timeout
15+
return false
16+
end
17+
end
18+
sleep back_off_wait
19+
back_off_wait = back_off_wait * factor
20+
if max_wait
21+
back_off_wait = ::Math.min(back_off_wait, max_wait)
22+
end
23+
end
24+
true
25+
end
26+
end

0 commit comments

Comments
 (0)