Browse Source

Refactor controls

Martijn Braam 1 year ago
parent
commit
d51a2fb263
6 changed files with 272 additions and 205 deletions
  1. 1 0
      CMakeLists.txt
  2. 207 195
      src/io_pipeline.c
  3. 14 9
      src/main.c
  4. 4 0
      src/process_pipeline.c
  5. 5 0
      src/process_pipeline.h
  6. 41 1
      src/state.h

+ 1 - 0
CMakeLists.txt

@@ -82,6 +82,7 @@ set_source_files_properties(
 )
 add_dependencies(megapixels-gtk dummy-resource)
 target_link_libraries(megapixels-gtk
+        m
         ${GTK4_LIBRARIES}
         ${FEEDBACK_LIBRARIES}
         ${TIFF_LIBRARIES}

+ 207 - 195
src/io_pipeline.c

@@ -4,6 +4,7 @@
 #include "flash.h"
 #include "pipeline.h"
 #include "process_pipeline.h"
+#include "state.h"
 #include <assert.h>
 #include <errno.h>
 #include <fcntl.h>
@@ -13,36 +14,10 @@
 #include <string.h>
 #include <sys/ioctl.h>
 
-libmegapixels_camera *io_camera = NULL;
-libmegapixels_mode *mode_capture = NULL;
-libmegapixels_mode *mode_preview = NULL;
-MPCamera *mpcamera = NULL;
-
-static bool just_switched_mode = false;
-static int blank_frame_count = 0;
-
-static int burst_length;
-static int captures_remaining = 0;
-
-static int preview_width;
-static int preview_height;
-
-static int device_rotation;
-
-struct control_state {
-        bool gain_is_manual;
-        int gain;
+mp_state_io state_io;
 
-        bool exposure_is_manual;
-        int exposure;
-};
-
-static struct control_state desired_controls = {};
-static struct control_state current_controls = {};
-
-static bool flash_enabled = false;
+MPCamera *mpcamera = NULL;
 
-static bool want_focus = false;
 
 static MPPipeline *pipeline;
 static GSource *capture_source;
@@ -57,9 +32,7 @@ void
 mp_io_pipeline_start()
 {
         mp_process_pipeline_start();
-
         pipeline = mp_pipeline_new();
-
         mp_pipeline_invoke(pipeline, setup, NULL, 0);
 }
 
@@ -69,57 +42,59 @@ mp_io_pipeline_stop()
         if (capture_source) {
                 g_source_destroy(capture_source);
         }
-
         mp_pipeline_free(pipeline);
-
         mp_process_pipeline_stop();
 }
 
