ios vout: signal while holding the lock
[vlc.git] / modules / video_output / ios.m
blob9a075194f230dc658151a55c9a83bca7683b69c3
1 /*****************************************************************************
2  * ios.m: iOS OpenGL ES provider
3  *****************************************************************************
4  * Copyright (C) 2001-2017 VLC authors and VideoLAN
5  *
6  * Authors: Pierre d'Herbemont <pdherbemont at videolan dot org>
7  *          Felix Paul Kühne <fkuehne at videolan dot org>
8  *          David Fuhrmann <david dot fuhrmann at googlemail dot com>
9  *          Rémi Denis-Courmont
10  *          Laurent Aimar <fenrir _AT_ videolan _DOT_ org>
11  *          Eric Petit <titer@m0k.org>
12  *
13  * This program is free software; you can redistribute it and/or modify it
14  * under the terms of the GNU Lesser General Public License as published by
15  * the Free Software Foundation; either version 2.1 of the License, or
16  * (at your option) any later version.
17  *
18  * This program is distributed in the hope that it will be useful,
19  * but WITHOUT ANY WARRANTY; without even the implied warranty of
20  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21  * GNU Lesser General Public License for more details.
22  *
23  * You should have received a copy of the GNU Lesser General Public License
24  * along with this program; if not, write to the Free Software Foundation,
25  * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
26  *****************************************************************************/
28 /*****************************************************************************
29  * Preamble
30  *****************************************************************************/
32 #import <UIKit/UIKit.h>
33 #import <OpenGLES/EAGL.h>
34 #import <OpenGLES/ES2/gl.h>
35 #import <OpenGLES/ES2/glext.h>
36 #import <QuartzCore/QuartzCore.h>
37 #import <dlfcn.h>
39 #ifdef HAVE_CONFIG_H
40 # import "config.h"
41 #endif
43 #import <vlc_common.h>
44 #import <vlc_plugin.h>
45 #import <vlc_vout_display.h>
46 #import <vlc_opengl.h>
47 #import <vlc_dialog.h>
48 #import "opengl/vout_helper.h"
50 /**
51  * Forward declarations
52  */
53 static int Open(vout_display_t *vd, const vout_display_cfg_t *cfg,
54                 video_format_t *fmt, vlc_video_context *context);
55 static void Close(vout_display_t *vd);
57 static void PictureRender(vout_display_t *, picture_t *, subpicture_t *, vlc_tick_t);
58 static void PictureDisplay(vout_display_t *, picture_t *);
59 static int Control(vout_display_t*, int, va_list);
61 static void *OurGetProcAddress(vlc_gl_t *, const char *);
63 static int GLESMakeCurrent(vlc_gl_t *);
64 static void GLESSwap(vlc_gl_t *);
65 static void GLESReleaseCurrent(vlc_gl_t *);
67 /**
68  * Module declaration
69  */
70 vlc_module_begin ()
71     set_shortname("iOS vout")
72     set_description("iOS OpenGL video output")
73     set_category(CAT_VIDEO)
74     set_subcategory(SUBCAT_VIDEO_VOUT)
75     set_callback_display(Open, 300)
77     add_shortcut("vout_ios2", "vout_ios")
78     add_glopts()
79 vlc_module_end ()
81 @interface VLCOpenGLES2VideoView : UIView {
82     vout_display_t *_voutDisplay;
83     EAGLContext *_eaglContext;
84     CAEAGLLayer *_layer;
86     vlc_mutex_t _mutex;
87     vlc_cond_t  _gl_attached_wait;
88     BOOL        _gl_attached;
90     BOOL _bufferNeedReset;
91     BOOL _appActive;
92     BOOL _eaglEnabled;
93     BOOL _placeInvalidated;
95     UIView *_viewContainer;
96     UITapGestureRecognizer *_tapRecognizer;
98     /* Written from MT, read locked from vout */
99     vout_display_place_t _place;
100     CGSize _viewSize;
101     CGFloat _scaleFactor;
103     /* Written from vout, read locked from MT */
104     vout_display_cfg_t _cfg;
107 - (id)initWithFrame:(CGRect)frame andVD:(vout_display_t*)vd;
108 - (void)cleanAndRelease:(BOOL)flushed;
109 - (BOOL)makeCurrent:(EAGLContext **)previousEaglContext withGL:(vlc_gl_t *)gl;
110 - (void)releaseCurrent:(EAGLContext *)previousEaglContext;
111 - (void)presentRenderbuffer;
113 - (void)updateVoutCfg:(const vout_display_cfg_t *)cfg withVGL:(vout_display_opengl_t *)vgl;
114 - (void)getPlaceLocked:(vout_display_place_t *)place;
115 @end
117 struct vout_display_sys_t
119     VLCOpenGLES2VideoView *glESView;
121     vlc_gl_t *gl;
123     vout_window_t *embed;
126 struct gl_sys
128     VLCOpenGLES2VideoView *glESView;
129     vout_display_opengl_t *vgl;
130     GLuint renderBuffer;
131     GLuint frameBuffer;
132     EAGLContext *previousEaglContext;
135 static void *OurGetProcAddress(vlc_gl_t *gl, const char *name)
137     VLC_UNUSED(gl);
139     return dlsym(RTLD_DEFAULT, name);
142 static int Open(vout_display_t *vd, const vout_display_cfg_t *cfg,
143                 video_format_t *fmt, vlc_video_context *context)
145     if (vout_display_cfg_IsWindowed(cfg))
146         return VLC_EGENERIC;
148     vout_display_sys_t *sys = vlc_obj_calloc(VLC_OBJECT(vd), 1, sizeof(*sys));
150     if (!sys)
151         return VLC_ENOMEM;
153     vd->sys = sys;
154     sys->gl = NULL;
156     var_Create(vlc_object_parent(vd), "ios-eaglcontext", VLC_VAR_ADDRESS);
158     @autoreleasepool {
159         /* setup the actual OpenGL ES view */
161         [VLCOpenGLES2VideoView performSelectorOnMainThread:@selector(getNewView:)
162                                                 withObject:[NSArray arrayWithObjects:
163                                                            [NSValue valueWithPointer:&sys->glESView],
164                                                            [NSValue valueWithPointer:vd], nil]
165                                              waitUntilDone:YES];
166         if (!sys->glESView) {
167             msg_Err(vd, "Creating OpenGL ES 2 view failed");
168             var_Destroy(vlc_object_parent(vd), "ios-eaglcontext");
169             return VLC_EGENERIC;
170         }
172         const vlc_fourcc_t *subpicture_chromas;
174         sys->embed = cfg->window;
175         sys->gl = vlc_object_create(vd, sizeof(*sys->gl));
176         if (!sys->gl)
177             goto bailout;
179         struct gl_sys *glsys = sys->gl->sys =
180             vlc_obj_malloc(VLC_OBJECT(vd), sizeof(struct gl_sys));
181         if (unlikely(!sys->gl->sys))
182             goto bailout;
183         glsys->glESView = sys->glESView;
184         glsys->vgl = NULL;
185         glsys->renderBuffer = glsys->frameBuffer = 0;
187         /* Initialize common OpenGL video display */
188         sys->gl->makeCurrent = GLESMakeCurrent;
189         sys->gl->releaseCurrent = GLESReleaseCurrent;
190         sys->gl->swap = GLESSwap;
191         sys->gl->getProcAddress = OurGetProcAddress;
193         if (vlc_gl_MakeCurrent(sys->gl) != VLC_SUCCESS)
194             goto bailout;
196         vout_display_opengl_t *vgl = vout_display_opengl_New(fmt, &subpicture_chromas,
197                                                              sys->gl, &cfg->viewpoint,
198                                                              context);
199         vlc_gl_ReleaseCurrent(sys->gl);
200         if (!vgl)
201             goto bailout;
202         glsys->vgl = vgl;
204         /* Setup vout_display_t once everything is fine */
205         vd->info.subpicture_chromas = subpicture_chromas;
207         vd->prepare = PictureRender;
208         vd->display = PictureDisplay;
209         vd->control = Control;
210         vd->close   = Close;
212         return VLC_SUCCESS;
214     bailout:
215         Close(vd);
216         return VLC_EGENERIC;
217     }
220 static void Close(vout_display_t *vd)
222     vout_display_sys_t *sys = vd->sys;
224     @autoreleasepool {
225         BOOL flushed = NO;
226         if (sys->gl != NULL) {
227             struct gl_sys *glsys = sys->gl->sys;
228             msg_Dbg(vd, "deleting display");
230             if (likely(glsys->vgl))
231             {
232                 int ret = vlc_gl_MakeCurrent(sys->gl);
233                 vout_display_opengl_Delete(glsys->vgl);
234                 if (ret == VLC_SUCCESS)
235                 {
236                     vlc_gl_ReleaseCurrent(sys->gl);
237                     flushed = YES;
238                 }
239             }
240             vlc_object_delete(sys->gl);
241         }
243         [sys->glESView cleanAndRelease:flushed];
244     }
245     var_Destroy(vlc_object_parent(vd), "ios-eaglcontext");
248 /*****************************************************************************
249  * vout display callbacks
250  *****************************************************************************/
252 static int Control(vout_display_t *vd, int query, va_list ap)
254     vout_display_sys_t *sys = vd->sys;
255     struct gl_sys *glsys = sys->gl->sys;
257     switch (query) {
258         case VOUT_DISPLAY_CHANGE_DISPLAY_FILLED:
259         case VOUT_DISPLAY_CHANGE_ZOOM:
260         case VOUT_DISPLAY_CHANGE_SOURCE_ASPECT:
261         case VOUT_DISPLAY_CHANGE_SOURCE_CROP:
262         case VOUT_DISPLAY_CHANGE_DISPLAY_SIZE:
263         {
264             const vout_display_cfg_t *cfg =
265                 va_arg(ap, const vout_display_cfg_t *);
267             assert(cfg);
269             [sys->glESView updateVoutCfg:cfg withVGL:glsys->vgl];
271             return VLC_SUCCESS;
272         }
274         case VOUT_DISPLAY_CHANGE_VIEWPOINT:
275             return vout_display_opengl_SetViewpoint(glsys->vgl,
276                 &va_arg (ap, const vout_display_cfg_t* )->viewpoint);
278         case VOUT_DISPLAY_RESET_PICTURES:
279             vlc_assert_unreachable ();
280         default:
281             msg_Err(vd, "Unknown request %d", query);
282             return VLC_EGENERIC;
283     }
286 static void PictureDisplay(vout_display_t *vd, picture_t *pic)
288     vout_display_sys_t *sys = vd->sys;
289     struct gl_sys *glsys = sys->gl->sys;
290     VLC_UNUSED(pic);
292     if (vlc_gl_MakeCurrent(sys->gl) == VLC_SUCCESS)
293     {
294         vout_display_opengl_Display(glsys->vgl, &vd->source);
295         vlc_gl_ReleaseCurrent(sys->gl);
296     }
299 static void PictureRender(vout_display_t *vd, picture_t *pic, subpicture_t *subpicture,
300                           vlc_tick_t date)
302     VLC_UNUSED(date);
303     vout_display_sys_t *sys = vd->sys;
304     struct gl_sys *glsys = sys->gl->sys;
306     if (vlc_gl_MakeCurrent(sys->gl) == VLC_SUCCESS)
307     {
308         vout_display_opengl_Prepare(glsys->vgl, pic, subpicture);
309         vlc_gl_ReleaseCurrent(sys->gl);
310     }
313 /*****************************************************************************
314  * vout opengl callbacks
315  *****************************************************************************/
316 static int GLESMakeCurrent(vlc_gl_t *gl)
318     struct gl_sys *sys = gl->sys;
320     if (![sys->glESView makeCurrent:&sys->previousEaglContext withGL:gl])
321         return VLC_EGENERIC;
322     return VLC_SUCCESS;
325 static void GLESReleaseCurrent(vlc_gl_t *gl)
327     struct gl_sys *sys = gl->sys;
329     [sys->glESView releaseCurrent:sys->previousEaglContext];
332 static void GLESSwap(vlc_gl_t *gl)
334     struct gl_sys *sys = gl->sys;
336     [sys->glESView presentRenderbuffer];
340 /*****************************************************************************
341  * Our UIView object
342  *****************************************************************************/
343 @implementation VLCOpenGLES2VideoView
345 + (Class)layerClass
347     return [CAEAGLLayer class];
350 + (void)getNewView:(NSArray *)value
352     id *ret = [[value objectAtIndex:0] pointerValue];
353     vout_display_t *vd = [[value objectAtIndex:1] pointerValue];
354     *ret = [[self alloc] initWithFrame:CGRectMake(0.,0.,320.,240.) andVD:vd];
357 - (id)initWithFrame:(CGRect)frame andVD:(vout_display_t*)vd
359     _appActive = ([UIApplication sharedApplication].applicationState == UIApplicationStateActive);
360     if (unlikely(!_appActive))
361         return nil;
363     self = [super initWithFrame:frame];
364     if (!self)
365         return nil;
367     _eaglEnabled = YES;
368     _bufferNeedReset = YES;
369     _voutDisplay = vd;
370     _cfg = *_voutDisplay->cfg;
372     vlc_mutex_init(&_mutex);
373     vlc_cond_init(&_gl_attached_wait);
374     _gl_attached = YES;
376     /* the following creates a new OpenGL ES context with the API version we
377      * need if there is already an active context created by another OpenGL
378      * provider we cache it and restore analog to the
379      * makeCurrent/releaseCurrent pattern used through-out the class */
380     EAGLContext *previousEaglContext = [EAGLContext currentContext];
382     _eaglContext = [[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES2];
384     if (unlikely(!_eaglContext)
385      || unlikely(![EAGLContext setCurrentContext:_eaglContext]))
386     {
387         [_eaglContext release];
388         [self release];
389         return nil;
390     }
391     [self releaseCurrent:previousEaglContext];
393     /* Set "ios-eaglcontext" to be used by cvpx fitlers/glconv */
394     var_SetAddress(vlc_object_parent(_voutDisplay), "ios-eaglcontext", _eaglContext);
396     _layer = (CAEAGLLayer *)self.layer;
397     _layer.drawableProperties = [NSDictionary dictionaryWithObject:kEAGLColorFormatRGBA8 forKey: kEAGLDrawablePropertyColorFormat];
398     _layer.opaque = YES;
400     self.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
402     if (![self fetchViewContainer])
403     {
404         [_eaglContext release];
405         [self release];
406         return nil;
407     }
409     /* */
410     [[NSNotificationCenter defaultCenter] addObserver:self
411                                              selector:@selector(applicationStateChanged:)
412                                                  name:UIApplicationWillResignActiveNotification
413                                                object:nil];
414     [[NSNotificationCenter defaultCenter] addObserver:self
415                                              selector:@selector(applicationStateChanged:)
416                                                  name:UIApplicationDidBecomeActiveNotification
417                                                object:nil];
418     [[NSNotificationCenter defaultCenter] addObserver:self
419                                              selector:@selector(applicationStateChanged:)
420                                                  name:UIApplicationDidEnterBackgroundNotification
421                                                object:nil];
422     [[NSNotificationCenter defaultCenter] addObserver:self
423                                              selector:@selector(applicationStateChanged:)
424                                                  name:UIApplicationWillEnterForegroundNotification
425                                                object:nil];
427     return self;
430 - (BOOL)fetchViewContainer
432     @try {
433         /* get the object we will draw into */
434         UIView *viewContainer = var_InheritAddress (_voutDisplay, "drawable-nsobject");
435         if (unlikely(viewContainer == nil)) {
436             msg_Err(_voutDisplay, "provided view container is nil");
437             return NO;
438         }
440         if (unlikely(![viewContainer respondsToSelector:@selector(isKindOfClass:)])) {
441             msg_Err(_voutDisplay, "void pointer not an ObjC object");
442             return NO;
443         }
445         [viewContainer retain];
447         if (![viewContainer isKindOfClass:[UIView class]]) {
448             msg_Err(_voutDisplay, "passed ObjC object not of class UIView");
449             return NO;
450         }
452         /* This will be released in Close(), on
453          * main thread, after we are done using it. */
454         _viewContainer = viewContainer;
456         self.frame = viewContainer.bounds;
457         [self reshape];
459         [_viewContainer addSubview:self];
461         /* add tap gesture recognizer for DVD menus and stuff */
462         _tapRecognizer = [[UITapGestureRecognizer alloc] initWithTarget:self
463                                                                  action:@selector(tapRecognized:)];
464         if (_viewContainer.window
465          && _viewContainer.window.rootViewController
466          && _viewContainer.window.rootViewController.view)
467             [_viewContainer.superview addGestureRecognizer:_tapRecognizer];
468         _tapRecognizer.cancelsTouchesInView = NO;
469         return YES;
470     } @catch (NSException *exception) {
471         msg_Err(_voutDisplay, "Handling the view container failed due to an Obj-C exception (%s, %s", [exception.name UTF8String], [exception.reason UTF8String]);
472         vout_display_sys_t *sys = _voutDisplay->sys;
473         if (_tapRecognizer)
474             [_tapRecognizer release];
475         return NO;
476     }
479 - (void)cleanAndReleaseFromMainThread
481     [[NSNotificationCenter defaultCenter] removeObserver:self];
483     [_tapRecognizer.view removeGestureRecognizer:_tapRecognizer];
484     [_tapRecognizer release];
486     [self removeFromSuperview];
487     [_viewContainer release];
489     assert(!_gl_attached);
490     [_eaglContext release];
491     [self release];
494 - (void)cleanAndRelease:(BOOL)flushed
496     vlc_mutex_lock(&_mutex);
497     if (_eaglEnabled && !flushed)
498         [self flushEAGLLocked];
499     _voutDisplay = nil;
500     _eaglEnabled = NO;
501     vlc_mutex_unlock(&_mutex);
503     [self performSelectorOnMainThread:@selector(cleanAndReleaseFromMainThread)
504                            withObject:nil
505                         waitUntilDone:NO];
508 - (void)dealloc
510     vlc_mutex_destroy(&_mutex);
511     vlc_cond_destroy(&_gl_attached_wait);
512     [super dealloc];
515 - (void)didMoveToWindow
517     self.contentScaleFactor = self.window.screen.scale;
519     vlc_mutex_lock(&_mutex);
520     _bufferNeedReset = YES;
521     vlc_mutex_unlock(&_mutex);
524 - (BOOL)doResetBuffers:(vlc_gl_t *)gl
526     struct gl_sys *glsys = gl->sys;
528     if (glsys->frameBuffer != 0)
529     {
530         /* clear frame buffer */
531         glDeleteFramebuffers(1, &glsys->frameBuffer);
532         glsys->frameBuffer = 0;
533     }
535     if (glsys->renderBuffer != 0)
536     {
537         /* clear render buffer */
538         glDeleteRenderbuffers(1, &glsys->renderBuffer);
539         glsys->renderBuffer = 0;
540     }
542     glDisable(GL_DEPTH_TEST);
544     glGenFramebuffers(1, &glsys->frameBuffer);
545     glBindFramebuffer(GL_FRAMEBUFFER, glsys->frameBuffer);
547     glGenRenderbuffers(1, &glsys->renderBuffer);
548     glBindRenderbuffer(GL_RENDERBUFFER, glsys->renderBuffer);
550     [_eaglContext renderbufferStorage:GL_RENDERBUFFER fromDrawable:_layer];
552     glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, glsys->renderBuffer);
553     if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE)
554     {
555         msg_Err(_voutDisplay, "Failed to make complete framebuffer object %x", glCheckFramebufferStatus(GL_FRAMEBUFFER));
556         return NO;
557     }
558     return YES;
561 - (BOOL)makeCurrent:(EAGLContext **)previousEaglContext withGL:(vlc_gl_t *)gl
563     vlc_mutex_lock(&_mutex);
564     assert(!_gl_attached);
566     if (unlikely(!_appActive))
567     {
568         vlc_mutex_unlock(&_mutex);
569         return NO;
570     }
572     assert(_eaglEnabled);
573     *previousEaglContext = [EAGLContext currentContext];
575     if (![EAGLContext setCurrentContext:_eaglContext])
576     {
577         vlc_mutex_unlock(&_mutex);
578         return NO;
579     }
581     BOOL resetBuffers = NO;
583     if (gl != NULL)
584     {
585         struct gl_sys *glsys = gl->sys;
587         if (unlikely(_bufferNeedReset))
588         {
589             _bufferNeedReset = NO;
590             resetBuffers = YES;
591         }
592         if (unlikely(_placeInvalidated && glsys->vgl))
593         {
594             _placeInvalidated = NO;
596             vout_display_place_t place;
597             [self getPlaceLocked: &place];
598             vout_display_opengl_SetWindowAspectRatio(glsys->vgl, (float)place.width / place.height);
600             // x / y are top left corner, but we need the lower left one
601             vout_display_opengl_Viewport(glsys->vgl, _place.x, _place.y, _place.width, _place.height);
602         }
603     }
605     _gl_attached = YES;
607     vlc_mutex_unlock(&_mutex);
609     if (resetBuffers && ![self doResetBuffers:gl])
610     {
611         [self releaseCurrent:*previousEaglContext];
612         return NO;
613     }
614     return YES;
617 - (void)releaseCurrent:(EAGLContext *)previousEaglContext
619     [EAGLContext setCurrentContext:previousEaglContext];
621     vlc_mutex_lock(&_mutex);
622     assert(_gl_attached);
623     _gl_attached = NO;
624     vlc_cond_signal(&_gl_attached_wait);
625     vlc_mutex_unlock(&_mutex);
628 - (void)presentRenderbuffer
630     [_eaglContext presentRenderbuffer:GL_RENDERBUFFER];
633 - (void)layoutSubviews
635     [self reshape];
637     vlc_mutex_lock(&_mutex);
638     _bufferNeedReset = YES;
639     vlc_mutex_unlock(&_mutex);
642 - (void)getPlaceLocked:(vout_display_place_t *)place
644     assert(_voutDisplay);
645     vout_display_cfg_t cfg = _cfg;
647     cfg.display.width  = _viewSize.width * _scaleFactor;
648     cfg.display.height = _viewSize.height * _scaleFactor;
650     vout_display_PlacePicture(place, &_voutDisplay->source, &cfg);
653 - (void)reshape
655     assert([NSThread isMainThread]);
657     vlc_mutex_lock(&_mutex);
658     if (!_voutDisplay)
659     {
660         vlc_mutex_unlock(&_mutex);
661         return;
662     }
663     _viewSize = [self bounds].size;
664     _scaleFactor = self.contentScaleFactor;
666     vout_display_place_t place;
667     [self getPlaceLocked: &place];
669     if (memcmp(&place, &_place, sizeof(vout_display_place_t)) != 0)
670     {
671         _placeInvalidated = YES;
672         _place = place;
673     }
675     vlc_mutex_unlock(&_mutex);
678 - (void)tapRecognized:(UITapGestureRecognizer *)tapRecognizer
680     vlc_mutex_lock(&_mutex);
681     if (!_voutDisplay)
682     {
683         vlc_mutex_unlock(&_mutex);
684         return;
685     }
687     UIGestureRecognizerState state = [tapRecognizer state];
688     CGPoint touchPoint = [tapRecognizer locationInView:self];
689     CGFloat scaleFactor = self.contentScaleFactor;
690     vout_display_SendMouseMovedDisplayCoordinates(_voutDisplay,
691                                                   (int)touchPoint.x * scaleFactor, (int)touchPoint.y * scaleFactor);
693     vout_display_SendEventMousePressed(_voutDisplay, MOUSE_BUTTON_LEFT);
694     vout_display_SendEventMouseReleased(_voutDisplay, MOUSE_BUTTON_LEFT);
696     vlc_mutex_unlock(&_mutex);
699 - (void)updateVoutCfg:(const vout_display_cfg_t *)cfg withVGL:(vout_display_opengl_t *)vgl
701     if (memcmp(&_cfg, cfg, sizeof(vout_display_cfg_t)) == 0)
702         return;
704     vlc_mutex_lock(&_mutex);
705     _cfg = *cfg;
707     vout_display_place_t place;
708     [self getPlaceLocked: &place];
709     vout_display_opengl_SetWindowAspectRatio(vgl, (float)place.width / place.height);
711     vlc_mutex_unlock(&_mutex);
713     [self performSelectorOnMainThread:@selector(setNeedsUpdateConstraints)
714                            withObject:nil
715                         waitUntilDone:NO];
718 - (void)flushEAGLLocked
720     assert(_eaglEnabled);
722     /* Ensure that all previously submitted commands are drained from the
723      * command buffer and are executed by OpenGL ES before moving to the
724      * background.*/
725     EAGLContext *previousEaglContext = [EAGLContext currentContext];
726     if ([EAGLContext setCurrentContext:_eaglContext])
727         glFinish();
728     [EAGLContext setCurrentContext:previousEaglContext];
731 - (void)applicationStateChanged:(NSNotification *)notification
733     vlc_mutex_lock(&_mutex);
735     if ([[notification name] isEqualToString:UIApplicationWillResignActiveNotification])
736         _appActive = NO;
737     else if ([[notification name] isEqualToString:UIApplicationDidEnterBackgroundNotification])
738     {
739         _appActive = NO;
741         /* Wait for the vout to unlock the eagl context before releasing
742          * it. */
743         while (_gl_attached && _eaglEnabled)
744             vlc_cond_wait(&_gl_attached_wait, &_mutex);
746         /* _eaglEnabled can change during the vlc_cond_wait
747          * as the mutex is unlocked during that, so this check
748          * has to be done after the vlc_cond_wait! */
749         if (_eaglEnabled) {
750             [self flushEAGLLocked];
751             _eaglEnabled = NO;
752         }
753     }
754     else if ([[notification name] isEqualToString:UIApplicationWillEnterForegroundNotification])
755         _eaglEnabled = YES;
756     else
757     {
758         assert([[notification name] isEqualToString:UIApplicationDidBecomeActiveNotification]);
759         _appActive = YES;
760     }
762     vlc_mutex_unlock(&_mutex);
765 - (void)updateConstraints
767     [super updateConstraints];
768     [self reshape];
771 - (BOOL)isOpaque
773     return YES;
776 - (BOOL)acceptsFirstResponder
778     return YES;
781 @end