Anklang 0.3.0-460-gc4ef46ba
ASE — Anklang Sound Engine (C++)

« « « Anklang Documentation
Loading...
Searching...
No Matches
gtk2wrap.cc
Go to the documentation of this file.
1 // This Source Code Form is licensed MPL-2.0: http://mozilla.org/MPL/2.0
2#include "gtk2wrap.hh"
3#include <gtk/gtk.h>
4#include <semaphore.h>
5#include <thread>
6
7namespace { // Anon
8using namespace Ase;
9
10static std::thread *gtkthred = nullptr;
11
12// == Semaphore ==
13class Semaphore {
14 /*copy*/ Semaphore (const Semaphore&) = delete;
15 Semaphore& operator= (const Semaphore&) = delete;
16 sem_t sem = {};
17public:
18 explicit Semaphore () noexcept { const int err = sem_init (&sem, 0, 0); g_return_if_fail (!err); }
19 void post () noexcept { sem_post (&sem); }
20 void wait () noexcept { sem_wait (&sem); }
21 /*dtor*/ ~Semaphore () noexcept { sem_destroy (&sem); }
22};
23
24// == GBoolean lambda callback ==
25using BWrapFunc = std::function<bool()>;
26struct BWrap {
27 void *data = this;
28 void (*deleter) (void*) = [] (void *data) {
29 BWrap *self = (BWrap*) data;
30 self->deleter = nullptr;
31 delete self;
32 };
33 gboolean (*boolfunc) (void*) = [] (void *data) -> int {
34 BWrap *self = (BWrap*) data;
35 return self->func();
36 };
37 gboolean (*truefunc) (void*) = [] (void *data) -> int {
38 BWrap *self = (BWrap*) data;
39 self->func();
40 return true;
41 };
42 gboolean (*falsefunc) (void*) = [] (void *data) -> int {
43 BWrap *self = (BWrap*) data;
44 self->func();
45 return true;
46 };
47 const BWrapFunc func;
48 BWrap (const BWrapFunc &fun) : func (fun) {}
49};
50static BWrap* bwrap (const BWrapFunc &fun) {
51 return new BWrap (fun);
52}
53static BWrap* vwrap (const std::function<void()> &fun) {
54 return new BWrap ([fun]() { fun(); return false; });
55}
56
57// == functions ==
58static void
59gtkmain()
60{
61 pthread_setname_np (pthread_self(), "gtk2wrap:thread");
62 gdk_threads_init();
63 gdk_threads_enter();
64
65 if (1) {
66 int argc = 0;
67 char **argv = nullptr;
68 gtk_init (&argc, &argv);
69 }
70
71 gtk_main();
72 gdk_threads_leave();
73}
74
76
77static ulong
78create_window (const Gtk2WindowSetup &wsetup)
79{
80 GtkWidget *window = gtk_window_new (GTK_WINDOW_TOPLEVEL);
81 // g_signal_connect (window, "destroy", gtk_main_quit, nullptr);
82 if (wsetup.width > 0 && wsetup.height > 0)
83 gtk_window_set_resizable (GTK_WINDOW (window), false);
84 if (wsetup.deleterequest_mt) {
85 BWrap *bw = vwrap (wsetup.deleterequest_mt);
86 g_signal_connect_data (window, "delete-event", (GCallback) bw->truefunc, bw, (GClosureNotify) bw->deleter, G_CONNECT_SWAPPED);
87 }
88 GtkWidget *socket = gtk_socket_new();
89 gtk_container_add (GTK_CONTAINER (window), socket);
90 gtk_widget_set_size_request (socket, wsetup.width, wsetup.height);
91 gtk_widget_realize (socket);
92 ulong windowid = gtk_socket_get_id (GTK_SOCKET (socket));
93 windows[windowid] = window;
94 gtk_widget_show_all (gtk_bin_get_child (GTK_BIN (window)));
95 gtk_window_set_title (GTK_WINDOW (window), wsetup.title.c_str());
96 return windowid;
97}
98
99static bool
100destroy_window (ulong windowid)
101{
102 auto it = windows.find (windowid);
103 if (it == windows.end()) return false;
104 GtkWidget *window = it->second;
105 windows.erase (it);
106 gtk_widget_destroy (window);
107 return true;
108}
109
110static bool
111resize_window (ulong windowid, int width, int height)
112{
113 auto it = windows.find (windowid);
114 if (it == windows.end()) return false;
115 GtkWidget *window = it->second;
116 gtk_widget_set_size_request (gtk_bin_get_child (GTK_BIN (window)), width, height);
117 return true;
118}
119
120static bool
121show_window (ulong windowid)
122{
123 auto it = windows.find (windowid);
124 if (it == windows.end()) return false;
125 GtkWidget *window = it->second;
126 gtk_widget_show (window);
127 return true;
128}
129
130static bool
131hide_window (ulong windowid)
132{
133 auto it = windows.find (windowid);
134 if (it == windows.end()) return false;
135 GtkWidget *window = it->second;
136 gtk_widget_hide (window);
137 return true;
138}
139
140template<typename Ret, typename ...Args, typename ...Params> static Ret
141gtkidle_call (Ret (*func) (Params...), Args &&...args)
142{
143 if (!gtkthred) // TODO: clean this up on process exit
144 gtkthred = new std::thread (gtkmain);
145 Semaphore sem;
146 Ret ret = {};
147 BWrap *bw = bwrap ([&sem, &ret, func, &args...] () -> bool {
148 GDK_THREADS_ENTER ();
149 ret = func (std::forward<Args> (args)...);
150 GDK_THREADS_LEAVE ();
151 sem.post();
152 return false;
153 });
154 g_idle_add_full (G_PRIORITY_HIGH, bw->boolfunc, bw, bw->deleter);
155 // See gdk_threads_add_idle_full for LEAVE/ENTER reasoning
156 sem.wait();
157 return ret;
158}
159
160} // Anon
161
162extern "C" {
163Ase::Gtk2DlWrapEntry Ase__Gtk2__wrapentry {
164 .create_window = [] (const Gtk2WindowSetup &windowsetup) -> ulong {
165 return gtkidle_call (create_window, windowsetup);
166 },
167 .resize_window = [] (ulong windowid, int width, int height) {
168 return gtkidle_call (resize_window, windowid, width, height);
169 },
170 .show_window = [] (ulong windowid) {
171 gtkidle_call (show_window, windowid);
172 },
173 .hide_window = [] (ulong windowid) {
174 gtkidle_call (hide_window, windowid);
175 },
176 .destroy_window = [] (ulong windowid) {
177 gtkidle_call (destroy_window, windowid);
178 },
179 .threads_enter = gdk_threads_enter,
180 .threads_leave = gdk_threads_leave,
181};
182} // "C"
T c_str(T... args)
T data(T... args)
The Anklang C++ API namespace.
Definition api.hh:9
pthread_self
sem_destroy
sem_init
sem_post
sem_wait
socket
wait