1 /*
2   Copyright 2011-2015 David Robillard <http://drobilla.net>
3 
4   Permission to use, copy, modify, and/or distribute this software for any
5   purpose with or without fee is hereby granted, provided that the above
6   copyright notice and this permission notice appear in all copies.
7 
8   THIS SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
9   WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
10   MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
11   ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
12   WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
13   ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
14   OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
15 */
16 
17 /* TODO make it GTK3 compatible */
18 
19 #include "zrythm-config.h"
20 
21 #ifdef _WOE32
22 
23 #include <string.h>
24 
25 #include <gtk/gtk.h>
26 #include <gdk/gdkwin32.h>
27 
28 #ifndef WM_MOUSEWHEEL
29 #    define WM_MOUSEWHEEL 0x020A
30 #endif
31 #ifndef WM_MOUSEHWHEEL
32 #    define WM_MOUSEHWHEEL 0x020E
33 #endif
34 
35 #include <suil/suil.h>
36 
37 #include "lv2/options/options.h"
38 #include "lv2/urid/urid.h"
39 
40 extern "C" {
41 
42 #define SUIL_TYPE_WIN_WRAPPER (suil_win_wrapper_get_type())
43 #define SUIL_WIN_WRAPPER(obj) (G_TYPE_CHECK_INSTANCE_CAST((obj), SUIL_TYPE_WIN_WRAPPER, SuilWinWrapper))
44 
45 typedef struct _SuilWinWrapper      SuilWinWrapper;
46 typedef struct _SuilWinWrapperClass SuilWinWrapperClass;
47 
48 struct _SuilWinWrapper {
49 	GtkDrawingArea              area;
50 	SuilWrapper*                wrapper;
51 	SuilInstance*               instance;
52 	GdkWindow*                  flt_win;
53 	const LV2UI_Idle_Interface* idle_iface;
54 	guint                       idle_id;
55 	guint                       idle_ms;
56 };
57 
58 struct _SuilWinWrapperClass {
59 	GtkDrawingAreaClass parent_class;
60 };
61 
62 GType suil_win_wrapper_get_type(void);  // Accessor for SUIL_TYPE_WIN_WRAPPER
63 
G_DEFINE_TYPE(SuilWinWrapper,suil_win_wrapper,GTK_TYPE_DRAWING_AREA)64 G_DEFINE_TYPE(SuilWinWrapper, suil_win_wrapper, GTK_TYPE_DRAWING_AREA)
65 
66 static void
67 suil_win_wrapper_finalize(GObject* gobject)
68 {
69 	SuilWinWrapper* const self = SUIL_WIN_WRAPPER(gobject);
70 
71 	self->wrapper->impl = NULL;
72 	self->instance      = NULL;
73 
74 	G_OBJECT_CLASS(suil_win_wrapper_parent_class)->finalize(gobject);
75 }
76 
77 static void
suil_win_size_allocate(GtkWidget * widget,GtkAllocation * allocation)78 suil_win_size_allocate(GtkWidget* widget, GtkAllocation* allocation)
79 {
80 	SuilWinWrapper* const self = SUIL_WIN_WRAPPER(widget);
81 	g_return_if_fail(self != NULL);
82 
83   gtk_widget_set_allocation (
84     widget, allocation);
85 	if (gtk_widget_get_realized(widget))
86     {
87       GdkWindow * gdk_window =
88         gtk_widget_get_window (widget);
89 		gdk_window_move_resize(gdk_window,
90 		                       allocation->x, allocation->y,
91 		                       allocation->width, allocation->height);
92 
93 		RECT wr = { 0, 0, (long)allocation->width, (long)allocation->height };
94 		AdjustWindowRectEx(&wr, WS_CHILD, FALSE, WS_EX_TOPMOST);
95 
96 		SetWindowPos((HWND)self->instance->ui_widget, HWND_NOTOPMOST,
97 		             0, 0, wr.right - wr.left, wr.bottom - wr.top,
98 		             SWP_NOACTIVATE|SWP_NOMOVE|SWP_NOOWNERZORDER|SWP_NOZORDER);
99 		UpdateWindow((HWND)self->instance->ui_widget);
100 		PostMessage((HWND)self->instance->ui_widget, WM_PAINT, 0, 0);
101 	}
102 }
103 
104 static void
suil_win_wrapper_class_init(SuilWinWrapperClass * klass)105 suil_win_wrapper_class_init(SuilWinWrapperClass* klass)
106 {
107 	GObjectClass* const   gobject_class = G_OBJECT_CLASS(klass);
108 	GtkWidgetClass* const widget_class  = (GtkWidgetClass*)(klass);
109 
110 	widget_class->size_allocate = suil_win_size_allocate;
111 	gobject_class->finalize     = suil_win_wrapper_finalize;
112 }
113 
114 static void
suil_win_wrapper_init(SuilWinWrapper * self)115 suil_win_wrapper_init(SuilWinWrapper* self)
116 {
117 	self->instance   = NULL;
118 	self->flt_win    = NULL;
119 	self->idle_iface = NULL;
120 	self->idle_ms    = 1000 / 30;  // 30 Hz default
121 }
122 
123 static gboolean
suil_win_wrapper_idle(void * data)124 suil_win_wrapper_idle(void* data)
125 {
126 	SuilWinWrapper* const wrap = SUIL_WIN_WRAPPER(data);
127 	wrap->idle_iface->idle(wrap->instance->handle);
128 	return TRUE;  // Continue calling
129 }
130 
131 static int
wrapper_resize(LV2UI_Feature_Handle handle,int width,int height)132 wrapper_resize(LV2UI_Feature_Handle handle, int width, int height)
133 {
134 	gtk_widget_set_size_request (
135     GTK_WIDGET(handle), width, height);
136 	return 0;
137 }
138 
139 static int
wrapper_wrap(SuilWrapper * wrapper,SuilInstance * instance)140 wrapper_wrap(SuilWrapper*  wrapper,
141              SuilInstance* instance)
142 {
143 	SuilWinWrapper* const wrap = SUIL_WIN_WRAPPER(wrapper->impl);
144 
145 	instance->host_widget = GTK_WIDGET(wrap);
146 	wrap->wrapper         = wrapper;
147 	wrap->instance        = instance;
148 
149 	const LV2UI_Idle_Interface* idle_iface = NULL;
150 	if (instance->descriptor->extension_data) {
151 		idle_iface = (const LV2UI_Idle_Interface*)
152 			instance->descriptor->extension_data(LV2_UI__idleInterface);
153 	}
154 	if (idle_iface) {
155 		wrap->idle_iface = idle_iface;
156 		wrap->idle_id    = g_timeout_add (wrap->idle_ms, suil_win_wrapper_idle, wrap);
157 	}
158 
159 	return 0;
160 }
161 
162 static GdkFilterReturn
event_filter(GdkXEvent * xevent,GdkEvent * event,gpointer data)163 event_filter(GdkXEvent* xevent, GdkEvent* event, gpointer data)
164 {
165 	SuilWinWrapper* wrap = (SuilWinWrapper*)data;
166 	MSG*            msg  = (MSG*)xevent;
167 	if (msg->message == WM_KEYDOWN || msg->message == WM_KEYUP) {
168 		// Forward keyboard events to UI window
169 		PostMessage((HWND)wrap->instance->ui_widget,
170 		            msg->message, msg->wParam, msg->lParam);
171 		return GDK_FILTER_REMOVE;
172 	} else if (msg->message == WM_MOUSEWHEEL || msg->message == WM_MOUSEHWHEEL) {
173 		PostMessage((HWND)wrap->instance->ui_widget,
174 		            msg->message, msg->wParam, msg->lParam);
175 		return GDK_FILTER_REMOVE;
176 	}
177 	return GDK_FILTER_CONTINUE;
178 }
179 
180 static void
wrapper_free(SuilWrapper * wrapper)181 wrapper_free(SuilWrapper* wrapper)
182 {
183 	if (wrapper->impl) {
184 		SuilWinWrapper* const wrap = SUIL_WIN_WRAPPER(wrapper->impl);
185 		if (wrap->idle_id) {
186 			g_source_remove(wrap->idle_id);
187 			wrap->idle_id = 0;
188 		}
189 
190 		gdk_window_remove_filter(wrap->flt_win, event_filter, wrapper->impl);
191 		gtk_widget_destroy (GTK_WIDGET(wrap));
192 	}
193 }
194 
195 SuilWrapper*
suil_wrapper_new_woe(SuilHost * host,const char * host_type_uri,const char * ui_type_uri,LV2_Feature *** features,unsigned n_features)196 suil_wrapper_new_woe (SuilHost*      host,
197                  const char*    host_type_uri,
198                  const char*    ui_type_uri,
199                  LV2_Feature*** features,
200                  unsigned       n_features)
201 {
202 	GtkWidget* parent = NULL;
203 	for (unsigned i = 0; i < n_features; ++i) {
204 		if (!strcmp((*features)[i]->URI, LV2_UI__parent)) {
205 			parent = (GtkWidget*)(*features)[i]->data;
206 		}
207 	}
208 
209 	if (!GTK_CONTAINER(parent)) {
210 		SUIL_ERRORF("No GtkContainer parent given for %s UI\n",
211 		            ui_type_uri);
212 		return NULL;
213 	}
214 
215 	SuilWrapper* wrapper = (SuilWrapper*)calloc(1, sizeof(SuilWrapper));
216 	wrapper->wrap = wrapper_wrap;
217 	wrapper->free = wrapper_free;
218 
219 	SuilWinWrapper* const wrap = SUIL_WIN_WRAPPER(
220 		g_object_new(SUIL_TYPE_WIN_WRAPPER, NULL));
221 
222 	wrap->wrapper = NULL;
223 
224 	wrapper->impl             = wrap;
225 	wrapper->resize.handle    = wrap;
226 	wrapper->resize.ui_resize = wrapper_resize;
227 
228 	gtk_container_add(GTK_CONTAINER(parent), GTK_WIDGET(wrap));
229 	gtk_widget_set_can_focus(GTK_WIDGET(wrap), TRUE);
230 	gtk_widget_set_sensitive(GTK_WIDGET(wrap), TRUE);
231 	gtk_widget_realize(GTK_WIDGET(wrap));
232 
233 	GdkWindow* window = gtk_widget_get_window(GTK_WIDGET(wrap));
234 
235 	wrap->flt_win = gtk_widget_get_window(parent);
236 	gdk_window_add_filter(wrap->flt_win, event_filter, wrap);
237 
238 	HWND parent_window = (HWND)GDK_WINDOW_HWND(window);
239 	suil_add_feature(features, &n_features, LV2_UI__parent, parent_window);
240 	suil_add_feature(features, &n_features, LV2_UI__resize, &wrapper->resize);
241 	suil_add_feature(features, &n_features, LV2_UI__idleInterface, NULL);
242 
243 	// Scan for URID map and options
244 	LV2_URID_Map*       map     = NULL;
245 	LV2_Options_Option* options = NULL;
246 	for (LV2_Feature** f = *features; *f && (!map || !options); ++f) {
247 		if (!strcmp((*f)->URI, LV2_OPTIONS__options)) {
248 			options = (LV2_Options_Option *)(*f)->data;
249 		} else if (!strcmp((*f)->URI, LV2_URID__map)) {
250 			map = (LV2_URID_Map *)(*f)->data;
251 		}
252 	}
253 
254 	if (map && options) {
255 		// Set UI update rate if given
256 		LV2_URID ui_updateRate = map->map(map->handle, LV2_UI__updateRate);
257 		for (LV2_Options_Option* o = options; o->key; ++o) {
258 			if (o->key == ui_updateRate) {
259 				wrap->idle_ms = 1000.0f / *(const float*)o->value;
260 				break;
261 			}
262 		}
263 	}
264 
265 	return wrapper;
266 }
267 
268 }  // extern "C"
269 
270 #endif
271