diff --git a/src/GLib/GLib.jl b/src/GLib/GLib.jl index c74eeda3..b70a9cb3 100644 --- a/src/GLib/GLib.jl +++ b/src/GLib/GLib.jl @@ -32,7 +32,6 @@ export set_gtk_property!, get_gtk_property export GConnectFlags export @sigatom, cfunction_ -const IDLE = Ref{Bool}(true) cfunction_(@nospecialize(f), r, a::Tuple) = cfunction_(f, r, Tuple{a...}) @@ -42,6 +41,8 @@ cfunction_(@nospecialize(f), r, a::Tuple) = cfunction_(f, r, Tuple{a...}) end end +const gtk_eventloop_f = Ref{Function}() + # local function, handles Symbol and makes UTF8-strings easier const AbstractStringLike = Union{AbstractString, Symbol} bytestring(s) = String(s) diff --git a/src/GLib/signals.jl b/src/GLib/signals.jl index 3d7f820c..a917048a 100644 --- a/src/GLib/signals.jl +++ b/src/GLib/signals.jl @@ -288,9 +288,7 @@ function uv_prepare(src::Ptr{Nothing}, timeout::Ptr{Cint}) global expiration, uv_pollfd local tmout_ms::Cint evt = Base.eventloop() - if IDLE[] - return Int32(1) - elseif !_isempty_workqueue() + if !_isempty_workqueue() tmout_ms = 0 elseif !uv_loop_alive(evt) tmout_ms = -1 @@ -318,7 +316,7 @@ end function uv_check(src::Ptr{Nothing}) global expiration ex = expiration::UInt64 - if !_isempty_workqueue() || IDLE[] + if !_isempty_workqueue() return Int32(1) elseif !uv_loop_alive(Base.eventloop()) return Int32(0) @@ -383,6 +381,7 @@ end @deprecate g_timeout_add(interval, cb, user_data) g_timeout_add(() -> cb(user_data), interval) function g_idle_add(cb::Function) + gtk_eventloop_f[](true) callback = @cfunction(_g_callback, Cint, (Ref{Function},)) ref, deref = gc_ref_closure(cb) return ccall((:g_idle_add_full , libglib),Cint, diff --git a/src/Gtk.jl b/src/Gtk.jl index eb16da42..7908876c 100644 --- a/src/Gtk.jl +++ b/src/Gtk.jl @@ -128,35 +128,49 @@ function __init__() C_NULL, C_NULL, "Julia Gtk Bindings", C_NULL, C_NULL, error_check) end - # if g_main_depth > 0, a glib main-loop is already running, - # so we don't need to start a new one - if ccall((:g_main_depth, GLib.libglib), Cint, ()) == 0 - global gtk_main_task = schedule(Task(gtk_main)) - end + # if g_main_depth > 0, a glib main-loop is already running. + # unfortunately this call does not reliably reflect when the loop stops, or restarts, so + # only use it once + gtk_main_running[] = ccall((:g_main_depth, GLib.libglib), Cint, ()) > 0 + + # Given GLib provides `g_idle_add` to specify what happens during idle, this allows + # that call to also start the eventloop + GLib.gtk_eventloop_f[] = enable_eventloop - AUTO_IDLE[] = get(ENV, "GTK_AUTO_IDLE", "true") == "true" - idle(AUTO_IDLE[]) + auto_idle[] = get(ENV, "GTK_AUTO_IDLE", "true") == "true" + + # by default, defer starting the event loop until either `show`, `showall`, or `g_idle_add` is called + enable_eventloop(!auto_idle[], force = true) end -const AUTO_IDLE = Ref{Bool}(true) +const auto_idle = Ref{Bool}(true) # control default via ENV["GTK_AUTO_IDLE"] +const gtk_main_running = Ref{Bool}(false) """ - Gtk.idle(b::Bool = true) + Gtk.enable_eventloop(b::Bool = true) -Set whether Gtk's event loop should be idle. +Set whether Gtk's event loop is running. """ -function idle(b::Bool) - GLib.IDLE[] = b +function enable_eventloop(b::Bool = true; force = false) + if b + if !is_eventloop_running() + global gtk_main_task = schedule(Task(gtk_main)) + gtk_main_running[] = true + end + else + if is_eventloop_running() + gtk_quit() + gtk_main_running[] = false + end + end end """ - Gtk.isidle()::Bool + Gtk.is_eventloop_running()::Bool -Check whether Gtk's event loop is idle. +Check whether Gtk's event loop is running. """ -function isidle() - GLib.IDLE[] -end +is_eventloop_running() = gtk_main_running[] const ser_version = Serialization.ser_version let cachedir = joinpath(splitdir(@__FILE__)[1], "..", "gen") diff --git a/src/base.jl b/src/base.jl index 270097bc..da456a19 100644 --- a/src/base.jl +++ b/src/base.jl @@ -28,14 +28,14 @@ screen_size(w::GtkWindowLeaf) = screen_size(Gtk.GAccessor.screen(w)) visible(w::GtkWidget) = Bool(ccall((:gtk_widget_get_visible, libgtk), Cint, (Ptr{GObject},), w)) visible(w::GtkWidget, state::Bool) = @sigatom ccall((:gtk_widget_set_visible, libgtk), Nothing, (Ptr{GObject}, Cint), w, state) -const SHOWN_WIDGETS = WeakKeyDict() +const shown_widgets = WeakKeyDict() function handle_auto_idle(w::GtkWidget) - if AUTO_IDLE[] - idle(false) - SHOWN_WIDGETS[w] = nothing + if auto_idle[] + enable_eventloop(true) + shown_widgets[w] = nothing signal_connect(w, :destroy) do w - delete!(SHOWN_WIDGETS, w) - isempty(SHOWN_WIDGETS) && idle(true) + delete!(shown_widgets, w) + isempty(shown_widgets) && enable_eventloop(false) end end end diff --git a/src/windows.jl b/src/windows.jl index 0defc677..1e8198ae 100644 --- a/src/windows.jl +++ b/src/windows.jl @@ -17,9 +17,9 @@ end resize!(win::GtkWindow, w::Integer, h::Integer) = ccall((:gtk_window_resize, libgtk), Nothing, (Ptr{GObject}, Int32, Int32), win, w, h) -present(win::GtkWindow) = ccall((:gtk_window_present, libgtk), Nothing, (Ptr{GObject},), win) +present(win::GtkWindow) = (handle_auto_idle(win); ccall((:gtk_window_present, libgtk), Nothing, (Ptr{GObject},), win)) -fullscreen(win::GtkWindow) = ccall((:gtk_window_fullscreen, libgtk), Nothing, (Ptr{GObject},), win) +fullscreen(win::GtkWindow) = (handle_auto_idle(win); ccall((:gtk_window_fullscreen, libgtk), Nothing, (Ptr{GObject},), win)) unfullscreen(win::GtkWindow) = ccall((:gtk_window_unfullscreen, libgtk), Nothing, (Ptr{GObject},), win) maximize(win::GtkWindow) = ccall((:gtk_window_maximize, libgtk), Nothing, (Ptr{GObject},), win)