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