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  * stdctl-specific stuff
21  *
22  */
23 #ifdef HAVE_CONFIG_H
24 #include "config.h"
25 #endif
26 
27 #include <pthread.h>
28 #include <signal.h>
29 #include <sys/time.h>
30 #include <sys/types.h>
31 #include <unistd.h>
32 #include <string.h>
33 
34 #include "common.h"
35 
36 #define DEBUG_STDCTL 0
37 
38 static struct {
39   int                   fd;
40   pthread_t             thread;
41   FILE                 *fbk;
42 } stdctl;
43 
xine_stdctl_loop(void * dummy)44 static __attribute__((noreturn)) void *xine_stdctl_loop(void *dummy) {
45   char              buf[256], *c, *c1;
46   int               len, selrt;
47   kbinding_entry_t *k;
48   fd_set            set;
49   struct timeval    tv;
50   int               secs, last_secs;
51   char             *params;
52   gGui_t           *gui = gGui;
53 
54   last_secs = -1;
55   params = NULL;
56 
57   while (gui->running) {
58 
59     FD_ZERO(&set);
60     FD_SET(stdctl.fd, &set);
61 
62     tv.tv_sec  = 0;
63     tv.tv_usec = 500000;
64 
65     selrt = select(stdctl.fd + 1, &set, NULL, NULL, &tv);
66 
67     if(selrt > 0 && FD_ISSET(stdctl.fd, &set)) {
68 
69       len = read(stdctl.fd, &buf, sizeof(buf) - 1);
70 
71       if(len > 0) {
72 
73 	buf[len] = '\0';
74 
75 	c = buf;
76 	while((c1 = strchr(c, '\n'))) {
77 	  *c1 = '\0';
78 
79 #if DEBUG_STDCTL
80 	  fprintf(stderr, "Command Received = '%s'\n", c);
81 #endif
82 
83 	  /* Handle optional parameter */
84 
85 	  /* alphanum: separated from the command by a '$'        */
86 	  /* syntax:   "command$parameter"                        */
87 	  /* example:  "OSDWriteText$Some Information to Display" */
88 
89           gui->alphanum.set = 0;
90 	  params = strchr(c, '$');
91 
92 	  if(params != NULL) {
93 
94 	    /* parameters available */
95 
96 	    *params = '\0';
97 	    params++;
98 
99             gui->alphanum.set = 1;
100             gui->alphanum.arg = params;
101 
102 #if DEBUG_STDCTL
103 	    fprintf(stderr, "Command: '%s'\nParameters: '%s'\n", c, params);
104 #endif
105 	  }
106 
107 	  /* numeric: separated from the command by a '#' */
108 	  /* syntax:  "command#parameter"                 */
109 	  /* example: "SetPosition%#99"                   */
110 
111           gui->numeric.set = 0;
112 	  params = strchr(c, '#');
113 
114 	  if(params != NULL) {
115 
116 	    /* parameters available */
117 
118 	    *params = '\0';
119 	    params++;
120 
121             gui->numeric.set = 1;
122             gui->numeric.arg = atoi(params);
123 
124             if (gui->numeric.arg < 0)
125 	    {
126               gui->numeric.arg = 0;
127 	      fprintf(stderr, "WARNING: stdctl: Negative num argument not supported, set to 0\n");
128 	    }
129 
130 #if DEBUG_STDCTL
131             fprintf (stderr, "Command: '%s'\nParameters: '%d'\n", c, gui->numeric.arg);
132 #endif
133 	  }
134 
135           k = kbindings_lookup_action (gui->kbindings, c);
136 
137 	  if(k)
138             gui_execute_action_id (gui, kbindings_get_action_id (k));
139 
140 	  c = c1 + 1;
141 	}
142       }
143 
144       panel_paint (gui->panel);
145 
146       if(len <= 0) {
147         gui_execute_action_id (gui, ACTID_QUIT);
148 	break;
149       }
150     }
151 
152     if (gui_xine_get_pos_length (gui->stream, NULL, &secs, NULL)) {
153 
154       secs /= 1000;
155 
156       if (secs != last_secs) {
157 	fprintf(stdctl.fbk, "time: %d\n", secs);
158 	last_secs = secs;
159       }
160     }
161 
162   }
163 
164   pthread_exit(NULL);
165 }
166 
stdctl_start(void)167 void stdctl_start(void) {
168   int err;
169 
170   stdctl.fd = STDIN_FILENO;
171   stdctl.fbk = gGui->orig_stdout;
172 
173   if((err = pthread_create(&(stdctl.thread), NULL, xine_stdctl_loop, NULL)) != 0) {
174     fprintf(stderr, _("%s(): can't create new thread (%s)\n"), __XINE_FUNCTION__, strerror(err));
175     gGui->stdctl_enable = 0;
176   }
177 }
178 
stdctl_stop(void)179 void stdctl_stop(void) {
180   /*
181    * We print the exit feedback here, not on exit of the stdctl thread.
182    * Otherwise, if ACTID_QUIT (bringing us to this point) was triggered
183    * by stdctl itself, we run into a race with the main thread which
184    * could close the feedback channel beforehand: pthread_join doesn't
185    * wait but returns immediately with EDEADLK as stdctl tries to join
186    * itself and termination continues asynchronously.
187    */
188   fprintf(stdctl.fbk, "Exiting\n");
189   pthread_join(stdctl.thread, NULL);
190 }
191 
stdctl_event(const xine_event_t * event)192 void stdctl_event(const xine_event_t *event)
193 {
194   switch(event->type) {
195   case XINE_EVENT_UI_PLAYBACK_FINISHED:
196     fprintf(stdctl.fbk, "PlaybackFinished\n");
197     break;
198   }
199 }
200 
stdctl_keypress(XKeyEvent event)201 void stdctl_keypress(XKeyEvent event)
202 {
203     KeySym  sym;
204     char   *str;
205 
206     if((sym = XKeycodeToKeysym(event.display, event.keycode, 0)))
207       if((str = XKeysymToString(sym)))
208 	fprintf(stdctl.fbk, "KeyPress$%s\n", str);
209 }
210 
stdctl_playing(const char * mrl)211 void stdctl_playing(const char *mrl)
212 {
213     fprintf(stdctl.fbk, "PlaybackStart$%s\n", mrl);
214     fprintf(stdctl.fbk, "PlaylistPos#%i\n", gGui->playlist.cur);
215 }
216