1 /*
2 *
3 * Author: Giacomo Lozito <james@develia.org>, (C) 2005-2007
4 *
5 * This program is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License as published by the
7 * Free Software Foundation; either version 2 of the License, or (at your
8 * option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful, but
11 * WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13 * General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License along
16 * with this program; if not, write to the Free Software Foundation, Inc.,
17 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301  USA
18 *
19 */
20 
21 #include <glib.h>
22 #include <string.h>
23 
24 #include <libaudcore/drct.h>
25 #include <libaudcore/i18n.h>
26 #include <libaudcore/audstrings.h>
27 #include <libaudcore/hook.h>
28 
29 #include "aosd_trigger.h"
30 #include "aosd_trigger_private.h"
31 #include "aosd_cfg.h"
32 #include "aosd_osd.h"
33 
34 /* prototypes of trigger functions */
35 static void aosd_trigger_func_pb_start_onoff ( bool );
36 static void aosd_trigger_func_pb_start_cb ( void * , void * );
37 static void aosd_trigger_func_pb_titlechange_onoff ( bool );
38 static void aosd_trigger_func_pb_titlechange_cb ( void * , void * );
39 static void aosd_trigger_func_pb_pauseon_onoff ( bool );
40 static void aosd_trigger_func_pb_pauseon_cb ( void * , void * );
41 static void aosd_trigger_func_pb_pauseoff_onoff ( bool );
42 static void aosd_trigger_func_pb_pauseoff_cb ( void * , void * );
43 static void aosd_trigger_func_hook_cb ( void * markup_text , void * unused );
44 
45 /* map trigger codes to trigger objects */
46 aosd_trigger_t aosd_triggers[] =
47 {
48   // AOSD_TRIGGER_PB_START
49   { N_("Playback Start") ,
50     N_("Triggers OSD when a playlist entry is played.") ,
51     aosd_trigger_func_pb_start_onoff ,
52     aosd_trigger_func_pb_start_cb },
53 
54   // AOSD_TRIGGER_PB_TITLECHANGE
55   { N_("Title Change") ,
56     N_("Triggers OSD when the song title changes (for internet streams).") ,
57     aosd_trigger_func_pb_titlechange_onoff ,
58     aosd_trigger_func_pb_titlechange_cb },
59 
60   // AOSD_TRIGGER_PB_PAUSEON
61   { N_("Pause On") ,
62     N_("Triggers OSD when playback is paused.") ,
63     aosd_trigger_func_pb_pauseon_onoff ,
64     aosd_trigger_func_pb_pauseon_cb },
65 
66   // AOSD_TRIGGER_PB_PAUSEOFF
67   { N_("Pause Off") ,
68     N_("Triggers OSD when playback is unpaused.") ,
69     aosd_trigger_func_pb_pauseoff_onoff ,
70     aosd_trigger_func_pb_pauseoff_cb }
71 };
72 
73 static_assert (aud::n_elems (aosd_triggers) == AOSD_NUM_TRIGGERS, "update aosd_triggers");
74 
75 /* TRIGGER API */
76 
77 const char *
aosd_trigger_get_name(int trig_code)78 aosd_trigger_get_name ( int trig_code )
79 {
80   if (trig_code >= 0 && trig_code < aud::n_elems (aosd_triggers))
81     return aosd_triggers[trig_code].name;
82   return nullptr;
83 }
84 
85 
86 const char *
aosd_trigger_get_desc(int trig_code)87 aosd_trigger_get_desc ( int trig_code )
88 {
89   if (trig_code >= 0 && trig_code < aud::n_elems (aosd_triggers))
90     return aosd_triggers[trig_code].desc;
91   return nullptr;
92 }
93 
94 
aosd_trigger_start(const aosd_cfg_osd_trigger_t & cfg_trigger)95 void aosd_trigger_start (const aosd_cfg_osd_trigger_t & cfg_trigger)
96 {
97   for (int i = 0; i < AOSD_NUM_TRIGGERS; i ++)
98   {
99     if (cfg_trigger.enabled[i])
100       aosd_triggers[i].onoff_func (true);
101   }
102 
103   /* When called, this hook will display the text of the user pointer
104      or the current playing song, if nullptr */
105   hook_associate( "aosd toggle" , aosd_trigger_func_hook_cb , nullptr );
106 }
107 
108 
aosd_trigger_stop(const aosd_cfg_osd_trigger_t & cfg_trigger)109 void aosd_trigger_stop (const aosd_cfg_osd_trigger_t & cfg_trigger)
110 {
111   hook_dissociate( "aosd toggle" , aosd_trigger_func_hook_cb );
112 
113   for (int i = 0; i < AOSD_NUM_TRIGGERS; i ++)
114   {
115     if (cfg_trigger.enabled[i])
116       aosd_triggers[i].onoff_func (false);
117   }
118 }
119 
120 
121 /* TRIGGER FUNCTIONS */
122 
123 static void
aosd_trigger_func_pb_start_onoff(bool turn_on)124 aosd_trigger_func_pb_start_onoff ( bool turn_on )
125 {
126   if (turn_on == true)
127     hook_associate("playback ready", aosd_trigger_func_pb_start_cb, nullptr);
128   else
129     hook_dissociate("playback ready", aosd_trigger_func_pb_start_cb);
130 }
131 
132 static void
aosd_trigger_func_pb_start_cb(void * hook_data,void * user_data)133 aosd_trigger_func_pb_start_cb(void * hook_data, void * user_data)
134 {
135   String title = aud_drct_get_title ();
136   char * markup = g_markup_printf_escaped ("<span font_desc='%s'>%s</span>",
137    (const char *) global_config.text.fonts_name[0], (const char *) title);
138 
139   aosd_osd_display (markup, & global_config, false);
140   g_free (markup);
141 }
142 
143 typedef struct
144 {
145   String title;
146   String filename;
147 }
148 aosd_pb_titlechange_prevs_t;
149 
150 static void
aosd_trigger_func_pb_titlechange_onoff(bool turn_on)151 aosd_trigger_func_pb_titlechange_onoff ( bool turn_on )
152 {
153   static aosd_pb_titlechange_prevs_t *prevs = nullptr;
154 
155   if ( turn_on == true )
156   {
157     prevs = new aosd_pb_titlechange_prevs_t;
158     hook_associate( "title change" , aosd_trigger_func_pb_titlechange_cb , prevs );
159   }
160   else
161   {
162     hook_dissociate( "title change" , aosd_trigger_func_pb_titlechange_cb );
163     if ( prevs != nullptr )
164     {
165       delete prevs;
166       prevs = nullptr;
167     }
168   }
169 }
170 
171 static void
aosd_trigger_func_pb_titlechange_cb(void * plentry_gp,void * prevs_gp)172 aosd_trigger_func_pb_titlechange_cb ( void * plentry_gp , void * prevs_gp )
173 {
174   if (aud_drct_get_playing ())
175   {
176     aosd_pb_titlechange_prevs_t *prevs = (aosd_pb_titlechange_prevs_t *) prevs_gp;
177     String pl_entry_filename = aud_drct_get_filename ();
178     Tuple pl_entry_tuple = aud_drct_get_tuple ();
179     String pl_entry_title = pl_entry_tuple.get_str (Tuple::FormattedTitle);
180 
181     /* same filename but title changed, useful to detect http stream song changes */
182 
183     if ( ( prevs->title != nullptr ) && ( prevs->filename != nullptr ) )
184     {
185       if ( ( pl_entry_filename != nullptr ) && ( !strcmp(pl_entry_filename,prevs->filename) ) )
186       {
187         if ( ( pl_entry_title != nullptr ) && ( strcmp(pl_entry_title,prevs->title) ) )
188         {
189           /* string formatting is done here a.t.m. - TODO - improve this area */
190           char * markup = g_markup_printf_escaped
191            ("<span font_desc='%s'>%s</span>",
192            (const char *) global_config.text.fonts_name[0],
193            (const char *) pl_entry_title);
194 
195           aosd_osd_display (markup, & global_config, false);
196           g_free (markup);
197 
198           prevs->title = pl_entry_title;
199         }
200       }
201       else
202       {
203         prevs->filename = pl_entry_filename;
204         /* if filename changes, reset title as well */
205         prevs->title = pl_entry_title;
206       }
207     }
208     else
209     {
210       prevs->title = pl_entry_title;
211       prevs->filename = pl_entry_filename;
212     }
213   }
214 }
215 
216 static void
aosd_trigger_func_pb_pauseon_onoff(bool turn_on)217 aosd_trigger_func_pb_pauseon_onoff ( bool turn_on )
218 {
219   if ( turn_on == true )
220     hook_associate( "playback pause" , aosd_trigger_func_pb_pauseon_cb , nullptr );
221   else
222     hook_dissociate( "playback pause" , aosd_trigger_func_pb_pauseon_cb );
223 }
224 
225 static void
aosd_trigger_func_pb_pauseon_cb(void * unused1,void * unused2)226 aosd_trigger_func_pb_pauseon_cb ( void * unused1 , void * unused2 )
227 {
228   char * markup = g_markup_printf_escaped ("<span font_desc='%s'>Paused</span>",
229    (const char *) global_config.text.fonts_name[0]);
230   aosd_osd_display (markup, & global_config, false);
231   g_free (markup);
232 }
233 
234 
235 static void
aosd_trigger_func_pb_pauseoff_onoff(bool turn_on)236 aosd_trigger_func_pb_pauseoff_onoff ( bool turn_on )
237 {
238   if ( turn_on == true )
239     hook_associate( "playback unpause" , aosd_trigger_func_pb_pauseoff_cb , nullptr );
240   else
241     hook_dissociate( "playback unpause" , aosd_trigger_func_pb_pauseoff_cb );
242 }
243 
244 static void
aosd_trigger_func_pb_pauseoff_cb(void * unused1,void * unused2)245 aosd_trigger_func_pb_pauseoff_cb ( void * unused1 , void * unused2 )
246 {
247   Tuple tuple = aud_drct_get_tuple ();
248   int time_cur, time_tot;
249   int time_cur_m, time_cur_s, time_tot_m, time_tot_s;
250 
251   time_tot = tuple.get_int (Tuple::Length) / 1000;
252   time_cur = aud_drct_get_time() / 1000;
253   time_cur_s = time_cur % 60;
254   time_cur_m = (time_cur - time_cur_s) / 60;
255   time_tot_s = time_tot % 60;
256   time_tot_m = (time_tot - time_tot_s) / 60;
257 
258   String title = tuple.get_str (Tuple::FormattedTitle);
259   char * markup = g_markup_printf_escaped
260    ("<span font_desc='%s'>%s (%i:%02i/%i:%02i)</span>",
261    (const char *) global_config.text.fonts_name[0], (const char *) title,
262    time_cur_m, time_cur_s, time_tot_m, time_tot_s);
263 
264   aosd_osd_display (markup, & global_config, false);
265   g_free (markup);
266 }
267 
268 
269 /* Call with hook_call("aosd toggle", param);
270    If param != nullptr, display the supplied text in the OSD
271    If param == nullptr, display the current playing song */
272 static void
aosd_trigger_func_hook_cb(void * markup_text,void * unused)273 aosd_trigger_func_hook_cb ( void * markup_text , void * unused )
274 {
275   if ( markup_text != nullptr )
276   {
277     /* Display text from caller */
278     aosd_osd_display ((char *) markup_text, & global_config, false);
279   } else {
280     /* Display currently playing song */
281     aosd_trigger_func_pb_start_cb (nullptr, nullptr);
282   }
283 }
284