1 /* gjackclock - jack transport big clock
2 * Copyright (C) 2010 Robin Gareus <robin@gareus.org>
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version.
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software
16 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
27 #include <gdk/gdkkeysyms.h>
30 #include <jack/jack.h>
31 #include <jack/transport.h>
33 #include "framerate.h"
34 #include "gjackclock.h"
37 #define _(X) gettext(X)
42 static GdkPixmap
*gjc_icon_pixmap
;
43 static GdkBitmap
*gjc_icon_mask
;
44 #include "gjc_icon.xpm"
47 GtkWidget
*labelSMPTE
[12];
48 GtkWidget
*label1
= NULL
;
53 /* command-line args */
58 char *font_family
= NULL
;
61 int want_ignore_timemaster
= 0;
64 char *color_bg
= NULL
;
65 char *color_fg
= NULL
;
66 int use_table_layout
= 0;
69 gint key_cfg
[MAX_KEYBINDING
] = { GDK_space
, GDK_v
, GDK_n
, GDK_b
, GDK_p
, GDK_r
, GDK_less
, GDK_greater
};
72 /*****************************************************************************
76 jack_client_t
*jack_client
= NULL
;
77 jack_nframes_t j_srate
= 48000;
80 /* when jack shuts down... */
81 void jack_shutdown(void *arg
) {
85 int jack_connected(void) {
86 if (jack_client
) return (1);
90 void open_jack(void ) {
93 fprintf (stderr
, "already connected to jack..\n");
99 snprintf(jackid
,16,"gjackclock-%i",i
);
100 jack_client
= jack_client_open (jackid
, JackUseExactName
, NULL
);
101 } while (jack_client
== 0 && i
++<16);
104 fprintf(stderr
, "could not connect to jack server.\n");
106 jack_on_shutdown (jack_client
, jack_shutdown
, 0);
108 fprintf(stderr
, "connected as jack client '%s'\n",jackid
);
109 jack_activate (jack_client
);
113 void close_jack(void) {
115 jack_deactivate (jack_client
);
116 jack_client_close (jack_client
);
118 fprintf(stderr
, "disconnected from jack.\n");
123 void jack_stop(void) {
125 jack_transport_stop (jack_client
);
129 void jack_play(void) {
131 jack_transport_start (jack_client
);
135 int jack_poll_transport_state (void) {
137 switch (jack_transport_query(jack_client
, NULL
)) {
138 case JackTransportRolling
:
140 case JackTransportStopped
:
148 long long int jack_poll_time (void) {
149 jack_position_t jack_position
;
150 if (!jack_client
) return (-1);
152 jack_transport_query(jack_client
, &jack_position
);
153 FR_setsamplerate(fr
, jack_position
.frame_rate
);
155 if (jack_position
.valid
&JackAudioVideoRatio
&& !want_ignore_timemaster
) {
156 double jfps
= jack_position
.frame_rate
/ (double) jack_position
.audio_frames_per_video_frame
;
157 FR_setdbl(fr
,jfps
,1);
159 return (jack_position
.frame
);
162 void jack_reposition(jack_nframes_t f
) {
163 if (!jack_client
) return;
164 jack_transport_locate (jack_client
, f
);
167 void jack_relative_reposition(double off
) {
168 if (!jack_client
) return;
169 long long int frame
= jack_poll_time();
170 jack_nframes_t f
= 0;
171 frame
+= off
* fr
->samplerate
;
172 if (frame
> 0) f
= frame
;
176 /*****************************************************************************/
178 static void usage (int status
) {
179 printf("%s - JACK transport big clock.\n",program_name
);
180 printf("usage: %s [Options]\n",program_name
);
183 " -a, --ontop start 'always-on-top' of other windows\n"
184 " -C <color> background-color ('#RRGGBB' or name eg 'black')\n"
185 " -c <color> text-color ('#RRGGBB' or name eg 'blue')\n"
186 " -d <sec> set seek-distance (default 10.0 sec)\n"
187 " -f <FPS>, --fps <FPS> set timecode-fps (default 25 or timebase-master)\n"
188 " -h, --help display this help and exit\n"
189 " -I, --ignore-timemaster ignore JACK-timebase-master FPS setting\n"
191 " --config <file> load configuration from <file>\n"
192 " -N, --no-scale do not scale font when resizing the window\n"
193 " -n, --no-fps-display hide FPS info from display\n"
194 " -q, --quiet, --silent inhibit usual output\n"
195 " -S <FONT-Family> default 'Monospace'\n"
196 " -s <FONT-size> default 60\n"
197 " -t, --table-layout render time in equally spaced table (use with\n"
198 " variable width fonts)\n"
199 " -V, --version print version information and exit\n"
200 " -w <XOFFxYOFF> set window position (eg '100x200')\n"
203 " * use -f 29.97 for drop-frame timecode\n"
204 " * default key-bindings (override with ~/.gjackclockrc)\n"
205 " 'space' - toggle play/pause\n"
209 " '<' - skip backward\n"
210 " '>' - skip forward\n"
211 " CTRL-[q|w] - quit\n"
217 static void printversion (void) {
218 printf ("gjackclock %s\n (C) GPL 2010 Robin Gareus <robin@gareus.org>\n", VERSION
);
221 static struct option
const long_options
[] =
223 {"help", no_argument
, 0, 'h'},
224 {"quiet", no_argument
, 0, 'q'},
225 {"silent", no_argument
, 0, 'q'},
226 {"fps", required_argument
, 0, 'f'},
227 {"ignore-timemaster", no_argument
, 0, 'I'},
228 {"font-size", required_argument
, 0, 's'},
229 {"font-family", required_argument
, 0, 'S'},
230 {"window-position", required_argument
, 0, 'w'},
231 {"no-fps-display", no_argument
, 0, 'n'},
232 {"no-scale", no_argument
, 0, 'N'},
233 {"ontop", no_argument
, 0, 'a'},
234 {"table-layout", no_argument
, 0, 't'},
235 {"version", no_argument
, 0, 'V'},
236 {"config", required_argument
, 0, 'l'},
241 decode_switches (int argc
, char **argv
)
244 while ((c
= getopt_long (argc
, argv
,
245 "q" /* quiet or silent */
249 "S:" /* font-family */
251 "w:" /* window-pos */
254 "d:" /* stride-dist */
255 "I" /* ignore time-master */
256 "t" /* table-layout */
258 "n" /* no-fps-display */
259 "l:" /* config file */
261 long_options
, (int *) 0)) != EOF
)
268 want_ignore_timemaster
= 1;
271 user_fps
= atof(optarg
);
274 font_size
= atoi (optarg
);
275 if (font_size
< 6 || font_size
> 500) font_size
= 60;
278 if (font_family
) free(font_family
);
279 font_family
= strdup(optarg
);
282 if (color_fg
) free(color_fg
);
283 color_fg
= strdup(optarg
);
286 if (color_bg
) free(color_bg
);
287 color_bg
= strdup(optarg
);
289 case 'd': // XXX 'd' ??
290 stride
= atof(optarg
);
291 if (stride
<=0) stride
= 10.0;
294 if (win_pos
) free(win_pos
);
295 win_pos
= strdup(optarg
);
298 if (rc_file
) free(rc_file
);
299 rc_file
= strdup(optarg
);
311 use_table_layout
= 1;
319 usage (EXIT_FAILURE
);
325 /*****************************************************************************/
326 void set_smpte_font (char *font
) {
327 if (use_table_layout
) {
330 gtk_widget_modify_font (labelSMPTE
[i
], pango_font_description_from_string (font
));
333 gtk_widget_modify_font (label1
, pango_font_description_from_string (font
));
337 void set_smpte_text (gchar
*txt
) {
338 if (use_table_layout
) {
344 if (i
<len
) st
[0]= txt
[i
];
345 gtk_label_set_text(GTK_LABEL(labelSMPTE
[i
]), st
);
348 gtk_label_set_text(GTK_LABEL(label1
),txt
);
352 gboolean
gpolljack (gpointer data
) {
355 long long int jt
= jack_poll_time();
356 if (jt
< 0 ) { open_jack(); return (TRUE
);}
358 long int frame
= FR_af2vfi(fr
, jt
);
359 if (frame
< 0) snprintf(text
, 16 , "--:--:--.--");
360 else FR_vf2smpte(fr
, text
, frame
);
361 set_smpte_text(text
);
363 snprintf(fpstxt
,16,"%.2f", FR_todbl(fr
));
364 gtk_label_set_text(GTK_LABEL(label2
),fpstxt
);
368 /*****************************************************************************/
370 on_key_press_event (GtkWidget
*widget
,
375 if(event
->keyval
==GDK_Escape
) return (rv
);
377 if(event
->state
&GDK_CONTROL_MASK
&& (event
->keyval
==GDK_q
|| event
->keyval
==GDK_w
)) {
381 if(event
->keyval
==key_cfg
[0]) {
382 int val
= jack_poll_transport_state();
383 if (val
&1) jack_stop();
384 else if (val
&2) jack_play();
386 } else if(event
->keyval
==key_cfg
[3]) {
389 } else if(event
->keyval
==key_cfg
[4]) {
392 } else if(event
->keyval
==key_cfg
[5]) {
395 } else if(event
->keyval
==key_cfg
[6]) {
396 jack_relative_reposition(-1.0*stride
);
398 } else if(event
->keyval
==key_cfg
[7]) {
399 jack_relative_reposition(stride
);
404 printf("KEY key:%x state:%x\n", event
->keyval
, event
->state
);
411 gint orig_win_width
= -1;
412 gint orig_win_height
= -1;
413 #define MINIMUM(a,b) ((a>b)?(b):(a))
414 /* concept: see 'idle_big_clock_text_resizer() in gtk2_ardour/ardour_ui_ed.cc
415 * Thanks to Paul Davis */
416 gboolean
gfontresize (gpointer data
) {
417 static gint win_width
= -1;
418 static gint win_height
= -1;
419 static int cur_font_size
= -1;
421 gint my_width
, my_height
;
422 gtk_window_get_size (GTK_WINDOW(window1
), &my_width
, &my_height
);
424 if (win_width
< 0 || win_height
< 0 ) {
425 win_width
= my_width
;
426 win_height
= my_height
;
429 if (win_width
== my_width
&& win_height
== my_height
) {
433 double scale
= MINIMUM((double) (my_width
-vboxw
) / (double) (orig_win_width
-vboxw
),
434 (double) my_height
/ (double) orig_win_height
);
435 int my_font_size
= floor((double) font_size
* scale
);
436 if (my_font_size
< 4) my_font_size
=4;
437 if (cur_font_size
== my_font_size
) return TRUE
;
439 snprintf(font
, 1024, "%s %i",font_family
?font_family
:"Monospace", my_font_size
);
440 set_smpte_font(font
);
441 cur_font_size
= my_font_size
;
445 /*****************************************************************************/
448 int main (int argc
, char *argv
[]) {
450 GtkWidget
*table1
= NULL
;
451 GtkWidget
*hbox1
, *vbox1
, *vbox2
;
452 GtkWidget
*alignment1
;
456 program_name
= argv
[0];
459 bindtextdomain (GETTEXT_PACKAGE
, PACKAGE_LOCALE_DIR
);
460 bind_textdomain_codeset (GETTEXT_PACKAGE
, "UTF-8");
461 textdomain (GETTEXT_PACKAGE
);
464 gtk_init(&argc
, &argv
);
466 window1
= gtk_window_new (GTK_WINDOW_TOPLEVEL
);
467 gtk_window_set_title (GTK_WINDOW (window1
), _("gjackclock"));
469 hbox1
= gtk_hbox_new (FALSE
, 8);
470 vbox1
= gtk_vbox_new (FALSE
, 0);
471 vbox2
= gtk_vbox_new (FALSE
, 0);
472 alignment1
= gtk_alignment_new (.5, .5, 0, 0);
473 label2
= gtk_label_new ("");
474 label3
= gtk_label_new (_("fps:"));
478 i
= decode_switches (argc
, argv
);
479 if (argc
!= i
) usage(1);
481 if(rc_file
) try_load_rc_file(rc_file
);
483 fr
= FR_create(25,1,FRF_NONE
);
484 FR_setdbl(fr
,user_fps
,1);
486 if (use_table_layout
) {
487 table1
= gtk_table_new (1,12, TRUE
);
489 labelSMPTE
[i
] = gtk_label_new ("--");
490 //gtk_widget_set_size_request(labelSMPTE[i],30,-1);
491 gtk_table_attach (GTK_TABLE (table1
), labelSMPTE
[i
],i
, i
+1, 0, 1,
492 (GtkAttachOptions
) (GTK_EXPAND
),
493 (GtkAttachOptions
) (GTK_FILL
), 0, 0);
495 set_smpte_text(_("HH:MM:SS:FFF"));
496 gtk_box_pack_start (GTK_BOX (vbox2
), table1
, TRUE
, FALSE
, 0);
498 label1
= gtk_label_new (_("HH:MM:SS:FFF"));
499 gtk_box_pack_start (GTK_BOX (vbox2
), label1
, TRUE
, FALSE
, 0);
502 gtk_container_add (GTK_CONTAINER (window1
), hbox1
);
503 gtk_box_pack_start (GTK_BOX (hbox1
), vbox2
, TRUE
, TRUE
, 0);
504 gtk_box_pack_start (GTK_BOX (hbox1
), alignment1
, FALSE
, FALSE
, 4);
505 gtk_container_add (GTK_CONTAINER(alignment1
), vbox1
);
506 gtk_box_pack_start (GTK_BOX (vbox1
), label3
, FALSE
, FALSE
, 2);
507 gtk_box_pack_start (GTK_BOX (vbox1
), label2
, FALSE
, FALSE
, 2);
509 snprintf(font
, 1024, "%s %i",font_family
?font_family
:"Monospace", font_size
);
510 set_smpte_font(font
);
514 gdk_color_parse (color_bg
, &color
);
515 gtk_widget_modify_bg (window1
, GTK_STATE_NORMAL
, &color
);
519 gdk_color_parse (color_fg
, &color
);
520 if (use_table_layout
) {
522 gtk_widget_modify_fg (labelSMPTE
[i
], GTK_STATE_NORMAL
, &color
);
525 gtk_widget_modify_fg (label1
, GTK_STATE_NORMAL
, &color
);
527 gtk_widget_modify_fg (label2
, GTK_STATE_NORMAL
, &color
);
528 gtk_widget_modify_fg (label3
, GTK_STATE_NORMAL
, &color
);
531 if (use_table_layout
) {
532 gtk_widget_show (table1
);
534 gtk_widget_show (labelSMPTE
[i
]);
537 gtk_widget_show (label1
);
539 gtk_widget_show (label2
);
540 gtk_widget_show (label3
);
541 gtk_widget_show (hbox1
);
542 gtk_widget_show (vbox2
);
544 gtk_widget_show (alignment1
);
545 gtk_widget_show (vbox1
);
548 gtk_widget_get_preferred_size(GTK_WIDGET(alignment1
), &rx
, NULL
);
550 if (vboxw
< 0) vboxw
= 32;
552 vboxw
= 32; // guess :)
558 gtk_window_get_size (GTK_WINDOW(window1
), &geo
.min_width
, &geo
.min_height
);
559 gtk_window_set_geometry_hints(GTK_WINDOW (window1
), NULL
, &geo
, GDK_HINT_MIN_SIZE
);
562 gtk_widget_show (window1
);
564 gjc_icon_pixmap
= gdk_pixmap_create_from_xpm_d (window1
->window
, &gjc_icon_mask
, NULL
, gjc_icon_xpm
);
565 gdk_window_set_icon(window1
->window
, NULL
, gjc_icon_pixmap
, gjc_icon_mask
);
566 g_signal_connect ((gpointer
) window1
, "destroy", G_CALLBACK (gtk_main_quit
), NULL
);
567 g_signal_connect ((gpointer
) window1
, "key_press_event", G_CALLBACK (on_key_press_event
), NULL
);
570 char *t
= strchr(win_pos
, 'x');
571 int win_x
= atoi(win_pos
);
573 if (t
) win_y
= atoi(t
+1);
575 printf("window position: %ix%i \n", win_x
, win_y
);
576 if (win_x
>=0 && win_y
>=0 ) {
577 gtk_window_set_gravity(GTK_WINDOW(window1
), GDK_GRAVITY_STATIC
);
578 gtk_window_move(GTK_WINDOW(window1
), win_x
, win_y
);
583 gtk_window_set_keep_above(GTK_WINDOW(window1
), 1);
585 gtk_window_get_size(GTK_WINDOW(window1
), &orig_win_width
, &orig_win_height
);
587 g_timeout_add(40,gpolljack
,NULL
); // TODO: update MAX(1/FPS , screen update freq)
590 g_timeout_add(80,gfontresize
,NULL
);
594 if (color_fg
) free(color_fg
);
595 if (color_bg
) free(color_bg
);
597 if (font_family
) free(font_family
);
598 if (win_pos
) free(win_pos
);
603 /* vi:set ts=8 sw=2 et: */