implement printer media query, implement status bar with clock

This commit is contained in:
Andreas Frisch 2016-04-28 16:19:25 +02:00
parent 7a9ebb08b9
commit 4443c43ad6
7 changed files with 274 additions and 64 deletions

View file

@ -1,6 +1,6 @@
CC ?= gcc CC ?= gcc
PKGCONFIG = $(shell which pkg-config) PKGCONFIG = $(shell which pkg-config)
CFLAGS = $(shell $(PKGCONFIG) --cflags gtk+-3.0 gstreamer-1.0 gstreamer-video-1.0 libgphoto2) -Wl,--export-dynamic -rdynamic CFLAGS = $(shell $(PKGCONFIG) --cflags gtk+-3.0 gstreamer-1.0 gstreamer-video-1.0 libgphoto2) -Wl,--export-dynamic -rdynamic -g
LIBS = $(shell $(PKGCONFIG) --libs gtk+-3.0 gstreamer-1.0 gstreamer-video-1.0 libgphoto2 gmodule-export-2.0) LIBS = $(shell $(PKGCONFIG) --libs gtk+-3.0 gstreamer-1.0 gstreamer-video-1.0 libgphoto2 gmodule-export-2.0)
GLIB_COMPILE_RESOURCES = $(shell $(PKGCONFIG) --variable=glib_compile_resources gio-2.0) GLIB_COMPILE_RESOURCES = $(shell $(PKGCONFIG) --variable=glib_compile_resources gio-2.0)
GLIB_COMPILE_SCHEMAS = $(shell $(PKGCONFIG) --variable=glib_compile_schemas gio-2.0) GLIB_COMPILE_SCHEMAS = $(shell $(PKGCONFIG) --variable=glib_compile_schemas gio-2.0)

View file

