]> git.sesse.net Git - vlc/commitdiff
macosx_dialog_provider: Don't waste a thread for nothing and make sure we call AppKit...
authorPierre d'Herbemont <pdherbemont@free.fr>
Mon, 28 Dec 2009 02:02:29 +0000 (03:02 +0100)
committerPierre d'Herbemont <pdherbemont@free.fr>
Mon, 28 Dec 2009 02:02:29 +0000 (03:02 +0100)
modules/gui/macosx_dialog_provider/dialogProvider.m

index fccec6e629bfb8d832e2ae70171f2f5ba6121845..d0505e7acc829e13e151eca99a6fbb0e14eafd3b 100644 (file)
@@ -57,28 +57,62 @@ static void updateProgressPanel (void *, const char *, float);
 static bool checkProgressPanel (void *);
 static void destroyProgressPanel (void *);
 
+@interface VLCDialogDisplayer : NSObject
+{
+    VLCProgressPanel *_currentProgressBarPanel;
+}
+
++ (NSDictionary *)dictionaryForDialog:(const char *)title :(const char *)message :(const char *)yes :(const char *)no :(const char *)cancel;
+
+- (void)displayError:(NSDictionary *)dialog;
+- (void)displayCritical:(NSDictionary *)dialog;
+- (NSNumber *)displayQuestion:(NSDictionary *)dialog;
+- (NSDictionary *)displayLogin:(NSDictionary *)dialog;
+
+- (void)displayProgressBar:(NSDictionary *)dict;
+- (void)updateProgressPanel:(NSDictionary *)dict;
+- (void)destroyProgressPanel;
+- (NSNumber *)checkProgressPanel;
+
+- (id)resultFromSelectorOnMainThread:(SEL)sel withObject:(id)object;
+@end
+
+static inline NSDictionary *DictFromDialogFatal(dialog_fatal_t *dialog) {
+    return [VLCDialogDisplayer dictionaryForDialog:dialog->title :dialog->message :NULL :NULL :NULL];
+}
+static inline NSDictionary *DictFromDialogLogin(dialog_login_t *dialog) {
+    return [VLCDialogDisplayer dictionaryForDialog:dialog->title :dialog->message :NULL :NULL :NULL];
+}
+static inline NSDictionary *DictFromDialogQuestion(dialog_question_t *dialog) {
+    return [VLCDialogDisplayer dictionaryForDialog:dialog->title :dialog->message :dialog->yes :dialog->no :dialog->cancel];
+}
+static inline NSDictionary *DictFromDialogProgressBar(dialog_progress_bar_t *dialog) {
+    return [VLCDialogDisplayer dictionaryForDialog:dialog->title :dialog->message :NULL :NULL :dialog->cancel];
+}
+
 struct intf_sys_t
 {
-    VLCProgressPanel *currentProgressBarPanel;
+    VLCDialogDisplayer *displayer;
 
     vlc_mutex_t lock;
     vlc_cond_t wait;
 };
 
+
 /*****************************************************************************
  * Module descriptor
  *****************************************************************************/
 
 vlc_module_begin()
