diff --git a/src/GLib/signals.jl b/src/GLib/signals.jl index d4d1085f..d7337ab7 100644 --- a/src/GLib/signals.jl +++ b/src/GLib/signals.jl @@ -400,8 +400,11 @@ macro idle_add(ex) end end +const simple_loop = Ref{Bool}(false) + const exiting = Ref(false) function __init__() + simple_loop[] = get(ENV, "GTK_SIMPLE_LOOP", "true") == "true" if isdefined(GLib, :__init__bindeps__) GLib.__init__bindeps__() end @@ -410,6 +413,8 @@ function __init__() exiting[] = false atexit(() -> (exiting[] = true)) __init__gtype__() - __init__gmainloop__() + if !simple_loop[] + __init__gmainloop__() + end nothing end diff --git a/src/Gtk.jl b/src/Gtk.jl index 3d8b4ded..c4f98f8d 100644 --- a/src/Gtk.jl +++ b/src/Gtk.jl @@ -77,6 +77,7 @@ include("gio.jl") include("application.jl") function __init__() + Sys.iswindows() && (ENV["GTK_CSD"] = 0) # Set XDG_DATA_DIRS so that Gtk can find its icons and schemas ENV["XDG_DATA_DIRS"] = join(filter(x -> x !== nothing, [ dirname(adwaita_icons_dir), @@ -168,12 +169,46 @@ function __init__() # that call to also start the eventloop GLib.gtk_eventloop_f[] = enable_eventloop - auto_idle[] = get(ENV, "GTK_AUTO_IDLE", "true") == "true" + auto_idle[] = get(ENV, "GTK_AUTO_IDLE", "false") == "true" # by default, defer starting the event loop until either `show`, `showall`, or `g_idle_add` is called enable_eventloop(!auto_idle[]) end +""" + Gtk.iteration(may_block) + +Run a single interation of the Gtk event loop. If `may_block` is true, this +function will wait until events are ready to be processed. Otherwise, it will +return immediately if no events need to be processed. Returns `true` if events +were processed. +""" +function iteration(may_block::Bool) + while events_pending() + ccall((:g_main_context_iteration, libglib), Cint, (Ptr{Cvoid}, Cint), C_NULL, may_block) + end +end + +const pause_loop = Ref{Bool}(false) + +iterate(timer) = pause_loop[] || iteration(false) + +""" + Gtk.events_pending() + +Check whether events need processing by the Gtk's event loop. This function can +be used in conjuction with `iterate` to refresh the GUI during long operations +or in cases where widgets must be realized before proceeding. +""" +events_pending() = ccall((:gtk_events_pending, libgtk), Cint, ()) != 0 + +const mainloop_timer = Ref{Timer}() + +function glib_main_simple() + mainloop_timer[]=Timer(iterate,0.01;interval=0.005) + wait(mainloop_timer[]) +end + const auto_idle = Ref{Bool}(true) # control default via ENV["GTK_AUTO_IDLE"] const gtk_main_running = Ref{Bool}(false) const quit_task = Ref{Task}() @@ -184,6 +219,16 @@ const enable_eventloop_lock = Base.ReentrantLock() Set whether Gtk's event loop is running. """ function enable_eventloop(b::Bool = true; wait_stopped::Bool = false) + if GLib.simple_loop[] + if b + auto_idle[] = false + global glib_main_task = schedule(Task(glib_main_simple)) + return + else + close(mainloop_timer[]) + return + end + end lock(enable_eventloop_lock) do # handle widgets that are being shown/destroyed from different threads isassigned(quit_task) && wait(quit_task[]) # prevents starting while the async is still stopping if b @@ -210,16 +255,25 @@ end Gtk.pause_eventloop(f; force = false) Pauses the eventloop around a function. Restores the state of the eventloop after -pausing. Respects whether Gtk.jl is configured to allow auto-stopping of the -eventloop, unless `force = true`. +pausing. If GLib.simple_loop[] is disabled, respects whether Gtk.jl is configured +to allow auto-stopping of the eventloop, unless `force = true`. """ function pause_eventloop(f; force = false) - was_running = is_eventloop_running() - (force || auto_idle[]) && enable_eventloop(false, wait_stopped = true) - try - f() - finally - (force || auto_idle[]) && enable_eventloop(was_running) + if GLib.simple_loop[] + pause_loop[] = true + try + f() + finally + pause_loop[] = false + end + else + was_running = is_eventloop_running() + (force || auto_idle[]) && enable_eventloop(false, wait_stopped = true) + try + f() + finally + (force || auto_idle[]) && enable_eventloop(was_running) + end end end @@ -228,7 +282,7 @@ end Check whether Gtk's event loop is running. """ -is_eventloop_running() = gtk_main_running[] +is_eventloop_running() = GLib.simple_loop[] ? !pause_loop[] : 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 d44760b4..6b9f15eb 100644 --- a/src/base.jl +++ b/src/base.jl @@ -22,7 +22,7 @@ visible(w::GtkWidget, state::Bool) = @sigatom ccall((:gtk_widget_set_visible, li const shown_widgets = WeakKeyDict() function handle_auto_idle(w::GtkWidget) - if auto_idle[] + if auto_idle[] && !GLib.simple_loop[] signal_connect(w, :realize) do w enable_eventloop(true) shown_widgets[w] = nothing diff --git a/src/cairo.jl b/src/cairo.jl index 23c85d2d..4f7965f2 100644 --- a/src/cairo.jl +++ b/src/cairo.jl @@ -115,6 +115,9 @@ function canvas_on_expose_event(::Ptr{GObject}, e::Ptr{Nothing}, widget::GtkCanv end function getgc(c::GtkCanvas) + while GLib.simple_loop[] && events_pending() # next step requires a realized canvas + iteration(true) + end if !isdefined(c,:backcc) error("GtkCanvas not yet initialized.") end diff --git a/test/misc.jl b/test/misc.jl index 347fed78..74a70c88 100644 --- a/test/misc.jl +++ b/test/misc.jl @@ -38,6 +38,8 @@ destroy(win) @test isa(Gtk.GdkEventKey(), Gtk.GdkEventKey) +if !Gtk.GLib.simple_loop[] + @testset "Eventloop control" begin before = Gtk.auto_idle[] @@ -64,4 +66,18 @@ destroy(win) Gtk.auto_idle[] = before end +else + +@testset "Eventloop control" begin + Gtk.enable_eventloop(true) + @test Gtk.is_eventloop_running() + + Gtk.pause_eventloop() do + @test !Gtk.is_eventloop_running() + end + @test Gtk.is_eventloop_running() +end + +end + end