implement printer media query, implement status bar with clock
This commit is contained in:
parent
7a9ebb08b9
commit
4443c43ad6
7 changed files with 274 additions and 64 deletions
2
Makefile
2
Makefile
|
@ -1,6 +1,6 @@
|
|||
CC ?= gcc
|
||||
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)
|
||||
GLIB_COMPILE_RESOURCES = $(shell $(PKGCONFIG) --variable=glib_compile_resources gio-2.0)
|
||||
GLIB_COMPILE_SCHEMAS = $(shell $(PKGCONFIG) --variable=glib_compile_schemas gio-2.0)
|
||||
|
|
168
photobooth.c
168
photobooth.c
|
@ -13,8 +13,6 @@
|
|||
* 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 <string.h>
|
||||
#include <stdlib.h>
|
||||
|
@ -37,14 +35,17 @@ typedef struct _PhotoBoothPrivate PhotoBoothPrivate;
|
|||
struct _PhotoBoothPrivate
|
||||
{
|
||||
PhotoBoothWindow *win;
|
||||
GSettings *settings;
|
||||
guint countdown;
|
||||
guint32 countdown;
|
||||
gchar *printer_backend;
|
||||
gint prints_remaining;
|
||||
GstElement *audio_playbin;
|
||||
GstVideoRectangle video_size;
|
||||
GThread *capture_thread;
|
||||
};
|
||||
|
||||
#define DEFAULT_AUDIOFILE_COUNTDOWN "/net/home/fraxinas/microcontroller/photobooth/beep.m4a"
|
||||
#define DEFAULT_COUNTDOWN 5
|
||||
#define DEFAULT_PRINTER_BACKEND "mitsu9550"
|
||||
#define PRINT_WIDTH 2076
|
||||
#define PRINT_HEIGHT 1384
|
||||
#define PREVIEW_WIDTH 640
|
||||
|
@ -55,6 +56,8 @@ enum
|
|||
{
|
||||
ARG_0,
|
||||
ARG_COUNTDOWN,
|
||||
ARG_PRINTER_BACKEND,
|
||||
ARG_PRINTS_REMAINING,
|
||||
};
|
||||
|
||||
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 gboolean photo_booth_snapshot_prepare (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);
|
||||
|
||||
/* libgphoto2 */
|
||||
|
@ -108,15 +112,25 @@ static void photo_booth_class_init (PhotoBoothClass *klass)
|
|||
GST_DEBUG ("photo_booth_class_init");
|
||||
gp_log_add_func(GP_LOG_ERROR, _gphoto_err, NULL);
|
||||
|
||||
gobject_class->finalize = photo_booth_finalize;
|
||||
gobject_class->set_property = photo_booth_set_property;
|
||||
gobject_class->get_property = photo_booth_get_property;
|
||||
gobject_class->finalize = photo_booth_finalize;
|
||||
gobject_class->dispose = photo_booth_dispose;
|
||||
gobject_class->set_property = photo_booth_set_property;
|
||||
gobject_class->get_property = photo_booth_get_property;
|
||||
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_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_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)
|
||||
|
@ -157,10 +171,7 @@ static void photo_booth_init (PhotoBooth *pb)
|
|||
g_application_quit (G_APPLICATION (pb));
|
||||
}
|
||||
|
||||
pb->capture_thread = NULL;
|
||||
pb->capture_thread = g_thread_try_new ("gphoto-capture", (GThreadFunc) photo_booth_capture_thread_func, pb, NULL);
|
||||
|
||||
priv->settings = NULL;
|
||||
priv->capture_thread = NULL;
|
||||
}
|
||||
|
||||
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);
|
||||
gtk_window_present (GTK_WINDOW (priv->win));
|
||||
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_get_printer_status (pb);
|
||||
}
|
||||
|
||||
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)
|
||||
{
|
||||
PhotoBooth *pb = PHOTO_BOOTH (object);
|
||||
PhotoBoothPrivate *priv = photo_booth_get_instance_private (pb);
|
||||
|
||||
GST_INFO_OBJECT (pb, "finalize");
|
||||
SEND_COMMAND (pb, CONTROL_STOP);
|
||||
photo_booth_flush_pipe (pb->video_fd);
|
||||
g_thread_join (pb->capture_thread);
|
||||
g_thread_join (priv->capture_thread);
|
||||
if (pb->cam_info)
|
||||
photo_booth_cam_close (&pb->cam_info);
|
||||
if (pb->video_fd)
|
||||
|
@ -205,7 +220,7 @@ static void photo_booth_dispose (GObject *object)
|
|||
{
|
||||
PhotoBoothPrivate *priv;
|
||||
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);
|
||||
}
|
||||
|
||||
|
@ -295,6 +310,9 @@ static void photo_booth_set_property (GObject * object, guint prop_id, const GVa
|
|||
case ARG_COUNTDOWN:
|
||||
priv->countdown = g_value_get_uint (value);
|
||||
break;
|
||||
case ARG_PRINTER_BACKEND:
|
||||
priv->printer_backend = g_strdup (g_value_get_string (value));
|
||||
break;
|
||||
default:
|
||||
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
|
||||
break;
|
||||
|
@ -308,7 +326,13 @@ static void photo_booth_get_property (GObject * object, guint prop_id, GValue *
|
|||
|
||||
switch (prop_id) {
|
||||
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;
|
||||
default:
|
||||
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)
|
||||
{
|
||||
PhotoboothCaptureThreadState state = CAPTURE_INIT;
|
||||
|
||||
GST_DEBUG_OBJECT (pb, "enter capture thread fd = %d", pb->video_fd);
|
||||
|
||||
PhotoBoothPrivate *priv = photo_booth_get_instance_private (pb);
|
||||
CameraFile *gp_file = NULL;
|
||||
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)
|
||||
{
|
||||
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;
|
||||
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.");
|
||||
}
|
||||
timeout = 5000;
|
||||
}
|
||||
else if (state == CAPTURE_PAUSED)
|
||||
|
@ -399,11 +425,11 @@ static void photo_booth_capture_thread_func (PhotoBooth *pb)
|
|||
{
|
||||
if (pb->cam_info)
|
||||
{
|
||||
gtk_label_set_text (priv->win->status, "taking photo...");
|
||||
ret = photo_booth_take_photo (pb->cam_info);
|
||||
if (ret)
|
||||
g_main_context_invoke (NULL, (GSourceFunc) photo_booth_snapshot_taken, pb);
|
||||
else
|
||||
{
|
||||
else {
|
||||
GST_ERROR_OBJECT (pb, "taking photo failed!");
|
||||
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);
|
||||
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)));
|
||||
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!");
|
||||
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:
|
||||
{
|
||||
GST_DEBUG ("GST_MESSAGE_STREAM_START! state=%i", pb->state);
|
||||
// if (pb->state == PB_STATE_ASKING)
|
||||
// {
|
||||
// }
|
||||
}
|
||||
default:
|
||||
{
|
||||
|
@ -727,6 +750,7 @@ static void photo_booth_video_widget_ready (PhotoBooth *pb)
|
|||
|
||||
static gboolean photo_booth_preview (PhotoBooth *pb)
|
||||
{
|
||||
PhotoBoothPrivate *priv = photo_booth_get_instance_private (pb);
|
||||
GstPad *pad;
|
||||
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_DEBUG_OBJECT (pb, "photo_booth_preview done");
|
||||
pb->state = PB_STATE_PREVIEW;
|
||||
gtk_label_set_text (priv->win->status, "camera ready, showing preview video");
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
|
@ -754,8 +779,6 @@ void photo_booth_background_clicked (GtkWidget *widget, GdkEventButton *event, P
|
|||
{
|
||||
PhotoBoothPrivate *priv;
|
||||
PhotoBooth *pb = PHOTO_BOOTH_FROM_WINDOW (win);
|
||||
GST_DEBUG_OBJECT (widget, "photo_booth_background_clicked state=%d", pb->state);
|
||||
|
||||
switch (pb->state) {
|
||||
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)
|
||||
{
|
||||
PhotoBoothPrivate *priv;
|
||||
|
@ -914,12 +1003,14 @@ static gboolean photo_booth_take_photo (CameraInfo *cam_info)
|
|||
|
||||
static gboolean photo_booth_snapshot_taken (PhotoBooth *pb)
|
||||
{
|
||||
PhotoBoothPrivate *priv = photo_booth_get_instance_private (pb);
|
||||
GstElement *appsrc;
|
||||
GstBuffer *buffer;
|
||||
GstFlowReturn flowret;
|
||||
GstPad *pad;
|
||||
|
||||
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");
|
||||
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)
|
||||
GST_ERROR_OBJECT (appsrc, "couldn't push %" GST_PTR_FORMAT " to appsrc", buffer);
|
||||
gst_object_unref (appsrc);
|
||||
GST_INFO_OBJECT (pb, "photo_booth_snapshot now waiting for user input... PB_STATE_ASKING");
|
||||
|
||||
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);
|
||||
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_label_set_text (priv->win->status, "Print Photo? Touch background to cancel!");
|
||||
return GST_PAD_PROBE_PASS;
|
||||
}
|
||||
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)
|
||||
{
|
||||
PhotoBoothPrivate *priv;
|
||||
GST_DEBUG_OBJECT (pb, "!!!PRINT!!!");
|
||||
priv = photo_booth_get_instance_private (pb);
|
||||
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");
|
||||
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);
|
||||
gtk_widget_hide (GTK_WIDGET (priv->win->button_yes));
|
||||
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)
|
||||
|
@ -1033,7 +1135,7 @@ int main (int argc, char *argv[])
|
|||
PhotoBooth *pb;
|
||||
int ret;
|
||||
|
||||
gst_init (&argc, &argv);
|
||||
gst_init (0, NULL);
|
||||
|
||||
pb = photo_booth_new ();
|
||||
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
}
|
||||
|
||||
.countdown_label {
|
||||
color: rgba (150, 200, 200, 255);
|
||||
color: rgba (150, 200, 200, 1);
|
||||
text-shadow: 10px 10px #000000
|
||||
font-weight: bold;
|
||||
font-size: 80px;
|
||||
|
@ -11,9 +11,20 @@
|
|||
}
|
||||
|
||||
.button_yes {
|
||||
color: rgba (0, 0, 110, 255);
|
||||
background: rgba (168, 233, 255, 200);
|
||||
color: rgba (0, 0, 110, 1);
|
||||
background: rgba (168, 233, 255, 0.9);
|
||||
font-weight: bold;
|
||||
font-size: 80px;
|
||||
padding: 20px 20px 20px 20px;
|
||||
}
|
||||
|
||||
.statusbox {
|
||||
background: rgba (0, 0, 0, 0.8);
|
||||
}
|
||||
|
||||
.status_label {
|
||||
color: white;
|
||||
font-weight: bold;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
|
|
|
@ -110,7 +110,6 @@ struct _PhotoBooth
|
|||
gulong photo_block_id;
|
||||
|
||||
int control_sock[2];
|
||||
GThread *capture_thread;
|
||||
PhotoboothState state;
|
||||
};
|
||||
|
||||
|
|
123
photobooth.ui
123
photobooth.ui
|
@ -13,15 +13,12 @@
|
|||
<property name="can_focus">False</property>
|
||||
<property name="events">GDK_BUTTON_PRESS_MASK</property>
|
||||
<signal name="button-press-event" handler="photo_booth_background_clicked" swapped="no"/>
|
||||
<child>
|
||||
<placeholder/>
|
||||
</child>
|
||||
<child type="overlay">
|
||||
<object class="GtkLabel" id="countdown_label">
|
||||
<property name="width_request">400</property>
|
||||
<property name="height_request">300</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="valign">center</property>
|
||||
<property name="label" translatable="yes">label</property>
|
||||
|
@ -36,26 +33,6 @@
|
|||
<class name="countdown_label"/>
|
||||
</style>
|
||||
</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>
|
||||
<property name="index">2</property>
|
||||
</packing>
|
||||
|
@ -89,7 +66,103 @@
|
|||
</object>
|
||||
<packing>
|
||||
<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>
|
||||
</child>
|
||||
<style>
|
||||
|
|
|
@ -14,7 +14,7 @@
|
|||
*/
|
||||
|
||||
#include <gtk/gtk.h>
|
||||
|
||||
#include <time.h>
|
||||
#include "photobooth.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);
|
||||
#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)
|
||||
{
|
||||
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, 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, 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)
|
||||
|
@ -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);
|
||||
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)
|
||||
|
@ -72,6 +79,21 @@ void photo_booth_window_add_gtkgstwidget (PhotoBoothWindow *win, GtkWidget *gtkg
|
|||
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)
|
||||
{
|
||||
PhotoBoothWindowPrivate *priv;
|
||||
|
@ -97,6 +119,8 @@ gboolean _pbw_tick_countdown (PhotoBoothWindow *win)
|
|||
GST_DEBUG ("_pbw_tick_countdown %i", priv->countdown);
|
||||
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);
|
||||
gtk_label_set_text (priv->countdown_label, str);
|
||||
g_free (str);
|
||||
|
|
|
@ -35,6 +35,7 @@ struct _PhotoBoothWindow
|
|||
GtkApplicationWindow parent;
|
||||
GtkWidget *gtkgstwidget;
|
||||
GtkButton *button_yes;
|
||||
GtkLabel *status_clock, *status, *status_printer;
|
||||
};
|
||||
|
||||
struct _PhotoBoothWindowClass
|
||||
|
|
Loading…
Add table
Reference in a new issue