Added initial support for displaying on Wayland compositors.
[vuplus_xbmc] / xbmc / windowing / egl / EGLNativeTypeWayland.cpp
1 /*
2  *      Copyright (C) 2011-2013 Team XBMC
3  *      http://www.xbmc.org
4  *
5  *  This Program is free software; you can redistribute it and/or modify
6  *  it under the terms of the GNU General Public License as published by
7  *  the Free Software Foundation; either version 2, or (at your option)
8  *  any later version.
9  *
10  *  This Program is distributed in the hope that it will be useful,
11  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
12  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13  *  GNU General Public License for more details.
14  *
15  *  You should have received a copy of the GNU General Public License
16  *  along with XBMC; see the file COPYING.  If not, see
17  *  <http://www.gnu.org/licenses/>.
18  *
19  */
20 #include "system.h"
21
22 #if defined(HAVE_WAYLAND)
23
24 #define WL_EGL_PLATFORM
25  
26 #include <sstream>
27 #include <iostream>
28 #include <stdexcept>
29
30 #include <boost/noncopyable.hpp>
31 #include <boost/function.hpp>
32 #include <boost/bind.hpp>
33 #include <boost/scoped_ptr.hpp>
34 #include <boost/shared_ptr.hpp>
35
36 #include <cstdlib>
37
38 #include <wayland-client.h>
39 #include <wayland-version.h>
40
41 #include "windowing/DllWaylandClient.h"
42 #include "windowing/DllWaylandEgl.h"
43 #include "windowing/DllXKBCommon.h"
44 #include "windowing/WaylandProtocol.h"
45
46 #include "guilib/gui3d.h"
47 #include "utils/log.h"
48 #include "windowing/WinEvents.h"
49 #include "windowing/WinEventsWayland.h"
50
51 #include "wayland/WaylandLibraries.h"
52 #include "wayland/XBMCConnection.h"
53 #include "wayland/XBMCSurface.h"
54
55 #endif
56
57 #include "EGLNativeTypeWayland.h"
58
59 #if defined(HAVE_WAYLAND)
60 namespace xw = xbmc::wayland;
61
62 class CEGLNativeTypeWayland::Private
63 {
64 public:
65
66   boost::scoped_ptr<xw::Libraries> m_libraries;
67   boost::scoped_ptr<xw::XBMCConnection> m_connection;
68   boost::scoped_ptr<xw::XBMCSurface> m_surface;
69
70   bool LoadWaylandLibraries();
71   void UnloadWaylandLibraries();
72 };
73
74 bool CEGLNativeTypeWayland::Private::LoadWaylandLibraries()
75 {
76   try
77   {
78     m_libraries.reset(new xw::Libraries());
79   }
80   catch (const std::runtime_error &err)
81   {
82     CLog::Log(LOGWARNING, "%s: %s\n",
83               __FUNCTION__, err.what());
84     return false;
85   }
86   
87   return true;
88 }
89
90 void CEGLNativeTypeWayland::Private::UnloadWaylandLibraries()
91 {
92   m_libraries.reset();
93 }
94
95 #else
96 class CEGLNativeTypeWayland::Private
97 {
98 };
99 #endif
100
101 CEGLNativeTypeWayland::CEGLNativeTypeWayland() :
102   priv(new Private())
103 {
104 }
105
106 CEGLNativeTypeWayland::~CEGLNativeTypeWayland()
107 {
108
109
110 bool CEGLNativeTypeWayland::CheckCompatibility()
111 {
112 #if defined(HAVE_WAYLAND)
113   if (!getenv("WAYLAND_DISPLAY"))
114   {
115     CLog::Log(LOGWARNING, "%s:, WAYLAND_DISPLAY is not set",
116               __FUNCTION__);
117     return false;
118   }
119   
120   /* FIXME:
121    * There appears to be a bug in DllDynamic::CanLoad() which causes
122    * it to always return false. We are just loading the library 
123    * directly at CheckCompatibility time now */
124   if (!priv->LoadWaylandLibraries())
125     return false;
126
127   return true;
128 #else
129   return false;
130 #endif
131 }
132
133 void CEGLNativeTypeWayland::Initialize()
134 {
135 }
136
137 void CEGLNativeTypeWayland::Destroy()
138 {
139 #if defined(HAVE_WAYLAND)
140   priv->UnloadWaylandLibraries();
141 #endif
142 }
143
144 bool CEGLNativeTypeWayland::CreateNativeDisplay()
145 {
146 #if defined(HAVE_WAYLAND)
147
148   /* On CreateNativeDisplay we connect to the running wayland
149    * compositor on our current socket (as specified by WAYLAND_DISPLAY)
150    * and then do some initial set up like registering event handlers.
151    * 
152    * xbmc::wayland::XBMCConnection is an encapsulation of all of our
153    * current global state with regards to a wayland connection. We
154    * need to give it access to the wayland client libraries and
155    * libxkbcommon for it to do its work.
156    * 
157    * We also inject an xbmc::wayland::XBMCConnection::EventInjector
158    * which is basically just a table of function pointers to functions
159    * in CWinEventsWayland, which are all static. CWinEvents is still
160    * effectively a static, singleton class, and depending on it
161    * means that testing becomes substantially more difficult. As such
162    * we just inject the bits that we need here so that they can be
163    * stubbed out later in testing environments if need be.
164    * 
165    * xbmc::wayland::XBMCConnection's constructor will throw an
166    * std::runtime_error in case it runs into any trouble in connecting
167    * to the wayland compositor or getting the initial global objects.
168    * 
169    * The best we can do when that happens is just report the error
170    * and bail out, possibly to try another (fallback) windowing system.
171    */
172   try
173   {
174     xw::XBMCConnection::EventInjector injector =
175     {
176       CWinEventsWayland::SetWaylandDisplay,
177       CWinEventsWayland::DestroyWaylandDisplay,
178       CWinEvents::MessagePump
179     };
180       
181     priv->m_connection.reset(new xw::XBMCConnection(priv->m_libraries->ClientLibrary(),
182                                                     priv->m_libraries->XKBCommonLibrary(),
183                                                     injector));
184   }
185   catch (const std::runtime_error &err)
186   {
187     CLog::Log(LOGERROR, "%s: %s", __FUNCTION__, err.what());
188     return false;
189   }
190
191   return true;
192 #else
193   return false;
194 #endif
195 }
196
197 bool CEGLNativeTypeWayland::CreateNativeWindow()
198 {
199 #if defined(HAVE_WAYLAND)
200
201   /* CreateNativeWindow is where we allocate a new wayland surface
202    * using libwayland-egl and ask the compositor to display it by
203    * creating a new remote surface object.
204    * 
205    * xbmc::wayland::XBMCSurface encapsulates all of this information. It
206    * needs access to various client libraries, as well as the compositor
207    * and shell global interfaces from xbmc::wayland::XBMCConnection
208    * in order to actually create the internal "surface" and "shell
209    * surface" representations.
210    * 
211    * Once xbmc::wayland::XBMCSurface is created, an EGL bindable
212    * surface will be available for later use.
213    * 
214    * The last two parameters are the requested width and height of
215    * the surface.
216    * 
217    * If any problems are encountered in creating the surface
218    * an std::runtime_error is thrown. Like above, we catch it and
219    * report the error, since there's not much we can do about it.
220    */
221   try
222   {
223     priv->m_surface.reset(new xw::XBMCSurface(priv->m_libraries->ClientLibrary(),
224                                               priv->m_libraries->EGLLibrary(),
225                                               priv->m_connection->GetCompositor(),
226                                               priv->m_connection->GetShell(),
227                                               640,
228                                               480));
229   }
230   catch (const std::runtime_error &err)
231   {
232     CLog::Log(LOGERROR, "%s: %s", __FUNCTION__, err.what());
233     return false;
234   }
235
236   return true;
237 #else
238   return false;
239 #endif
240 }
241
242 bool CEGLNativeTypeWayland::GetNativeDisplay(XBNativeDisplayType **nativeDisplay) const
243 {
244 #if defined(HAVE_WAYLAND)
245   /* We need to return a pointer to the wl_display * (eg wl_display **),
246    * as EGLWrapper needs to dereference our return value to get the
247    * actual display and not its first member */
248   *nativeDisplay =
249       reinterpret_cast <XBNativeDisplayType *>(priv->m_connection->NativeDisplay());
250   return true;
251 #else
252   return false;
253 #endif
254 }
255
256 bool CEGLNativeTypeWayland::GetNativeWindow(XBNativeDisplayType **nativeWindow) const
257 {
258 #if defined(HAVE_WAYLAND)
259   *nativeWindow =
260       reinterpret_cast <XBNativeWindowType *>(priv->m_surface->EGLNativeWindow());
261   return true;
262 #else
263   return false;
264 #endif
265 }  
266
267 /* DestroyNativeDisplay and DestroyNativeWindow simply just call
268  * reset on the relevant scoped_ptr. This will effectively destroy
269  * the encapsulating objects which cleans up all of the relevant
270  * connections and surfaces */
271 bool CEGLNativeTypeWayland::DestroyNativeDisplay()
272 {
273 #if defined(HAVE_WAYLAND)
274   priv->m_connection.reset();
275   return true;
276 #else
277   return false;
278 #endif
279 }
280
281 bool CEGLNativeTypeWayland::DestroyNativeWindow()
282 {
283 #if defined(HAVE_WAYLAND)
284   priv->m_surface.reset();
285   return true;  
286 #else
287   return false;
288 #endif
289 }
290
291 /* The connection knowns about the resolution size, so we ask it
292  * about it. This information is all cached locally, but stored in
293  * the xbmc::wayland::XBMCConnection object */
294 bool CEGLNativeTypeWayland::GetNativeResolution(RESOLUTION_INFO *res) const
295 {
296 #if defined(HAVE_WAYLAND)
297   priv->m_connection->CurrentResolution(*res);
298
299   return true;
300 #else
301   return false;
302 #endif
303 }
304
305 bool CEGLNativeTypeWayland::SetNativeResolution(const RESOLUTION_INFO &res)
306 {
307 #if defined(HAVE_WAYLAND)
308   priv->m_surface->Resize(res.iScreenWidth, res.iScreenHeight);
309   return true;
310 #else
311   return false;
312 #endif
313 }
314
315 bool CEGLNativeTypeWayland::ProbeResolutions(std::vector<RESOLUTION_INFO> &resolutions)
316 {
317 #if defined(HAVE_WAYLAND)
318   priv->m_connection->AvailableResolutions(resolutions);
319   return true;
320 #else
321   return false;
322 #endif
323 }
324
325 bool CEGLNativeTypeWayland::GetPreferredResolution(RESOLUTION_INFO *res) const
326 {
327 #if defined(HAVE_WAYLAND)
328   priv->m_connection->PreferredResolution(*res);
329   return true;
330 #else
331   return false;
332 #endif
333 }
334
335 bool CEGLNativeTypeWayland::ShowWindow(bool show)
336 {
337 #if defined(HAVE_WAYLAND)
338   if (show)
339     priv->m_surface->Show();
340   else
341     return false;
342
343   return true;
344 #else
345   return false;
346 #endif
347 }