main.c 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612
  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 "camera_config.h"
  20. #include "quickpreview.h"
  21. #include "io_pipeline.h"
  22. enum user_control { USER_CONTROL_ISO, USER_CONTROL_SHUTTER };
  23. static bool camera_is_initialized = false;
  24. static const struct mp_camera_config *camera = NULL;
  25. static MPCameraMode mode;
  26. static int preview_width = -1;
  27. static int preview_height = -1;
  28. static bool gain_is_manual = false;
  29. static int gain;
  30. static int gain_max;
  31. static bool exposure_is_manual = false;
  32. static int exposure;
  33. static bool has_auto_focus_continuous;
  34. static bool has_auto_focus_start;
  35. static cairo_surface_t *surface = NULL;
  36. static cairo_surface_t *status_surface = NULL;
  37. static char last_path[260] = "";
  38. static int burst_length = 3;
  39. static enum user_control current_control;
  40. // Widgets
  41. GtkWidget *preview;
  42. GtkWidget *error_box;
  43. GtkWidget *error_message;
  44. GtkWidget *main_stack;
  45. GtkWidget *open_last_stack;
  46. GtkWidget *thumb_last;
  47. GtkWidget *process_spinner;
  48. GtkWidget *control_box;
  49. GtkWidget *control_name;
  50. GtkAdjustment *control_slider;
  51. GtkWidget *control_auto;
  52. int
  53. remap(int value, int input_min, int input_max, int output_min, int output_max)
  54. {
  55. const long long factor = 1000000000;
  56. long long output_spread = output_max - output_min;
  57. long long input_spread = input_max - input_min;
  58. long long zero_value = value - input_min;
  59. zero_value *= factor;
  60. long long percentage = zero_value / input_spread;
  61. long long zero_output = percentage * output_spread / factor;
  62. long long result = output_min + zero_output;
  63. return (int)result;
  64. }
  65. static void
  66. update_io_pipeline()
  67. {
  68. struct mp_io_pipeline_state io_state = {
  69. .camera = camera,
  70. .burst_length = burst_length,
  71. .preview_width = preview_width,
  72. .preview_height = preview_height,
  73. .gain_is_manual = gain_is_manual,
  74. .gain = gain,
  75. .exposure_is_manual = exposure_is_manual,
  76. .exposure = exposure,
  77. };
  78. mp_io_pipeline_update_state(&io_state);
  79. }
  80. static bool
  81. update_state(const struct mp_main_state *state)
  82. {
  83. if (!camera_is_initialized) {
  84. camera_is_initialized = true;
  85. }
  86. if (camera == state->camera) {
  87. mode = state->mode;
  88. if (!gain_is_manual) {
  89. gain = state->gain;
  90. }
  91. gain_max = state->gain_max;
  92. if (!exposure_is_manual) {
  93. exposure = state->exposure;
  94. }
  95. has_auto_focus_continuous = state->has_auto_focus_continuous;
  96. has_auto_focus_start = state->has_auto_focus_start;
  97. }
  98. return false;
  99. }
  100. void
  101. mp_main_update_state(const struct mp_main_state *state)
  102. {
  103. struct mp_main_state *state_copy = malloc(sizeof(struct mp_main_state));
  104. *state_copy = *state;
  105. g_main_context_invoke_full(g_main_context_default(), G_PRIORITY_DEFAULT_IDLE,
  106. (GSourceFunc)update_state, state_copy, free);
  107. }
  108. static bool
  109. set_preview(cairo_surface_t *image)
  110. {
  111. if (surface) {
  112. cairo_surface_destroy(surface);
  113. }
  114. surface = image;
  115. gtk_widget_queue_draw(preview);
  116. return false;
  117. }
  118. void
  119. mp_main_set_preview(cairo_surface_t *image)
  120. {
  121. g_main_context_invoke_full(g_main_context_default(), G_PRIORITY_DEFAULT_IDLE,
  122. (GSourceFunc)set_preview, image, NULL);
  123. }
  124. void
  125. draw_surface_scaled_centered(cairo_t *cr, uint32_t dst_width, uint32_t dst_height,
  126. cairo_surface_t *surface)
  127. {
  128. cairo_save(cr);
  129. cairo_translate(cr, dst_width / 2, dst_height / 2);
  130. int width = cairo_image_surface_get_width(surface);
  131. int height = cairo_image_surface_get_height(surface);
  132. double scale = MIN(dst_width / (double)width, dst_height / (double)height);
  133. cairo_scale(cr, scale, scale);
  134. cairo_translate(cr, -width / 2, -height / 2);
  135. cairo_set_source_surface(cr, surface, 0, 0);
  136. cairo_paint(cr);
  137. cairo_restore(cr);
  138. }
  139. struct capture_completed_args {
  140. cairo_surface_t *thumb;
  141. char *fname;
  142. };
  143. static bool
  144. capture_completed(struct capture_completed_args *args)
  145. {
  146. strncpy(last_path, args->fname, 259);
  147. gtk_image_set_from_surface(GTK_IMAGE(thumb_last), args->thumb);
  148. gtk_spinner_stop(GTK_SPINNER(process_spinner));
  149. gtk_stack_set_visible_child(GTK_STACK(open_last_stack), thumb_last);
  150. cairo_surface_destroy(args->thumb);
  151. g_free(args->fname);
  152. return false;
  153. }
  154. void
  155. mp_main_capture_completed(cairo_surface_t *thumb, const char *fname)
  156. {
  157. struct capture_completed_args *args = malloc(sizeof(struct capture_completed_args));
  158. args->thumb = thumb;
  159. args->fname = g_strdup(fname);
  160. g_main_context_invoke_full(g_main_context_default(), G_PRIORITY_DEFAULT_IDLE,
  161. (GSourceFunc)capture_completed, args, free);
  162. }
  163. static void
  164. draw_controls()
  165. {
  166. cairo_t *cr;
  167. char iso[6];
  168. int temp;
  169. char shutterangle[6];
  170. if (exposure_is_manual) {
  171. temp = (int)((float)exposure / (float)camera->capture_mode.height *
  172. 360);
  173. sprintf(shutterangle, "%d\u00b0", temp);
  174. } else {
  175. sprintf(shutterangle, "auto");
  176. }
  177. if (gain_is_manual) {
  178. temp = remap(gain - 1, 0, gain_max, camera->iso_min,
  179. camera->iso_max);
  180. sprintf(iso, "%d", temp);
  181. } else {
  182. sprintf(iso, "auto");
  183. }
  184. if (status_surface)
  185. cairo_surface_destroy(status_surface);
  186. // Make a service to show status of controls, 32px high
  187. if (gtk_widget_get_window(preview) == NULL) {
  188. return;
  189. }
  190. status_surface =
  191. gdk_window_create_similar_surface(gtk_widget_get_window(preview),
  192. CAIRO_CONTENT_COLOR_ALPHA,
  193. preview_width, 32);
  194. cr = cairo_create(status_surface);
  195. cairo_set_source_rgba(cr, 0, 0, 0, 0.0);
  196. cairo_paint(cr);
  197. // Draw the outlines for the headings
  198. cairo_select_font_face(cr, "sans-serif", CAIRO_FONT_SLANT_NORMAL,
  199. CAIRO_FONT_WEIGHT_BOLD);
  200. cairo_set_font_size(cr, 9);
  201. cairo_set_source_rgba(cr, 0, 0, 0, 1);
  202. cairo_move_to(cr, 16, 16);
  203. cairo_text_path(cr, "ISO");
  204. cairo_stroke(cr);
  205. cairo_move_to(cr, 60, 16);
  206. cairo_text_path(cr, "Shutter");
  207. cairo_stroke(cr);
  208. // Draw the fill for the headings
  209. cairo_set_source_rgba(cr, 1, 1, 1, 1);
  210. cairo_move_to(cr, 16, 16);
  211. cairo_show_text(cr, "ISO");
  212. cairo_move_to(cr, 60, 16);
  213. cairo_show_text(cr, "Shutter");
  214. // Draw the outlines for the values
  215. cairo_select_font_face(cr, "sans-serif", CAIRO_FONT_SLANT_NORMAL,
  216. CAIRO_FONT_WEIGHT_NORMAL);
  217. cairo_set_font_size(cr, 11);
  218. cairo_set_source_rgba(cr, 0, 0, 0, 1);
  219. cairo_move_to(cr, 16, 26);
  220. cairo_text_path(cr, iso);
  221. cairo_stroke(cr);
  222. cairo_move_to(cr, 60, 26);
  223. cairo_text_path(cr, shutterangle);
  224. cairo_stroke(cr);
  225. // Draw the fill for the values
  226. cairo_set_source_rgba(cr, 1, 1, 1, 1);
  227. cairo_move_to(cr, 16, 26);
  228. cairo_show_text(cr, iso);
  229. cairo_move_to(cr, 60, 26);
  230. cairo_show_text(cr, shutterangle);
  231. cairo_destroy(cr);
  232. gtk_widget_queue_draw_area(preview, 0, 0, preview_width, 32);
  233. }
  234. static gboolean
  235. preview_draw(GtkWidget *widget, cairo_t *cr, gpointer data)
  236. {
  237. if (!camera_is_initialized) {
  238. return FALSE;
  239. }
  240. // Clear preview area with black
  241. cairo_paint(cr);
  242. // Draw camera preview
  243. if (surface) {
  244. draw_surface_scaled_centered(cr, preview_width, preview_height,
  245. surface);
  246. }
  247. // Draw control overlay
  248. cairo_set_source_surface(cr, status_surface, 0, 0);
  249. cairo_paint(cr);
  250. return FALSE;
  251. }
  252. static gboolean
  253. preview_configure(GtkWidget *widget, GdkEventConfigure *event)
  254. {
  255. int new_preview_width = gtk_widget_get_allocated_width(widget);
  256. int new_preview_height = gtk_widget_get_allocated_height(widget);
  257. if (preview_width != new_preview_width ||
  258. preview_height != new_preview_height) {
  259. preview_width = new_preview_width;
  260. preview_height = new_preview_height;
  261. update_io_pipeline();
  262. }
  263. draw_controls();
  264. return TRUE;
  265. }
  266. void
  267. on_open_last_clicked(GtkWidget *widget, gpointer user_data)
  268. {
  269. char uri[275];
  270. GError *error = NULL;
  271. if (strlen(last_path) == 0) {
  272. return;
  273. }
  274. sprintf(uri, "file://%s", last_path);
  275. if (!g_app_info_launch_default_for_uri(uri, NULL, &error)) {
  276. g_printerr("Could not launch image viewer for '%s': %s\n", uri, error->message);
  277. }
  278. }
  279. void
  280. on_open_directory_clicked(GtkWidget *widget, gpointer user_data)
  281. {
  282. char uri[270];
  283. GError *error = NULL;
  284. sprintf(uri, "file://%s", g_get_user_special_dir(G_USER_DIRECTORY_PICTURES));
  285. if (!g_app_info_launch_default_for_uri(uri, NULL, &error)) {
  286. g_printerr("Could not launch image viewer: %s\n", error->message);
  287. }
  288. }
  289. void
  290. on_shutter_clicked(GtkWidget *widget, gpointer user_data)
  291. {
  292. gtk_spinner_start(GTK_SPINNER(process_spinner));
  293. gtk_stack_set_visible_child(GTK_STACK(open_last_stack), process_spinner);
  294. mp_io_pipeline_capture();
  295. }
  296. void
  297. on_preview_tap(GtkWidget *widget, GdkEventButton *event, gpointer user_data)
  298. {
  299. if (event->type != GDK_BUTTON_PRESS)
  300. return;
  301. // Handle taps on the controls
  302. if (event->y < 32) {
  303. if (gtk_widget_is_visible(control_box)) {
  304. gtk_widget_hide(control_box);
  305. return;
  306. } else {
  307. gtk_widget_show(control_box);
  308. }
  309. if (event->x < 60) {
  310. // ISO
  311. current_control = USER_CONTROL_ISO;
  312. gtk_label_set_text(GTK_LABEL(control_name), "ISO");
  313. gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(control_auto),
  314. !gain_is_manual);
  315. gtk_adjustment_set_lower(control_slider, 0.0);
  316. gtk_adjustment_set_upper(control_slider, (float)gain_max);
  317. gtk_adjustment_set_value(control_slider, (double)gain);
  318. } else if (event->x > 60 && event->x < 120) {
  319. // Shutter angle
  320. current_control = USER_CONTROL_SHUTTER;
  321. gtk_label_set_text(GTK_LABEL(control_name), "Shutter");
  322. gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(control_auto),
  323. !exposure_is_manual);
  324. gtk_adjustment_set_lower(control_slider, 1.0);
  325. gtk_adjustment_set_upper(control_slider, 360.0);
  326. gtk_adjustment_set_value(control_slider, (double)exposure);
  327. }
  328. return;
  329. }
  330. // Tapped preview image itself, try focussing
  331. if (has_auto_focus_start) {
  332. mp_io_pipeline_focus();
  333. }
  334. }
  335. void
  336. on_error_close_clicked(GtkWidget *widget, gpointer user_data)
  337. {
  338. gtk_widget_hide(error_box);
  339. }
  340. void
  341. on_camera_switch_clicked(GtkWidget *widget, gpointer user_data)
  342. {
  343. size_t next_index = camera->index + 1;
  344. const struct mp_camera_config *next_camera =
  345. mp_get_camera_config(next_index);
  346. if (!next_camera) {
  347. next_index = 0;
  348. next_camera = mp_get_camera_config(next_index);
  349. }
  350. camera = next_camera;
  351. update_io_pipeline();
  352. }
  353. void
  354. on_settings_btn_clicked(GtkWidget *widget, gpointer user_data)
  355. {
  356. gtk_stack_set_visible_child_name(GTK_STACK(main_stack), "settings");
  357. }
  358. void
  359. on_back_clicked(GtkWidget *widget, gpointer user_data)
  360. {
  361. gtk_stack_set_visible_child_name(GTK_STACK(main_stack), "main");
  362. }
  363. void
  364. on_control_auto_toggled(GtkToggleButton *widget, gpointer user_data)
  365. {
  366. bool is_manual = gtk_toggle_button_get_active(widget) ? false : true;
  367. bool has_changed;
  368. switch (current_control) {
  369. case USER_CONTROL_ISO:
  370. if (gain_is_manual != is_manual) {
  371. gain_is_manual = is_manual;
  372. has_changed = true;
  373. }
  374. break;
  375. case USER_CONTROL_SHUTTER:
  376. if (exposure_is_manual != is_manual) {
  377. exposure_is_manual = is_manual;
  378. has_changed = true;
  379. }
  380. break;
  381. }
  382. if (has_changed) {
  383. // The slider might have been moved while Auto mode is active. When entering
  384. // Manual mode, first read the slider value to sync with those changes.
  385. double value = gtk_adjustment_get_value(control_slider);
  386. switch (current_control) {
  387. case USER_CONTROL_ISO:
  388. if (value != gain) {
  389. gain = (int)value;
  390. }
  391. break;
  392. case USER_CONTROL_SHUTTER: {
  393. // So far all sensors use exposure time in number of sensor rows
  394. int new_exposure =
  395. (int)(value / 360.0 * camera->capture_mode.height);
  396. if (new_exposure != exposure) {
  397. exposure = new_exposure;
  398. }
  399. break;
  400. }
  401. }
  402. update_io_pipeline();
  403. draw_controls();
  404. }
  405. }
  406. void
  407. on_control_slider_changed(GtkAdjustment *widget, gpointer user_data)
  408. {
  409. double value = gtk_adjustment_get_value(widget);
  410. bool has_changed = false;
  411. switch (current_control) {
  412. case USER_CONTROL_ISO:
  413. if (value != gain) {
  414. gain = (int)value;
  415. has_changed = true;
  416. }
  417. break;
  418. case USER_CONTROL_SHUTTER: {
  419. // So far all sensors use exposure time in number of sensor rows
  420. int new_exposure =
  421. (int)(value / 360.0 * camera->capture_mode.height);
  422. if (new_exposure != exposure) {
  423. exposure = new_exposure;
  424. has_changed = true;
  425. }
  426. break;
  427. }
  428. }
  429. if (has_changed) {
  430. update_io_pipeline();
  431. draw_controls();
  432. }
  433. }
  434. int
  435. main(int argc, char *argv[])
  436. {
  437. if (!mp_load_config())
  438. return 1;
  439. setenv("LC_NUMERIC", "C", 1);
  440. gtk_init(&argc, &argv);
  441. g_object_set(gtk_settings_get_default(), "gtk-application-prefer-dark-theme",
  442. TRUE, NULL);
  443. GtkBuilder *builder = gtk_builder_new_from_resource(
  444. "/org/postmarketos/Megapixels/camera.glade");
  445. GtkWidget *window = GTK_WIDGET(gtk_builder_get_object(builder, "window"));
  446. GtkWidget *shutter = GTK_WIDGET(gtk_builder_get_object(builder, "shutter"));
  447. GtkWidget *switch_btn =
  448. GTK_WIDGET(gtk_builder_get_object(builder, "switch_camera"));
  449. GtkWidget *settings_btn =
  450. GTK_WIDGET(gtk_builder_get_object(builder, "settings"));
  451. GtkWidget *settings_back =
  452. GTK_WIDGET(gtk_builder_get_object(builder, "settings_back"));
  453. GtkWidget *error_close =
  454. GTK_WIDGET(gtk_builder_get_object(builder, "error_close"));
  455. GtkWidget *open_last =
  456. GTK_WIDGET(gtk_builder_get_object(builder, "open_last"));
  457. GtkWidget *open_directory =
  458. GTK_WIDGET(gtk_builder_get_object(builder, "open_directory"));
  459. preview = GTK_WIDGET(gtk_builder_get_object(builder, "preview"));
  460. error_box = GTK_WIDGET(gtk_builder_get_object(builder, "error_box"));
  461. error_message = GTK_WIDGET(gtk_builder_get_object(builder, "error_message"));
  462. main_stack = GTK_WIDGET(gtk_builder_get_object(builder, "main_stack"));
  463. open_last_stack = GTK_WIDGET(gtk_builder_get_object(builder, "open_last_stack"));
  464. thumb_last = GTK_WIDGET(gtk_builder_get_object(builder, "thumb_last"));
  465. process_spinner = GTK_WIDGET(gtk_builder_get_object(builder, "process_spinner"));
  466. control_box = GTK_WIDGET(gtk_builder_get_object(builder, "control_box"));
  467. control_name = GTK_WIDGET(gtk_builder_get_object(builder, "control_name"));
  468. control_slider =
  469. GTK_ADJUSTMENT(gtk_builder_get_object(builder, "control_adj"));
  470. control_auto = GTK_WIDGET(gtk_builder_get_object(builder, "control_auto"));
  471. g_signal_connect(window, "destroy", G_CALLBACK(gtk_main_quit), NULL);
  472. g_signal_connect(shutter, "clicked", G_CALLBACK(on_shutter_clicked), NULL);
  473. g_signal_connect(error_close, "clicked", G_CALLBACK(on_error_close_clicked),
  474. NULL);
  475. g_signal_connect(switch_btn, "clicked", G_CALLBACK(on_camera_switch_clicked),
  476. NULL);
  477. g_signal_connect(settings_btn, "clicked",
  478. G_CALLBACK(on_settings_btn_clicked), NULL);
  479. g_signal_connect(settings_back, "clicked", G_CALLBACK(on_back_clicked),
  480. NULL);
  481. g_signal_connect(open_last, "clicked", G_CALLBACK(on_open_last_clicked),
  482. NULL);
  483. g_signal_connect(open_directory, "clicked",
  484. G_CALLBACK(on_open_directory_clicked), NULL);
  485. g_signal_connect(preview, "draw", G_CALLBACK(preview_draw), NULL);
  486. g_signal_connect(preview, "configure-event", G_CALLBACK(preview_configure),
  487. NULL);
  488. gtk_widget_set_events(preview, gtk_widget_get_events(preview) |
  489. GDK_BUTTON_PRESS_MASK |
  490. GDK_POINTER_MOTION_MASK);
  491. g_signal_connect(preview, "button-press-event", G_CALLBACK(on_preview_tap),
  492. NULL);
  493. g_signal_connect(control_auto, "toggled",
  494. G_CALLBACK(on_control_auto_toggled), NULL);
  495. g_signal_connect(control_slider, "value-changed",
  496. G_CALLBACK(on_control_slider_changed), NULL);
  497. GtkCssProvider *provider = gtk_css_provider_new();
  498. if (access("camera.css", F_OK) != -1) {
  499. gtk_css_provider_load_from_path(provider, "camera.css", NULL);
  500. } else {
  501. gtk_css_provider_load_from_resource(
  502. provider, "/org/postmarketos/Megapixels/camera.css");
  503. }
  504. GtkStyleContext *context = gtk_widget_get_style_context(error_box);
  505. gtk_style_context_add_provider(context, GTK_STYLE_PROVIDER(provider),
  506. GTK_STYLE_PROVIDER_PRIORITY_USER);
  507. context = gtk_widget_get_style_context(control_box);
  508. gtk_style_context_add_provider(context, GTK_STYLE_PROVIDER(provider),
  509. GTK_STYLE_PROVIDER_PRIORITY_USER);
  510. mp_io_pipeline_start();
  511. camera = mp_get_camera_config(0);
  512. update_io_pipeline();
  513. gtk_widget_show(window);
  514. gtk_main();
  515. mp_io_pipeline_stop();
  516. return 0;
  517. }