diff --git a/src/dorkbox/util/jna/linux/GMainContext.java b/src/dorkbox/util/jna/linux/GMainContext.java new file mode 100644 index 0000000..a6733df --- /dev/null +++ b/src/dorkbox/util/jna/linux/GMainContext.java @@ -0,0 +1,30 @@ +/* + * Copyright 2017 dorkbox, llc + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package dorkbox.util.jna.linux; + +import com.sun.jna.Pointer; + +public +class GMainContext extends GObject { + public + GMainContext() { + } + + public + GMainContext(Pointer p) { + super(p); + } +} diff --git a/src/dorkbox/util/jna/linux/GMainLoop.java b/src/dorkbox/util/jna/linux/GMainLoop.java new file mode 100644 index 0000000..c47907d --- /dev/null +++ b/src/dorkbox/util/jna/linux/GMainLoop.java @@ -0,0 +1,30 @@ +/* + * Copyright 2017 dorkbox, llc + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package dorkbox.util.jna.linux; + +import com.sun.jna.Pointer; + +public +class GMainLoop extends GObject { + public + GMainLoop() { + } + + public + GMainLoop(Pointer p) { + super(p); + } +} diff --git a/src/dorkbox/util/jna/linux/GObject.java b/src/dorkbox/util/jna/linux/GObject.java new file mode 100644 index 0000000..b2fabdb --- /dev/null +++ b/src/dorkbox/util/jna/linux/GObject.java @@ -0,0 +1,47 @@ +/* + * Copyright 2017 dorkbox, llc + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package dorkbox.util.jna.linux; + +import com.sun.jna.Pointer; +import com.sun.jna.PointerType; + +public +class GObject extends PointerType { + public + GObject() { + } + + public + GObject(Pointer p) { + super(p); + } + + @Override + protected + void finalize() throws Throwable { + super.finalize(); + } + + public + void ref() { + Gobject.g_object_ref(getPointer()); + } + + public + void unref() { + Gobject.g_object_unref(getPointer()); + } +} diff --git a/src/dorkbox/util/jna/linux/Gobject.java b/src/dorkbox/util/jna/linux/Gobject.java index 7b60aad..3aa4f4c 100644 --- a/src/dorkbox/util/jna/linux/Gobject.java +++ b/src/dorkbox/util/jna/linux/Gobject.java @@ -44,6 +44,7 @@ class Gobject { // objdump -T /usr/lib/x86_64-linux-gnu/libgobject-2.0.so.0 | grep block + public static native void g_object_ref(Pointer object); public static native void g_object_unref(Pointer object); public static native void g_object_force_floating(Pointer object); diff --git a/src/dorkbox/util/jna/linux/Gtk.java b/src/dorkbox/util/jna/linux/Gtk.java index d8a89aa..0c454e5 100644 --- a/src/dorkbox/util/jna/linux/Gtk.java +++ b/src/dorkbox/util/jna/linux/Gtk.java @@ -57,18 +57,6 @@ interface Gtk { Function gtk_status_icon_position_menu = GtkLoader.gtk_status_icon_position_menu; - - - /** - * Adds a function to be called whenever there are no higher priority events pending. If the function returns FALSE it is automatically - * removed from the list of event sources and will not be called again. - *

- * This variant of g_idle_add_full() calls function with the GDK lock held. It can be thought of a MT-safe version for GTK+ widgets - * for the following use case, where you have to worry about idle_callback() running in thread A and accessing self after it has - * been finalized in thread B. - */ - int gdk_threads_add_idle_full(int priority, FuncCallback function, Pointer data, Pointer notify); - /** * This would NORMALLY have a 2nd argument that is a String[] -- however JNA direct-mapping DOES NOT support this. We are lucky * enough that we just pass 'null' as the second argument, therefore, we don't have to define that parameter here. @@ -76,18 +64,31 @@ interface Gtk { boolean gtk_init_check(int argc); /** - * Runs the main loop until gtk_main_quit() is called. You can nest calls to gtk_main(). In that case gtk_main_quit() will make the - * innermost invocation of the main loop return. + * Creates a new GMainLoop structure. */ - void gtk_main(); + GMainLoop g_main_loop_new(Pointer context, boolean is_running); /** - * Makes the innermost invocation of the main loop return when it regains control. ONLY CALL FROM THE GtkSupport class, UNLESS you know - * what you're doing! + * Runs a main loop until g_main_loop_quit() is called on the loop. If this is called for the thread of the loop's GMainContext, + * it will process events from the loop, otherwise it will simply wait. */ - void gtk_main_quit(); + void g_main_loop_run(GMainLoop loop); + /** + * Stops a GMainLoop from running. Any calls to g_main_loop_run() for the loop will return. + * Note that sources that have already been dispatched when g_main_loop_quit() is called will still be executed. + */ + void g_main_loop_quit(GMainLoop loop); + /** + * Returns the GMainContext of loop . + */ + GMainContext g_main_loop_get_context(GMainLoop loop); + + /** + * Invokes a function in such a way that context is owned during the invocation of function . + */ + void g_main_context_invoke(GMainContext c, FuncCallback func, Pointer data); /** * Creates a new GtkMenu diff --git a/src/dorkbox/util/jna/linux/Gtk2.java b/src/dorkbox/util/jna/linux/Gtk2.java index 766975c..c9d1df0 100644 --- a/src/dorkbox/util/jna/linux/Gtk2.java +++ b/src/dorkbox/util/jna/linux/Gtk2.java @@ -29,21 +29,29 @@ public class Gtk2 implements Gtk { // objdump -T /usr/lib/x86_64-linux-gnu/libgtk-x11-2.0.so.0 | grep gtk - @Override - public native - int gdk_threads_add_idle_full(final int priority, final FuncCallback function, final Pointer data, final Pointer notify); - @Override public native boolean gtk_init_check(final int argc); @Override public native - void gtk_main(); + GMainLoop g_main_loop_new(Pointer context, boolean is_running); @Override public native - void gtk_main_quit(); + GMainContext g_main_loop_get_context(GMainLoop loop); + + @Override + public native + void g_main_loop_run(GMainLoop loop); + + @Override + public native + void g_main_context_invoke(GMainContext c, FuncCallback func, Pointer data); + + @Override + public native + void g_main_loop_quit(GMainLoop loop); @Override public native diff --git a/src/dorkbox/util/jna/linux/Gtk3.java b/src/dorkbox/util/jna/linux/Gtk3.java index ec11227..2627c7b 100644 --- a/src/dorkbox/util/jna/linux/Gtk3.java +++ b/src/dorkbox/util/jna/linux/Gtk3.java @@ -128,21 +128,29 @@ class Gtk3 implements Gtk { this.gtk_widget_get_preferred_size(widget, requisition, null); } - @Override - public native - int gdk_threads_add_idle_full(final int priority, final FuncCallback function, final Pointer data, final Pointer notify); - @Override public native boolean gtk_init_check(final int argc); @Override public native - void gtk_main(); + GMainLoop g_main_loop_new(Pointer context, boolean is_running); @Override public native - void gtk_main_quit(); + GMainContext g_main_loop_get_context(GMainLoop loop); + + @Override + public native + void g_main_loop_run(GMainLoop loop); + + @Override + public native + void g_main_context_invoke(GMainContext c, FuncCallback func, Pointer data); + + @Override + public native + void g_main_loop_quit(GMainLoop loop); @Override public native diff --git a/src/dorkbox/util/jna/linux/GtkEventDispatch.java b/src/dorkbox/util/jna/linux/GtkEventDispatch.java index 8f43744..df5d903 100644 --- a/src/dorkbox/util/jna/linux/GtkEventDispatch.java +++ b/src/dorkbox/util/jna/linux/GtkEventDispatch.java @@ -52,6 +52,9 @@ class GtkEventDispatch { @SuppressWarnings("FieldCanBeLocal") private static Thread gtkUpdateThread = null; + private static GMainLoop mainloop; + private static GMainContext context; + // when debugging the EDT, we need a longer timeout. private static final boolean debugEDT = false; @@ -72,7 +75,6 @@ class GtkEventDispatch { // startup the GTK GUI event loop. There can be multiple/nested loops. - if (!GtkLoader.alreadyRunningGTK) { // If JavaFX/SWT is used, this is UNNECESSARY (we can detect if the GTK main_loop is running) @@ -91,24 +93,22 @@ class GtkEventDispatch { } - // prep for the event loop. - // GThread.g_thread_init(null); would be needed for g_idle_add() - if (!Gtk2.gtk_init_check(0)) { throw new RuntimeException("Error starting GTK"); } - // gdk_threads_enter(); would be needed for g_idle_add() + + // create the main-loop + mainloop = Gtk2.g_main_loop_new(null, false); + context = Gtk2.g_main_loop_get_context(mainloop); + + // blocks until we quit the main loop + Gtk2.g_main_loop_run(mainloop); + if (orig != null) { Glib.g_log_set_default_handler(orig, null); } - - // blocks unit quit - Gtk2.gtk_main(); - - // clean up threads - // gdk_threads_leave(); would be needed for g_idle_add() } }; gtkUpdateThread.setDaemon(false); // explicitly NOT daemon so that this will hold the JVM open as necessary @@ -302,10 +302,6 @@ class GtkEventDispatch { @Override public int callback(final Pointer data) { - synchronized (gtkCallbacks) { - gtkCallbacks.removeFirst(); // now that we've 'handled' it, we can remove it from our callback list - } - isDispatch.set(true); try { @@ -314,6 +310,10 @@ class GtkEventDispatch { isDispatch.set(false); } + synchronized (gtkCallbacks) { + gtkCallbacks.removeFirst(); // now that we've 'handled' it, we can remove it from our callback list + } + return Gtk2.FALSE; // don't want to call this again } }; @@ -322,8 +322,8 @@ class GtkEventDispatch { gtkCallbacks.offer(callback); // prevent GC from collecting this object before it can be called } - // the correct way to do it. Add with a slightly higher value - Gtk2.gdk_threads_add_idle_full(100, callback, null, null); + // explicitly invoke on our new GTK main loop context + Gtk2.g_main_context_invoke(context, callback, null); } /** @@ -352,7 +352,8 @@ class GtkEventDispatch { void run() { // If JavaFX/SWT is used, this is UNNECESSARY (and will break SWT/JavaFX shutdown) if (!GtkLoader.alreadyRunningGTK) { - Gtk2.gtk_main_quit(); + Gtk2.g_main_loop_quit(mainloop); + // Gtk2.gtk_main_quit(); } started = false;