"Fossies" - the Fresh Open Source Software Archive 
Member "mozplugger-2.1.6/mozplugger-controller.c" (17 Apr 2014, 21491 Bytes) of package /linux/www/old/mozplugger-2.1.6.tar.gz:
As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style:
standard) with prefixed line numbers and
code folding option.
Alternatively you can here
view or
download the uninterpreted source code file.
1 /**
2 * This file is part of mozplugger a fork of plugger, for list of developers
3 * see the README file.
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA.
18 */
19
20 #ifdef HAVE_CONFIG_H
21 #include "config.h"
22 #endif
23
24 #include <stdlib.h>
25 #include <stdint.h>
26 #include <unistd.h>
27 #include <string.h>
28 #include <stdio.h>
29 #include <sysexits.h>
30 #include <signal.h>
31 #include <sys/stat.h>
32 #include <fcntl.h>
33 #include <sys/wait.h>
34 #include <errno.h>
35 #include <X11/X.h>
36 #include <X11/Xutil.h>
37
38 #include "cmd_flags.h"
39 #include "child.h"
40 #include "debug.h"
41 #include "pipe_msg.h"
42 #include "widgets.h"
43
44
45 #define WINDOW_BORDER_WIDTH 1
46
47 struct SigGlobals_s
48 {
49 pid_t childPid;
50 };
51
52 typedef struct SigGlobals_s SigGlobals_t;
53
54 struct AppData_s
55 {
56 unsigned long cmd_flags;
57 char * command;
58 int repeats;
59 int dlProgress;
60 int paused;
61 };
62
63 typedef struct AppData_s AppData_t;
64
65
66
67 static SigGlobals_t sig_globals;
68 static GC gc_white;
69 static GC gc_black;
70 static GC gc_onColor;
71 static GC gc_offColor;
72
73 /**
74 * Redraw the three control buttons
75 *
76 * @param[in] dpy
77 * @param[in] win
78 * @param[in] mouseClickPos, where mouse was clicked
79 *
80 * @return Which button is down
81 */
82 static int redraw(Display * dpy, Window win, int mouseClickPos,
83 AppData_t * appData)
84 {
85 static int old_buttonsize = -1;
86
87 int buttonsize;
88 XWindowAttributes attr;
89 XPoint base;
90 int buttonDown = -1;
91
92 XGetWindowAttributes(dpy, win, &attr);
93 buttonsize = attr.width / 3;
94 if(attr.height < buttonsize)
95 {
96 buttonsize = attr.height;
97 }
98
99 if(old_buttonsize != buttonsize)
100 {
101 old_buttonsize = buttonsize;
102 XFillRectangle(dpy, win, gc_white,
103 0, 0, (unsigned)attr.width, (unsigned)attr.height);
104 }
105
106
107 base.x = (attr.width - buttonsize * 3)/2;
108 base.y = (attr.height - buttonsize)/2;
109
110 if(mouseClickPos > 0)
111 {
112 buttonDown = (mouseClickPos - base.x) / buttonsize;
113 }
114
115 /***** play ******/
116
117 drawPlayButton(dpy, win, &base, buttonsize,
118 ((sig_globals.childPid > 0) && (!appData->paused)) ? gc_onColor : gc_offColor,
119 gc_black, gc_white, buttonDown == 0);
120 #if 0
121 drawProgressBar(dpy, win, &base, buttonsize, gc_onColor, gc_black, gc_white, appData->dlProgress);
122 #endif
123
124 /***** pause *****/
125 base.x += buttonsize;
126 drawPauseButton(dpy, win, &base, buttonsize,
127 ((sig_globals.childPid > 0) && appData->paused) ? gc_onColor : gc_offColor,
128 gc_black, gc_white, buttonDown == 1);
129
130 /***** stop *****/
131 base.x += buttonsize;
132 drawStopButton(dpy, win, &base, buttonsize,
133 (sig_globals.childPid > 0) ? gc_onColor : gc_offColor, gc_black, gc_white,
134 buttonDown == 2);
135 return buttonDown;
136 }
137
138 /**
139 * Get the value of environment variable 'var' and convert to an integer value
140 * - use 'def' if enviroment variable does not exist
141 *
142 * @param[in] var The name of the variable
143 * @param[in] def The default value to use
144 *
145 * @return The integer value
146 */
147 static int igetenv(char *var, int def)
148 {
149 char *tmp=getenv(var);
150 if(!tmp)
151 {
152 return def;
153 }
154 return atoi(tmp);
155 }
156
157 /**
158 * Called when the user clicks on the play button
159 *
160 * @param[in] data Data associated with the application to execute
161 * @param[in] maxFd The maximum open file descriptor
162 */
163 static void my_play(AppData_t * appData)
164 {
165 appData->paused = 0;
166 if(sig_globals.childPid > 0)
167 {
168 if(!kill(-sig_globals.childPid, SIGCONT))
169 {
170 return;
171 }
172 }
173
174 sig_globals.childPid = spawn_app(appData->command, appData->cmd_flags);
175 }
176
177 /**
178 * Called when the user clicks on the pause button
179 *
180 * @param[in] appData Pointer to the App data structure
181 */
182 static void my_pause(AppData_t * appData)
183 {
184 if(sig_globals.childPid > 0)
185 {
186 if(!kill(-sig_globals.childPid, SIGSTOP))
187 {
188 appData->paused = 1;
189 }
190 }
191 }
192
193 /**
194 * Terminate the controller application
195 */
196 static void low_die(void)
197 {
198 if(sig_globals.childPid > 0)
199 {
200 kill_app(sig_globals.childPid);
201 }
202 _exit(0);
203 }
204
205 /**
206 * Called when the user clicks on the stop button
207 *
208 * @param[in] appData Pointer to the App data structure
209 */
210 static void my_stop(AppData_t * appData)
211 {
212 if(sig_globals.childPid > 0)
213 {
214 if(appData->paused)
215 {
216 kill(-sig_globals.childPid, SIGCONT);
217 appData->paused = 0;
218 }
219 kill_app(sig_globals.childPid);
220 sig_globals.childPid = -1;
221 }
222 }
223
224 /**
225 * Terminate, called from signal handler or when SHUTDOWN_MSG received
226 */
227 static void terminate(pid_t pid, Display * dpy)
228 {
229 if(pid >= 0)
230 {
231 kill_app(pid);
232 }
233
234 if(dpy)
235 {
236 XCloseDisplay(dpy);
237 }
238 }
239
240
241 /**
242 * Callback function passed to X for when error occurs. This terminates the
243 * controlled application
244 *
245 * @param[in] dpy The display pointer (not used)
246 * @param[in] ev The X event (not used)
247 *
248 * @return Always returns zero
249 */
250 static int die(Display *dpy, XErrorEvent *ev)
251 {
252 low_die();
253 return 0;
254 }
255
256 /**
257 * Callback function passed to X for when error occurs. This terminates the
258 * controlled application
259 *
260 * @param[in] dpy The display pointer (not used)
261 *
262 * @return Always returns zero
263 */
264 static int die2(Display *dpy)
265 {
266 low_die();
267 return 0;
268 }
269
270 /**
271 * Callback function attached as a signal handler. This terminates the
272 * controlled application
273 *
274 * @param[in] sig The signal (not used)
275 */
276 static void sigdie(int sig)
277 {
278 low_die();
279 }
280
281 /**
282 * Force the repaint to happen
283 *
284 * @param[in] dpy The display pointer
285 * @param[in] win The window ID
286 */
287 static void forceRepaint(Display * dpy, Window win)
288 {
289 XEvent event;
290
291 event.type = Expose;
292 event.xexpose.display = dpy;
293 event.xexpose.window = win;
294 event.xexpose.count = 0;
295
296 XSendEvent(dpy, win, False, ExposureMask, &event);
297 }
298
299 /**
300 * Checks for X events and processes them. Returns when no more events left
301 * to process.
302 *
303 * @param[in] dpy
304 * @param[in] win
305 *
306 * @return nothing
307 */
308 static void check_x_events(Display * dpy, Window win, AppData_t * appData)
309 {
310 int numEvents = XPending(dpy);
311 static int mouseClickPos = -1;
312
313 while(numEvents > 0)
314 {
315 XEvent ev;
316
317 XNextEvent(dpy, &ev);
318
319 switch(ev.type)
320 {
321 case ButtonPress:
322 if(ev.xbutton.button == 1)
323 {
324 int buttonDown;
325 mouseClickPos = ev.xbutton.x;
326 buttonDown = redraw(dpy, win, mouseClickPos, appData); /* do first gives quicker visual feedback */
327 XSync(dpy, False);
328
329 switch(buttonDown)
330 {
331 case 0: /* play */
332 my_play(appData);
333 break;
334 case 1: /* pause*/
335 my_pause(appData);
336 break;
337 case 2: /* stop */
338 my_stop(appData);
339 break;
340 }
341
342 redraw(dpy, win, mouseClickPos, appData);
343 }
344 break;
345
346 case ButtonRelease:
347 if(mouseClickPos != -1)
348 {
349 mouseClickPos = -1;
350 redraw(dpy, win, mouseClickPos, appData);
351 }
352 break;
353
354 case Expose:
355 if(ev.xexpose.count)
356 {
357 break;
358 }
359 case ResizeRequest:
360 case MapNotify:
361 redraw(dpy, win, mouseClickPos, appData);
362 break;
363
364 default:
365 D("Unknown event %d\n",ev.type);
366 break;
367 }
368
369 /* If this is the last of this batch, check that more havent
370 * been added in the meantime as a result of an action */
371 numEvents--;
372 if(numEvents == 0)
373 {
374 numEvents = XPending(dpy);
375 }
376 }
377 }
378
379 /**
380 * Given initial width & height of the window containing the buttons
381 * limit the width and aspect ratio and place in the center of the
382 * window (calculate x & y)
383 *
384 * @param[in,out] width The width before and after
385 * @param[in,out] height The height before and after
386 * @param[in,out] x The x co-ordinate before and after
387 * @param[in,out] y The y co-ordinate before and after
388 * @param[in] flags The Flags as read from mozpluggerrc
389 */
390 static void normalise_window_coords(unsigned * width, unsigned * height,
391 int * x, int * y, unsigned long flags)
392 {
393 unsigned w = *width;
394 unsigned h = *height;
395 const unsigned target_aspect = 3;
396
397 if(flags & H_SMALL_CNTRLS)
398 {
399 *y = *x = 0;
400 *width = 3 * MIN_BUTTON_SIZE;
401 *height = MIN_BUTTON_SIZE;
402 return;
403 }
404
405 if(w > 3 * MAX_BUTTON_SIZE)
406 {
407 w = 3 * MAX_BUTTON_SIZE;
408 }
409
410 /* Keep the controls as close to default ratio as possible */
411 if(h > w / target_aspect)
412 {
413 h = w / target_aspect;
414 }
415
416 *x = (int) (*width - w)/2;
417 *y = (int) (*height - h)/2;
418
419 *height = h;
420 *width = w;
421 }
422
423 /**
424 * Check to see if new window size information has arrived from plugin and if
425 * so resize the controls accordingly.
426 *
427 * @param[in] dpy The display
428 * @param[in] win The window
429 * @param[in] appData Pointer to the application data structure
430 * @param[in] pipeFd The Fd of the pipe
431 */
432 static void check_pipe_fd_events(Display * dpy, Window win,
433 AppData_t * appData, int pipe_fd)
434 {
435 struct PipeMsg_s msg;
436 int n;
437
438 D("Got pipe_fd data, pipe_fd=%d\n", pipe_fd);
439
440 n = read(pipe_fd, ((char *) &msg), sizeof(msg));
441 if((n == 0) || ((n < 0) && (errno != EINTR)))
442 {
443 D("Pipe returned n=%i\n", n);
444 terminate(sig_globals.childPid, dpy);
445 exit(EX_UNAVAILABLE);
446 }
447
448 if(n != sizeof(msg))
449 {
450 if(n > 0)
451 {
452 D("Pipe msg too short, size = %i\n", n);
453 }
454 return;
455 }
456
457 switch(msg.msgType)
458 {
459 case WINDOW_MSG:
460 {
461 int x, y;
462 /* Adjust the width & height to compensate for the window border width */
463 unsigned w = (unsigned) msg.window_msg.width - 2 * WINDOW_BORDER_WIDTH;
464 unsigned h = (unsigned) msg.window_msg.height - 2 * WINDOW_BORDER_WIDTH;
465
466 normalise_window_coords(&w, &h, &x, &y, appData->cmd_flags);
467
468 D("Controller window: x=%i, y=%i, w=%u, h=%u\n", x, y, w, h);
469
470 XMoveResizeWindow(dpy, win, x, y, w, h);
471 }
472 break;
473
474 case PROGRESS_MSG:
475 {
476 if(msg.progress_msg.done)
477 {
478 appData->dlProgress = -1;
479 }
480 else
481 {
482 appData->dlProgress++;
483 }
484 forceRepaint(dpy, win);
485 }
486 break;
487
488 case STATE_CHG_MSG:
489 {
490 switch(msg.stateChg_msg.stateChgReq)
491 {
492 case STOP_REQ:
493 my_stop(appData);
494 break;
495
496 case PLAY_REQ:
497 my_play(appData);
498 break;
499
500 case PAUSE_REQ:
501 my_pause(appData);
502 break;
503 }
504 forceRepaint(dpy, win);
505 }
506 break;
507 }
508 }
509
510 /**
511 * Called if we exit main() early because of invalid passed argument into
512 * the main function. print to both debug and stderr (just in case someone
513 * called mozplugger-controller directly)
514 */
515 static void exitEarly(void)
516 {
517 D("Invalid parameters passed to Controller - controller exiting\n");
518
519 fprintf(stderr,"MozPlugger version " VERSION
520 " controller application.\n"
521 "Please see 'man mozplugger' for details.\n");
522 exit(EXIT_FAILURE);
523 }
524
525 /**
526 * mozplugger-controller takes two arguments. The first is a shell command line
527 * to run when the 'play' button is pressed (or if autostart is set). The second
528 * parameter is optional and is the ID of the parent window into which to draw
529 * the controls.
530 *
531 * If the command line contains the string "$window", then no controls are
532 * drawn.
533 *
534 * @param[in] argc The number of arguments
535 * @param[in] argv Array of the arguments
536 * @param[out] pWidth Width of window
537 * @param[out] pHeight Height of window
538 * @param[out] pWindow The Window ID
539 * @param[out] command The command line
540 *
541 * @return True if all Ok
542 */
543 static int readCommandLine(int argc, char **argv, unsigned int * pWidth,
544 unsigned int * pHeight,
545 Window * pWindow,
546 AppData_t * appData,
547 int * pPipeFd)
548 {
549 unsigned long temp = 0;
550 int i;
551 char * fileName;
552
553 D("Controller started.....\n");
554
555 if (argc < 3)
556 {
557 return 0;
558 }
559
560 i = sscanf(argv[1],"%lu,%d,%d,%lu,%d,%d",
561 &appData->cmd_flags,
562 &appData->repeats,
563 pPipeFd,
564 &temp,
565 (int *)pWidth,
566 (int *)pHeight);
567
568 if(i < 6)
569 {
570 return 0;
571 }
572
573 *pWindow = (Window)temp;
574 appData->command = argv[2];
575
576 fileName = getenv("file");
577
578 D("CONTROLLER: %s %s %s %s\n",
579 argv[0],
580 argv[1],
581 fileName ? fileName : "NULL",
582 argv[2]);
583
584 /* The application will use the $repeat variable */
585 if (appData->cmd_flags & H_REPEATCOUNT)
586 {
587 appData->repeats = 1;
588 }
589
590 /* If the command contains a reference to window it is likely that that
591 * application also wants to draw into the same window as the controls
592 * so lets use small controls in top left corner */
593 if(strstr(appData->command,"$window") || strstr(appData->command,"$hexwindow"))
594 {
595 appData->cmd_flags |= H_SMALL_CNTRLS;
596 }
597 appData->paused = 0;
598 appData->dlProgress = 0;
599 return 1;
600 }
601
602 /**
603 * Create the on color
604 *
605 * @param[in] dpy Display handle
606 * @param[in] window The window handle
607 */
608 static void createOnColor(Display * dpy, Window window)
609 {
610 XColor colour;
611
612 gc_onColor = XCreateGC(dpy, window, 0, 0);
613 colour.red = 0x0000;
614 colour.green = 0xa000;
615 colour.blue = 0x0000;
616 colour.pixel = 0;
617 colour.flags=0;
618
619 XAllocColor(dpy, DefaultColormap(dpy, DefaultScreen(dpy)), &colour);
620 XSetForeground(dpy, gc_onColor, colour.pixel);
621 }
622
623 /**
624 * Create the off color
625 *
626 * @param[in] dpy Display handle
627 * @param[in] window The window handle
628 */
629 static void createOffColor(Display * dpy, Window window)
630 {
631 XColor colour;
632
633 gc_offColor = XCreateGC(dpy, window, 0, 0);
634 colour.red = 0x8000;
635 colour.green = 0x8000;
636 colour.blue = 0x8000;
637 colour.pixel = 0;
638 colour.flags = 0;
639
640 XAllocColor(dpy, DefaultColormap(dpy, DefaultScreen(dpy)), &colour);
641 XSetForeground(dpy, gc_offColor, colour.pixel);
642 }
643
644
645 /**
646 * mozplugger-controller main()
647 *
648 * If the command line contains the string "$window", then no controls are
649 * drawn.
650 *
651 * @param[in] argc The number of arguments
652 * @param[in] argv Array of the arguments
653 *
654 * @return Never returns unless the application exits
655 */
656 int main(int argc, char **argv)
657 {
658 /* Set defaults, may be changed later */
659 unsigned int width = 3 * DEFAULT_BUTTON_SIZE;
660 unsigned int height = DEFAULT_BUTTON_SIZE;
661 int x, y;
662
663 Window parentWid = 0;
664 AppData_t appData;
665
666 XSetWindowAttributes attr;
667 Display * dpy = 0;
668 Window topLevel;
669 int pipe_fd;
670 int repeatsLeft;
671 int sig_chld_fd;
672
673 if(!readCommandLine(argc, argv, &width,
674 &height,
675 &parentWid,
676 &appData,
677 &pipe_fd))
678 {
679 exitEarly();
680 }
681
682
683 repeatsLeft = appData.repeats;
684
685 if(!(dpy = XOpenDisplay(getenv("DISPLAY"))))
686 {
687 D("%s: unable to open display %s\n",
688 argv[0], XDisplayName(getenv("DISPLAY")));
689 exitEarly();
690 }
691
692 /* Adjust the width & height to compensate for the window border
693 * width */
694 width -= 2 * WINDOW_BORDER_WIDTH;
695 height -= 2 * WINDOW_BORDER_WIDTH;
696
697 /* x, y co-ords of the parent is of no interest, we need to know
698 * the x, y relative to the parent. */
699 normalise_window_coords(&width, &height, &x, &y, appData.cmd_flags);
700
701 D("Controller window: x=%i, y=%i, w=%u, h=%u\n", x, y, width, height);
702
703 attr.border_pixel = 0;
704 attr.background_pixel = WhitePixelOfScreen(DefaultScreenOfDisplay(dpy));
705 attr.event_mask = ExposureMask | ButtonPressMask | ButtonReleaseMask;
706 attr.override_redirect=0;
707
708 topLevel = XCreateWindow(dpy,
709 parentWid,
710 x, y,
711 width, height,
712 WINDOW_BORDER_WIDTH,
713 CopyFromParent,
714 InputOutput, (Visual *) CopyFromParent,
715 (unsigned long)(CWBorderPixel|
716 CWEventMask|
717 CWOverrideRedirect|
718 CWBackPixel),
719 &attr);
720
721 setWindowClassHint(dpy, topLevel, "mozplugger-controller");
722
723 gc_black=XCreateGC(dpy,topLevel,0,0);
724 XSetForeground(dpy,gc_black,BlackPixel(dpy,DefaultScreen(dpy)));
725
726 gc_white=XCreateGC(dpy,topLevel,0,0);
727 XSetForeground(dpy,gc_white,WhitePixel(dpy,DefaultScreen(dpy)));
728
729 createOnColor(dpy, topLevel);
730
731 createOffColor(dpy, topLevel);
732
733 setWindowHints(dpy, topLevel, 3);
734
735 /* Map the window, if the parent has asked for redirect this does nothing
736 * (i.e. if swallow has been used in mozplugger.c) */
737 XMapWindow(dpy, topLevel);
738
739 XSetIOErrorHandler(die2);
740 XSetErrorHandler(die);
741
742
743 sig_globals.childPid = -1;
744 signal(SIGHUP, sigdie);
745 signal(SIGINT, sigdie);
746 signal(SIGTERM, sigdie);
747
748 if(igetenv("autostart",1))
749 {
750 my_play(&appData);
751 }
752
753 sig_chld_fd = redirect_SIGCHLD_to_fd();
754
755 while(1)
756 {
757 fd_set fds;
758 int maxFd;
759 int rd_chld_fd = get_chld_out_fd();
760
761 check_x_events(dpy, topLevel, &appData);
762
763 FD_ZERO(&fds);
764
765 maxFd = ConnectionNumber(dpy);
766 FD_SET(ConnectionNumber(dpy), &fds);
767
768 maxFd = pipe_fd > maxFd ? pipe_fd : maxFd;
769 FD_SET(pipe_fd, &fds);
770
771 if(sig_chld_fd >= 0)
772 {
773 maxFd = sig_chld_fd > maxFd ? sig_chld_fd : maxFd;
774 FD_SET(sig_chld_fd, &fds);
775 }
776 if(rd_chld_fd >= 0)
777 {
778 FD_SET(rd_chld_fd, &fds);
779 maxFd = rd_chld_fd > maxFd ? rd_chld_fd : maxFd;
780 }
781
782
783 D("SELECT IN maxFd = %i\n", maxFd);
784 if( select(maxFd + 1, &fds, NULL, NULL, 0) > 0)
785 {
786 if (FD_ISSET(pipe_fd, &fds))
787 {
788 check_pipe_fd_events(dpy, topLevel, &appData, pipe_fd);
789 }
790 if(FD_ISSET(sig_chld_fd, &fds))
791 {
792 handle_SIGCHLD_event();
793 }
794 if( FD_ISSET(rd_chld_fd, &fds))
795 {
796 handle_chld_out_event(rd_chld_fd);
797 }
798 }
799 D("SELECT OUT\n");
800
801 if(sig_globals.childPid > 0)
802 {
803 int status;
804 if(waitpid(sig_globals.childPid, &status, WNOHANG) > 0)
805 {
806 appData.paused = 0;
807 sig_globals.childPid = -1;
808 if(appData.repeats != INF_LOOPS)
809 {
810 repeatsLeft--;
811 }
812 if(repeatsLeft > 0)
813 {
814 my_play(&appData);
815 rd_chld_fd = get_chld_out_fd();
816 }
817 forceRepaint(dpy, topLevel);
818 }
819 }
820 else
821 {
822 repeatsLeft = appData.repeats;
823 }
824 }
825 }
826