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