From 74bed91d957626ec16438f134319218d53a481b7 Mon Sep 17 00:00:00 2001
From: Lars Kanis <kanis@comcard.de>
Date: Fri, 26 Jun 2015 09:58:47 +0200
Subject: [PATCH] Add runOnUiThread to FXApp and FXId.

This allows to safely execute GUI code from other threads.
---
 lib/fox16.rb          |  1 +
 lib/fox16/thread.rb   | 50 +++++++++++++++++++++++++++++++++++++++++++
 rdoc-sources/FXApp.rb |  5 +++++
 rdoc-sources/FXId.rb  |  5 +++++
 test/TC_FXApp.rb      | 35 ++++++++++++++++++++++++++++++
 5 files changed, 96 insertions(+)
 create mode 100644 lib/fox16/thread.rb

diff --git a/lib/fox16.rb b/lib/fox16.rb
index 7902976..bfe60ea 100644
--- a/lib/fox16.rb
+++ b/lib/fox16.rb
@@ -28,3 +28,4 @@ require "fox16/execute_nonmodal"
 require "fox16/version"
 require "fox16/kwargs"
 require "fox16/exceptions_for_fxerror"
+require "fox16/thread"
diff --git a/lib/fox16/thread.rb b/lib/fox16/thread.rb
new file mode 100644
index 0000000..0cf58ee
--- /dev/null
+++ b/lib/fox16/thread.rb
@@ -0,0 +1,50 @@
+require 'thread'
+
+module Fox
+
+  class FXApp
+
+    alias initialize_before_thread initialize # :nodoc:
+
+    def initialize(*args, &block)
+      initialize_before_thread(*args, &block)
+      event_handler_setup
+    end
+
+    def runOnUiThread(&block)
+      @event_handler_events << block
+      @event_handler_pwr.write 'e' if @event_handler_pwr
+    end
+
+    private
+
+    def event_handler_setup
+      if RUBY_PLATFORM =~ /mingw|mswin/i
+        require 'socket'
+        gs = TCPServer.open('localhost', 0)
+        prd = TCPSocket.open('localhost', gs.addr[1])
+        pwr = gs.accept
+      else
+        prd, pwr = IO.pipe
+      end
+      self.addInput(prd, Fox::INPUT_READ){ event_handler_pull(prd) }
+      @event_handler_pwr = pwr
+      @event_handler_events = Queue.new
+    end
+
+    def event_handler_pull(prd)
+      prd.read(1) if prd
+      while !@event_handler_events.empty?
+        ev = @event_handler_events.shift
+        ev.call
+      end
+    end
+
+  end # class FXApp
+
+  class FXId
+    def runOnUiThread(&block)
+      app.runOnUiThread(&block)
+    end
+  end
+end # module Fox
diff --git a/rdoc-sources/FXApp.rb b/rdoc-sources/FXApp.rb
index d21b7f3..3e533be 100755
--- a/rdoc-sources/FXApp.rb
+++ b/rdoc-sources/FXApp.rb
@@ -538,5 +538,10 @@ module Fox
 
     # Check to see if multithreaded applications are supported
     def threadsEnabled?(); end
+
+    # Runs the specified block on the UI thread.
+    #
+    # The block is posted to the event queue of the UI thread.
+    def runOnUiThread(&block); end
   end
 end
diff --git a/rdoc-sources/FXId.rb b/rdoc-sources/FXId.rb
index 664c4f9..cbef725 100755
--- a/rdoc-sources/FXId.rb
+++ b/rdoc-sources/FXId.rb
@@ -30,5 +30,10 @@ module Fox
     # Destroy resource.
     #
     def destroy(); end
+
+    # Runs the specified block on the UI thread.
+    #
+    # The block is posted to the event queue of the UI thread.
+    def runOnUiThread(&block); end
   end
 end
diff --git a/test/TC_FXApp.rb b/test/TC_FXApp.rb
index 1ebef42..c870993 100755
--- a/test/TC_FXApp.rb
+++ b/test/TC_FXApp.rb
@@ -81,4 +81,39 @@ class TC_FXApp2 < Fox::TestCase
     pipe_rdwr = IO.popen("cat", "r+")
     check_events pipe_rdwr, pipe_rdwr
   end
+
+  def test_runOnUiThread
+    count = 0
+    thread = nil
+    Thread.new do
+      10.times do |idx|
+        app.runOnUiThread do
+          count += 1
+          thread = Thread.current
+          app.stop if idx == 9
+        end
+        sleep 0.001
+      end
+    end
+    app.run
+
+    assert_equal Thread.current, thread
+    assert_equal 10, count
+  end
+
+  def test_runOnUiThread_same_thread
+    count = 0
+    app.addTimeout(1) do
+      10.times do |idx|
+        app.runOnUiThread do
+          count += 1
+          app.stop if idx == 9
+        end
+        sleep 0.001
+      end
+    end
+    app.run
+
+    assert_equal 10, count
+  end
 end
-- 
GitLab