fixed i18n warning.
[gjacktransport.git] / src / gjackclock.c
blob0cac2c39d892beb42f16aac3fe5868d1043286ca
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.
19 #ifdef HAVE_CONFIG_H
20 # include <config.h>
21 #endif
23 #include <stdio.h>
24 #include <stdlib.h>
25 #include <string.h>
26 #include <gtk/gtk.h>
27 #include <gdk/gdkkeysyms.h>
28 #include <getopt.h>
29 #include <math.h>
30 #include <jack/jack.h>
31 #include <jack/transport.h>
33 #include "framerate.h"
34 #include "gjackclock.h"
36 #include <libintl.h>
37 #define _(X) gettext(X)
39 /* globals */
40 char *program_name;
42 static GdkPixmap *gjc_icon_pixmap;
43 static GdkBitmap *gjc_icon_mask;
44 #include "gjc_icon.xpm"
46 GtkWidget *window1;
47 GtkWidget *labelSMPTE[12];
48 GtkWidget *label1 = NULL;
49 GtkWidget *label2;
50 FrameRate *fr = NULL;
51 int vboxw = 0;
53 /* command-line args */
54 int want_quiet = 0;
55 int want_ontop = 0;
56 int want_scaling = 1;
57 int show_fps = 1;
58 char *font_family = NULL;
59 int font_size = 44;
60 double user_fps = 25;
61 int want_ignore_timemaster = 0;
62 char *win_pos = NULL;
63 double stride = 10;
64 char *color_bg = NULL;
65 char *color_fg = NULL;
66 int use_table_layout = 0;
67 char *rc_file = NULL;
69 gint key_cfg[MAX_KEYBINDING] = { GDK_space, GDK_v, GDK_n, GDK_b, GDK_p, GDK_r, GDK_less, GDK_greater };
72 /*****************************************************************************
73 * JACK FUNCTIONS
76 jack_client_t *jack_client = NULL;
77 jack_nframes_t j_srate = 48000;
78 char jackid[16];
80 /* when jack shuts down... */
81 void jack_shutdown(void *arg) {
82 jack_client=NULL;
85 int jack_connected(void) {
86 if (jack_client) return (1);
87 return (0);
90 void open_jack(void ) {
91 if (jack_client) {
92 if (!want_quiet)
93 fprintf (stderr, "already connected to jack..\n");
94 return;
97 int i=0;
98 do {
99 snprintf(jackid,16,"gjackclock-%i",i);
100 jack_client = jack_client_open (jackid, JackUseExactName, NULL);
101 } while (jack_client == 0 && i++<16);
103 if (!jack_client) {
104 fprintf(stderr, "could not connect to jack server.\n");
105 } else {
106 jack_on_shutdown (jack_client, jack_shutdown, 0);
107 if (!want_quiet)
108 fprintf(stderr, "connected as jack client '%s'\n",jackid);
109 jack_activate (jack_client);
113 void close_jack(void) {
114 if (jack_client) {
115 jack_deactivate (jack_client);
116 jack_client_close (jack_client);
117 if (!want_quiet)
118 fprintf(stderr, "disconnected from jack.\n");
120 jack_client=NULL;
123 void jack_stop(void) {
124 if (jack_client) {
125 jack_transport_stop (jack_client);
129 void jack_play(void) {
130 if (jack_client) {
131 jack_transport_start (jack_client);
135 int jack_poll_transport_state (void) {
136 if (jack_client)
137 switch (jack_transport_query(jack_client, NULL)) {
138 case JackTransportRolling:
139 return 1|4;
140 case JackTransportStopped:
141 return 2|4;
142 default:
143 return 3|4;
145 return 0;
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;
173 jack_reposition(f);
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);
181 printf (""
182 "Options:\n"
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"
190 " -l <file>,\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"
202 "Notes:\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"
206 " 'p' - pause\n"
207 " 'b' - play\n"
208 " 'r' - rewind\n"
209 " '<' - skip backward\n"
210 " '>' - skip forward\n"
211 " CTRL-[q|w] - quit\n"
214 exit(status);
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'},
237 {NULL, 0, NULL, 0}
240 static int
241 decode_switches (int argc, char **argv)
243 int c;
244 while ((c = getopt_long (argc, argv,
245 "q" /* quiet or silent */
246 "h" /* help */
247 "f:" /* fps */
248 "s:" /* font-size */
249 "S:" /* font-family */
250 "a" /* ontop */
251 "w:" /* window-pos */
252 "c:" /* color-fg */
253 "C:" /* color-bg */
254 "d:" /* stride-dist */
255 "I" /* ignore time-master */
256 "t" /* table-layout */
257 "N" /* no-scale */
258 "n" /* no-fps-display */
259 "l:" /* config file */
260 "V", /* version */
261 long_options, (int *) 0)) != EOF)
263 switch (c) {
264 case 'q':
265 want_quiet = 1;
266 break;
267 case 'I':
268 want_ignore_timemaster = 1;
269 break;
270 case 'f':
271 user_fps = atof(optarg);
272 break;
273 case 's':
274 font_size = atoi (optarg);
275 if (font_size < 6 || font_size > 500) font_size = 60;
276 break;
277 case 'S':
278 if (font_family) free(font_family);
279 font_family = strdup(optarg);
280 break;
281 case 'c':
282 if (color_fg) free(color_fg);
283 color_fg = strdup(optarg);
284 break;
285 case 'C':
286 if (color_bg) free(color_bg);
287 color_bg = strdup(optarg);
288 break;
289 case 'd': // XXX 'd' ??
290 stride = atof(optarg);
291 if (stride <=0) stride = 10.0;
292 break;
293 case 'w':
294 if (win_pos) free(win_pos);
295 win_pos = strdup(optarg);
296 break;
297 case 'l':
298 if (rc_file) free(rc_file);
299 rc_file = strdup(optarg);
300 break;
301 case 'a':
302 want_ontop = 1;
303 break;
304 case 'N':
305 want_scaling = 0;
306 break;
307 case 'n':
308 show_fps = 0;
309 break;
310 case 't':
311 use_table_layout = 1;
312 break;
313 case 'V':
314 printversion();
315 exit(0);
316 case 'h':
317 usage (0);
318 default:
319 usage (EXIT_FAILURE);
322 return optind;
325 /*****************************************************************************/
326 void set_smpte_font (char *font) {
327 if (use_table_layout) {
328 int i;
329 for (i=0;i<12;i++) {
330 gtk_widget_modify_font (labelSMPTE[i], pango_font_description_from_string (font));
332 } else {
333 gtk_widget_modify_font (label1, pango_font_description_from_string (font));
337 void set_smpte_text (gchar *txt) {
338 if (use_table_layout) {
339 int i;
340 int len=strlen(txt);
341 for (i=0;i<12;i++) {
342 gchar st[2];
343 memset(&st,0,2);
344 if (i<len) st[0]= txt[i];
345 gtk_label_set_text(GTK_LABEL(labelSMPTE[i]), st);
347 } else {
348 gtk_label_set_text(GTK_LABEL(label1),txt);
352 gboolean gpolljack (gpointer data) {
353 gchar text[16];
354 char fpstxt[16];
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);
365 return(TRUE);
368 /*****************************************************************************/
369 gboolean
370 on_key_press_event (GtkWidget *widget,
371 GdkEventKey *event,
372 gpointer user_data)
374 gboolean rv = FALSE;
375 if(event->keyval==GDK_Escape) return (rv);
377 if(event->state&GDK_CONTROL_MASK && (event->keyval==GDK_q || event->keyval==GDK_w)) {
378 gtk_main_quit();
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();
385 rv = TRUE;
386 } else if(event->keyval==key_cfg[3]) {
387 jack_play();
388 rv = TRUE;
389 } else if(event->keyval==key_cfg[4]) {
390 jack_stop();
391 rv = TRUE;
392 } else if(event->keyval==key_cfg[5]) {
393 jack_reposition(0);
394 rv = TRUE;
395 } else if(event->keyval==key_cfg[6]) {
396 jack_relative_reposition(-1.0*stride);
397 rv = TRUE;
398 } else if(event->keyval==key_cfg[7]) {
399 jack_relative_reposition(stride);
400 rv = TRUE;
402 #if 0
403 else {
404 printf("KEY key:%x state:%x\n", event->keyval, event->state);
406 #endif
408 return rv;
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;
420 char font[1024];
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;
427 return TRUE;
429 if (win_width == my_width && win_height == my_height ) {
430 return TRUE;
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;
442 return TRUE;
445 /*****************************************************************************/
448 int main (int argc, char *argv[]) {
449 int i;
450 GtkWidget *table1 = NULL;
451 GtkWidget *hbox1, *vbox1, *vbox2;
452 GtkWidget *alignment1;
453 GtkWidget *label3;
454 char font[1024];
456 program_name = argv[0];
458 #ifdef ENABLE_NLS
459 bindtextdomain (GETTEXT_PACKAGE, PACKAGE_LOCALE_DIR);
460 bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8");
461 textdomain (GETTEXT_PACKAGE);
462 #endif
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:"));
476 load_rc();
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);
488 for (i=0;i<12;i++) {
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);
497 } else {
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);
512 if (color_bg) {
513 GdkColor color;
514 gdk_color_parse (color_bg, &color);
515 gtk_widget_modify_bg (window1, GTK_STATE_NORMAL, &color);
517 if (color_fg) {
518 GdkColor color;
519 gdk_color_parse (color_fg, &color);
520 if (use_table_layout) {
521 for (i=0;i<12;i++) {
522 gtk_widget_modify_fg (labelSMPTE[i], GTK_STATE_NORMAL, &color);
524 } else {
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);
533 for (i=0;i<12;i++) {
534 gtk_widget_show (labelSMPTE[i]);
536 } else {
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);
543 if (show_fps) {
544 gtk_widget_show (alignment1);
545 gtk_widget_show (vbox1);
546 #if 0 /* GTK-3.0 */
547 GtkRequisition rx;
548 gtk_widget_get_preferred_size(GTK_WIDGET(alignment1), &rx, NULL);
549 vboxw = rx.width;
550 if (vboxw < 0) vboxw = 32;
551 #else
552 vboxw = 32; // guess :)
553 #endif
556 if (want_scaling) {
557 GdkGeometry geo;
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);
569 if (win_pos) {
570 char *t = strchr(win_pos, 'x');
571 int win_x = atoi(win_pos);
572 int win_y = -1;
573 if (t) win_y = atoi(t+1);
574 if (!want_quiet)
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);
582 if (want_ontop)
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)
589 if (want_scaling)
590 g_timeout_add(80,gfontresize,NULL);
592 gtk_main ();
594 if (color_fg) free(color_fg);
595 if (color_bg) free(color_bg);
596 if (fr) FR_free(fr);
597 if (font_family) free(font_family);
598 if (win_pos) free(win_pos);
599 close_jack();
600 return 0;
603 /* vi:set ts=8 sw=2 et: */