-/* Minimal interface. see intf.m */
-set_shortname("Mac OS X Dialogs")
-add_shortcut("macosx_dialog_provider")
-add_shortcut("miosx")
-set_description("Minimal Mac OS X Dialog Provider")
-set_capability("interface", 50)
-set_callbacks(OpenIntf, CloseIntf)
-set_category(CAT_INTERFACE)
-set_subcategory(SUBCAT_INTERFACE_MAIN)
+    /* Minimal interface. see intf.m */
+    set_shortname("Mac OS X Dialogs")
+    add_shortcut("macosx_dialog_provider")
+    add_shortcut("miosx")
+    set_description("Minimal Mac OS X Dialog Provider")
+    set_capability("interface", 50)
+    set_callbacks(OpenIntf, CloseIntf)
+    set_category(CAT_INTERFACE)
+    set_subcategory(SUBCAT_INTERFACE_MAIN)
 vlc_module_end()
 
 /*****************************************************************************
@@ -94,18 +128,8 @@ int OpenIntf(vlc_object_t *p_this)
 
     memset(p_intf->p_sys,0,sizeof(*p_intf->p_sys));
 
-    p_intf->b_should_run_on_first_thread = true;
-    p_intf->pf_run = Run;
+    p_intf->p_sys->displayer = [[VLCDialogDisplayer alloc] init];
 
-    msg_Dbg(p_intf,"Opening Mac OS X dialog provider");
-    return VLC_SUCCESS;
-}
-
-/*****************************************************************************
- * Run: waiting for the death
- *****************************************************************************/
-static void Run( intf_thread_t *p_intf )
-{
     /* subscribe to various interactive dialogues */
     var_Create(p_intf,"dialog-error",VLC_VAR_ADDRESS);
     var_AddCallback(p_intf,"dialog-error",DisplayError,p_intf);
@@ -118,21 +142,12 @@ static void Run( intf_thread_t *p_intf )
     var_Create(p_intf,"dialog-progress-bar",VLC_VAR_ADDRESS);
     var_AddCallback(p_intf,"dialog-progress-bar",DisplayProgressPanelAction,p_intf);
     dialog_Register(p_intf);
-
+    
     msg_Dbg(p_intf,"Mac OS X dialog provider initialised");
-
-    /* idle */
-    while(vlc_object_alive(p_intf))
-        msleep(INTF_IDLE_SLEEP);
     
-    /* unsubscribe from the interactive dialogues */
-    dialog_Unregister(p_intf);
-    var_DelCallback(p_intf,"dialog-error",DisplayError,p_intf);
-    var_DelCallback(p_intf,"dialog-critical",DisplayCritical,p_intf);
-    var_DelCallback(p_intf,"dialog-login",DisplayLogin,p_intf);
-    var_DelCallback(p_intf,"dialog-question",DisplayQuestion,p_intf);
-    var_DelCallback(p_intf,"dialog-progress-bar",DisplayProgressPanelAction,p_intf);
+    return VLC_SUCCESS;
 }
+
 /*****************************************************************************
  * CloseIntf: destroy interface
  *****************************************************************************/
@@ -140,6 +155,16 @@ void CloseIntf(vlc_object_t *p_this)
 {
     intf_thread_t *p_intf = (intf_thread_t*) p_this;
 
+    /* unsubscribe from the interactive dialogues */
+    dialog_Unregister(p_intf);
+    var_DelCallback(p_intf,"dialog-error",DisplayError,p_intf);
+    var_DelCallback(p_intf,"dialog-critical",DisplayCritical,p_intf);
+    var_DelCallback(p_intf,"dialog-login",DisplayLogin,p_intf);
+    var_DelCallback(p_intf,"dialog-question",DisplayQuestion,p_intf);
+    var_DelCallback(p_intf,"dialog-progress-bar",DisplayProgressPanelAction,p_intf);
+    
+    [p_intf->p_sys->displayer release];
+
     msg_Dbg(p_intf,"Mac OS X dialog provider closed");
     free(p_intf->p_sys);
 }
