commit ceb1f6b91c628390be9360939f35643d209789e4
parent 1633c87b8b96e2b8437d7e1c1149812ed2c5550a
Author: lash <dev@holbrook.no>
Date: Sun, 10 Mar 2024 03:34:07 +0000
Replace gstreamer scan code with zbar tee
Diffstat:
7 files changed, 274 insertions(+), 50 deletions(-)
diff --git a/src/gtk/kee-uicontext.c b/src/gtk/kee-uicontext.c
@@ -93,13 +93,14 @@ static void kee_uicontext_class_init(KeeUicontextClass *kls) {
static void kee_uicontext_init(KeeUicontext *self) {
//KeeUicontextPrivate *o = kee_uicontext_get_instance_private(self);
-
}
void kee_uicontext_scanstart(KeeUicontext *o) {
if (KEE_UI_STATE_IS_SCANNING(o->ui)) {
+ g_log(G_LOG_DOMAIN, G_LOG_LEVEL_WARNING, "already in scanning state");
return;
}
+ ui_state_change(o->ui, KEE_UI_STATE_SCANNING, 0);
g_signal_emit(o, kee_sigs[SCAN_WANT], 0);
}
diff --git a/src/gtk/main.c b/src/gtk/main.c
@@ -22,7 +22,6 @@ static void deactivate(GtkApplication *app, gpointer user_data) {
ui_free(user_data);
}
-
int main(int argc, char **argv) {
int r;
KeeUicontext *uctx;
@@ -36,7 +35,6 @@ int main(int argc, char **argv) {
kee_context_new(&ctx, &ui);
uctx = g_object_new(KEE_TYPE_UICONTEXT, "ui_container", &ui, "core_context", &ctx, NULL);
- g_object_set(&uctx, "app", ui.gapp, NULL);
settings_new_from_xdg(&ctx.settings);
settings_init(&ctx.settings);
@@ -45,11 +43,10 @@ int main(int argc, char **argv) {
g_signal_connect (ui.gapp, "startup", G_CALLBACK (startup), uctx);
g_signal_connect (ui.gapp, "activate", G_CALLBACK (activate), &ui);
g_signal_connect (ui.gapp, "shutdown", G_CALLBACK (deactivate), uctx);
- //g_signal_connect (uctx, "scan_want", G_CALLBACK( tmpscan ), NULL);
+ g_signal_connect (uctx, "scan_want", G_CALLBACK( ui_handle_scan) , &ui);
r = g_application_run (G_APPLICATION (ui.gapp), argc, argv);
-
g_object_unref(ui.gapp);
return r;
}
diff --git a/src/gtk/menu.c b/src/gtk/menu.c
@@ -3,16 +3,20 @@
#include "kee-uicontext.h"
-static void act_scan(GSimpleAction *act, GVariant *param, GApplication *app) {
+static void act_scan(GSimpleAction *act, GVariant *param, KeeUicontext *ui) {
//GDBusConnection *conn;
g_log(G_LOG_DOMAIN, G_LOG_LEVEL_DEBUG, "scan clicked");
+ kee_uicontext_scanstart(ui);
//conn = g_application_get_dbus_connection(app);
}
-static void act_quit(GSimpleAction *act, GVariant *param, GApplication *app) {
- g_application_quit(app);
+static void act_quit(GSimpleAction *act, GVariant *param, KeeUicontext *ui) {
+ GApplication *gapp;
+ g_object_get(ui, "app", &gapp, NULL);
+
+ g_application_quit(gapp);
}
//void menu_setup(struct ui_container *ui) {
@@ -39,11 +43,11 @@ void menu_setup(KeeUicontext *ui) {
act = g_simple_action_new("quit", NULL);
g_action_map_add_action(G_ACTION_MAP(gapp), G_ACTION(act));
- g_signal_connect(act, "activate", G_CALLBACK(act_quit), gapp);
+ g_signal_connect(act, "activate", G_CALLBACK(act_quit), ui);
act = g_simple_action_new("scan", NULL);
g_action_map_add_action(G_ACTION_MAP(gapp), G_ACTION(act));
- g_signal_connect(act, "activate", G_CALLBACK(act_scan), gapp);
+ g_signal_connect(act, "activate", G_CALLBACK(act_scan), ui);
g_menu_item_set_submenu(menu_item_menu, G_MENU_MODEL(menu));
g_object_unref(menu);
diff --git a/src/gtk/scan.c b/src/gtk/scan.c
@@ -4,61 +4,190 @@
#include "scan.h"
#include "err.h"
-gboolean msg_func(GstBus *bus, GstMessage *msg, gpointer user_data) {
- return false;
-}
+//gboolean msg_func(GstBus *bus, GstMessage *msg, gpointer user_data) {
+// GError *err;
+// gchar *debug_info;
+// GstState oldstate;
+// GstState newstate;
+// GstState pendingstate;
+// //struct _gst_data *data;
+// //GstStateChangeReturn rsc;
+//
+// //data = (struct _gst_data*)user_data;
+//
+// switch (GST_MESSAGE_TYPE (msg)) {
+// case GST_MESSAGE_ERROR:
+// gst_message_parse_error(msg, &err, &debug_info);
+// g_log(G_LOG_DOMAIN, G_LOG_LEVEL_WARNING, "logg %s: %s", GST_OBJECT_NAME(msg->src), err->message);
+// g_log(G_LOG_DOMAIN, G_LOG_LEVEL_WARNING, "debug %s", debug_info ? debug_info : "none");
+// g_clear_error(&err);
+// g_free(debug_info);
+// break;
+// case GST_MESSAGE_EOS:
+// g_log(G_LOG_DOMAIN, G_LOG_LEVEL_INFO, "eos");
+// break;
+// case GST_MESSAGE_STATE_CHANGED:
+// gst_message_parse_state_changed(msg, &oldstate, &newstate, &pendingstate);
+// g_log(G_LOG_DOMAIN, G_LOG_LEVEL_INFO, "state change: %s -> %s", gst_element_state_get_name(oldstate), gst_element_state_get_name(newstate));
+// break;
+// default:
+// g_log(G_LOG_DOMAIN, G_LOG_LEVEL_ERROR, "unknown message (ext %d): %s", GST_MESSAGE_TYPE_IS_EXTENDED(msg), GST_MESSAGE_TYPE_NAME(msg));
+// }
+//
+// return false;
+//}
+
+//int scan_init(struct kee_scanner *scan) {
+// GdkPaintable *img;
+// GstCaps *caps;
+// GstElement *convert;
+// GstElement *filter;
+// GstStateChangeReturn rsc;
+// gchar *txt;
+//
+// memset(scan, 0, sizeof(struct kee_scanner));
+//
+// scan->pipeline = gst_pipeline_new("kee-qr-scan");
+// scan->source = gst_element_factory_make("v4l2src", "v4l2src");
+// scan->video_sink = gst_element_factory_make("gtk4paintablesink", "gtk4paintablesink");
+// if (scan->video_sink == NULL) {
+// g_log(G_LOG_DOMAIN, G_LOG_LEVEL_WARNING, "cant create gl sinks");
+// return 1;
+// }
+// //g_object_set(G_OBJECT(scan->source), "device", "/dev/video0", NULL);
+// g_object_set(G_OBJECT(scan->source), "device", "/dev/video4", NULL);
+//
+// g_object_get(scan->video_sink, "paintable", &img, NULL);
+// if (!img) {
+// g_log(G_LOG_DOMAIN, G_LOG_LEVEL_WARNING, "failed getting paintable");
+// return 1;
+// }
+//
+// g_object_get(scan->source, "device", &txt, NULL);
+// g_log(G_LOG_DOMAIN, G_LOG_LEVEL_INFO, "using video device: %s", txt);
+//
+// filter = gst_element_factory_make("capsfilter", "capsfilter");
+// convert = gst_element_factory_make("videoconvert", "videoconvert");
+// caps = gst_caps_new_simple("video/x-raw", "format", G_TYPE_STRING, "RGB", "width", G_TYPE_INT, 640, "height", G_TYPE_INT, 480, "framerate", GST_TYPE_FRACTION, 30, 1, "pixel-aspect-ratio", GST_TYPE_FRACTION, 1, 1, NULL);
+// g_object_set(G_OBJECT(filter), "caps", caps, NULL);
+// scan->video_view = GTK_PICTURE(gtk_picture_new_for_paintable(img));
+// gtk_picture_set_content_fit(scan->video_view, GTK_CONTENT_FIT_CONTAIN);
+// gst_bin_add_many(GST_BIN(scan->pipeline), scan->source, convert, filter, scan->video_sink, NULL);
+//
+// if (gst_element_link_many(scan->source, convert, filter, scan->video_sink, NULL) != TRUE) {
+// g_log(G_LOG_DOMAIN, G_LOG_LEVEL_ERROR, "gst pipeline link fail");
+// gst_object_unref(scan->pipeline);
+// return 1;
+// }
+//
+// rsc = gst_element_set_state(scan->pipeline, GST_STATE_PLAYING);
+// if (rsc == GST_STATE_CHANGE_FAILURE) {
+// g_log(G_LOG_DOMAIN, G_LOG_LEVEL_ERROR, "fail set pipeline to pause");
+// gst_object_unref(scan->pipeline);
+// return 1;
+// }
+//
+// return ERR_OK;
+//}
int scan_init(struct kee_scanner *scan) {
- GdkPaintable *img;
- GstCaps *caps;
- GstElement *convert;
- GstElement *filter;
+ GstElement *tee;
+ GstElement *zbar;
+ GstElement *queue_display;
+ GstElement *queue_scan;
+ GstPad *queue_display_pad;
+ GstPad *queue_scan_pad;
+ GstPad *tee_display;
+ GstPad *tee_scan;
GstStateChangeReturn rsc;
- GstBus *bus;
- gchar *txt;
+ GstElement *convert_display;
+ GstElement *convert_scan;
+ GstElement *filter;
+ GstElement *devnull;
+ GstCaps *caps;
+ GdkPaintable *img;
+ g_log(G_LOG_DOMAIN, G_LOG_LEVEL_DEBUG, "camera setup");
memset(scan, 0, sizeof(struct kee_scanner));
- scan->pipeline = gst_pipeline_new("kee-qr-scan");
+ scan->pipeline = gst_pipeline_new("webcam-zbar");
scan->source = gst_element_factory_make("v4l2src", "v4l2src");
+ filter = gst_element_factory_make("capsfilter", "capsfilter");
+ convert_display = gst_element_factory_make("videoconvert", "videoconvert_display");
+ convert_scan = gst_element_factory_make("videoconvert", "videoconvert_scan");
scan->video_sink = gst_element_factory_make("gtk4paintablesink", "gtk4paintablesink");
- if (scan->video_sink == NULL) {
- g_log(G_LOG_DOMAIN, G_LOG_LEVEL_WARNING, "cant create gl sinks");
+ tee = gst_element_factory_make("tee", "split-view-zbar");
+ zbar = gst_element_factory_make("zbar", "zbar");
+ devnull = gst_element_factory_make("fakesink", "fakesink");
+ queue_display = gst_element_factory_make("queue", "queue-display");
+ queue_scan = gst_element_factory_make("queue", "queue-scan");
+
+ caps = gst_caps_new_simple("video/x-raw", "format", G_TYPE_STRING, "RGB", "width", G_TYPE_INT, 640, "height", G_TYPE_INT, 360, "framerate", GST_TYPE_FRACTION, 30, 1, "pixel-aspect-ratio", GST_TYPE_FRACTION, 1, 1, NULL);
+ g_object_set(G_OBJECT(filter), "caps", caps, NULL);
+
+ gst_bin_add_many(GST_BIN(scan->pipeline), scan->source, scan->video_sink, convert_display, convert_scan, filter, tee, zbar, queue_display, queue_scan, devnull, NULL);
+ if (gst_element_link_many(scan->source, tee, NULL) != TRUE) {
+ g_log(G_LOG_DOMAIN, G_LOG_LEVEL_ERROR, "fail link src to muxer");
+ gst_object_unref(scan->pipeline);
+ return 1;
+ }
+
+ if (gst_element_link_many(queue_display, convert_display, filter, scan->video_sink, NULL) != TRUE) {
+ g_log(G_LOG_DOMAIN, G_LOG_LEVEL_ERROR, "fail link muxer to display");
+ gst_object_unref(scan->pipeline);
return 1;
}
- g_object_set(G_OBJECT(scan->source), "device", "/dev/video0", NULL);
+ if (gst_element_link_many(queue_scan, convert_scan, zbar, devnull, NULL) != TRUE) {
+ g_log(G_LOG_DOMAIN, G_LOG_LEVEL_ERROR, "fail link muxer to scanner");
+ gst_object_unref(scan->pipeline);
+ return 1;
+ }
+
+ tee_display = gst_element_request_pad_simple(tee, "src_%u");
+ queue_display_pad = gst_element_get_static_pad(queue_display, "sink");
+ g_log(G_LOG_DOMAIN, G_LOG_LEVEL_DEBUG, "display queue linked with %s", gst_pad_get_name(tee_display));
+ tee_scan = gst_element_request_pad_simple(tee, "src_%u");
+ queue_scan_pad = gst_element_get_static_pad(queue_scan, "sink");
+ g_log(G_LOG_DOMAIN, G_LOG_LEVEL_DEBUG, "scan queue linked with %s", gst_pad_get_name(tee_scan));
+
+ g_object_set(G_OBJECT(scan->source), "device", "/dev/video4", NULL);
g_object_get(scan->video_sink, "paintable", &img, NULL);
if (!img) {
g_log(G_LOG_DOMAIN, G_LOG_LEVEL_WARNING, "failed getting paintable");
return 1;
}
-
- g_object_get(scan->source, "device", &txt, NULL);
- g_log(G_LOG_DOMAIN, G_LOG_LEVEL_INFO, "using video device: %s", txt);
-
- filter = gst_element_factory_make("capsfilter", "capsfilter");
- convert = gst_element_factory_make("videoconvert", "videoconvert");
- caps = gst_caps_new_simple("video/x-raw", "format", G_TYPE_STRING, "RGB", "width", G_TYPE_INT, 640, "height", G_TYPE_INT, 480, "framerate", GST_TYPE_FRACTION, 30, 1, "pixel-aspect-ratio", GST_TYPE_FRACTION, 1, 1, NULL);
- g_object_set(G_OBJECT(filter), "caps", caps, NULL);
scan->video_view = GTK_PICTURE(gtk_picture_new_for_paintable(img));
- gst_bin_add_many(GST_BIN(scan->pipeline), scan->source, convert, filter, scan->video_sink, NULL);
+ gtk_picture_set_content_fit(scan->video_view, GTK_CONTENT_FIT_CONTAIN);
- if (gst_element_link_many(scan->source, convert, filter, scan->video_sink, NULL) != TRUE) {
- g_log(G_LOG_DOMAIN, G_LOG_LEVEL_ERROR, "gst pipeline link fail");
+ if (gst_pad_link(tee_display, queue_display_pad) != GST_PAD_LINK_OK) {
+ g_log(G_LOG_DOMAIN, G_LOG_LEVEL_ERROR, "mux link fail for display");
gst_object_unref(scan->pipeline);
return 1;
}
- rsc = gst_element_set_state(scan->pipeline, GST_STATE_PAUSED);
+ if (gst_pad_link(tee_scan, queue_scan_pad) != GST_PAD_LINK_OK) {
+ g_log(G_LOG_DOMAIN, G_LOG_LEVEL_ERROR, "mux link fail for scan");
+ gst_object_unref(scan->pipeline);
+ return 1;
+ }
+
+ g_object_unref(queue_display_pad);
+ g_object_unref(queue_scan_pad);
+
+ rsc = gst_element_set_state(scan->pipeline, GST_STATE_PLAYING);
if (rsc == GST_STATE_CHANGE_FAILURE) {
g_log(G_LOG_DOMAIN, G_LOG_LEVEL_ERROR, "fail set pipeline to pause");
gst_object_unref(scan->pipeline);
return 1;
}
- bus = gst_element_get_bus(scan->pipeline);
- gst_bus_add_watch(bus, msg_func, &scan);
-
return ERR_OK;
}
+
+void scan_set_handler(struct kee_scanner *scan, gboolean(*fn)(GstBus *bus, GstMessage *msg, gpointer user_data)) {
+ GstBus *bus;
+
+ bus = gst_element_get_bus(scan->pipeline);
+ gst_bus_add_watch(bus, fn, scan);
+}
diff --git a/src/gtk/scan.h b/src/gtk/scan.h
@@ -13,5 +13,6 @@ struct kee_scanner {
};
int scan_init(struct kee_scanner *scan);
+void scan_set_handler(struct kee_scanner *scan, gboolean(*fn)(GstBus *bus, GstMessage *msg, gpointer user_data));
#endif // _KEE_GTK_SCAN_H
diff --git a/src/gtk/ui.c b/src/gtk/ui.c
@@ -1,3 +1,4 @@
+#include <string.h>
#include <gtk/gtk.h>
#include <gst/gst.h>
@@ -13,7 +14,7 @@ int ui_init(struct ui_container *ui) {
if (ui->gapp == NULL) {
return ERR_FAIL;
}
- //scan_init(&ui->scan);
+ ui->state = 0;
return ERR_OK;
}
@@ -46,7 +47,19 @@ GtkWidget* ui_build_unlock(struct ui_container *ui) {
return GTK_WIDGET(box);
}
-GtkWidget* ui_build_view(struct ui_container *ui) {
+static GtkWidget* ui_build_scan(struct ui_container *ui) {
+ GtkWidget *label;
+
+ ui->front_scan = GTK_BOX(gtk_box_new(GTK_ORIENTATION_VERTICAL, 10));
+
+ label = gtk_label_new("please scan qr code");
+ gtk_box_append(GTK_BOX(ui->front_scan), label);
+
+ return GTK_WIDGET(ui->front_scan);
+}
+
+
+static GtkWidget* ui_build_view(struct ui_container *ui) {
GtkSelectionModel *sel;
GtkListItemFactory *factory;
@@ -60,8 +73,7 @@ GtkWidget* ui_build_view(struct ui_container *ui) {
}
void ui_build(GtkApplication *app, struct ui_container *ui) {
- GtkWidget *unlock;
- GtkWidget *view;
+ GtkWidget *widget;
ui->win = GTK_APPLICATION_WINDOW(gtk_application_window_new (app));
ui->stack = GTK_STACK(gtk_stack_new());
@@ -72,12 +84,14 @@ void ui_build(GtkApplication *app, struct ui_container *ui) {
gtk_window_set_child(GTK_WINDOW(ui->win), GTK_WIDGET(ui->stack));
gtk_application_window_set_show_menubar(GTK_APPLICATION_WINDOW(ui->win), TRUE);
- unlock = ui_build_unlock(ui);
- gtk_stack_add_child(ui->stack, unlock);
- view = ui_build_view(ui);
- gtk_stack_add_child(ui->stack, view);
+ widget = ui_build_unlock(ui);
+ gtk_stack_add_child(ui->stack, widget);
+ gtk_stack_set_visible_child(GTK_STACK(ui->stack), widget);
+ widget = ui_build_view(ui);
+ gtk_stack_add_child(ui->stack, widget);
+ widget = ui_build_scan(ui);
+ gtk_stack_add_child(ui->stack, widget);
- gtk_stack_set_visible_child(GTK_STACK(ui->stack), unlock);
//gtk_stack_set_visible_child(GTK_STACK(ui->stack), view);
gtk_window_present(GTK_WINDOW (ui->win));
@@ -112,3 +126,77 @@ int ui_state_change(struct ui_container *ui, int set, int reset) {
ui->state |= set;
return ui->state;
}
+
+gboolean ui_scan_code_handler(GstBus *bus, GstMessage *msg, gpointer user_data) {
+ GError *err;
+ gchar *debug_info;
+ GstState oldstate;
+ GstState newstate;
+ GstState pendingstate;
+ const gchar *src;
+ const gchar *code;
+ const GstStructure *strctr;
+ //struct _gst_data *data;
+ //GstStateChangeReturn rsc;
+
+ //data = (struct _gst_data*)user_data;
+
+ g_log(G_LOG_DOMAIN, G_LOG_LEVEL_DEBUG, "scan msg got");
+
+ switch (GST_MESSAGE_TYPE (msg)) {
+ case GST_MESSAGE_ERROR:
+ gst_message_parse_error(msg, &err, &debug_info);
+ g_log(G_LOG_DOMAIN, G_LOG_LEVEL_WARNING, "logg %s: %s", GST_OBJECT_NAME(msg->src), err->message);
+ g_log(G_LOG_DOMAIN, G_LOG_LEVEL_WARNING, "debug %s", debug_info ? debug_info : "none");
+ g_clear_error(&err);
+ g_free(debug_info);
+ break;
+ case GST_MESSAGE_EOS:
+ g_log(G_LOG_DOMAIN, G_LOG_LEVEL_INFO, "eos");
+ break;
+ case GST_MESSAGE_STATE_CHANGED:
+ gst_message_parse_state_changed(msg, &oldstate, &newstate, &pendingstate);
+ g_log(G_LOG_DOMAIN, G_LOG_LEVEL_INFO, "state change: %s -> %s", gst_element_state_get_name(oldstate), gst_element_state_get_name(newstate));
+ break;
+ case GST_MESSAGE_ELEMENT:
+ src = gst_object_get_name(msg->src);
+ if (strcmp(src, "zbar")) {
+ break;
+ }
+ strctr = gst_message_get_structure(msg);
+ code = gst_structure_get_string(strctr, "symbol");
+ g_log(G_LOG_DOMAIN, G_LOG_LEVEL_DEBUG, "message %s: %d (%s) - decoded: %s", src, msg->type, gst_message_type_get_name(msg->type), code);
+ break;
+ default:
+ g_log(G_LOG_DOMAIN, G_LOG_LEVEL_WARNING, "unknown message (ext %d): %s", GST_MESSAGE_TYPE_IS_EXTENDED(msg), GST_MESSAGE_TYPE_NAME(msg));
+ break;
+ }
+
+ return true;
+}
+
+GtkWidget* ui_build_scan_attach(struct ui_container *ui) {
+ int r;
+ struct kee_scanner *scan;
+ GtkWidget *view;
+
+ scan = &ui->scan;
+ r = scan_init(scan);
+ if (r) {
+ g_log(G_LOG_DOMAIN, G_LOG_LEVEL_ERROR, "fail scan setup");
+ return NULL;
+ }
+ view = GTK_WIDGET(scan->video_view);
+ gtk_box_append(GTK_BOX(ui->front_scan), view);
+ scan_set_handler(scan, ui_scan_code_handler);
+ return view;
+}
+
+void ui_handle_scan(GtkApplication *app, struct ui_container *ui) {
+ if (!(ui->state & KEE_UI_STATE_SCAN_INIT)) {
+ ui_build_scan_attach(ui);
+
+ }
+ ui_state_change(ui, KEE_UI_STATE_SCANNING | KEE_UI_STATE_SCAN_INIT, 0);
+ gtk_stack_set_visible_child(GTK_STACK(ui->stack), GTK_WIDGET(ui->front_scan));
+}
diff --git a/src/gtk/ui.h b/src/gtk/ui.h
@@ -7,11 +7,12 @@
typedef enum {
- KEE_UI_INIT = 1,
- KEE_UI_SCANNING = 2,
+ KEE_UI_STATE_INIT = 1,
+ KEE_UI_STATE_SCAN_INIT = 2,
+ KEE_UI_STATE_SCANNING = 4,
} UiState;
-#define KEE_UI_STATE_IS_SCANNING(c) c->state & KEE_UI_SCANNING
+#define KEE_UI_STATE_IS_SCANNING(c) c->state & KEE_UI_STATE_SCANNING
struct ui_container {
GtkApplication *gapp;
@@ -19,6 +20,7 @@ struct ui_container {
GtkStack *stack;
GListModel *front_list;
GtkListView *front_view;
+ GtkBox *front_scan;
struct kee_scanner scan;
int state;
};
@@ -28,4 +30,6 @@ void ui_build(GtkApplication *app, struct ui_container *ui);
int ui_state_change(struct ui_container *ui, int set, int reset);
void ui_free(struct ui_container *ui);
+void ui_handle_scan(GtkApplication *app, struct ui_container *ui);
+
#endif // _UI_H