+/*
+ * Update state from IO -> Process
+ */
 static void
 update_process_pipeline()
 {
         // Grab the latest control values
-        if (!current_controls.gain_is_manual) {
-                // current_controls.gain =
-                //         mp_camera_control_get_int32(info->camera,
-                //         info->gain_ctrl);
+        if (!state_io.gain.manual && state_io.gain.control) {
+                state_io.gain.value = mp_camera_control_get_int32(
+                        state_io.camera, state_io.gain.control);
         }
-        if (!current_controls.exposure_is_manual) {
-                // current_controls.exposure =
-                //         mp_camera_control_get_int32(info->camera,
-                //         V4L2_CID_EXPOSURE);
+
+        if (!state_io.exposure.manual && state_io.exposure.control) {
+                state_io.exposure.value = mp_camera_control_get_int32(
+                        state_io.camera, state_io.exposure.control);
         }
 
         MPControl control;
         float balance_red = 1.0f;
         float balance_blue = 1.0f;
-        /*
-        if (mp_camera_query_control(info->camera, V4L2_CID_RED_BALANCE, &control)) {
-                int red = mp_camera_control_get_int32(info->camera,
-                                                      V4L2_CID_RED_BALANCE);
-                int blue = mp_camera_control_get_int32(info->camera,
-                                                       V4L2_CID_BLUE_BALANCE);
-                balance_red = (float)red / (float)control.max;
-                balance_blue = (float)blue / (float)control.max;
+        if (state_io.red.control && state_io.blue.control) {
+                int red = mp_camera_control_get_int32(state_io.camera,
+                                                      state_io.red.control);
+                int blue = mp_camera_control_get_int32(state_io.camera,
+                                                       state_io.blue.control);
+                balance_red = (float)red / (float)state_io.red.max;
+                balance_blue = (float)blue / (float)state_io.blue.max;
         }
-         */
 
         struct mp_process_pipeline_state pipeline_state = {
-                .camera = io_camera,
-                .burst_length = burst_length,
-                .preview_width = preview_width,
-                .preview_height = preview_height,
-                .device_rotation = device_rotation,
-                .gain_is_manual = current_controls.gain_is_manual,
-                .gain = current_controls.gain,
-                .gain_max = 1, // TODO: Fix
+                .camera = state_io.camera,
+                .burst_length = state_io.burst_length,
+                .preview_width = state_io.preview_width,
+                .preview_height = state_io.preview_height,
+                .device_rotation = state_io.device_rotation,
+                .gain_is_manual = state_io.gain.manual,
+                .gain = state_io.gain.value,
+                .gain_max = state_io.gain.max,
                 .balance_red = balance_red,
                 .balance_blue = balance_blue,
-                .exposure_is_manual = current_controls.exposure_is_manual,
-                .exposure = current_controls.exposure,
-                .has_auto_focus_continuous = false, // TODO: fix
-                .has_auto_focus_start = false, // TODO: fix
-                .flash_enabled = flash_enabled,
+                .exposure_is_manual = state_io.exposure.manual,
+                .exposure = state_io.exposure.value,
+                .has_auto_focus_continuous = state_io.focus.control != 0,
+                .has_auto_focus_start = state_io.can_af_trigger,
+                .flash_enabled = state_io.flash_enabled,
+                .control_gain = state_io.gain.control != 0,
+                .control_exposure = state_io.exposure.control != 0,
+                .control_focus = state_io.focus.control != 0,
+                .control_flash = true,
         };
         mp_process_pipeline_update_state(&pipeline_state);
 }
@@ -127,7 +102,7 @@ update_process_pipeline()
 static void
 focus(MPPipeline *pipeline, const void *data)
 {
-        want_focus = true;
+        state_io.trigger_af = true;
 }
 
 void
@@ -139,32 +114,30 @@ mp_io_pipeline_focus()
 static void
 capture(MPPipeline *pipeline, const void *data)
 {
-        uint32_t gain;
         float gain_norm;
 
         // Disable the autogain/exposure while taking the burst
-        /* TODO: Fix
-        mp_camera_control_set_int32(info->camera, V4L2_CID_AUTOGAIN, 0);
+        mp_camera_control_set_int32(state_io.camera, V4L2_CID_AUTOGAIN, 0);
         mp_camera_control_set_int32(
-                info->camera, V4L2_CID_EXPOSURE_AUTO, V4L2_EXPOSURE_MANUAL);
-        */
+                state_io.camera, V4L2_CID_EXPOSURE_AUTO, V4L2_EXPOSURE_MANUAL);
 
         // Get current gain to calculate a burst length;
         // with low gain there's 3, with the max automatic gain of the ov5640
         // the value seems to be 248 which creates a 5 frame burst
         // for manual gain you can go up to 11 frames
-        gain = mp_camera_control_get_int32(io_camera, V4L2_CID_GAIN);
-        // gain_norm = (float)gain / (float)mpcamera.gain_max;
-        // burst_length = (int)fmax(sqrt(gain_norm) * 10, 2) + 1;
-        burst_length = MIN(1, burst_length);
-        captures_remaining = burst_length;
+        state_io.gain.value =
+                mp_camera_control_get_int32(state_io.camera, V4L2_CID_GAIN);
+        gain_norm = (float)state_io.gain.value / (float)state_io.gain.max;
+        state_io.burst_length = (int)fmax(sqrtf(gain_norm) * 10, 2) + 1;
+        state_io.burst_length = MIN(1, state_io.burst_length);
+        state_io.captures_remaining = state_io.burst_length;
 
         // Change camera mode for capturing
         mp_process_pipeline_sync();
         mp_camera_stop_capture(mpcamera);
-        struct v4l2_format format = {0};
-        libmegapixels_select_mode(io_camera, mode_capture, &format);
-        just_switched_mode = true;
+        struct v4l2_format format = { 0 };
+        libmegapixels_select_mode(state_io.camera, state_io.mode_capture, &format);
+        state_io.flush_pipeline = true;
 
         mp_camera_start_capture(mpcamera);
 
@@ -211,59 +184,58 @@ start_focus()
             !mp_camera_check_task_complete(mpcamera, focus_continuous_task))
                 return;
 
-        /* TODO: implement
-        if (mpcamera.has_auto_focus_continuous) {
+        if (state_io.focus.control) {
                 focus_continuous_task = mp_camera_control_set_bool_bg(
-                        info->camera, V4L2_CID_FOCUS_AUTO, 1);
-        } else if (info->has_auto_focus_start) {
+                        state_io.camera, state_io.focus.control, 1);
+        } else if (state_io.can_af_trigger) {
                 start_focus_task = mp_camera_control_set_bool_bg(
-                        info->camera, V4L2_CID_AUTO_FOCUS_START, 1);
+                        state_io.camera, V4L2_CID_AUTO_FOCUS_START, 1);
         }
-         */
 }
 
 static void
 update_controls()
 {
         // Don't update controls while capturing
-        if (captures_remaining > 0) {
+        if (state_io.captures_remaining > 0) {
                 return;
         }
-        /* TODO: implement
-        if (want_focus) {
-                start_focus(mpcamera);
-                want_focus = false;
+
+        if (state_io.trigger_af) {
+                state_io.trigger_af = false;
+                start_focus();
         }
 
-        if (current_controls.gain_is_manual != desired_controls.gain_is_manual) {
-                mp_camera_control_set_bool_bg(info->camera,
+        if (state_io.gain.manual != state_io.gain.manual_req) {
+                mp_camera_control_set_bool_bg(state_io.camera,
                                               V4L2_CID_AUTOGAIN,
-                                              !desired_controls.gain_is_manual);
+                                              !state_io.gain.manual_req);
+                state_io.gain.manual = state_io.gain.manual_req;
         }
 
-        if (desired_controls.gain_is_manual &&
-            current_controls.gain != desired_controls.gain) {
-                mp_camera_control_set_int32_bg(
-                        info->camera, info->gain_ctrl, desired_controls.gain);
+        if (state_io.gain.manual && state_io.gain.value != state_io.gain.value_req) {
+                mp_camera_control_set_int32_bg(state_io.camera,
+                                               state_io.gain.control,
+                                               state_io.gain.value_req);
+                state_io.gain.value = state_io.gain.value_req;
         }
 
-        if (current_controls.exposure_is_manual !=
-            desired_controls.exposure_is_manual) {
-                mp_camera_control_set_int32_bg(info->camera,
-                                               V4L2_CID_EXPOSURE_AUTO,
-                                               desired_controls.exposure_is_manual ?
-                                                       V4L2_EXPOSURE_MANUAL :
-                                                       V4L2_EXPOSURE_AUTO);
+        if (state_io.exposure.manual != state_io.exposure.manual_req) {
+                mp_camera_control_set_bool_bg(state_io.camera,
+                                              V4L2_CID_EXPOSURE_AUTO,
+                                              state_io.exposure.manual_req ?
+                                                      V4L2_EXPOSURE_MANUAL :
+                                                      V4L2_EXPOSURE_AUTO);
+                state_io.exposure.manual = state_io.exposure.manual_req;
         }
 
-        if (desired_controls.exposure_is_manual &&
-            current_controls.exposure != desired_controls.exposure) {
-                mp_camera_control_set_int32_bg(
-                        info->camera, V4L2_CID_EXPOSURE, desired_controls.exposure);
+        if (state_io.exposure.manual &&
+            state_io.exposure.value != state_io.exposure.value_req) {
+                mp_camera_control_set_int32_bg(state_io.camera,
+                                               state_io.exposure.control,
+                                               state_io.exposure.value_req);
+                state_io.exposure.value = state_io.exposure.value_req;
         }
-
-        current_controls = desired_controls;
-         */
 }
 
 static void
@@ -274,11 +246,12 @@ on_frame(MPBuffer buffer, void *_data)
 
         // When the mode is switched while capturing we get a couple blank frames,
         // presumably from buffers made ready during the switch. Ignore these.
-        if (just_switched_mode) {
-                if (blank_frame_count < 20) {
+        if (state_io.flush_pipeline) {
+                if (state_io.blank_frame_count < 20) {
                         // Only check a 10x10 area
-                        size_t test_size = MIN(10, io_camera->current_mode->width) *
-                                           MIN(10, io_camera->current_mode->height);
+                        size_t test_size =
+                                MIN(10, state_io.camera->current_mode->width) *
+                                MIN(10, state_io.camera->current_mode->height);
 
                         bool image_is_blank = true;
                         for (size_t i = 0; i < test_size; ++i) {
@@ -288,45 +261,44 @@ on_frame(MPBuffer buffer, void *_data)
                         }
 
                         if (image_is_blank) {
-                                ++blank_frame_count;
+                                ++state_io.blank_frame_count;
                                 return;
                         }
                 } else {
                         printf("Blank image limit reached, resulting capture may be blank\n");
                 }
 
-                just_switched_mode = false;
-                blank_frame_count = 0;
+                state_io.flush_pipeline = false;
+                state_io.blank_frame_count = 0;
         }
 
         // Send the image off for processing
         mp_process_pipeline_process_image(buffer);
 
-        if (captures_remaining > 0) {
-                --captures_remaining;
+        if (state_io.captures_remaining > 0) {
+                --state_io.captures_remaining;
 
-                if (captures_remaining == 0) {
+                if (state_io.captures_remaining == 0) {
                         // Restore the auto exposure and gain if needed
-                        if (!current_controls.exposure_is_manual) {
+                        if (!state_io.exposure.manual) {
                                 mp_camera_control_set_int32_bg(
-                                        io_camera,
+                                        state_io.camera,
                                         V4L2_CID_EXPOSURE_AUTO,
                                         V4L2_EXPOSURE_AUTO);
                         }
 
-                        /* TODO: implement
-                        if (!current_controls.gain_is_manual) {
+                        if (!state_io.gain.manual) {
                                 mp_camera_control_set_bool_bg(
-                                        info->camera, V4L2_CID_AUTOGAIN, true);
+                                        state_io.camera, V4L2_CID_AUTOGAIN, true);
                         }
-                         */
 
                         // Go back to preview mode
                         mp_process_pipeline_sync();
                         mp_camera_stop_capture(mpcamera);
-                        struct v4l2_format format = {0};
-                        libmegapixels_select_mode(io_camera, mode_preview, &format);
-                        just_switched_mode = true;
+                        struct v4l2_format format = { 0 };
+                        libmegapixels_select_mode(
+                                state_io.camera, state_io.mode_preview, &format);
+                        state_io.flush_pipeline = true;
 
                         mp_camera_start_capture(mpcamera);
 
@@ -343,19 +315,81 @@ on_frame(MPBuffer buffer, void *_data)
 }
 
 static void
-update_state(MPPipeline *pipeline, const struct mp_io_pipeline_state *state)
+init_controls()
 {
-        // Make sure the state isn't updated more than it needs to be by checking
-        // whether this state change actually changes anything.
-        bool has_changed = false;
+        if (mp_camera_query_control(
+                    state_io.camera, V4L2_CID_FOCUS_ABSOLUTE, NULL)) {
+                // TODO: Set focus state
+                state_io.focus.control = V4L2_CID_FOCUS_ABSOLUTE;
+        } else {
+                state_io.focus.control = 0;
+        }
+
+        if (mp_camera_query_control(state_io.camera, V4L2_CID_FOCUS_AUTO, NULL)) {
+                mp_camera_control_set_bool_bg(
+                        state_io.camera, V4L2_CID_FOCUS_AUTO, true);
+        }
+
+        state_io.can_af_trigger = mp_camera_query_control(
+                state_io.camera, V4L2_CID_AUTO_FOCUS_START, NULL);
 
-        if (io_camera != state->camera) {
-                has_changed = true;
+        MPControl control;
+        if (mp_camera_query_control(state_io.camera, V4L2_CID_GAIN, &control)) {
+                state_io.gain.control = V4L2_CID_GAIN;
+                state_io.gain.max = control.max;
+        } else if (mp_camera_query_control(
+                           state_io.camera, V4L2_CID_ANALOGUE_GAIN, &control)) {
+                state_io.gain.control = V4L2_CID_ANALOGUE_GAIN;
+                state_io.gain.max = control.max;
+        } else {
+                state_io.gain.max = 0;
+                state_io.gain.control = 0;
+        }
+        if (state_io.gain.control) {
+                state_io.gain.value = mp_camera_control_get_int32(
+                        state_io.camera, state_io.gain.control);
+        } else {
+                state_io.gain.value = 0;
+        }
+
+        state_io.gain.manual =
+                mp_camera_control_get_bool(state_io.camera, V4L2_CID_AUTOGAIN) == 0;
+
+        state_io.exposure.value =
+                mp_camera_control_get_int32(state_io.camera, V4L2_CID_EXPOSURE);
+        state_io.exposure.manual =
+                mp_camera_control_get_int32(state_io.camera,
+                                            V4L2_CID_EXPOSURE_AUTO) ==
+                V4L2_EXPOSURE_MANUAL;
+
+        if (mp_camera_query_control(
+                    state_io.camera, V4L2_CID_RED_BALANCE, &control)) {
+                state_io.red.control = V4L2_CID_RED_BALANCE;
+                state_io.red.max = control.max;
+        } else {
+                state_io.red.control = 0;
+        }
 
-                if (io_camera != NULL) {
+        if (mp_camera_query_control(
+                    state_io.camera, V4L2_CID_BLUE_BALANCE, &control)) {
+                state_io.blue.control = V4L2_CID_BLUE_BALANCE;
+                state_io.blue.max = control.max;
+        } else {
+                state_io.blue.control = 0;
+        }
+}
+
+/*
+ * State transfer from Main -> IO
+ */
+static void
+update_state(MPPipeline *pipeline, const struct mp_io_pipeline_state *state)
+{
+        if (state_io.camera != state->camera) {
+                if (state_io.camera != NULL) {
                         mp_process_pipeline_sync();
                         mp_camera_stop_capture(mpcamera);
-                        libmegapixels_close(io_camera);
+                        libmegapixels_close(state_io.camera);
                 }
 
                 if (capture_source) {
@@ -363,90 +397,68 @@ update_state(MPPipeline *pipeline, const struct mp_io_pipeline_state *state)
                         capture_source = NULL;
                 }
 
-                io_camera = state->camera;
-                if (io_camera) {
-                        libmegapixels_open(io_camera);
-                        mpcamera = mp_camera_new(io_camera);
-                        mode_preview = NULL;
-                        mode_capture = NULL;
-                        for (int m = 0; m < io_camera->num_modes; m++) {
-                                if (io_camera->modes[m]->rate > 29) {
-                                        mode_preview = io_camera->modes[m];
+                state_io.camera = state->camera;
+                if (state_io.camera) {
+                        libmegapixels_open(state_io.camera);
+                        mpcamera = mp_camera_new(state_io.camera);
+                        state_io.mode_preview = NULL;
+                        state_io.mode_capture = NULL;
+                        for (int m = 0; m < state_io.camera->num_modes; m++) {
+                                if (state_io.camera->modes[m]->rate > 29) {
+                                        state_io.mode_preview =
+                                                state_io.camera->modes[m];
                                         break;
                                 }
                         }
                         long area = 0;
-                        for (int m = 0; m < io_camera->num_modes; m++) {
-                                long this_pixels = io_camera->modes[m]->width *
-                                                   io_camera->modes[m]->height;
+                        for (int m = 0; m < state_io.camera->num_modes; m++) {
+                                long this_pixels = state_io.camera->modes[m]->width *
+                                                   state_io.camera->modes[m]->height;
 
                                 if (this_pixels > area) {
                                         area = this_pixels;
-                                        mode_capture = io_camera->modes[m];
+                                        state_io.mode_capture =
+                                                state_io.camera->modes[m];
                                 }
                         }
-                        if (mode_preview == NULL && mode_capture != NULL) {
-                                // If no fast preview mode is available, make do with
-                                // slow modes.
-                                mode_preview = mode_capture;
+                        if (state_io.mode_preview == NULL &&
+                            state_io.mode_capture != NULL) {
+                                // If no fast preview mode is available, make due
+                                // with slow modes.
+                                state_io.mode_preview = state_io.mode_capture;
                         }
-                        if (mode_preview != NULL) {
-                                if (io_camera->video_fd == 0) {
-                                        libmegapixels_open(io_camera);
+                        if (state_io.mode_preview != NULL) {
+                                if (state_io.camera->video_fd == 0) {
+                                        libmegapixels_open(state_io.camera);
                                 }
-                                struct v4l2_format format = {0};
-                                libmegapixels_select_mode(io_camera, mode_preview, &format);
+                                struct v4l2_format format = { 0 };
+                                libmegapixels_select_mode(state_io.camera,
+                                                          state_io.mode_preview,
+                                                          &format);
                         }
 
                         mp_camera_start_capture(mpcamera);
                         capture_source = mp_pipeline_add_capture_source(
                                 pipeline, mpcamera, on_frame, NULL);
 
-                        current_controls.gain_is_manual =
-                                mp_camera_control_get_bool(io_camera,
-                                                           V4L2_CID_AUTOGAIN) == 0;
-                        // current_controls.gain =
-                        //         mp_camera_control_get_int32(camera,
-                        //         info->gain_ctrl);
-
-                        // current_controls.exposure_is_manual =
-                        //         mp_camera_control_get_int32(
-                        //                 info->camera, V4L2_CID_EXPOSURE_AUTO) ==
-                        //         V4L2_EXPOSURE_MANUAL;
-                        // current_controls.exposure = mp_camera_control_get_int32(
-                        //         info->camera, V4L2_CID_EXPOSURE);
+                        init_controls();
                 }
         }
 
-        has_changed = has_changed || burst_length != state->burst_length ||
-                      preview_width != state->preview_width ||
-                      preview_height != state->preview_height ||
-                      device_rotation != state->device_rotation;
-
-        burst_length = state->burst_length;
-        preview_width = state->preview_width;
-        preview_height = state->preview_height;
-        device_rotation = state->device_rotation;
-
-        if (io_camera) {
-                struct control_state previous_desired = desired_controls;
+        state_io.burst_length = state->burst_length;
+        state_io.preview_width = state->preview_width;
+        state_io.preview_height = state->preview_height;
+        state_io.device_rotation = state->device_rotation;
 
-                desired_controls.gain_is_manual = state->gain_is_manual;
-                desired_controls.gain = state->gain;
-                desired_controls.exposure_is_manual = state->exposure_is_manual;
-                desired_controls.exposure = state->exposure;
+        if (state_io.camera) {
+                state_io.gain.manual_req = state->gain_is_manual;
+                state_io.gain.value_req = state->gain;
+                state_io.exposure.manual_req = state->exposure_is_manual;
+                state_io.exposure.value_req = state->exposure;
 
-                has_changed = has_changed ||
-                              memcmp(&previous_desired,
-                                     &desired_controls,
-                                     sizeof(struct control_state)) != 0 ||
-                              flash_enabled != state->flash_enabled;
-
-                flash_enabled = state->flash_enabled;
+                state_io.flash_enabled = state->flash_enabled;
         }
 
-        assert(has_changed);
-
         update_process_pipeline();
 }
 

+ 14 - 9
src/main.c

@@ -48,8 +48,6 @@ RENDERDOC_API_1_1_2 *rdoc_api = NULL;
 
 mp_state_main state;
 
-static bool camera_is_initialized = false;
-
 static MPProcessPipelineBuffer *current_preview_buffer = NULL;
 
 static char last_path[260] = "";
@@ -65,7 +63,11 @@ GtkWidget *process_spinner;
 GtkWidget *scanned_codes;
 GtkWidget *preview_top_box;
 GtkWidget *preview_bottom_box;
+
 GtkWidget *flash_button;
+GtkWidget *iso_button;
+GtkWidget *shutter_button;
+
 LfbEvent *capture_event;
 
 GSettings *settings;
@@ -106,16 +108,14 @@ update_io_pipeline()
         mp_io_pipeline_update_state(&io_state);
 
         // Make the right settings available for the camera
-        gtk_widget_set_visible(flash_button, state.flash_enabled);
+        gtk_widget_set_visible(flash_button, state.control_flash);
+        gtk_widget_set_visible(iso_button, state.control_gain);
+        gtk_widget_set_visible(shutter_button, state.control_exposure);
 }
 
 static bool
 update_state(const mp_state_main *new_state)
 {
-        if (!camera_is_initialized) {
-                camera_is_initialized = true;
-        }
-
         if (state.camera == new_state->camera) {
                 state.gain_is_manual = new_state->gain_is_manual;
                 state.gain = new_state->gain;
@@ -131,6 +131,11 @@ update_state(const mp_state_main *new_state)
 
         state.preview_buffer_width = new_state->preview_buffer_width;
         state.preview_buffer_height = new_state->preview_buffer_height;
+
+        state.control_gain = new_state->control_gain;
+        state.control_exposure = new_state->control_exposure;
+        state.control_focus = new_state->control_focus;
+        state.control_flash = new_state->control_flash;
         return false;
 }
 
@@ -1121,9 +1126,9 @@ activate(GtkApplication *app, gpointer data)
                 "/org/postmarketos/Megapixels/camera.ui");
 
         GtkWidget *window = GTK_WIDGET(gtk_builder_get_object(builder, "window"));
-        GtkWidget *iso_button =
+        iso_button =
                 GTK_WIDGET(gtk_builder_get_object(builder, "iso-controls-button"));
-        GtkWidget *shutter_button = GTK_WIDGET(
+        shutter_button = GTK_WIDGET(
                 gtk_builder_get_object(builder, "shutter-controls-button"));
         flash_button =
                 GTK_WIDGET(gtk_builder_get_object(builder, "flash-controls-button"));

+ 4 - 0
src/process_pipeline.c

@@ -1083,6 +1083,10 @@ update_state(MPPipeline *pipeline, const struct mp_process_pipeline_state *state
                 .has_auto_focus_start = state->has_auto_focus_start,
                 .preview_buffer_width = output_buffer_width,
                 .preview_buffer_height = output_buffer_height,
+                .control_gain = state->control_gain,
+                .control_exposure = state->control_exposure,
+                .control_focus = state->control_focus,
+                .control_flash = state->control_flash,
         };
         mp_main_update_state(&new_state);
 }

+ 5 - 0
src/process_pipeline.h

@@ -29,6 +29,11 @@ struct mp_process_pipeline_state {
         bool has_auto_focus_start;
 
         bool flash_enabled;
+
+        bool control_gain;
+        bool control_exposure;
+        bool control_flash;
+        bool control_focus;
 };
 
 bool mp_process_find_processor(char *script);

+ 41 - 1
src/state.h

@@ -19,6 +19,11 @@ typedef struct state_main {
         int burst_length;
 
         // Control state
+        bool control_gain;
+        bool control_exposure;
+        bool control_flash;
+        bool control_focus;
+
         bool gain_is_manual;
         int gain;
         int gain_max;
@@ -30,4 +35,39 @@ typedef struct state_main {
 
         bool has_auto_focus_continuous;
         bool has_auto_focus_start;
-} mp_state_main;
+} mp_state_main;
+
+typedef struct cstate {
+        uint32_t control;
+        int32_t value;
+        int32_t value_req;
+        int32_t max;
+        bool manual;
+        bool manual_req;
+} controlstate;
+
+typedef struct state_io {
+        libmegapixels_camera *camera;
+        libmegapixels_mode *mode_capture;
+        libmegapixels_mode *mode_preview;
+
+        int burst_length;
+        int captures_remaining;
+        bool flush_pipeline;
+        int blank_frame_count;
+
+        // Control state
+        controlstate gain;
+        controlstate exposure;
+        controlstate focus;
+        controlstate red;
+        controlstate blue;
+        bool can_af_trigger;
+        bool trigger_af;
+        bool flash_enabled;
+
+        // State passed through to the process pipeline
+        int preview_width;
+        int preview_height;
+        int device_rotation;
+} mp_state_io;