@@ -150,144 +175,273 @@ void CloseIntf(vlc_object_t *p_this)
  *****************************************************************************/
 static int DisplayError(vlc_object_t *p_this, const char *type, vlc_value_t previous, vlc_value_t value, void *data)
 {
-    NSAutoreleasePool * o_pool = [[NSAutoreleasePool alloc] init];
-    dialog_fatal_t *p_dialog = (dialog_fatal_t *)value.p_address;
-    NSRunInformationalAlertPanel([NSString stringWithUTF8String:p_dialog->title],
-                            [NSString stringWithUTF8String:p_dialog->message],
-                            @"OK", nil, nil);
-    [o_pool release];
+    NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
+    dialog_fatal_t *dialog = value.p_address;
+    intf_thread_t *p_intf = (intf_thread_t*) p_this;
+    intf_sys_t *sys = p_intf->p_sys;
+    [sys->displayer performSelectorOnMainThread:@selector(displayError:) withObject:DictFromDialogFatal(dialog) waitUntilDone:NO];
+    [pool release];
     return VLC_SUCCESS;
 }
 
 static int DisplayCritical(vlc_object_t *p_this, const char *type, vlc_value_t previous, vlc_value_t value, void *data)
 {
-    NSAutoreleasePool * o_pool = [[NSAutoreleasePool alloc] init];
-    dialog_fatal_t *p_dialog = (dialog_fatal_t *)value.p_address;
-    NSRunCriticalAlertPanel([NSString stringWithUTF8String:p_dialog->title],
-                            [NSString stringWithUTF8String:p_dialog->message],
-                            @"OK", nil, nil);
-    [o_pool release];
+    NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
+    dialog_fatal_t *dialog = value.p_address;
+    intf_thread_t *p_intf = (intf_thread_t*) p_this;
+    intf_sys_t *sys = p_intf->p_sys;
+    [sys->displayer performSelectorOnMainThread:@selector(displayCritical:) withObject:DictFromDialogFatal(dialog) waitUntilDone:NO];
+    [pool release];
     return VLC_SUCCESS;
 }
 
 static int DisplayQuestion(vlc_object_t *p_this, const char *type, vlc_value_t previous, vlc_value_t value, void *data)
 {
-    NSAutoreleasePool * o_pool = [[NSAutoreleasePool alloc] init];
-    dialog_question_t *p_dialog = (dialog_question_t *)value.p_address;
-    NSAlert *o_alert;
-    NSString *o_yes, *o_no, *o_cancel;
-    NSInteger i_returnValue = 0;
-
-    if (p_dialog->yes != NULL)
-        o_yes = [NSString stringWithUTF8String:p_dialog->yes];
-    if (p_dialog->no != NULL)
-        o_no = [NSString stringWithUTF8String:p_dialog->no];
-    if (p_dialog->cancel != NULL)
-        o_cancel = [NSString stringWithUTF8String:p_dialog->cancel];
-
-    o_alert = [NSAlert alertWithMessageText:[NSString stringWithUTF8String:p_dialog->title]
-                              defaultButton:o_yes
-                            alternateButton:o_no 
-                                otherButton:o_cancel
-                  informativeTextWithFormat:[NSString stringWithUTF8String:p_dialog->message]];
-    [o_alert setAlertStyle:NSInformationalAlertStyle];
-    i_returnValue = [o_alert runModal];
-
-    if (i_returnValue == NSAlertDefaultReturn)
-        p_dialog->answer = 1;
-    if (i_returnValue == NSAlertAlternateReturn)
-        p_dialog->answer = 2;
-    if (i_returnValue == NSAlertOtherReturn)
-        p_dialog->answer = 3;
-    [o_pool release];
+    NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
+    dialog_question_t *dialog = value.p_address;
+    intf_thread_t *p_intf = (intf_thread_t*) p_this;
+    intf_sys_t *sys = p_intf->p_sys;
+    dialog->answer = [[sys->displayer resultFromSelectorOnMainThread:@selector(displayQuestion:) withObject:DictFromDialogQuestion(dialog)] intValue];
+    [pool release];
     return VLC_SUCCESS;
 }
 
 static int DisplayLogin(vlc_object_t *p_this, const char *type, vlc_value_t previous, vlc_value_t value, void *data)
 {
-    NSAutoreleasePool * o_pool = [[NSAutoreleasePool alloc] init];
-    dialog_login_t *p_dialog = (dialog_login_t *)value.p_address;
-    NSInteger i_returnValue = 0;
-    VLCLoginPanel *thePanel = [[VLCLoginPanel alloc] init];
-    [thePanel createContentView];
-    [thePanel setDialogTitle:[NSString stringWithUTF8String:p_dialog->title]];
-    [thePanel setDialogMessage:[NSString stringWithUTF8String:p_dialog->message]];
-    [thePanel center];
-    i_returnValue = [NSApp runModalForWindow:thePanel];
-    [thePanel close];
-    if (i_returnValue) {
-        *p_dialog->username = strdup( [[thePanel userName] UTF8String] );
-        *p_dialog->password = strdup( [[thePanel password] UTF8String] );
-    } else
-        *p_dialog->username = *p_dialog->password = NULL;
-    [o_pool release];
+    NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
+    dialog_login_t *dialog = value.p_address;
+    intf_thread_t *p_intf = (intf_thread_t*) p_this;
+    intf_sys_t *sys = p_intf->p_sys;
+    NSDictionary *dict = [sys->displayer resultFromSelectorOnMainThread:@selector(displayCritical:) withObject:DictFromDialogLogin(dialog)];
+    if (dict) {
+        *dialog->username = strdup([[dict objectForKey:@"username"] UTF8String]);
+        *dialog->password = strdup([[dict objectForKey:@"password"] UTF8String]);
+    }
+    [pool release];
     return VLC_SUCCESS;
 }
 
 static int DisplayProgressPanelAction(vlc_object_t *p_this, const char *type, vlc_value_t previous, vlc_value_t value, void *data)
 {
-    NSAutoreleasePool * o_pool = [[NSAutoreleasePool alloc] init];
-    dialog_progress_bar_t * p_dialog = (dialog_progress_bar_t *)value.p_address;
+    NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
+    dialog_progress_bar_t *dialog = value.p_address;
     intf_thread_t *p_intf = (intf_thread_t*) p_this;
-    intf_sys_t *p_sys = p_intf->p_sys;
-
-    if(p_sys->currentProgressBarPanel)
-        [p_sys->currentProgressBarPanel release];
-
-    p_sys->currentProgressBarPanel = [[VLCProgressPanel alloc] init];
-    [p_sys->currentProgressBarPanel createContentView];
-    if (p_dialog->title)
-        [p_sys->currentProgressBarPanel setDialogTitle:[NSString stringWithUTF8String:p_dialog->title]];
-    if (p_dialog->message)
-        [p_sys->currentProgressBarPanel setDialogMessage:[NSString stringWithUTF8String:p_dialog->message]];
-    if (p_dialog->cancel)
-        [p_sys->currentProgressBarPanel setCancelButtonLabel:[NSString stringWithUTF8String:p_dialog->cancel]];
-    [p_sys->currentProgressBarPanel center];
-    [p_sys->currentProgressBarPanel makeKeyAndOrderFront:nil];
-
-    p_dialog->pf_update = updateProgressPanel;
-    p_dialog->pf_check = checkProgressPanel;
-    p_dialog->pf_destroy = destroyProgressPanel;
-    p_dialog->p_sys = p_intf->p_sys;
-
-    [o_pool release];
+    intf_sys_t *sys = p_intf->p_sys;
+
+    [sys->displayer performSelectorOnMainThread:@selector(displayProgressBar) withObject:DictFromDialogProgressBar(dialog) waitUntilDone:YES];
+
+    dialog->pf_update = updateProgressPanel;
+    dialog->pf_check = checkProgressPanel;
+    dialog->pf_destroy = destroyProgressPanel;
+    dialog->p_sys = p_intf->p_sys;
+
+    [pool release];
     return VLC_SUCCESS;
 }
 
 void updateProgressPanel (void *priv, const char *text, float value)
 {
-    NSAutoreleasePool *o_pool = [[NSAutoreleasePool alloc] init];
-    intf_sys_t *p_sys = (intf_sys_t *)priv;
+    NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
+    intf_sys_t *sys = (intf_sys_t *)priv;
 
-    if (text)
-        [p_sys->currentProgressBarPanel setDialogMessage:[NSString stringWithUTF8String:text]];
-    else
-        [p_sys->currentProgressBarPanel setDialogMessage:@""];
-    [p_sys->currentProgressBarPanel setProgressAsDouble:(double)(value * 1000.)];
+    NSDictionary *dict = [NSDictionary dictionaryWithObjectsAndKeys:
+                          [NSNumber numberWithFloat:value], @"value",
+                          text ? [NSString stringWithUTF8String:text] : nil, @"text",
+                          nil];
+                          
+    [sys->displayer performSelectorOnMainThread:@selector(updateProgressPanel) withObject:dict waitUntilDone:YES];
 
-    [o_pool release];
+    [pool release];
 }
 
 void destroyProgressPanel (void *priv)
 {
-    NSAutoreleasePool *o_pool = [[NSAutoreleasePool alloc] init];
-    intf_sys_t *p_sys = (intf_sys_t *)priv;
+    NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
+    intf_sys_t *sys = (intf_sys_t *)priv;
 
-    [p_sys->currentProgressBarPanel close];
-    [p_sys->currentProgressBarPanel release];
-
-    [o_pool release];
+    [sys->displayer performSelectorOnMainThread:@selector(destroyProgressPanel) withObject:nil waitUntilDone:YES];
+    
+    [pool release];
 }
 
 bool checkProgressPanel (void *priv)
 {
-    NSAutoreleasePool *o_pool = [[NSAutoreleasePool alloc] init];
-    intf_sys_t *p_sys = (intf_sys_t *)priv;
-    BOOL b_returned;
+    NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
+    intf_sys_t *sys = (intf_sys_t *)priv;
+    BOOL ret;
+
+    ret = [[sys->displayer resultFromSelectorOnMainThread:@selector(checkProgressPanel) withObject:nil] boolValue];
+
+    [pool release];
+    return ret;
+}
+
+
+@implementation VLCDialogDisplayer
+- (void)dealloc
+{
+    assert(!_currentProgressBarPanel); // This has to be closed on main thread.
+    [super dealloc];
+}
+
++ (NSDictionary *)dictionaryForDialog:(const char *)title :(const char *)message :(const char *)yes :(const char *)no :(const char *)cancel
+{
+    NSMutableDictionary *dict = [NSMutableDictionary dictionary];
+    if (title)
+        [dict setObject:[NSString stringWithUTF8String:title] forKey:@"title"];
+    if (message)
+        [dict setObject:[NSString stringWithUTF8String:message] forKey:@"message"];
+    if (yes)
+        [dict setObject:[NSString stringWithUTF8String:yes] forKey:@"yes"];
+    if (no)
+        [dict setObject:[NSString stringWithUTF8String:no] forKey:@"no"];
+    if (cancel)
+        [dict setObject:[NSString stringWithUTF8String:cancel] forKey:@"cancel"];
+    
+    return dict;
+}
+#define VLCAssertIsMainThread() assert([NSThread isMainThread])
+
+- (void)displayError:(NSDictionary *)dialog
+{
+    VLCAssertIsMainThread();
+    
+    NSRunInformationalAlertPanel([dialog objectForKey:@"title"],
+                                 [dialog objectForKey:@"message"],
+                                 @"OK", nil, nil);
+}
+
+- (void)displayCritical:(NSDictionary *)dialog
+{
+    VLCAssertIsMainThread();
+    
+    NSRunCriticalAlertPanel([dialog objectForKey:@"title"],
+                                 [dialog objectForKey:@"message"],
+                                 @"OK", nil, nil);
+}
+
+- (NSNumber *)displayQuestion:(NSDictionary *)dialog
+{
+    VLCAssertIsMainThread();
+    
+    NSInteger alertRet = 0;
+    
+    NSAlert *alert = [NSAlert alertWithMessageText:[dialog objectForKey:@"title"]
+                              defaultButton:[dialog objectForKey:@"yes"]
+                            alternateButton:[dialog objectForKey:@"no"] 
+                                otherButton:[dialog objectForKey:@"cancel"]
+                  informativeTextWithFormat:[dialog objectForKey:@"message"]];
+    [alert setAlertStyle:NSInformationalAlertStyle];
+    alertRet = [alert runModal];
+
+    int ret;
+    switch (alertRet) {
+        case NSAlertDefaultReturn:
+            ret = 1;
+            break;
+        case NSAlertAlternateReturn:
+            ret = 2;
+            break;
+        case NSAlertOtherReturn:
+            ret = 3;
+            break;
+        default:
+            assert(0);
+            ret = 0;
+            break;
+    }
+
+    return [NSNumber numberWithInt:ret];
+}
+
+- (NSDictionary *)displayLogin:(NSDictionary *)dialog
+{
+    VLCAssertIsMainThread();
+
+    VLCLoginPanel *panel = [[VLCLoginPanel alloc] init];
+    [panel createContentView];
+    [panel setDialogTitle:[dialog objectForKey:@"title"]];
+    [panel setDialogMessage:[dialog objectForKey:@"message"]];
+    [panel center];
+    NSInteger ret = [NSApp runModalForWindow:panel];
+    [panel close];
+    
+    if (!ret)
+        return nil;
+    
+    return [NSDictionary dictionaryWithObjectsAndKeys:
+            [panel userName], @"username",
+            [panel password], @"password",
+            nil];
+}
 
