Merge svn changes up to r31256
[mplayer/glamo.git] / libmenu / menu_console.c
blob8e824123275dab323c99e6b3af2a93aea71e3e2b
1 /*
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.
19 #include "config.h"
20 #include "mp_msg.h"
22 #include <stdlib.h>
23 #include <stdio.h>
24 #include <string.h>
25 #include <ctype.h>
26 #include <sys/time.h>
27 #include <sys/types.h>
28 #ifndef __MINGW32__
29 #include <sys/wait.h>
30 #endif
31 #include <unistd.h>
32 #include <errno.h>
34 #include "libmpcodecs/img_format.h"
35 #include "libmpcodecs/mp_image.h"
37 #include "m_struct.h"
38 #include "m_option.h"
39 #include "menu.h"
41 #include "libvo/font_load.h"
42 #include "osdep/keycodes.h"
43 #include "input/input.h"
44 #include "osdep/timer.h"
46 typedef struct history_st history_t;
48 struct history_st {
49 char* buffer;
50 int size;
51 history_t* next;
52 history_t* prev;
55 struct menu_priv_s {
56 char** lines; // Our buffer
57 int last_line;
58 int num_lines;
59 int add_line;
60 unsigned int hide_ts;
61 unsigned int show_ts;
62 pid_t child; // Child process if we are running a shell cmd
63 int child_fd[3]; // The 3 default fd
64 char* prompt;
65 //int max_lines; // Max number of lines with the last mpi
66 history_t* history;
67 history_t* cur_history;
68 int history_size;
70 char* mp_prompt;
71 char* child_prompt;
72 int buf_lines; // Buffer size (in line)
73 int height; // Display size in %
74 int minb;
75 int vspace;
76 int bg,bg_alpha;
77 unsigned int hide_time;
78 unsigned int show_time;
79 int history_max;
80 int raw_child;
83 static struct menu_priv_s cfg_dflt = {
84 NULL,
91 { 0 , 0, 0 },
92 NULL,
93 NULL,
94 NULL,
97 "# ",
98 "$ ",
99 50, // lines
100 33, // %
103 0x80,0x40,
104 500,
105 500,
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');
135 if(eol) {
136 if(eol != l) {
137 eol[0] = '\0';
138 add_line(priv,l);
140 if(eol[1]) add_line(priv,eol+1);
141 return;
144 if(priv->num_lines >= priv->buf_lines && priv->lines[priv->last_line])
145 free(priv->lines[priv->last_line]);
146 else
147 priv->num_lines++;
149 priv->lines[priv->last_line] = strdup(l);
150 priv->last_line = (priv->last_line + 1) % priv->buf_lines;
151 priv->add_line = 1;
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) {
159 add_line(priv,l);
160 priv->add_line = 0;
161 return;
164 if(eol) {
165 eol[0] = '\0';
166 add_string(priv,l);
167 if(eol[1]) {
168 add_line(priv,eol+1);
169 priv->add_line = 0;
170 } else
171 priv->add_line = 1;
172 return;
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;
185 int lw,lh,i, ll;
187 if(mpriv->child) check_child(menu);
189 ll = mpriv->last_line - 1;
191 if(mpriv->hide_ts) {
192 unsigned int t = GetTimerMS() - mpriv->hide_ts;
193 if(t >= mpriv->hide_time) {
194 mpriv->hide_ts = 0;
195 menu->show = 0;
196 return;
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();
201 return;
202 } else if(mpriv->show_ts > 0) {
203 unsigned int t = GetTimerMS() - mpriv->show_ts;
204 if(t > mpriv->show_time)
205 mpriv->show_ts = -1;
206 else
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 )
213 return;
215 if(mpriv->bg >= 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;
237 return;
240 static void check_child(menu_t* menu) {
241 #ifndef __MINGW32__
242 fd_set rfd;
243 struct timeval tv;
244 int max_fd = mpriv->child_fd[2] > mpriv->child_fd[1] ? mpriv->child_fd[2] :
245 mpriv->child_fd[1];
246 int i,r,child_status,w;
247 char buffer[256];
249 if(!mpriv->child) return;
251 memset(&tv,0,sizeof(struct timeval));
252 FD_ZERO(&rfd);
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);
257 if(r == 0) {
258 r = waitpid(mpriv->child,&child_status,WNOHANG);
259 if(r < 0){
260 if(errno==ECHILD){ ///exiting children get handled in mplayer.c
261 for(i = 0 ; i < 3 ; i++)
262 close(mpriv->child_fd[i]);
263 mpriv->child = 0;
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));
269 } else if(r < 0) {
270 mp_tmsg(MSGT_GLOBAL,MSGL_ERR,"[MENU] Select error.\n");
271 return;
274 w = 0;
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);
279 if(r < 0)
280 mp_tmsg(MSGT_GLOBAL,MSGL_ERR,"[MENU] Read error on child's file descriptor: %s.\n", i == 1 ? "stdout":"stderr");
281 else if(r>0) {
282 buffer[r] = '\0';
283 add_string(mpriv,buffer);
285 w = 1;
288 #endif
292 #define close_pipe(pipe) close(pipe[0]); close(pipe[1])
294 static int run_shell_cmd(menu_t* menu, char* cmd) {
295 #ifndef __MINGW32__
296 int in[2],out[2],err[2];
298 mp_tmsg(MSGT_GLOBAL,MSGL_INFO,"[MENU] Console run: %s ...\n",cmd);
299 if(mpriv->child) {
300 mp_tmsg(MSGT_GLOBAL,MSGL_ERR,"[MENU] A child is already running.\n");
301 return 0;
304 pipe(in);
305 pipe(out);
306 pipe(err);
308 mpriv->child = fork();
309 if(mpriv->child < 0) {
310 mp_tmsg(MSGT_GLOBAL,MSGL_ERR,"[MENU] Fork failed !!!\n");
311 close_pipe(in);
312 close_pipe(out);
313 close_pipe(err);
314 return 0;
316 if(!mpriv->child) { // Chlid process
317 int err_fd = dup(2);
318 FILE* errf = fdopen(err_fd,"w");
319 // Bind the std fd to our pipes
320 dup2(in[0],0);
321 dup2(out[1],1);
322 dup2(err[1],2);
323 execl("/bin/sh","sh","-c",cmd,(void*)NULL);
324 fprintf(errf,"exec failed : %s\n",strerror(errno));
325 exit(1);
327 // MPlayer
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");
333 #endif
334 return 1;
337 static void enter_cmd(menu_t* menu) {
338 history_t* h;
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) {
346 history_t* i;
347 for(i = mpriv->history ; i->prev ; i = i->prev)
348 /**/;
349 i->next->prev = NULL;
350 free(i->buffer);
351 free(i);
352 } else
353 mpriv->history_size++;
354 h = calloc(1,sizeof(history_t));
355 h->size = 255;
356 h->buffer = calloc(h->size,1);
357 h->prev = mpriv->history;
358 mpriv->history->next = h;
359 mpriv->history = h;
360 } else
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) {
368 switch(cmd) {
369 case MENU_CMD_CANCEL:
370 if(mpriv->hide_time)
371 mpriv->hide_ts = GetTimerMS();
372 else
373 menu->show = 0;
374 mpriv->show_ts = 0;
375 return;
376 case MENU_CMD_OK: {
377 mp_cmd_t* c;
378 if(mpriv->child) {
379 char *str = mpriv->cur_history->buffer;
380 int l = strlen(str);
381 while(l > 0) {
382 int w = write(mpriv->child_fd[0],str,l);
383 if(w < 0) {
384 mp_tmsg(MSGT_GLOBAL,MSGL_ERR,"[MENU] write error\n");
385 break;
387 l -= w;
388 str += w;
390 if(write(mpriv->child_fd[0],"\n",1) < 0)
391 mp_tmsg(MSGT_GLOBAL,MSGL_ERR,"[MENU] write error\n");
392 enter_cmd(menu);
393 return;
395 c = mp_input_parse_cmd(mpriv->cur_history->buffer);
396 enter_cmd(menu);
397 if(!c)
398 add_line(mpriv,"Invalid command try help");
399 else {
400 switch(c->id) {
401 case MP_CMD_CHELP:
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");
406 break;
407 case MP_CMD_CEXIT:
408 menu->show = 0;
409 menu->cl = 1;
410 break;
411 case MP_CMD_CHIDE:
412 if(mpriv->hide_time)
413 mpriv->hide_ts = GetTimerMS();
414 else
415 menu->show = 0;
416 mpriv->show_ts = 0;
417 break;
418 case MP_CMD_RUN:
419 run_shell_cmd(menu,c->args[0].v.s);
420 break;
421 default: // Send the other commands to mplayer
422 mp_input_queue_cmd(menu->input_ctx, c);
425 return;
427 case MENU_CMD_UP:
428 if(mpriv->cur_history->prev)
429 mpriv->cur_history = mpriv->cur_history->prev;
430 break;
431 case MENU_CMD_DOWN:
432 if(mpriv->cur_history->next)
433 mpriv->cur_history = mpriv->cur_history->next;
434 break;
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));
441 return 1;
444 if (c == KEY_DELETE || c == KEY_BS) {
445 unsigned int i = strlen(mpriv->cur_history->buffer);
446 if(i > 0)
447 mpriv->cur_history->buffer[i-1] = '\0';
448 return 1;
450 if (menu_dflt_read_key(menu, c))
451 return 1;
453 if(isascii(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';
461 return 1;
463 return 0;
467 static int openMenu(menu_t* menu, char* args) {
470 menu->draw = draw;
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;
480 if(args)
481 add_line(mpriv,args);
483 return 1;
486 const menu_info_t menu_info_console = {
487 "MPlayer console",
488 "console",
489 "Albeu",
492 "console_cfg",
493 sizeof(struct menu_priv_s),
494 &cfg_dflt,
495 cfg_fields
497 openMenu,