main.c 14 KB

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