main.c 38 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141
  1. #include "main.h"
  2. #include "camera_config.h"
  3. #include "flash.h"
  4. #include "gl_util.h"
  5. #include "io_pipeline.h"
  6. #include "process_pipeline.h"
  7. #include <asm/errno.h>
  8. #include <assert.h>
  9. #include <errno.h>
  10. #include <fcntl.h>
  11. #include <gtk/gtk.h>
  12. #include <limits.h>
  13. #include <linux/kdev_t.h>
  14. #include <linux/media.h>
  15. #include <linux/v4l2-subdev.h>
  16. #include <linux/videodev2.h>
  17. #include <locale.h>
  18. #include <sys/ioctl.h>
  19. #include <sys/mman.h>
  20. #include <sys/stat.h>
  21. #include <sys/sysmacros.h>
  22. #include <time.h>
  23. #include <wordexp.h>
  24. #include <zbar.h>
  25. // #define RENDERDOC
  26. #ifdef RENDERDOC
  27. #include <dlfcn.h>
  28. #include <renderdoc/app.h>
  29. RENDERDOC_API_1_1_2 *rdoc_api = NULL;
  30. #endif
  31. enum user_control { USER_CONTROL_ISO, USER_CONTROL_SHUTTER };
  32. static bool camera_is_initialized = false;
  33. static const struct mp_camera_config *camera = NULL;
  34. static MPCameraMode mode;
  35. static int preview_width = -1;
  36. static int preview_height = -1;
  37. static int device_rotation = 0;
  38. static bool gain_is_manual = false;
  39. static int gain;
  40. static int gain_max;
  41. static bool exposure_is_manual = false;
  42. static int exposure;
  43. static bool has_auto_focus_continuous;
  44. static bool has_auto_focus_start;
  45. static bool flash_enabled = true;
  46. static bool setting_save_dng;
  47. static MPProcessPipelineBuffer *current_preview_buffer = NULL;
  48. static int preview_buffer_width = -1;
  49. static int preview_buffer_height = -1;
  50. static char last_path[260] = "";
  51. static MPZBarScanResult *zbar_result = NULL;
  52. static int burst_length = 3;
  53. // Widgets
  54. GtkWidget *preview;
  55. GtkWidget *main_stack;
  56. GtkWidget *open_last_stack;
  57. GtkWidget *thumb_last;
  58. GtkWidget *process_spinner;
  59. GtkWidget *scanned_codes;
  60. GtkWidget *preview_top_box;
  61. GtkWidget *preview_bottom_box;
  62. GtkWidget *flash_button;
  63. GSettings *settings;
  64. int
  65. remap(int value, int input_min, int input_max, int output_min, int output_max)
  66. {
  67. const long long factor = 1000000000;
  68. long long output_spread = output_max - output_min;
  69. long long input_spread = input_max - input_min;
  70. long long zero_value = value - input_min;
  71. zero_value *= factor;
  72. long long percentage = zero_value / input_spread;
  73. long long zero_output = percentage * output_spread / factor;
  74. long long result = output_min + zero_output;
  75. return (int)result;
  76. }
  77. static void
  78. update_io_pipeline()
  79. {
  80. struct mp_io_pipeline_state io_state = {
  81. .camera = camera,
  82. .burst_length = burst_length,
  83. .preview_width = preview_width,
  84. .preview_height = preview_height,
  85. .device_rotation = device_rotation,
  86. .gain_is_manual = gain_is_manual,
  87. .gain = gain,
  88. .exposure_is_manual = exposure_is_manual,
  89. .exposure = exposure,
  90. .save_dng = setting_save_dng,
  91. .flash_enabled = flash_enabled,
  92. };
  93. mp_io_pipeline_update_state(&io_state);
  94. // Make the right settings available for the camera
  95. gtk_widget_set_visible(flash_button, camera->has_flash);
  96. }
  97. static bool
  98. update_state(const struct mp_main_state *state)
  99. {
  100. if (!camera_is_initialized) {
  101. camera_is_initialized = true;
  102. }
  103. if (camera == state->camera) {
  104. mode = state->mode;
  105. if (!gain_is_manual) {
  106. gain = state->gain;
  107. }
  108. gain_max = state->gain_max;
  109. if (!exposure_is_manual) {
  110. exposure = state->exposure;
  111. }
  112. has_auto_focus_continuous = state->has_auto_focus_continuous;
  113. has_auto_focus_start = state->has_auto_focus_start;
  114. }
  115. preview_buffer_width = state->image_width;
  116. preview_buffer_height = state->image_height;
  117. return false;
  118. }
  119. void
  120. mp_main_update_state(const struct mp_main_state *state)
  121. {
  122. struct mp_main_state *state_copy = malloc(sizeof(struct mp_main_state));
  123. *state_copy = *state;
  124. g_main_context_invoke_full(g_main_context_default(),
  125. G_PRIORITY_DEFAULT_IDLE,
  126. (GSourceFunc)update_state,
  127. state_copy,
  128. free);
  129. }
  130. static bool
  131. set_zbar_result(MPZBarScanResult *result)
  132. {
  133. if (zbar_result) {
  134. for (uint8_t i = 0; i < zbar_result->size; ++i) {
  135. free(zbar_result->codes[i].data);
  136. }
  137. free(zbar_result);
  138. }
  139. zbar_result = result;
  140. gtk_widget_queue_draw(preview);
  141. return false;
  142. }
  143. void
  144. mp_main_set_zbar_result(MPZBarScanResult *result)
  145. {
  146. g_main_context_invoke_full(g_main_context_default(),
  147. G_PRIORITY_DEFAULT_IDLE,
  148. (GSourceFunc)set_zbar_result,
  149. result,
  150. NULL);
  151. }
  152. static bool
  153. set_preview(MPProcessPipelineBuffer *buffer)
  154. {
  155. if (current_preview_buffer) {
  156. mp_process_pipeline_buffer_unref(current_preview_buffer);
  157. }
  158. current_preview_buffer = buffer;
  159. gtk_widget_queue_draw(preview);
  160. return false;
  161. }
  162. void
  163. mp_main_set_preview(MPProcessPipelineBuffer *buffer)
  164. {
  165. g_main_context_invoke_full(g_main_context_default(),
  166. G_PRIORITY_DEFAULT_IDLE,
  167. (GSourceFunc)set_preview,
  168. buffer,
  169. NULL);
  170. }
  171. struct capture_completed_args {
  172. GdkTexture *thumb;
  173. char *fname;
  174. };
  175. static bool
  176. capture_completed(struct capture_completed_args *args)
  177. {
  178. strncpy(last_path, args->fname, 259);
  179. gtk_image_set_from_paintable(GTK_IMAGE(thumb_last),
  180. GDK_PAINTABLE(args->thumb));
  181. gtk_spinner_stop(GTK_SPINNER(process_spinner));
  182. gtk_stack_set_visible_child(GTK_STACK(open_last_stack), thumb_last);
  183. g_object_unref(args->thumb);
  184. g_free(args->fname);
  185. return false;
  186. }
  187. void
  188. mp_main_capture_completed(GdkTexture *thumb, const char *fname)
  189. {
  190. struct capture_completed_args *args =
  191. malloc(sizeof(struct capture_completed_args));
  192. args->thumb = thumb;
  193. args->fname = g_strdup(fname);
  194. g_main_context_invoke_full(g_main_context_default(),
  195. G_PRIORITY_DEFAULT_IDLE,
  196. (GSourceFunc)capture_completed,
  197. args,
  198. free);
  199. }
  200. static GLuint blit_program;
  201. static GLuint blit_uniform_transform;
  202. static GLuint blit_uniform_texture;
  203. static GLuint solid_program;
  204. static GLuint solid_uniform_color;
  205. static GLuint quad;
  206. static void
  207. preview_realize(GtkGLArea *area)
  208. {
  209. gtk_gl_area_make_current(area);
  210. if (gtk_gl_area_get_error(area) != NULL) {
  211. return;
  212. }
  213. // Make a VAO for OpenGL
  214. if (!gtk_gl_area_get_use_es(area)) {
  215. GLuint vao;
  216. glGenVertexArrays(1, &vao);
  217. glBindVertexArray(vao);
  218. check_gl();
  219. }
  220. GLuint blit_shaders[] = {
  221. gl_util_load_shader("/org/postmarketos/Megapixels/blit.vert",
  222. GL_VERTEX_SHADER,
  223. NULL,
  224. 0),
  225. gl_util_load_shader("/org/postmarketos/Megapixels/blit.frag",
  226. GL_FRAGMENT_SHADER,
  227. NULL,
  228. 0),
  229. };
  230. blit_program = gl_util_link_program(blit_shaders, 2);
  231. glBindAttribLocation(blit_program, GL_UTIL_VERTEX_ATTRIBUTE, "vert");
  232. glBindAttribLocation(blit_program, GL_UTIL_TEX_COORD_ATTRIBUTE, "tex_coord");
  233. check_gl();
  234. blit_uniform_transform = glGetUniformLocation(blit_program, "transform");
  235. blit_uniform_texture = glGetUniformLocation(blit_program, "texture");
  236. GLuint solid_shaders[] = {
  237. gl_util_load_shader("/org/postmarketos/Megapixels/solid.vert",
  238. GL_VERTEX_SHADER,
  239. NULL,
  240. 0),
  241. gl_util_load_shader("/org/postmarketos/Megapixels/solid.frag",
  242. GL_FRAGMENT_SHADER,
  243. NULL,
  244. 0),
  245. };
  246. solid_program = gl_util_link_program(solid_shaders, 2);
  247. glBindAttribLocation(solid_program, GL_UTIL_VERTEX_ATTRIBUTE, "vert");
  248. check_gl();
  249. solid_uniform_color = glGetUniformLocation(solid_program, "color");
  250. quad = gl_util_new_quad();
  251. }
  252. static void
  253. position_preview(float *offset_x, float *offset_y, float *size_x, float *size_y)
  254. {
  255. int buffer_width, buffer_height;
  256. if (device_rotation == 0 || device_rotation == 180) {
  257. buffer_width = preview_buffer_width;
  258. buffer_height = preview_buffer_height;
  259. } else {
  260. buffer_width = preview_buffer_height;
  261. buffer_height = preview_buffer_width;
  262. }
  263. int scale_factor = gtk_widget_get_scale_factor(preview);
  264. int top_height =
  265. gtk_widget_get_allocated_height(preview_top_box) * scale_factor;
  266. int bottom_height =
  267. gtk_widget_get_allocated_height(preview_bottom_box) * scale_factor;
  268. int inner_height = preview_height - top_height - bottom_height;
  269. double scale = MIN(preview_width / (float)buffer_width,
  270. preview_height / (float)buffer_height);
  271. *size_x = scale * buffer_width;
  272. *size_y = scale * buffer_height;
  273. *offset_x = (preview_width - *size_x) / 2.0;
  274. if (*size_y > inner_height) {
  275. *offset_y = (preview_height - *size_y) / 2.0;
  276. } else {
  277. *offset_y = top_height + (inner_height - *size_y) / 2.0;
  278. }
  279. }
  280. static gboolean
  281. preview_draw(GtkGLArea *area, GdkGLContext *ctx, gpointer data)
  282. {
  283. if (gtk_gl_area_get_error(area) != NULL) {
  284. return FALSE;
  285. }
  286. if (!camera_is_initialized) {
  287. return FALSE;
  288. }
  289. #ifdef RENDERDOC
  290. if (rdoc_api) {
  291. rdoc_api->StartFrameCapture(NULL, NULL);
  292. }
  293. #endif
  294. glClearColor(0, 0, 0, 1);
  295. glClear(GL_COLOR_BUFFER_BIT);
  296. float offset_x, offset_y, size_x, size_y;
  297. position_preview(&offset_x, &offset_y, &size_x, &size_y);
  298. glViewport(offset_x, preview_height - size_y - offset_y, size_x, size_y);
  299. if (current_preview_buffer) {
  300. glUseProgram(blit_program);
  301. GLfloat rotation_list[4] = { 0, -1, 0, 1 };
  302. int rotation_index = device_rotation / 90;
  303. GLfloat sin_rot = rotation_list[rotation_index];
  304. GLfloat cos_rot = rotation_list[(4 + rotation_index - 1) % 4];
  305. GLfloat matrix[9] = {
  306. // clang-format off
  307. cos_rot, sin_rot, 0,
  308. -sin_rot, cos_rot, 0,
  309. 0, 0, 1,
  310. // clang-format on
  311. };
  312. glUniformMatrix3fv(blit_uniform_transform, 1, GL_FALSE, matrix);
  313. check_gl();
  314. glActiveTexture(GL_TEXTURE0);
  315. glBindTexture(GL_TEXTURE_2D,
  316. mp_process_pipeline_buffer_get_texture_id(
  317. current_preview_buffer));
  318. glUniform1i(blit_uniform_texture, 0);
  319. check_gl();
  320. gl_util_bind_quad(quad);
  321. gl_util_draw_quad(quad);
  322. }
  323. if (zbar_result) {
  324. GLuint buffer;
  325. if (!gtk_gl_area_get_use_es(area)) {
  326. glGenBuffers(1, &buffer);
  327. glBindBuffer(GL_ARRAY_BUFFER, buffer);
  328. check_gl();
  329. }
  330. glUseProgram(solid_program);
  331. glEnable(GL_BLEND);
  332. glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
  333. glUniform4f(solid_uniform_color, 1, 0, 0, 0.5);
  334. for (uint8_t i = 0; i < zbar_result->size; ++i) {
  335. MPZBarCode *code = &zbar_result->codes[i];
  336. GLfloat vertices[] = {
  337. code->bounds_x[0], code->bounds_y[0],
  338. code->bounds_x[1], code->bounds_y[1],
  339. code->bounds_x[3], code->bounds_y[3],
  340. code->bounds_x[2], code->bounds_y[2],
  341. };
  342. for (int i = 0; i < 4; ++i) {
  343. vertices[i * 2] =
  344. 2 * vertices[i * 2] / preview_buffer_width -
  345. 1.0;
  346. vertices[i * 2 + 1] =
  347. 1.0 - 2 * vertices[i * 2 + 1] /
  348. preview_buffer_height;
  349. }
  350. if (gtk_gl_area_get_use_es(area)) {
  351. glVertexAttribPointer(GL_UTIL_VERTEX_ATTRIBUTE,
  352. 2,
  353. GL_FLOAT,
  354. 0,
  355. 0,
  356. vertices);
  357. check_gl();
  358. glEnableVertexAttribArray(GL_UTIL_VERTEX_ATTRIBUTE);
  359. check_gl();
  360. } else {
  361. glBufferData(GL_ARRAY_BUFFER,
  362. sizeof(vertices),
  363. vertices,
  364. GL_STREAM_DRAW);
  365. check_gl();
  366. glVertexAttribPointer(GL_UTIL_VERTEX_ATTRIBUTE,
  367. 2,
  368. GL_FLOAT,
  369. GL_FALSE,
  370. 0,
  371. 0);
  372. glEnableVertexAttribArray(GL_UTIL_VERTEX_ATTRIBUTE);
  373. check_gl();
  374. }
  375. glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
  376. check_gl();
  377. }
  378. glDisable(GL_BLEND);
  379. glBindBuffer(GL_ARRAY_BUFFER, 0);
  380. }
  381. glFlush();
  382. #ifdef RENDERDOC
  383. if (rdoc_api) {
  384. rdoc_api->EndFrameCapture(NULL, NULL);
  385. }
  386. #endif
  387. return FALSE;
  388. }
  389. static gboolean
  390. preview_resize(GtkWidget *widget, int width, int height, gpointer data)
  391. {
  392. if (preview_width != width || preview_height != height) {
  393. preview_width = width;
  394. preview_height = height;
  395. update_io_pipeline();
  396. }
  397. return TRUE;
  398. }
  399. void
  400. run_open_last_action(GSimpleAction *action, GVariant *param, gpointer user_data)
  401. {
  402. char uri[275];
  403. GError *error = NULL;
  404. if (strlen(last_path) == 0) {
  405. return;
  406. }
  407. sprintf(uri, "file://%s", last_path);
  408. if (!g_app_info_launch_default_for_uri(uri, NULL, &error)) {
  409. g_printerr("Could not launch image viewer for '%s': %s\n",
  410. uri,
  411. error->message);
  412. g_error_free(error);
  413. }
  414. }
  415. void
  416. run_open_photos_action(GSimpleAction *action, GVariant *param, gpointer user_data)
  417. {
  418. char uri[270];
  419. GError *error = NULL;
  420. sprintf(uri, "file://%s", g_get_user_special_dir(G_USER_DIRECTORY_PICTURES));
  421. if (!g_app_info_launch_default_for_uri(uri, NULL, &error)) {
  422. g_printerr("Could not launch image viewer: %s\n", error->message);
  423. g_error_free(error);
  424. }
  425. }
  426. void
  427. run_capture_action(GSimpleAction *action, GVariant *param, gpointer user_data)
  428. {
  429. gtk_spinner_start(GTK_SPINNER(process_spinner));
  430. gtk_stack_set_visible_child(GTK_STACK(open_last_stack), process_spinner);
  431. mp_io_pipeline_capture();
  432. }
  433. void
  434. run_about_action(GSimpleAction *action, GVariant *param, GApplication *app)
  435. {
  436. gtk_show_about_dialog(NULL,
  437. "program-name",
  438. "Megapixels",
  439. "title",
  440. "Megapixels",
  441. "logo-icon-name",
  442. "org.postmarketos.Megapixels",
  443. "comments",
  444. "The postmarketOS camera application",
  445. "website",
  446. "https://sr.ht/~martijnbraam/megapixels",
  447. "version",
  448. VERSION,
  449. "license-type",
  450. GTK_LICENSE_GPL_3_0_ONLY,
  451. NULL);
  452. }
  453. void
  454. run_quit_action(GSimpleAction *action, GVariant *param, GApplication *app)
  455. {
  456. g_application_quit(app);
  457. }
  458. static bool
  459. check_point_inside_bounds(int x, int y, int *bounds_x, int *bounds_y)
  460. {
  461. bool right = false, left = false, top = false, bottom = false;
  462. for (int i = 0; i < 4; ++i) {
  463. if (x <= bounds_x[i])
  464. left = true;
  465. if (x >= bounds_x[i])
  466. right = true;
  467. if (y <= bounds_y[i])
  468. top = true;
  469. if (y >= bounds_y[i])
  470. bottom = true;
  471. }
  472. return right && left && top && bottom;
  473. }
  474. static void
  475. on_zbar_dialog_response(GtkDialog *dialog, int response, char *data)
  476. {
  477. GError *error = NULL;
  478. switch (response) {
  479. case GTK_RESPONSE_YES:
  480. if (!g_app_info_launch_default_for_uri(data, NULL, &error)) {
  481. g_printerr("Could not launch application: %s\n",
  482. error->message);
  483. g_error_free(error);
  484. }
  485. case GTK_RESPONSE_ACCEPT: {
  486. GdkDisplay *display = gtk_widget_get_display(GTK_WIDGET(dialog));
  487. gdk_clipboard_set_text(gdk_display_get_clipboard(display), data);
  488. }
  489. case GTK_RESPONSE_CANCEL:
  490. break;
  491. default:
  492. g_printerr("Wrong dialog response: %d\n", response);
  493. }
  494. g_free(data);
  495. gtk_window_destroy(GTK_WINDOW(dialog));
  496. }
  497. static void
  498. on_zbar_code_tapped(GtkWidget *widget, const MPZBarCode *code)
  499. {
  500. GtkWidget *dialog;
  501. GtkDialogFlags flags = GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT;
  502. bool data_is_url =
  503. g_uri_is_valid(code->data, G_URI_FLAGS_PARSE_RELAXED, NULL);
  504. char *data = strdup(code->data);
  505. if (data_is_url) {
  506. dialog = gtk_message_dialog_new(
  507. GTK_WINDOW(gtk_widget_get_root(widget)),
  508. flags,
  509. GTK_MESSAGE_QUESTION,
  510. GTK_BUTTONS_NONE,
  511. "Found a URL '%s' encoded in a %s.",
  512. code->data,
  513. code->type);
  514. gtk_dialog_add_buttons(
  515. GTK_DIALOG(dialog), "_Open URL", GTK_RESPONSE_YES, NULL);
  516. } else {
  517. dialog = gtk_message_dialog_new(
  518. GTK_WINDOW(gtk_widget_get_root(widget)),
  519. flags,
  520. GTK_MESSAGE_QUESTION,
  521. GTK_BUTTONS_NONE,
  522. "Found data encoded in a %s.",
  523. code->type);
  524. gtk_message_dialog_format_secondary_markup(
  525. GTK_MESSAGE_DIALOG(dialog), "<small>%s</small>", code->data);
  526. }
  527. gtk_dialog_add_buttons(GTK_DIALOG(dialog),
  528. "_Copy",
  529. GTK_RESPONSE_ACCEPT,
  530. "_Cancel",
  531. GTK_RESPONSE_CANCEL,
  532. NULL);
  533. g_signal_connect(
  534. dialog, "response", G_CALLBACK(on_zbar_dialog_response), data);
  535. gtk_widget_show(GTK_WIDGET(dialog));
  536. }
  537. static void
  538. preview_pressed(GtkGestureClick *gesture, int n_press, double x, double y)
  539. {
  540. GtkWidget *widget =
  541. gtk_event_controller_get_widget(GTK_EVENT_CONTROLLER(gesture));
  542. int scale_factor = gtk_widget_get_scale_factor(widget);
  543. // Tapped zbar result
  544. if (zbar_result) {
  545. // Transform the event coordinates to the image
  546. float offset_x, offset_y, size_x, size_y;
  547. position_preview(&offset_x, &offset_y, &size_x, &size_y);
  548. int zbar_x = (x - offset_x) * scale_factor / size_x *
  549. preview_buffer_width;
  550. int zbar_y = (y - offset_y) * scale_factor / size_y *
  551. preview_buffer_height;
  552. for (uint8_t i = 0; i < zbar_result->size; ++i) {
  553. MPZBarCode *code = &zbar_result->codes[i];
  554. if (check_point_inside_bounds(zbar_x,
  555. zbar_y,
  556. code->bounds_x,
  557. code->bounds_y)) {
  558. on_zbar_code_tapped(widget, code);
  559. return;
  560. }
  561. }
  562. }
  563. // Tapped preview image itself, try focussing
  564. if (has_auto_focus_start) {
  565. mp_io_pipeline_focus();
  566. }
  567. }
  568. static void
  569. run_camera_switch_action(GSimpleAction *action, GVariant *param, gpointer user_data)
  570. {
  571. size_t next_index = camera->index + 1;
  572. const struct mp_camera_config *next_camera =
  573. mp_get_camera_config(next_index);
  574. if (!next_camera) {
  575. next_index = 0;
  576. next_camera = mp_get_camera_config(next_index);
  577. }
  578. camera = next_camera;
  579. update_io_pipeline();
  580. }
  581. static void
  582. run_open_settings_action(GSimpleAction *action, GVariant *param, gpointer user_data)
  583. {
  584. gtk_stack_set_visible_child_name(GTK_STACK(main_stack), "settings");
  585. }
  586. static void
  587. run_close_settings_action(GSimpleAction *action, GVariant *param, gpointer user_data)
  588. {
  589. gtk_stack_set_visible_child_name(GTK_STACK(main_stack), "main");
  590. // Update settings
  591. bool save_dng = g_settings_get_boolean(settings, "save-raw");
  592. if (save_dng != setting_save_dng) {
  593. setting_save_dng = save_dng;
  594. update_io_pipeline();
  595. }
  596. }
  597. static void
  598. on_controls_scale_changed(GtkAdjustment *adjustment, void (*set_fn)(double))
  599. {
  600. set_fn(gtk_adjustment_get_value(adjustment));
  601. }
  602. static void
  603. update_value(GtkAdjustment *adjustment, GtkLabel *label)
  604. {
  605. char buf[12];
  606. snprintf(buf, 12, "%.0f", gtk_adjustment_get_value(adjustment));
  607. gtk_label_set_label(label, buf);
  608. }
  609. static void
  610. on_auto_controls_toggled(GtkToggleButton *button, void (*set_auto_fn)(bool))
  611. {
  612. set_auto_fn(gtk_toggle_button_get_active(button));
  613. }
  614. static void
  615. update_scale(GtkToggleButton *button, GtkScale *scale)
  616. {
  617. gtk_widget_set_sensitive(GTK_WIDGET(scale),
  618. !gtk_toggle_button_get_active(button));
  619. }
  620. static void
  621. open_controls(GtkWidget *parent,
  622. const char *title_name,
  623. double min_value,
  624. double max_value,
  625. double current,
  626. bool auto_enabled,
  627. void (*set_fn)(double),
  628. void (*set_auto_fn)(bool))
  629. {
  630. GtkBuilder *builder = gtk_builder_new_from_resource(
  631. "/org/postmarketos/Megapixels/controls-popover.ui");
  632. GtkPopover *popover =
  633. GTK_POPOVER(gtk_builder_get_object(builder, "controls"));
  634. GtkScale *scale = GTK_SCALE(gtk_builder_get_object(builder, "scale"));
  635. GtkLabel *title = GTK_LABEL(gtk_builder_get_object(builder, "title"));
  636. GtkLabel *value_label =
  637. GTK_LABEL(gtk_builder_get_object(builder, "value-label"));
  638. GtkToggleButton *auto_button =
  639. GTK_TOGGLE_BUTTON(gtk_builder_get_object(builder, "auto-button"));
  640. gtk_label_set_label(title, title_name);
  641. GtkAdjustment *adjustment = gtk_range_get_adjustment(GTK_RANGE(scale));
  642. gtk_adjustment_set_lower(adjustment, min_value);
  643. gtk_adjustment_set_upper(adjustment, max_value);
  644. gtk_adjustment_set_value(adjustment, current);
  645. update_value(adjustment, value_label);
  646. gtk_toggle_button_set_active(auto_button, auto_enabled);
  647. update_scale(auto_button, scale);
  648. g_signal_connect(adjustment,
  649. "value-changed",
  650. G_CALLBACK(on_controls_scale_changed),
  651. set_fn);
  652. g_signal_connect(
  653. adjustment, "value-changed", G_CALLBACK(update_value), value_label);
  654. g_signal_connect(auto_button,
  655. "toggled",
  656. G_CALLBACK(on_auto_controls_toggled),
  657. set_auto_fn);
  658. g_signal_connect(auto_button, "toggled", G_CALLBACK(update_scale), scale);
  659. gtk_widget_set_parent(GTK_WIDGET(popover), parent);
  660. gtk_popover_popup(popover);
  661. // g_object_unref(popover);
  662. }
  663. static void
  664. set_gain(double value)
  665. {
  666. if (gain != (int)value) {
  667. gain = value;
  668. update_io_pipeline();
  669. }
  670. }
  671. static void
  672. set_gain_auto(bool is_auto)
  673. {
  674. if (gain_is_manual != !is_auto) {
  675. gain_is_manual = !is_auto;
  676. update_io_pipeline();
  677. }
  678. }
  679. static void
  680. open_iso_controls(GtkWidget *button, gpointer user_data)
  681. {
  682. open_controls(button,
  683. "ISO",
  684. 0,
  685. gain_max,
  686. gain,
  687. !gain_is_manual,
  688. set_gain,
  689. set_gain_auto);
  690. }
  691. static void
  692. set_shutter(double value)
  693. {
  694. int new_exposure = (int)(value / 360.0 * camera->capture_mode.height);
  695. if (new_exposure != exposure) {
  696. exposure = new_exposure;
  697. update_io_pipeline();
  698. }
  699. }
  700. static void
  701. set_shutter_auto(bool is_auto)
  702. {
  703. if (exposure_is_manual != !is_auto) {
  704. exposure_is_manual = !is_auto;
  705. update_io_pipeline();
  706. }
  707. }
  708. static void
  709. open_shutter_controls(GtkWidget *button, gpointer user_data)
  710. {
  711. open_controls(button,
  712. "Shutter",
  713. 1.0,
  714. 360.0,
  715. exposure,
  716. !exposure_is_manual,
  717. set_shutter,
  718. set_shutter_auto);
  719. }
  720. static void
  721. flash_button_clicked(GtkWidget *button, gpointer user_data)
  722. {
  723. flash_enabled = !flash_enabled;
  724. update_io_pipeline();
  725. const char *icon_name =
  726. flash_enabled ? "flash-enabled-symbolic" : "flash-disabled-symbolic";
  727. gtk_button_set_icon_name(GTK_BUTTON(button), icon_name);
  728. }
  729. static void
  730. on_realize(GtkWidget *window, gpointer *data)
  731. {
  732. GtkNative *native = gtk_widget_get_native(window);
  733. mp_process_pipeline_init_gl(gtk_native_get_surface(native));
  734. camera = mp_get_camera_config(0);
  735. update_io_pipeline();
  736. }
  737. static GSimpleAction *
  738. create_simple_action(GtkApplication *app, const char *name, GCallback callback)
  739. {
  740. GSimpleAction *action = g_simple_action_new(name, NULL);
  741. g_signal_connect(action, "activate", callback, app);
  742. g_action_map_add_action(G_ACTION_MAP(app), G_ACTION(action));
  743. return action;
  744. }
  745. static void
  746. update_ui_rotation()
  747. {
  748. if (device_rotation == 0 || device_rotation == 180) {
  749. // Portrait
  750. gtk_widget_set_halign(preview_top_box, GTK_ALIGN_FILL);
  751. gtk_orientable_set_orientation(GTK_ORIENTABLE(preview_top_box),
  752. GTK_ORIENTATION_VERTICAL);
  753. gtk_widget_set_halign(preview_bottom_box, GTK_ALIGN_FILL);
  754. gtk_orientable_set_orientation(GTK_ORIENTABLE(preview_bottom_box),
  755. GTK_ORIENTATION_HORIZONTAL);
  756. if (device_rotation == 0) {
  757. gtk_widget_set_valign(preview_top_box, GTK_ALIGN_START);
  758. gtk_widget_set_valign(preview_bottom_box, GTK_ALIGN_END);
  759. } else {
  760. gtk_widget_set_valign(preview_top_box, GTK_ALIGN_END);
  761. gtk_widget_set_valign(preview_bottom_box, GTK_ALIGN_START);
  762. }
  763. } else {
  764. // Landscape
  765. gtk_widget_set_valign(preview_top_box, GTK_ALIGN_FILL);
  766. gtk_orientable_set_orientation(GTK_ORIENTABLE(preview_top_box),
  767. GTK_ORIENTATION_HORIZONTAL);
  768. gtk_widget_set_valign(preview_bottom_box, GTK_ALIGN_FILL);
  769. gtk_orientable_set_orientation(GTK_ORIENTABLE(preview_bottom_box),
  770. GTK_ORIENTATION_VERTICAL);
  771. if (device_rotation == 90) {
  772. gtk_widget_set_halign(preview_top_box, GTK_ALIGN_END);
  773. gtk_widget_set_halign(preview_bottom_box, GTK_ALIGN_START);
  774. } else {
  775. gtk_widget_set_halign(preview_top_box, GTK_ALIGN_START);
  776. gtk_widget_set_halign(preview_bottom_box, GTK_ALIGN_END);
  777. }
  778. }
  779. }
  780. static void
  781. display_config_received(GDBusConnection *conn, GAsyncResult *res, gpointer user_data)
  782. {
  783. GError *error = NULL;
  784. GVariant *result = g_dbus_connection_call_finish(conn, res, &error);
  785. if (!result) {
  786. printf("Failed to get display configuration: %s\n", error->message);
  787. g_error_free(error);
  788. return;
  789. }
  790. GVariant *configs = g_variant_get_child_value(result, 1);
  791. if (g_variant_n_children(configs) == 0) {
  792. return;
  793. }
  794. GVariant *config = g_variant_get_child_value(configs, 0);
  795. GVariant *rot_config = g_variant_get_child_value(config, 7);
  796. uint32_t rotation_index = g_variant_get_uint32(rot_config);
  797. assert(rotation_index < 4);
  798. int new_rotation = rotation_index * 90;
  799. if (new_rotation != device_rotation) {
  800. device_rotation = new_rotation;
  801. update_io_pipeline();
  802. update_ui_rotation();
  803. }
  804. g_variant_unref(result);
  805. }
  806. static void
  807. update_screen_rotation(GDBusConnection *conn)
  808. {
  809. g_dbus_connection_call(conn,
  810. "org.gnome.Mutter.DisplayConfig",
  811. "/org/gnome/Mutter/DisplayConfig",
  812. "org.gnome.Mutter.DisplayConfig",
  813. "GetResources",
  814. NULL,
  815. NULL,
  816. G_DBUS_CALL_FLAGS_NO_AUTO_START,
  817. -1,
  818. NULL,
  819. (GAsyncReadyCallback)display_config_received,
  820. NULL);
  821. }
  822. static void
  823. on_screen_rotate(GDBusConnection *conn,
  824. const gchar *sender_name,
  825. const gchar *object_path,
  826. const gchar *interface_name,
  827. const gchar *signal_name,
  828. GVariant *parameters,
  829. gpointer user_data)
  830. {
  831. update_screen_rotation(conn);
  832. }
  833. static void
  834. activate(GtkApplication *app, gpointer data)
  835. {
  836. g_object_set(gtk_settings_get_default(),
  837. "gtk-application-prefer-dark-theme",
  838. TRUE,
  839. NULL);
  840. GdkDisplay *display = gdk_display_get_default();
  841. GtkIconTheme *icon_theme = gtk_icon_theme_get_for_display(display);
  842. gtk_icon_theme_add_resource_path(icon_theme, "/org/postmarketos/Megapixels");
  843. GtkCssProvider *provider = gtk_css_provider_new();
  844. gtk_css_provider_load_from_resource(
  845. provider, "/org/postmarketos/Megapixels/camera.css");
  846. gtk_style_context_add_provider_for_display(
  847. display,
  848. GTK_STYLE_PROVIDER(provider),
  849. GTK_STYLE_PROVIDER_PRIORITY_APPLICATION);
  850. GtkBuilder *builder = gtk_builder_new_from_resource(
  851. "/org/postmarketos/Megapixels/camera.ui");
  852. GtkWidget *window = GTK_WIDGET(gtk_builder_get_object(builder, "window"));
  853. GtkWidget *iso_button =
  854. GTK_WIDGET(gtk_builder_get_object(builder, "iso-controls-button"));
  855. GtkWidget *shutter_button = GTK_WIDGET(
  856. gtk_builder_get_object(builder, "shutter-controls-button"));
  857. flash_button =
  858. GTK_WIDGET(gtk_builder_get_object(builder, "flash-controls-button"));
  859. GtkWidget *setting_dng_button =
  860. GTK_WIDGET(gtk_builder_get_object(builder, "setting-raw"));
  861. preview = GTK_WIDGET(gtk_builder_get_object(builder, "preview"));
  862. main_stack = GTK_WIDGET(gtk_builder_get_object(builder, "main_stack"));
  863. open_last_stack =
  864. GTK_WIDGET(gtk_builder_get_object(builder, "open_last_stack"));
  865. thumb_last = GTK_WIDGET(gtk_builder_get_object(builder, "thumb_last"));
  866. process_spinner =
  867. GTK_WIDGET(gtk_builder_get_object(builder, "process_spinner"));
  868. scanned_codes = GTK_WIDGET(gtk_builder_get_object(builder, "scanned-codes"));
  869. preview_top_box = GTK_WIDGET(gtk_builder_get_object(builder, "top-box"));
  870. preview_bottom_box =
  871. GTK_WIDGET(gtk_builder_get_object(builder, "bottom-box"));
  872. g_signal_connect(window, "realize", G_CALLBACK(on_realize), NULL);
  873. g_signal_connect(preview, "realize", G_CALLBACK(preview_realize), NULL);
  874. g_signal_connect(preview, "render", G_CALLBACK(preview_draw), NULL);
  875. g_signal_connect(preview, "resize", G_CALLBACK(preview_resize), NULL);
  876. GtkGesture *click = gtk_gesture_click_new();
  877. g_signal_connect(click, "pressed", G_CALLBACK(preview_pressed), NULL);
  878. gtk_widget_add_controller(preview, GTK_EVENT_CONTROLLER(click));
  879. g_signal_connect(iso_button, "clicked", G_CALLBACK(open_iso_controls), NULL);
  880. g_signal_connect(
  881. shutter_button, "clicked", G_CALLBACK(open_shutter_controls), NULL);
  882. g_signal_connect(
  883. flash_button, "clicked", G_CALLBACK(flash_button_clicked), NULL);
  884. // Setup actions
  885. create_simple_action(app, "capture", G_CALLBACK(run_capture_action));
  886. create_simple_action(
  887. app, "switch-camera", G_CALLBACK(run_camera_switch_action));
  888. create_simple_action(
  889. app, "open-settings", G_CALLBACK(run_open_settings_action));
  890. create_simple_action(
  891. app, "close-settings", G_CALLBACK(run_close_settings_action));
  892. create_simple_action(app, "open-last", G_CALLBACK(run_open_last_action));
  893. create_simple_action(app, "open-photos", G_CALLBACK(run_open_photos_action));
  894. create_simple_action(app, "about", G_CALLBACK(run_about_action));
  895. create_simple_action(app, "quit", G_CALLBACK(run_quit_action));
  896. // Setup shortcuts
  897. const char *capture_accels[] = { "space", NULL };
  898. gtk_application_set_accels_for_action(app, "app.capture", capture_accels);
  899. const char *quit_accels[] = { "<Ctrl>q", "<Ctrl>w", NULL };
  900. gtk_application_set_accels_for_action(app, "app.quit", quit_accels);
  901. // Setup settings
  902. settings = g_settings_new("org.postmarketos.Megapixels");
  903. g_settings_bind(settings,
  904. "save-raw",
  905. setting_dng_button,
  906. "active",
  907. G_SETTINGS_BIND_DEFAULT);
  908. setting_save_dng = g_settings_get_boolean(settings, "save-raw");
  909. // Listen for phosh rotation
  910. GDBusConnection *conn =
  911. g_application_get_dbus_connection(G_APPLICATION(app));
  912. g_dbus_connection_signal_subscribe(conn,
  913. NULL,
  914. "org.gnome.Mutter.DisplayConfig",
  915. "MonitorsChanged",
  916. "/org/gnome/Mutter/DisplayConfig",
  917. NULL,
  918. G_DBUS_SIGNAL_FLAGS_NONE,
  919. &on_screen_rotate,
  920. NULL,
  921. NULL);
  922. update_screen_rotation(conn);
  923. // Initialize display flash
  924. mp_flash_gtk_init(conn);
  925. mp_io_pipeline_start();
  926. gtk_application_add_window(app, GTK_WINDOW(window));
  927. gtk_widget_show(window);
  928. }
  929. static void
  930. shutdown(GApplication *app, gpointer data)
  931. {
  932. // Only do cleanup in development, let the OS clean up otherwise
  933. #ifdef DEBUG
  934. mp_io_pipeline_stop();
  935. mp_flash_gtk_clean();
  936. #endif
  937. }
  938. int
  939. main(int argc, char *argv[])
  940. {
  941. #ifdef RENDERDOC
  942. {
  943. void *mod = dlopen("librenderdoc.so", RTLD_NOW | RTLD_NOLOAD);
  944. if (mod) {
  945. pRENDERDOC_GetAPI RENDERDOC_GetAPI =
  946. (pRENDERDOC_GetAPI)dlsym(mod, "RENDERDOC_GetAPI");
  947. int ret = RENDERDOC_GetAPI(eRENDERDOC_API_Version_1_1_2,
  948. (void **)&rdoc_api);
  949. assert(ret == 1);
  950. } else {
  951. printf("Renderdoc not found\n");
  952. }
  953. }
  954. #endif
  955. if (!mp_load_config())
  956. return 1;
  957. setenv("LC_NUMERIC", "C", 1);
  958. GtkApplication *app = gtk_application_new("org.postmarketos.Megapixels", 0);
  959. g_signal_connect(app, "activate", G_CALLBACK(activate), NULL);
  960. g_signal_connect(app, "shutdown", G_CALLBACK(shutdown), NULL);
  961. g_application_run(G_APPLICATION(app), argc, argv);
  962. return 0;
  963. }