@ -13,8 +13,6 @@
* distributed other than under the conditions noted above. * distributed other than under the conditions noted above.
*/ */
// gcc -Wall -g `pkg-config gstreamer-1.0 gstreamer-video-1.0 libgphoto2 gtk+-3.0 gtk+-x11-3.0 --cflags --libs` photobooth.c focus.c -o photobooth
#include <poll.h> #include <poll.h>
#include <string.h> #include <string.h>
#include <stdlib.h> #include <stdlib.h>
@ -37,14 +35,17 @@ typedef struct _PhotoBoothPrivate PhotoBoothPrivate;
struct _PhotoBoothPrivate struct _PhotoBoothPrivate
{ {
PhotoBoothWindow *win; PhotoBoothWindow *win;
GSettings *settings; guint32 countdown;
guint countdown; gchar *printer_backend;
gint prints_remaining;
GstElement *audio_playbin; GstElement *audio_playbin;
GstVideoRectangle video_size; GstVideoRectangle video_size;
GThread *capture_thread;
}; };
#define DEFAULT_AUDIOFILE_COUNTDOWN "/net/home/fraxinas/microcontroller/photobooth/beep.m4a" #define DEFAULT_AUDIOFILE_COUNTDOWN "/net/home/fraxinas/microcontroller/photobooth/beep.m4a"
#define DEFAULT_COUNTDOWN 5 #define DEFAULT_COUNTDOWN 5
#define DEFAULT_PRINTER_BACKEND "mitsu9550"
#define PRINT_WIDTH 2076 #define PRINT_WIDTH 2076
#define PRINT_HEIGHT 1384 #define PRINT_HEIGHT 1384
#define PREVIEW_WIDTH 640 #define PREVIEW_WIDTH 640
@ -55,6 +56,8 @@ enum
{ {
ARG_0, ARG_0,
ARG_COUNTDOWN, ARG_COUNTDOWN,
ARG_PRINTER_BACKEND,
ARG_PRINTS_REMAINING,
}; };
G_DEFINE_TYPE_WITH_PRIVATE (PhotoBooth, photo_booth, GTK_TYPE_APPLICATION); G_DEFINE_TYPE_WITH_PRIVATE (PhotoBooth, photo_booth, GTK_TYPE_APPLICATION);
@ -82,6 +85,7 @@ static gboolean photo_booth_preview (PhotoBooth *pb);
static void photo_booth_snapshot_start (PhotoBooth *pb); static void photo_booth_snapshot_start (PhotoBooth *pb);
static gboolean photo_booth_snapshot_prepare (PhotoBooth *pb); static gboolean photo_booth_snapshot_prepare (PhotoBooth *pb);
static gboolean photo_booth_snapshot_taken (PhotoBooth *pb); static gboolean photo_booth_snapshot_taken (PhotoBooth *pb);
static void photo_booth_get_printer_status (PhotoBooth *pb);
static void photo_booth_print (PhotoBooth *pb); static void photo_booth_print (PhotoBooth *pb);
/* libgphoto2 */ /* libgphoto2 */
@ -109,14 +113,24 @@ static void photo_booth_class_init (PhotoBoothClass *klass)
gp_log_add_func(GP_LOG_ERROR, _gphoto_err, NULL); gp_log_add_func(GP_LOG_ERROR, _gphoto_err, NULL);
gobject_class->finalize = photo_booth_finalize; gobject_class->finalize = photo_booth_finalize;
gobject_class->dispose = photo_booth_dispose;
gobject_class->set_property = photo_booth_set_property; gobject_class->set_property = photo_booth_set_property;
gobject_class->get_property = photo_booth_get_property; gobject_class->get_property = photo_booth_get_property;
gapplication_class->activate = photo_booth_activate; gapplication_class->activate = photo_booth_activate;
gapplication_class->open = photo_booth_open; gapplication_class->open = photo_booth_open;
g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_COUNTDOWN, g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_COUNTDOWN,
g_param_spec_uint ("countdown", "Shutter delay (s)", g_param_spec_uint ("countdown", "Shutter delay (s)",
"Shutter actuation delay countdown in seconds", 0, 60, DEFAULT_COUNTDOWN, "Specify shutter actuation delay countdown in seconds", 0, 60, DEFAULT_COUNTDOWN,
G_PARAM_CONSTRUCT | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS)); G_PARAM_CONSTRUCT | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_PRINTER_BACKEND,
g_param_spec_string ("printer-backend", "Gutenprint backend",
"Specify which Gutenprint backend to use", DEFAULT_PRINTER_BACKEND,
G_PARAM_CONSTRUCT | G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
g_object_class_install_property (G_OBJECT_CLASS (klass), ARG_PRINTS_REMAINING,
g_param_spec_int ("prints-remaining", "Print media remaining",
"Show remaining prints on media roll (-1 = Unknown)", -1, 1000, -1,
G_PARAM_READABLE | G_PARAM_STATIC_STRINGS));
} }
static void photo_booth_init (PhotoBooth *pb) static void photo_booth_init (PhotoBooth *pb)
@ -157,10 +171,7 @@ static void photo_booth_init (PhotoBooth *pb)
g_application_quit (G_APPLICATION (pb)); g_application_quit (G_APPLICATION (pb));
} }
pb->capture_thread = NULL; priv->capture_thread = NULL;
pb->capture_thread = g_thread_try_new ("gphoto-capture", (GThreadFunc) photo_booth_capture_thread_func, pb, NULL);
priv->settings = NULL;
} }
static void photo_booth_setup_window (PhotoBooth *pb) static void photo_booth_setup_window (PhotoBooth *pb)
@ -170,7 +181,9 @@ static void photo_booth_setup_window (PhotoBooth *pb)
priv->win = photo_booth_window_new (pb); priv->win = photo_booth_window_new (pb);
gtk_window_present (GTK_WINDOW (priv->win)); gtk_window_present (GTK_WINDOW (priv->win));
g_signal_connect (G_OBJECT (priv->win), "destroy", G_CALLBACK (photo_booth_window_destroyed_signal), pb); g_signal_connect (G_OBJECT (priv->win), "destroy", G_CALLBACK (photo_booth_window_destroyed_signal), pb);
priv->capture_thread = g_thread_try_new ("gphoto-capture", (GThreadFunc) photo_booth_capture_thread_func, pb, NULL);
photo_booth_setup_gstreamer (pb); photo_booth_setup_gstreamer (pb);
photo_booth_get_printer_status (pb);
} }
static void photo_booth_activate (GApplication *app) static void photo_booth_activate (GApplication *app)
@ -188,10 +201,12 @@ static void photo_booth_open (GApplication *app, GFile **files, gint n_files, c
static void photo_booth_finalize (GObject *object) static void photo_booth_finalize (GObject *object)
{ {
PhotoBooth *pb = PHOTO_BOOTH (object); PhotoBooth *pb = PHOTO_BOOTH (object);
PhotoBoothPrivate *priv = photo_booth_get_instance_private (pb);
GST_INFO_OBJECT (pb, "finalize"); GST_INFO_OBJECT (pb, "finalize");
SEND_COMMAND (pb, CONTROL_STOP); SEND_COMMAND (pb, CONTROL_STOP);
photo_booth_flush_pipe (pb->video_fd); photo_booth_flush_pipe (pb->video_fd);
g_thread_join (pb->capture_thread); g_thread_join (priv->capture_thread);
if (pb->cam_info) if (pb->cam_info)
photo_booth_cam_close (&pb->cam_info); photo_booth_cam_close (&pb->cam_info);
if (pb->video_fd) if (pb->video_fd)
@ -205,7 +220,7 @@ static void photo_booth_dispose (GObject *object)
{ {
PhotoBoothPrivate *priv; PhotoBoothPrivate *priv;
priv = photo_booth_get_instance_private (PHOTO_BOOTH (object)); priv = photo_booth_get_instance_private (PHOTO_BOOTH (object));
g_clear_object (&priv->settings); g_free (priv->printer_backend);
G_OBJECT_CLASS (photo_booth_parent_class)->dispose (object); G_OBJECT_CLASS (photo_booth_parent_class)->dispose (object);
} }
@ -295,6 +310,9 @@ static void photo_booth_set_property (GObject * object, guint prop_id, const GVa
case ARG_COUNTDOWN: case ARG_COUNTDOWN:
priv->countdown = g_value_get_uint (value); priv->countdown = g_value_get_uint (value);
break; break;
case ARG_PRINTER_BACKEND:
priv->printer_backend = g_strdup (g_value_get_string (value));
break;
default: default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
break; break;
@ -308,7 +326,13 @@ static void photo_booth_get_property (GObject * object, guint prop_id, GValue *
switch (prop_id) { switch (prop_id) {
case ARG_COUNTDOWN: case ARG_COUNTDOWN:
g_value_set_int (value, priv->countdown); g_value_set_uint (value, priv->countdown);
break;
case ARG_PRINTER_BACKEND:
g_value_set_string (value, priv->printer_backend);
break;
case ARG_PRINTS_REMAINING:
g_value_set_int (value, priv->prints_remaining);
break; break;
default: default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec); G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
@ -319,12 +343,12 @@ static void photo_booth_get_property (GObject * object, guint prop_id, GValue *
static void photo_booth_capture_thread_func (PhotoBooth *pb) static void photo_booth_capture_thread_func (PhotoBooth *pb)
{ {
PhotoboothCaptureThreadState state = CAPTURE_INIT; PhotoboothCaptureThreadState state = CAPTURE_INIT;
PhotoBoothPrivate *priv = photo_booth_get_instance_private (pb);
GST_DEBUG_OBJECT (pb, "enter capture thread fd = %d", pb->video_fd);
CameraFile *gp_file = NULL; CameraFile *gp_file = NULL;
int gpret, captured_frames = 0; int gpret, captured_frames = 0;
GST_DEBUG_OBJECT (pb, "enter capture thread fd = %d", pb->video_fd);
if (gp_file_new_from_fd (&gp_file, pb->video_fd) != GP_OK) if (gp_file_new_from_fd (&gp_file, pb->video_fd) != GP_OK)
{ {
GST_ERROR_OBJECT (pb, "couldn't start capture thread because gp_file_new_from_fd (%d) failed!", pb->video_fd); GST_ERROR_OBJECT (pb, "couldn't start capture thread because gp_file_new_from_fd (%d) failed!", pb->video_fd);
@ -348,8 +372,10 @@ static void photo_booth_capture_thread_func (PhotoBooth *pb)
state = CAPTURE_VIDEO; state = CAPTURE_VIDEO;
g_main_context_invoke (NULL, (GSourceFunc) photo_booth_preview, pb); g_main_context_invoke (NULL, (GSourceFunc) photo_booth_preview, pb);
} }
else else {
gtk_label_set_text (priv->win->status, "no camera connected!");
GST_INFO_OBJECT (pb, "no camera info."); GST_INFO_OBJECT (pb, "no camera info.");
}
timeout = 5000; timeout = 5000;
} }
else if (state == CAPTURE_PAUSED) else if (state == CAPTURE_PAUSED)
@ -399,11 +425,11 @@ static void photo_booth_capture_thread_func (PhotoBooth *pb)
{ {
if (pb->cam_info) if (pb->cam_info)
{ {
gtk_label_set_text (priv->win->status, "taking photo...");
ret = photo_booth_take_photo (pb->cam_info); ret = photo_booth_take_photo (pb->cam_info);
if (ret) if (ret)
g_main_context_invoke (NULL, (GSourceFunc) photo_booth_snapshot_taken, pb); g_main_context_invoke (NULL, (GSourceFunc) photo_booth_snapshot_taken, pb);
else else {
{
GST_ERROR_OBJECT (pb, "taking photo failed!"); GST_ERROR_OBJECT (pb, "taking photo failed!");
state = CAPTURE_INIT; state = CAPTURE_INIT;
} }
@ -656,7 +682,7 @@ static gboolean photo_booth_bus_callback (GstBus *bus, GstMessage *message, Phot
GstStateChange transition = (GstStateChange)GST_STATE_TRANSITION (old_state, new_state); GstStateChange transition = (GstStateChange)GST_STATE_TRANSITION (old_state, new_state);
GstElement *src = GST_ELEMENT (GST_MESSAGE_SRC (message)); GstElement *src = GST_ELEMENT (GST_MESSAGE_SRC (message));
GST_LOG ("%" GST_PTR_FORMAT " state transition %s -> %s", src, gst_element_state_get_name(GST_STATE_TRANSITION_CURRENT(transition)), gst_element_state_get_name(GST_STATE_TRANSITION_NEXT(transition))); GST_LOG ("%" GST_PTR_FORMAT " state transition %s -> %s", src, gst_element_state_get_name(GST_STATE_TRANSITION_CURRENT(transition)), gst_element_state_get_name(GST_STATE_TRANSITION_NEXT(transition)));
if (src == pb->video_bin && transition == GST_STATE_CHANGE_PAUSED_TO_PLAYING) if (src == pb->video_bin && transition == GST_STATE_CHANGE_PAUSED_TO_PLAYING && pb->state != PB_STATE_WAITING_FOR_ANSWER)
{ {
GST_DEBUG ("video_bin GST_STATE_CHANGE_READY_TO_PAUSED -> CAPTURE VIDEO!"); GST_DEBUG ("video_bin GST_STATE_CHANGE_READY_TO_PAUSED -> CAPTURE VIDEO!");
SEND_COMMAND (pb, CONTROL_VIDEO); SEND_COMMAND (pb, CONTROL_VIDEO);
@ -678,9 +704,6 @@ static gboolean photo_booth_bus_callback (GstBus *bus, GstMessage *message, Phot
case GST_MESSAGE_STREAM_START: case GST_MESSAGE_STREAM_START:
{ {
GST_DEBUG ("GST_MESSAGE_STREAM_START! state=%i", pb->state); GST_DEBUG ("GST_MESSAGE_STREAM_START! state=%i", pb->state);
// if (pb->state == PB_STATE_ASKING)
// {
// }
} }
default: default:
{ {
@ -727,6 +750,7 @@ static void photo_booth_video_widget_ready (PhotoBooth *pb)
static gboolean photo_booth_preview (PhotoBooth *pb) static gboolean photo_booth_preview (PhotoBooth *pb)
{ {
PhotoBoothPrivate *priv = photo_booth_get_instance_private (pb);
GstPad *pad; GstPad *pad;
if (pb->video_block_id) if (pb->video_block_id)
{ {
@ -747,6 +771,7 @@ static gboolean photo_booth_preview (PhotoBooth *pb)
gst_element_set_state (pb->video_bin, GST_STATE_PLAYING); gst_element_set_state (pb->video_bin, GST_STATE_PLAYING);
GST_DEBUG_OBJECT (pb, "photo_booth_preview done"); GST_DEBUG_OBJECT (pb, "photo_booth_preview done");
pb->state = PB_STATE_PREVIEW; pb->state = PB_STATE_PREVIEW;
gtk_label_set_text (priv->win->status, "camera ready, showing preview video");
return FALSE; return FALSE;
} }
@ -754,8 +779,6 @@ void photo_booth_background_clicked (GtkWidget *widget, GdkEventButton *event, P
{ {
PhotoBoothPrivate *priv; PhotoBoothPrivate *priv;
PhotoBooth *pb = PHOTO_BOOTH_FROM_WINDOW (win); PhotoBooth *pb = PHOTO_BOOTH_FROM_WINDOW (win);
GST_DEBUG_OBJECT (widget, "photo_booth_background_clicked state=%d", pb->state);
switch (pb->state) { switch (pb->state) {
case PB_STATE_PREVIEW: case PB_STATE_PREVIEW:
{ {
@ -777,6 +800,72 @@ void photo_booth_background_clicked (GtkWidget *widget, GdkEventButton *event, P
} }
} }
static void photo_booth_get_printer_status (PhotoBooth *pb)
{
PhotoBoothPrivate *priv = photo_booth_get_instance_private (pb);
gchar *label_string;
gchar *backend_environment = g_strdup_printf ("BACKEND=%s", priv->printer_backend);
gchar *argv[] = { "/usr/lib/cups/backend/gutenprint52+usb", "-m", NULL };
gchar *envp[] = { backend_environment, NULL };
gchar *output = NULL;
GError *error = NULL;
gint remain = -1;
gint ret = 0;
if (g_spawn_sync (NULL, argv, envp, G_SPAWN_DEFAULT, NULL, NULL, NULL, &output, &ret, &error))
{
GMatchInfo *match_info;
GRegex *regex;
if (ret == 0)
{
regex = g_regex_new ("INFO: Media type\\s.*?: (?<code>\\d+) \\((?<size>.*?)\\)\nINFO: Media remaining\\s.*?: (?<remain>\\d{3})/(?<total>\\d{3})\n", G_REGEX_MULTILINE|G_REGEX_DOTALL, 0, &error);
if (error) {
g_critical ("%s\n", error->message);
return;
}
if (g_regex_match (regex, output, 0, &match_info))
{
guint code = atoi(g_match_info_fetch_named(match_info, "code"));
gchar *size = g_match_info_fetch_named(match_info, "size");
remain = atoi(g_match_info_fetch_named(match_info, "remain"));
guint total = atoi(g_match_info_fetch_named(match_info, "total"));
label_string = g_strdup_printf("printer %s online. media (%s) %i prints remaining", priv->printer_backend, size, remain);
GST_INFO_OBJECT (pb, "printer %s status: media code %i (%s) prints remaining %i of %i", priv->printer_backend, code, size, remain, total);
}
else {
label_string = g_strdup_printf("can't parse printer backend output");
GST_ERROR_OBJECT (pb, "%s: '%s'", label_string, output);
}
}
else
{
regex = g_regex_new ("ERROR: Printer open failure", G_REGEX_MULTILINE|G_REGEX_DOTALL, 0, &error);
if (g_regex_match (regex, output, 0, &match_info))
{
label_string = g_strdup_printf("printer %s off-line", priv->printer_backend);
GST_WARNING_OBJECT (pb, "%s", label_string);
}
else {
label_string = g_strdup_printf("can't parse printer backend output");
GST_ERROR_OBJECT (pb, "%s: '%s'", label_string, output);
}
}
g_free (output);
g_match_info_free (match_info);
g_regex_unref (regex);
}
else {
label_string = g_strdup_printf("can't spawn %s", argv[0]);
GST_ERROR_OBJECT (pb, "%s %s %s (%s)", label_string, argv[1], envp[0], error->message);
g_error_free (error);
}
priv->prints_remaining = remain;
gtk_label_set_text (priv->win->status_printer, label_string);
g_free (label_string);
g_free (backend_environment);
return;
}
static void photo_booth_snapshot_start (PhotoBooth *pb) static void photo_booth_snapshot_start (PhotoBooth *pb)
{ {
PhotoBoothPrivate *priv; PhotoBoothPrivate *priv;
@ -914,12 +1003,14 @@ static gboolean photo_booth_take_photo (CameraInfo *cam_info)
static gboolean photo_booth_snapshot_taken (PhotoBooth *pb) static gboolean photo_booth_snapshot_taken (PhotoBooth *pb)
{ {
PhotoBoothPrivate *priv = photo_booth_get_instance_private (pb);
GstElement *appsrc; GstElement *appsrc;
GstBuffer *buffer; GstBuffer *buffer;
GstFlowReturn flowret; GstFlowReturn flowret;
GstPad *pad; GstPad *pad;
GST_INFO_OBJECT (pb, "photo_booth_snapshot_taken size=%lu", pb->cam_info->size); GST_INFO_OBJECT (pb, "photo_booth_snapshot_taken size=%lu", pb->cam_info->size);
gtk_label_set_text (priv->win->status, "processing photo...");
appsrc = gst_bin_get_by_name (GST_BIN (pb->photo_bin), "photo-appsrc"); appsrc = gst_bin_get_by_name (GST_BIN (pb->photo_bin), "photo-appsrc");
buffer = gst_buffer_new_wrapped (pb->cam_info->data, pb->cam_info->size); buffer = gst_buffer_new_wrapped (pb->cam_info->data, pb->cam_info->size);
@ -928,7 +1019,6 @@ static gboolean photo_booth_snapshot_taken (PhotoBooth *pb)
if (flowret != GST_FLOW_OK) if (flowret != GST_FLOW_OK)
GST_ERROR_OBJECT (appsrc, "couldn't push %" GST_PTR_FORMAT " to appsrc", buffer); GST_ERROR_OBJECT (appsrc, "couldn't push %" GST_PTR_FORMAT " to appsrc", buffer);
gst_object_unref (appsrc); gst_object_unref (appsrc);
GST_INFO_OBJECT (pb, "photo_booth_snapshot now waiting for user input... PB_STATE_ASKING");
SEND_COMMAND (pb, CONTROL_PAUSE); SEND_COMMAND (pb, CONTROL_PAUSE);
@ -953,7 +1043,7 @@ static GstPadProbeReturn photo_booth_catch_photo_buffer (GstPad * pad, GstPadPro
photo_booth_window_set_spinner (priv->win, FALSE); photo_booth_window_set_spinner (priv->win, FALSE);
GST_INFO_OBJECT (priv->win->button_yes, "PB_STATE_TAKING_PHOTO -> PB_STATE_PROCESS_PHOTO. hide spinner, show button"); GST_INFO_OBJECT (priv->win->button_yes, "PB_STATE_TAKING_PHOTO -> PB_STATE_PROCESS_PHOTO. hide spinner, show button");
gtk_widget_show (GTK_WIDGET (priv->win->button_yes)); gtk_widget_show (GTK_WIDGET (priv->win->button_yes));
gtk_label_set_text (priv->win->status, "Print Photo? Touch background to cancel!");
return GST_PAD_PROBE_PASS; return GST_PAD_PROBE_PASS;
} }
if (pb->state == PB_STATE_PROCESS_PHOTO) if (pb->state == PB_STATE_PROCESS_PHOTO)
@ -1014,10 +1104,22 @@ void photo_booth_button_yes_clicked (GtkButton *button, PhotoBoothWindow *win)
static void photo_booth_print (PhotoBooth *pb) static void photo_booth_print (PhotoBooth *pb)
{ {
PhotoBoothPrivate *priv; PhotoBoothPrivate *priv;
GST_DEBUG_OBJECT (pb, "!!!PRINT!!!"); priv = photo_booth_get_instance_private (pb);
GST_DEBUG_BIN_TO_DOT_FILE (GST_BIN (pb->pipeline), GST_DEBUG_GRAPH_SHOW_ALL, "photo_booth_photo_print.dot");
photo_booth_get_printer_status (pb);
GST_DEBUG_OBJECT (pb, "!!!PRINT!!! prints_remaining=%i", priv->prints_remaining);
if (priv->prints_remaining > 1)
{
priv = photo_booth_get_instance_private (pb); priv = photo_booth_get_instance_private (pb);
gtk_widget_hide (GTK_WIDGET (priv->win->button_yes)); gtk_widget_hide (GTK_WIDGET (priv->win->button_yes));
GST_DEBUG_BIN_TO_DOT_FILE (GST_BIN (pb->pipeline), GST_DEBUG_GRAPH_SHOW_ALL, "photo_booth_photo_print.dot"); gtk_label_set_text (priv->win->status, "PRINTING................");
}
else if (priv->prints_remaining == -1) {
gtk_label_set_text (priv->win->status, "can't print... no printer connected!!!");
}
else
gtk_label_set_text (priv->win->status, "can't print... out of paper!!!");
} }
PhotoBooth *photo_booth_new (void) PhotoBooth *photo_booth_new (void)
@ -1033,7 +1135,7 @@ int main (int argc, char *argv[])
PhotoBooth *pb; PhotoBooth *pb;
int ret; int ret;
gst_init (&argc, &argv); gst_init (0, NULL);
pb = photo_booth_new (); pb = photo_booth_new ();

View file

@ -3,7 +3,7 @@
} }
.countdown_label { .countdown_label {
color: rgba (150, 200, 200, 255); color: rgba (150, 200, 200, 1);
text-shadow: 10px 10px #000000 text-shadow: 10px 10px #000000
font-weight: bold; font-weight: bold;
font-size: 80px; font-size: 80px;
@ -11,9 +11,20 @@
} }
.button_yes { .button_yes {
color: rgba (0, 0, 110, 255); color: rgba (0, 0, 110, 1);
background: rgba (168, 233, 255, 200); background: rgba (168, 233, 255, 0.9);
font-weight: bold; font-weight: bold;
font-size: 80px; font-size: 80px;
padding: 20px 20px 20px 20px; padding: 20px 20px 20px 20px;
} }
.statusbox {
background: rgba (0, 0, 0, 0.8);
}
.status_label {
color: white;
font-weight: bold;
font-size: 14px;
}

View file

@ -110,7 +110,6 @@ struct _PhotoBooth
gulong photo_block_id; gulong photo_block_id;
int control_sock[2]; int control_sock[2];
GThread *capture_thread;
PhotoboothState state; PhotoboothState state;
}; };

View file

@ -13,15 +13,12 @@
<property name="can_focus">False</property> <property name="can_focus">False</property>
<property name="events">GDK_BUTTON_PRESS_MASK</property> <property name="events">GDK_BUTTON_PRESS_MASK</property>
<signal name="button-press-event" handler="photo_booth_background_clicked" swapped="no"/> <signal name="button-press-event" handler="photo_booth_background_clicked" swapped="no"/>
<child>
<placeholder/>
</child>
<child type="overlay"> <child type="overlay">
<object class="GtkLabel" id="countdown_label"> <object class="GtkLabel" id="countdown_label">
<property name="width_request">400</property> <property name="width_request">400</property>
<property name="height_request">300</property> <property name="height_request">300</property>
<property name="can_focus">False</property> <property name="can_focus">False</property>
<property name="opacity">0.9</property> <property name="opacity">0.90000000000000002</property>
<property name="halign">center</property> <property name="halign">center</property>
<property name="valign">center</property> <property name="valign">center</property>
<property name="label" translatable="yes">label</property> <property name="label" translatable="yes">label</property>
@ -36,26 +33,6 @@
<class name="countdown_label"/> <class name="countdown_label"/>
</style> </style>
</object> </object>
<packing>
<property name="index">1</property>
</packing>
</child>
<child type="overlay">
<object class="GtkSpinner" id="spinner">
<property name="width_request">200</property>
<property name="height_request">200</property>
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="opacity">0.8</property>
<property name="halign">center</property>
<property name="valign">center</property>
<property name="hexpand">False</property>
<property name="vexpand">False</property>
<property name="active">True</property>
<style>
<class name="transparentbg"/>
</style>
</object>
<packing> <packing>
<property name="index">2</property> <property name="index">2</property>
</packing> </packing>
@ -89,7 +66,103 @@
</object> </object>
<packing> <packing>
<property name="pass_through">True</property> <property name="pass_through">True</property>
<property name="index">2</property> <property name="index">3</property>
</packing>
</child>
<child type="overlay">
<object class="GtkBox" id="statusbox">
<property name="name">statusbox</property>
<property name="width_request">800</property>
<property name="height_request">40</property>
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="valign">end</property>
<property name="margin_left">10</property>
<property name="margin_right">10</property>
<property name="margin_bottom">10</property>
<property name="homogeneous">True</property>
<property name="baseline_position">bottom</property>
<child>
<object class="GtkLabel" id="status_clock">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="halign">start</property>
<property name="valign">center</property>
<property name="label" translatable="yes">label</property>
<property name="single_line_mode">True</property>
<property name="track_visited_links">False</property>
<style>
<class name="status_label"/>
</style>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">0</property>
</packing>
</child>
<child>
<object class="GtkLabel" id="status">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="halign">center</property>
<property name="valign">center</property>
<property name="label" translatable="yes">label</property>
<property name="justify">center</property>
<style>
<class name="status_label"/>
</style>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">1</property>
</packing>
</child>
<child>
<object class="GtkLabel" id="status_printer">
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="halign">end</property>
<property name="valign">center</property>
<property name="label" translatable="yes">label</property>
<property name="justify">right</property>
<style>
<class name="status_label"/>
</style>
</object>
<packing>
<property name="expand">False</property>
<property name="fill">True</property>
<property name="position">2</property>
</packing>
</child>
<style>
<class name="statusbox"/>
</style>
</object>
<packing>
<property name="index">3</property>
</packing>
</child>
<child type="overlay">
<object class="GtkSpinner" id="spinner">
<property name="width_request">200</property>
<property name="height_request">200</property>
<property name="visible">True</property>
<property name="can_focus">False</property>
<property name="opacity">0.80000000000000004</property>
<property name="halign">center</property>
<property name="valign">center</property>
<property name="hexpand">False</property>
<property name="vexpand">False</property>
<property name="active">True</property>
<style>
<class name="transparentbg"/>
</style>
</object>
<packing>
<property name="index">9</property>
</packing> </packing>
</child> </child>
<style> <style>

View file

@ -14,7 +14,7 @@
*/ */
#include <gtk/gtk.h> #include <gtk/gtk.h>
#include <time.h>
#include "photobooth.h" #include "photobooth.h"
#include "photoboothwin.h" #include "photoboothwin.h"
@ -33,6 +33,9 @@ G_DEFINE_TYPE_WITH_PRIVATE (PhotoBoothWindow, photo_booth_window, GTK_TYPE_APPLI
GST_DEBUG_CATEGORY_STATIC (photo_booth_windows_debug); GST_DEBUG_CATEGORY_STATIC (photo_booth_windows_debug);
#define GST_CAT_DEFAULT photo_booth_windows_debug #define GST_CAT_DEFAULT photo_booth_windows_debug
gboolean _pbw_tick_countdown (PhotoBoothWindow *win);
gboolean _pbw_clock_tick (GtkLabel *status_clock);
static void photo_booth_window_class_init (PhotoBoothWindowClass *klass) static void photo_booth_window_class_init (PhotoBoothWindowClass *klass)
{ {
GST_DEBUG_CATEGORY_INIT (photo_booth_windows_debug, "photoboothwin", GST_DEBUG_BOLD | GST_DEBUG_FG_WHITE | GST_DEBUG_BG_BLUE, "PhotoBoothWindow"); GST_DEBUG_CATEGORY_INIT (photo_booth_windows_debug, "photoboothwin", GST_DEBUG_BOLD | GST_DEBUG_FG_WHITE | GST_DEBUG_BG_BLUE, "PhotoBoothWindow");
@ -41,6 +44,9 @@ static void photo_booth_window_class_init (PhotoBoothWindowClass *klass)
gtk_widget_class_bind_template_child_private (GTK_WIDGET_CLASS (klass), PhotoBoothWindow, spinner); gtk_widget_class_bind_template_child_private (GTK_WIDGET_CLASS (klass), PhotoBoothWindow, spinner);
gtk_widget_class_bind_template_child_private (GTK_WIDGET_CLASS (klass), PhotoBoothWindow, countdown_label); gtk_widget_class_bind_template_child_private (GTK_WIDGET_CLASS (klass), PhotoBoothWindow, countdown_label);
gtk_widget_class_bind_template_child (GTK_WIDGET_CLASS (klass), PhotoBoothWindow, button_yes); gtk_widget_class_bind_template_child (GTK_WIDGET_CLASS (klass), PhotoBoothWindow, button_yes);
gtk_widget_class_bind_template_child (GTK_WIDGET_CLASS (klass), PhotoBoothWindow, status_clock);
gtk_widget_class_bind_template_child (GTK_WIDGET_CLASS (klass), PhotoBoothWindow, status);
gtk_widget_class_bind_template_child (GTK_WIDGET_CLASS (klass), PhotoBoothWindow, status_printer);
} }
static void photo_booth_window_init (PhotoBoothWindow *win) static void photo_booth_window_init (PhotoBoothWindow *win)
@ -58,6 +64,7 @@ static void photo_booth_window_init (PhotoBoothWindow *win)
gtk_style_context_add_provider_for_screen (screen, (GtkStyleProvider *)cssprovider, GTK_STYLE_PROVIDER_PRIORITY_APPLICATION); gtk_style_context_add_provider_for_screen (screen, (GtkStyleProvider *)cssprovider, GTK_STYLE_PROVIDER_PRIORITY_APPLICATION);
g_object_unref (cssfile); g_object_unref (cssfile);
} }
g_timeout_add (1000, (GSourceFunc) _pbw_clock_tick, win->status_clock);
} }
void photo_booth_window_add_gtkgstwidget (PhotoBoothWindow *win, GtkWidget *gtkgstwidget) void photo_booth_window_add_gtkgstwidget (PhotoBoothWindow *win, GtkWidget *gtkgstwidget)
@ -72,6 +79,21 @@ void photo_booth_window_add_gtkgstwidget (PhotoBoothWindow *win, GtkWidget *gtkg
win->gtkgstwidget = gtkgstwidget; win->gtkgstwidget = gtkgstwidget;
} }
gboolean _pbw_clock_tick (GtkLabel *status_clock)
{
gchar clockstr[200];
time_t now;
struct tm *now_tm;
time (&now);
now_tm = localtime (&now);
if (!now_tm)
return TRUE;
if (strftime(clockstr, sizeof(clockstr), "%A, %d. %B %Y\t%T", now_tm) == 0)
return TRUE;
gtk_label_set_text (status_clock, clockstr);
return TRUE;
}
void photo_booth_window_set_spinner (PhotoBoothWindow *win, gboolean active) void photo_booth_window_set_spinner (PhotoBoothWindow *win, gboolean active)
{ {
PhotoBoothWindowPrivate *priv; PhotoBoothWindowPrivate *priv;
@ -97,6 +119,8 @@ gboolean _pbw_tick_countdown (PhotoBoothWindow *win)
GST_DEBUG ("_pbw_tick_countdown %i", priv->countdown); GST_DEBUG ("_pbw_tick_countdown %i", priv->countdown);
if (priv->countdown > 0) if (priv->countdown > 0)
{ {
gchar *status_str = g_strdup_printf ("Taking photo in %d seconds...", priv->countdown);
gtk_label_set_text (win->status, status_str);
str = g_strdup_printf ("%d...", priv->countdown); str = g_strdup_printf ("%d...", priv->countdown);
gtk_label_set_text (priv->countdown_label, str); gtk_label_set_text (priv->countdown_label, str);
g_free (str); g_free (str);

View file

@ -35,6 +35,7 @@ struct _PhotoBoothWindow
GtkApplicationWindow parent; GtkApplicationWindow parent;
GtkWidget *gtkgstwidget; GtkWidget *gtkgstwidget;
GtkButton *button_yes; GtkButton *button_yes;
GtkLabel *status_clock, *status, *status_printer;
}; };
struct _PhotoBoothWindowClass struct _PhotoBoothWindowClass