Compare commits

..

10 Commits

Author SHA1 Message Date
Andrey Skvortsov
5f2b7136ca main: replace sprintf with safer snprintf 2025-05-31 10:09:51 +00:00
Andrey Skvortsov
886fea58bd zbar_pipeline: fix memory leak, when pixel format is unsupported 2025-05-31 10:09:51 +00:00
Andrey Skvortsov
4c29f88069 postprocess: fix empty filename for UVC cameras
filename printed to stdout is used to bind to open filename
button. Without this nothing opened, when button was clicked.
2025-05-31 10:09:51 +00:00
Andrey Skvortsov
928714595d process_pipeline: correctly handle empty stdout
When stdout is empty and end is zero. There are several incorrect read and write
access.

Valgrind reported following:

```
==70402== Invalid write of size 1
==70402==    at 0x11F626: post_process_finished (process_pipeline.c:1266)
==70402==    by 0x53D7C32: ??? (in /usr/lib/x86_64-linux-gnu/libgio-2.0.so.0.8400.1)
==70402==    by 0x53D88D2: ??? (in /usr/lib/x86_64-linux-gnu/libgio-2.0.so.0.8400.1)
==70402==    by 0x53D6627: ??? (in /usr/lib/x86_64-linux-gnu/libgio-2.0.so.0.8400.1)
==70402==    by 0x53D7C32: ??? (in /usr/lib/x86_64-linux-gnu/libgio-2.0.so.0.8400.1)
==70402==    by 0x53D7C6C: ??? (in /usr/lib/x86_64-linux-gnu/libgio-2.0.so.0.8400.1)
==70402==    by 0x55D5344: ??? (in /usr/lib/x86_64-linux-gnu/libglib-2.0.so.0.8400.1)
==70402==    by 0x55D7576: ??? (in /usr/lib/x86_64-linux-gnu/libglib-2.0.so.0.8400.1)
==70402==    by 0x55D7CDF: g_main_context_iteration (in /usr/lib/x86_64-linux-gnu/libglib-2.0.so.0.8400.1)
==70402==    by 0x5407EDC: g_application_run (in /usr/lib/x86_64-linux-gnu/libgio-2.0.so.0.8400.1)
==70402==    by 0x11BB5F: main (main.c:1551)
==70402==  Address 0xb84651f is 1 bytes before a block of size 16 alloc'd
==70402==    at 0x484BDD0: realloc (vg_replace_malloc.c:1801)
==70402==    by 0x55DE761: g_realloc (in /usr/lib/x86_64-linux-gnu/libglib-2.0.so.0.8400.1)
==70402==    by 0x53AE250: ??? (in /usr/lib/x86_64-linux-gnu/libgio-2.0.so.0.8400.1)
==70402==    by 0x53AE3C0: ??? (in /usr/lib/x86_64-linux-gnu/libgio-2.0.so.0.8400.1)
==70402==    by 0x53B6CFC: g_output_stream_write (in /usr/lib/x86_64-linux-gnu/libgio-2.0.so.0.8400.1)
==70402==    by 0x53B6E48: g_output_stream_write_all (in /usr/lib/x86_64-linux-gnu/libgio-2.0.so.0.8400.1)
==70402==    by 0x53D65EA: ??? (in /usr/lib/x86_64-linux-gnu/libgio-2.0.so.0.8400.1)
==70402==    by 0x53D7C32: ??? (in /usr/lib/x86_64-linux-gnu/libgio-2.0.so.0.8400.1)
==70402==    by 0x53D88D2: ??? (in /usr/lib/x86_64-linux-gnu/libgio-2.0.so.0.8400.1)
==70402==    by 0x53B7D8A: ??? (in /usr/lib/x86_64-linux-gnu/libgio-2.0.so.0.8400.1)
==70402==    by 0x53D7C32: ??? (in /usr/lib/x86_64-linux-gnu/libgio-2.0.so.0.8400.1)
==70402==    by 0x53D88D2: ??? (in /usr/lib/x86_64-linux-gnu/libgio-2.0.so.0.8400.1)
==70402==
==70402== Invalid read of size 1
==70402==    at 0x11F642: post_process_finished (process_pipeline.c:1270)
==70402==    by 0x53D7C32: ??? (in /usr/lib/x86_64-linux-gnu/libgio-2.0.so.0.8400.1)
==70402==    by 0x53D88D2: ??? (in /usr/lib/x86_64-linux-gnu/libgio-2.0.so.0.8400.1)
==70402==    by 0x53D6627: ??? (in /usr/lib/x86_64-linux-gnu/libgio-2.0.so.0.8400.1)
==70402==    by 0x53D7C32: ??? (in /usr/lib/x86_64-linux-gnu/libgio-2.0.so.0.8400.1)
==70402==    by 0x53D7C6C: ??? (in /usr/lib/x86_64-linux-gnu/libgio-2.0.so.0.8400.1)
==70402==    by 0x55D5344: ??? (in /usr/lib/x86_64-linux-gnu/libglib-2.0.so.0.8400.1)
==70402==    by 0x55D7576: ??? (in /usr/lib/x86_64-linux-gnu/libglib-2.0.so.0.8400.1)
==70402==    by 0x55D7CDF: g_main_context_iteration (in /usr/lib/x86_64-linux-gnu/libglib-2.0.so.0.8400.1)
==70402==    by 0x5407EDC: g_application_run (in /usr/lib/x86_64-linux-gnu/libgio-2.0.so.0.8400.1)
==70402==    by 0x11BB5F: main (main.c:1551)
```
2025-05-31 10:09:51 +00:00
Andrey Skvortsov
529e7841ab process_pipeline: replace sprintf with snprintf
to avoid potential buffer overflows
2025-05-31 10:09:51 +00:00
Andrey Skvortsov
f64782d9a5 main: fix memleak, when reading g_settings 2025-05-31 10:09:51 +00:00
Andrey Skvortsov
9567f00e01 process_pipeline: replace strcpy with snprintf
snprintf is always NULL-terminated in compare to strcpy.
2025-05-31 10:09:51 +00:00
Andrey Skvortsov
22549185f8 process_pipeline: fix memleak in save_jpeg
```
==70481== 7,680 bytes in 2 blocks are definitely lost in loss record 64,498 of 64,727
==70481==    at 0x4844818: malloc (vg_replace_malloc.c:446)
==70481==    by 0x11EDA5: save_jpeg (process_pipeline.c:1016)
==70481==    by 0x11F090: process_image_for_capture_yuv (process_pipeline.c:1078)
==70481==    by 0x11F5BC: process_image_for_capture (process_pipeline.c:1249)
==70481==    by 0x11FAB3: process_image (process_pipeline.c:1393)
```
2025-05-31 10:09:51 +00:00
Andrey Skvortsov
17228b1afa Add possibility to use hardware volume buttons to take a picture
On many mobile operating systems it's possible to take picture using
hardware volume buttons.
It's easier to hold the phone and take pictures this way. Additionally
it makes possible to use selfie stick (at least with jack plug).

