Implement initial Wayland support
[freeglut] / src / wayland / fg_window_wl.c
1 /*
2  * fg_window_wl.c
3  *
4  * Window management methods for Wayland
5  *
6  * Copyright (c) 2015 Manuel Bachmann. All Rights Reserved.
7  * Written by Manuel Bachmann, <tarnyko@tarnyko.net>
8  * Creation date: Tue Mar 17, 2015
9  *
10  * Permission is hereby granted, free of charge, to any person obtaining a
11  * copy of this software and associated documentation files (the "Software"),
12  * to deal in the Software without restriction, including without limitation
13  * the rights to use, copy, modify, merge, publish, distribute, sublicense,
14  * and/or sell copies of the Software, and to permit persons to whom the
15  * Software is furnished to do so, subject to the following conditions:
16  *
17  * The above copyright notice and this permission notice shall be included
18  * in all copies or substantial portions of the Software.
19  *
20  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
21  * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
22  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
23  * MANUEL BACHMANN BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
24  * IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
25  * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
26  */
27
28 #define FREEGLUT_BUILDING_LIB
29 #include <GL/freeglut.h>
30 #include "../fg_internal.h"
31 #include "egl/fg_window_egl.h"
32 #define fghCreateNewContext fghCreateNewContextEGL
33
34 extern void fghOnReshapeNotify( SFG_Window *window, int width, int height, GLboolean forceNotify );
35 void fgPlatformReshapeWindow( SFG_Window *window, int width, int height );
36 void fgPlatformIconifyWindow( SFG_Window *window );
37
38
39 static void fghShSurfacePing( void* data,
40                            struct wl_shell_surface* shsurface,
41                            uint32_t serial )
42 {
43     wl_shell_surface_pong( shsurface, serial );
44 }
45 static void fghShSurfaceConfigure( void* data,
46                                 struct wl_shell_surface* shsurface,
47                                 uint32_t edges,
48                                 int32_t width, int32_t height )
49 {
50     SFG_Window* window = data;
51     fgPlatformReshapeWindow( window, width, height );
52 }
53 static const struct wl_shell_surface_listener fghShSurfaceListener =
54 {
55     fghShSurfacePing,
56     fghShSurfaceConfigure,
57     NULL
58 };
59
60
61 static int fghToggleFullscreen(void)
62 {
63     SFG_Window* win = fgStructure.CurrentWindow;
64
65     if ( ! win->State.IsFullscreen )
66     {
67       win->State.pWState.OldWidth = win->State.Width;
68       win->State.pWState.OldHeight = win->State.Height;
69       wl_shell_surface_set_fullscreen( win->Window.pContext.shsurface,
70                                        WL_SHELL_SURFACE_FULLSCREEN_METHOD_DEFAULT,
71                                        0, NULL );
72     }
73     else
74     {
75       fgPlatformReshapeWindow( win, win->State.pWState.OldWidth,
76                                     win->State.pWState.OldHeight );
77       wl_shell_surface_set_toplevel( win->Window.pContext.shsurface );
78     }
79
80     return 0;
81 }
82
83 void fgPlatformOpenWindow( SFG_Window* window, const char* title,
84                            GLboolean positionUse, int x, int y,
85                            GLboolean sizeUse, int w, int h,
86                            GLboolean gameMode, GLboolean isSubWindow )
87 {
88     /* Save the display mode if we are creating a menu window */
89     if( window->IsMenu && ( ! fgStructure.MenuContext ) )
90         fgState.DisplayMode = GLUT_DOUBLE | GLUT_RGB ;
91
92     fghChooseConfig( &window->Window.pContext.egl.Config );
93
94     if( ! window->Window.pContext.egl.Config )
95     {
96         /*
97          * The "fghChooseConfig" returned a null meaning that the visual
98          * context is not available.
99          * Try a couple of variations to see if they will work.
100          */
101         if( fgState.DisplayMode & GLUT_MULTISAMPLE )
102         {
103             fgState.DisplayMode &= ~GLUT_MULTISAMPLE ;
104             fghChooseConfig( &window->Window.pContext.egl.Config );
105             fgState.DisplayMode |= GLUT_MULTISAMPLE;
106         }
107     }
108
109     FREEGLUT_INTERNAL_ERROR_EXIT( window->Window.pContext.egl.Config != NULL,
110                                   "EGL configuration with necessary capabilities "
111                                   "not found", "fgOpenWindow" );
112
113     if( ! positionUse )
114         x = y = -1; /* default window position */
115     if( ! sizeUse )
116         w = h = 300; /* default window size */
117
118     /*  Create the cursor  */
119    window->Window.pContext.cursor = wl_cursor_theme_get_cursor(
120                                       fgDisplay.pDisplay.cursor_theme,
121                                       "left_ptr" ); 
122    window->Window.pContext.cursor_surface = wl_compositor_create_surface(
123                                               fgDisplay.pDisplay.compositor );
124
125     /*  Create the main surface  */
126     window->Window.pContext.surface = wl_compositor_create_surface(
127                                         fgDisplay.pDisplay.compositor );
128
129     /*  Create the shell surface with respects to the parent/child tree  */
130     window->Window.pContext.shsurface = wl_shell_get_shell_surface(
131                                           fgDisplay.pDisplay.shell,
132                                            window->Window.pContext.surface );
133     wl_shell_surface_add_listener( window->Window.pContext.shsurface,
134                                    &fghShSurfaceListener, window );
135
136     if( title)
137       wl_shell_surface_set_title( window->Window.pContext.shsurface, title );
138
139     if( gameMode )
140     {
141         window->State.IsFullscreen = GL_TRUE;
142         wl_shell_surface_set_fullscreen( window->Window.pContext.shsurface,
143                                          WL_SHELL_SURFACE_FULLSCREEN_METHOD_DEFAULT,
144                                          0, NULL );
145     }
146     else if( !isSubWindow && !window->IsMenu )
147     {
148         wl_shell_surface_set_toplevel( window->Window.pContext.shsurface );
149     }
150     else
151     {
152         wl_shell_surface_set_transient( window->Window.pContext.shsurface,
153                                         window->Parent->Window.pContext.surface,
154                                         x, y, 0 );
155     }
156
157     /*  Create the Wl_EGL_Window  */
158     window->Window.Context = fghCreateNewContext( window );
159     window->Window.pContext.egl_window = wl_egl_window_create( 
160                                            window->Window.pContext.surface,
161                                            w, h);
162     window->Window.pContext.egl.Surface = eglCreateWindowSurface( 
163                               fgDisplay.pDisplay.egl.Display,
164                               window->Window.pContext.egl.Config,
165                               (EGLNativeWindowType)window->Window.pContext.egl_window,
166                               NULL );
167     eglMakeCurrent( fgDisplay.pDisplay.egl.Display, window->Window.pContext.egl.Surface,
168                     window->Window.pContext.egl.Surface, window->Window.Context );
169
170    window->Window.pContext.pointer_button_pressed = GL_FALSE;
171 }
172
173
174 /*
175  * Request a window resize
176  */
177 void fgPlatformReshapeWindow( SFG_Window *window, int width, int height )
178 {
179     fghOnReshapeNotify(window, width, height, GL_FALSE);
180
181     if( window->Window.pContext.egl_window )
182       wl_egl_window_resize( window->Window.pContext.egl_window,
183                             width, height, 0, 0 );
184 }
185
186
187 /*
188  * Closes a window, destroying the frame and OpenGL context
189  */
190 void fgPlatformCloseWindow( SFG_Window* window )
191 {
192     fghPlatformCloseWindowEGL(window);
193
194     if ( window->Window.pContext.egl_window )
195       wl_egl_window_destroy( window->Window.pContext.egl_window );
196     if ( window->Window.pContext.shsurface )
197       wl_shell_surface_destroy( window->Window.pContext.shsurface );
198     if ( window->Window.pContext.surface )
199       wl_surface_destroy( window->Window.pContext.surface );
200     if ( window->Window.pContext.cursor_surface )
201       wl_surface_destroy( window->Window.pContext.cursor_surface );
202 }
203
204
205 /*
206  * This function re-creates the window assets if they
207  * have been destroyed
208  */
209 void fgPlatformShowWindow( SFG_Window *window )
210 {
211     if ( ! window->Window.pContext.egl_window ||
212          ! window->Window.pContext.shsurface ||
213          ! window->Window.pContext.surface)
214     {
215         fgPlatformCloseWindow( window );
216         fgPlatformOpenWindow( window, "", /* TODO : save the title for further use */
217                               GL_TRUE, window->State.Xpos, window->State.Ypos,
218                               GL_TRUE, window->State.Width, window->State.Height,
219                               (GLboolean)(window->State.IsFullscreen ? GL_TRUE : GL_FALSE),
220                               (GLboolean)(window->Parent ? GL_TRUE : GL_FALSE) );
221     }
222     else 
223     {
224     /*     TODO : support this once we start using xdg-shell
225      *
226      *     xdg_surface_present( window->Window.pContext.shsurface, 0 );
227      *     INVOKE_WCB( *window, WindowStatus, ( GLUT_FULLY_RETAINED ) );
228      *     window->State.Visible = GL_TRUE;
229      */
230         fgWarning( "glutShownWindow(): function unsupported for an already existing"
231                                      " window under Wayland" );
232     }
233 }
234
235 /*
236  * This function hides the specified window
237  */
238 void fgPlatformHideWindow( SFG_Window *window )
239 {
240     fgPlatformIconifyWindow( window );
241 }
242
243 /*
244  * Iconify the specified window (top-level windows only)
245  */
246 void fgPlatformIconifyWindow( SFG_Window *window )
247 {
248     /* TODO : support this once we start using xdg-shell
249      *
250      * xdg_surface_set_minimized( window->Window.pContext.shsurface );
251      * INVOKE_WCB( *window, WindowStatus, ( GLUT_HIDDEN ) );
252      * window->State.Visible = GL_FALSE;
253      */
254     fgWarning( "glutIconifyWindow(): function unsupported under Wayland" );
255 }
256
257 /*
258  * Set the current window's title
259  */
260 void fgPlatformGlutSetWindowTitle( const char* title )
261 {
262     SFG_Window* win = fgStructure.CurrentWindow;
263     wl_shell_surface_set_title( win->Window.pContext.shsurface, title );
264 }
265
266 /*
267  * Set the current window's iconified title
268  */
269 void fgPlatformGlutSetIconTitle( const char* title )
270 {
271     fgPlatformGlutSetWindowTitle( title );
272 }
273
274 /*
275  * Change the specified window's position
276  */
277 void fgPlatformPositionWindow( SFG_Window *window, int x, int y )
278 {
279     /* pointless under Wayland */
280     fgWarning( "glutPositionWindow(): function unsupported under Wayland" );
281 }
282
283 /*
284  * Lowers the specified window (by Z order change)
285  */
286 void fgPlatformPushWindow( SFG_Window *window )
287 {
288     /* pointless under Wayland */
289     fgWarning( "glutPushWindow(): function unsupported under Wayland" );
290 }
291
292 /*
293  * Raises the specified window (by Z order change)
294  */
295 void fgPlatformPopWindow( SFG_Window *window )
296 {
297     /* pointless under Wayland */
298     fgWarning( "glutPopWindow(): function unsupported under Wayland" );
299 }
300
301 /*
302  * Toggle the window's full screen state.
303  */
304 void fgPlatformFullScreenToggle( SFG_Window *win )
305 {
306     if(fghToggleFullscreen() != -1) {
307         win->State.IsFullscreen = !win->State.IsFullscreen;
308     }
309 }
310