1 /**
2  * \file gnome_ui.c
3  *
4  * This file contains the main control UI for xvidcap
5  * \todo rename the global variables pthread* to xvc_pthread*
6  */
7 
8 /* Copyright (C) 2003-07 Karl H. Beckers, Frankfurt
9  * EMail: khb@jarre-de-the.net
10  *
11  * This program is free software; you can redistribute it and/or modify
12  * it under the terms of the GNU General Public License as published by
13  * the Free Software Foundation; either version 2 of the License, or
14  * (at your option) any later version.
15  *
16  * This program is distributed in the hope that it will be useful,
17  * but WITHOUT ANY WARRANTY; without even the implied warranty of
18  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19  * GNU General Public License for more details.
20  *
21  * You should have received a copy of the GNU General Public License
22  * along with this program; if not, write to the Free Software
23  * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
24  *
25  *
26  */
27 #ifndef DOXYGEN_SHOULD_SKIP_THIS
28 #ifdef HAVE_CONFIG_H
29 # include <config.h>
30 #endif
31 
32 #define DEBUGFILE "gnome_ui.c"
33 #endif     // DOXYGEN_SHOULD_SKIP_THIS
34 
35 #include <sys/time.h>                  // for timeval struct and related
36 #include <sys/poll.h>
37 #include <stdlib.h>
38 #include <unistd.h>
39 
40 #ifdef SOLARIS
41 #include <X11/X.h>
42 #include <X11/Xlib.h>
43 #endif     // SOLARIS
44 #include <X11/cursorfont.h>
45 #include <X11/Xmu/WinUtil.h>
46 #include <X11/Xatom.h>
47 
48 #ifdef USE_XDAMAGE
49 #include <X11/extensions/Xfixes.h>
50 #include <X11/extensions/Xdamage.h>
51 #include <X11/Xproto.h>
52 #endif     // USE_XDAMAGE
53 
54 #include <glib.h>
55 #include <gdk/gdkx.h>
56 #include <glade/glade.h>
57 #include <pthread.h>
58 #include <signal.h>
59 #include <errno.h>
60 
61 #include "led_meter.h"
62 #include "job.h"
63 #include "app_data.h"
64 #include "control.h"
65 #include "colors.h"
66 #include "codecs.h"
67 #include "frame.h"
68 #include "gnome_warning.h"
69 #include "gnome_options.h"
70 #include "gnome_ui.h"
71 #include "gnome_frame.h"
72 #include "xvidcap-intl.h"
73 #include "eggtrayicon.h"
74 
75 /*
76  * globals
77  */
78 #ifndef DOXYGEN_SHOULD_SKIP_THIS
79 extern GtkWidget *xvc_pref_main_window;
80 extern GtkWidget *xvc_warn_main_window;
81 #endif     // DOXYGEN_SHOULD_SKIP_THIS
82 
83 /** \brief make error numbers accessible */
84 extern int errno;
85 
86 /** \brief make the main control panel globally available */
87 GtkWidget *xvc_ctrl_main_window = NULL;
88 
89 /** \brief popup menu for the system tray icon */
90 GtkWidget *xvc_tray_icon_menu = NULL;
91 
92 /**
93  * \brief make the main control panel's menu globally available
94  *
95  * @note this is needed because the warning dialog may need to change the
96  *      status of capture type select buttons. If you change from e. g.
97  *      single-frame capture to multi-frame capture, xvidcap checks your
98  *      preferences for the new capture type and finding errors allows you
99  *      to cancel back out of the change ... and needs to reset the menu.
100  */
101 GtkWidget *xvc_ctrl_m1 = NULL;
102 
103 /**
104  * \brief the value here is picked up by the LED frame drop monitor widget.
105  *      Set to 0 to clear the widget.
106  */
107 int xvc_led_time = 0;
108 
109 /** \brief the recording thread */
110 static pthread_t recording_thread;
111 
112 /** \brief attributes of the recording thread */
113 static pthread_attr_t recording_thread_attr;
114 
115 /** \brief make the results dialog globally available to callbacks here */
116 static GtkWidget *xvc_result_dialog = NULL;
117 
118 /** \brief remember the previous led_time set */
119 static int last_led_time = 0;
120 
121 /** \brief remember the previously set picture number to allow for a quick
122  *      check on the next call if we need to do anything */
123 static int last_pic_no = 0;
124 
125 /** \brief store a timer that may be registered when setting a max recording
126  *      time */
127 static guint stop_timer_id = 0;
128 
129 /** calculate recording time */
130 static long start_time = 0, pause_time = 0, time_captured = 0;
131 
132 /** \brief used to faciltate passing the filename selection from a file
133  *      selector dialog back off the results dialog back to the results
134  *      dialog and eventually the main dialog */
135 static char *target_file_name = NULL;
136 
137 /**
138  * \brief the errors found on submitting a set of preferences
139  *
140  * Used to pass this between the various components involved in deciding
141  * what to do when OK is clicked
142  * \todo the whole display of preferences and warning dialogs should be
143  *      simplified through _run_dialog()
144  */
145 static XVC_ErrorListItem *errors_after_cli = NULL;
146 
147 /**
148  * \brief remember the number of attempts to submit a set of preferences
149  *
150  * Hitting OK on a warning may make automatic changes to the preferences.
151  * But it may not resolve all conflicts in one go and pop up the warning
152  * dialog again, again offering automatic resultion actions. This should be
153  * a rare case, however
154  */
155 static int OK_attempts = 0;
156 
157 /** \brief eggtrayicon for minimizing xvidcap to the system tray */
158 static GtkWidget *tray_icon = NULL;
159 
160 /** \brief frame drop monitor for inclusion in the system tray */
161 static GtkWidget *tray_frame_mon = NULL;
162 
163 /*
164  * HELPER FUNCTIONS ...
165  *
166  *
167  */
168 /**
169  * \brief create the LED frame drop meter widget
170  *
171  * This function needs to conform to the custom widget creation functions
172  * as libglade uses them
173  * @param widget_name the name of the widget as set in glade
174  * @param string1 not used
175  * @param string2 not used
176  * @param int1 not used
177  * @param int2 not used
178  * @return the created LED frame drop meter widget
179  */
180 GtkWidget *
glade_create_led_meter(gchar * widget_name,gchar * string1,gchar * string2,gint int1,gint int2)181 glade_create_led_meter (gchar * widget_name, gchar * string1,
182                         gchar * string2, gint int1, gint int2)
183 {
184 #define DEBUGFUNCTION "glade_create_led_meter()"
185     GtkWidget *frame_monitor = led_meter_new ();
186 
187     g_assert (frame_monitor);
188 
189     gtk_widget_set_name (GTK_WIDGET (frame_monitor), widget_name);
190     gtk_widget_show (GTK_WIDGET (frame_monitor));
191 
192     return frame_monitor;
193 #undef DEBUGFUNCTION
194 }
195 
196 /*
197  * this isn't used atm
198  *
199  gint keySnooper (GtkWidget *grab_widget, GdkEventKey *event, gpointer func_data) {
200  printf("keyval: %i - mods: %i\n", event->keyval, event->state);
201  } */
202 
203 /**
204  * \brief change value of frame/filename display
205  *
206  * @param pic_no update the display according to this frame number
207  */
208 void
GtkChangeLabel(int pic_no)209 GtkChangeLabel (int pic_no)
210 {
211 #define DEBUGFUNCTION "GtkChangeLabel()"
212     static char file[PATH_MAX + 1], tmp_buf[PATH_MAX + 1];
213     PangoLayout *layout = NULL;
214     gint width = 0, height = 0;
215     int filename_length = 0;
216     GladeXML *xml = NULL;
217     GtkWidget *w = NULL;
218     Job *jobp = xvc_job_ptr ();
219     XVC_AppData *app = xvc_appdata_ptr ();
220 
221     // generate the string to display in the filename button
222     if (app->current_mode > 0) {
223         sprintf (tmp_buf, jobp->file, jobp->movie_no);
224         sprintf (file, "%s[%04d]", tmp_buf, pic_no);
225     } else {
226         sprintf (file, jobp->file, pic_no);
227     }
228     // cut the string to a sensible maximum length
229     filename_length = strlen (file);
230     if (filename_length > 60) {
231         char tmp_file[PATH_MAX + 1];
232         char *tmp_file2, *ptr;
233         int n;
234 
235         strncpy (tmp_file, file, 20);
236         tmp_file[20] = 0;
237         n = filename_length - 20;
238         ptr = (char *) &file + n;
239         tmp_file2 = strncat (tmp_file, "...", 4);
240         tmp_file2 = strncat (tmp_file, ptr, 45);
241         strncpy (file, tmp_file2, 45);
242     }
243     // get the filename button widget
244     xml = glade_get_widget_tree (xvc_ctrl_main_window);
245     g_assert (xml);
246     w = glade_xml_get_widget (xml, "xvc_ctrl_filename_button");
247     g_assert (w);
248 
249     // set the text
250     gtk_button_set_label (GTK_BUTTON (w), file);
251 
252     // adjust the size of the filname button
253     layout = gtk_widget_create_pango_layout (w, file);
254     g_assert (layout);
255     pango_layout_get_pixel_size (layout, &width, &height);
256     g_object_unref (layout);
257 
258     gtk_widget_set_size_request (GTK_WIDGET (w), (width + 30), -1);
259 #undef DEBUGFUNCTION
260 }
261 
262 /**
263  * \brief implements the actions required when successfully submitting the
264  *      warning dialog, mainly resetting the control UI to the current
265  *      state of the preferences.
266  */
267 void
warning_submit()268 warning_submit ()
269 {
270 #define DEBUGFUNCTION "warning_submit()"
271 #ifdef DEBUG
272     printf ("%s %s: Entering\n", DEBUGFILE, DEBUGFUNCTION);
273 #endif     // DEBUG
274     XVC_AppData *app = xvc_appdata_ptr ();
275 
276     xvc_job_set_from_app_data (app);
277 
278     // set controls active/inactive/sensitive/insensitive according to
279     // current options
280     if (!(app->flags & FLG_NOGUI)) {
281         xvc_reset_ctrl_main_window_according_to_current_prefs ();
282     }
283 
284     if (xvc_errorlist_delete (errors_after_cli)) {
285         fprintf (stderr,
286                  "%s %s: Unrecoverable error while freeing error list, please contact the xvidcap project.\n",
287                  DEBUGFILE, DEBUGFUNCTION);
288         exit (1);
289     }
290     // reset attempts so warnings will be shown again next time ...
291     OK_attempts = 0;
292 
293     // when starting, we are ready for remote commands via dbus after this
294 
295 #ifdef DEBUG
296     printf ("%s %s: Leaving\n", DEBUGFILE, DEBUGFUNCTION);
297 #endif     // DEBUG
298 #undef DEBUGFUNCTION
299 }
300 
301 
302 /**
303  * \brief retrieves the main controls window's extent rectangle. This is needed
304  *      because of bug # 524110 in gdk_window_get_frame_extents.
305  *      The code below is partly taken from the suggested fix for the bug.
306  */
307 void
xvc_get_ctrl_frame_extents(GdkWindow * window,GdkRectangle * rect)308 xvc_get_ctrl_frame_extents (GdkWindow * window, GdkRectangle * rect)
309 {
310 #define DEBUGFUNCTION "get_frame_extents()"
311     XVC_AppData *app = xvc_appdata_ptr ();
312     Window xwindow;
313     Atom type_return;
314     gint format_return;
315     gulong nitems_return;
316     gulong bytes_after_return;
317     guchar *data;
318     gboolean got_frame_extents = FALSE;
319     Window root;
320     guint ww, wh, wb, wd;
321     gint wx, wy;
322 
323     xwindow = GDK_WINDOW_XID (window);
324 
325     Window *wm_child = NULL;
326     Atom nwm_atom, utf8_string, wm_name_atom, rt;
327     unsigned long nbytes, nitems;
328     char *wm_name_str = NULL;
329     int fmt;
330 
331     utf8_string = XInternAtom (app->dpy, "UTF8_STRING", False);
332 
333     nwm_atom = XInternAtom (app->dpy, "_NET_SUPPORTING_WM_CHECK", True);
334     wm_name_atom = XInternAtom (app->dpy, "_NET_WM_NAME", True);
335 
336     if (nwm_atom != None && wm_name_atom != None) {
337         if (XGetWindowProperty (app->dpy, app->root_window,
338                                 nwm_atom, 0, 100,
339                                 False, XA_WINDOW,
340                                 &rt, &fmt, &nitems, &nbytes,
341                                 (unsigned char **) ((void *)
342                                                     &wm_child))
343             != Success) {
344             fprintf (stderr,
345                      "%s %s: Error while trying to get a window to identify the window manager.\n",
346                      DEBUGFILE, DEBUGFUNCTION);
347         }
348         if ((wm_child == NULL) ||
349             (XGetWindowProperty
350              (app->dpy, *wm_child, wm_name_atom, 0, 100, False,
351               utf8_string, &rt, &fmt, &nitems, &nbytes,
352               (unsigned char **) ((void *)
353                                   &wm_name_str))
354              != Success)) {
355             fprintf (stderr,
356                      "%s %s: Warning!!!\nYour window manager appears to be non-compliant!\n",
357                      DEBUGFILE, DEBUGFUNCTION);
358         }
359     }
360     // Right now only wm's that I know of performing 3d compositing
361     // are beryl and compiz. names can be compiz for compiz and
362     // beryl/beryl-co/beryl-core for beryl (so it's strncmp )
363     if (wm_name_str && (!strcmp (wm_name_str, "compiz") ||
364                         !strncmp (wm_name_str, "beryl", 5))) {
365 
366         /* first try: use _NET_FRAME_EXTENTS */
367         if (XGetWindowProperty (app->dpy, xwindow,
368                                 XInternAtom (app->dpy,
369                                              "_NET_FRAME_EXTENTS", TRUE),
370                                 0, G_MAXLONG, False, XA_CARDINAL, &type_return,
371                                 &format_return, &nitems_return,
372                                 &bytes_after_return, &data)
373             == Success) {
374 
375             if ((type_return == XA_CARDINAL) && (format_return == 32) &&
376                 (nitems_return == 4) && (data)) {
377                 guint32 *ldata = (guint32 *) data;
378 
379                 got_frame_extents = TRUE;
380 
381                 /* try to get the real client window geometry */
382                 if (XGetGeometry (app->dpy, xwindow,
383                                   &root, &wx, &wy, &ww, &wh, &wb, &wd)) {
384                     rect->x = wx;
385                     rect->y = wy;
386                     rect->width = ww;
387                     rect->height = wh;
388                 }
389 
390                 /* _NET_FRAME_EXTENTS format is left, right, top, bottom */
391                 rect->x -= ldata[0];
392                 rect->y -= ldata[2];
393                 rect->width += ldata[0] + ldata[1];
394                 rect->height += ldata[2] + ldata[3];
395             }
396 
397             if (data)
398                 XFree (data);
399         }
400 
401         if (wm_name_str)
402             XFree (wm_name_str);
403     }
404 
405     if (got_frame_extents == FALSE) {
406         gdk_window_get_frame_extents (window, rect);
407     }
408 #undef DEBUGFUNCTION
409 }
410 
411 
412 #ifdef USE_FFMPEG
413 /**
414  * \brief this toggles the type of capture (sf vs. mf) currently active
415  *
416  * This is global because it can be necessary to call this from the warning
417  * dialog.
418  */
419 void
xvc_toggle_cap_type()420 xvc_toggle_cap_type ()
421 {
422 #define DEBUGFUNCTION "xvc_toggle_cap_type()"
423     int count_non_info_messages, rc;
424     XVC_AppData *app = xvc_appdata_ptr ();
425 
426     count_non_info_messages = 0;
427     rc = 0;
428     errors_after_cli = xvc_appdata_validate (app, 1, &rc);
429 
430     if (rc == -1) {
431         fprintf (stderr,
432                  "%s %s: Unrecoverable error while validating options, please contact the xvidcap project.\n",
433                  DEBUGFILE, DEBUGFUNCTION);
434         exit (1);
435     }
436     // warning dialog
437     if (errors_after_cli != NULL) {
438         XVC_ErrorListItem *err;
439 
440         err = errors_after_cli;
441         for (; err != NULL; err = err->next) {
442             if (err->err->type != XVC_ERR_INFO)
443                 count_non_info_messages++;
444         }
445         if (count_non_info_messages > 0
446             || (app->flags & FLG_RUN_VERBOSE && OK_attempts == 0)) {
447             xvc_warn_main_window =
448                 xvc_create_warning_with_errors (errors_after_cli, 1);
449             OK_attempts++;
450         } else {
451             warning_submit ();
452         }
453     } else {
454         warning_submit ();
455     }
456 #undef DEBUGFUNCTION
457 }
458 
459 /**
460  * \brief this undoes an attempt to toggle the type of capture (sf vs. mf)
461  *      currently active
462  *
463  * This is global because it can be necessary to call this from the warning
464  * dialog.
465  */
466 void
xvc_undo_toggle_cap_type()467 xvc_undo_toggle_cap_type ()
468 {
469 #define DEBUGFUNCTION "xvc_undo_toggle_cap_type()"
470     GladeXML *xml = NULL;
471     GtkWidget *mitem = NULL;
472     XVC_AppData *app = xvc_appdata_ptr ();
473 
474     xml = glade_get_widget_tree (xvc_ctrl_m1);
475     g_assert (xml);
476 
477     OK_attempts = 0;
478 
479 #ifdef DEBUG
480     printf ("%s %s: pre current_mode %i\n", DEBUGFILE, DEBUGFUNCTION,
481             app->current_mode);
482 #endif     // DEBUG
483 
484     app->current_mode = (app->current_mode == 0) ? 1 : 0;
485 
486 #ifdef DEBUG
487     printf ("%s %s: post current_mode %i\n", DEBUGFILE, DEBUGFUNCTION,
488             app->current_mode);
489 #endif     // DEBUG
490 
491     if (app->current_mode == 0) {
492         mitem = glade_xml_get_widget (xml, "xvc_ctrl_m1_mitem_sf_capture");
493         g_assert (mitem);
494         gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (mitem), TRUE);
495     } else {
496         mitem = glade_xml_get_widget (xml, "xvc_ctrl_m1_mitem_mf_capture");
497         g_assert (mitem);
498         gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (mitem), TRUE);
499     }
500 
501 #undef DEBUGFUNCTION
502 }
503 #endif     // USE_FFMPEG
504 
505 #ifdef USE_XDAMAGE
506 /**
507  * \brief event filter to register with gdk to retrieve X11 events
508  *
509  * @param xevent pointer to the wrapped X11 event
510  * @param event pointer to the GdkEvent
511  * @param user_data pointer to other data
512  * @return flag to gdk whether to pass the event on or drop it
513  */
514 GdkFilterReturn
xvc_xdamage_event_filter(GdkXEvent * xevent,GdkEvent * event,void * user_data)515 xvc_xdamage_event_filter (GdkXEvent * xevent, GdkEvent * event, void *user_data)
516 {
517     XVC_AppData *app = xvc_appdata_ptr ();
518     XEvent *xev = (XEvent *) xevent;
519     XDamageNotifyEvent *e = (XDamageNotifyEvent *) (xevent);
520     static XserverRegion region = None;
521     Job *job = xvc_job_ptr ();
522 
523     // the following bits are purely for perormance reasons
524     if (!app->recording_thread_running || job == NULL ||
525         (job->flags & FLG_USE_XDAMAGE) == 0) {
526         if (region != None) {
527             XFixesDestroyRegion (app->dpy, region);
528             region = None;
529         }
530         return GDK_FILTER_CONTINUE;
531     }
532 
533     if (region == None)
534         region = XFixesCreateRegion (app->dpy, 0, 0);
535 
536     if (xev->type == MapNotify) {
537         XWindowAttributes attribs;
538         XWindowAttributes root_attrs;
539         Status ret;
540 
541         gdk_error_trap_push ();
542         XGetWindowAttributes (app->dpy, app->root_window, &root_attrs);
543         ret = XGetWindowAttributes (app->dpy,
544                                     xev->xcreatewindow.window, &attribs);
545         gdk_error_trap_pop ();
546 
547 //        XSelectInput (app->dpy, xev->xcreatewindow.window, StructureNotifyMask);
548         if (!attribs.override_redirect && attribs.depth == root_attrs.depth) {
549 //            gdk_error_trap_push ();
550             XDamageCreate (app->dpy, xev->xcreatewindow.window,
551                            XDamageReportRawRectangles);
552 //            gdk_error_trap_pop ();
553         }
554     } else if (xev->type == app->dmg_event_base) {
555         XRectangle rect = {
556             e->area.x, e->area.y, e->area.width, e->area.height
557 /*
558             XVC_MAX (e->area.x - 10, 0),
559             XVC_MAX (e->area.y - 10, 0),
560             e->area.width + 20,
561             e->area.height + 20
562 */
563         };
564 
565         // set the region required by XDamageSubtract unclipped
566         XFixesSetRegion (app->dpy, region, &(e->area), 1);
567 
568         // Offset the region with the windows' position
569         XFixesTranslateRegion (app->dpy, region, e->geometry.x, e->geometry.y);
570 
571         // subtract the damage
572         // this will not work on a locked display, so we need to synchronize
573         // this with the capture thread
574         pthread_mutex_lock (&(app->capturing_mutex));
575         XDamageSubtract (app->dpy, e->damage, region, None);
576         XSync (app->dpy, False);
577 
578         // clip the damaged rectangle
579         // need to do that in the lock or otherwise an event triggered before
580         // the caputre of a moved frame may queue up and add to regions after
581         // the full capture. And then the area of the event may be outside the
582         // new capture area and anyway, we don't need this anymore because
583         // we've captured this change in the full frame captured
584         if (rect.x < app->area->x && (rect.x + rect.width) > app->area->x) {
585             rect.width -= (app->area->x - rect.x);
586             rect.x = app->area->x;
587         }
588         if ((rect.x + rect.width) > (app->area->x + app->area->width)) {
589             rect.width = (app->area->x + app->area->width) - rect.x;
590         }
591         if (rect.y < app->area->y && (rect.y + rect.height) > app->area->y) {
592             rect.height -= (app->area->y - rect.y);
593             rect.y = app->area->y;
594         }
595         if ((rect.y + rect.height) > (app->area->y + app->area->height)) {
596             rect.height = (app->area->y + app->area->height) - rect.y;
597         }
598 
599         if ((rect.x + rect.width) < app->area->x ||
600             rect.x > (app->area->x + app->area->width) ||
601             (rect.y + rect.height) < app->area->y ||
602             rect.y > (app->area->y + app->area->height)) {
603             rect.x = rect.width = rect.y = rect.height = 0;
604         } else {
605             // remember the damage done
606             pthread_mutex_lock (&(app->damage_regions_mutex));
607             XUnionRectWithRegion (&rect, job->dmg_region, job->dmg_region);
608             pthread_mutex_unlock (&(app->damage_regions_mutex));
609         }
610         pthread_mutex_unlock (&(app->capturing_mutex));
611     }
612 
613     return GDK_FILTER_CONTINUE;
614 }
615 #endif     // USE_XDAMAGE
616 
617 /**
618  * \brief this is what the thread spawned on record actually does. It is
619  *      normally stopped by setting the state machine to VC_STOP
620  */
621 void
do_record_thread()622 do_record_thread ()
623 {
624 #define DEBUGFUNCTION "do_record_thread()"
625     XVC_AppData *app = xvc_appdata_ptr ();
626     Job *job = xvc_job_ptr ();
627     long pause = 1000;
628 
629 #ifdef DEBUG
630     printf ("%s %s: Entering with state = %i\n", DEBUGFILE,
631             DEBUGFUNCTION, job->state);
632 #endif     // DEBUG
633 
634     app->recording_thread_running = TRUE;
635 
636     // if the frame was moved before this, ignore
637     job->frame_moved_x = 0;
638     job->frame_moved_y = 0;
639 
640     if (!(job->flags & FLG_NOGUI)) {
641         xvc_idle_add (xvc_change_filename_display, (void *) NULL);
642         xvc_idle_add (xvc_frame_monitor, (void *) NULL);
643     }
644 
645     while ((job->state & VC_READY) == 0) {
646 #ifdef DEBUG
647         printf ("%s %s: going for next frame with state %i\n", DEBUGFILE,
648                 DEBUGFUNCTION, job->state);
649 #endif
650         if ((job->state & VC_PAUSE) && !(job->state & VC_STEP)) {
651             // make the led monitor stop for pausing
652             xvc_led_time = 0;
653 
654             pthread_mutex_lock (&(app->recording_paused_mutex));
655             pthread_cond_wait (&(app->recording_condition_unpaused),
656                                &(app->recording_paused_mutex));
657             pthread_mutex_unlock (&(app->recording_paused_mutex));
658 #ifdef DEBUG
659             printf ("%s %s: unpaused\n", DEBUGFILE, DEBUGFUNCTION);
660 #endif     // DEBUG
661         }
662 
663         pause = job->capture ();
664 
665         if (pause > 0)
666             usleep (pause * 1000);
667 #ifdef DEBUG
668         printf ("%s %s: woke up\n", DEBUGFILE, DEBUGFUNCTION);
669 #endif     // DEBUG
670     }
671 
672 #ifdef DEBUG
673     printf ("%s %s: Leaving\n", DEBUGFILE, DEBUGFUNCTION);
674 #endif     // DEBUG
675 
676     app->recording_thread_running = FALSE;
677     pthread_exit (NULL);
678 #undef DEBUGFUNCTION
679 }
680 
681 /**
682  * \brief implements those actions required for stopping a capture session
683  *      that are not GUI related
684  */
685 static void
stop_recording_nongui_stuff()686 stop_recording_nongui_stuff ()
687 {
688 #define DEBUGFUNCTION "stop_recording_nongui_stuff()"
689     int state = 0;
690     struct timeval curr_time;
691     long stop_time = 0;
692     XVC_AppData *app = xvc_appdata_ptr ();
693     Job *jobp = xvc_job_ptr ();
694 
695 #ifdef DEBUG
696     printf ("%s %s: Entering with thread running %i\n",
697             DEBUGFILE, DEBUGFUNCTION, app->recording_thread_running);
698 #endif     // DEBUG
699 
700     if (pause_time) {
701         stop_time = pause_time;
702     } else {
703         gettimeofday (&curr_time, NULL);
704         stop_time = curr_time.tv_sec * 1000 + curr_time.tv_usec / 1000;
705         time_captured += (stop_time - start_time);
706     }
707 
708     if (stop_timer_id)
709         g_source_remove (stop_timer_id);
710 
711     state = VC_STOP;
712     if (app->flags & FLG_AUTO_CONTINUE && jobp->capture_returned_errno == 0) {
713         state |= VC_CONTINUE;
714     }
715     xvc_job_set_state (state);
716 
717     if (app->recording_thread_running) {
718         pthread_join (recording_thread, NULL);
719 #ifdef DEBUG
720         printf ("%s %s: joined thread\n", DEBUGFILE, DEBUGFUNCTION);
721 #endif     // DEBUG
722     }
723 #ifdef USE_XDAMAGE
724     if (app->flags & FLG_USE_XDAMAGE)
725         gdk_window_remove_filter (NULL,
726                                   (GdkFilterFunc) xvc_xdamage_event_filter,
727                                   NULL);
728 #endif     // USE_XDAMAGE
729 
730     if ((jobp->flags & FLG_NOGUI) != 0 && jobp->capture_returned_errno != 0) {
731         char file[PATH_MAX + 1];
732 
733         if (app->current_mode > 0) {
734             sprintf (file, jobp->file, jobp->movie_no);
735         } else {
736             sprintf (file, jobp->file, jobp->pic_no);
737         }
738 
739         fprintf (stderr, "Error Capturing\nFile used: %s\nError: %s\n",
740                  file, strerror (jobp->capture_returned_errno));
741 
742         jobp->capture_returned_errno = 0;
743     }
744 #ifdef DEBUG
745     printf ("%s %s: Leaving\n", DEBUGFILE, DEBUGFUNCTION);
746 #endif     // DEBUG
747 
748 #undef DEBUGFUNCTION
749 }
750 
751 /**
752  * \brief displays the xvidcap user manual.
753  */
754 void
do_results_help()755 do_results_help ()
756 {
757     system ((char *) "yelp ghelp:xvidcap?xvidcap-results &");
758 }
759 
760 /**
761  * \brief implements the GUI related actions involved when stopping a
762  *      capture session.
763  */
764 static void
stop_recording_gui_stuff()765 stop_recording_gui_stuff ()
766 {
767 #define DEBUGFUNCTION "stop_recording_gui_stuff()"
768     GladeXML *xml = NULL;
769     GtkWidget *w = NULL;
770     XVC_CapTypeOptions *target = NULL;
771     Job *jobp = xvc_job_ptr ();
772     XVC_AppData *app = xvc_appdata_ptr ();
773 
774 #ifdef DEBUG
775     printf ("%s %s: Entering\n", DEBUGFILE, DEBUGFUNCTION);
776 #endif     // DEBUG
777 
778 #ifdef USE_FFMPEG
779     if (app->current_mode > 0)
780         target = &(app->multi_frame);
781     else
782 #endif     // USE_FFMPEG
783         target = &(app->single_frame);
784 
785     xml = glade_get_widget_tree (GTK_WIDGET (xvc_ctrl_main_window));
786     g_assert (xml);
787 
788     // GUI stuff
789     w = glade_xml_get_widget (xml, "xvc_ctrl_pause_toggle");
790     g_assert (w);
791     gtk_toggle_tool_button_set_active (GTK_TOGGLE_TOOL_BUTTON (w), FALSE);
792 
793     w = glade_xml_get_widget (xml, "xvc_ctrl_select_toggle");
794     g_assert (w);
795     gtk_widget_set_sensitive (GTK_WIDGET (w), TRUE);
796 
797     w = glade_xml_get_widget (xml, "xvc_ctrl_record_toggle");
798     g_assert (w);
799     gtk_widget_set_sensitive (GTK_WIDGET (w), TRUE);
800     if (gtk_toggle_tool_button_get_active (GTK_TOGGLE_TOOL_BUTTON (w)))
801         gtk_toggle_tool_button_set_active (GTK_TOGGLE_TOOL_BUTTON (w), FALSE);
802 
803     w = glade_xml_get_widget (xml, "xvc_ctrl_filename_button");
804     g_assert (w);
805     gtk_widget_set_sensitive (GTK_WIDGET (w), TRUE);
806 
807     w = glade_xml_get_widget (xml, "xvc_ctrl_edit_button");
808     g_assert (w);
809     gtk_widget_set_sensitive (GTK_WIDGET (w), TRUE);
810 
811     w = glade_xml_get_widget (xml, "xvc_ctrl_step_button");
812     g_assert (w);
813     gtk_widget_set_sensitive (GTK_WIDGET (w), FALSE);
814 
815     if (app->current_mode > 0) {
816 
817         if (xvc_is_filename_mutable (jobp->file) == TRUE) {
818             if (jobp->movie_no > 0) {
819                 w = glade_xml_get_widget (xml, "xvc_ctrl_back_button");
820                 g_assert (w);
821                 gtk_widget_set_sensitive (GTK_WIDGET (w), TRUE);
822             }
823 
824             w = glade_xml_get_widget (xml, "xvc_ctrl_forward_button");
825             g_assert (w);
826             gtk_widget_set_sensitive (GTK_WIDGET (w), TRUE);
827         }
828     } else {
829         if (xvc_is_filename_mutable (jobp->file) == TRUE) {
830             if (jobp->pic_no >= target->step) {
831                 w = glade_xml_get_widget (xml, "xvc_ctrl_back_button");
832                 g_assert (w);
833                 gtk_widget_set_sensitive (GTK_WIDGET (w), TRUE);
834             }
835 
836             if (target->frames == 0
837                 || jobp->pic_no < (target->frames - target->step)) {
838                 w = glade_xml_get_widget (xml, "xvc_ctrl_forward_button");
839                 g_assert (w);
840                 gtk_widget_set_sensitive (GTK_WIDGET (w), TRUE);
841             }
842         }
843     }
844 
845     w = glade_xml_get_widget (xml, "xvc_ctrl_pause_toggle");
846     g_assert (w);
847     if (gtk_toggle_tool_button_get_active (GTK_TOGGLE_TOOL_BUTTON (w)))
848         gtk_toggle_tool_button_set_active (GTK_TOGGLE_TOOL_BUTTON (w), FALSE);
849     w = glade_xml_get_widget (xml, "xvc_ctrl_stop_toggle");
850     g_assert (w);
851     if (!gtk_toggle_tool_button_get_active (GTK_TOGGLE_TOOL_BUTTON (w)))
852         gtk_toggle_tool_button_set_active (GTK_TOGGLE_TOOL_BUTTON (w), TRUE);
853 
854     GtkChangeLabel (jobp->pic_no);
855 
856     if (app->flags & FLG_TO_TRAY) {
857         gtk_widget_destroy (GTK_WIDGET (tray_frame_mon));
858         tray_frame_mon = NULL;
859         gtk_widget_destroy (GTK_WIDGET (tray_icon));
860         tray_icon = NULL;
861         gtk_widget_destroy (GTK_WIDGET (xvc_tray_icon_menu));
862         xvc_tray_icon_menu = NULL;
863         gtk_window_set_skip_taskbar_hint (GTK_WINDOW (xvc_ctrl_main_window),
864                                           FALSE);
865         gtk_window_deiconify (GTK_WINDOW (xvc_ctrl_main_window));
866     }
867 
868     if (jobp->capture_returned_errno != 0) {
869         GtkWidget *dialog = NULL;
870         char file[PATH_MAX + 1];
871 
872         if (app->current_mode > 0) {
873             sprintf (file, jobp->file, jobp->movie_no);
874         } else {
875             sprintf (file, jobp->file, jobp->pic_no);
876         }
877 
878         dialog = gtk_message_dialog_new (GTK_WINDOW (xvc_warn_main_window),
879                                          GTK_DIALOG_DESTROY_WITH_PARENT,
880                                          GTK_MESSAGE_ERROR,
881                                          GTK_BUTTONS_CLOSE,
882                                          _("Error Capturing"));
883         gtk_message_dialog_format_secondary_text (GTK_MESSAGE_DIALOG (dialog),
884                                                   _("File used: %s\nError: %s"),
885                                                   file,
886                                                   g_strerror (jobp->
887                                                               capture_returned_errno));
888         gtk_dialog_run (GTK_DIALOG (dialog));
889         gtk_widget_destroy (dialog);
890 
891         jobp->capture_returned_errno = 0;
892     } else if ((strlen (target->file) < 1 ||
893                 (app->flags & FLG_ALWAYS_SHOW_RESULTS) > 0) &&
894                (app->flags & FLG_AUTO_CONTINUE) == 0) {
895         int result = 0;
896         float fps_ratio = 0;
897         char buf[100];
898 
899         GladeXML *xml = NULL;
900         GtkWidget *w = NULL;
901 
902         // load the interface
903         xml = glade_xml_new (XVC_GLADE_FILE, "xvc_result_dialog", NULL);
904         g_assert (xml);
905         // connect the signals in the interface
906         glade_xml_signal_autoconnect (xml);
907         // get the widget
908         xvc_result_dialog = glade_xml_get_widget (xml, "xvc_result_dialog");
909         g_assert (xvc_result_dialog);
910 
911         // width
912         w = glade_xml_get_widget (xml, "xvc_result_dialog_width_label");
913         g_assert (w);
914         snprintf (buf, 99, "%i", app->area->width);
915         gtk_label_set_text (GTK_LABEL (w), buf);
916 
917         // height
918         w = glade_xml_get_widget (xml, "xvc_result_dialog_height_label");
919         g_assert (w);
920         snprintf (buf, 99, "%i", app->area->height);
921         gtk_label_set_text (GTK_LABEL (w), buf);
922 
923         // video format
924         w = glade_xml_get_widget (xml, "xvc_result_dialog_video_format_label");
925         g_assert (w);
926         gtk_label_set_text (GTK_LABEL (w),
927                             _(xvc_formats[jobp->target].longname));
928 
929         // video codec
930         w = glade_xml_get_widget (xml, "xvc_result_dialog_video_codec_label");
931         g_assert (w);
932         gtk_label_set_text (GTK_LABEL (w), xvc_codecs[jobp->targetCodec].name);
933 
934         // audio codec
935         w = glade_xml_get_widget (xml, "xvc_result_dialog_audio_codec_label");
936         g_assert (w);
937         gtk_label_set_text (GTK_LABEL (w),
938                             ((jobp->flags & FLG_REC_SOUND) ?
939                              xvc_audio_codecs[jobp->au_targetCodec].name :
940                              _("NONE")));
941 
942         // set fps
943         w = glade_xml_get_widget (xml, "xvc_result_dialog_fps_label");
944         g_assert (w);
945         snprintf (buf, 99, "%.2f",
946                   ((float) target->fps.num / (float) target->fps.den));
947         gtk_label_set_text (GTK_LABEL (w), buf);
948 
949         // achieved fps
950         w = glade_xml_get_widget (xml, "xvc_result_dialog_actual_fps_label");
951         g_assert (w);
952         {
953             char *str_template = NULL;
954             float total_frames =
955                 ((float) jobp->pic_no / (float) target->step) + 1;
956             float actual_fps =
957                 ((float) total_frames) / ((float) time_captured / 1000);
958 
959             if (actual_fps >
960                 ((float) target->fps.num / (float) target->fps.den))
961                 actual_fps = (float) target->fps.num / (float) target->fps.den;
962             fps_ratio =
963                 actual_fps / ((float) target->fps.num /
964                               (float) target->fps.den);
965 
966             if (fps_ratio > (((float) LM_NUM_DAS - LM_LOW_THRESHOLD) / 10)) {
967                 str_template = "%.2f";
968             } else if (fps_ratio >
969                        (((float) LM_NUM_DAS - LM_MEDIUM_THRESHOLD) / 10)) {
970                 str_template = "<span background=\"#EEEE00\">%.2f</span>";
971             } else {
972                 str_template = "<span background=\"#EE0000\">%.2f</span>";
973             }
974 
975             snprintf (buf, 99, str_template, actual_fps);
976             gtk_label_set_markup (GTK_LABEL (w), buf);
977         }
978 
979         // fps ratio
980         w = glade_xml_get_widget (xml,
981                                   "xvc_result_dialog_fps_ratio_progressbar");
982         g_assert (w);
983         gtk_progress_bar_set_fraction (GTK_PROGRESS_BAR (w), fps_ratio);
984         snprintf (buf, 99, "%.2f %%", fps_ratio * 100);
985         gtk_progress_bar_set_text (GTK_PROGRESS_BAR (w), buf);
986 
987         // captured frames
988         w = glade_xml_get_widget (xml, "xvc_result_dialog_total_frames_label");
989         g_assert (w);
990         snprintf (buf, 99, "%i", (jobp->pic_no / target->step) + 1);
991         gtk_label_set_text (GTK_LABEL (w), buf);
992 
993         // captured time
994         w = glade_xml_get_widget (xml, "xvc_result_dialog_video_length_label");
995         g_assert (w);
996         snprintf (buf, 99, "%.2f seconds", ((float) time_captured / 1000));
997         gtk_label_set_text (GTK_LABEL (w), buf);
998 
999         if (strlen (target->file) > 0) {
1000             w = glade_xml_get_widget (xml,
1001                                       "xvc_result_dialog_select_filename_button");
1002             g_assert (w);
1003             gtk_widget_hide (GTK_WIDGET (w));
1004 
1005             w = glade_xml_get_widget (xml, "xvc_result_dialog_filename_label");
1006             g_assert (w);
1007             gtk_widget_hide (GTK_WIDGET (w));
1008 
1009             w = glade_xml_get_widget (xml, "xvc_result_dialog_no_button");
1010             g_assert (w);
1011             gtk_widget_hide (GTK_WIDGET (w));
1012 
1013             w = glade_xml_get_widget (xml, "xvc_result_dialog_save_button");
1014             g_assert (w);
1015             gtk_widget_hide (GTK_WIDGET (w));
1016 
1017             w = glade_xml_get_widget (xml,
1018                                       "xvc_result_dialog_show_next_time_checkbutton");
1019             g_assert (w);
1020             gtk_widget_show (GTK_WIDGET (w));
1021 
1022             w = glade_xml_get_widget (xml, "xvc_result_dialog_close_button");
1023             g_assert (w);
1024             gtk_widget_show (GTK_WIDGET (w));
1025         }
1026 
1027         do {
1028             result = gtk_dialog_run (GTK_DIALOG (xvc_result_dialog));
1029 
1030             switch (result) {
1031             case GTK_RESPONSE_OK:
1032                 if (target_file_name != NULL) {
1033                     char cmd_buf[PATH_MAX * 2 + 5];
1034                     int errnum = 0;
1035 
1036                     snprintf (cmd_buf, (PATH_MAX * 2 + 5), "mv %s %s",
1037                               target->file, target_file_name);
1038                     errnum = system ((char *) cmd_buf);
1039                     if (errnum != 0) {
1040                         GtkWidget *err_dialog =
1041                             gtk_message_dialog_new (GTK_WINDOW
1042                                                     (xvc_ctrl_main_window),
1043                                                     GTK_DIALOG_DESTROY_WITH_PARENT,
1044                                                     GTK_MESSAGE_ERROR,
1045                                                     GTK_BUTTONS_CLOSE,
1046                                                     "Error moving file from '%s' to '%s'\n%s",
1047                                                     target->file,
1048                                                     target_file_name,
1049                                                     g_strerror (errnum));
1050 
1051                         gtk_dialog_run (GTK_DIALOG (err_dialog));
1052                         gtk_widget_destroy (err_dialog);
1053                     }
1054                 }
1055             case GTK_RESPONSE_CANCEL:
1056             case GTK_RESPONSE_CLOSE:
1057                 w = glade_xml_get_widget (xml,
1058                                           "xvc_result_dialog_show_next_time_checkbutton");
1059                 g_assert (w);
1060                 if (!gtk_toggle_button_get_active (GTK_TOGGLE_BUTTON (w)))
1061                     app->flags &= ~FLG_ALWAYS_SHOW_RESULTS;
1062                 break;
1063             case GTK_RESPONSE_HELP:
1064                 do_results_help ();
1065                 break;
1066             case GTK_RESPONSE_ACCEPT:
1067                 if (!app->current_mode) {
1068                     xvc_command_execute (target->play_cmd, 1, 0,
1069                                          target->file, target->start_no,
1070                                          jobp->pic_no, app->area->width,
1071                                          app->area->height, target->fps);
1072                 } else {
1073                     xvc_command_execute (target->play_cmd, 2,
1074                                          jobp->movie_no, target->file,
1075                                          target->start_no, jobp->pic_no,
1076                                          app->area->width,
1077                                          app->area->height, target->fps);
1078                 }
1079                 break;
1080             default:
1081                 break;
1082             }
1083         } while (result == GTK_RESPONSE_HELP || result == GTK_RESPONSE_ACCEPT);
1084 
1085         gtk_widget_destroy (xvc_result_dialog);
1086         xvc_result_dialog = NULL;
1087 
1088         // FIXME: realize move in a way that gives me real error codes
1089     }
1090 #ifdef DEBUG
1091     printf ("%s %s: Leaving\n", DEBUGFILE, DEBUGFUNCTION);
1092 #endif     // DEBUG
1093 
1094 #undef DEBUGFUNCTION
1095 }
1096 
1097 /**
1098  * \brief this function is hooked up as the action to execute when a
1099  *      capture session with max_time set times out.
1100  *
1101  * \todo might be able to trigger xvc_capture_stop_signal directly
1102  */
1103 gboolean
timer_stop_recording()1104 timer_stop_recording ()
1105 {
1106 #define DEBUGFUNCTION "timer_stop_recording()"
1107     Job *jobp = xvc_job_ptr ();
1108 
1109     if ((jobp->flags & FLG_AUTO_CONTINUE) != 0) {
1110         xvc_job_merge_and_remove_state ((VC_STOP | VC_CONTINUE),
1111                                         (VC_START | VC_REC));
1112         return TRUE;
1113     } else {
1114         xvc_job_set_state (VC_STOP);
1115         return FALSE;
1116     }
1117 #undef DEBUGFUNCTION
1118 }
1119 
1120 static gboolean
on_tray_icon_button_press_event(GtkWidget * widget,GdkEvent * event,gpointer user_data)1121 on_tray_icon_button_press_event (GtkWidget * widget,
1122                                  GdkEvent * event, gpointer user_data)
1123 {
1124     GdkEventButton *bevent = NULL;
1125 
1126     g_assert (widget);
1127     g_assert (event);
1128     bevent = (GdkEventButton *) event;
1129 
1130     if (bevent->button == (guint) 3) {
1131         g_assert (xvc_tray_icon_menu);
1132 
1133         gtk_menu_popup (GTK_MENU (xvc_tray_icon_menu), NULL, NULL,
1134                         NULL, widget, bevent->button, bevent->time);
1135         // Tell calling code that we have handled this event; the buck
1136         // stops here.
1137         return TRUE;
1138     }
1139     return FALSE;
1140 }
1141 
1142 /**
1143  * \brief implements the GUI related actions involved in starting a
1144  *      capture session
1145  */
1146 static void
start_recording_gui_stuff()1147 start_recording_gui_stuff ()
1148 {
1149 #define DEBUGFUNCTION "start_recording_gui_stuff()"
1150     GladeXML *xml = NULL;
1151     GtkWidget *w = NULL;
1152     Job *jobp = xvc_job_ptr ();
1153     XVC_CapTypeOptions *target = NULL;
1154     XVC_AppData *app = xvc_appdata_ptr ();
1155 
1156 #ifdef USE_FFMPEG
1157     if (app->current_mode > 0)
1158         target = &(app->multi_frame);
1159     else
1160 #endif     // USE_FFMPEG
1161         target = &(app->single_frame);
1162 
1163     if (app->flags & FLG_TO_TRAY) {
1164         GtkWidget *ebox = NULL, *hbox = NULL, *pause_cb;
1165 
1166 //        GtkWidget *image = NULL;
1167 //        GdkBitmap *bm = NULL;
1168 
1169         gtk_window_set_skip_taskbar_hint (GTK_WINDOW (xvc_ctrl_main_window),
1170                                           TRUE);
1171 
1172         tray_icon = GTK_WIDGET (egg_tray_icon_new ("xvc_tray_icon"));
1173         g_assert (tray_icon);
1174         ebox = gtk_event_box_new ();
1175         g_assert (ebox);
1176         hbox = gtk_hbox_new (FALSE, 2);
1177         g_assert (hbox);
1178         tray_frame_mon = NULL;
1179         tray_frame_mon = glade_create_led_meter ("tray_frame_mon", NULL,
1180                                                  NULL, 0, 0);
1181         g_assert (tray_frame_mon);
1182         gtk_container_add (GTK_CONTAINER (hbox), tray_frame_mon);
1183 
1184 //        image = gtk_image_new_from_stock (GTK_STOCK_MEDIA_RECORD,
1185 //                                          GTK_ICON_SIZE_SMALL_TOOLBAR);
1186 //        g_assert (image);
1187 //        gtk_container_add (GTK_CONTAINER (hbox), image);
1188         gtk_container_add (GTK_CONTAINER (ebox), hbox);
1189         gtk_container_add (GTK_CONTAINER (tray_icon), ebox);
1190         gtk_widget_show_all (tray_icon);
1191 
1192         g_signal_connect (G_OBJECT (ebox), "button_press_event",
1193                           G_CALLBACK (on_tray_icon_button_press_event), NULL);
1194 
1195         // load the interface
1196         xml = glade_xml_new (XVC_GLADE_FILE, "xvc_ti_menu", NULL);
1197         g_assert (xml);
1198 
1199         // connect the signals in the interface
1200         glade_xml_signal_autoconnect (xml);
1201         // store the toplevel widget for further reference
1202         xvc_tray_icon_menu = glade_xml_get_widget (xml, "xvc_ti_menu");
1203         if (jobp->state & VC_PAUSE) {
1204             pause_cb = glade_xml_get_widget (xml, "xvc_ti_pause");
1205             gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (pause_cb),
1206                                             TRUE);
1207         }
1208 
1209         gtk_window_iconify (GTK_WINDOW (xvc_ctrl_main_window));
1210     }
1211     xml = glade_get_widget_tree (GTK_WIDGET (xvc_ctrl_main_window));
1212     g_assert (xml);
1213 
1214     // GUI stuff
1215     w = glade_xml_get_widget (xml, "xvc_ctrl_record_toggle");
1216     g_assert (w);
1217     gtk_widget_set_sensitive (GTK_WIDGET (w), FALSE);
1218 
1219     w = glade_xml_get_widget (xml, "xvc_ctrl_stop_toggle");
1220     g_assert (w);
1221     if ((jobp->state & VC_PAUSE) == 0) {
1222         gtk_widget_set_sensitive (GTK_WIDGET (w), TRUE);
1223     }
1224     gtk_toggle_tool_button_set_active (GTK_TOGGLE_TOOL_BUTTON (w), FALSE);
1225 
1226     w = glade_xml_get_widget (xml, "xvc_ctrl_select_toggle");
1227     g_assert (w);
1228     gtk_widget_set_sensitive (GTK_WIDGET (w), FALSE);
1229 
1230     w = glade_xml_get_widget (xml, "xvc_ctrl_pause_toggle");
1231     g_assert (w);
1232     gtk_widget_set_sensitive (GTK_WIDGET (w), TRUE);
1233 
1234     w = glade_xml_get_widget (xml, "xvc_ctrl_filename_button");
1235     g_assert (w);
1236     gtk_widget_set_sensitive (GTK_WIDGET (w), FALSE);
1237 
1238     w = glade_xml_get_widget (xml, "xvc_ctrl_edit_button");
1239     g_assert (w);
1240     gtk_widget_set_sensitive (GTK_WIDGET (w), FALSE);
1241 
1242     if (jobp->state & VC_PAUSE) {
1243         w = glade_xml_get_widget (xml, "xvc_ctrl_step_button");
1244         g_assert (w);
1245         if (app->current_mode == 0) {
1246             gtk_widget_set_sensitive (GTK_WIDGET (w), TRUE);
1247 
1248             if (target->frames == 0
1249                 || jobp->pic_no <= (target->frames - target->step)) {
1250                 w = glade_xml_get_widget (xml, "xvc_ctrl_forward_button");
1251                 g_assert (w);
1252                 gtk_widget_set_sensitive (GTK_WIDGET (w), TRUE);
1253             }
1254             if (jobp->pic_no >= target->step) {
1255                 w = glade_xml_get_widget (xml, "xvc_ctrl_back_button");
1256                 g_assert (w);
1257                 gtk_widget_set_sensitive (GTK_WIDGET (w), TRUE);
1258             }
1259         } else {
1260             gtk_widget_set_sensitive (GTK_WIDGET (w), FALSE);
1261             if (!xvc_is_filename_mutable (target->file)) {
1262                 w = glade_xml_get_widget (xml, "xvc_ctrl_forward_button");
1263                 g_assert (w);
1264                 gtk_widget_set_sensitive (GTK_WIDGET (w), FALSE);
1265 
1266                 w = glade_xml_get_widget (xml, "xvc_ctrl_back_button");
1267                 g_assert (w);
1268                 gtk_widget_set_sensitive (GTK_WIDGET (w), FALSE);
1269             }
1270         }
1271     } else {
1272         w = glade_xml_get_widget (xml, "xvc_ctrl_forward_button");
1273         g_assert (w);
1274         gtk_widget_set_sensitive (GTK_WIDGET (w), FALSE);
1275 
1276         w = glade_xml_get_widget (xml, "xvc_ctrl_back_button");
1277         g_assert (w);
1278         gtk_widget_set_sensitive (GTK_WIDGET (w), FALSE);
1279     }
1280 #undef DEBUGFUNCTION
1281 }
1282 
1283 /**
1284  * \brief implements those actions involved in starting a capture session
1285  *      that are not GUI related
1286  */
1287 static void
start_recording_nongui_stuff()1288 start_recording_nongui_stuff ()
1289 {
1290 #define DEBUGFUNCTION "start_recording_nongui_stuff()"
1291     XVC_AppData *app = xvc_appdata_ptr ();
1292 
1293     if (!app->recording_thread_running) {
1294         struct timeval curr_time;
1295         Job *job = xvc_job_ptr ();
1296         XVC_CapTypeOptions *target = NULL;
1297 
1298 #ifdef USE_FFMPEG
1299         if (app->current_mode > 0)
1300             target = &(app->multi_frame);
1301         else
1302 #endif     // USE_FFMPEG
1303             target = &(app->single_frame);
1304 
1305         // the following also unsets VC_READY
1306         xvc_job_keep_and_merge_state (VC_PAUSE, (VC_REC | VC_START));
1307 
1308         if (app->current_mode > 0) {
1309             job->pic_no = target->start_no;
1310         }
1311 
1312         if ((job->state & VC_PAUSE) == 0) {
1313             // if (job->max_time != 0 && (job->state & VC_PAUSE) == 0) {
1314             gettimeofday (&curr_time, NULL);
1315             time_captured = 0;
1316             start_time = curr_time.tv_sec * 1000 + curr_time.tv_usec / 1000;
1317             if (target->time != 0) {
1318                 // install a timer which stops recording
1319                 // we need milli secs ..
1320                 stop_timer_id =
1321                     g_timeout_add ((guint32) (target->time * 1000),
1322                                    (GtkFunction) timer_stop_recording, job);
1323             }
1324         }
1325 #ifdef USE_XDAMAGE
1326         if (job->flags & FLG_USE_XDAMAGE) {
1327             GdkDisplay *gdpy = gdk_x11_lookup_xdisplay (app->dpy);
1328             Window *children, root_return, parent_return;
1329             XWindowAttributes root_attrs;
1330             unsigned int nchildren, i;
1331 
1332             XGetWindowAttributes (app->dpy, app->root_window, &root_attrs);
1333             XSelectInput (app->dpy, app->root_window, StructureNotifyMask);
1334             XDamageCreate (app->dpy, app->root_window,
1335                            XDamageReportRawRectangles);
1336             XQueryTree (app->dpy, app->root_window,
1337                         &root_return, &parent_return, &children, &nchildren);
1338 
1339             for (i = 0; i < nchildren; i++) {
1340                 XWindowAttributes attribs;
1341                 Status ret;
1342 
1343                 gdk_error_trap_push ();
1344                 ret = XGetWindowAttributes (app->dpy, children[i], &attribs);
1345                 gdk_error_trap_pop ();
1346 
1347                 if (ret) {
1348 //                    XSelectInput (app->dpy, children[i], StructureNotifyMask);
1349                     if (!attribs.
1350                         override_redirect
1351                         && attribs.depth == root_attrs.depth) {
1352 //                        gdk_error_trap_push ();
1353                         XDamageCreate (app->dpy, children[i],
1354                                        XDamageReportRawRectangles);
1355 //                        gdk_error_trap_pop ();
1356                     }
1357                 }
1358             }
1359             XSync (app->dpy, False);
1360             XFree (children);
1361 
1362             gdk_window_add_filter (NULL,
1363                                    (GdkFilterFunc) xvc_xdamage_event_filter,
1364                                    NULL);
1365 
1366             g_assert (gdpy);
1367             gdk_x11_register_standard_event_type (gdpy,
1368                                                   app->dmg_event_base,
1369                                                   XDamageNumberEvents);
1370         }
1371 #endif     // USE_XDAMAGE
1372         // initialize recording thread
1373         pthread_attr_init (&recording_thread_attr);
1374         pthread_attr_setdetachstate (&recording_thread_attr,
1375                                      PTHREAD_CREATE_JOINABLE);
1376         pthread_create (&recording_thread, &recording_thread_attr,
1377                         (void *) do_record_thread, (void *) job);
1378 
1379     }
1380 #undef DEBUGFUNCTION
1381 }
1382 
1383 /**
1384  * \brief this positions the main control's menu (which is actually a
1385  *      popup menu a.k.a. context menu) in the same way it would if it
1386  *      were a normal menu
1387  *
1388  * @param menu pointer to the menu widget
1389  * @param x pointer to an int where the calculated x-position will be returned
1390  * @param y pointer to an int where the calculated y-position will be returned
1391  * @param push_in not actively used
1392  * @param user_data not actively used
1393  */
1394 void
position_popup_menu(GtkMenu * menu,gint * x,gint * y,gboolean * push_in,gpointer user_data)1395 position_popup_menu (GtkMenu * menu, gint * x, gint * y,
1396                      gboolean * push_in, gpointer user_data)
1397 {
1398 #define DEBUGFUNCTION "position_popup_menu()"
1399     int pheight = 0, px = 0, py = 0, tx = 0, ty = 0;
1400     GtkWidget *w = NULL;
1401 
1402     w = GTK_WIDGET (user_data);
1403 
1404     g_return_if_fail (w != NULL);
1405 
1406     pheight = w->allocation.height;
1407     px = w->allocation.x;
1408     py = w->allocation.y;
1409     tx += px;
1410     ty += py;
1411 
1412     w = gtk_widget_get_toplevel (GTK_WIDGET (w));
1413 
1414     g_return_if_fail (w != NULL);
1415 
1416     gdk_window_get_origin (GDK_WINDOW (w->window), &px, &py);
1417     tx += px;
1418     ty += py;
1419 
1420     *x = tx;
1421     *y = ty + pheight;
1422 #undef DEBUGFUNCTION
1423 }
1424 
1425 /**
1426  * \brief resets the main control window according to the preferences
1427  *      currently set
1428  */
1429 void
xvc_reset_ctrl_main_window_according_to_current_prefs()1430 xvc_reset_ctrl_main_window_according_to_current_prefs ()
1431 {
1432 #define DEBUGFUNCTION "xvc_reset_ctrl_main_window_according_to_current_prefs()"
1433     GladeXML *mwxml = NULL, *menuxml = NULL;
1434     GtkWidget *w = NULL;
1435     GtkTooltips *tooltips;
1436     Job *jobp = xvc_job_ptr ();
1437     XVC_CapTypeOptions *target = NULL;
1438     XVC_AppData *app = xvc_appdata_ptr ();
1439 
1440 #ifdef DEBUG
1441     printf ("%s %s: Entering\n", DEBUGFILE, DEBUGFUNCTION);
1442 #endif     // DEBUG
1443 
1444 #ifdef USE_FFMPEG
1445     if (app->current_mode > 0)
1446         target = &(app->multi_frame);
1447     else
1448 #endif     // USE_FFMPEG
1449         target = &(app->single_frame);
1450 
1451     mwxml = glade_get_widget_tree (xvc_ctrl_main_window);
1452     g_assert (mwxml);
1453     menuxml = glade_get_widget_tree (xvc_ctrl_m1);
1454     g_assert (menuxml);
1455 
1456     // destroy or create a frame
1457     // and set show frame check button
1458     //
1459     w = NULL;
1460     w = glade_xml_get_widget (menuxml, "xvc_ctrl_m1_show_frame");
1461     g_return_if_fail (w != NULL);
1462 
1463     if (!(app->flags & FLG_NOFRAME)) {
1464         gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (w), TRUE);
1465     } else {
1466         // the callback destroys the frame
1467         gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (w), FALSE);
1468     }
1469 
1470     //
1471     // the autocontinue menu item
1472     //
1473     // make autocontinue menuitem invisible if no ffmpeg
1474 #ifndef USE_FFMPEG
1475     w = NULL;
1476     w = glade_xml_get_widget (menuxml, "xvc_ctrl_m1_mitem_autocontinue");
1477     g_return_if_fail (w != NULL);
1478     gtk_widget_hide (GTK_WIDGET (w));
1479 
1480     w = NULL;
1481     w = glade_xml_get_widget (menuxml, "xvc_ctrl_m1_separator1");
1482     g_return_if_fail (w != NULL);
1483     gtk_widget_hide (GTK_WIDGET (w));
1484 
1485 #else      // USE_FFMPEG
1486     // the rest in case we have ffmpeg
1487     if (app->current_mode > 0) {
1488         w = glade_xml_get_widget (menuxml, "xvc_ctrl_m1_mitem_autocontinue");
1489         g_assert (w);
1490 
1491         if ((app->flags & FLG_AUTO_CONTINUE) != 0) {
1492             gtk_widget_set_sensitive (GTK_WIDGET (w), TRUE);
1493             gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (w), TRUE);
1494         } else {
1495             if (xvc_is_filename_mutable (jobp->file)) {
1496                 gtk_widget_set_sensitive (GTK_WIDGET (w), TRUE);
1497                 gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (w), FALSE);
1498             } else {
1499                 gtk_widget_set_sensitive (GTK_WIDGET (w), FALSE);
1500                 gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (w), FALSE);
1501             }
1502         }
1503     } else {
1504         w = glade_xml_get_widget (menuxml, "xvc_ctrl_m1_mitem_autocontinue");
1505         g_assert (w);
1506 
1507         gtk_widget_set_sensitive (GTK_WIDGET (w), FALSE);
1508         gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (w), FALSE);
1509     }
1510 #endif     // USE_FFMPEG
1511 
1512     //
1513     // the filename button
1514     //
1515     GtkChangeLabel (jobp->pic_no);
1516 
1517     // previous and next buttons have different meanings for on-the-fly
1518     // encoding and individual frame capture ...
1519     // this sets the tooltips accordingly
1520     if (app->current_mode == 0) {
1521         tooltips = gtk_tooltips_new ();
1522         g_assert (tooltips);
1523 
1524         w = NULL;
1525         w = glade_xml_get_widget (mwxml, "xvc_ctrl_back_button");
1526         g_assert (w);
1527         gtk_tool_item_set_tooltip (GTK_TOOL_ITEM (w), tooltips,
1528                                    _("Move cursor back one frame"),
1529                                    _("Move cursor back one frame"));
1530         if (jobp->pic_no >= target->step)
1531             gtk_widget_set_sensitive (GTK_WIDGET (w), TRUE);
1532         else
1533             gtk_widget_set_sensitive (GTK_WIDGET (w), FALSE);
1534 
1535         w = glade_xml_get_widget (mwxml, "xvc_ctrl_forward_button");
1536         g_assert (w);
1537         gtk_tool_item_set_tooltip (GTK_TOOL_ITEM (w), tooltips,
1538                                    _("Move cursor to next frame"),
1539                                    _("Move cursor to next frame"));
1540         gtk_widget_set_sensitive (GTK_WIDGET (w), TRUE);
1541 
1542         w = glade_xml_get_widget (mwxml, "xvc_ctrl_filename_button");
1543         g_assert (w);
1544         gtk_tooltips_set_tip (GTK_TOOLTIPS (tooltips), GTK_WIDGET (w),
1545                               _
1546                               ("Left Click: Reset frame counter and filename\nRight Click: Popup Menu"),
1547                               _
1548                               ("Left Click: Reset frame counter and filename\nRight Click: Popup Menu"));
1549 
1550         w = glade_xml_get_widget (mwxml, "xvc_ctrl_edit_button");
1551         g_assert (w);
1552         gtk_tool_item_set_tooltip (GTK_TOOL_ITEM (w), tooltips,
1553                                    _("Edit current individual frame"),
1554                                    _("Edit current individual frame"));
1555 
1556         w = glade_xml_get_widget (mwxml, "xvc_ctrl_step_button");
1557         g_assert (w);
1558         gtk_tool_item_set_tooltip (GTK_TOOL_ITEM (w), tooltips,
1559                                    _("Capture single frame"),
1560                                    _("Capture single frame"));
1561     } else {
1562         GtkWidget *next = NULL, *previous = NULL, *filename = NULL, *w = NULL;
1563 
1564         next = glade_xml_get_widget (mwxml, "xvc_ctrl_forward_button");
1565         g_assert (next);
1566         previous = glade_xml_get_widget (mwxml, "xvc_ctrl_back_button");
1567         g_assert (previous);
1568         filename = glade_xml_get_widget (mwxml, "xvc_ctrl_filename_button");
1569         g_assert (filename);
1570         tooltips = gtk_tooltips_new ();
1571         g_assert (tooltips);
1572 
1573         if (xvc_is_filename_mutable (jobp->file)) {
1574             gtk_widget_set_sensitive (GTK_WIDGET (next), TRUE);
1575             if (jobp->movie_no > 0)
1576                 gtk_widget_set_sensitive (GTK_WIDGET (previous), TRUE);
1577             else
1578                 gtk_widget_set_sensitive (GTK_WIDGET (previous), FALSE);
1579         } else {
1580             gtk_widget_set_sensitive (GTK_WIDGET (next), FALSE);
1581             gtk_widget_set_sensitive (GTK_WIDGET (previous), FALSE);
1582         }
1583         gtk_tool_item_set_tooltip (GTK_TOOL_ITEM (previous), tooltips,
1584                                    _("Move cursor to previous movie"),
1585                                    _("Move cursor to previous movie"));
1586         gtk_tool_item_set_tooltip (GTK_TOOL_ITEM (next), tooltips,
1587                                    _("Move cursor to next movie"),
1588                                    _("Move cursor to next movie"));
1589         gtk_tooltips_set_tip (GTK_TOOLTIPS (tooltips),
1590                               GTK_WIDGET (filename),
1591                               _
1592                               ("Left Click: Reset movie counter to zero\nRight Click: Popup Menu"),
1593                               _
1594                               ("Left Click: Reset movie counter to zero\nRight Click: Popup Menu"));
1595 
1596         w = glade_xml_get_widget (mwxml, "xvc_ctrl_edit_button");
1597         g_assert (w);
1598         gtk_tool_item_set_tooltip (GTK_TOOL_ITEM (w), tooltips,
1599                                    _("Edit current movie"),
1600                                    _("Edit current movie"));
1601 
1602         w = glade_xml_get_widget (mwxml, "xvc_ctrl_step_button");
1603         g_assert (w);
1604         gtk_tool_item_set_tooltip (GTK_TOOL_ITEM (w), tooltips, NULL, NULL);
1605     }
1606 
1607     //
1608     // capture type radio buttons
1609     //
1610     // make capture type radio buttons invisible if no ffmpeg
1611 #ifndef USE_FFMPEG
1612     w = NULL;
1613     w = glade_xml_get_widget (menuxml, "xvc_ctrl_m1_mitem_sf_capture");
1614     g_return_if_fail (w != NULL);
1615     gtk_widget_hide (GTK_WIDGET (w));
1616 
1617     w = NULL;
1618     w = glade_xml_get_widget (menuxml, "xvc_ctrl_m1_mitem_mf_capture");
1619     g_return_if_fail (w != NULL);
1620     gtk_widget_hide (GTK_WIDGET (w));
1621 #else      // USE_FFMPEG
1622 
1623     if (app->current_mode == 0) {
1624         w = NULL;
1625         w = glade_xml_get_widget (menuxml, "xvc_ctrl_m1_mitem_mf_capture");
1626         g_return_if_fail (w != NULL);
1627         gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (w), FALSE);
1628 
1629         w = NULL;
1630         w = glade_xml_get_widget (menuxml, "xvc_ctrl_m1_mitem_sf_capture");
1631         g_return_if_fail (w != NULL);
1632         gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (w), TRUE);
1633     } else {
1634         w = NULL;
1635         w = glade_xml_get_widget (menuxml, "xvc_ctrl_m1_mitem_sf_capture");
1636         g_return_if_fail (w != NULL);
1637         gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (w), FALSE);
1638 
1639         w = NULL;
1640         w = glade_xml_get_widget (menuxml, "xvc_ctrl_m1_mitem_mf_capture");
1641         g_return_if_fail (w != NULL);
1642         gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (w), TRUE);
1643     }
1644 #endif     // USE_FFMPEG
1645 
1646 #undef DEBUGFUNCTION
1647 }
1648 
1649 /**
1650  * XVIDCAP GUI API<br>
1651  * ( IF THAT WORD IS ALLOWED HERE ;) )<br>
1652  * <br><table>
1653  * <tr><td>xvc_init_pre</td><td>do gui preintialization like setting fallback options, initializing thread libraries and the like</td></tr>
1654  * <tr><td>xvc_ui_create</td><td>create the gui</td></tr>
1655  * <tr><td>xvc_frame_create</td><td>create the frame for selecting the area to capture</td></tr>
1656  * <tr><td>xvc_check_start_options</td><td>check the preferences on program start</td></tr>
1657  * <tr><td>xvc_ui_init</td><td>gui initialization</td></tr>
1658  * <tr><td>xvc_ui_run</td><td>start the ui's main loop</td></tr>
1659  * <tr><td>xvc_idle_add</td><td>queue an idle action</td></tr>
1660  * <tr><td>xvc_change_filename_display</td><td>update the display of the current frame/file</td></tr>
1661  * <tr><td>xvc_capture_stop_signal</td><td>tell the ui you want it to stop recording</td></tr>
1662  * <tr><td>xvc_capture_stop</td><td>implements the functions for actually stopping</td></tr>
1663  * <tr><td>xvc_capture_start</td><td>implements the functions for starting a recording</td></tr>
1664  * <tr><td>xvc_frame_change</td><td>change the area to capture</td></tr>
1665  * <tr><td>xvc_frame_monitor</td><td>update a widget monitoring frame rate</td></tr>
1666  * </table>
1667  */
1668 
1669 /**
1670  * \brief does gui preintialization, mainly initializing thread libraries
1671  *      and calling gtk_init with the command line arguments
1672  *
1673  * @param argc number of command line arguments
1674  * @param argv pointer to the command line arguments
1675  * @return Could be FALSE for failure, but de facto always TRUE
1676  */
1677 Boolean
xvc_init_pre(int argc,char ** argv)1678 xvc_init_pre (int argc, char **argv)
1679 {
1680 #define DEBUGFUNCTION "xvc_init_pre()"
1681     g_thread_init (NULL);
1682     /*gdk_threads_init ();*/
1683 
1684     gtk_init (&argc, &argv);
1685     return TRUE;
1686 #undef DEBUGFUNCTION
1687 }
1688 
1689 /**
1690  * \brief creates the main control and menu from the glade definition,
1691  *      connects the signals and stores the widget pointers for further
1692  *      reference.
1693  *
1694  * @return Could be FALSE for failure, but de facto always TRUE
1695  */
1696 Boolean
xvc_ui_create()1697 xvc_ui_create ()
1698 {
1699 #define DEBUGFUNCTION "xvc_ui_create()"
1700     GladeXML *xml = NULL;
1701     XVC_AppData *app = xvc_appdata_ptr ();
1702     GtkWidget *w = NULL;
1703 
1704 #ifdef DEBUG
1705     printf ("%s %s: Entering\n", DEBUGFILE, DEBUGFUNCTION);
1706 #endif     // DEBUG
1707 
1708     // only show the ui if not in nogui
1709     if ((app->flags & FLG_NOGUI) == 0) {
1710         // main window
1711         // load the interface
1712         xml = glade_xml_new (XVC_GLADE_FILE, "xvc_ctrl_main_window", NULL);
1713 
1714         g_assert (xml);
1715 
1716         // connect the signals in the interface
1717         glade_xml_signal_autoconnect (xml);
1718 
1719         // store the toplevel widget for further reference
1720         xvc_ctrl_main_window =
1721             glade_xml_get_widget (xml, "xvc_ctrl_main_window");
1722 
1723 #if GTK_CHECK_VERSION(2, 5, 0)
1724 #else
1725         {
1726             GtkWidget *w = NULL;
1727 
1728 /*
1729  *            replace the following stock ids
1730  *
1731 gtk-media-stop gtk-stop
1732 gtk-media-pause gtk-go-up (funky, but it works)
1733 gtk-media-record gtk-convert
1734 gtk-media-next gtk-goto-last
1735 gtk-media-rewind gtk-go-back
1736 gtk-media-forward gtk-go-forward
1737 gtk-edit gtk-paste (2nd choice would be gtk-open)
1738 *
1739 */
1740             w = glade_xml_get_widget (xml, "xvc_ctrl_stop_toggle");
1741             if (w) {
1742                 gtk_tool_button_set_icon_widget (GTK_TOOL_BUTTON (w), NULL);
1743                 gtk_tool_button_set_stock_id (GTK_TOOL_BUTTON (w), "gtk-stop");
1744             }
1745 
1746             w = glade_xml_get_widget (xml, "xvc_ctrl_pause_toggle");
1747             if (w) {
1748                 gtk_tool_button_set_icon_widget (GTK_TOOL_BUTTON (w), NULL);
1749                 gtk_tool_button_set_stock_id (GTK_TOOL_BUTTON (w), "gtk-go-up");
1750             }
1751 
1752             w = glade_xml_get_widget (xml, "xvc_ctrl_record_toggle");
1753             if (w) {
1754                 gtk_tool_button_set_icon_widget (GTK_TOOL_BUTTON (w), NULL);
1755                 gtk_tool_button_set_stock_id (GTK_TOOL_BUTTON (w),
1756                                               "gtk-convert");
1757             }
1758 
1759             w = glade_xml_get_widget (xml, "xvc_ctrl_step_button");
1760             if (w) {
1761                 gtk_tool_button_set_icon_widget (GTK_TOOL_BUTTON (w), NULL);
1762                 gtk_tool_button_set_stock_id (GTK_TOOL_BUTTON (w),
1763                                               "gtk-goto-last");
1764             }
1765 
1766             w = glade_xml_get_widget (xml, "xvc_ctrl_back_button");
1767             if (w) {
1768                 gtk_tool_button_set_icon_widget (GTK_TOOL_BUTTON (w), NULL);
1769                 gtk_tool_button_set_stock_id (GTK_TOOL_BUTTON (w),
1770                                               "gtk-go-back");
1771             }
1772 
1773             w = glade_xml_get_widget (xml, "xvc_ctrl_forward_button");
1774             if (w) {
1775                 gtk_tool_button_set_icon_widget (GTK_TOOL_BUTTON (w), NULL);
1776                 gtk_tool_button_set_stock_id (GTK_TOOL_BUTTON (w),
1777                                               "gtk-go-forward");
1778             }
1779 
1780             w = glade_xml_get_widget (xml, "xvc_ctrl_edit_button");
1781             if (w) {
1782                 gtk_tool_button_set_icon_widget (GTK_TOOL_BUTTON (w), NULL);
1783                 gtk_tool_button_set_stock_id (GTK_TOOL_BUTTON (w), "gtk-paste");
1784             }
1785 
1786             w = NULL;
1787         }
1788 #endif     // GTK_CHECK_VERSION
1789 
1790 #if GTK_CHECK_VERSION(2, 6, 0)
1791 #else
1792         {
1793             GtkWidget *w = NULL;
1794 
1795             w = glade_xml_get_widget (xml, "xvc_ctrl_m1_mitem_about");
1796             if (w)
1797                 gtk_widget_hide (w);
1798         }
1799 #endif     // GKT_CHECK_VERSION
1800 
1801         xml = NULL;
1802         // popup window
1803         // load the interface
1804         xml = glade_xml_new (XVC_GLADE_FILE, "xvc_ctrl_m1", NULL);
1805 
1806         g_assert (xml);
1807 
1808         xvc_ctrl_m1 = glade_xml_get_widget (xml, "xvc_ctrl_m1");
1809 
1810         // this needs to go here to avoid double frame when done later
1811         // should trigger any callback here
1812         w = NULL;
1813         w = glade_xml_get_widget (xml, "xvc_ctrl_m1_show_frame");
1814         g_assert (w);
1815 
1816         if (!(app->flags & FLG_NOFRAME)) {
1817             gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (w), TRUE);
1818         } else {
1819             gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM (w), FALSE);
1820         }
1821 
1822         // connect the signals in the interface
1823         glade_xml_signal_autoconnect (xml);
1824 
1825     }
1826 #ifdef DEBUG
1827     printf ("%s %s: Leaving\n", DEBUGFILE, DEBUGFUNCTION);
1828 #endif     // DEBUG
1829 
1830     return TRUE;
1831 #undef DEBUGFUNCTION
1832 }
1833 
1834 /**
1835  * \brief creates the frame around the area to capture by calling the
1836  *      actual implementation in xvc_create_gtk_frame
1837  *
1838  * @see xvc_create_gtk_frame
1839  * @param win a Window (ID) to allow passing a window id for selecting
1840  *      the area to capture (i.e. capture that window ... but not if you
1841  *      move the window afterwards)
1842  * @return Could be FALSE for failure, but de facto always TRUE
1843  */
1844 Boolean
xvc_frame_create(Window win)1845 xvc_frame_create (Window win)
1846 {
1847 #define DEBUGFUNCTION "xvc_frame_create()"
1848     XVC_AppData *app = xvc_appdata_ptr ();
1849 
1850     if ((app->flags & FLG_NOGUI) == 0) {    /* there's one good reason for not
1851                                              * having a main window */
1852         g_assert (xvc_ctrl_main_window);
1853     }
1854 
1855     if (win == None) {
1856         // display and window attributes seem to be set correctly only if
1857         // retrieved after the UI was mapped
1858         gtk_init_add ((GtkFunction) xvc_appdata_set_window_attributes,
1859                       (void *) app->root_window);
1860 
1861         if (app->area->width == 0)
1862             app->area->width = 10;
1863         if (app->area->height == 0)
1864             app->area->height = 10;
1865 
1866         xvc_create_gtk_frame (xvc_ctrl_main_window, app->area->width,
1867                               app->area->height, app->area->x, app->area->y);
1868     } else {
1869         int x, y;
1870         Window temp = None;
1871 
1872         // display and window attributes seem to be set correctly only if
1873         // retrieved after the UI was mapped
1874         gtk_init_add ((GtkFunction) xvc_appdata_set_window_attributes,
1875                       (void *) win);
1876         XTranslateCoordinates (app->dpy, win, app->root_window,
1877                                0, 0, &x, &y, &temp);
1878 
1879         app->area->x = x;
1880         app->area->y = y;
1881         app->area->width = app->win_attr.width;
1882         app->area->height = app->win_attr.height;
1883 
1884         xvc_create_gtk_frame (xvc_ctrl_main_window, app->area->width,
1885                               app->area->height, app->area->x, app->area->y);
1886     }
1887     return TRUE;
1888 #undef DEBUGFUNCTION
1889 }
1890 
1891 /**
1892  * \brief checks the current preferences and should be used to check
1893  *      preferences right before running the UI
1894  *
1895  * This does not return anything but must react on errors found on its own.
1896  * It is global mainly for allowing it to be called from the warning dialog
1897  * in case the startup check produced an error with a default action, you
1898  * clicked OK and then the validation needs to run again.
1899  */
1900 void
xvc_check_start_options()1901 xvc_check_start_options ()
1902 {
1903 #define DEBUGFUNCTION "xvc_check_start_options()"
1904     int count_non_info_messages = 0;
1905     int rc = 0;
1906     XVC_AppData *app = xvc_appdata_ptr ();
1907 
1908 #ifdef DEBUG
1909     printf ("%s %s: Entering with errors_after_cli = %p\n", DEBUGFILE,
1910             DEBUGFUNCTION, errors_after_cli);
1911 #endif     // DEBUG
1912 
1913     if (OK_attempts > 0 && errors_after_cli != NULL) {
1914         errors_after_cli = xvc_appdata_validate (app, 1, &rc);
1915 
1916 #ifdef DEBUG
1917         printf ("%s %s: new errors_after_cli = %p\n", DEBUGFILE,
1918                 DEBUGFUNCTION, errors_after_cli);
1919 #endif     // DEBUG
1920 
1921         if (rc == -1) {
1922             fprintf (stderr,
1923                      "%s %s: Unrecoverable error while validating options, please contact the xvidcap project.\n",
1924                      DEBUGFILE, DEBUGFUNCTION);
1925             exit (1);
1926         }
1927     }
1928 
1929     if ((app->flags & FLG_NOGUI) == 0) {    // we're running with gui
1930         if (errors_after_cli != NULL) {
1931             XVC_ErrorListItem *err;
1932 
1933             err = errors_after_cli;
1934             count_non_info_messages = 0;
1935 
1936             for (; err != NULL; err = err->next) {
1937                 if (err->err->type != XVC_ERR_INFO)
1938                     count_non_info_messages++;
1939             }
1940             if (count_non_info_messages > 0
1941                 || (app->flags & FLG_RUN_VERBOSE && OK_attempts == 0)) {
1942                 xvc_warn_main_window =
1943                     xvc_create_warning_with_errors (errors_after_cli, 2);
1944                 OK_attempts++;
1945             } else {
1946                 warning_submit ();
1947             }
1948         } else {
1949             warning_submit ();
1950         }
1951     } else {                           // or without
1952         while (errors_after_cli != NULL && OK_attempts < 6) {
1953             XVC_ErrorListItem *err;
1954 
1955             err = errors_after_cli;
1956             count_non_info_messages = rc = 0;
1957 
1958             for (; err != NULL; err = err->next) {
1959                 if (err->err->type != XVC_ERR_INFO)
1960                     count_non_info_messages++;
1961             }
1962             if (count_non_info_messages > 0
1963                 || (app->flags & FLG_RUN_VERBOSE && OK_attempts == 0)) {
1964                 err = errors_after_cli;
1965                 for (; err != NULL; err = err->next) {
1966                     if (err->err->type != XVC_ERR_INFO ||
1967                         app->flags & FLG_RUN_VERBOSE) {
1968                         xvc_error_write_msg (err->err->code,
1969                                              ((app->
1970                                                flags &
1971                                                FLG_RUN_VERBOSE) ? 1 : 0));
1972                         (*err->err->action) (err);
1973                     }
1974                 }
1975             }
1976 
1977             OK_attempts++;
1978 
1979             errors_after_cli = xvc_appdata_validate (app, 1, &rc);
1980             if (rc == -1) {
1981                 fprintf (stderr,
1982                          "%s %s: Unrecoverable error while validating options, please contact the xvidcap project.\n",
1983                          DEBUGFILE, DEBUGFUNCTION);
1984                 exit (1);
1985             }
1986         }
1987 
1988         if (errors_after_cli != NULL && count_non_info_messages > 0) {
1989             fprintf (stderr,
1990                      "%s %s: You have specified some conflicting settings which could not be resolved automatically.\n",
1991                      DEBUGFILE, DEBUGFUNCTION);
1992             exit (1);
1993         }
1994         warning_submit ();
1995     }
1996 
1997 #ifdef DEBUG
1998     printf ("%s %s: Leaving\n", DEBUGFILE, DEBUGFUNCTION);
1999 #endif     // DEBUG
2000 
2001 #undef DEBUGFUNCTION
2002 }
2003 
2004 /**
2005  * \brief initializes the UI by mainly ensuring xvc_check_start_options is
2006  *      called with the errors found in main
2007  *
2008  * @see xvc_check_start_options
2009  * @param errors the errors in the preferences found in the main function
2010  * @return Could be FALSE for failure, but de facto always TRUE
2011  */
2012 Boolean
xvc_ui_init(XVC_ErrorListItem * errors)2013 xvc_ui_init (XVC_ErrorListItem * errors)
2014 {
2015 #define DEBUGFUNCTION "xvc_ui_init()"
2016     XVC_AppData *app = xvc_appdata_ptr ();
2017 
2018 #ifdef DEBUG
2019     printf ("%s %s: Entering\n", DEBUGFILE, DEBUGFUNCTION);
2020 #endif     // DEBUG
2021 
2022     // display warning dialog if required
2023     errors_after_cli = errors;
2024     // the gui warning dialog needs a realized window, therefore we
2025     // schedule it do be displayed (potentially) when the main loop starts
2026     // this does not seem to work with nogui
2027     if (!(app->flags & FLG_NOGUI)) {
2028         gtk_init_add ((GtkFunction) xvc_check_start_options, NULL);
2029     } else {
2030         xvc_check_start_options ();
2031         gtk_init_add ((GtkFunction) xvc_capture_start, NULL);
2032     }
2033 
2034 #ifdef DEBUG
2035     printf ("%s %s: Leaving\n", DEBUGFILE, DEBUGFUNCTION);
2036 #endif     // DEBUG
2037 
2038     return TRUE;
2039 #undef DEBUGFUNCTION
2040 }
2041 
2042 /**
2043  * \brief runs the UI by executing the main loop
2044  *
2045  * @return Could be any integer for failure, but de facto always 0
2046  */
2047 int
xvc_ui_run()2048 xvc_ui_run ()
2049 {
2050 #define DEBUGFUNCTION "xvc_ui_run()";
2051 
2052     gtk_main ();
2053 
2054     return 0;
2055 #undef DEBUGFUNCTION
2056 }
2057 
2058 /**
2059  * \brief adds an action to the event queue to be executed when there's time
2060  *
2061  * The functions added here are executed whenever there's time until they
2062  * return FALSE.
2063  *
2064  * @param func a pointer to a function to queue
2065  * @param data a pointer to an argument to that function
2066  */
2067 void
xvc_idle_add(void * func,void * data)2068 xvc_idle_add (void *func, void *data)
2069 {
2070     g_idle_add (func, data);
2071 }
2072 
2073 /**
2074  * \brief updates the display of the current frame/filename according to
2075  *      the current state of job.
2076  *
2077  * This is meant to be used through xvc_idle_add during recording
2078  * @return TRUE for as long as the recording thread is running or FALSE
2079  *      otherwise
2080  */
2081 Boolean
xvc_change_filename_display()2082 xvc_change_filename_display ()
2083 {
2084 #define DEBUGFUNCTION "xvc_change_filename_display()"
2085 #ifdef DEBUG
2086     printf ("%s %s: Entering\n", DEBUGFILE, DEBUGFUNCTION);
2087 #endif     // DEBUG
2088     XVC_AppData *app = xvc_appdata_ptr ();
2089     Job *job = xvc_job_ptr ();
2090     int ret = app->recording_thread_running;
2091 
2092     if (!ret) {
2093         last_pic_no = 0;
2094         GtkChangeLabel (job->pic_no);
2095     } else if (job->pic_no != last_pic_no) {
2096         last_pic_no = job->pic_no;
2097         GtkChangeLabel (last_pic_no);
2098     }
2099 #ifdef DEBUG
2100     printf ("%s %s: Leaving! ...continuing? %i\n", DEBUGFILE, DEBUGFUNCTION,
2101             (int) ret);
2102 #endif     // DEBUG
2103 
2104     return ret;
2105 #undef DEBUGFUNCTION
2106 }
2107 
2108 /**
2109  * \brief tell the recording thread to stop
2110  *
2111  * The actual stopping is done through the capture's state machine. This
2112  * just sets the state to VC_STOP and sends the recording thread an ALARM
2113  * signal to terminate a potential usleep. It may then wait for the thread
2114  * to finish.
2115  * @param wait should this function actually wait for the thread to finish?
2116  */
2117 void
xvc_capture_stop_signal(Boolean wait)2118 xvc_capture_stop_signal (Boolean wait)
2119 {
2120 #define DEBUGFUNCTION "xvc_capture_stop_signal()"
2121     int status = -1;
2122     XVC_AppData *app = xvc_appdata_ptr ();
2123 
2124 #ifdef DEBUG
2125     printf ("%s %s: Entering, should we wait? %i\n",
2126             DEBUGFILE, DEBUGFUNCTION, wait);
2127 #endif     // DEBUG
2128 
2129     xvc_job_set_state (VC_STOP);
2130 
2131     if (app->recording_thread_running) {
2132 #ifdef DEBUG
2133         printf ("%s %s: stop pressed while recording\n",
2134                 DEBUGFILE, DEBUGFUNCTION);
2135 #endif     // DEBUG
2136         // stop waiting for next frame to capture
2137         status = pthread_kill (recording_thread, SIGALRM);
2138 #ifdef DEBUG
2139         printf ("%s %s: thread %i kill with rc %i\n",
2140                 DEBUGFILE, DEBUGFUNCTION, (int) recording_thread, status);
2141 #endif     // DEBUG
2142     }
2143 
2144     if (wait) {
2145         while (app->recording_thread_running) {
2146             usleep (100);
2147         }
2148     }
2149 #ifdef DEBUG
2150     printf ("%s %s: Leaving\n", DEBUGFILE, DEBUGFUNCTION);
2151 #endif     // DEBUG
2152 #undef DEBUGFUNCTION
2153 }
2154 
2155 /**
2156  * \brief implements the actual actions required when stopping a recording
2157  *      session, both GUI related an not.
2158  */
2159 Boolean
xvc_capture_stop()2160 xvc_capture_stop ()
2161 {
2162 #define DEBUGFUNCTION "xvc_capture_stop()"
2163     Job *job = xvc_job_ptr ();
2164 
2165 #ifdef DEBUG
2166     printf ("%s %s: stopping\n", DEBUGFILE, DEBUGFUNCTION);
2167 #endif     // DEBUG
2168     stop_recording_nongui_stuff (job);
2169 #ifdef DEBUG
2170     printf ("%s %s: done stopping non-gui stuff\n", DEBUGFILE, DEBUGFUNCTION);
2171 #endif     // DEBUG
2172     if (!(job->flags & FLG_NOGUI)) {
2173         gdk_threads_enter ();
2174         stop_recording_gui_stuff (job);
2175         gdk_flush ();
2176         gdk_threads_leave ();
2177     } else {
2178         gtk_main_quit ();
2179     }
2180 
2181     return FALSE;
2182 #undef DEBUGFUNCTION
2183 }
2184 
2185 /**
2186  * \brief starts a recording session
2187  */
2188 void
xvc_capture_start()2189 xvc_capture_start ()
2190 {
2191 #define DEBUGFUNCTION "xvc_capture_start()"
2192     Job *job = xvc_job_ptr ();
2193 
2194     start_recording_nongui_stuff (job);
2195     if (!(job->flags & FLG_NOGUI))
2196         start_recording_gui_stuff (job);
2197 
2198 #undef DEBUGFUNCTION
2199 }
2200 
2201 /*
2202  * \brief changes the frame around the area to capture by calling the
2203  *      implementing function xvc_change_gtk_frame
2204  *
2205  * @see xvc_change_gtk_frame
2206  */
2207 void
xvc_frame_change(int x,int y,int width,int height,Boolean reposition_control,Boolean show_dimensions)2208 xvc_frame_change (int x, int y, int width, int height,
2209                   Boolean reposition_control, Boolean show_dimensions)
2210 {
2211 #define DEBUGFUNCTION "xvc_frame_change()"
2212     xvc_change_gtk_frame (x, y, width, height, reposition_control,
2213                           show_dimensions);
2214 #undef DEBUGFUNCTION
2215 }
2216 
2217 /**
2218  * \brief updates the widget monitoring the current frame rate based on
2219  *      the current value of xvc_led_time and last_led_time
2220  *
2221  * This is meant to be used through xvc_idle_add during recording
2222  * @return TRUE for as long as the recording thread is running or FALSE
2223  *      otherwise
2224  * @see xvc_led_time
2225  * @see last_led_time
2226  * @see xvc_idle_add
2227  */
2228 Boolean
xvc_frame_monitor()2229 xvc_frame_monitor ()
2230 {
2231 #define DEBUGFUNCTION "xvc_frame_monitor()"
2232 
2233     Job *job = xvc_job_ptr ();
2234     XVC_AppData *app = xvc_appdata_ptr ();
2235     int percent = 0, diff = 0;
2236     GladeXML *xml = NULL;
2237     GtkWidget *w = NULL;
2238     int ret = app->recording_thread_running;
2239 
2240 #ifdef DEBUG
2241     printf ("%s %s: Entering with time = %i\n", DEBUGFILE, DEBUGFUNCTION,
2242             xvc_led_time);
2243 #endif     // DEBUG
2244 
2245     // fastpath
2246     if (xvc_led_time != 0 && last_led_time == xvc_led_time)
2247         return TRUE;
2248 
2249     if (app->flags & FLG_TO_TRAY && tray_frame_mon) {
2250         w = tray_frame_mon;
2251         g_return_val_if_fail (w != NULL, FALSE);
2252     } else {
2253         xml = glade_get_widget_tree (xvc_ctrl_main_window);
2254         g_return_val_if_fail (xml != NULL, FALSE);
2255         w = glade_xml_get_widget (xml, "xvc_ctrl_led_meter");
2256         g_return_val_if_fail (w != NULL, FALSE);
2257     }
2258 
2259     if (!ret) {
2260         xvc_led_time = last_led_time = 0;
2261     }
2262 
2263     if (xvc_led_time == 0) {
2264         percent = 0;
2265     } else if (xvc_led_time <= job->time_per_frame)
2266         percent = 30;
2267     else if (xvc_led_time >= (job->time_per_frame * 2))
2268         percent = 100;
2269     else {
2270         diff = xvc_led_time - job->time_per_frame;
2271         percent = diff * 70 / job->time_per_frame;
2272         percent += 30;
2273     }
2274 
2275     led_meter_set_percent (LED_METER (w), percent);
2276 
2277     if (percent == 0)
2278         LED_METER (w)->old_max_da = 0;
2279 
2280 #ifdef DEBUG
2281     printf ("%s %s: Leaving with percent = %i ... continuing? %i\n",
2282             DEBUGFILE, DEBUGFUNCTION, percent, ret);
2283 #endif     // DEBUG
2284 
2285     return ret;
2286 #undef DEBUGFUNCTION
2287 }
2288 
2289 /*
2290  * callbacks here ....
2291  *
2292  */
2293 #ifndef DOXYGEN_SHOULD_SKIP_THIS
2294 gboolean
on_xvc_ctrl_main_window_delete_event(GtkWidget * widget,GdkEvent * event,gpointer user_data)2295 on_xvc_ctrl_main_window_delete_event (GtkWidget * widget,
2296                                       GdkEvent * event, gpointer user_data)
2297 {
2298 #define DEBUGFUNCTION "on_xvc_ctrl_main_window_delete_event()"
2299     Job *jobp = xvc_job_ptr ();
2300 
2301     if (jobp && (jobp->state & VC_STOP) == 0) {
2302         xvc_capture_stop_signal (TRUE);
2303     }
2304 
2305     xvc_destroy_gtk_frame ();
2306     gtk_main_quit ();                  /** \todo why does this seem to be
2307 necessary with libglade where it was not previously */
2308     return FALSE;
2309 #undef DEBUGFUNCTION
2310 }
2311 
2312 gboolean
on_xvc_ctrl_main_window_destroy_event(GtkWidget * widget,GdkEvent * event,gpointer user_data)2313 on_xvc_ctrl_main_window_destroy_event (GtkWidget * widget,
2314                                        GdkEvent * event, gpointer user_data)
2315 {
2316 #define DEBUGFUNCTION "on_xvc_ctrl_main_window_destroy_event()"
2317     gtk_main_quit ();
2318     return FALSE;
2319 #undef DEBUGFUNCTION
2320 }
2321 
2322 void
on_xvc_ctrl_m1_mitem_preferences_activate(GtkMenuItem * menuitem,gpointer user_data)2323 on_xvc_ctrl_m1_mitem_preferences_activate (GtkMenuItem * menuitem,
2324                                            gpointer user_data)
2325 {
2326 #define DEBUGFUNCTION "on_xvc_ctrl_m1_mitem_preferences_activate()"
2327     XVC_AppData *app = xvc_appdata_ptr ();
2328 
2329 #ifdef DEBUG
2330     printf ("%s %s: Entering with app %p\n", DEBUGFILE, DEBUGFUNCTION, app);
2331 #endif     // DEBUG
2332 
2333     xvc_create_pref_dialog (app);
2334 
2335 #ifdef DEBUG
2336     printf ("%s %s: Leaving\n", DEBUGFILE, DEBUGFUNCTION);
2337 #endif     // DEBUG
2338 #undef DEBUGFUNCTION
2339 }
2340 
2341 void
on_xvc_ctrl_m1_mitem_save_preferences_activate(GtkMenuItem * menuitem,gpointer user_data)2342 on_xvc_ctrl_m1_mitem_save_preferences_activate (GtkMenuItem *
2343                                                 menuitem, gpointer user_data)
2344 {
2345 #define DEBUGFUNCTION "on_xvc_ctrl_m1_mitem_save_preferences_activate()"
2346     Job *jobp = xvc_job_ptr ();
2347 
2348     xvc_write_options_file (jobp);
2349 #undef DEBUGFUNCTION
2350 }
2351 
2352 void
on_xvc_ctrl_m1_mitem_sf_capture_activate(GtkMenuItem * menuitem,gpointer user_data)2353 on_xvc_ctrl_m1_mitem_sf_capture_activate (GtkMenuItem * menuitem,
2354                                           gpointer user_data)
2355 {
2356 #ifdef USE_FFMPEG
2357 #define DEBUGFUNCTION "on_xvc_ctrl_m1_mitem_sf_capture_activate()"
2358     XVC_AppData *app = xvc_appdata_ptr ();
2359 
2360     if (app->current_mode == 1
2361         && gtk_check_menu_item_get_active (GTK_CHECK_MENU_ITEM (menuitem)) !=
2362         0) {
2363         app->current_mode = (app->current_mode == 0) ? 1 : 0;
2364         xvc_toggle_cap_type ();
2365     }
2366 #undef DEBUGFUNCTION
2367 #endif     // USE_FFMPEG
2368 }
2369 
2370 void
on_xvc_ctrl_m1_mitem_mf_capture_activate(GtkMenuItem * menuitem,gpointer user_data)2371 on_xvc_ctrl_m1_mitem_mf_capture_activate (GtkMenuItem * menuitem,
2372                                           gpointer user_data)
2373 {
2374 #ifdef USE_FFMPEG
2375 #define DEBUGFUNCTION "on_xvc_ctrl_m1_mitem_mf_capture_activate()"
2376     XVC_AppData *app = xvc_appdata_ptr ();
2377 
2378     if (app->current_mode == 0
2379         && gtk_check_menu_item_get_active (GTK_CHECK_MENU_ITEM (menuitem)) !=
2380         0) {
2381         app->current_mode = (app->current_mode == 0) ? 1 : 0;
2382         xvc_toggle_cap_type ();
2383     }
2384 #undef DEBUGFUNCTION
2385 #endif     // USE_FFMPEG
2386 }
2387 
2388 void
on_xvc_ctrl_m1_mitem_autocontinue_activate(GtkMenuItem * menuitem,gpointer user_data)2389 on_xvc_ctrl_m1_mitem_autocontinue_activate (GtkMenuItem * menuitem,
2390                                             gpointer user_data)
2391 {
2392 #define DEBUGFUNCTION "on_xvc_ctrl_m1_mitem_autocontinue_activate()"
2393     Job *jobp = xvc_job_ptr ();
2394     XVC_AppData *app = xvc_appdata_ptr ();
2395 
2396     if (gtk_check_menu_item_get_active (GTK_CHECK_MENU_ITEM (menuitem))) {
2397         if ((!xvc_is_filename_mutable (jobp->file))
2398             || app->current_mode == 0) {
2399             printf
2400                 ("Output not a video file or no counter in filename\nDisabling autocontinue!\n");
2401             gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM
2402                                             (menuitem), FALSE);
2403         } else {
2404             jobp->flags |= FLG_AUTO_CONTINUE;
2405             app->flags |= FLG_AUTO_CONTINUE;
2406         }
2407     } else {
2408         jobp->flags &= ~FLG_AUTO_CONTINUE;
2409         app->flags &= ~FLG_AUTO_CONTINUE;
2410     }
2411 #undef DEBUGFUNCTION
2412 }
2413 
2414 void
on_xvc_ctrl_m1_show_frame_activate(GtkMenuItem * menuitem,gpointer user_data)2415 on_xvc_ctrl_m1_show_frame_activate (GtkMenuItem * menuitem, gpointer user_data)
2416 {
2417 #define DEBUGFUNCTION "on_xvc_ctrl_m1_show_frame_activate()"
2418     Job *jobp = xvc_job_ptr ();
2419     XVC_AppData *app = xvc_appdata_ptr ();
2420 
2421     if (gtk_check_menu_item_get_active (GTK_CHECK_MENU_ITEM (menuitem))) {
2422         app->flags &= ~FLG_NOFRAME;
2423         jobp->flags &= ~FLG_NOFRAME;
2424         xvc_create_gtk_frame (xvc_ctrl_main_window, app->area->width,
2425                               app->area->height, app->area->x, app->area->y);
2426     } else {
2427         jobp->flags |= FLG_NOFRAME;
2428         app->flags |= FLG_NOFRAME;
2429         xvc_destroy_gtk_frame ();
2430     }
2431 #undef DEBUGFUNCTION
2432 }
2433 
2434 void
on_xvc_ctrl_m1_mitem_make_activate(GtkMenuItem * menuitem,gpointer user_data)2435 on_xvc_ctrl_m1_mitem_make_activate (GtkMenuItem * menuitem, gpointer user_data)
2436 {
2437 #define DEBUGFUNCTION "on_xvc_ctrl_m1_mitem_make_activate()"
2438     Job *jobp = xvc_job_ptr ();
2439     XVC_CapTypeOptions *target = NULL;
2440     XVC_AppData *app = xvc_appdata_ptr ();
2441 
2442 #ifdef USE_FFMPEG
2443     if (app->current_mode > 0)
2444         target = &(app->multi_frame);
2445     else
2446 #endif     // USE_FFMPEG
2447         target = &(app->single_frame);
2448 
2449     if (!app->current_mode) {
2450         xvc_command_execute (target->video_cmd, 0, 0,
2451                              jobp->file, target->start_no, jobp->pic_no,
2452                              app->area->width, app->area->height, target->fps);
2453     } else {
2454         xvc_command_execute (target->video_cmd, 2,
2455                              jobp->movie_no, jobp->file, target->start_no,
2456                              jobp->pic_no, app->area->width,
2457                              app->area->height, target->fps);
2458     }
2459 #undef DEBUGFUNCTION
2460 }
2461 
2462 void
on_xvc_ctrl_m1_mitem_help_activate(GtkMenuItem * menuitem,gpointer user_data)2463 on_xvc_ctrl_m1_mitem_help_activate (GtkMenuItem * menuitem, gpointer user_data)
2464 {
2465 #define DEBUGFUNCTION "on_xvc_ctrl_m1_mitem_help_activate()"
2466     system ("yelp ghelp:xvidcap &");
2467 #undef DEBUGFUNCTION
2468 }
2469 
2470 void
on_xvc_ctrl_m1_mitem_quit_activate(GtkMenuItem * menuitem,gpointer user_data)2471 on_xvc_ctrl_m1_mitem_quit_activate (GtkMenuItem * menuitem, gpointer user_data)
2472 {
2473 #define DEBUGFUNCTION "on_xvc_ctrl_m1_mitem_quit_activate()"
2474     gboolean ignore = TRUE;
2475 
2476     g_signal_emit_by_name ((GtkObject *) xvc_ctrl_main_window,
2477                            "destroy_event", 0, &ignore);
2478 #undef DEBUGFUNCTION
2479 }
2480 
2481 gint
on_xvc_result_dialog_key_press_event(GtkWidget * widget,GdkEvent * event)2482 on_xvc_result_dialog_key_press_event (GtkWidget * widget, GdkEvent * event)
2483 {
2484 #define DEBUGFUNCTION "on_xvc_result_dialog_key_press_event()"
2485     GdkEventKey *kevent = NULL;
2486 
2487     g_assert (widget);
2488     g_assert (event);
2489     kevent = (GdkEventKey *) event;
2490 
2491 #ifdef DEBUG
2492     printf ("%s %s: Entering\n", DEBUGFILE, DEBUGFUNCTION);
2493 #endif     // DEBUG
2494 
2495     if (kevent->keyval == 65470) {
2496         do_results_help ();
2497     }
2498 #ifdef DEBUG
2499     printf ("%s %s: Leaving\n", DEBUGFILE, DEBUGFUNCTION);
2500 #endif     // DEBUG
2501 
2502     // Tell calling code that we have not handled this event; pass it on.
2503     return FALSE;
2504 #undef DEBUGFUNCTION
2505 }
2506 
2507 void
on_xvc_result_dialog_select_filename_button_clicked(GtkButton * button,gpointer user_data)2508 on_xvc_result_dialog_select_filename_button_clicked (GtkButton * button,
2509                                                      gpointer user_data)
2510 {
2511 #define DEBUGFUNCTION "on_xvc_result_select_filename_button_clicked()"
2512     int result = 0;
2513 
2514     GladeXML *xml = NULL;
2515     GtkWidget *w = NULL, *dialog = NULL;
2516 
2517 #ifdef DEBUG
2518     printf ("%s %s: Entering\n", DEBUGFILE, DEBUGFUNCTION);
2519 #endif     // DEBUG
2520 
2521     // load the interface
2522     xml = glade_xml_new (XVC_GLADE_FILE, "xvc_save_filechooserdialog", NULL);
2523     g_assert (xml);
2524     // connect the signals in the interface
2525     glade_xml_signal_autoconnect (xml);
2526 
2527     dialog = glade_xml_get_widget (xml, "xvc_save_filechooserdialog");
2528     g_assert (dialog);
2529     gtk_window_set_title (GTK_WINDOW (dialog), "Save Video As:");
2530 
2531     result = gtk_dialog_run (GTK_DIALOG (dialog));
2532 
2533     if (result == GTK_RESPONSE_OK) {
2534         target_file_name =
2535             gtk_file_chooser_get_filename (GTK_FILE_CHOOSER (dialog));
2536 
2537         if (xvc_result_dialog != NULL) {
2538             xml = NULL;
2539             xml = glade_get_widget_tree (GTK_WIDGET (xvc_result_dialog));
2540             g_assert (xml);
2541 
2542             w = NULL;
2543             w = glade_xml_get_widget (xml, "xvc_result_dialog_filename_label");
2544             g_assert (w);
2545 
2546             gtk_label_set_text (GTK_LABEL (w), target_file_name);
2547 
2548             w = NULL;
2549             w = glade_xml_get_widget (xml, "xvc_result_dialog_save_button");
2550             g_assert (w);
2551             printf ("setting save button sensitive\n");
2552             gtk_widget_set_sensitive (GTK_WIDGET (w), TRUE);
2553         }
2554     }
2555 
2556     gtk_widget_destroy (dialog);
2557 
2558 #ifdef DEBUG
2559     printf ("%s %s: Leaving with filename %s\n", DEBUGFILE, DEBUGFUNCTION,
2560             target_file_name);
2561 #endif     // DEBUG
2562 #undef DEBUGFUNCTION
2563 }
2564 
2565 void
on_xvc_ctrl_stop_toggle_toggled(GtkToggleToolButton * button,gpointer user_data)2566 on_xvc_ctrl_stop_toggle_toggled (GtkToggleToolButton * button,
2567                                  gpointer user_data)
2568 {
2569 #define DEBUGFUNCTION "on_xvc_ctrl_stop_toggle_toggled()"
2570     XVC_AppData *app = xvc_appdata_ptr ();
2571 
2572 #ifdef DEBUG
2573     printf ("%s %s: stopp button toggled (%i)\n", DEBUGFILE, DEBUGFUNCTION,
2574             gtk_toggle_tool_button_get_active (GTK_TOGGLE_TOOL_BUTTON
2575                                                (button)));
2576 #endif     // DEBUG
2577 
2578     if (gtk_toggle_tool_button_get_active (GTK_TOGGLE_TOOL_BUTTON (button))) {
2579         if (app->recording_thread_running)
2580             xvc_capture_stop_signal (FALSE);
2581         else {
2582             GladeXML *xml = NULL;
2583             GtkWidget *w = NULL;
2584 
2585             xml = glade_get_widget_tree (GTK_WIDGET (xvc_ctrl_main_window));
2586             g_assert (xml);
2587             w = glade_xml_get_widget (xml, "xvc_ctrl_pause_toggle");
2588             g_assert (w);
2589             if (gtk_toggle_tool_button_get_active (GTK_TOGGLE_TOOL_BUTTON (w)))
2590                 gtk_toggle_tool_button_set_active (GTK_TOGGLE_TOOL_BUTTON
2591                                                    (w), FALSE);
2592         }
2593     } else {
2594         // empty
2595     }
2596 #undef DEBUGFUNCTION
2597 }
2598 
2599 void
on_xvc_ctrl_record_toggle_toggled(GtkToggleToolButton * button,gpointer user_data)2600 on_xvc_ctrl_record_toggle_toggled (GtkToggleToolButton * button,
2601                                    gpointer user_data)
2602 {
2603 #define DEBUGFUNCTION "on_xvc_ctrl_record_toggle_toggled()"
2604 
2605     if (gtk_toggle_tool_button_get_active (GTK_TOGGLE_TOOL_BUTTON (button))) {
2606         xvc_capture_start ();
2607     } else {
2608         // empty
2609     }
2610 #undef DEBUGFUNCTION
2611 }
2612 
2613 void
on_xvc_ctrl_pause_toggle_toggled(GtkToggleToolButton * button,gpointer user_data)2614 on_xvc_ctrl_pause_toggle_toggled (GtkToggleToolButton * button,
2615                                   gpointer user_data)
2616 {
2617 #define DEBUGFUNCTION "on_xvc_ctrl_pause_toggle_toggled()"
2618     struct timeval curr_time;
2619     GladeXML *xml = NULL;
2620     GtkWidget *w = NULL;
2621     Job *jobp = xvc_job_ptr ();
2622     XVC_CapTypeOptions *target = NULL;
2623     XVC_AppData *app = xvc_appdata_ptr ();
2624 
2625 #ifdef USE_FFMPEG
2626     if (app->current_mode > 0)
2627         target = &(app->multi_frame);
2628     else
2629 #endif     // USE_FFMPEG
2630         target = &(app->single_frame);
2631 
2632     xml = glade_get_widget_tree (GTK_WIDGET (xvc_ctrl_main_window));
2633     g_assert (xml);
2634 
2635 #ifdef DEBUG
2636     printf ("%s %s: is paused? (%d)\n", DEBUGFILE, DEBUGFUNCTION,
2637             gtk_toggle_tool_button_get_active (GTK_TOGGLE_TOOL_BUTTON
2638                                                (button)));
2639 #endif
2640 
2641     if (gtk_toggle_tool_button_get_active (GTK_TOGGLE_TOOL_BUTTON (button))) {
2642         gettimeofday (&curr_time, NULL);
2643         pause_time = curr_time.tv_sec * 1000 + curr_time.tv_usec / 1000;
2644         if ((jobp->state & VC_REC) != 0) {
2645             time_captured += (pause_time - start_time);
2646         } else {
2647             time_captured = 0;
2648         }
2649         // stop timer handling only if max_time is configured
2650         if (target->time != 0) {
2651             if (stop_timer_id)
2652                 g_source_remove (stop_timer_id);
2653         }
2654         xvc_job_merge_and_remove_state (VC_PAUSE, VC_STOP);
2655 
2656         // GUI stuff
2657         w = glade_xml_get_widget (xml, "xvc_ctrl_stop_toggle");
2658         g_assert (w);
2659         gtk_toggle_tool_button_set_active (GTK_TOGGLE_TOOL_BUTTON (w), FALSE);
2660 
2661         if (app->current_mode == 0 && jobp->state & VC_REC) {
2662             if (target->frames == 0
2663                 || jobp->pic_no <= (target->frames - target->step)) {
2664                 w = glade_xml_get_widget (xml, "xvc_ctrl_step_button");
2665                 g_assert (w);
2666                 gtk_widget_set_sensitive (GTK_WIDGET (w), TRUE);
2667 
2668                 w = glade_xml_get_widget (xml, "xvc_ctrl_forward_button");
2669                 g_assert (w);
2670                 gtk_widget_set_sensitive (GTK_WIDGET (w), TRUE);
2671             }
2672 
2673             if (jobp->pic_no >= target->step) {
2674                 w = glade_xml_get_widget (xml, "xvc_ctrl_back_button");
2675                 g_assert (w);
2676                 gtk_widget_set_sensitive (GTK_WIDGET (w), TRUE);
2677             }
2678 
2679             w = glade_xml_get_widget (xml, "xvc_ctrl_edit_button");
2680             g_assert (w);
2681             gtk_widget_set_sensitive (GTK_WIDGET (w), TRUE);
2682         }
2683     } else {
2684         xvc_job_remove_state (VC_PAUSE | VC_STEP);
2685         // step is always only active if a running recording session is
2686         // paused
2687         // so releasing pause can always deactivate it
2688 
2689         w = glade_xml_get_widget (xml, "xvc_ctrl_step_button");
2690         g_assert (w);
2691         gtk_widget_set_sensitive (GTK_WIDGET (w), FALSE);
2692         // the following only when recording is going on (not when just
2693         // pressing and
2694         // releasing pause
2695         if (jobp->state & VC_REC) {
2696             w = glade_xml_get_widget (xml, "xvc_ctrl_forward_button");
2697             g_assert (w);
2698             gtk_widget_set_sensitive (GTK_WIDGET (w), FALSE);
2699 
2700             w = glade_xml_get_widget (xml, "xvc_ctrl_back_button");
2701             g_assert (w);
2702             gtk_widget_set_sensitive (GTK_WIDGET (w), FALSE);
2703 
2704             w = glade_xml_get_widget (xml, "xvc_ctrl_edit_button");
2705             g_assert (w);
2706             gtk_widget_set_sensitive (GTK_WIDGET (w), FALSE);
2707         } else {
2708             xvc_job_merge_state (VC_STOP);
2709         }
2710 
2711         pause_time = 0;
2712         gettimeofday (&curr_time, NULL);
2713         start_time = curr_time.tv_sec * 1000 + curr_time.tv_usec / 1000;
2714         // restart timer handling only if max_time is configured
2715         if (target->time != 0) {
2716             // install a timer which stops recording
2717             // we need milli secs ..
2718             stop_timer_id = g_timeout_add ((guint32)
2719                                            (target->time * 1000 -
2720                                             time_captured), (GtkFunction)
2721                                            timer_stop_recording, jobp);
2722         }
2723     }
2724 
2725 #undef DEBUGFUNCTION
2726 }
2727 
2728 void
on_xvc_ctrl_step_button_clicked(GtkButton * button,gpointer user_data)2729 on_xvc_ctrl_step_button_clicked (GtkButton * button, gpointer user_data)
2730 {
2731 #define DEBUGFUNCTION "on_xvc_ctrl_step_button_clicked()"
2732     GladeXML *xml = NULL;
2733     GtkWidget *w = NULL;
2734     Job *jobp = xvc_job_ptr ();
2735     int pic_no = jobp->pic_no;
2736     XVC_CapTypeOptions *target = NULL;
2737     XVC_AppData *app = xvc_appdata_ptr ();
2738 
2739 #ifdef USE_FFMPEG
2740     if (app->current_mode > 0)
2741         target = &(app->multi_frame);
2742     else
2743 #endif     // USE_FFMPEG
2744         target = &(app->single_frame);
2745 
2746     if (app->current_mode == 0) {
2747         if (!(jobp->state & (VC_PAUSE | VC_REC)))
2748             return;
2749 
2750         xvc_job_merge_state (VC_STEP);
2751 
2752         xml = glade_get_widget_tree (GTK_WIDGET (xvc_ctrl_main_window));
2753         g_assert (xml);
2754 
2755         if (pic_no == 0) {
2756             w = glade_xml_get_widget (xml, "xvc_ctrl_back_button");
2757             g_assert (w);
2758             gtk_widget_set_sensitive (GTK_WIDGET (w), TRUE);
2759         }
2760 
2761         if (target->frames > 0 && pic_no >= (target->frames - target->step)) {
2762             if (pic_no > (target->frames - target->step)) {
2763                 w = glade_xml_get_widget (xml, "xvc_ctrl_step_button");
2764                 g_assert (w);
2765                 gtk_widget_set_sensitive (GTK_WIDGET (w), FALSE);
2766             }
2767 
2768             w = glade_xml_get_widget (xml, "xvc_ctrl_forward_button");
2769             g_assert (w);
2770             gtk_widget_set_sensitive (GTK_WIDGET (w), FALSE);
2771         }
2772     }
2773 #undef DEBUGFUNCTION
2774 }
2775 
2776 void
on_xvc_ctrl_filename_button_clicked(GtkButton * button,gpointer user_data)2777 on_xvc_ctrl_filename_button_clicked (GtkButton * button, gpointer user_data)
2778 {
2779 #define DEBUGFUNCTION "on_xvc_ctrl_filename_button_clicked()"
2780     GladeXML *xml = NULL;
2781     GtkWidget *w = NULL;
2782     Job *jobp = xvc_job_ptr ();
2783     XVC_CapTypeOptions *target = NULL;
2784     XVC_AppData *app = xvc_appdata_ptr ();
2785 
2786 #ifdef USE_FFMPEG
2787     if (app->current_mode > 0)
2788         target = &(app->multi_frame);
2789     else
2790 #endif     // USE_FFMPEG
2791         target = &(app->single_frame);
2792 
2793     if (app->current_mode == 0) {
2794         if (jobp->pic_no != target->start_no
2795             && ((jobp->state & VC_STOP) > 0 || (jobp->state & VC_PAUSE) > 0)) {
2796             xml = glade_get_widget_tree (GTK_WIDGET (xvc_ctrl_main_window));
2797             g_assert (xml);
2798 
2799             w = glade_xml_get_widget (xml, "xvc_ctrl_back_button");
2800             g_assert (w);
2801             gtk_widget_set_sensitive (GTK_WIDGET (w), FALSE);
2802 
2803             jobp->pic_no = target->start_no;
2804             GtkChangeLabel (jobp->pic_no);
2805         }
2806     } else {
2807         if (jobp->movie_no != 0 && (jobp->state & VC_STOP) > 0) {
2808             jobp->movie_no = 0;
2809             jobp->pic_no = target->start_no;
2810             GtkChangeLabel (jobp->pic_no);
2811         }
2812     }
2813 #undef DEBUGFUNCTION
2814 }
2815 
2816 void
on_xvc_ctrl_back_button_clicked(GtkButton * button,gpointer user_data)2817 on_xvc_ctrl_back_button_clicked (GtkButton * button, gpointer user_data)
2818 {
2819 #define DEBUGFUNCTION "on_xvc_ctrl_back_button_clicked()"
2820     GladeXML *xml = NULL;
2821     GtkWidget *w = NULL;
2822     Job *jobp = xvc_job_ptr ();
2823     XVC_CapTypeOptions *target = NULL;
2824     XVC_AppData *app = xvc_appdata_ptr ();
2825 
2826 #ifdef USE_FFMPEG
2827     if (app->current_mode > 0)
2828         target = &(app->multi_frame);
2829     else
2830 #endif     // USE_FFMPEG
2831         target = &(app->single_frame);
2832 
2833     if (app->current_mode == 0) {
2834         if (jobp->pic_no >= target->step) {
2835             jobp->pic_no -= target->step;
2836             GtkChangeLabel (jobp->pic_no);
2837         } else {
2838             fprintf (stderr,
2839                      "%s %s: back button active although picture number < step. this should never happen\n",
2840                      DEBUGFILE, DEBUGFUNCTION);
2841         }
2842         if (jobp->pic_no < target->step)
2843             gtk_widget_set_sensitive (GTK_WIDGET (button), FALSE);
2844 
2845         if (target->frames == 0
2846             || jobp->pic_no == (target->frames - target->step)) {
2847             xml = glade_get_widget_tree (GTK_WIDGET (xvc_ctrl_main_window));
2848             g_assert (xml);
2849 
2850             if (jobp->state & VC_PAUSE) {
2851                 w = glade_xml_get_widget (xml, "xvc_ctrl_step_button");
2852                 g_assert (w);
2853                 if (app->current_mode == 0)
2854                     gtk_widget_set_sensitive (GTK_WIDGET (w), TRUE);
2855             }
2856 
2857             w = glade_xml_get_widget (xml, "xvc_ctrl_forward_button");
2858             g_assert (w);
2859             gtk_widget_set_sensitive (GTK_WIDGET (w), TRUE);
2860         }
2861     } else {
2862         if (jobp->movie_no > 0) {
2863             jobp->movie_no -= 1;
2864             GtkChangeLabel (jobp->pic_no);
2865         } else {
2866             fprintf (stderr,
2867                      "%s %s: back button active although movie number == 0. this should never happen\n",
2868                      DEBUGFILE, DEBUGFUNCTION);
2869         }
2870         if (jobp->movie_no == 0)
2871             gtk_widget_set_sensitive (GTK_WIDGET (button), FALSE);
2872     }
2873 #undef DEBUGFUNCTION
2874 }
2875 
2876 void
on_xvc_ctrl_forward_button_clicked(GtkButton * button,gpointer user_data)2877 on_xvc_ctrl_forward_button_clicked (GtkButton * button, gpointer user_data)
2878 {
2879 #define DEBUGFUNCTION "on_xvc_ctrl_forward_button_clicked()"
2880     GladeXML *xml = NULL;
2881     GtkWidget *w = NULL;
2882     Job *jobp = xvc_job_ptr ();
2883     XVC_CapTypeOptions *target = NULL;
2884     XVC_AppData *app = xvc_appdata_ptr ();
2885 
2886 #ifdef USE_FFMPEG
2887     if (app->current_mode > 0)
2888         target = &(app->multi_frame);
2889     else
2890 #endif     // USE_FFMPEG
2891         target = &(app->single_frame);
2892 
2893     if (app->current_mode == 0) {
2894         jobp->pic_no += target->step;
2895         GtkChangeLabel (jobp->pic_no);
2896 
2897         xml = glade_get_widget_tree (GTK_WIDGET (xvc_ctrl_main_window));
2898         g_assert (xml);
2899 
2900         if (jobp->pic_no == target->step) {
2901             w = glade_xml_get_widget (xml, "xvc_ctrl_back_button");
2902             g_assert (w);
2903 
2904             gtk_widget_set_sensitive (GTK_WIDGET (w), TRUE);
2905         }
2906         if (target->frames > 0
2907             && jobp->pic_no > (target->frames - target->step)) {
2908             if (jobp->pic_no >= (target->frames - target->step)) {
2909                 w = glade_xml_get_widget (xml, "xvc_ctrl_step_button");
2910                 g_assert (w);
2911                 gtk_widget_set_sensitive (GTK_WIDGET (w), FALSE);
2912             }
2913 
2914             w = glade_xml_get_widget (xml, "xvc_ctrl_forward_button");
2915             g_assert (w);
2916             gtk_widget_set_sensitive (GTK_WIDGET (w), FALSE);
2917         }
2918 
2919     } else {
2920         jobp->movie_no += 1;
2921         GtkChangeLabel (jobp->pic_no);
2922         if (jobp->movie_no == 1) {
2923             xml = glade_get_widget_tree (GTK_WIDGET (xvc_ctrl_main_window));
2924             g_assert (xml);
2925 
2926             w = glade_xml_get_widget (xml, "xvc_ctrl_back_button");
2927             g_assert (w);
2928 
2929             gtk_widget_set_sensitive (GTK_WIDGET (w), TRUE);
2930         }
2931     }
2932 #undef DEBUGFUNCTION
2933 }
2934 
2935 void
on_xvc_ctrl_lock_toggle_toggled(GtkToggleToolButton * togglebutton,gpointer user_data)2936 on_xvc_ctrl_lock_toggle_toggled (GtkToggleToolButton *
2937                                  togglebutton, gpointer user_data)
2938 {
2939 #define DEBUGFUNCTION "on_xvc_ctrl_lock_toggle_toggled()"
2940     GtkTooltips *tooltips;
2941 
2942     tooltips = gtk_tooltips_new ();
2943 
2944     if (gtk_toggle_tool_button_get_active (togglebutton)) {
2945         XRectangle *frame_rectangle = NULL;
2946         GdkRectangle ctrl_rect;
2947         int x, y, height;
2948 
2949         xvc_set_frame_locked (1);      /* button pressed = move frame with
2950                                         * control */
2951         gtk_tool_item_set_tooltip (GTK_TOOL_ITEM (togglebutton), tooltips,
2952                                    _("Detach selection frame"),
2953                                    _("Detach selection frame"));
2954 
2955 /*
2956         gtk_window_set_gravity (GTK_WINDOW (xvc_ctrl_main_window),
2957                                 GDK_GRAVITY_NORTH_WEST);
2958         gtk_window_get_position (GTK_WINDOW (xvc_ctrl_main_window), &x, &y);
2959         gtk_window_get_size (GTK_WINDOW (xvc_ctrl_main_window), &pwidth,
2960                              &pheight);
2961         gtk_window_set_gravity (GTK_WINDOW (xvc_ctrl_main_window),
2962                                 GDK_GRAVITY_STATIC);
2963         gtk_window_get_position (GTK_WINDOW (xvc_ctrl_main_window), &x, &y);
2964         gtk_window_get_size (GTK_WINDOW (xvc_ctrl_main_window), &pwidth,
2965                              &pheight);
2966 */
2967         xvc_get_ctrl_frame_extents(GDK_WINDOW(xvc_ctrl_main_window->window),
2968                 &ctrl_rect);
2969         x = ctrl_rect.x;
2970         y = ctrl_rect.y;
2971         height = ctrl_rect.height;
2972 //        width = ctrl_rect.width;
2973 
2974         if (x < 0)
2975             x = 0;
2976         y += height + FRAME_OFFSET + FRAME_WIDTH;
2977         if (y < 0)
2978             y = 0;
2979         frame_rectangle = xvc_get_capture_area ();
2980         xvc_change_gtk_frame (x, y, frame_rectangle->width,
2981                               frame_rectangle->height, FALSE, FALSE);
2982     } else {
2983         xvc_set_frame_locked (0);
2984         gtk_tool_item_set_tooltip (GTK_TOOL_ITEM (togglebutton), tooltips,
2985                                    _("Attach selection frame"),
2986                                    _("Attach selection frame"));
2987     }
2988 #undef DEBUGFUNCTION
2989 }
2990 
2991 void
on_xvc_ctrl_select_toggle_toggled(GtkToggleToolButton * togglebutton,gpointer user_data)2992 on_xvc_ctrl_select_toggle_toggled (GtkToggleToolButton *
2993                                    togglebutton, gpointer user_data)
2994 {
2995 #define DEBUGFUNCTION "on_xvc_ctrl_select_toggle_toggled()"
2996     Job *jobp = xvc_job_ptr ();
2997     XVC_AppData *app = xvc_appdata_ptr ();
2998 
2999     if (gtk_toggle_tool_button_get_active (togglebutton)) {
3000         Cursor cursor;
3001         Window target_win = None, temp = None;
3002         XEvent event;
3003         int buttons = 0;
3004         int x_down, y_down, x_up, y_up, x, y;
3005         int width, height;
3006         XGCValues gcv;
3007         GC gc;
3008         XVC_CapTypeOptions *target = NULL;
3009         int toggled_frame_on = FALSE;
3010         GladeXML *menuxml = NULL;
3011         GtkWidget *show_frame_mitem = NULL;
3012 
3013 #ifdef USE_FFMPEG
3014         if (app->current_mode > 0)
3015             target = &(app->multi_frame);
3016         else
3017 #endif     // USE_FFMPEG
3018             target = &(app->single_frame);
3019 
3020         g_assert (app->dpy);
3021 
3022         if ((app->flags & FLG_NOFRAME)) {
3023             // if "show_frame" is off we have to switch it
3024             // on before using the non existing frame
3025             menuxml = glade_get_widget_tree (xvc_ctrl_m1);
3026             g_assert (menuxml);
3027 
3028             show_frame_mitem = glade_xml_get_widget (menuxml,
3029                                                      "xvc_ctrl_m1_show_frame");
3030             g_assert (show_frame_mitem);
3031         }
3032 
3033         cursor = XCreateFontCursor (app->dpy, XC_crosshair);
3034 
3035         gcv.background = XBlackPixel (app->dpy, app->default_screen_num);
3036         gcv.foreground = XWhitePixel (app->dpy, app->default_screen_num);
3037         gcv.function = GXinvert;
3038         gcv.plane_mask = gcv.background ^ gcv.foreground;
3039         gcv.subwindow_mode = IncludeInferiors;
3040         gc = XCreateGC (app->dpy, app->root_window,
3041                         GCBackground | GCForeground | GCFunction |
3042                         GCPlaneMask | GCSubwindowMode, &gcv);
3043 
3044         // grab the mouse
3045         //
3046         if (XGrabPointer (app->dpy, app->root_window, False,
3047                           PointerMotionMask | ButtonPressMask |
3048                           ButtonReleaseMask, GrabModeSync,
3049                           GrabModeAsync, app->root_window, cursor, CurrentTime)
3050             != GrabSuccess) {
3051             fprintf (stderr, "%s %s: Can't grab mouse!\n", DEBUGFILE,
3052                      DEBUGFUNCTION);
3053             return;
3054         }
3055         x_down = y_down = x_up = y_up = width = height = 0;
3056 
3057         while (buttons < 2) {
3058             // allow pointer events
3059             XAllowEvents (app->dpy, SyncPointer, CurrentTime);
3060             // search in the queue for button events
3061             XWindowEvent (app->dpy, app->root_window,
3062                           PointerMotionMask | ButtonPressMask |
3063                           ButtonReleaseMask, &event);
3064             switch (event.type) {
3065             case ButtonPress:
3066                 x_down = event.xbutton.x;
3067                 y_down = event.xbutton.y;
3068                 target_win = event.xbutton.subwindow;   // window selected
3069                 //
3070                 if (target_win == None) {
3071                     target_win = app->root_window;
3072                 }
3073                 buttons++;
3074 
3075                 // in case the frame is off, set it on and store the fact that
3076                 // we did so
3077                 if ((app->flags & FLG_NOFRAME)) {
3078                     app->area->x = x_down;
3079                     app->area->y = y_down;
3080                     app->area->width = app->area->height = 0;
3081 
3082                     gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM
3083                                                     (show_frame_mitem), TRUE);
3084                     while (gtk_events_pending ()) {
3085                         gtk_main_iteration ();
3086                     }
3087                     toggled_frame_on = TRUE;
3088                 } else {
3089                     xvc_destroy_gtk_frame ();
3090                     xvc_create_gtk_frame (xvc_ctrl_main_window, 0, 0,
3091                                           x_down, y_down);
3092                     while (gtk_events_pending ()) {
3093                         gtk_main_iteration ();
3094                     }
3095                 }
3096                 break;
3097             case ButtonRelease:
3098                 x_up = event.xbutton.x;
3099                 y_up = event.xbutton.y;
3100                 buttons++;
3101                 break;
3102             default:
3103                 // motion notify
3104                 if (buttons == 1) {
3105                     // button is pressed
3106                     if (x_down > event.xbutton.x) {
3107                         width = x_down - event.xbutton.x + 1;
3108                         x = event.xbutton.x;
3109                     } else {
3110                         width = event.xbutton.x - x_down + 1;
3111                         x = x_down;
3112                     }
3113                     if (y_down > event.xbutton.y) {
3114                         height = y_down - event.xbutton.y + 1;
3115                         y = event.xbutton.y;
3116                     } else {
3117                         height = event.xbutton.y - y_down + 1;
3118                         y = y_down;
3119                     }
3120                     xvc_change_gtk_frame (x, y, width, height, FALSE, TRUE);
3121                     // the previous call changes the frame edges, which are
3122                     // gtk windows. we need to call the gtk main loop for
3123                     // them to be drawn properly within this callback
3124                     while (gtk_events_pending ())
3125                         gtk_main_iteration ();
3126                 }
3127                 break;
3128             }
3129         }
3130 
3131         XUngrabPointer (app->dpy, CurrentTime); // Done with pointer
3132 
3133         XFreeCursor (app->dpy, cursor);
3134         XFreeGC (app->dpy, gc);
3135 
3136         if ((x_down != x_up) && (y_down != y_up)) {
3137             // an individual frame was selected
3138             if (x_down > x_up) {
3139                 width = x_down - x_up + 1;
3140                 x = x_up;
3141             } else {
3142                 width = x_up - x_down + 1;
3143                 x = x_down;
3144             }
3145             if (y_down > y_up) {
3146                 height = y_down - y_up + 1;
3147                 y = y_up;
3148             } else {
3149                 height = y_up - y_down + 1;
3150                 y = y_down;
3151             }
3152             xvc_appdata_set_window_attributes (target_win);
3153         } else {
3154             if (target_win != app->root_window) {
3155                 // get the real window
3156                 target_win = XmuClientWindow (app->dpy, target_win);
3157             }
3158             xvc_appdata_set_window_attributes (target_win);
3159             XTranslateCoordinates (app->dpy, target_win, app->root_window, 0, 0,
3160                                    &x, &y, &temp);
3161             width = app->win_attr.width;
3162             height = app->win_attr.height;
3163         }
3164 
3165         xvc_change_gtk_frame (x, y, width,
3166                               height, xvc_is_frame_locked (), FALSE);
3167 
3168         // update colors and colormap
3169         xvc_job_set_colors ();
3170 
3171         if (app->flags & FLG_RUN_VERBOSE) {
3172             fprintf (stderr, "%s %s: color_table first entry: 0x%.8X\n",
3173                      DEBUGFILE, DEBUGFUNCTION,
3174                      *(u_int32_t *) jobp->color_table);
3175         }
3176         // in case the frame is off, set it on and store the fact that
3177         // we did so
3178         if (toggled_frame_on) {
3179             gtk_check_menu_item_set_active (GTK_CHECK_MENU_ITEM
3180                                             (show_frame_mitem), FALSE);
3181             while (gtk_events_pending ()) {
3182                 gtk_main_iteration ();
3183             }
3184         }
3185 
3186         xvc_job_set_save_function (jobp->target);
3187 
3188 #ifdef DEBUG
3189         printf ("%s%s: new visual: %d\n", DEBUGFILE, DEBUGFUNCTION,
3190                 app->win_attr.visual->class);
3191 #endif
3192 
3193     }
3194     gtk_toggle_tool_button_set_active (togglebutton, FALSE);
3195 
3196 #undef DEBUGFUNCTION
3197 }
3198 
3199 void
on_xvc_ctrl_m1_mitem_animate_activate(GtkButton * button,gpointer user_data)3200 on_xvc_ctrl_m1_mitem_animate_activate (GtkButton * button, gpointer user_data)
3201 {
3202 #define DEBUGFUNCTION "on_xvc_ctrl_m1_mitem_animate_activate()"
3203     Job *jobp = xvc_job_ptr ();
3204     XVC_CapTypeOptions *target = NULL;
3205     XVC_AppData *app = xvc_appdata_ptr ();
3206 
3207 #ifdef USE_FFMPEG
3208     if (app->current_mode > 0)
3209         target = &(app->multi_frame);
3210     else
3211 #endif     // USE_FFMPEG
3212         target = &(app->single_frame);
3213 
3214     if (!app->current_mode) {
3215         xvc_command_execute (target->play_cmd, 1, 0,
3216                              jobp->file, target->start_no, jobp->pic_no,
3217                              app->area->width, app->area->height, target->fps);
3218     } else {
3219         xvc_command_execute (target->play_cmd, 2,
3220                              jobp->movie_no, jobp->file, target->start_no,
3221                              jobp->pic_no, app->area->width,
3222                              app->area->height, target->fps);
3223     }
3224 #undef DEBUGFUNCTION
3225 }
3226 
3227 void
on_xvc_ctrl_edit_button_clicked(GtkToolButton * button,gpointer user_data)3228 on_xvc_ctrl_edit_button_clicked (GtkToolButton * button, gpointer user_data)
3229 {
3230 #define DEBUGFUNCTION "on_xvc_ctrl_edit_button_clicked()"
3231     Job *jobp = xvc_job_ptr ();
3232     XVC_CapTypeOptions *target = NULL;
3233     XVC_AppData *app = xvc_appdata_ptr ();
3234 
3235 #ifdef USE_FFMPEG
3236     if (app->current_mode > 0)
3237         target = &(app->multi_frame);
3238     else
3239 #endif     // USE_FFMPEG
3240         target = &(app->single_frame);
3241 
3242     if (!app->current_mode) {
3243         xvc_command_execute (target->edit_cmd, 2,
3244                              jobp->pic_no, jobp->file, target->start_no,
3245                              jobp->pic_no, app->area->width,
3246                              app->area->height, target->fps);
3247     } else {
3248         xvc_command_execute (target->edit_cmd, 2,
3249                              jobp->movie_no, jobp->file, target->start_no,
3250                              jobp->pic_no, app->area->width,
3251                              app->area->height, target->fps);
3252     }
3253 #undef DEBUGFUNCTION
3254 }
3255 
3256 gint
on_xvc_ctrl_filename_button_button_press_event(GtkWidget * widget,GdkEvent * event)3257 on_xvc_ctrl_filename_button_button_press_event (GtkWidget * widget,
3258                                                 GdkEvent * event)
3259 {
3260 #define DEBUGFUNCTION "on_xvc_ctrl_filename_button_press_event()"
3261     gboolean is_sensitive = FALSE;
3262     GdkEventButton *bevent = NULL;
3263 
3264     g_assert (widget);
3265     g_assert (event);
3266     bevent = (GdkEventButton *) event;
3267 
3268     // FIXME: changing from menu sensitive or not to button sensitive or
3269     // not ... make sure that that's what I set elsewhere, too.
3270     g_object_get ((gpointer) widget, (gchar *) "sensitive", &is_sensitive,
3271                   NULL);
3272     if (bevent->button == (guint) 3 && is_sensitive == TRUE) {
3273 
3274         g_assert (xvc_ctrl_m1);
3275 
3276         gtk_menu_popup (GTK_MENU (xvc_ctrl_m1), NULL, NULL,
3277                         position_popup_menu, widget, bevent->button,
3278                         bevent->time);
3279         // Tell calling code that we have handled this event; the buck
3280         // stops here.
3281         return TRUE;
3282     }
3283     // Tell calling code that we have not handled this event; pass it on.
3284     return FALSE;
3285 #undef DEBUGFUNCTION
3286 }
3287 
3288 // this handles the shortcut keybindings for the menu
3289 gint
on_xvc_ctrl_main_window_key_press_event(GtkWidget * widget,GdkEvent * event)3290 on_xvc_ctrl_main_window_key_press_event (GtkWidget * widget, GdkEvent * event)
3291 {
3292 #define DEBUGFUNCTION "on_xvc_ctrl_main_window_key_press_event()"
3293     gboolean is_sensitive = FALSE;
3294     GtkWidget *button = NULL, *mitem = NULL;
3295     GdkEventKey *kevent = NULL;
3296     GladeXML *xml = NULL;
3297 
3298     g_assert (widget);
3299     g_assert (event);
3300     kevent = (GdkEventKey *) event;
3301 
3302 #ifdef DEBUG
3303     printf ("%s %s: Entering\n", DEBUGFILE, DEBUGFUNCTION);
3304 #endif     // DEBUG
3305 
3306     xml = glade_get_widget_tree (xvc_ctrl_main_window);
3307     g_assert (xml);
3308     button = glade_xml_get_widget (xml, "xvc_ctrl_filename_button");
3309     g_assert (button);
3310 
3311     g_object_get ((gpointer) button, (gchar *) "sensitive", &is_sensitive,
3312                   NULL);
3313 
3314     if (kevent->keyval == 112 && kevent->state == 4) {
3315         xml = glade_get_widget_tree (xvc_ctrl_m1);
3316         g_assert (xml);
3317         mitem = glade_xml_get_widget (xml, "xvc_ctrl_m1_mitem_preferences");
3318         g_assert (mitem);
3319         gtk_menu_item_activate (GTK_MENU_ITEM (mitem));
3320         return TRUE;
3321     } else if (kevent->keyval == 113 && kevent->state == 4) {
3322         xml = glade_get_widget_tree (xvc_ctrl_m1);
3323         g_assert (xml);
3324         mitem = glade_xml_get_widget (xml, "xvc_ctrl_m1_mitem_quit");
3325         g_assert (mitem);
3326         gtk_menu_item_activate (GTK_MENU_ITEM (mitem));
3327         return TRUE;
3328     } else if (kevent->keyval == 65470
3329                || (kevent->state == 4 && kevent->keyval == 104)) {
3330         xml = glade_get_widget_tree (xvc_ctrl_m1);
3331         g_assert (xml);
3332         mitem = glade_xml_get_widget (xml, "xvc_ctrl_m1_mitem_help");
3333         g_assert (mitem);
3334         gtk_menu_item_activate (GTK_MENU_ITEM (mitem));
3335         return TRUE;
3336     }
3337 #ifdef DEBUG
3338     printf ("%s %s: Leaving\n", DEBUGFILE, DEBUGFUNCTION);
3339 #endif     // DEBUG
3340 
3341     // Tell calling code that we have not handled this event; pass it on.
3342     return FALSE;
3343 #undef DEBUGFUNCTION
3344 }
3345 
3346 #if GTK_CHECK_VERSION(2, 6, 0)
3347 void
on_xvc_about_main_window_close(GtkAboutDialog * window,gpointer user_data)3348 on_xvc_about_main_window_close (GtkAboutDialog * window, gpointer user_data)
3349 {
3350     gtk_widget_destroy (GTK_WIDGET (window));
3351 }
3352 #endif     // GTK_CHECK_VERSION
3353 
3354 void
on_xvc_ctrl_m1_mitem_about_activate(GtkMenuItem * menuitem,gpointer user_data)3355 on_xvc_ctrl_m1_mitem_about_activate (GtkMenuItem * menuitem, gpointer user_data)
3356 {
3357     GladeXML *xml = NULL;
3358 
3359     // load the interface
3360     xml = glade_xml_new (XVC_GLADE_FILE, "xvc_about_main_window", NULL);
3361     g_assert (xml);
3362     // connect the signals in the interface
3363     glade_xml_signal_autoconnect (xml);
3364 }
3365 
3366 void
on_xvc_ti_stop_selected(GtkMenuItem * menuitem,gpointer user_data)3367 on_xvc_ti_stop_selected (GtkMenuItem * menuitem, gpointer user_data)
3368 {
3369     GtkWidget *button = NULL;
3370     GladeXML *xml = NULL;
3371 
3372     xml = glade_get_widget_tree (xvc_ctrl_main_window);
3373     g_assert (xml);
3374     button = glade_xml_get_widget (xml, "xvc_ctrl_stop_toggle");
3375     g_assert (button);
3376 
3377     gtk_toggle_tool_button_set_active (GTK_TOGGLE_TOOL_BUTTON (button), TRUE);
3378 }
3379 
3380 void
on_xvc_ti_pause_selected(GtkMenuItem * menuitem,gpointer user_data)3381 on_xvc_ti_pause_selected (GtkMenuItem * menuitem, gpointer user_data)
3382 {
3383     GtkWidget *button = NULL;
3384     GladeXML *xml = NULL;
3385 
3386     xml = glade_get_widget_tree (xvc_ctrl_main_window);
3387     g_assert (xml);
3388     button = glade_xml_get_widget (xml, "xvc_ctrl_pause_toggle");
3389     g_assert (button);
3390 
3391     gtk_toggle_tool_button_set_active (GTK_TOGGLE_TOOL_BUTTON (button),
3392                                        gtk_check_menu_item_get_active
3393                                        (GTK_CHECK_MENU_ITEM (menuitem)));
3394 }
3395 
3396 #endif     // DOXYGEN_SHOULD_SKIP_THIS
3397