-    b_returned = [p_sys->currentProgressBarPanel isCancelled];
+- (void)displayProgressBar:(NSDictionary *)dialog
+{
+    VLCAssertIsMainThread();
 
-    [o_pool release];
-    return b_returned;
+    if(_currentProgressBarPanel)
+        [self destroyProgressPanel];
+
+    assert(!_currentProgressBarPanel);
+    _currentProgressBarPanel = [[VLCProgressPanel alloc] init];
+    [_currentProgressBarPanel createContentView];
+    [_currentProgressBarPanel setDialogTitle:[dialog objectForKey:@"title"]];
+    [_currentProgressBarPanel setDialogMessage:[dialog objectForKey:@"message"]];
+    [_currentProgressBarPanel setCancelButtonLabel:[dialog objectForKey:@"cancel"]];
+
+    [_currentProgressBarPanel center];
+    [_currentProgressBarPanel makeKeyAndOrderFront:nil];
+}
+
+- (void)updateProgressPanel:(NSDictionary *)dict
+{
+    VLCAssertIsMainThread();
+
+    assert(_currentProgressBarPanel);
+    [_currentProgressBarPanel setDialogMessage:[dict objectForKey:@"text"] ?: @""];
+    [_currentProgressBarPanel setProgressAsDouble:[[dict objectForKey:@"value"] doubleValue] * 1000.];
+}
+
+- (void)destroyProgressPanel
+{
+    VLCAssertIsMainThread();
+
+    [_currentProgressBarPanel close];
+    [_currentProgressBarPanel release];
+    _currentProgressBarPanel = nil;
+}
+
+- (NSNumber *)checkProgressPanel
+{
+    VLCAssertIsMainThread();
+    
+    return [NSNumber numberWithBool:[_currentProgressBarPanel isCancelled]];
+}
+
+
+/**
+ * Helper to execute a function on main thread and get its return value.
+ */
+- (void)execute:(NSDictionary *)dict
+{
+    SEL sel = [[dict objectForKey:@"sel"] pointerValue];
+    id * result = [[dict objectForKey:@"result"] pointerValue];
+    id object = [dict objectForKey:@"object"];
+    *result = [self performSelector:sel withObject:object];
+    [*result retain]; // Balanced in -resultFromSelectorOnMainThread
+}
+
+- (id)resultFromSelectorOnMainThread:(SEL)sel withObject:(id)object
+{
+    id result = nil;
+    [NSDictionary dictionaryWithObjectsAndKeys:
+     [NSValue valueWithPointer:sel], @"sel",
+     [NSValue valueWithPointer:&result], @"result",
+     object, @"object", nil];
+    [self performSelectorOnMainThread:@selector(execute:) withObject:object waitUntilDone:YES];
+    return [result autorelease];
 }
+@end
+                                  
+