1 /*
2  * Copyright (C) 2000-2019 the xine project
3  *
4  * This file is part of xine, a unix video player.
5  *
6  * xine is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 2 of the License, or
9  * (at your option) any later version.
10  *
11  * xine is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software
18  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA
19  *
20  * MRL Browser
21  *
22  */
23 #ifdef HAVE_CONFIG_H
24 #include "config.h"
25 #endif
26 
27 #include <stdio.h>
28 #include <errno.h>
29 #include <X11/Xlib.h>
30 #include <X11/Xutil.h>
31 
32 #include "common.h"
33 
34 struct xui_mrlb_st {
35   gGui_t *gui;
36   xitk_widget_t *w;
37 };
38 
39 /*
40  *
41  */
mrl_browser_show_tips(xui_mrlb_t * mrlb,int enabled,unsigned long timeout)42 void mrl_browser_show_tips (xui_mrlb_t *mrlb, int enabled, unsigned long timeout) {
43   if (mrlb && mrlb->w)
44     xitk_mrlbrowser_set_tips_timeout (mrlb->w, enabled, timeout);
45 }
46 
mrl_browser_update_tips_timeout(xui_mrlb_t * mrlb,unsigned long timeout)47 void mrl_browser_update_tips_timeout (xui_mrlb_t *mrlb, unsigned long timeout) {
48   if (mrlb && mrlb->w) {
49     if (xitk_get_widget_tips_timeout (mrlb->w) > 0)
50       xitk_mrlbrowser_set_tips_timeout (mrlb->w, 1, timeout);
51   }
52 }
53 
54 /*
55  *
56  */
set_mrl_browser_transient(xui_mrlb_t * mrlb)57 void set_mrl_browser_transient (xui_mrlb_t *mrlb) {
58   if (mrlb && mrlb->w) {
59     if (!mrlb->gui->use_root_window && mrlb->gui->video_display == mrlb->gui->display)
60       xitk_mrlbrowser_set_transient (mrlb->w, mrlb->gui->video_window);
61   }
62 }
63 
64 /*
65  *
66  */
mrl_browser_change_skins(xui_mrlb_t * mrlb,int synthetic)67 void mrl_browser_change_skins (xui_mrlb_t *mrlb, int synthetic) {
68   (void)synthetic;
69   if (mrlb && mrlb->w) {
70     xitk_mrlbrowser_change_skins (mrlb->w, mrlb->gui->skin_config);
71     set_mrl_browser_transient (mrlb);
72     if (mrl_browser_is_visible (mrlb))
73       raise_window ((xitk_mrlbrowser_get_window_id (mrlb->w)), 1, 1);
74   }
75 }
76 
77 /*
78  *
79  */
mrl_browser_is_visible(xui_mrlb_t * mrlb)80 int mrl_browser_is_visible (xui_mrlb_t *mrlb) {
81   if (mrlb && mrlb->w)
82     return (xitk_mrlbrowser_is_visible (mrlb->w));
83   return 0;
84 }
85 
86 /*
87  *
88  */
mrl_browser_is_running(xui_mrlb_t * mrlb)89 int mrl_browser_is_running (xui_mrlb_t *mrlb) {
90   if (mrlb && mrlb->w)
91     return (xitk_mrlbrowser_is_running (mrlb->w));
92   return 0;
93 }
94 
95 /*
96  *
97  */
show_mrl_browser(xui_mrlb_t * mrlb)98 void show_mrl_browser (xui_mrlb_t *mrlb) {
99   if (mrlb && mrlb->w) {
100     mrlb->gui->nongui_error_msg = NULL;
101     xitk_mrlbrowser_show (mrlb->w);
102     set_mrl_browser_transient (mrlb);
103     layer_above_video ((xitk_mrlbrowser_get_window_id (mrlb->w)));
104   }
105 }
106 
107 /*
108  *
109  */
hide_mrl_browser(xui_mrlb_t * mrlb)110 void hide_mrl_browser (xui_mrlb_t *mrlb) {
111   if (mrlb && mrlb->w)
112     xitk_mrlbrowser_hide (mrlb->w);
113 }
114 
115 /*
116  *
117  */
mrl_browser_toggle_visibility(xitk_widget_t * w,void * data)118 void mrl_browser_toggle_visibility (xitk_widget_t *w, void *data) {
119   xui_mrlb_t *mrlb = data;
120 
121   (void)w;
122   if (mrlb) {
123     if (mrl_browser_is_visible (mrlb))
124       hide_mrl_browser (mrlb);
125     else
126       show_mrl_browser (mrlb);
127   }
128 }
129 
130 /*
131  *
132  */
destroy_mrl_browser(xui_mrlb_t * mrlb)133 void destroy_mrl_browser (xui_mrlb_t *mrlb) {
134   if (mrlb) {
135     if (mrlb->w) {
136       window_info_t wi;
137 
138       if ((xitk_mrlbrowser_get_window_info (mrlb->w, &wi))) {
139         config_update_num ("gui.mrl_browser_x", wi.x);
140         config_update_num ("gui.mrl_browser_y", wi.y);
141       }
142       xitk_mrlbrowser_destroy (mrlb->w);
143       mrlb->w = NULL;
144     }
145     mrlb->gui->mrlb = NULL;
146     try_to_set_input_focus (mrlb->gui->video_window);
147     free (mrlb);
148   }
149 }
150 
151 /*
152  *
153  */
mrl_browser_kill(xitk_widget_t * w,void * data)154 static void mrl_browser_kill(xitk_widget_t *w, void *data) {
155   xui_mrlb_t *mrlb = data;
156 
157   (void)w;
158   /* xitk_mrlbrowser_exit () calls this, then xitk_mrlbrowser_destroy ().
159      so we just shut down our own stuff here. */
160   if (mrlb) {
161     if (mrlb->w) {
162       window_info_t wi;
163 
164       if ((xitk_mrlbrowser_get_window_info (mrlb->w, &wi))) {
165         config_update_num ("gui.mrl_browser_x", wi.x);
166         config_update_num ("gui.mrl_browser_y", wi.y);
167       }
168       mrlb->w = NULL;
169     }
170     mrlb->gui->mrlb = NULL;
171     try_to_set_input_focus (mrlb->gui->video_window);
172     free (mrlb);
173   }
174 }
175 
mrl_browser_get_valid_mrl_ending(xui_mrlb_t * mrlb)176 static xitk_mrlbrowser_filter_t **mrl_browser_get_valid_mrl_ending (xui_mrlb_t *mrlb) {
177   xitk_mrlbrowser_filter_t **filters = NULL;
178   int                        num_endings = 0;
179   char                      *mrl_exts, *pmrl_exts, *p, *pp;
180 
181   filters                      = (xitk_mrlbrowser_filter_t **)
182     calloc((num_endings + 2), sizeof(xitk_mrlbrowser_filter_t *));
183   filters[num_endings]         = (xitk_mrlbrowser_filter_t *)
184     xitk_xmalloc(sizeof(xitk_mrlbrowser_filter_t));
185   filters[num_endings]->name   = strdup("All");
186   filters[num_endings]->ending = strdup("*");
187 
188   mrl_exts = xine_get_file_extensions (mrlb->gui->xine);
189   if(mrl_exts) {
190     char  patterns[2048];
191     char *e;
192 
193     p = strdup(mrl_exts);
194 
195     num_endings++;
196 
197     pp = p;
198     while(*p != '\0') {
199       if(*p == ' ')
200 	*p = ',';
201       p++;
202     }
203 
204     filters[num_endings]         = (xitk_mrlbrowser_filter_t *) xitk_xmalloc(sizeof(xitk_mrlbrowser_filter_t));
205     filters[num_endings]->name   = strdup(_("All extensions"));
206     filters[num_endings]->ending = pp;
207 
208     pmrl_exts = mrl_exts;
209     while((e = xine_strsep(&pmrl_exts, " ")) != NULL) {
210 
211       snprintf(patterns, sizeof(patterns), "*.%s", e);
212 
213       num_endings++;
214 
215       filters                      = (xitk_mrlbrowser_filter_t **)
216 	realloc(filters, sizeof(xitk_mrlbrowser_filter_t *) * (num_endings + 2));
217 
218       filters[num_endings]         = (xitk_mrlbrowser_filter_t *)
219 	xitk_xmalloc(sizeof(xitk_mrlbrowser_filter_t));
220 
221       filters[num_endings]->name   = strdup(patterns);
222       filters[num_endings]->ending = strdup(e);
223     }
224 
225     free(mrl_exts);
226   }
227 
228   filters[num_endings + 1]         = (xitk_mrlbrowser_filter_t *)
229     xitk_xmalloc(sizeof(xitk_mrlbrowser_filter_t));
230   filters[num_endings + 1]->name   = NULL;
231   filters[num_endings + 1]->ending = NULL;
232 
233   return filters;
234 }
235 
236 
237 /*
238  *
239  */
mrl_browser(gGui_t * gui,xitk_mrl_callback_t add_cb,xitk_mrl_callback_t play_cb,select_cb_t sel_cb,xitk_dnd_callback_t dnd_cb)240 static xui_mrlb_t *mrl_browser (gGui_t *gui,
241   xitk_mrl_callback_t add_cb, xitk_mrl_callback_t play_cb, select_cb_t sel_cb, xitk_dnd_callback_t dnd_cb) {
242   xui_mrlb_t *mrlb;
243   xitk_mrlbrowser_widget_t     mb;
244   const char *const           *ip_availables;
245   xitk_mrlbrowser_filter_t   **mrl_filters;
246 
247   if (!gui)
248     return NULL;
249 
250   if (gui->mrlb) {
251     show_mrl_browser (gui->mrlb);
252     set_mrl_browser_transient (gui->mrlb);
253     return gui->mrlb;
254   }
255 
256   mrlb = calloc (1, sizeof (*mrlb));
257   if (!mrlb)
258     return NULL;
259   mrlb->gui = gui;
260 
261   ip_availables = xine_get_browsable_input_plugin_ids (mrlb->gui->xine);
262   mrl_filters = mrl_browser_get_valid_mrl_ending (mrlb);
263 
264   XITK_WIDGET_INIT (&mb, mrlb->gui->imlib_data);
265 
266   mb.window_trans = (mrlb->gui->use_root_window || mrlb->gui->video_display != mrlb->gui->display) ? None
267                   : mrlb->gui->video_window;
268   mb.layer_above  = (is_layer_above ());
269   mb.icon         = &mrlb->gui->icon;
270   mb.set_wm_window_normal = !video_window_is_visible (mrlb->gui->vwin);
271   mb.x = xine_config_register_num (mrlb->gui->xine, "gui.mrl_browser_x",
272     200, "gui mrl browser x coordinate",
273     CONFIG_NO_HELP, CONFIG_LEVEL_DEB, CONFIG_NO_CB, CONFIG_NO_DATA);
274   mb.y = xine_config_register_num (mrlb->gui->xine, "gui.mrl_browser_y",
275     100, "gui mrl browser y coordinate",
276     CONFIG_NO_HELP, CONFIG_LEVEL_DEB, CONFIG_NO_CB, CONFIG_NO_DATA);
277   mb.window_title = _("xine MRL Browser");
278 
279   mb.skin_element_name        = "MrlBG";
280   mb.resource_name            = mb.window_title;
281   mb.resource_class           = "xine";
282 
283   mb.origin.skin_element_name = "MrlCurOrigin";
284   mb.origin.cur_origin        = NULL;
285 
286   mb.dndcallback              = dnd_cb;
287   mb.dnddata                  = gui;
288 
289   mb.select.skin_element_name = "MrlSelect";
290   mb.select.caption           = _("Add");
291   mb.select.callback          = add_cb;
292   mb.select.data              = mrlb;
293 
294   mb.play.skin_element_name   = "MrlPlay";
295   mb.play.callback            = play_cb;
296   mb.play.data                = mrlb;
297 
298   mb.dismiss.skin_element_name = "MrlDismiss";
299   mb.dismiss.caption           = _("Dismiss");
300 
301   mb.kill.callback            = mrl_browser_kill;
302   mb.kill.data                = mrlb;
303 
304   mb.ip_availables            = ip_availables;
305 
306   mb.ip_name.button.skin_element_name = "MrlPlugNameBG";
307 
308   mb.ip_name.label.skin_element_name  = "MrlPlugLabel";
309   /* TRANSLATORS: only ASCII characters (skin) */
310   mb.ip_name.label.label_str  = pgettext ("skin", "Source:");
311 
312   mb.xine = mrlb->gui->xine;
313 
314   /* The browser */
315 
316   mb.browser.arrow_up.skin_element_name    = "MrlUp";
317   mb.browser.slider.skin_element_name      = "SliderMrl";
318   mb.browser.arrow_dn.skin_element_name    = "MrlDn";
319 
320   mb.browser.arrow_left.skin_element_name  = "MrlLeft";
321   mb.browser.slider_h.skin_element_name    = "SliderHMrl";
322   mb.browser.arrow_right.skin_element_name = "MrlRight";
323 
324   mb.browser.browser.skin_element_name     = "MrlItemBtn";
325   mb.browser.browser.num_entries           = 0;
326   mb.browser.browser.entries               = NULL;
327   mb.browser.callback                      = sel_cb;
328   mb.browser.userdata                      = mrlb;
329 
330   mb.combo.skin_element_name               = "MrlFilt";
331 
332   mb.mrl_filters                           = mrl_filters;
333 
334   mrlb->w = xitk_mrlbrowser_create (NULL, mrlb->gui->skin_config, &mb);
335 
336   if(mrl_filters) {
337     int i;
338 
339     for(i = 0; mrl_filters[i] && (mrl_filters[i]->name && mrl_filters[i]->ending); i++) {
340       free(mrl_filters[i]->name);
341       free(mrl_filters[i]->ending);
342       free(mrl_filters[i]);
343     }
344     free(mrl_filters[i]);
345     free(mrl_filters);
346   }
347 
348   mrlb->gui->mrlb = mrlb;
349   return mrlb;
350 }
351 
352 /*
353  *
354  */
mrl_handle_selection(xitk_widget_t * w,void * data,int selected)355 static void mrl_handle_selection (xitk_widget_t *w, void *data, int selected) {
356   (void)w;
357   (void)data;
358   (void)selected;
359 }
360 
361 /*
362  * Callback called by mrlbrowser on add event.
363  */
mrl_add_noautoplay(xitk_widget_t * w,void * data,xine_mrl_t * mrl)364 static void mrl_add_noautoplay(xitk_widget_t *w, void *data, xine_mrl_t *mrl) {
365   xui_mrlb_t *mrlb = data;
366 
367   (void)w;
368   if (mrlb && mrl) {
369     int num = mrlb->gui->playlist.num;
370 
371     if(!playlist_is_running()) {
372       playlist_editor();
373     }
374     else {
375       if(!playlist_is_visible())
376 	playlist_toggle_visibility(NULL, NULL);
377     }
378 
379     mediamark_append_entry((char *)mrl->mrl, (char *)mrl->mrl, NULL, 0, -1, 0, 0);
380 
381     if ((!num) && ((xine_get_status (mrlb->gui->stream) == XINE_STATUS_STOP) || mrlb->gui->logo_mode)) {
382       mrlb->gui->playlist.cur = mrlb->gui->playlist.num - 1;
383       gui_set_current_mmk(mediamark_get_current_mmk());
384     }
385 
386     playlist_update_playlist();
387 
388     if ((!is_playback_widgets_enabled (mrlb->gui->panel)) && mrlb->gui->playlist.num)
389       enable_playback_controls (mrlb->gui->panel, 1);
390   }
391 }
392 
mrl_add(xitk_widget_t * w,void * data,xine_mrl_t * mrl)393 static void mrl_add(xitk_widget_t *w, void *data, xine_mrl_t *mrl) {
394   xui_mrlb_t *mrlb = data;
395 
396   (void)w;
397   if (mrlb && mrl) {
398 
399     if(!playlist_is_running()) {
400       playlist_editor();
401     }
402     else {
403       if(!playlist_is_visible())
404 	playlist_toggle_visibility(NULL, NULL);
405     }
406 
407     gui_dndcallback((char *)mrl->mrl);
408   }
409 }
410 
411 /*
412  * Callback called by mrlbrowser on play event.
413  */
mrl_play(xitk_widget_t * w,void * data,xine_mrl_t * mrl)414 static void mrl_play(xitk_widget_t *w, void *data, xine_mrl_t *mrl) {
415   xui_mrlb_t *mrlb = data;
416 
417   (void)w;
418   if (mrlb && mrl) {
419     mediamark_t mmk;
420     char        *_mrl = mrl->mrl;
421 
422     if ((xine_get_status (mrlb->gui->stream) != XINE_STATUS_STOP)) {
423       mrlb->gui->ignore_next = 1;
424       xine_stop (mrlb->gui->stream);
425       mrlb->gui->ignore_next = 0;
426     }
427 
428     if (!is_playback_widgets_enabled (mrlb->gui->panel))
429       enable_playback_controls (mrlb->gui->panel, 1);
430 
431     if(mrl_look_like_playlist(_mrl)) {
432       if(mediamark_concat_mediamarks(_mrl)) {
433 	gui_set_current_mmk(mediamark_get_current_mmk());
434 	_mrl = (char *) mediamark_get_current_mrl();
435 	playlist_update_playlist();
436       }
437     }
438 
439     osd_hide();
440 
441     if (!xine_open (mrlb->gui->stream, (const char *) _mrl)) {
442       gui_handle_xine_error (mrlb->gui->stream, _mrl);
443       enable_playback_controls (mrlb->gui->panel, 0);
444       gui_display_logo();
445       return;
446     }
447 
448     if (!gui_xine_play (mrlb->gui, mrlb->gui->stream, 0, 0, 0)) {
449       enable_playback_controls (mrlb->gui->panel, 0);
450       gui_display_logo();
451       return;
452     }
453 
454     mmk.mrl           = _mrl;
455     mmk.ident         = NULL;
456     mmk.sub           = NULL;
457     mmk.start         = 0;
458     mmk.end           = -1;
459     mmk.av_offset     = 0;
460     mmk.spu_offset    = 0;
461     mmk.got_alternate = 0;
462     mmk.alternates    = NULL;
463     mmk.cur_alt       = NULL;
464     gui_set_current_mmk(&mmk);
465   }
466 }
467 
mrl_browser_reparent(xui_mrlb_t * mrlb)468 void mrl_browser_reparent (xui_mrlb_t *mrlb) {
469   if (mrlb && mrlb->w)
470     reparent_window ((xitk_mrlbrowser_get_window_id (mrlb->w)));
471 }
472 
473 /*
474  * Create a new mrl browser.
475  */
open_mrlbrowser(xitk_widget_t * w,void * data)476 void open_mrlbrowser(xitk_widget_t *w, void *data) {
477   gGui_t *gui = data;
478 
479   if (gui) {
480     mrl_browser (gui, mrl_add, mrl_play, mrl_handle_selection, gui_dndcallback);
481     set_mrl_browser_transient (gui->mrlb);
482     mrl_browser_show_tips (gui->mrlb, panel_get_tips_enable (gui->panel), panel_get_tips_timeout (gui->panel));
483   }
484 }
485 
open_mrlbrowser_from_playlist(xitk_widget_t * w,void * data)486 void open_mrlbrowser_from_playlist(xitk_widget_t *w, void *data) {
487   gGui_t *gui = data;
488 
489   if (gui) {
490     mrl_browser (gui, mrl_add_noautoplay, mrl_play, mrl_handle_selection, gui_dndcallback);
491     set_mrl_browser_transient (gui->mrlb);
492     mrl_browser_show_tips (gui->mrlb, panel_get_tips_enable (gui->panel), panel_get_tips_timeout (gui->panel));
493   }
494 }
495