To be able to handle volume buttons global system shortcuts, that
controls audio level has to be disabled.

gdk_toplevel_inhibit_system_shortcuts tells compositor to block system
keyboard shortcuts, when the camera app has focus. In all other cases
system shortcuts are working like before even if camera app is running
in background.
2025-05-31 10:05:58 +00:00
Andrey Skvortsov
4db24ddc5e Automatically rotate image based on accelerometer values
Device orientation is provided by net.hadess.SensorProxy
iio-proxy-service. If there is no accelerometer on target platform,
then window orientation from X11/wayland is used as before.
2025-05-19 13:42:21 +03:00
11 changed files with 317 additions and 57 deletions

View File

@@ -33,6 +33,7 @@ MAIN_PICTURE="$BURST_DIR"/1
if [ -f "$MAIN_PICTURE.jpg" ]; then
# Just move over the main jpeg if this was a YUV capture
cp "$MAIN_PICTURE.jpg" "$TARGET_NAME.jpg"
echo "$TARGET_NAME.jpg"
# Clean up the temp dir containing the burst
rm -rf "$BURST_DIR"

View File

@@ -27,6 +27,9 @@ finish-args:
# camera shutter feedback
- --talk-name=org.sigxcpu.Feedback
# accelerometer sensor for orientation
- --system-talk-name=net.hadess.SensorProxy
modules:
- name: megapixels
buildsystem: meson

