Skip to content

Commit

Permalink
Fix unsetting Clipbord / Add internal clip_wait logic to setting an…
Browse files Browse the repository at this point in the history
…d retrieving `Clipboard`, so that those accesses always succeed (or explicitly show an error)
  • Loading branch information
phil294 committed Jul 20, 2023
1 parent b2b0b67 commit 05052bb
Show file tree
Hide file tree
Showing 3 changed files with 63 additions and 16 deletions.
28 changes: 22 additions & 6 deletions src/cmd/misc/clip-wait.cr
Expand Up @@ -4,20 +4,36 @@ class Cmd::Misc::ClipWait < Cmd::Base
def self.max_args; 1 end
def self.sets_error_level; true end
def run(thread, args)
timeout_sec = args[0]?.try &.to_f?
start = Time.monotonic
timeout = args[0]?.try &.to_f?.try &.seconds
success = ClipWait.clip_wait(thread.runner.display.gtk, timeout) do |clp|
! clp.empty?
end
success ? "0" : "1"
end

# Waits until the clipboard passes the *&block*. Increases the sleep time inbetween operations
# slightly exponentially, but sleeps max. 0.5 seconds.
# Returns `true` when the *&block* returns true or `false` when *timeout* was exceeded.
def self.clip_wait(gtk, timeout : Time::Span? = nil, &block : ::String -> Bool)
# The following works great and low on performance, but it only checks for the *possibility*
# 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
while (txt = thread.runner.display.gtk.clipboard &.wait_for_text || "").empty?
if timeout_sec
return "1" if Time.monotonic - start > timeout_sec.seconds
start = Time.monotonic
loop 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)
end
"0"
true
end
end
40 changes: 40 additions & 0 deletions src/run/display/gtk.cr
Expand Up @@ -54,6 +54,46 @@ module Run
block.call(clip)
end
end
def set_clipboard(text : String)
if text.empty?
clipboard do |clip|
# clip.clear # Doesn't do anything
# Doesn't work for access from outside of ahkx11 itself, even though https://stackoverflow.com/q/2418487 says so. Maybe a Crystal GIR bug?:
# clip.set_text("", 0)
# Ugly but works:
clip.image = GdkPixbuf::Pixbuf.new
end
if ! Cmd::Misc::ClipWait.clip_wait(self, 2.seconds, &.empty?)
raise Run::RuntimeException.new "Unsetting Clipboard failed [1]"
end
# But even now that we have confirmation that it was unset, the clipboard is sometimes still
# in the process of being reported to the active window. Setting it to a text and then unsetting
# it again seems to resolve it somehow. All of this is necessary, putting a sleep of the same duration
# instead is not enough.
clipboard do |clip|
clip.set_text("_ahk_x11_clipboard_internal", 27)
clip.store
end
if ! Cmd::Misc::ClipWait.clip_wait(self, 2.seconds, &.==("_ahk_x11_clipboard_internal"))
raise Run::RuntimeException.new "Unsetting Clipboard failed [2]"
end
clipboard do |clip|
clip.set_text("", 0)
clip.store
end
if ! Cmd::Misc::ClipWait.clip_wait(self, 2.seconds, &.empty?)
raise Run::RuntimeException.new "Unsetting Clipboard failed [3]"
end
else
clipboard do |clip|
clip.set_text(text, text.size)
clip.store
end
if ! Cmd::Misc::ClipWait.clip_wait(self, 2.seconds, &.==(text))
raise Run::RuntimeException.new "Setting Clipboard failed"
end
end
end

# Can't use @[Flags] because some values are *not* strict 2^n
enum MsgBoxOptions
Expand Down
11 changes: 1 addition & 10 deletions src/run/runner.cr
Expand Up @@ -210,16 +210,7 @@ module Run
down = var.downcase
case down
when "clipboard"
display.gtk.clipboard do |clip|
if value.empty?
# clip.clear # Doesn't do anything
# clip.set_text("", 0) # Doesn't work outside of ahkx11 itself, even though https://stackoverflow.com/q/2418487 says so. Maybe a Crystal GIR bug?
clip.image = GdkPixbuf::Pixbuf.new # Ugly but works
else
clip.set_text(value, value.size)
end
clip.store
end
display.gtk.set_clipboard(value)
else
return if @built_in_static_vars[down]? || get_global_built_in_computed_var(down)
{% if ! flag?(:release) %}
Expand Down

0 comments on commit 05052bb

Please sign in to comment.