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.
28 #include <sys/types.h>
35 #include "libmpcodecs/img_format.h"
36 #include "libmpcodecs/mp_image.h"
42 #include "libvo/font_load.h"
43 #include "osdep/keycodes.h"
44 #include "input/input.h"
45 #include "osdep/timer.h"
47 typedef struct history_st history_t
;
57 char** lines
; // Our buffer
63 pid_t child
; // Child process if we are running a shell cmd
64 int child_fd
[3]; // The 3 default fd
66 //int max_lines; // Max number of lines with the last mpi
68 history_t
* cur_history
;
73 int buf_lines
; // Buffer size (in line)
74 int height
; // Display size in %
78 unsigned int hide_time
;
79 unsigned int show_time
;
84 static struct menu_priv_s cfg_dflt
= {
111 #define ST_OFF(m) M_ST_OFF(struct menu_priv_s,m)
113 static m_option_t cfg_fields
[] = {
114 { "prompt", ST_OFF(mp_prompt
), CONF_TYPE_STRING
, M_OPT_MIN
, 1, 0, NULL
},
115 { "child-prompt", ST_OFF(child_prompt
), CONF_TYPE_STRING
, M_OPT_MIN
, 1, 0, NULL
},
116 { "buffer-lines", ST_OFF(buf_lines
), CONF_TYPE_INT
, M_OPT_MIN
, 5, 0, NULL
},
117 { "height", ST_OFF(height
), CONF_TYPE_INT
, M_OPT_RANGE
, 1, 100, NULL
},
118 { "minbor", ST_OFF(minb
), CONF_TYPE_INT
, M_OPT_MIN
, 0, 0, NULL
},
119 { "vspace", ST_OFF(vspace
), CONF_TYPE_INT
, M_OPT_MIN
, 0, 0, NULL
},
120 { "bg", ST_OFF(bg
), CONF_TYPE_INT
, M_OPT_RANGE
, -1, 255, NULL
},
121 { "bg-alpha", ST_OFF(bg_alpha
), CONF_TYPE_INT
, M_OPT_RANGE
, 0, 255, NULL
},
122 { "show-time",ST_OFF(show_time
), CONF_TYPE_INT
, M_OPT_MIN
, 0, 0, NULL
},
123 { "hide-time",ST_OFF(hide_time
), CONF_TYPE_INT
, M_OPT_MIN
, 0, 0, NULL
},
124 { "history-size",ST_OFF(history_max
), CONF_TYPE_INT
, M_OPT_MIN
, 1, 0, NULL
},
125 { "raw-child", ST_OFF(raw_child
), CONF_TYPE_FLAG
, 0, 0, 1, NULL
},
126 { NULL
, NULL
, NULL
, 0,0,0,NULL
}
129 #define mpriv (menu->priv)
131 static void check_child(menu_t
* menu
);
133 static void add_line(struct menu_priv_s
* priv
, char* l
) {
134 char* eol
= strchr(l
,'\n');
141 if(eol
[1]) add_line(priv
,eol
+1);
145 if(priv
->num_lines
>= priv
->buf_lines
&& priv
->lines
[priv
->last_line
])
146 free(priv
->lines
[priv
->last_line
]);
150 priv
->lines
[priv
->last_line
] = strdup(l
);
151 priv
->last_line
= (priv
->last_line
+ 1) % priv
->buf_lines
;
155 static void add_string(struct menu_priv_s
* priv
, char* l
) {
156 char* eol
= strchr(l
,'\n');
157 int ll
= priv
->last_line
> 0 ? priv
->last_line
- 1 : priv
->buf_lines
-1;
159 if(priv
->num_lines
<= 0 || priv
->add_line
|| eol
== l
) {
169 add_line(priv
,eol
+1);
175 priv
->lines
[ll
] = realloc(priv
->lines
[ll
],strlen(priv
->lines
[ll
]) + strlen(l
) + 1);
176 if ( priv
->lines
[ll
] != NULL
)
178 strcat(priv
->lines
[ll
],l
);
182 static void draw(menu_t
* menu
, mp_image_t
* mpi
) {
183 int h
= mpi
->h
*mpriv
->height
/100;
184 int w
= mpi
->w
- 2* mpriv
->minb
;
185 int x
= mpriv
->minb
, y
;
188 if(mpriv
->child
) check_child(menu
);
190 ll
= mpriv
->last_line
- 1;
193 unsigned int t
= GetTimerMS() - mpriv
->hide_ts
;
194 if(t
>= mpriv
->hide_time
) {
199 h
= mpi
->h
*(mpriv
->height
- (mpriv
->height
* t
/mpriv
->hide_time
))/100;
200 } else if(mpriv
->show_time
&& mpriv
->show_ts
== 0) {
201 mpriv
->show_ts
= GetTimerMS();
203 } else if(mpriv
->show_ts
> 0) {
204 unsigned int t
= GetTimerMS() - mpriv
->show_ts
;
205 if(t
> mpriv
->show_time
)
208 h
= mpi
->h
*(mpriv
->height
* t
/mpriv
->hide_time
)/100;
211 y
= h
- mpriv
->vspace
;
213 if(x
< 0 || y
< 0 || w
<= 0 || h
<= 0 )
217 menu_draw_box(mpi
,mpriv
->bg
,mpriv
->bg_alpha
,0,0,mpi
->w
,h
);
219 if(!mpriv
->child
|| !mpriv
->raw_child
){
220 char input
[strlen(mpriv
->cur_history
->buffer
) + strlen(mpriv
->prompt
) + 1];
221 sprintf(input
,"%s%s",mpriv
->prompt
,mpriv
->cur_history
->buffer
);
222 menu_text_size(input
,w
,mpriv
->vspace
,1,&lw
,&lh
);
223 menu_draw_text_full(mpi
,input
,x
,y
,w
,h
,mpriv
->vspace
,1,
224 MENU_TEXT_BOT
|MENU_TEXT_LEFT
,
225 MENU_TEXT_BOT
|MENU_TEXT_LEFT
);
226 y
-= lh
+ mpriv
->vspace
;
230 for( i
= 0 ; y
> mpriv
->minb
&& i
< mpriv
->num_lines
; i
++){
231 int c
= (ll
- i
) >= 0 ? ll
- i
: mpriv
->buf_lines
+ ll
- i
;
232 menu_text_size(mpriv
->lines
[c
],w
,mpriv
->vspace
,1,&lw
,&lh
);
233 menu_draw_text_full(mpi
,mpriv
->lines
[c
],x
,y
,w
,h
,mpriv
->vspace
,1,
234 MENU_TEXT_BOT
|MENU_TEXT_LEFT
,
235 MENU_TEXT_BOT
|MENU_TEXT_LEFT
);
236 y
-= lh
+ mpriv
->vspace
;
241 static void check_child(menu_t
* menu
) {
245 int max_fd
= mpriv
->child_fd
[2] > mpriv
->child_fd
[1] ? mpriv
->child_fd
[2] :
247 int i
,r
,child_status
,w
;
250 if(!mpriv
->child
) return;
252 memset(&tv
,0,sizeof(struct timeval
));
254 FD_SET(mpriv
->child_fd
[1],&rfd
);
255 FD_SET(mpriv
->child_fd
[2],&rfd
);
257 r
= select(max_fd
+1,&rfd
, NULL
, NULL
, &tv
);
259 r
= waitpid(mpriv
->child
,&child_status
,WNOHANG
);
261 if(errno
==ECHILD
){ ///exiting children get handled in mplayer.c
262 for(i
= 0 ; i
< 3 ; i
++)
263 close(mpriv
->child_fd
[i
]);
265 mpriv
->prompt
= mpriv
->mp_prompt
;
266 //add_line(mpriv,"Child process exited");
268 else mp_msg(MSGT_GLOBAL
,MSGL_ERR
,MSGTR_LIBMENU_WaitPidError
,strerror(errno
));
271 mp_msg(MSGT_GLOBAL
,MSGL_ERR
,MSGTR_LIBMENU_SelectError
);
276 for(i
= 1 ; i
< 3 ; i
++) {
277 if(FD_ISSET(mpriv
->child_fd
[i
],&rfd
)){
278 if(w
) mpriv
->add_line
= 1;
279 r
= read(mpriv
->child_fd
[i
],buffer
,255);
281 mp_msg(MSGT_GLOBAL
,MSGL_ERR
,MSGTR_LIBMENU_ReadErrorOnChildFD
, i
== 1 ? "stdout":"stderr");
284 add_string(mpriv
,buffer
);
293 #define close_pipe(pipe) close(pipe[0]); close(pipe[1])
295 static int run_shell_cmd(menu_t
* menu
, char* cmd
) {
297 int in
[2],out
[2],err
[2];
299 mp_msg(MSGT_GLOBAL
,MSGL_INFO
,MSGTR_LIBMENU_ConsoleRun
,cmd
);
301 mp_msg(MSGT_GLOBAL
,MSGL_ERR
,MSGTR_LIBMENU_AChildIsAlreadyRunning
);
309 mpriv
->child
= fork();
310 if(mpriv
->child
< 0) {
311 mp_msg(MSGT_GLOBAL
,MSGL_ERR
,MSGTR_LIBMENU_ForkFailed
);
317 if(!mpriv
->child
) { // Chlid process
319 FILE* errf
= fdopen(err_fd
,"w");
320 // Bind the std fd to our pipes
324 execl("/bin/sh","sh","-c",cmd
,(void*)NULL
);
325 fprintf(errf
,"exec failed : %s\n",strerror(errno
));
329 mpriv
->child_fd
[0] = in
[1];
330 mpriv
->child_fd
[1] = out
[0];
331 mpriv
->child_fd
[2] = err
[0];
332 mpriv
->prompt
= mpriv
->child_prompt
;
333 //add_line(mpriv,"Child process started");
338 static void enter_cmd(menu_t
* menu
) {
340 char input
[strlen(mpriv
->cur_history
->buffer
) + strlen(mpriv
->prompt
) + 1];
342 sprintf(input
,"%s%s",mpriv
->prompt
,mpriv
->cur_history
->buffer
);
343 add_line(mpriv
,input
);
345 if(mpriv
->history
== mpriv
->cur_history
) {
346 if(mpriv
->history_size
>= mpriv
->history_max
) {
348 for(i
= mpriv
->history
; i
->prev
; i
= i
->prev
)
350 i
->next
->prev
= NULL
;
354 mpriv
->history_size
++;
355 h
= calloc(1,sizeof(history_t
));
357 h
->buffer
= calloc(h
->size
,1);
358 h
->prev
= mpriv
->history
;
359 mpriv
->history
->next
= h
;
362 mpriv
->history
->buffer
[0] = '\0';
364 mpriv
->cur_history
= mpriv
->history
;
365 //mpriv->input = mpriv->cur_history->buffer;
368 static void read_cmd(menu_t
* menu
,int cmd
) {
370 case MENU_CMD_CANCEL
:
372 mpriv
->hide_ts
= GetTimerMS();
380 char *str
= mpriv
->cur_history
->buffer
;
383 int w
= write(mpriv
->child_fd
[0],str
,l
);
385 mp_msg(MSGT_GLOBAL
,MSGL_ERR
,MSGTR_LIBMENU_WriteError
);
391 if(write(mpriv
->child_fd
[0],"\n",1) < 0)
392 mp_msg(MSGT_GLOBAL
,MSGL_ERR
,MSGTR_LIBMENU_WriteError
);
396 c
= mp_input_parse_cmd(mpriv
->cur_history
->buffer
);
399 add_line(mpriv
,"Invalid command try help");
403 add_line(mpriv
,"MPlayer console 0.01");
404 add_line(mpriv
,"TODO: meaningful help message ;)");
405 add_line(mpriv
,"Enter any slave command");
406 add_line(mpriv
,"exit close this console");
414 mpriv
->hide_ts
= GetTimerMS();
420 run_shell_cmd(menu
,c
->args
[0].v
.s
);
422 default: // Send the other commands to mplayer
423 mp_input_queue_cmd(menu
->input_ctx
, c
);
429 if(mpriv
->cur_history
->prev
)
430 mpriv
->cur_history
= mpriv
->cur_history
->prev
;
433 if(mpriv
->cur_history
->next
)
434 mpriv
->cur_history
= mpriv
->cur_history
->next
;
439 static int read_key(menu_t
* menu
,int c
) {
440 if(mpriv
->child
&& mpriv
->raw_child
) {
441 write(mpriv
->child_fd
[0],&c
,sizeof(int));
445 if (c
== KEY_DELETE
|| c
== KEY_BS
) {
446 unsigned int i
= strlen(mpriv
->cur_history
->buffer
);
448 mpriv
->cur_history
->buffer
[i
-1] = '\0';
451 if (menu_dflt_read_key(menu
, c
))
455 int l
= strlen(mpriv
->cur_history
->buffer
);
456 if(l
>= mpriv
->cur_history
->size
) {
457 mpriv
->cur_history
->size
+= 255;
458 mpriv
->cur_history
->buffer
= realloc(mpriv
->cur_history
,mpriv
->cur_history
->size
);
460 mpriv
->cur_history
->buffer
[l
] = (char)c
;
461 mpriv
->cur_history
->buffer
[l
+1] = '\0';
468 static int openMenu(menu_t
* menu
, char* args
) {
472 menu
->read_cmd
= read_cmd
;
473 menu
->read_key
= read_key
;
475 mpriv
->lines
= calloc(mpriv
->buf_lines
,sizeof(char*));
476 mpriv
->prompt
= mpriv
->mp_prompt
;
477 mpriv
->cur_history
= mpriv
->history
= calloc(1,sizeof(history_t
));
478 mpriv
->cur_history
->buffer
= calloc(255,1);
479 mpriv
->cur_history
->size
= 255;
482 add_line(mpriv
,args
);
487 const menu_info_t menu_info_console
= {
494 sizeof(struct menu_priv_s
),