View File

@@ -54,6 +54,7 @@ executable('megapixels',
'src/matrix.c',
'src/pipeline.c',
'src/process_pipeline.c',
'src/rotation.c',
'src/zbar_pipeline.c',
'src/dcp.c',
resources,
@@ -126,6 +127,8 @@ if clang_format.found()
'src/pipeline.h',
'src/process_pipeline.c',
'src/process_pipeline.h',
'src/rotation.c',
'src/rotation.h',
'src/zbar_pipeline.c',
'src/zbar_pipeline.h',
'tools/camera_test.c',

View File

@@ -96,6 +96,8 @@ update_process_pipeline()
.preview_width = state_io.preview_width,
.preview_height = state_io.preview_height,
.device_rotation = state_io.device_rotation,
.device_accel_rotation = state_io.device_accel_rotation,
.device_accel_rotation_good = state_io.device_accel_rotation_good,
.gain.control = state_io.gain.control,
.gain.auto_control = state_io.gain.auto_control,
@@ -669,6 +671,8 @@ update_state(MPPipeline *pipeline, const mp_state_io *new_state)
state_io.preview_width = new_state->preview_width;
state_io.preview_height = new_state->preview_height;
state_io.device_rotation = new_state->device_rotation;
state_io.device_accel_rotation = new_state->device_accel_rotation;
state_io.device_accel_rotation_good = new_state->device_accel_rotation_good;
if (state_io.camera) {
state_io.gain.value = new_state->gain.value;

View File

@@ -4,6 +4,7 @@
#include "gl_util.h"
#include "io_pipeline.h"
#include "process_pipeline.h"
#include "rotation.h"
#include "state.h"
#include <asm/errno.h>
#include <assert.h>
@@ -120,6 +121,8 @@ update_io_pipeline()
.preview_width = state.preview_width,
.preview_height = state.preview_height,
.device_rotation = state.device_rotation,
.device_accel_rotation = state.device_accel_rotation,
.device_accel_rotation_good = state.device_accel_rotation_good,
.gain.control = state.gain.control,
.gain.auto_control = state.gain.auto_control,
@@ -315,7 +318,7 @@ struct capture_completed_args {
static bool
capture_completed(struct capture_completed_args *args)
{
strncpy(last_path, args->fname, 259);
snprintf(last_path, sizeof(last_path), args->fname);
gtk_image_set_from_paintable(GTK_IMAGE(thumb_last),
GDK_PAINTABLE(args->thumb));
@@ -615,7 +618,7 @@ run_open_last_action(GSimpleAction *action, GVariant *param, gpointer user_data)
if (strlen(last_path) == 0) {
return;
}
sprintf(uri, "file://%s", last_path);
snprintf(uri, sizeof(uri), "file://%s", last_path);
if (!g_app_info_launch_default_for_uri(uri, NULL, &error)) {
g_printerr("Could not launch image viewer for '%s': %s\n",
uri,
@@ -628,7 +631,7 @@ run_open_photos_action(GSimpleAction *action, GVariant *param, gpointer user_dat
{
char uri[270];
g_autoptr(GError) error = NULL;
sprintf(uri, "file://%s", g_get_user_special_dir(G_USER_DIRECTORY_PICTURES));
snprintf(uri, sizeof(uri), "file://%s", g_get_user_special_dir(G_USER_DIRECTORY_PICTURES));
if (!g_app_info_launch_default_for_uri(uri, NULL, &error)) {
g_printerr("Could not launch image viewer: %s\n", error->message);
}
@@ -1291,6 +1294,17 @@ xevent_handler(GdkDisplay *display, XEvent *xevent, gpointer data)
}
#endif // GDK_WINDOWING_X11
static void
rotation_sensor_handler(int rotation, bool good)
{
if (rotation != state.device_accel_rotation ||
good != state.device_accel_rotation_good) {
state.device_accel_rotation = rotation;
state.device_accel_rotation_good = good;
update_io_pipeline();
}
}
static void
activate(GtkApplication *app, gpointer data)
{
@@ -1381,7 +1395,12 @@ activate(GtkApplication *app, gpointer data)
create_simple_action(app, "quit", G_CALLBACK(run_quit_action));
// Setup shortcuts
const char *capture_accels[] = { "space", NULL };
const char *capture_accels[] = {
"space",
"AudioLowerVolume",
"AudioRaiseVolume",
NULL
};
gtk_application_set_accels_for_action(app, "app.capture", capture_accels);
const char *quit_accels[] = { "<Ctrl>q", "<Ctrl>w", NULL };
@@ -1394,15 +1413,17 @@ activate(GtkApplication *app, gpointer data)
// Initialize the postprocessing gsetting to the old processor if
// it was not set yet
if (setting_postproc == NULL || setting_postproc[0] == '\0') {
const int size = 512;
printf("Initializing postprocessor gsetting\n");
setting_postproc = malloc(512);
if (!mp_process_find_processor(setting_postproc, "postprocess.sh")) {
setting_postproc = malloc(size);
if (!mp_process_find_processor(setting_postproc, size, "postprocess.sh")) {
printf("No processor found\n");
exit(1);
}
g_settings_set_string(settings, "postprocessor", setting_postproc);
printf("Initialized postprocessor to %s\n", setting_postproc);
}
free(setting_postproc);
// Find all postprocessors for the settings list
mp_process_find_all_processors(setting_postprocessor_list);
@@ -1472,6 +1493,7 @@ activate(GtkApplication *app, gpointer data)
GDBusConnection *conn =
g_application_get_dbus_connection(G_APPLICATION(app));
mp_flash_gtk_init(conn);
mp_device_rotation_init(rotation_sensor_handler);
if (state.configuration->count > 0) {
mp_io_pipeline_start();
@@ -1481,6 +1503,12 @@ activate(GtkApplication *app, gpointer data)
gtk_application_add_window(app, GTK_WINDOW(window));
gtk_widget_set_visible(window, true);
// Inhibit system shortcuts to be able to use
// VolumeUp and VolumeDown hardware buttons to take a picture
GtkNative *native = gtk_widget_get_native (GTK_WIDGET (window));
GdkSurface *surface = gtk_native_get_surface (native);
gdk_toplevel_inhibit_system_shortcuts (GDK_TOPLEVEL (surface), NULL);
}
static void
@@ -1501,6 +1529,7 @@ shutdown(GApplication *app, gpointer data)
#ifdef DEBUG
mp_io_pipeline_stop();
mp_flash_gtk_clean();
mp_device_rotation_clean();
g_clear_object(&fb_settings);
g_clear_object(&capture_event);

View File

@@ -68,7 +68,7 @@ mp_process_find_all_processors(GtkListStore *store)
}
// Check for a script in XDG_CONFIG_HOME
sprintf(buffer, "%s/megapixels/postprocess.sh", g_get_user_config_dir());
snprintf(buffer, sizeof(buffer), "%s/megapixels/postprocess.sh", g_get_user_config_dir());
if (access(buffer, F_OK) != -1) {
gtk_list_store_insert(store, &iter, -1);
gtk_list_store_set(
@@ -76,7 +76,7 @@ mp_process_find_all_processors(GtkListStore *store)
}
// Check user overridden /etc/megapixels/postprocess.sh
sprintf(buffer, "%s/megapixels/postprocess.sh", SYSCONFDIR);
snprintf(buffer, sizeof(buffer), "%s/megapixels/postprocess.sh", SYSCONFDIR);
if (access(buffer, F_OK) != -1) {
gtk_list_store_insert(store, &iter, -1);
gtk_list_store_set(
@@ -84,7 +84,7 @@ mp_process_find_all_processors(GtkListStore *store)
}
// Check user overridden /usr/share/megapixels/postprocess.sh
sprintf(buffer, "%s/megapixels/postprocess.sh", DATADIR);
snprintf(buffer, sizeof(buffer), "%s/megapixels/postprocess.sh", DATADIR);
if (access(buffer, F_OK) != -1) {
gtk_list_store_insert(store, &iter, -1);
gtk_list_store_set(
@@ -94,7 +94,7 @@ mp_process_find_all_processors(GtkListStore *store)
// Find extra packaged postprocessor scripts
// These should be packaged in
// /usr/share/megapixels/postprocessor.d/executable
sprintf(buffer, "%s/megapixels/postprocessor.d", DATADIR);
snprintf(buffer, sizeof(buffer), "%s/megapixels/postprocessor.d", DATADIR);
DIR *d;
struct dirent *dir;
d = opendir(buffer);
@@ -103,10 +103,11 @@ mp_process_find_all_processors(GtkListStore *store)
if (dir->d_name[0] == '.') {
continue;
}
sprintf(buffer,
"%s/megapixels/postprocessor.d/%s",
DATADIR,
dir->d_name);
snprintf(buffer,
sizeof(buffer),
"%s/megapixels/postprocessor.d/%s",
DATADIR,
dir->d_name);
gtk_list_store_insert(store, &iter, -1);
gtk_list_store_set(
store, &iter, 0, buffer, 1, dir->d_name, -1);
@@ -116,32 +117,32 @@ mp_process_find_all_processors(GtkListStore *store)
}
bool
mp_process_find_processor(char *script, char *filename)
mp_process_find_processor(char *script, int size, char *filename)
{
// Check postprocess.sh in the current working directory
sprintf(script, "./data/%s", filename);
snprintf(script, size, "./data/%s", filename);
if (access(script, F_OK) != -1) {
sprintf(script, "./data/%s", filename);
snprintf(script, size, "./data/%s", filename);
printf("Found postprocessor script at %s\n", script);
return true;
}
// Check for a script in XDG_CONFIG_HOME
sprintf(script, "%s/megapixels/%s", g_get_user_config_dir(), filename);
snprintf(script, size, "%s/megapixels/%s", g_get_user_config_dir(), filename);
if (access(script, F_OK) != -1) {
printf("Found postprocessor script at %s\n", script);
return true;
}
// Check user overridden /etc/megapixels/postprocessor.sh
sprintf(script, "%s/megapixels/%s", SYSCONFDIR, filename);
snprintf(script, size, "%s/megapixels/%s", SYSCONFDIR, filename);
if (access(script, F_OK) != -1) {
printf("Found postprocessor script at %s\n", script);
return true;
}
// Check packaged /usr/share/megapixels/postprocessor.sh
sprintf(script, "%s/megapixels/%s", DATADIR, filename);
snprintf(script, size, "%s/megapixels/%s", DATADIR, filename);
if (access(script, F_OK) != -1) {
printf("Found postprocessor script at %s\n", script);
return true;
@@ -161,7 +162,7 @@ static void setup_capture(void)
exit(EXIT_FAILURE);
}
strcpy(burst_dir, tempdir);
snprintf(burst_dir, sizeof(burst_dir), tempdir);
}
static void
@@ -175,7 +176,7 @@ setup(MPPipeline *pipeline, const void *data)
state_proc.mode_exposure = AAA_BY_V4L2_CONTROLS;
state_proc.mode_focus = AAA_DISABLED;
if (!mp_process_find_processor(movie_script, "movie.sh")) {
if (!mp_process_find_processor(movie_script, sizeof(movie_script), "movie.sh")) {
fprintf(stderr,"movie.sh not found, video pipeline disabled\n");
}
setup_capture();
@@ -525,10 +526,11 @@ summarize()
(float) time, (float) gain, state_proc.gain.value, state_proc.dgain.value,
(float) 1/diopt);
sprintf(buf, "1/%s%.0f%sISO%s%.0f%sm%s%.2f",
sep, (float) (1.0/time), sep,
sep, (float) (gain*100),
sep, sep, 1/diopt);
snprintf(buf, sizeof(buf),
"1/%s%.0f%sISO%s%.0f%sm%s%.2f",
sep, (float) (1.0/time), sep,
sep, (float) (gain*100),
sep, sep, 1/diopt);
/* Not sure what to do here. Looks like we need to call gtk
functions from main thread.
@@ -979,9 +981,10 @@ save_grw(const uint8_t *image, char *fname)
fwrite(image, size, 1, outfile);
char buf[1024];
buf[0] = 0;
int header = sprintf(buf+1,
"Caps: video/x-raw,format=%s,width=%d,height=%d\nSize: %d\nGRW",
format, width, height, size);
int header = snprintf(buf + 1,
sizeof(buf) - 1,
"Caps: video/x-raw,format=%s,width=%d,height=%d\nSize: %d\nGRW",
format, width, height, size);
fwrite(buf, header+1, 1, outfile);
fclose(outfile);
}
@@ -1067,13 +1070,14 @@ save_jpeg(const uint8_t *image, char *fname)
jpeg_finish_compress(&cinfo);
fclose(outfile);
jpeg_destroy_compress(&cinfo);
free(row);
}
static void
process_image_for_capture_yuv(const uint8_t *image, int count)
{
char fname[255];
sprintf(fname, "%s/%d.jpg", burst_dir, count);
snprintf(fname, sizeof(fname), "%s/%d.jpg", burst_dir, count);
save_jpeg(image, fname);
}
@@ -1233,7 +1237,7 @@ static void
process_image_for_capture_bayer(const uint8_t *image, int count)
{
char fname[255];
sprintf(fname, "%s/%d.dng", burst_dir, count);
snprintf(fname, sizeof(fname), "%s/%d.dng", burst_dir, count);
save_dng(image, fname, count);
}
@@ -1258,21 +1262,27 @@ static void
post_process_finished(GSubprocess *proc, GAsyncResult *res, GdkTexture *thumb)
{
char *stdout;
char *path;
g_subprocess_communicate_utf8_finish(proc, res, &stdout, NULL, NULL);
// The last line contains the file name
int end = strlen(stdout);
// Skip the newline at the end
stdout[--end] = '\0';
char *path = path = stdout + end - 1;
do {
if (*path == '\n') {
path++;
break;
}
--path;
} while (path > stdout);
if (end > 0) {
// Skip the newline at the end
stdout[--end] = '\0';
path = stdout + end - 1;
do {
if (*path == '\n') {
path++;
break;
}
--path;
} while (path > stdout);
} else {
path = stdout;
}
mp_main_capture_completed(thumb, path);
}
@@ -1286,20 +1296,23 @@ process_capture_burst(GdkTexture *thumb)
format_timestamp(timestamp);
if (g_get_user_special_dir(G_USER_DIRECTORY_PICTURES) != NULL) {
sprintf(capture_fname,
"%s/IMG%s",
g_get_user_special_dir(G_USER_DIRECTORY_PICTURES),
timestamp);
snprintf(capture_fname,
sizeof(capture_fname),
"%s/IMG%s",
g_get_user_special_dir(G_USER_DIRECTORY_PICTURES),
timestamp);
} else if (getenv("XDG_PICTURES_DIR") != NULL) {
sprintf(capture_fname,
"%s/IMG%s",
getenv("XDG_PICTURES_DIR"),
timestamp);
snprintf(capture_fname,
sizeof(capture_fname),
"%s/IMG%s",
getenv("XDG_PICTURES_DIR"),
timestamp);
} else {
sprintf(capture_fname,
"%s/Pictures/IMG%s",
getenv("HOME"),
timestamp);
snprintf(capture_fname,
sizeof(capture_fname),
"%s/Pictures/IMG%s",
getenv("HOME"),
timestamp);
}
bool save_dng = g_settings_get_boolean(settings, "save-raw");
@@ -1564,7 +1577,9 @@ update_state(MPPipeline *pipeline, const mp_state_proc *new_state)
new_state->camera->current_mode) ||
state_proc.preview_width != new_state->preview_width ||
state_proc.preview_height != new_state->preview_height ||
state_proc.device_rotation != new_state->device_rotation;
state_proc.device_rotation != new_state->device_rotation ||
state_proc.device_accel_rotation != new_state->device_accel_rotation ||
state_proc.device_accel_rotation_good != new_state->device_accel_rotation_good;
bool format_changed = state_proc.mode == NULL;
@@ -1579,6 +1594,8 @@ update_state(MPPipeline *pipeline, const mp_state_proc *new_state)
state_proc.preview_height = new_state->preview_height;
state_proc.device_rotation = new_state->device_rotation;
state_proc.device_accel_rotation = new_state->device_accel_rotation;
state_proc.device_accel_rotation_good = new_state->device_accel_rotation_good;
state_proc.burst_length = new_state->burst_length;
state_proc.balance[0] = new_state->balance[0];
@@ -1602,9 +1619,12 @@ update_state(MPPipeline *pipeline, const mp_state_proc *new_state)
}
if (output_changed || camera_changed) {
int rotation;
rotation = state_proc.device_accel_rotation_good ?
state_proc.device_accel_rotation :
state_proc.device_rotation;
state_proc.camera_rotation = mod(
state_proc.mode->rotation - state_proc.device_rotation, 360);
state_proc.mode->rotation - rotation, 360);
on_output_changed(format_changed);
}

View File

@@ -37,7 +37,8 @@ struct mp_process_pipeline_state {
bool control_focus;
};
bool mp_process_find_processor(char *script, char *filename);
bool mp_process_find_processor(char *script, int size, char *filename);
void mp_process_find_all_processors(GtkListStore *store);
void mp_process_pipeline_start();

182
src/rotation.c Normal file
View File

@@ -0,0 +1,182 @@
#include "gtk/gtk.h"
#include <stdbool.h>
#include "rotation.h"
struct sensor_cb_data {
sensor_cb rotation_update;
};
static GDBusProxy *iio_proxy;
static bool rotation_valid = false;
static int rotation;
struct sensor_cb_data cb_data;
static int
rotation_string_to_degree(const gchar* val)
{
int rot = -1;
if (!g_ascii_strcasecmp(val, "normal"))
rot = 0;
if (!g_ascii_strcasecmp(val, "right-up"))
rot = 270;
if (!g_ascii_strcasecmp(val, "left-up"))
rot = 90;
if (!g_ascii_strcasecmp(val, "bottom-up"))
rot = 180;
return rot;
}
static void
rotation_properties_changed (GDBusProxy *proxy,
GVariant *changed_properties,
GStrv invalidated_properties,
gpointer user_data)
{
GVariant *v;
GVariantDict dict;
const gchar *val;
struct sensor_cb_data *cb_data = (struct sensor_cb_data*)user_data;
g_variant_dict_init (&dict, changed_properties);
if (g_variant_dict_contains (&dict, "HasAccelerometer")) {
v = g_dbus_proxy_get_cached_property (iio_proxy, "HasAccelerometer");
rotation_valid = g_variant_get_boolean (v);
g_debug ("accelerometer changed: %d\n", rotation_valid);
g_variant_unref (v);
}
if (g_variant_dict_contains (&dict, "AccelerometerOrientation")) {
v = g_dbus_proxy_get_cached_property (iio_proxy, "AccelerometerOrientation");
val = g_variant_get_string (v, NULL);
rotation = rotation_string_to_degree(val);
g_variant_unref (v);
}
g_variant_dict_clear (&dict);
g_debug ("sensor rotation changed: %d %d\n",
rotation, rotation_valid);
cb_data->rotation_update(rotation, rotation_valid);
}
static void
rotation_get_initial_values (struct sensor_cb_data *cb_data)
{
GVariant *v;
const gchar *val;
v = g_dbus_proxy_get_cached_property (iio_proxy, "HasAccelerometer");
rotation_valid = g_variant_get_boolean (v);
if (rotation_valid) {
g_variant_unref (v);
v = g_dbus_proxy_get_cached_property (iio_proxy, "AccelerometerOrientation");
val = g_variant_get_string (v, NULL);
rotation = rotation_string_to_degree(val);
g_debug ("rotation: %d\n", rotation);
} else
g_debug ("no accelerometer\n");
g_variant_unref (v);
cb_data->rotation_update(rotation, rotation_valid);
}
static void
rotation_accelerometer_claimed(GDBusProxy *proxy,
GAsyncResult *res,
gpointer user_data)
{
struct sensor_cb_data *cb_data = (struct sensor_cb_data*)user_data;
g_autoptr(GError) error = NULL;
g_autoptr(GVariant) result = g_dbus_proxy_call_finish(proxy, res, &error);
if (!result) {
if (!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_CANCELLED))
g_warning ("Failed to claim accelerometer: %s",
error->message);
return;
}
rotation_get_initial_values (cb_data);
}
static void
rotation_iio_proxy_init (GObject *src,
GAsyncResult *res,
gpointer user_data)
{
struct sensor_cb_data *cb_data = (struct sensor_cb_data*)user_data;
g_autoptr(GError) err = NULL;
iio_proxy = g_dbus_proxy_new_finish(res, &err);
if (!iio_proxy || err) {
g_warning ("Failed to connect to sensor proxy service %s\n",
err->message);
return;
}
g_signal_connect (G_OBJECT (iio_proxy), "g-properties-changed",
G_CALLBACK (rotation_properties_changed),
cb_data);
g_dbus_proxy_call (iio_proxy,
"ClaimAccelerometer",
NULL,
G_DBUS_CALL_FLAGS_NONE,
-1,
NULL,
(GAsyncReadyCallback)rotation_accelerometer_claimed,
cb_data);
}
static void
rotation_iio_proxy_appeared_cb (GDBusConnection *connection,
const gchar *name,
const gchar *name_owner,
gpointer user_data)
{
g_debug ("iio-sensor-proxy appeared\n");
g_dbus_proxy_new (connection,
G_DBUS_PROXY_FLAGS_NONE,
NULL,
"net.hadess.SensorProxy",
"/net/hadess/SensorProxy",
"net.hadess.SensorProxy",
NULL,
(GAsyncReadyCallback)rotation_iio_proxy_init,
user_data);
}
static void
rotation_iio_proxy_vanished_cb (GDBusConnection *connection,
const gchar *name,
gpointer user_data)
{
struct sensor_cb_data *cb_data = (struct sensor_cb_data*)user_data;
if (iio_proxy) {
g_clear_object (&iio_proxy);
g_debug ("iio-sensor-proxy vanished\n");
rotation_valid = false;
rotation = 0;
cb_data->rotation_update(rotation, rotation_valid);
}
}
void
mp_device_rotation_init(sensor_cb cb)
{
g_assert(cb != NULL);
cb_data.rotation_update = cb;
g_bus_watch_name (G_BUS_TYPE_SYSTEM,
"net.hadess.SensorProxy",
G_BUS_NAME_WATCHER_FLAGS_NONE,
rotation_iio_proxy_appeared_cb,
rotation_iio_proxy_vanished_cb,
&cb_data,
NULL);
}
void
mp_device_rotation_clean(void)
{
g_object_unref(iio_proxy);
}

9
src/rotation.h Normal file
View File

@@ -0,0 +1,9 @@
#ifndef _MP_ROTATION_H
#define _MP_ROTATION_H
typedef void(*sensor_cb)(int, bool);
void mp_device_rotation_init(sensor_cb cb);
void mp_device_rotation_clean(void);
#endif

View File

@@ -29,6 +29,8 @@ typedef struct state_main {
int preview_buffer_height;
int device_rotation;
int device_accel_rotation;
bool device_accel_rotation_good;
int burst_length;
@@ -70,6 +72,8 @@ typedef struct state_io {
int preview_width;
int preview_height;
int device_rotation;
int device_accel_rotation;
bool device_accel_rotation_good;
} mp_state_io;
typedef enum aaa_mode {
@@ -93,6 +97,8 @@ typedef struct state_proc {
// Device orientation in degrees
int device_rotation;
int device_accel_rotation;
bool device_accel_rotation_good;
// Camera orientation as number
int camera_rotation;

View File

@@ -241,6 +241,8 @@ process_image(MPPipeline *pipeline, MPZBarImage **_image)
break;
default:
printf("Preview pixel format not supported - zbar won't work\n");
free(data);
mp_zbar_image_unref(image);
return;
}