1 //-----------------------------------------------------------------------------
2 // Flags : clang-format auto
3 // Project : VST SDK
4 //
5 // Category : EditorHost
6 // Filename : public.sdk/samples/vst-hosting/editorhost/source/platform/linux/window.cpp
7 // Created by : Steinberg 09.2016
8 // Description : Example of opening a plug-in editor
9 //
10 //-----------------------------------------------------------------------------
11 // LICENSE
12 // (c) 2020, Steinberg Media Technologies GmbH, All Rights Reserved
13 //-----------------------------------------------------------------------------
14 // Redistribution and use in source and binary forms, with or without modification,
15 // are permitted provided that the following conditions are met:
16 //
17 // * Redistributions of source code must retain the above copyright notice,
18 // this list of conditions and the following disclaimer.
19 // * Redistributions in binary form must reproduce the above copyright notice,
20 // this list of conditions and the following disclaimer in the documentation
21 // and/or other materials provided with the distribution.
22 // * Neither the name of the Steinberg Media Technologies nor the names of its
23 // contributors may be used to endorse or promote products derived from this
24 // software without specific prior written permission.
25 //
26 // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
27 // ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
28 // WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
29 // IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
30 // INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
31 // BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
32 // DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
33 // LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
34 // OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
35 // OF THE POSSIBILITY OF SUCH DAMAGE.
36 //-----------------------------------------------------------------------------
37
38 #include "window.h"
39
40 #ifdef EDITORHOST_GTK
41 #include <gtkmm.h>
42 #endif
43
44 #include "public.sdk/source/vst/utility/stringconvert.h"
45 #include <X11/Xutil.h>
46 #include <cassert>
47 #include <iostream>
48 #include <unordered_map>
49
50 #ifdef EDITORHOST_GTK
51 #include <gdk/gdkx.h>
52 #include <gtkmm/socket.h>
53 #else
54 #include "public.sdk/samples/vst-hosting/editorhost/source/platform/linux/runloop.h"
55 #include <X11/Xlib.h>
56 #endif
57
58 //------------------------------------------------------------------------
59 namespace Steinberg {
60 namespace Vst {
61 namespace EditorHost {
62
63 #ifdef EDITORHOST_GTK
64
65 struct ContentView : Gtk::Fixed
66 {
on_size_allocateSteinberg::Vst::EditorHost::ContentView67 void on_size_allocate (Gtk::Allocation& allocation) override
68 {
69 Gtk::Widget::on_size_allocate (allocation);
70 for (auto child : get_children ())
71 {
72 child->size_allocate (allocation);
73 }
74 }
75 };
76
77 struct WindowGtk : Gtk::Window
78 {
WindowGtkSteinberg::Vst::EditorHost::WindowGtk79 WindowGtk ()
80 {
81 add (container);
82 container.put (label, 0, 0);
83 container.add (socket);
84 show_all_children ();
85 }
86
on_showSteinberg::Vst::EditorHost::WindowGtk87 void on_show () override
88 {
89 Gtk::Window::on_show ();
90 if (controller)
91 controller->onShow (*window);
92 }
93
on_unmapSteinberg::Vst::EditorHost::WindowGtk94 void on_unmap () override
95 {
96 Gtk::Window::on_unmap ();
97 if (controller)
98 controller->onClose (*window);
99 if (windowClosedFunc)
100 windowClosedFunc (window);
101 }
102
on_configure_eventSteinberg::Vst::EditorHost::WindowGtk103 bool on_configure_event (GdkEventConfigure* event) override
104 {
105 // TODO: does not work yet
106 Gtk::Window::on_configure_event (event);
107 return false;
108 if (controller)
109 {
110 if (currentSize.width != event->width || currentSize.height != event->height)
111 {
112 Size newSize {event->width, event->height};
113 auto size = controller->constrainSize (*window, newSize);
114 if (size != newSize)
115 {
116 resize (size.width, size.height);
117 }
118 }
119 }
120 return true;
121 }
122
on_size_allocateSteinberg::Vst::EditorHost::WindowGtk123 void on_size_allocate (Gtk::Allocation& allocation) override
124 {
125 auto size = Size {allocation.get_width (), allocation.get_height ()};
126 if (size != currentSize)
127 {
128 if (controller)
129 {
130 size = controller->constrainSize (*window, size);
131 controller->onResize (*window, size);
132 }
133 currentSize = size;
134 allocation.set_width (currentSize.width);
135 allocation.set_height (currentSize.height);
136 resize (currentSize.width, currentSize.height);
137 }
138 Gtk::Window::on_size_allocate (allocation);
139 }
140
checkSizeSteinberg::Vst::EditorHost::WindowGtk141 void checkSize ()
142 {
143 int width, height;
144 get_size (width, height);
145 if (currentSize.width != width || currentSize.height != height)
146 {
147 resize (currentSize.width, currentSize.height);
148 }
149 }
150
getIdSteinberg::Vst::EditorHost::WindowGtk151 ::Window getId ()
152 {
153 if (!socket.is_visible ())
154 return 0;
155 std::cout << "Socket ID: " << socket.get_id () << "\n";
156 std::cout.flush ();
157 return socket.get_id ();
158 }
159
160 X11Window::WindowClosedFunc windowClosedFunc;
161 WindowControllerPtr controller {nullptr};
162 X11Window* window {nullptr};
163 Size currentSize;
164 ContentView container;
165 Gtk::Label label {"HostLabel"};
166 Gtk::Socket socket;
167 };
168 #else
169
170 #endif
171
172 //------------------------------------------------------------------------
173 struct X11Window::Impl : public Linux::IRunLoop
174 {
175 Impl (X11Window* x11Window);
176 bool init (const std::string& name, Size size, bool resizeable,
177 const WindowControllerPtr& controller, Display* display,
178 const WindowClosedFunc& windowClosedFunc);
179
180 void show ();
181 void close ();
182 Size getSize () const;
183 void resize (Size newSize, bool force);
184
185 #ifdef EDITORHOST_GTK
186 WindowGtk window;
187
188 //------------------------------------------------------------------------
189 struct EventHandler
190 {
191 IPtr<Linux::IEventHandler> handler;
192 GIOChannel* ioChannel;
193 };
194
195 //------------------------------------------------------------------------
196 using TimerHandler = IPtr<Linux::ITimerHandler>;
197 using TimerID = guint;
198 #else
199 //------------------------------------------------------------------------
200 struct XEmbedInfo
201 {
202 uint32_t version;
203 uint32_t flags;
204 };
205
206 ~Impl ();
207 void onClose ();
208 bool handleMainWindowEvent (const XEvent& event);
209 bool handlePlugEvent (const XEvent& event);
210 XEmbedInfo* getXEmbedInfo ();
211 void checkSize ();
212 void callPlugEventHandlers ();
213
214 WindowControllerPtr controller {nullptr};
215 WindowClosedFunc windowClosedFunc;
216 Display* xDisplay {nullptr};
217 XEmbedInfo* xembedInfo {nullptr};
218 Window xWindow {};
219 Window plugParentWindow {};
220 Window plugWindow {};
221 GC xGraphicContext {};
222 Atom xEmbedInfoAtom {None};
223 Atom xEmbedAtom {None};
224 bool isMapped {false};
225
226 using EventHandler = IPtr<Linux::IEventHandler>;
227 using TimerHandler = IPtr<Linux::ITimerHandler>;
228 #endif
229 Size mCurrentSize {};
230 X11Window* x11Window {nullptr};
231
232 using EventHandlers = std::unordered_map<Linux::FileDescriptor, EventHandler>;
233 EventHandlers eventHandlers;
234
235 using TimerHandlers = std::unordered_map<TimerID, TimerHandler>;
236 TimerHandlers timerHandlers;
237
238 tresult PLUGIN_API registerEventHandler (Linux::IEventHandler* handler,
239 Linux::FileDescriptor fd) override;
240 tresult PLUGIN_API unregisterEventHandler (Linux::IEventHandler* handler) override;
241 tresult PLUGIN_API registerTimer (Linux::ITimerHandler* handler,
242 Linux::TimerInterval milliseconds) override;
243 tresult PLUGIN_API unregisterTimer (Linux::ITimerHandler* handler) override;
244
addRefSteinberg::Vst::EditorHost::X11Window::Impl245 uint32 PLUGIN_API addRef () override { return 1000; }
releaseSteinberg::Vst::EditorHost::X11Window::Impl246 uint32 PLUGIN_API release () override { return 1000; }
queryInterfaceSteinberg::Vst::EditorHost::X11Window::Impl247 tresult PLUGIN_API queryInterface (const TUID, void**) override { return kNoInterface; }
248 };
249
250 //------------------------------------------------------------------------
make(const std::string & name,Size size,bool resizeable,const WindowControllerPtr & controller,Display * display,const WindowClosedFunc & windowClosedFunc)251 auto X11Window::make (const std::string& name, Size size, bool resizeable,
252 const WindowControllerPtr& controller, Display* display,
253 const WindowClosedFunc& windowClosedFunc) -> Ptr
254 {
255 auto window = std::make_shared<X11Window> ();
256 if (window->init (name, size, resizeable, controller, display, windowClosedFunc))
257 {
258 return window;
259 }
260 return nullptr;
261 }
262
263 //------------------------------------------------------------------------
X11Window()264 X11Window::X11Window ()
265 {
266 impl = std::unique_ptr<Impl> (new Impl (this));
267 }
268
269 //------------------------------------------------------------------------
~X11Window()270 X11Window::~X11Window ()
271 {
272 }
273
274 //------------------------------------------------------------------------
init(const std::string & name,Size size,bool resizeable,const WindowControllerPtr & controller,Display * display,const WindowClosedFunc & windowClosedFunc)275 bool X11Window::init (const std::string& name, Size size, bool resizeable,
276 const WindowControllerPtr& controller, Display* display,
277 const WindowClosedFunc& windowClosedFunc)
278 {
279 return impl->init (name, size, resizeable, controller, display, windowClosedFunc);
280 }
281
282 //------------------------------------------------------------------------
getSize() const283 Size X11Window::getSize () const
284 {
285 return impl->getSize ();
286 }
287
288 //------------------------------------------------------------------------
show()289 void X11Window::show ()
290 {
291 impl->show ();
292 }
293
294 //------------------------------------------------------------------------
close()295 void X11Window::close ()
296 {
297 impl->close ();
298 }
299
300 //------------------------------------------------------------------------
resize(Size newSize)301 void X11Window::resize (Size newSize)
302 {
303 impl->resize (newSize, false);
304 }
305
306 //------------------------------------------------------------------------
getContentSize()307 Size X11Window::getContentSize ()
308 {
309 return {};
310 }
311
312 //------------------------------------------------------------------------
getNativePlatformWindow() const313 NativePlatformWindow X11Window::getNativePlatformWindow () const
314 {
315 #ifdef EDITORHOST_GTK
316 return {kPlatformTypeX11EmbedWindowID, reinterpret_cast<void*> (impl->window.getId ())};
317 #else
318 return {kPlatformTypeX11EmbedWindowID, reinterpret_cast<void*> (impl->plugParentWindow)};
319 #endif
320 }
321
322 //------------------------------------------------------------------------
queryInterface(const TUID iid,void ** obj)323 tresult X11Window::queryInterface (const TUID iid, void** obj)
324 {
325 if (FUnknownPrivate::iidEqual (iid, Linux::IRunLoop::iid))
326 {
327 *obj = impl.get ();
328 return kResultTrue;
329 }
330 return kNoInterface;
331 }
332
333 //------------------------------------------------------------------------
onIdle()334 void X11Window::onIdle ()
335 {
336 #ifdef EDITORHOST_GTK
337 impl->window.checkSize ();
338 #endif
339 }
340
341 #ifdef EDITORHOST_GTK
342 //------------------------------------------------------------------------
Impl(X11Window * x11Window)343 X11Window::Impl::Impl (X11Window* x11Window) : x11Window (x11Window)
344 {
345 }
346
347 //------------------------------------------------------------------------
init(const std::string & name,Size size,bool resizeable,const WindowControllerPtr & controller,Display * display,const WindowClosedFunc & windowClosedFunc)348 bool X11Window::Impl::init (const std::string& name, Size size, bool resizeable,
349 const WindowControllerPtr& controller, Display* display,
350 const WindowClosedFunc& windowClosedFunc)
351 {
352 window.set_title (name.data ());
353 window.set_default_size (size.width, size.height);
354 window.set_resizable (resizeable);
355 window.show ();
356 window.set_visible (false);
357
358 window.window = x11Window;
359 window.controller = controller;
360 window.windowClosedFunc = windowClosedFunc;
361
362 return true;
363 }
364
365 //------------------------------------------------------------------------
show()366 void X11Window::Impl::show ()
367 {
368 window.set_visible (true);
369 window.show ();
370 }
371
372 //------------------------------------------------------------------------
close()373 void X11Window::Impl::close ()
374 {
375 window.close ();
376 }
377
378 //------------------------------------------------------------------------
getSize() const379 Size X11Window::Impl::getSize () const
380 {
381 int width, height;
382 window.get_size (width, height);
383 return {width, height};
384 }
385
386 //------------------------------------------------------------------------
resize(Size newSize,bool force)387 void X11Window::Impl::resize (Size newSize, bool force)
388 {
389 if (!force && mCurrentSize == newSize)
390 return;
391 window.resize (newSize.width, newSize.height);
392 mCurrentSize = newSize;
393 }
394
395 //------------------------------------------------------------------------
registerEventHandler(Linux::IEventHandler * handler,Linux::FileDescriptor fd)396 tresult PLUGIN_API X11Window::Impl::registerEventHandler (Linux::IEventHandler* handler,
397 Linux::FileDescriptor fd)
398 {
399 if (!handler)
400 return kInvalidArgument;
401
402 if (eventHandlers.find (fd) != eventHandlers.end ())
403 return kInvalidArgument;
404
405 auto display = gdk_window_get_display (window.get_window ()->gobj ());
406 auto xDisplay = gdk_x11_display_get_xdisplay (display);
407 if (XConnectionNumber (xDisplay) == fd)
408 {
409 // the plug-in is using GTK as well, so no need in registering it as event handler
410 return kResultTrue;
411 }
412
413 auto ioChannel = g_io_channel_unix_new (fd);
414 g_io_add_watch (ioChannel, (GIOCondition) (G_IO_IN | G_IO_OUT | G_IO_ERR | G_IO_HUP),
415 [] (auto* source, auto condition, auto data) {
416 (void)condition;
417 auto handler = reinterpret_cast<Linux::IEventHandler*> (data);
418 handler->onFDIsSet (g_io_channel_unix_get_fd (source));
419 return static_cast<int> (0);
420 },
421 handler);
422
423 eventHandlers[fd] = {handler, ioChannel};
424
425 return kResultTrue;
426 }
427
428 //------------------------------------------------------------------------
unregisterEventHandler(Linux::IEventHandler * handler)429 tresult PLUGIN_API X11Window::Impl::unregisterEventHandler (Linux::IEventHandler* handler)
430 {
431 if (!handler)
432 return kInvalidArgument;
433
434 for (auto it = eventHandlers.begin (), end = eventHandlers.end (); it != end; ++it)
435 {
436 if (it->second.handler == handler)
437 {
438 g_io_channel_unref (it->second.ioChannel);
439 eventHandlers.erase (it);
440 return kResultTrue;
441 }
442 }
443
444 return kResultFalse;
445 }
446
447 //------------------------------------------------------------------------
registerTimer(Linux::ITimerHandler * handler,Linux::TimerInterval milliseconds)448 tresult PLUGIN_API X11Window::Impl::registerTimer (Linux::ITimerHandler* handler,
449 Linux::TimerInterval milliseconds)
450 {
451 if (!handler || milliseconds == 0)
452 return kInvalidArgument;
453
454 auto id = g_timeout_add (milliseconds,
455 [] (auto data) {
456 auto handler = reinterpret_cast<Linux::ITimerHandler*> (data);
457 handler->onTimer ();
458 return static_cast<gboolean> (true);
459 },
460 handler);
461 timerHandlers.emplace (id, handler);
462 return kResultTrue;
463 }
464
465 //------------------------------------------------------------------------
unregisterTimer(Linux::ITimerHandler * handler)466 tresult PLUGIN_API X11Window::Impl::unregisterTimer (Linux::ITimerHandler* handler)
467 {
468 if (!handler)
469 return kInvalidArgument;
470
471 for (auto it = timerHandlers.begin (), end = timerHandlers.end (); it != end; ++it)
472 {
473 if (it->second == handler)
474 {
475 if (auto source = g_main_context_find_source_by_id (nullptr, it->first))
476 {
477 g_source_destroy (source);
478 }
479 timerHandlers.erase (it);
480 return kResultTrue;
481 }
482 }
483
484 return kNotImplemented;
485 }
486
487 #else
488
489 /* XEMBED messages */
490 #define XEMBED_EMBEDDED_NOTIFY 0
491 #define XEMBED_WINDOW_ACTIVATE 1
492 #define XEMBED_WINDOW_DEACTIVATE 2
493 #define XEMBED_REQUEST_FOCUS 3
494 #define XEMBED_FOCUS_IN 4
495 #define XEMBED_FOCUS_OUT 5
496 #define XEMBED_FOCUS_NEXT 6
497 #define XEMBED_FOCUS_PREV 7
498 /* 8-9 were used for XEMBED_GRAB_KEY/XEMBED_UNGRAB_KEY */
499 #define XEMBED_MODALITY_ON 10
500 #define XEMBED_MODALITY_OFF 11
501 #define XEMBED_REGISTER_ACCELERATOR 12
502 #define XEMBED_UNREGISTER_ACCELERATOR 13
503 #define XEMBED_ACTIVATE_ACCELERATOR 14
504
send_xembed_message(Display * dpy,Window w,Atom messageType,long message,long detail,long data1,long data2)505 void send_xembed_message (Display* dpy, /* display */
506 Window w, /* receiver */
507 Atom messageType, long message, /* message opcode */
508 long detail, /* message detail */
509 long data1, /* message data 1 */
510 long data2 /* message data 2 */
511 )
512 {
513 XEvent ev;
514 memset (&ev, 0, sizeof (ev));
515 ev.xclient.type = ClientMessage;
516 ev.xclient.window = w;
517 ev.xclient.message_type = messageType;
518 ev.xclient.format = 32;
519 ev.xclient.data.l[0] = CurrentTime;
520 ev.xclient.data.l[1] = message;
521 ev.xclient.data.l[2] = detail;
522 ev.xclient.data.l[3] = data1;
523 ev.xclient.data.l[4] = data2;
524 XSendEvent (dpy, w, False, NoEventMask, &ev);
525 XSync (dpy, False);
526 }
527
528 #define XEMBED_MAPPED (1 << 0)
529
530 //------------------------------------------------------------------------
Impl(X11Window * x11Window)531 X11Window::Impl::Impl (X11Window* x11Window) : x11Window (x11Window)
532 {
533 }
534
535 //------------------------------------------------------------------------
~Impl()536 X11Window::Impl::~Impl ()
537 {
538 }
539
540 //------------------------------------------------------------------------
init(const std::string & name,Size size,bool resizeable,const WindowControllerPtr & controller,Display * display,const WindowClosedFunc & windowClosedFunc)541 bool X11Window::Impl::init (const std::string& name, Size size, bool resizeable,
542 const WindowControllerPtr& controller, Display* display,
543 const WindowClosedFunc& windowClosedFunc)
544 {
545 this->windowClosedFunc = windowClosedFunc;
546 this->controller = controller;
547 xDisplay = display;
548
549 xEmbedInfoAtom = XInternAtom (xDisplay, "_XEMBED_INFO", true);
550 if (xEmbedInfoAtom == None)
551 {
552 std::cerr << "_XEMBED_INFO does not exist" << std::endl;
553 return false;
554 }
555
556 // Get screen size from display
557 auto screen_num = DefaultScreen (xDisplay);
558 auto displayWidth = DisplayWidth (xDisplay, screen_num);
559 auto displayHeight = DisplayHeight (xDisplay, screen_num);
560 unsigned int border_width = 1;
561
562 XVisualInfo vInfo;
563 if (!XMatchVisualInfo (xDisplay, screen_num, 24, TrueColor, &vInfo))
564 {
565 exit (-1);
566 }
567
568 XSetWindowAttributes winAttr {};
569 winAttr.border_pixel = BlackPixel (xDisplay, screen_num);
570 winAttr.background_pixel = WhitePixel (xDisplay, screen_num);
571 winAttr.colormap =
572 XCreateColormap (xDisplay, XDefaultRootWindow (xDisplay), vInfo.visual, AllocNone);
573 uint32_t winAttrMask = CWBackPixel | CWColormap | CWBorderPixel;
574 xWindow = XCreateWindow (xDisplay, RootWindow (xDisplay, screen_num), 0, 0, displayWidth,
575 displayHeight, border_width, vInfo.depth, InputOutput, vInfo.visual,
576 winAttrMask, &winAttr);
577 XFlush (xDisplay);
578
579 resize (size, true);
580
581 XSelectInput (xDisplay, xWindow, /* KeyPressMask | ButtonPressMask |*/
582 ExposureMask | /*ResizeRedirectMask |*/ StructureNotifyMask |
583 SubstructureNotifyMask | FocusChangeMask);
584
585 auto sizeHints = XAllocSizeHints ();
586 sizeHints->flags = PMinSize;
587 if (!resizeable)
588 {
589 sizeHints->flags |= PMaxSize;
590 sizeHints->min_width = sizeHints->max_width = size.width;
591 sizeHints->min_height = sizeHints->max_height = size.height;
592 }
593 else
594 {
595 sizeHints->min_width = sizeHints->min_height = 80;
596 }
597 XSetWMNormalHints (xDisplay, xWindow, sizeHints);
598 XFree (sizeHints);
599
600 // set a title
601 XStoreName (xDisplay, xWindow, name.data ());
602
603 XTextProperty iconName;
604 auto icon_name = const_cast<char*> (name.data ());
605 XStringListToTextProperty (&icon_name, 1, &iconName);
606 XSetWMIconName (xDisplay, xWindow, &iconName);
607
608 Atom wm_delete_window;
609 wm_delete_window = XInternAtom (xDisplay, "WM_DELETE_WINDOW", False);
610 XSetWMProtocols (xDisplay, xWindow, &wm_delete_window, 1);
611
612 xGraphicContext = XCreateGC (xDisplay, xWindow, 0, 0);
613 XSetForeground (xDisplay, xGraphicContext, WhitePixel (xDisplay, screen_num));
614 XSetBackground (xDisplay, xGraphicContext, BlackPixel (xDisplay, screen_num));
615
616 winAttr = {};
617 winAttr.override_redirect = true;
618 winAttr.event_mask =
619 ExposureMask | KeyPressMask | ButtonPressMask | ButtonReleaseMask | ButtonMotionMask;
620 plugParentWindow =
621 XCreateWindow (xDisplay, xWindow, 0, 0, size.width, size.height, border_width, vInfo.depth,
622 InputOutput, CopyFromParent, winAttrMask, &winAttr);
623
624 XSelectInput (xDisplay, plugParentWindow, SubstructureNotifyMask | PropertyChangeMask);
625
626 XMapWindow (xDisplay, plugParentWindow);
627
628 RunLoop::instance ().registerWindow (plugParentWindow,
629 [this] (const XEvent& e) { return handlePlugEvent (e); });
630
631 RunLoop::instance ().registerWindow (
632 xWindow, [this] (const XEvent& e) { return handleMainWindowEvent (e); });
633
634 return true;
635 }
636
637 //------------------------------------------------------------------------
registerEventHandler(Linux::IEventHandler * handler,Linux::FileDescriptor fd)638 tresult PLUGIN_API X11Window::Impl::registerEventHandler (Linux::IEventHandler* handler,
639 Linux::FileDescriptor fd)
640 {
641 if (!handler || eventHandlers.find (fd) != eventHandlers.end ())
642 return kInvalidArgument;
643
644 RunLoop::instance ().registerFileDescriptor (fd,
645 [handler] (int fd) { handler->onFDIsSet (fd); });
646 eventHandlers.emplace (fd, handler);
647 return kResultTrue;
648 }
649
650 //------------------------------------------------------------------------
unregisterEventHandler(Linux::IEventHandler * handler)651 tresult PLUGIN_API X11Window::Impl::unregisterEventHandler (Linux::IEventHandler* handler)
652 {
653 if (!handler)
654 return kInvalidArgument;
655
656 for (auto it = eventHandlers.begin (), end = eventHandlers.end (); it != end; ++it)
657 {
658 if (it->second == handler)
659 {
660 RunLoop::instance ().unregisterFileDescriptor (it->first);
661 eventHandlers.erase (it);
662 return kResultTrue;
663 }
664 }
665
666 return kResultFalse;
667 }
668
669 //------------------------------------------------------------------------
registerTimer(Linux::ITimerHandler * handler,Linux::TimerInterval milliseconds)670 tresult PLUGIN_API X11Window::Impl::registerTimer (Linux::ITimerHandler* handler,
671 Linux::TimerInterval milliseconds)
672 {
673 if (!handler || milliseconds == 0)
674 return kInvalidArgument;
675
676 auto id = RunLoop::instance ().registerTimer (milliseconds,
677 [handler] (auto) { handler->onTimer (); });
678 timerHandlers.emplace (id, handler);
679 return kResultTrue;
680 }
681
682 //------------------------------------------------------------------------
unregisterTimer(Linux::ITimerHandler * handler)683 tresult PLUGIN_API X11Window::Impl::unregisterTimer (Linux::ITimerHandler* handler)
684 {
685 if (!handler)
686 return kInvalidArgument;
687
688 for (auto it = timerHandlers.begin (), end = timerHandlers.end (); it != end; ++it)
689 {
690 if (it->second == handler)
691 {
692 RunLoop::instance ().unregisterTimer (it->first);
693 timerHandlers.erase (it);
694 return kResultTrue;
695 }
696 }
697
698 return kNotImplemented;
699 }
700
701 //------------------------------------------------------------------------
callPlugEventHandlers()702 void X11Window::Impl::callPlugEventHandlers ()
703 {
704 for (auto& e : eventHandlers)
705 {
706 e.second->onFDIsSet (e.first);
707 }
708 }
709
710 //------------------------------------------------------------------------
show()711 void X11Window::Impl::show ()
712 {
713 XMapWindow (xDisplay, xWindow);
714 }
715
716 //------------------------------------------------------------------------
close()717 void X11Window::Impl::close ()
718 {
719 XUnmapWindow (xDisplay, xWindow);
720 }
721
722 //------------------------------------------------------------------------
onClose()723 void X11Window::Impl::onClose ()
724 {
725 XFreeGC (xDisplay, xGraphicContext);
726 XDestroyWindow (xDisplay, xWindow);
727
728 xDisplay = nullptr;
729 xWindow = 0;
730
731 isMapped = false;
732 if (windowClosedFunc)
733 windowClosedFunc (x11Window);
734 }
735
736 //------------------------------------------------------------------------
resize(Size newSize,bool force)737 void X11Window::Impl::resize (Size newSize, bool force)
738 {
739 if (!force && mCurrentSize == newSize)
740 return;
741 if (xWindow)
742 XResizeWindow (xDisplay, xWindow, newSize.width, newSize.height);
743 if (plugParentWindow)
744 XResizeWindow (xDisplay, plugParentWindow, newSize.width, newSize.height);
745 mCurrentSize = newSize;
746 }
747
748 //------------------------------------------------------------------------
getSize() const749 Size X11Window::Impl::getSize () const
750 {
751 ::Window root;
752 int x, y;
753 unsigned int width, height;
754 unsigned int border_width;
755 unsigned int depth;
756
757 XGetGeometry (xDisplay, xWindow, &root, &x, &y, &width, &height, &border_width, &depth);
758
759 return {static_cast<int> (width), static_cast<int> (height)};
760 }
761
762 //------------------------------------------------------------------------
checkSize()763 void X11Window::Impl::checkSize ()
764 {
765 if (getSize () != mCurrentSize)
766 {
767 resize (mCurrentSize, true);
768 }
769 }
770
771 //------------------------------------------------------------------------
handleMainWindowEvent(const XEvent & event)772 bool X11Window::Impl::handleMainWindowEvent (const XEvent& event)
773 {
774 #if LOG_EVENTS
775 std::cout << "event " << event.type << "\n";
776 #endif
777 bool res = false;
778
779 switch (event.type)
780 {
781 case Expose:
782 if (event.xexpose.count == 0)
783 {
784 XClearWindow (xDisplay, xWindow);
785 XFillRectangle (xDisplay, xWindow, xGraphicContext, 0, 0, mCurrentSize.width,
786 mCurrentSize.height);
787 }
788 res = true;
789 break;
790
791 //--- StructureNotifyMask ------------------------------
792 // Window has been resized
793 case ConfigureNotify:
794 {
795 if (event.xconfigure.window != xWindow)
796 break;
797
798 auto width = event.xconfigure.width;
799 auto height = event.xconfigure.height;
800
801 Size size {width, height};
802 if (mCurrentSize != size)
803 {
804 auto constraintSize = controller->constrainSize (*x11Window, size);
805 if (constraintSize != mCurrentSize)
806 {
807 mCurrentSize = size;
808 controller->onResize (*x11Window, size);
809 }
810 if (constraintSize != size)
811 resize (constraintSize, true);
812 else
813 {
814 if (plugParentWindow)
815 XResizeWindow (xDisplay, plugParentWindow, size.width, size.height);
816 }
817
818 #if LOG_EVENTS
819 std::cout << "new size " << width << " x " << height << "\n";
820 #endif
821 }
822 res = true;
823 }
824 break;
825
826 // Window has been map to the screen
827 case MapNotify:
828 {
829 if (event.xany.window == xWindow && !isMapped)
830 {
831 controller->onShow (*x11Window);
832 isMapped = true;
833 res = true;
834 }
835 }
836 break;
837
838 case UnmapNotify:
839 {
840 if (event.xunmap.window == xWindow)
841 {
842 controller->onClose (*x11Window);
843 onClose ();
844 res = true;
845 }
846 break;
847 }
848 case DestroyNotify: break;
849
850 case ClientMessage:
851 {
852 if (event.xany.window == xWindow)
853 {
854 controller->onClose (*x11Window);
855 onClose ();
856 res = true;
857 }
858 break;
859 }
860
861 case FocusIn:
862 {
863 if (xembedInfo)
864 send_xembed_message (xDisplay, plugWindow, xEmbedAtom, XEMBED_WINDOW_ACTIVATE, 0,
865 plugParentWindow, xembedInfo->version);
866 break;
867 }
868 case FocusOut:
869 {
870 if (xembedInfo)
871 send_xembed_message (xDisplay, plugWindow, xEmbedAtom, XEMBED_WINDOW_DEACTIVATE, 0,
872 plugParentWindow, xembedInfo->version);
873 break;
874 }
875
876 //--- ResizeRedirectMask --------------------------------
877 case ResizeRequest:
878 {
879 if (event.xany.window == xWindow)
880 {
881 auto width = event.xresizerequest.width;
882 auto height = event.xresizerequest.height;
883 Size request {width, height};
884 if (mCurrentSize != request)
885 {
886 #if LOG_EVENTS
887 std::cout << "requested Size " << width << " x " << height << "\n";
888 #endif
889
890 auto constraintSize = controller->constrainSize (*x11Window, request);
891 if (constraintSize != request)
892 {
893 #if LOG_EVENTS
894 std::cout << "constraint Size " << constraintSize.width << " x "
895 << constraintSize.height << "\n";
896 #endif
897 }
898 resize (constraintSize, true);
899 }
900 res = true;
901 }
902 }
903 break;
904 }
905 return res;
906 }
907
908 //------------------------------------------------------------------------
getXEmbedInfo()909 auto X11Window::Impl::getXEmbedInfo () -> XEmbedInfo*
910 {
911 int actualFormat;
912 unsigned long itemsReturned;
913 unsigned long bytesAfterReturn;
914 Atom actualType;
915 XEmbedInfo* xembedInfo = NULL;
916 if (xEmbedInfoAtom == None)
917 xEmbedInfoAtom = XInternAtom (xDisplay, "_XEMBED_INFO", true);
918 auto err =
919 XGetWindowProperty (xDisplay, plugWindow, xEmbedInfoAtom, 0, sizeof (xembedInfo), false,
920 xEmbedInfoAtom, &actualType, &actualFormat, &itemsReturned,
921 &bytesAfterReturn, reinterpret_cast<unsigned char**> (&xembedInfo));
922 if (err != Success)
923 return nullptr;
924 return xembedInfo;
925 }
926
927 //------------------------------------------------------------------------
handlePlugEvent(const XEvent & event)928 bool X11Window::Impl::handlePlugEvent (const XEvent& event)
929 {
930 bool res = false;
931
932 switch (event.type)
933 {
934 // XEMBED specific
935 case ClientMessage:
936 {
937 auto name = XGetAtomName (xDisplay, event.xclient.message_type);
938 std::cout << name << std::endl;
939 if (event.xclient.message_type == xEmbedAtom)
940 {
941 switch (event.xclient.data.l[1])
942 {
943 case XEMBED_REQUEST_FOCUS:
944 {
945 send_xembed_message (xDisplay, plugWindow, xEmbedAtom, XEMBED_FOCUS_IN, 0,
946 plugParentWindow, xembedInfo->version);
947 break;
948 }
949 }
950 }
951 break;
952 }
953 case PropertyNotify:
954 {
955 auto name = XGetAtomName (xDisplay, event.xproperty.atom);
956 std::cout << name << std::endl;
957 if (event.xany.window == plugWindow)
958 {
959 if (event.xproperty.atom == xEmbedInfoAtom)
960 {
961 if (auto embedInfo = getXEmbedInfo ())
962 {
963 }
964 }
965 else
966 {
967 }
968 }
969 break;
970 }
971 case CreateNotify:
972 {
973 if (event.xcreatewindow.parent != plugParentWindow)
974 {
975 res = true;
976 break;
977 }
978
979 plugWindow = event.xcreatewindow.window;
980
981 xembedInfo = getXEmbedInfo ();
982 if (!xembedInfo)
983 {
984 std::cerr << "XGetWindowProperty for _XEMBED_INFO failed" << std::endl;
985 exit (-1);
986 }
987 if (xembedInfo->flags & XEMBED_MAPPED)
988 {
989 std::cerr << "Window already mapped error" << std::endl;
990 exit (-1);
991 }
992 RunLoop::instance ().registerWindow (
993 plugWindow, [this] (const XEvent& e) { return handlePlugEvent (e); });
994
995 // XSelectInput (xDisplay, plugWindow, PropertyChangeMask);
996
997 if (xEmbedAtom == None)
998 xEmbedAtom = XInternAtom (xDisplay, "_XEMBED", true);
999 assert (xEmbedAtom != None);
1000
1001 send_xembed_message (xDisplay, plugWindow, xEmbedAtom, XEMBED_EMBEDDED_NOTIFY, 0,
1002 plugParentWindow, xembedInfo->version);
1003 XMapWindow (xDisplay, plugWindow);
1004 XResizeWindow (xDisplay, plugWindow, mCurrentSize.width, mCurrentSize.height);
1005 // XSetInputFocus (xDisplay, plugWindow, RevertToParent, CurrentTime);
1006 send_xembed_message (xDisplay, plugWindow, xEmbedAtom, XEMBED_WINDOW_ACTIVATE, 0,
1007 plugParentWindow, xembedInfo->version);
1008 send_xembed_message (xDisplay, plugWindow, xEmbedAtom, XEMBED_FOCUS_IN, 0,
1009 plugParentWindow, xembedInfo->version);
1010 XSync (xDisplay, False);
1011 res = true;
1012 break;
1013 }
1014 }
1015
1016 return res;
1017 }
1018 #endif
1019
1020 //------------------------------------------------------------------------
1021 } // EditorHost
1022 } // Vst
1023 } // Steinberg
1024