2 * This file is part of MPlayer.
4 * MPlayer is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version.
9 * MPlayer is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
14 * You should have received a copy of the GNU General Public License along
15 * with MPlayer; if not, write to the Free Software Foundation, Inc.,
16 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
27 #include <sys/types.h>
34 #include "libmpcodecs/img_format.h"
35 #include "libmpcodecs/mp_image.h"
41 #include "sub/font_load.h"
42 #include "input/keycodes.h"
43 #include "input/input.h"
44 #include "osdep/timer.h"
46 typedef struct history_st history_t
;
56 char** lines
; // Our buffer
62 pid_t child
; // Child process if we are running a shell cmd
63 int child_fd
[3]; // The 3 default fd
65 //int max_lines; // Max number of lines with the last mpi
67 history_t
* cur_history
;
72 int buf_lines
; // Buffer size (in line)
73 int height
; // Display size in %
77 unsigned int hide_time
;
78 unsigned int show_time
;
83 static struct menu_priv_s cfg_dflt
= {
110 #define ST_OFF(m) M_ST_OFF(struct menu_priv_s,m)
112 static const m_option_t cfg_fields
[] = {
113 { "prompt", ST_OFF(mp_prompt
), CONF_TYPE_STRING
, M_OPT_MIN
, 1, 0, NULL
},
114 { "child-prompt", ST_OFF(child_prompt
), CONF_TYPE_STRING
, M_OPT_MIN
, 1, 0, NULL
},
115 { "buffer-lines", ST_OFF(buf_lines
), CONF_TYPE_INT
, M_OPT_MIN
, 5, 0, NULL
},
116 { "height", ST_OFF(height
), CONF_TYPE_INT
, M_OPT_RANGE
, 1, 100, NULL
},
117 { "minbor", ST_OFF(minb
), CONF_TYPE_INT
, M_OPT_MIN
, 0, 0, NULL
},
118 { "vspace", ST_OFF(vspace
), CONF_TYPE_INT
, M_OPT_MIN
, 0, 0, NULL
},
119 { "bg", ST_OFF(bg
), CONF_TYPE_INT
, M_OPT_RANGE
, -1, 255, NULL
},
120 { "bg-alpha", ST_OFF(bg_alpha
), CONF_TYPE_INT
, M_OPT_RANGE
, 0, 255, NULL
},
121 { "show-time",ST_OFF(show_time
), CONF_TYPE_INT
, M_OPT_MIN
, 0, 0, NULL
},
122 { "hide-time",ST_OFF(hide_time
), CONF_TYPE_INT
, M_OPT_MIN
, 0, 0, NULL
},
123 { "history-size",ST_OFF(history_max
), CONF_TYPE_INT
, M_OPT_MIN
, 1, 0, NULL
},
124 { "raw-child", ST_OFF(raw_child
), CONF_TYPE_FLAG
, 0, 0, 1, NULL
},
125 { NULL
, NULL
, NULL
, 0,0,0,NULL
}
128 #define mpriv (menu->priv)
130 static void check_child(menu_t
* menu
);
132 static void add_line(struct menu_priv_s
* priv
, char* l
) {
133 char* eol
= strchr(l
,'\n');
140 if(eol
[1]) add_line(priv
,eol
+1);
144 if(priv
->num_lines
>= priv
->buf_lines
)
145 free(priv
->lines
[priv
->last_line
]);
149 priv
->lines
[priv
->last_line
] = strdup(l
);
150 priv
->last_line
= (priv
->last_line
+ 1) % priv
->buf_lines
;
154 static void add_string(struct menu_priv_s
* priv
, char* l
) {
155 char* eol
= strchr(l
,'\n');
156 int ll
= priv
->last_line
> 0 ? priv
->last_line
- 1 : priv
->buf_lines
-1;
158 if(priv
->num_lines
<= 0 || priv
->add_line
|| eol
== l
) {
168 add_line(priv
,eol
+1);
174 priv
->lines
[ll
] = realloc(priv
->lines
[ll
],strlen(priv
->lines
[ll
]) + strlen(l
) + 1);
175 if ( priv
->lines
[ll
] != NULL
)
177 strcat(priv
->lines
[ll
],l
);
181 static void draw(menu_t
* menu
, mp_image_t
* mpi
) {
182 int h
= mpi
->h
*mpriv
->height
/100;
183 int w
= mpi
->w
- 2* mpriv
->minb
;
184 int x
= mpriv
->minb
, y
;
187 if(mpriv
->child
) check_child(menu
);
189 ll
= mpriv
->last_line
- 1;
192 unsigned int t
= GetTimerMS() - mpriv
->hide_ts
;
193 if(t
>= mpriv
->hide_time
) {
198 h
= mpi
->h
*(mpriv
->height
- (mpriv
->height
* t
/mpriv
->hide_time
))/100;
199 } else if(mpriv
->show_time
&& mpriv
->show_ts
== 0) {
200 mpriv
->show_ts
= GetTimerMS();
202 } else if(mpriv
->show_ts
> 0) {
203 unsigned int t
= GetTimerMS() - mpriv
->show_ts
;
204 if(t
> mpriv
->show_time
)
207 h
= mpi
->h
*(mpriv
->height
* t
/mpriv
->hide_time
)/100;
210 y
= h
- mpriv
->vspace
;
212 if(x
< 0 || y
< 0 || w
<= 0 || h
<= 0 )
216 menu_draw_box(mpi
,mpriv
->bg
,mpriv
->bg_alpha
,0,0,mpi
->w
,h
);
218 if(!mpriv
->child
|| !mpriv
->raw_child
){
219 char input
[strlen(mpriv
->cur_history
->buffer
) + strlen(mpriv
->prompt
) + 1];
220 sprintf(input
,"%s%s",mpriv
->prompt
,mpriv
->cur_history
->buffer
);
221 menu_text_size(input
,w
,mpriv
->vspace
,1,&lw
,&lh
);
222 menu_draw_text_full(mpi
,input
,x
,y
,w
,h
,mpriv
->vspace
,1,
223 MENU_TEXT_BOT
|MENU_TEXT_LEFT
,
224 MENU_TEXT_BOT
|MENU_TEXT_LEFT
);
225 y
-= lh
+ mpriv
->vspace
;
229 for( i
= 0 ; y
> mpriv
->minb
&& i
< mpriv
->num_lines
; i
++){
230 int c
= (ll
- i
) >= 0 ? ll
- i
: mpriv
->buf_lines
+ ll
- i
;
231 menu_text_size(mpriv
->lines
[c
],w
,mpriv
->vspace
,1,&lw
,&lh
);
232 menu_draw_text_full(mpi
,mpriv
->lines
[c
],x
,y
,w
,h
,mpriv
->vspace
,1,
233 MENU_TEXT_BOT
|MENU_TEXT_LEFT
,
234 MENU_TEXT_BOT
|MENU_TEXT_LEFT
);
235 y
-= lh
+ mpriv
->vspace
;
240 static void check_child(menu_t
* menu
) {
244 int max_fd
= mpriv
->child_fd
[2] > mpriv
->child_fd
[1] ? mpriv
->child_fd
[2] :
246 int i
,r
,child_status
,w
;
249 if(!mpriv
->child
) return;
251 memset(&tv
,0,sizeof(struct timeval
));
253 FD_SET(mpriv
->child_fd
[1],&rfd
);
254 FD_SET(mpriv
->child_fd
[2],&rfd
);
256 r
= select(max_fd
+1,&rfd
, NULL
, NULL
, &tv
);
258 r
= waitpid(mpriv
->child
,&child_status
,WNOHANG
);
260 if(errno
==ECHILD
){ ///exiting children get handled in mplayer.c
261 for(i
= 0 ; i
< 3 ; i
++)
262 close(mpriv
->child_fd
[i
]);
264 mpriv
->prompt
= mpriv
->mp_prompt
;
265 //add_line(mpriv,"Child process exited");
267 else mp_tmsg(MSGT_GLOBAL
,MSGL_ERR
,"[MENU] Waitpid error: %s.\n",strerror(errno
));
270 mp_tmsg(MSGT_GLOBAL
,MSGL_ERR
,"[MENU] Select error.\n");
275 for(i
= 1 ; i
< 3 ; i
++) {
276 if(FD_ISSET(mpriv
->child_fd
[i
],&rfd
)){
277 if(w
) mpriv
->add_line
= 1;
278 r
= read(mpriv
->child_fd
[i
],buffer
,255);
280 mp_tmsg(MSGT_GLOBAL
,MSGL_ERR
,"[MENU] Read error on child's file descriptor: %s.\n", i
== 1 ? "stdout":"stderr");
283 add_string(mpriv
,buffer
);
292 #define close_pipe(pipe) close(pipe[0]); close(pipe[1])
294 static int run_shell_cmd(menu_t
* menu
, char* cmd
) {
296 int in
[2],out
[2],err
[2];
298 mp_tmsg(MSGT_GLOBAL
,MSGL_INFO
,"[MENU] Console run: %s ...\n",cmd
);
300 mp_tmsg(MSGT_GLOBAL
,MSGL_ERR
,"[MENU] A child is already running.\n");
308 mpriv
->child
= fork();
309 if(mpriv
->child
< 0) {
310 mp_tmsg(MSGT_GLOBAL
,MSGL_ERR
,"[MENU] Fork failed !!!\n");
316 if(!mpriv
->child
) { // Chlid process
318 FILE* errf
= fdopen(err_fd
,"w");
319 // Bind the std fd to our pipes
323 execl("/bin/sh","sh","-c",cmd
,(void*)NULL
);
324 fprintf(errf
,"exec failed : %s\n",strerror(errno
));
328 mpriv
->child_fd
[0] = in
[1];
329 mpriv
->child_fd
[1] = out
[0];
330 mpriv
->child_fd
[2] = err
[0];
331 mpriv
->prompt
= mpriv
->child_prompt
;
332 //add_line(mpriv,"Child process started");
337 static void enter_cmd(menu_t
* menu
) {
339 char input
[strlen(mpriv
->cur_history
->buffer
) + strlen(mpriv
->prompt
) + 1];
341 sprintf(input
,"%s%s",mpriv
->prompt
,mpriv
->cur_history
->buffer
);
342 add_line(mpriv
,input
);
344 if(mpriv
->history
== mpriv
->cur_history
) {
345 if(mpriv
->history_size
>= mpriv
->history_max
) {
347 for(i
= mpriv
->history
; i
->prev
; i
= i
->prev
)
349 i
->next
->prev
= NULL
;
353 mpriv
->history_size
++;
354 h
= calloc(1,sizeof(history_t
));
356 h
->buffer
= calloc(h
->size
,1);
357 h
->prev
= mpriv
->history
;
358 mpriv
->history
->next
= h
;
361 mpriv
->history
->buffer
[0] = '\0';
363 mpriv
->cur_history
= mpriv
->history
;
364 //mpriv->input = mpriv->cur_history->buffer;
367 static void read_cmd(menu_t
* menu
,int cmd
) {
369 case MENU_CMD_CANCEL
:
371 mpriv
->hide_ts
= GetTimerMS();
379 char *str
= mpriv
->cur_history
->buffer
;
382 int w
= write(mpriv
->child_fd
[0],str
,l
);
384 mp_tmsg(MSGT_GLOBAL
,MSGL_ERR
,"[MENU] write error\n");
390 if(write(mpriv
->child_fd
[0],"\n",1) < 0)
391 mp_tmsg(MSGT_GLOBAL
,MSGL_ERR
,"[MENU] write error\n");
395 c
= mp_input_parse_cmd(mpriv
->cur_history
->buffer
);
398 add_line(mpriv
,"Invalid command try help");
402 add_line(mpriv
,"MPlayer console 0.01");
403 add_line(mpriv
,"TODO: meaningful help message ;)");
404 add_line(mpriv
,"Enter any slave command");
405 add_line(mpriv
,"exit close this console");
413 mpriv
->hide_ts
= GetTimerMS();
419 run_shell_cmd(menu
,c
->args
[0].v
.s
);
421 default: // Send the other commands to mplayer
422 mp_input_queue_cmd(menu
->input_ctx
, c
);
428 if(mpriv
->cur_history
->prev
)
429 mpriv
->cur_history
= mpriv
->cur_history
->prev
;
432 if(mpriv
->cur_history
->next
)
433 mpriv
->cur_history
= mpriv
->cur_history
->next
;
438 static int read_key(menu_t
* menu
,int c
) {
439 if(mpriv
->child
&& mpriv
->raw_child
) {
440 write(mpriv
->child_fd
[0],&c
,sizeof(int));
444 if (c
== KEY_DELETE
|| c
== KEY_BS
) {
445 unsigned int i
= strlen(mpriv
->cur_history
->buffer
);
447 mpriv
->cur_history
->buffer
[i
-1] = '\0';
450 if (menu_dflt_read_key(menu
, c
))
454 int l
= strlen(mpriv
->cur_history
->buffer
);
455 if(l
>= mpriv
->cur_history
->size
) {
456 mpriv
->cur_history
->size
+= 255;
457 mpriv
->cur_history
->buffer
= realloc(mpriv
->cur_history
,mpriv
->cur_history
->size
);
459 mpriv
->cur_history
->buffer
[l
] = (char)c
;
460 mpriv
->cur_history
->buffer
[l
+1] = '\0';
467 static int openMenu(menu_t
* menu
, char* args
) {
471 menu
->read_cmd
= read_cmd
;
472 menu
->read_key
= read_key
;
474 mpriv
->lines
= calloc(mpriv
->buf_lines
,sizeof(char*));
475 mpriv
->prompt
= mpriv
->mp_prompt
;
476 mpriv
->cur_history
= mpriv
->history
= calloc(1,sizeof(history_t
));
477 mpriv
->cur_history
->buffer
= calloc(255,1);
478 mpriv
->cur_history
->size
= 255;
481 add_line(mpriv
,args
);
486 const menu_info_t menu_info_console
= {
493 sizeof(struct menu_priv_s
),