diff --git a/src/GLib/GLib.jl b/src/GLib/GLib.jl index c9b2ad9f..b70a9cb3 100644 --- a/src/GLib/GLib.jl +++ b/src/GLib/GLib.jl @@ -41,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 075a7679..a917048a 100644 --- a/src/GLib/signals.jl +++ b/src/GLib/signals.jl @@ -381,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 09bf6a2c..c8ba8958 100644 --- a/src/Gtk.jl +++ b/src/Gtk.jl @@ -128,13 +128,50 @@ 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)) + # if g_main_depth > 0, a glib main-loop is already running. + # unfortunately this call does not reliably reflect the state after the + # loop has been stopped or restarted, so only use it once at the start + 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" + + # by default, defer starting the event loop until either `show`, `showall`, or `g_idle_add` is called + enable_eventloop(!auto_idle[]) +end + +const auto_idle = Ref{Bool}(true) # control default via ENV["GTK_AUTO_IDLE"] +const gtk_main_running = Ref{Bool}(false) + +""" + Gtk.enable_eventloop(b::Bool = true) + +Set whether Gtk's event loop is running. +""" +function enable_eventloop(b::Bool = true) + 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.is_eventloop_running()::Bool + +Check whether Gtk's event loop is running. +""" +is_eventloop_running() = gtk_main_running[] + const ser_version = Serialization.ser_version let cachedir = joinpath(splitdir(@__FILE__)[1], "..", "gen") fastgtkcache = joinpath(cachedir, "gtk$(libgtk_version.major)_julia_ser$(ser_version)") diff --git a/src/base.jl b/src/base.jl index d742f062..e5f464ed 100644 --- a/src/base.jl +++ b/src/base.jl @@ -27,8 +27,31 @@ screen_size(w::GtkWindowLeaf) = screen_size(Gtk.GAccessor.screen(w)) ### Functions and methods common to all GtkWidget objects 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) -show(w::GtkWidget) = (@sigatom ccall((:gtk_widget_show, libgtk), Nothing, (Ptr{GObject},), w); w) -showall(w::GtkWidget) = (@sigatom ccall((:gtk_widget_show_all, libgtk), Nothing, (Ptr{GObject},), w); w) + +const shown_widgets = WeakKeyDict() +function handle_auto_idle(w::GtkWidget) + if auto_idle[] + signal_connect(w, :realize) do w + enable_eventloop(true) + shown_widgets[w] = nothing + signal_connect(w, :destroy) do w + delete!(shown_widgets, w) + isempty(shown_widgets) && enable_eventloop(false) + end + end + end +end +function show(w::GtkWidget) + handle_auto_idle(w) + @sigatom ccall((:gtk_widget_show, libgtk), Nothing, (Ptr{GObject},), w) + w +end +function showall(w::GtkWidget) + handle_auto_idle(w) + @sigatom ccall((:gtk_widget_show_all, libgtk), Nothing, (Ptr{GObject},), w) + w +end + hide(w::GtkWidget) = (@sigatom ccall((:gtk_widget_hide , libgtk),Cvoid,(Ptr{GObject},),w); w) grab_focus(w::GtkWidget) = (@sigatom ccall((:gtk_widget_grab_focus , libgtk), Cvoid, (Ptr{GObject},), w); w) 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)