1 /*****************************************************************************
2  * dialog.c: User dialog functions
3  *****************************************************************************
4  * Copyright © 2009 Rémi Denis-Courmont
5  * Copyright © 2016 VLC authors and VideoLAN
6  *
7  * This program is free software; you can redistribute it and/or modify it
8  * under the terms of the GNU Lesser General Public License as published by
9  * the Free Software Foundation; either version 2.1 of the License, or
10  * (at your option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15  * GNU Lesser General Public License for more details.
16  *
17  * You should have received a copy of the GNU Lesser General Public License
18  * along with this program; if not, write to the Free Software Foundation,
19  * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
20  *****************************************************************************/
21 
22 /** @ingroup vlc_dialog */
23 #ifdef HAVE_CONFIG_H
24 # include "config.h"
25 #endif
26 
27 #include <stdarg.h>
28 
29 #include <vlc_common.h>
30 #include <vlc_dialog.h>
31 #include <vlc_interrupt.h>
32 #include <vlc_extensions.h>
33 #include <assert.h>
34 #include "libvlc.h"
35 
36 struct vlc_dialog_provider
37 {
38     vlc_mutex_t                 lock;
39     vlc_array_t                 dialog_array;
40     vlc_dialog_cbs              cbs;
41     void *                      p_cbs_data;
42 
43     vlc_dialog_ext_update_cb    pf_ext_update;
44     void *                      p_ext_data;
45 };
46 
47 enum dialog_type
48 {
49     VLC_DIALOG_ERROR,
50     VLC_DIALOG_LOGIN,
51     VLC_DIALOG_QUESTION,
52     VLC_DIALOG_PROGRESS,
53 };
54 
55 struct dialog_answer
56 {
57     enum dialog_type i_type;
58     union
59     {
60         struct
61         {
62             char *psz_username;
63             char *psz_password;
64             bool b_store;
65         } login;
66         struct
67         {
68             int i_action;
69         } question;
70     } u;
71 };
72 
73 struct dialog
74 {
75     enum dialog_type i_type;
76     const char *psz_title;
77     const char *psz_text;
78 
79     union
80     {
81         struct
82         {
83             const char *psz_default_username;
84             bool b_ask_store;
85         } login;
86         struct
87         {
88             vlc_dialog_question_type i_type;
89             const char *psz_cancel;
90             const char *psz_action1;
91             const char *psz_action2;
92         } question;
93         struct
94         {
95             bool b_indeterminate;
96             float f_position;
97             const char *psz_cancel;
98         } progress;
99     } u;
100 };
101 
102 struct vlc_dialog_id
103 {
104     vlc_mutex_t             lock;
105     vlc_cond_t              wait;
106     enum dialog_type        i_type;
107     void *                  p_context;
108     int                     i_refcount;
109     bool                    b_cancelled;
110     bool                    b_answered;
111     bool                    b_progress_indeterminate;
112     char *                  psz_progress_text;
113     struct dialog_answer    answer;
114 };
115 
116 struct dialog_i11e_context
117 {
118     vlc_dialog_provider *   p_provider;
119     vlc_dialog_id *         p_id;
120 };
121 
122 static inline vlc_dialog_provider *
get_dialog_provider(vlc_object_t * p_obj,bool b_check_interact)123 get_dialog_provider(vlc_object_t *p_obj, bool b_check_interact)
124 {
125     if (b_check_interact && p_obj->obj.flags & OBJECT_FLAGS_NOINTERACT)
126         return NULL;
127 
128     vlc_dialog_provider *p_provider =
129         libvlc_priv(p_obj->obj.libvlc)->p_dialog_provider;
130     assert(p_provider != NULL);
131     return p_provider;
132 }
133 
134 static void
dialog_id_release(vlc_dialog_id * p_id)135 dialog_id_release(vlc_dialog_id *p_id)
136 {
137     if (p_id->answer.i_type == VLC_DIALOG_LOGIN)
138     {
139         free(p_id->answer.u.login.psz_username);
140         free(p_id->answer.u.login.psz_password);
141     }
142     free(p_id->psz_progress_text);
143     vlc_mutex_destroy(&p_id->lock);
144     vlc_cond_destroy(&p_id->wait);
145     free(p_id);
146 }
147 
148 int
libvlc_InternalDialogInit(libvlc_int_t * p_libvlc)149 libvlc_InternalDialogInit(libvlc_int_t *p_libvlc)
150 {
151     assert(p_libvlc != NULL);
152     vlc_dialog_provider *p_provider = malloc(sizeof(*p_provider));
153     if (p_provider == NULL)
154         return VLC_EGENERIC;
155 
156     vlc_mutex_init(&p_provider->lock);
157     vlc_array_init(&p_provider->dialog_array);
158 
159     memset(&p_provider->cbs, 0, sizeof(p_provider->cbs));
160     p_provider->p_cbs_data = NULL;
161 
162     p_provider->pf_ext_update = NULL;
163     p_provider->p_ext_data = NULL;
164     libvlc_priv(p_libvlc)->p_dialog_provider = p_provider;
165 
166     return VLC_SUCCESS;
167 }
168 
169 static void
dialog_cancel_locked(vlc_dialog_provider * p_provider,vlc_dialog_id * p_id)170 dialog_cancel_locked(vlc_dialog_provider *p_provider, vlc_dialog_id *p_id)
171 {
172     vlc_mutex_lock(&p_id->lock);
173     if (p_id->b_cancelled || p_id->b_answered)
174     {
175         vlc_mutex_unlock(&p_id->lock);
176         return;
177     }
178     p_id->b_cancelled = true;
179     vlc_mutex_unlock(&p_id->lock);
180 
181     p_provider->cbs.pf_cancel(p_provider->p_cbs_data, p_id);
182 }
183 
184 static vlc_dialog_id *
dialog_add_locked(vlc_dialog_provider * p_provider,enum dialog_type i_type)185 dialog_add_locked(vlc_dialog_provider *p_provider, enum dialog_type i_type)
186 {
187     vlc_dialog_id *p_id = calloc(1, sizeof(*p_id));
188 
189     if (p_id == NULL)
190         return NULL;
191 
192     if(vlc_array_append(&p_provider->dialog_array, p_id))
193     {
194         free(p_id);
195         return NULL;
196     }
197 
198     vlc_mutex_init(&p_id->lock);
199     vlc_cond_init(&p_id->wait);
200 
201     p_id->i_type = i_type;
202     p_id->i_refcount = 2; /* provider and callbacks */
203 
204     return p_id;
205 }
206 
207 static void
dialog_remove_locked(vlc_dialog_provider * p_provider,vlc_dialog_id * p_id)208 dialog_remove_locked(vlc_dialog_provider *p_provider, vlc_dialog_id *p_id)
209 {
210     ssize_t i_idx = vlc_array_index_of_item(&p_provider->dialog_array, p_id);
211     assert(i_idx >= 0);
212     vlc_array_remove(&p_provider->dialog_array, i_idx);
213 
214     vlc_mutex_lock(&p_id->lock);
215     p_id->i_refcount--;
216     if (p_id->i_refcount == 0)
217     {
218         vlc_mutex_unlock(&p_id->lock);
219         dialog_id_release(p_id);
220     }
221     else
222         vlc_mutex_unlock(&p_id->lock);
223 }
224 
225 static void
dialog_clear_all_locked(vlc_dialog_provider * p_provider)226 dialog_clear_all_locked(vlc_dialog_provider *p_provider)
227 {
228     for (size_t i = 0; i < vlc_array_count(&p_provider->dialog_array); ++i)
229     {
230         vlc_dialog_id *p_id =
231             vlc_array_item_at_index(&p_provider->dialog_array, i);
232         dialog_cancel_locked(p_provider, p_id);
233     }
234 }
235 
236 void
libvlc_InternalDialogClean(libvlc_int_t * p_libvlc)237 libvlc_InternalDialogClean(libvlc_int_t *p_libvlc)
238 {
239     assert(p_libvlc != NULL);
240     vlc_dialog_provider *p_provider = libvlc_priv(p_libvlc)->p_dialog_provider;
241 
242     if (p_provider == NULL)
243         return;
244     vlc_mutex_lock(&p_provider->lock);
245     dialog_clear_all_locked(p_provider);
246     vlc_mutex_unlock(&p_provider->lock);
247 
248     vlc_mutex_destroy(&p_provider->lock);
249     free(p_provider);
250     libvlc_priv(p_libvlc)->p_dialog_provider = NULL;
251 }
252 
253 #undef vlc_dialog_provider_set_callbacks
254 void
vlc_dialog_provider_set_callbacks(vlc_object_t * p_obj,const vlc_dialog_cbs * p_cbs,void * p_data)255 vlc_dialog_provider_set_callbacks(vlc_object_t *p_obj,
256                                   const vlc_dialog_cbs *p_cbs, void *p_data)
257 {
258     assert(p_obj != NULL);
259     vlc_dialog_provider *p_provider = get_dialog_provider(p_obj, false);
260 
261     vlc_mutex_lock(&p_provider->lock);
262     dialog_clear_all_locked(p_provider);
263 
264     if (p_cbs == NULL)
265     {
266         memset(&p_provider->cbs, 0, sizeof(p_provider->cbs));
267         p_provider->p_cbs_data = NULL;
268     }
269     else
270     {
271         p_provider->cbs = *p_cbs;
272         p_provider->p_cbs_data = p_data;
273     }
274     vlc_mutex_unlock(&p_provider->lock);
275 }
276 
277 static void
dialog_wait_interrupted(void * p_data)278 dialog_wait_interrupted(void *p_data)
279 {
280     struct dialog_i11e_context *p_context = p_data;
281     vlc_dialog_provider *p_provider = p_context->p_provider;
282     vlc_dialog_id *p_id = p_context->p_id;
283 
284     vlc_mutex_lock(&p_provider->lock);
285     dialog_cancel_locked(p_provider, p_id);
286     vlc_mutex_unlock(&p_provider->lock);
287 
288     vlc_mutex_lock(&p_id->lock);
289     vlc_cond_signal(&p_id->wait);
290     vlc_mutex_unlock(&p_id->lock);
291 }
292 
293 static int
dialog_wait(vlc_dialog_provider * p_provider,vlc_dialog_id * p_id,enum dialog_type i_type,struct dialog_answer * p_answer)294 dialog_wait(vlc_dialog_provider *p_provider, vlc_dialog_id *p_id,
295             enum dialog_type i_type, struct dialog_answer *p_answer)
296 {
297     struct dialog_i11e_context context = {
298         .p_provider = p_provider,
299         .p_id = p_id,
300     };
301     vlc_interrupt_register(dialog_wait_interrupted, &context);
302 
303     vlc_mutex_lock(&p_id->lock);
304     /* Wait for the dialog to be dismissed, interrupted or answered */
305     while (!p_id->b_cancelled && !p_id->b_answered)
306         vlc_cond_wait(&p_id->wait, &p_id->lock);
307 
308     int i_ret;
309     if (p_id->b_cancelled)
310         i_ret = 0;
311     else if (p_id->answer.i_type != i_type)
312         i_ret = VLC_EGENERIC;
313     else
314     {
315         i_ret = 1;
316         memcpy(p_answer, &p_id->answer, sizeof(p_id->answer));
317         memset(&p_id->answer, 0, sizeof(p_id->answer));
318     }
319 
320     vlc_mutex_unlock(&p_id->lock);
321     vlc_interrupt_unregister();
322 
323     vlc_mutex_lock(&p_provider->lock);
324     dialog_remove_locked(p_provider, p_id);
325     vlc_mutex_unlock(&p_provider->lock);
326     return i_ret;
327 }
328 
329 static int
dialog_display_error_va(vlc_dialog_provider * p_provider,const char * psz_title,const char * psz_fmt,va_list ap)330 dialog_display_error_va(vlc_dialog_provider *p_provider, const char *psz_title,
331                         const char *psz_fmt, va_list ap)
332 {
333     vlc_mutex_lock(&p_provider->lock);
334     if (p_provider->cbs.pf_display_error == NULL)
335     {
336         vlc_mutex_unlock(&p_provider->lock);
337         return VLC_EGENERIC;
338     }
339 
340     char *psz_text;
341     if (vasprintf(&psz_text, psz_fmt, ap) == -1)
342     {
343         vlc_mutex_unlock(&p_provider->lock);
344         return VLC_ENOMEM;
345     }
346 
347     p_provider->cbs.pf_display_error(p_provider->p_cbs_data, psz_title, psz_text);
348     free(psz_text);
349     vlc_mutex_unlock(&p_provider->lock);
350 
351     return VLC_SUCCESS;
352 }
353 
354 int
vlc_dialog_display_error_va(vlc_object_t * p_obj,const char * psz_title,const char * psz_fmt,va_list ap)355 vlc_dialog_display_error_va(vlc_object_t *p_obj, const char *psz_title,
356                             const char *psz_fmt, va_list ap)
357 {
358     assert(p_obj != NULL && psz_title != NULL && psz_fmt != NULL);
359     int i_ret;
360     vlc_dialog_provider *p_provider = get_dialog_provider(p_obj, true);
361 
362     if (p_provider != NULL)
363         i_ret = dialog_display_error_va(p_provider, psz_title, psz_fmt, ap);
364     else
365         i_ret = VLC_EGENERIC;
366 
367     if (i_ret != VLC_SUCCESS)
368     {
369         msg_Err(p_obj, "%s", psz_title);
370         msg_GenericVa(p_obj, VLC_MSG_ERR, psz_fmt, ap);
371     }
372     return i_ret;
373 }
374 
375 
376 #undef vlc_dialog_display_error
377 int
vlc_dialog_display_error(vlc_object_t * p_obj,const char * psz_title,const char * psz_fmt,...)378 vlc_dialog_display_error(vlc_object_t *p_obj, const char *psz_title,
379                          const char *psz_fmt, ...)
380 {
381     assert(psz_fmt != NULL);
382     va_list ap;
383     va_start(ap, psz_fmt);
384     int i_ret = vlc_dialog_display_error_va(p_obj, psz_title, psz_fmt, ap);
385     va_end(ap);
386     return i_ret;
387 }
388 
389 static int
dialog_display_login_va(vlc_dialog_provider * p_provider,vlc_dialog_id ** pp_id,const char * psz_default_username,bool b_ask_store,const char * psz_title,const char * psz_fmt,va_list ap)390 dialog_display_login_va(vlc_dialog_provider *p_provider, vlc_dialog_id **pp_id,
391                         const char *psz_default_username, bool b_ask_store,
392                         const char *psz_title, const char *psz_fmt, va_list ap)
393 {
394     vlc_mutex_lock(&p_provider->lock);
395     if (p_provider->cbs.pf_display_login == NULL
396      || p_provider->cbs.pf_cancel == NULL)
397     {
398         vlc_mutex_unlock(&p_provider->lock);
399         return VLC_EGENERIC;
400     }
401 
402     char *psz_text;
403     if (vasprintf(&psz_text, psz_fmt, ap) == -1)
404     {
405         vlc_mutex_unlock(&p_provider->lock);
406         return VLC_ENOMEM;
407     }
408 
409     vlc_dialog_id *p_id = dialog_add_locked(p_provider, VLC_DIALOG_LOGIN);
410     if (p_id == NULL)
411     {
412         free(psz_text);
413         vlc_mutex_unlock(&p_provider->lock);
414         return VLC_ENOMEM;
415     }
416     p_provider->cbs.pf_display_login(p_provider->p_cbs_data, p_id, psz_title,
417                                      psz_text, psz_default_username, b_ask_store);
418     free(psz_text);
419     vlc_mutex_unlock(&p_provider->lock);
420     *pp_id = p_id;
421 
422     return VLC_SUCCESS;
423 }
424 
425 int
vlc_dialog_wait_login_va(vlc_object_t * p_obj,char ** ppsz_username,char ** ppsz_password,bool * p_store,const char * psz_default_username,const char * psz_title,const char * psz_fmt,va_list ap)426 vlc_dialog_wait_login_va(vlc_object_t *p_obj,  char **ppsz_username,
427                          char **ppsz_password, bool *p_store,
428                          const char *psz_default_username,
429                          const char *psz_title, const char *psz_fmt, va_list ap)
430 {
431     assert(p_obj != NULL && ppsz_username != NULL && ppsz_password != NULL
432         && psz_fmt != NULL && psz_title != NULL);
433 
434     vlc_dialog_provider *p_provider = get_dialog_provider(p_obj, true);
435     if (p_provider == NULL)
436         return VLC_EGENERIC;
437 
438     vlc_dialog_id *p_id;
439     int i_ret = dialog_display_login_va(p_provider, &p_id, psz_default_username,
440                                         p_store != NULL, psz_title, psz_fmt, ap);
441     if (i_ret < 0 || p_id == NULL)
442         return i_ret;
443 
444     struct dialog_answer answer;
445     i_ret = dialog_wait(p_provider, p_id, VLC_DIALOG_LOGIN, &answer);
446     if (i_ret <= 0)
447         return i_ret;
448 
449     *ppsz_username = answer.u.login.psz_username;
450     *ppsz_password = answer.u.login.psz_password;
451     if (p_store != NULL)
452         *p_store = answer.u.login.b_store;
453 
454     return 1;
455 }
456 
457 #undef vlc_dialog_wait_login
458 int
vlc_dialog_wait_login(vlc_object_t * p_obj,char ** ppsz_username,char ** ppsz_password,bool * p_store,const char * psz_default_username,const char * psz_title,const char * psz_fmt,...)459 vlc_dialog_wait_login(vlc_object_t *p_obj,  char **ppsz_username,
460                       char **ppsz_password, bool *p_store,
461                       const char *psz_default_username, const char *psz_title,
462                       const char *psz_fmt, ...)
463 {
464     assert(psz_fmt != NULL);
465     va_list ap;
466     va_start(ap, psz_fmt);
467     int i_ret = vlc_dialog_wait_login_va(p_obj, ppsz_username, ppsz_password,
468                                          p_store,psz_default_username,
469                                          psz_title, psz_fmt, ap);
470     va_end(ap);
471     return i_ret;
472 }
473 
474 static int
dialog_display_question_va(vlc_dialog_provider * p_provider,vlc_dialog_id ** pp_id,vlc_dialog_question_type i_type,const char * psz_cancel,const char * psz_action1,const char * psz_action2,const char * psz_title,const char * psz_fmt,va_list ap)475 dialog_display_question_va(vlc_dialog_provider *p_provider, vlc_dialog_id **pp_id,
476                            vlc_dialog_question_type i_type,
477                            const char *psz_cancel, const char *psz_action1,
478                            const char *psz_action2, const char *psz_title,
479                            const char *psz_fmt, va_list ap)
480 {
481     vlc_mutex_lock(&p_provider->lock);
482     if (p_provider->cbs.pf_display_question == NULL
483      || p_provider->cbs.pf_cancel == NULL)
484     {
485         vlc_mutex_unlock(&p_provider->lock);
486         return VLC_EGENERIC;
487     }
488 
489     char *psz_text;
490     if (vasprintf(&psz_text, psz_fmt, ap) == -1)
491     {
492         vlc_mutex_unlock(&p_provider->lock);
493         return VLC_ENOMEM;
494     }
495 
496     vlc_dialog_id *p_id = dialog_add_locked(p_provider, VLC_DIALOG_QUESTION);
497     if (p_id == NULL)
498     {
499         free(psz_text);
500         vlc_mutex_unlock(&p_provider->lock);
501         return VLC_ENOMEM;
502     }
503     p_provider->cbs.pf_display_question(p_provider->p_cbs_data, p_id, psz_title,
504                                         psz_text, i_type, psz_cancel, psz_action1,
505                                         psz_action2);
506     free(psz_text);
507     vlc_mutex_unlock(&p_provider->lock);
508     *pp_id = p_id;
509 
510     return VLC_SUCCESS;
511 }
512 
513 int
vlc_dialog_wait_question_va(vlc_object_t * p_obj,vlc_dialog_question_type i_type,const char * psz_cancel,const char * psz_action1,const char * psz_action2,const char * psz_title,const char * psz_fmt,va_list ap)514 vlc_dialog_wait_question_va(vlc_object_t *p_obj,
515                             vlc_dialog_question_type i_type,
516                             const char *psz_cancel, const char *psz_action1,
517                             const char *psz_action2, const char *psz_title,
518                             const char *psz_fmt, va_list ap)
519 {
520     assert(p_obj != NULL && psz_fmt != NULL && psz_title != NULL
521         && psz_cancel != NULL);
522 
523     vlc_dialog_provider *p_provider = get_dialog_provider(p_obj, true);
524     if (p_provider == NULL)
525         return VLC_EGENERIC;
526 
527     vlc_dialog_id *p_id;
528     int i_ret = dialog_display_question_va(p_provider, &p_id, i_type,
529                                            psz_cancel, psz_action1,
530                                            psz_action2, psz_title, psz_fmt, ap);
531     if (i_ret < 0 || p_id == NULL)
532         return i_ret;
533 
534     struct dialog_answer answer;
535     i_ret = dialog_wait(p_provider, p_id, VLC_DIALOG_QUESTION, &answer);
536     if (i_ret <= 0)
537         return i_ret;
538 
539     if (answer.u.question.i_action != 1 && answer.u.question.i_action != 2)
540         return VLC_EGENERIC;
541 
542     return answer.u.question.i_action;
543 }
544 
545 #undef vlc_dialog_wait_question
546 int
vlc_dialog_wait_question(vlc_object_t * p_obj,vlc_dialog_question_type i_type,const char * psz_cancel,const char * psz_action1,const char * psz_action2,const char * psz_title,const char * psz_fmt,...)547 vlc_dialog_wait_question(vlc_object_t *p_obj,
548                          vlc_dialog_question_type i_type,
549                          const char *psz_cancel, const char *psz_action1,
550                          const char *psz_action2, const char *psz_title,
551                          const char *psz_fmt, ...)
552 {
553     assert(psz_fmt != NULL);
554     va_list ap;
555     va_start(ap, psz_fmt);
556     int i_ret = vlc_dialog_wait_question_va(p_obj, i_type, psz_cancel,
557                                             psz_action1, psz_action2, psz_title,
558                                             psz_fmt, ap);
559     va_end(ap);
560     return i_ret;
561 }
562 
563 static int
display_progress_va(vlc_dialog_provider * p_provider,vlc_dialog_id ** pp_id,bool b_indeterminate,float f_position,const char * psz_cancel,const char * psz_title,const char * psz_fmt,va_list ap)564 display_progress_va(vlc_dialog_provider *p_provider, vlc_dialog_id **pp_id,
565                     bool b_indeterminate, float f_position,
566                     const char *psz_cancel, const char *psz_title,
567                     const char *psz_fmt, va_list ap)
568 {
569     vlc_mutex_lock(&p_provider->lock);
570     if (p_provider->cbs.pf_display_progress == NULL
571      || p_provider->cbs.pf_update_progress == NULL
572      || p_provider->cbs.pf_cancel == NULL)
573     {
574         vlc_mutex_unlock(&p_provider->lock);
575         return VLC_EGENERIC;
576     }
577 
578     char *psz_text;
579     if (vasprintf(&psz_text, psz_fmt, ap) == -1)
580     {
581         vlc_mutex_unlock(&p_provider->lock);
582         return VLC_ENOMEM;
583     }
584 
585     vlc_dialog_id *p_id = dialog_add_locked(p_provider, VLC_DIALOG_PROGRESS);
586     if (p_id == NULL)
587     {
588         free(psz_text);
589         vlc_mutex_unlock(&p_provider->lock);
590         return VLC_ENOMEM;
591     }
592     p_id->b_progress_indeterminate = b_indeterminate;
593     p_id->psz_progress_text = psz_text;
594     p_provider->cbs.pf_display_progress(p_provider->p_cbs_data, p_id, psz_title,
595                                         psz_text, b_indeterminate, f_position,
596                                         psz_cancel);
597     vlc_mutex_unlock(&p_provider->lock);
598     *pp_id = p_id;
599 
600     return VLC_SUCCESS;
601 }
602 
603 vlc_dialog_id *
vlc_dialog_display_progress_va(vlc_object_t * p_obj,bool b_indeterminate,float f_position,const char * psz_cancel,const char * psz_title,const char * psz_fmt,va_list ap)604 vlc_dialog_display_progress_va(vlc_object_t *p_obj, bool b_indeterminate,
605                                float f_position, const char *psz_cancel,
606                                const char *psz_title, const char *psz_fmt,
607                                va_list ap)
608 {
609     assert(p_obj != NULL && psz_title != NULL && psz_fmt != NULL);
610 
611     vlc_dialog_provider *p_provider = get_dialog_provider(p_obj, true);
612     if (p_provider == NULL)
613         return NULL;
614     vlc_dialog_id *p_id;
615     int i_ret = display_progress_va(p_provider, &p_id, b_indeterminate,
616                                     f_position, psz_cancel, psz_title, psz_fmt,
617                                     ap);
618     return i_ret == VLC_SUCCESS ? p_id : NULL;
619 }
620 
621 #undef vlc_dialog_display_progress
622 vlc_dialog_id *
vlc_dialog_display_progress(vlc_object_t * p_obj,bool b_indeterminate,float f_position,const char * psz_cancel,const char * psz_title,const char * psz_fmt,...)623 vlc_dialog_display_progress(vlc_object_t *p_obj, bool b_indeterminate,
624                             float f_position, const char *psz_cancel,
625                             const char *psz_title, const char *psz_fmt, ...)
626 {
627     assert(psz_fmt != NULL);
628     va_list ap;
629     va_start(ap, psz_fmt);
630     vlc_dialog_id *p_id =
631         vlc_dialog_display_progress_va(p_obj, b_indeterminate, f_position,
632                                        psz_cancel, psz_title, psz_fmt, ap);
633     va_end(ap);
634     return p_id;
635 }
636 
637 static int
dialog_update_progress(vlc_object_t * p_obj,vlc_dialog_id * p_id,float f_value,char * psz_text)638 dialog_update_progress(vlc_object_t *p_obj, vlc_dialog_id *p_id, float f_value,
639                        char *psz_text)
640 {
641     assert(p_obj != NULL && p_id != NULL);
642     vlc_dialog_provider *p_provider = get_dialog_provider(p_obj, false);
643 
644     vlc_mutex_lock(&p_provider->lock);
645     if (p_provider->cbs.pf_update_progress == NULL ||
646         vlc_dialog_is_cancelled(p_obj, p_id))
647     {
648         vlc_mutex_unlock(&p_provider->lock);
649         free(psz_text);
650         return VLC_EGENERIC;
651     }
652 
653     if (p_id->b_progress_indeterminate)
654         f_value = 0.0f;
655 
656     if (psz_text != NULL)
657     {
658         free(p_id->psz_progress_text);
659         p_id->psz_progress_text = psz_text;
660     }
661     p_provider->cbs.pf_update_progress(p_provider->p_cbs_data, p_id, f_value,
662                                        p_id->psz_progress_text);
663 
664     vlc_mutex_unlock(&p_provider->lock);
665     return VLC_SUCCESS;
666 }
667 
668 #undef vlc_dialog_update_progress
669 int
vlc_dialog_update_progress(vlc_object_t * p_obj,vlc_dialog_id * p_id,float f_value)670 vlc_dialog_update_progress(vlc_object_t *p_obj, vlc_dialog_id *p_id,
671                            float f_value)
672 {
673     return dialog_update_progress(p_obj, p_id, f_value, NULL);
674 }
675 
676 int
vlc_dialog_update_progress_text_va(vlc_object_t * p_obj,vlc_dialog_id * p_id,float f_value,const char * psz_fmt,va_list ap)677 vlc_dialog_update_progress_text_va(vlc_object_t *p_obj, vlc_dialog_id *p_id,
678                                    float f_value, const char *psz_fmt,
679                                    va_list ap)
680 {
681     assert(psz_fmt != NULL);
682 
683     char *psz_text;
684     if (vasprintf(&psz_text, psz_fmt, ap) == -1)
685         return VLC_ENOMEM;
686     return dialog_update_progress(p_obj, p_id, f_value, psz_text);
687 }
688 
689 #undef vlc_dialog_update_progress_text
690 int
vlc_dialog_update_progress_text(vlc_object_t * p_obj,vlc_dialog_id * p_id,float f_value,const char * psz_fmt,...)691 vlc_dialog_update_progress_text(vlc_object_t *p_obj, vlc_dialog_id *p_id,
692                                 float f_value, const char *psz_fmt, ...)
693 {
694     assert(psz_fmt != NULL);
695     va_list ap;
696     va_start(ap, psz_fmt);
697     int i_ret = vlc_dialog_update_progress_text_va(p_obj, p_id, f_value,
698                                                    psz_fmt, ap);
699     va_end(ap);
700     return i_ret;
701 }
702 
703 #undef vlc_dialog_release
704 void
vlc_dialog_release(vlc_object_t * p_obj,vlc_dialog_id * p_id)705 vlc_dialog_release(vlc_object_t *p_obj, vlc_dialog_id *p_id)
706 {
707     assert(p_obj != NULL && p_id != NULL);
708     vlc_dialog_provider *p_provider = get_dialog_provider(p_obj, false);
709 
710     vlc_mutex_lock(&p_provider->lock);
711     dialog_cancel_locked(p_provider, p_id);
712     dialog_remove_locked(p_provider, p_id);
713     vlc_mutex_unlock(&p_provider->lock);
714 }
715 
716 #undef vlc_dialog_is_cancelled
717 bool
vlc_dialog_is_cancelled(vlc_object_t * p_obj,vlc_dialog_id * p_id)718 vlc_dialog_is_cancelled(vlc_object_t *p_obj, vlc_dialog_id *p_id)
719 {
720     (void) p_obj;
721     assert(p_id != NULL);
722 
723     vlc_mutex_lock(&p_id->lock);
724     bool b_cancelled = p_id->b_cancelled;
725     vlc_mutex_unlock(&p_id->lock);
726     return b_cancelled;
727 }
728 
729 void
vlc_dialog_id_set_context(vlc_dialog_id * p_id,void * p_context)730 vlc_dialog_id_set_context(vlc_dialog_id *p_id, void *p_context)
731 {
732     vlc_mutex_lock(&p_id->lock);
733     p_id->p_context = p_context;
734     vlc_mutex_unlock(&p_id->lock);
735 }
736 
737 void *
vlc_dialog_id_get_context(vlc_dialog_id * p_id)738 vlc_dialog_id_get_context(vlc_dialog_id *p_id)
739 {
740     assert(p_id != NULL);
741     vlc_mutex_lock(&p_id->lock);
742     void *p_context = p_id->p_context;
743     vlc_mutex_unlock(&p_id->lock);
744     return p_context;
745 }
746 
747 static int
dialog_id_post(vlc_dialog_id * p_id,struct dialog_answer * p_answer)748 dialog_id_post(vlc_dialog_id *p_id, struct dialog_answer *p_answer)
749 {
750     vlc_mutex_lock(&p_id->lock);
751     if (p_answer == NULL)
752     {
753         p_id->b_cancelled = true;
754     }
755     else
756     {
757         p_id->answer = *p_answer;
758         p_id->b_answered = true;
759     }
760     p_id->i_refcount--;
761     if (p_id->i_refcount > 0)
762     {
763         vlc_cond_signal(&p_id->wait);
764         vlc_mutex_unlock(&p_id->lock);
765     }
766     else
767     {
768         vlc_mutex_unlock(&p_id->lock);
769         dialog_id_release(p_id);
770     }
771     return VLC_SUCCESS;
772 }
773 
774 int
vlc_dialog_id_post_login(vlc_dialog_id * p_id,const char * psz_username,const char * psz_password,bool b_store)775 vlc_dialog_id_post_login(vlc_dialog_id *p_id, const char *psz_username,
776                          const char *psz_password, bool b_store)
777 {
778     assert(p_id != NULL && psz_username != NULL && psz_password != NULL);
779 
780     struct dialog_answer answer = {
781         .i_type = VLC_DIALOG_LOGIN,
782         .u.login = {
783             .b_store = b_store,
784             .psz_username = strdup(psz_username),
785             .psz_password = strdup(psz_password),
786         },
787     };
788     if (answer.u.login.psz_username == NULL
789      || answer.u.login.psz_password == NULL)
790     {
791         free(answer.u.login.psz_username);
792         free(answer.u.login.psz_password);
793         dialog_id_post(p_id, NULL);
794         return VLC_ENOMEM;
795     }
796 
797     return dialog_id_post(p_id, &answer);
798 }
799 
800 int
vlc_dialog_id_post_action(vlc_dialog_id * p_id,int i_action)801 vlc_dialog_id_post_action(vlc_dialog_id *p_id, int i_action)
802 {
803     assert(p_id != NULL);
804 
805     struct dialog_answer answer = {
806         .i_type = VLC_DIALOG_QUESTION,
807         .u.question = { .i_action = i_action },
808     };
809 
810     return dialog_id_post(p_id, &answer);
811 }
812 
813 int
vlc_dialog_id_dismiss(vlc_dialog_id * p_id)814 vlc_dialog_id_dismiss(vlc_dialog_id *p_id)
815 {
816     return dialog_id_post(p_id, NULL);
817 }
818 
819 #undef vlc_dialog_provider_set_ext_callback
820 void
vlc_dialog_provider_set_ext_callback(vlc_object_t * p_obj,vlc_dialog_ext_update_cb pf_update,void * p_data)821 vlc_dialog_provider_set_ext_callback(vlc_object_t *p_obj,
822                                      vlc_dialog_ext_update_cb pf_update,
823                                      void *p_data)
824 {
825     assert(p_obj != NULL);
826     vlc_dialog_provider *p_provider = get_dialog_provider(p_obj, false);
827 
828     vlc_mutex_lock(&p_provider->lock);
829 
830     p_provider->pf_ext_update = pf_update;
831     p_provider->p_ext_data = p_data;
832 
833     vlc_mutex_unlock(&p_provider->lock);
834 }
835 
836 #undef vlc_ext_dialog_update
837 int
vlc_ext_dialog_update(vlc_object_t * p_obj,extension_dialog_t * p_ext_dialog)838 vlc_ext_dialog_update(vlc_object_t *p_obj, extension_dialog_t *p_ext_dialog)
839 {
840     assert(p_obj != NULL);
841     vlc_dialog_provider *p_provider = get_dialog_provider(p_obj, false);
842 
843     vlc_mutex_lock(&p_provider->lock);
844     if (p_provider->pf_ext_update == NULL)
845     {
846         vlc_mutex_unlock(&p_provider->lock);
847         return VLC_EGENERIC;
848     }
849     p_provider->pf_ext_update(p_ext_dialog, p_provider->p_ext_data);
850     vlc_mutex_unlock(&p_provider->lock);
851     return VLC_SUCCESS;
852 }
853