main.c 24 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919
  1. #include "main.h"
  2. #include <errno.h>
  3. #include <fcntl.h>
  4. #include <linux/videodev2.h>
  5. #include <linux/media.h>
  6. #include <linux/v4l2-subdev.h>
  7. #include <sys/ioctl.h>
  8. #include <sys/mman.h>
  9. #include <sys/stat.h>
  10. #include <time.h>
  11. #include <assert.h>
  12. #include <limits.h>
  13. #include <linux/kdev_t.h>
  14. #include <sys/sysmacros.h>
  15. #include <asm/errno.h>
  16. #include <wordexp.h>
  17. #include <gtk/gtk.h>
  18. #include <locale.h>
  19. #include <zbar.h>
  20. #include "gl_util.h"
  21. #include "camera_config.h"
  22. #include "io_pipeline.h"
  23. #include "process_pipeline.h"
  24. #define RENDERDOC
  25. #ifdef RENDERDOC
  26. #include <dlfcn.h>
  27. #include <renderdoc/app.h>
  28. RENDERDOC_API_1_1_2 *rdoc_api = NULL;
  29. #endif
  30. enum user_control { USER_CONTROL_ISO, USER_CONTROL_SHUTTER };
  31. static bool camera_is_initialized = false;
  32. static const struct mp_camera_config *camera = NULL;
  33. static MPCameraMode mode;
  34. static int preview_width = -1;
  35. static int preview_height = -1;
  36. static bool gain_is_manual = false;
  37. static int gain;
  38. static int gain_max;
  39. static bool exposure_is_manual = false;
  40. static int exposure;
  41. static bool has_auto_focus_continuous;
  42. static bool has_auto_focus_start;
  43. static MPProcessPipelineBuffer *current_preview_buffer = NULL;
  44. static int preview_buffer_width = -1;
  45. static int preview_buffer_height = -1;
  46. static char last_path[260] = "";
  47. static MPZBarScanResult *zbar_result = NULL;
  48. static int burst_length = 3;
  49. // Widgets
  50. GtkWidget *preview;
  51. GtkWidget *main_stack;
  52. GtkWidget *open_last_stack;
  53. GtkWidget *thumb_last;
  54. GtkWidget *process_spinner;
  55. GtkWidget *scanned_codes;
  56. int
  57. remap(int value, int input_min, int input_max, int output_min, int output_max)
  58. {
  59. const long long factor = 1000000000;
  60. long long output_spread = output_max - output_min;
  61. long long input_spread = input_max - input_min;
  62. long long zero_value = value - input_min;
  63. zero_value *= factor;
  64. long long percentage = zero_value / input_spread;
  65. long long zero_output = percentage * output_spread / factor;
  66. long long result = output_min + zero_output;
  67. return (int)result;
  68. }
  69. static void
  70. update_io_pipeline()
  71. {
  72. struct mp_io_pipeline_state io_state = {
  73. .camera = camera,
  74. .burst_length = burst_length,
  75. .preview_width = preview_width,
  76. .preview_height = preview_height,
  77. .gain_is_manual = gain_is_manual,
  78. .gain = gain,
  79. .exposure_is_manual = exposure_is_manual,
  80. .exposure = exposure,
  81. };
  82. mp_io_pipeline_update_state(&io_state);
  83. }
  84. static bool
  85. update_state(const struct mp_main_state *state)
  86. {
  87. if (!camera_is_initialized) {
  88. camera_is_initialized = true;
  89. }
  90. if (camera == state->camera) {
  91. mode = state->mode;
  92. if (!gain_is_manual) {
  93. gain = state->gain;
  94. }
  95. gain_max = state->gain_max;
  96. if (!exposure_is_manual) {
  97. exposure = state->exposure;
  98. }
  99. has_auto_focus_continuous = state->has_auto_focus_continuous;
  100. has_auto_focus_start = state->has_auto_focus_start;
  101. }
  102. preview_buffer_width = state->image_width;
  103. preview_buffer_height = state->image_height;
  104. return false;
  105. }
  106. void
  107. mp_main_update_state(const struct mp_main_state *state)
  108. {
  109. struct mp_main_state *state_copy = malloc(sizeof(struct mp_main_state));
  110. *state_copy = *state;
  111. g_main_context_invoke_full(g_main_context_default(), G_PRIORITY_DEFAULT_IDLE,
  112. (GSourceFunc)update_state, state_copy, free);
  113. }
  114. static bool set_zbar_result(MPZBarScanResult *result)
  115. {
  116. if (zbar_result) {
  117. for (uint8_t i = 0; i < zbar_result->size; ++i) {
  118. free(zbar_result->codes[i].data);
  119. }
  120. free(zbar_result);
  121. }
  122. zbar_result = result;
  123. gtk_widget_queue_draw(preview);
  124. return false;
  125. }
  126. void mp_main_set_zbar_result(MPZBarScanResult *result)
  127. {
  128. g_main_context_invoke_full(g_main_context_default(), G_PRIORITY_DEFAULT_IDLE,
  129. (GSourceFunc)set_zbar_result, result, NULL);
  130. }
  131. static bool
  132. set_preview(MPProcessPipelineBuffer *buffer)
  133. {
  134. if (current_preview_buffer) {
  135. mp_process_pipeline_buffer_unref(current_preview_buffer);
  136. }
  137. current_preview_buffer = buffer;
  138. gtk_widget_queue_draw(preview);
  139. return false;
  140. }
  141. void
  142. mp_main_set_preview(MPProcessPipelineBuffer *buffer)
  143. {
  144. g_main_context_invoke_full(g_main_context_default(), G_PRIORITY_DEFAULT_IDLE,
  145. (GSourceFunc)set_preview, buffer, NULL);
  146. }
  147. static void transform_centered(cairo_t *cr, uint32_t dst_width, uint32_t dst_height,
  148. int src_width, int src_height)
  149. {
  150. cairo_translate(cr, dst_width / 2, dst_height / 2);
  151. double scale = MIN(dst_width / (double)src_width, dst_height / (double)src_height);
  152. cairo_scale(cr, scale, scale);
  153. cairo_translate(cr, -src_width / 2, -src_height / 2);
  154. }
  155. void
  156. draw_surface_scaled_centered(cairo_t *cr, uint32_t dst_width, uint32_t dst_height,
  157. cairo_surface_t *surface)
  158. {
  159. cairo_save(cr);
  160. int width = cairo_image_surface_get_width(surface);
  161. int height = cairo_image_surface_get_height(surface);
  162. transform_centered(cr, dst_width, dst_height, width, height);
  163. cairo_set_source_surface(cr, surface, 0, 0);
  164. cairo_paint(cr);
  165. cairo_restore(cr);
  166. }
  167. struct capture_completed_args {
  168. cairo_surface_t *thumb;
  169. char *fname;
  170. };
  171. static bool
  172. capture_completed(struct capture_completed_args *args)
  173. {
  174. strncpy(last_path, args->fname, 259);
  175. // gtk_image_set_from_surface(GTK_IMAGE(thumb_last), args->thumb);
  176. gtk_spinner_stop(GTK_SPINNER(process_spinner));
  177. gtk_stack_set_visible_child(GTK_STACK(open_last_stack), thumb_last);
  178. cairo_surface_destroy(args->thumb);
  179. g_free(args->fname);
  180. return false;
  181. }
  182. void
  183. mp_main_capture_completed(cairo_surface_t *thumb, const char *fname)
  184. {
  185. struct capture_completed_args *args = malloc(sizeof(struct capture_completed_args));
  186. args->thumb = thumb;
  187. args->fname = g_strdup(fname);
  188. g_main_context_invoke_full(g_main_context_default(), G_PRIORITY_DEFAULT_IDLE,
  189. (GSourceFunc)capture_completed, args, free);
  190. }
  191. static GLuint blit_program;
  192. static GLuint blit_uniform_texture;
  193. static GLuint solid_program;
  194. static GLuint solid_uniform_color;
  195. static GLuint quad;
  196. static void
  197. preview_realize(GtkGLArea *area)
  198. {
  199. gtk_gl_area_make_current(area);
  200. if (gtk_gl_area_get_error(area) != NULL) {
  201. return;
  202. }
  203. // Make a VAO for OpenGL
  204. if (!gtk_gl_area_get_use_es(area)) {
  205. GLuint vao;
  206. glGenVertexArrays(1, &vao);
  207. glBindVertexArray(vao);
  208. check_gl();
  209. }
  210. GLuint blit_shaders[] = {
  211. gl_util_load_shader("/org/postmarketos/Megapixels/blit.vert", GL_VERTEX_SHADER, NULL, 0),
  212. gl_util_load_shader("/org/postmarketos/Megapixels/blit.frag", GL_FRAGMENT_SHADER, NULL, 0),
  213. };
  214. blit_program = gl_util_link_program(blit_shaders, 2);
  215. glBindAttribLocation(blit_program, GL_UTIL_VERTEX_ATTRIBUTE, "vert");
  216. glBindAttribLocation(blit_program, GL_UTIL_TEX_COORD_ATTRIBUTE, "tex_coord");
  217. check_gl();
  218. blit_uniform_texture = glGetUniformLocation(blit_program, "texture");
  219. GLuint solid_shaders[] = {
  220. gl_util_load_shader("/org/postmarketos/Megapixels/solid.vert", GL_VERTEX_SHADER, NULL, 0),
  221. gl_util_load_shader("/org/postmarketos/Megapixels/solid.frag", GL_FRAGMENT_SHADER, NULL, 0),
  222. };
  223. solid_program = gl_util_link_program(solid_shaders, 2);
  224. glBindAttribLocation(solid_program, GL_UTIL_VERTEX_ATTRIBUTE, "vert");
  225. check_gl();
  226. solid_uniform_color = glGetUniformLocation(solid_program, "color");
  227. quad = gl_util_new_quad();
  228. }
  229. static void
  230. position_preview(float *offset_x, float *offset_y, float *size_x, float *size_y)
  231. {
  232. double ratio = preview_buffer_height / (double)preview_buffer_width;
  233. *offset_x = 0;
  234. *offset_y = 0;
  235. *size_x = preview_width;
  236. *size_y = preview_width * ratio;
  237. }
  238. static gboolean
  239. preview_draw(GtkGLArea *area, GdkGLContext *ctx, gpointer data)
  240. {
  241. if (gtk_gl_area_get_error(area) != NULL) {
  242. return FALSE;
  243. }
  244. if (!camera_is_initialized) {
  245. return FALSE;
  246. }
  247. #ifdef RENDERDOC
  248. if (rdoc_api) rdoc_api->StartFrameCapture(NULL, NULL);
  249. #endif
  250. glClearColor(0, 0, 0, 1);
  251. glClear(GL_COLOR_BUFFER_BIT);
  252. float offset_x, offset_y, size_x, size_y;
  253. position_preview(&offset_x, &offset_y, &size_x, &size_y);
  254. glViewport(offset_x,
  255. preview_height - size_y - offset_y,
  256. size_x,
  257. size_y);
  258. if (current_preview_buffer) {
  259. glUseProgram(blit_program);
  260. glActiveTexture(GL_TEXTURE0);
  261. glBindTexture(GL_TEXTURE_2D, mp_process_pipeline_buffer_get_texture_id(current_preview_buffer));
  262. glUniform1i(blit_uniform_texture, 0);
  263. check_gl();
  264. gl_util_bind_quad(quad);
  265. gl_util_draw_quad(quad);
  266. }
  267. if (zbar_result) {
  268. GLuint buffer;
  269. if (!gtk_gl_area_get_use_es(area)) {
  270. glGenBuffers(1, &buffer);
  271. glBindBuffer(GL_ARRAY_BUFFER, buffer);
  272. check_gl();
  273. }
  274. glUseProgram(solid_program);
  275. glEnable(GL_BLEND);
  276. glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
  277. glUniform4f(solid_uniform_color, 1, 0, 0, 0.5);
  278. for (uint8_t i = 0; i < zbar_result->size; ++i) {
  279. MPZBarCode *code = &zbar_result->codes[i];
  280. GLfloat vertices[] = {
  281. code->bounds_x[0], code->bounds_y[0],
  282. code->bounds_x[1], code->bounds_y[1],
  283. code->bounds_x[3], code->bounds_y[3],
  284. code->bounds_x[2], code->bounds_y[2],
  285. };
  286. for (int i = 0; i < 4; ++i) {
  287. vertices[i * 2] = 2 * vertices[i * 2] / preview_buffer_width - 1.0;
  288. vertices[i * 2 + 1] = 1.0 - 2 * vertices[i * 2 + 1] / preview_buffer_height;
  289. }
  290. if (gtk_gl_area_get_use_es(area)) {
  291. glVertexAttribPointer(GL_UTIL_VERTEX_ATTRIBUTE, 2, GL_FLOAT, 0, 0, vertices);
  292. check_gl();
  293. glEnableVertexAttribArray(GL_UTIL_VERTEX_ATTRIBUTE);
  294. check_gl();
  295. } else {
  296. glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STREAM_DRAW);
  297. check_gl();
  298. glVertexAttribPointer(GL_UTIL_VERTEX_ATTRIBUTE, 2, GL_FLOAT, GL_FALSE, 0, 0);
  299. glEnableVertexAttribArray(GL_UTIL_VERTEX_ATTRIBUTE);
  300. check_gl();
  301. }
  302. glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
  303. check_gl();
  304. }
  305. glDisable(GL_BLEND);
  306. glBindBuffer(GL_ARRAY_BUFFER, 0);
  307. }
  308. glFlush();
  309. #ifdef RENDERDOC
  310. if(rdoc_api) rdoc_api->EndFrameCapture(NULL, NULL);
  311. #endif
  312. return FALSE;
  313. }
  314. static gboolean
  315. preview_resize(GtkWidget *widget, int width, int height, gpointer data)
  316. {
  317. if (preview_width != width || preview_height != height) {
  318. preview_width = width;
  319. preview_height = height;
  320. update_io_pipeline();
  321. }
  322. return TRUE;
  323. }
  324. void
  325. run_open_last_action(GSimpleAction *action, GVariant *param, gpointer user_data)
  326. {
  327. char uri[275];
  328. GError *error = NULL;
  329. if (strlen(last_path) == 0) {
  330. return;
  331. }
  332. sprintf(uri, "file://%s", last_path);
  333. if (!g_app_info_launch_default_for_uri(uri, NULL, &error)) {
  334. g_printerr("Could not launch image viewer for '%s': %s\n", uri, error->message);
  335. }
  336. }
  337. void
  338. run_open_photos_action(GSimpleAction *action, GVariant *param, gpointer user_data)
  339. {
  340. char uri[270];
  341. GError *error = NULL;
  342. sprintf(uri, "file://%s", g_get_user_special_dir(G_USER_DIRECTORY_PICTURES));
  343. if (!g_app_info_launch_default_for_uri(uri, NULL, &error)) {
  344. g_printerr("Could not launch image viewer: %s\n", error->message);
  345. }
  346. }
  347. void
  348. run_capture_action(GSimpleAction *action, GVariant *param, gpointer user_data)
  349. {
  350. gtk_spinner_start(GTK_SPINNER(process_spinner));
  351. gtk_stack_set_visible_child(GTK_STACK(open_last_stack), process_spinner);
  352. mp_io_pipeline_capture();
  353. }
  354. void
  355. run_quit_action(GSimpleAction *action, GVariant *param, GApplication *app)
  356. {
  357. g_application_quit(app);
  358. }
  359. static bool
  360. check_point_inside_bounds(int x, int y, int *bounds_x, int *bounds_y)
  361. {
  362. bool right = false, left = false, top = false, bottom = false;
  363. for (int i = 0; i < 4; ++i) {
  364. if (x <= bounds_x[i])
  365. left = true;
  366. if (x >= bounds_x[i])
  367. right = true;
  368. if (y <= bounds_y[i])
  369. top = true;
  370. if (y >= bounds_y[i])
  371. bottom = true;
  372. }
  373. return right && left && top && bottom;
  374. }
  375. static void
  376. on_zbar_dialog_response(GtkDialog *dialog, int response, char *data)
  377. {
  378. GError *error = NULL;
  379. switch (response) {
  380. case GTK_RESPONSE_YES:
  381. if (!g_app_info_launch_default_for_uri(data,
  382. NULL, &error)) {
  383. g_printerr("Could not launch application: %s\n",
  384. error->message);
  385. }
  386. case GTK_RESPONSE_ACCEPT:
  387. {
  388. GdkDisplay *display = gtk_widget_get_display(GTK_WIDGET(dialog));
  389. gdk_clipboard_set_text(
  390. gdk_display_get_primary_clipboard(display),
  391. data);
  392. }
  393. case GTK_RESPONSE_CANCEL:
  394. break;
  395. default:
  396. g_printerr("Wrong dialog response: %d\n", response);
  397. }
  398. g_free(data);
  399. gtk_window_destroy(GTK_WINDOW(dialog));
  400. }
  401. static void
  402. on_zbar_code_tapped(GtkWidget *widget, const MPZBarCode *code)
  403. {
  404. GtkWidget *dialog;
  405. GtkDialogFlags flags = GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT;
  406. bool data_is_url = g_uri_is_valid(
  407. code->data, G_URI_FLAGS_PARSE_RELAXED, NULL);
  408. char* data = strdup(code->data);
  409. if (data_is_url) {
  410. dialog = gtk_message_dialog_new(
  411. GTK_WINDOW(gtk_widget_get_root(widget)),
  412. flags,
  413. GTK_MESSAGE_QUESTION,
  414. GTK_BUTTONS_NONE,
  415. "Found a URL '%s' encoded in a %s code.",
  416. code->data,
  417. code->type);
  418. gtk_dialog_add_buttons(
  419. GTK_DIALOG(dialog),
  420. "_Open URL",
  421. GTK_RESPONSE_YES,
  422. NULL);
  423. } else {
  424. dialog = gtk_message_dialog_new(
  425. GTK_WINDOW(gtk_widget_get_root(widget)),
  426. flags,
  427. GTK_MESSAGE_QUESTION,
  428. GTK_BUTTONS_NONE,
  429. "Found '%s' encoded in a %s code.",
  430. code->data,
  431. code->type);
  432. }
  433. gtk_dialog_add_buttons(
  434. GTK_DIALOG(dialog),
  435. "_Copy",
  436. GTK_RESPONSE_ACCEPT,
  437. "_Cancel",
  438. GTK_RESPONSE_CANCEL,
  439. NULL);
  440. g_signal_connect(dialog, "response", G_CALLBACK(on_zbar_dialog_response), data);
  441. gtk_widget_show(GTK_WIDGET(dialog));
  442. }
  443. static void
  444. preview_pressed(GtkGestureClick *gesture, int n_press, double x, double y)
  445. {
  446. GtkWidget *widget = gtk_event_controller_get_widget(GTK_EVENT_CONTROLLER(gesture));
  447. int scale_factor = gtk_widget_get_scale_factor(widget);
  448. // Tapped zbar result
  449. if (zbar_result) {
  450. // Transform the event coordinates to the image
  451. float offset_x, offset_y, size_x, size_y;
  452. position_preview(&offset_x, &offset_y, &size_x, &size_y);
  453. int zbar_x = (x - offset_x) * scale_factor / size_x * preview_buffer_width;
  454. int zbar_y = (y - offset_y) * scale_factor / size_y * preview_buffer_height;
  455. for (uint8_t i = 0; i < zbar_result->size; ++i) {
  456. MPZBarCode *code = &zbar_result->codes[i];
  457. if (check_point_inside_bounds(zbar_x, zbar_y, code->bounds_x, code->bounds_y)) {
  458. on_zbar_code_tapped(widget, code);
  459. return;
  460. }
  461. }
  462. }
  463. // Tapped preview image itself, try focussing
  464. if (has_auto_focus_start) {
  465. mp_io_pipeline_focus();
  466. }
  467. }
  468. static void
  469. run_camera_switch_action(GSimpleAction *action, GVariant *param, gpointer user_data)
  470. {
  471. size_t next_index = camera->index + 1;
  472. const struct mp_camera_config *next_camera =
  473. mp_get_camera_config(next_index);
  474. if (!next_camera) {
  475. next_index = 0;
  476. next_camera = mp_get_camera_config(next_index);
  477. }
  478. camera = next_camera;
  479. update_io_pipeline();
  480. }
  481. static void
  482. run_open_settings_action(GSimpleAction *action, GVariant *param, gpointer user_data)
  483. {
  484. gtk_stack_set_visible_child_name(GTK_STACK(main_stack), "settings");
  485. }
  486. static void
  487. run_close_settings_action(GSimpleAction *action, GVariant *param, gpointer user_data)
  488. {
  489. gtk_stack_set_visible_child_name(GTK_STACK(main_stack), "main");
  490. }
  491. static void
  492. on_controls_scale_changed(GtkAdjustment *adjustment, void (*set_fn)(double))
  493. {
  494. set_fn(gtk_adjustment_get_value(adjustment));
  495. }
  496. static void
  497. update_value(GtkAdjustment *adjustment, GtkLabel *label)
  498. {
  499. char buf[12];
  500. snprintf(buf, 12, "%.0f", gtk_adjustment_get_value(adjustment));
  501. gtk_label_set_label(label, buf);
  502. }
  503. static void
  504. on_auto_controls_toggled(GtkToggleButton *button, void (*set_auto_fn)(bool))
  505. {
  506. set_auto_fn(gtk_toggle_button_get_active(button));
  507. }
  508. static void
  509. update_scale(GtkToggleButton *button, GtkScale *scale)
  510. {
  511. gtk_widget_set_sensitive(GTK_WIDGET(scale), !gtk_toggle_button_get_active(button));
  512. }
  513. static void
  514. open_controls(GtkWidget *parent, const char *title_name,
  515. double min_value, double max_value, double current,
  516. bool auto_enabled,
  517. void (*set_fn)(double),
  518. void (*set_auto_fn)(bool))
  519. {
  520. GtkBuilder *builder = gtk_builder_new_from_resource(
  521. "/org/postmarketos/Megapixels/controls-popover.ui");
  522. GtkPopover *popover = GTK_POPOVER(gtk_builder_get_object(builder, "controls"));
  523. GtkScale *scale = GTK_SCALE(gtk_builder_get_object(builder, "scale"));
  524. GtkLabel *title = GTK_LABEL(gtk_builder_get_object(builder, "title"));
  525. GtkLabel *value_label = GTK_LABEL(gtk_builder_get_object(builder, "value-label"));
  526. GtkToggleButton *auto_button = GTK_TOGGLE_BUTTON(gtk_builder_get_object(builder, "auto-button"));
  527. gtk_label_set_label(title, title_name);
  528. GtkAdjustment *adjustment = gtk_range_get_adjustment(GTK_RANGE(scale));
  529. gtk_adjustment_set_lower(adjustment, min_value);
  530. gtk_adjustment_set_upper(adjustment, max_value);
  531. gtk_adjustment_set_value(adjustment, current);
  532. update_value(adjustment, value_label);
  533. gtk_toggle_button_set_active(auto_button, auto_enabled);
  534. update_scale(auto_button, scale);
  535. g_signal_connect(adjustment, "value-changed", G_CALLBACK(on_controls_scale_changed), set_fn);
  536. g_signal_connect(adjustment, "value-changed", G_CALLBACK(update_value), value_label);
  537. g_signal_connect(auto_button, "toggled", G_CALLBACK(on_auto_controls_toggled), set_auto_fn);
  538. g_signal_connect(auto_button, "toggled", G_CALLBACK(update_scale), scale);
  539. gtk_widget_set_parent(GTK_WIDGET(popover), parent);
  540. gtk_popover_popup(popover);
  541. // g_object_unref(popover);
  542. }
  543. static void
  544. set_gain(double value)
  545. {
  546. if (gain != (int)value) {
  547. gain = value;
  548. update_io_pipeline();
  549. }
  550. }
  551. static void
  552. set_gain_auto(bool is_auto)
  553. {
  554. if (gain_is_manual != !is_auto) {
  555. gain_is_manual = !is_auto;
  556. update_io_pipeline();
  557. }
  558. }
  559. static void
  560. open_iso_controls(GtkWidget *button, gpointer user_data)
  561. {
  562. open_controls(button, "ISO", 0, gain_max, gain, !gain_is_manual, set_gain, set_gain_auto);
  563. }
  564. static void
  565. set_shutter(double value)
  566. {
  567. int new_exposure =
  568. (int)(value / 360.0 * camera->capture_mode.height);
  569. if (new_exposure != exposure) {
  570. exposure = new_exposure;
  571. update_io_pipeline();
  572. }
  573. }
  574. static void
  575. set_shutter_auto(bool is_auto)
  576. {
  577. if (exposure_is_manual != !is_auto) {
  578. exposure_is_manual = !is_auto;
  579. update_io_pipeline();
  580. }
  581. }
  582. static void
  583. open_shutter_controls(GtkWidget *button, gpointer user_data)
  584. {
  585. open_controls(button, "Shutter", 1.0, 360.0, exposure, !exposure_is_manual, set_shutter, set_shutter_auto);
  586. }
  587. /*
  588. static void
  589. on_control_auto_toggled(GtkToggleButton *widget, gpointer user_data)
  590. {
  591. bool is_manual = gtk_toggle_button_get_active(widget) ? false : true;
  592. bool has_changed;
  593. switch (current_control) {
  594. case USER_CONTROL_ISO:
  595. if (gain_is_manual != is_manual) {
  596. gain_is_manual = is_manual;
  597. has_changed = true;
  598. }
  599. break;
  600. case USER_CONTROL_SHUTTER:
  601. if (exposure_is_manual != is_manual) {
  602. exposure_is_manual = is_manual;
  603. has_changed = true;
  604. }
  605. break;
  606. }
  607. if (has_changed) {
  608. // The slider might have been moved while Auto mode is active. When entering
  609. // Manual mode, first read the slider value to sync with those changes.
  610. double value = gtk_adjustment_get_value(control_slider);
  611. switch (current_control) {
  612. case USER_CONTROL_ISO:
  613. if (value != gain) {
  614. gain = (int)value;
  615. }
  616. break;
  617. case USER_CONTROL_SHUTTER: {
  618. // So far all sensors use exposure time in number of sensor rows
  619. int new_exposure =
  620. (int)(value / 360.0 * camera->capture_mode.height);
  621. if (new_exposure != exposure) {
  622. exposure = new_exposure;
  623. }
  624. break;
  625. }
  626. }
  627. update_io_pipeline();
  628. draw_controls();
  629. }
  630. }
  631. static void
  632. on_control_slider_changed(GtkAdjustment *widget, gpointer user_data)
  633. {
  634. double value = gtk_adjustment_get_value(widget);
  635. bool has_changed = false;
  636. switch (current_control) {
  637. case USER_CONTROL_ISO:
  638. if (value != gain) {
  639. gain = (int)value;
  640. has_changed = true;
  641. }
  642. break;
  643. case USER_CONTROL_SHUTTER: {
  644. // So far all sensors use exposure time in number of sensor rows
  645. int new_exposure =
  646. (int)(value / 360.0 * camera->capture_mode.height);
  647. if (new_exposure != exposure) {
  648. exposure = new_exposure;
  649. has_changed = true;
  650. }
  651. break;
  652. }
  653. }
  654. if (has_changed) {
  655. update_io_pipeline();
  656. draw_controls();
  657. }
  658. }
  659. */
  660. static void
  661. on_realize(GtkWidget *window, gpointer *data)
  662. {
  663. GtkNative *native = gtk_widget_get_native(window);
  664. mp_process_pipeline_init_gl(gtk_native_get_surface(native));
  665. camera = mp_get_camera_config(0);
  666. update_io_pipeline();
  667. }
  668. static GSimpleAction *
  669. create_simple_action(GtkApplication *app, const char *name, GCallback callback)
  670. {
  671. GSimpleAction *action = g_simple_action_new(name, NULL);
  672. g_signal_connect(action, "activate", callback, app);
  673. g_action_map_add_action(G_ACTION_MAP(app), G_ACTION(action));
  674. return action;
  675. }
  676. static void
  677. activate(GtkApplication *app, gpointer data)
  678. {
  679. g_object_set(gtk_settings_get_default(), "gtk-application-prefer-dark-theme",
  680. TRUE, NULL);
  681. GdkDisplay *display = gdk_display_get_default();
  682. GtkIconTheme *icon_theme = gtk_icon_theme_get_for_display(display);
  683. gtk_icon_theme_add_resource_path(icon_theme, "/org/postmarketos/Megapixels");
  684. GtkCssProvider *provider = gtk_css_provider_new();
  685. gtk_css_provider_load_from_resource(
  686. provider, "/org/postmarketos/Megapixels/camera.css");
  687. gtk_style_context_add_provider_for_display(
  688. display, GTK_STYLE_PROVIDER(provider),
  689. GTK_STYLE_PROVIDER_PRIORITY_APPLICATION);
  690. GtkBuilder *builder = gtk_builder_new_from_resource(
  691. "/org/postmarketos/Megapixels/camera.ui");
  692. GtkWidget *window = GTK_WIDGET(gtk_builder_get_object(builder, "window"));
  693. GtkWidget *iso_button = GTK_WIDGET(gtk_builder_get_object(builder, "iso-controls-button"));
  694. GtkWidget *shutter_button = GTK_WIDGET(gtk_builder_get_object(builder, "shutter-controls-button"));
  695. preview = GTK_WIDGET(gtk_builder_get_object(builder, "preview"));
  696. main_stack = GTK_WIDGET(gtk_builder_get_object(builder, "main_stack"));
  697. open_last_stack = GTK_WIDGET(gtk_builder_get_object(builder, "open_last_stack"));
  698. thumb_last = GTK_WIDGET(gtk_builder_get_object(builder, "thumb_last"));
  699. process_spinner = GTK_WIDGET(gtk_builder_get_object(builder, "process_spinner"));
  700. scanned_codes = GTK_WIDGET(gtk_builder_get_object(builder, "scanned-codes"));
  701. g_signal_connect(window, "realize", G_CALLBACK(on_realize), NULL);
  702. g_signal_connect(preview, "realize", G_CALLBACK(preview_realize), NULL);
  703. g_signal_connect(preview, "render", G_CALLBACK(preview_draw), NULL);
  704. g_signal_connect(preview, "resize", G_CALLBACK(preview_resize), NULL);
  705. GtkGesture *click = gtk_gesture_click_new();
  706. g_signal_connect(click, "pressed", G_CALLBACK(preview_pressed), NULL);
  707. gtk_widget_add_controller(preview, GTK_EVENT_CONTROLLER(click));
  708. g_signal_connect(iso_button, "clicked", G_CALLBACK(open_iso_controls), NULL);
  709. g_signal_connect(shutter_button, "clicked", G_CALLBACK(open_shutter_controls), NULL);
  710. // Setup actions
  711. create_simple_action(app, "capture", G_CALLBACK(run_capture_action));
  712. create_simple_action(app, "camera-switch", G_CALLBACK(run_camera_switch_action));
  713. create_simple_action(app, "open-settings", G_CALLBACK(run_open_settings_action));
  714. create_simple_action(app, "close-settings", G_CALLBACK(run_close_settings_action));
  715. create_simple_action(app, "open-last", G_CALLBACK(run_open_last_action));
  716. create_simple_action(app, "open-photos", G_CALLBACK(run_open_photos_action));
  717. create_simple_action(app, "quit", G_CALLBACK(run_quit_action));
  718. // Setup shortcuts
  719. const char *capture_accels[] = { "space", NULL };
  720. gtk_application_set_accels_for_action(app, "app.capture", capture_accels);
  721. const char *quit_accels[] = { "<Ctrl>q", "<Ctrl>w", NULL };
  722. gtk_application_set_accels_for_action(app, "app.quit", quit_accels);
  723. mp_io_pipeline_start();
  724. gtk_application_add_window(app, GTK_WINDOW(window));
  725. gtk_widget_show(window);
  726. }
  727. static void
  728. shutdown(GApplication *app, gpointer data)
  729. {
  730. // Only do cleanup in development, let the OS clean up otherwise
  731. #ifdef DEBUG
  732. mp_io_pipeline_stop();
  733. #endif
  734. }
  735. int
  736. main(int argc, char *argv[])
  737. {
  738. #ifdef RENDERDOC
  739. {
  740. void *mod = dlopen("librenderdoc.so", RTLD_NOW | RTLD_NOLOAD);
  741. if (mod)
  742. {
  743. pRENDERDOC_GetAPI RENDERDOC_GetAPI = (pRENDERDOC_GetAPI)dlsym(mod, "RENDERDOC_GetAPI");
  744. int ret = RENDERDOC_GetAPI(eRENDERDOC_API_Version_1_1_2, (void **)&rdoc_api);
  745. assert(ret == 1);
  746. }
  747. else
  748. {
  749. printf("Renderdoc not found\n");
  750. }
  751. }
  752. #endif
  753. if (!mp_load_config())
  754. return 1;
  755. setenv("LC_NUMERIC", "C", 1);
  756. GtkApplication *app = gtk_application_new("org.postmarketos.Megapixels", 0);
  757. g_signal_connect(app, "activate", G_CALLBACK(activate), NULL);
  758. g_signal_connect(app, "shutdown", G_CALLBACK(shutdown), NULL);
  759. g_application_run(G_APPLICATION(app), argc, argv);
  760. return 0;
  761. }