main.c 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800
  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 "camera_config.h"
  21. #include "quickpreview.h"
  22. #include "io_pipeline.h"
  23. #include "process_pipeline.h"
  24. enum user_control { USER_CONTROL_ISO, USER_CONTROL_SHUTTER };
  25. static bool camera_is_initialized = false;
  26. static const struct mp_camera_config *camera = NULL;
  27. static MPCameraMode mode;
  28. static int preview_width = -1;
  29. static int preview_height = -1;
  30. static bool gain_is_manual = false;
  31. static int gain;
  32. static int gain_max;
  33. static bool exposure_is_manual = false;
  34. static int exposure;
  35. static bool has_auto_focus_continuous;
  36. static bool has_auto_focus_start;
  37. static cairo_surface_t *surface = NULL;
  38. static cairo_surface_t *status_surface = NULL;
  39. static char last_path[260] = "";
  40. static MPZBarScanResult *zbar_result = NULL;
  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 *open_last_stack;
  49. GtkWidget *thumb_last;
  50. GtkWidget *process_spinner;
  51. GtkWidget *control_box;
  52. GtkWidget *control_name;
  53. GtkAdjustment *control_slider;
  54. GtkWidget *control_auto;
  55. int
  56. remap(int value, int input_min, int input_max, int output_min, int output_max)
  57. {
  58. const long long factor = 1000000000;
  59. long long output_spread = output_max - output_min;
  60. long long input_spread = input_max - input_min;
  61. long long zero_value = value - input_min;
  62. zero_value *= factor;
  63. long long percentage = zero_value / input_spread;
  64. long long zero_output = percentage * output_spread / factor;
  65. long long result = output_min + zero_output;
  66. return (int)result;
  67. }
  68. static void
  69. update_io_pipeline()
  70. {
  71. struct mp_io_pipeline_state io_state = {
  72. .camera = camera,
  73. .burst_length = burst_length,
  74. .preview_width = preview_width,
  75. .preview_height = preview_height,
  76. .gain_is_manual = gain_is_manual,
  77. .gain = gain,
  78. .exposure_is_manual = exposure_is_manual,
  79. .exposure = exposure,
  80. };
  81. mp_io_pipeline_update_state(&io_state);
  82. }
  83. static bool
  84. update_state(const struct mp_main_state *state)
  85. {
  86. if (!camera_is_initialized) {
  87. camera_is_initialized = true;
  88. }
  89. if (camera == state->camera) {
  90. mode = state->mode;
  91. if (!gain_is_manual) {
  92. gain = state->gain;
  93. }
  94. gain_max = state->gain_max;
  95. if (!exposure_is_manual) {
  96. exposure = state->exposure;
  97. }
  98. has_auto_focus_continuous = state->has_auto_focus_continuous;
  99. has_auto_focus_start = state->has_auto_focus_start;
  100. }
  101. return false;
  102. }
  103. void
  104. mp_main_update_state(const struct mp_main_state *state)
  105. {
  106. struct mp_main_state *state_copy = malloc(sizeof(struct mp_main_state));
  107. *state_copy = *state;
  108. g_main_context_invoke_full(g_main_context_default(), G_PRIORITY_DEFAULT_IDLE,
  109. (GSourceFunc)update_state, state_copy, free);
  110. }
  111. static bool set_zbar_result(MPZBarScanResult *result)
  112. {
  113. if (zbar_result) {
  114. for (uint8_t i = 0; i < zbar_result->size; ++i) {
  115. free(zbar_result->codes[i].data);
  116. }
  117. free(zbar_result);
  118. }
  119. zbar_result = result;
  120. gtk_widget_queue_draw(preview);
  121. return false;
  122. }
  123. void mp_main_set_zbar_result(MPZBarScanResult *result)
  124. {
  125. g_main_context_invoke_full(g_main_context_default(), G_PRIORITY_DEFAULT_IDLE,
  126. (GSourceFunc)set_zbar_result, result, NULL);
  127. }
  128. static bool
  129. set_preview(cairo_surface_t *image)
  130. {
  131. if (surface) {
  132. cairo_surface_destroy(surface);
  133. }
  134. surface = image;
  135. gtk_widget_queue_draw(preview);
  136. return false;
  137. }
  138. void
  139. mp_main_set_preview(cairo_surface_t *image)
  140. {
  141. g_main_context_invoke_full(g_main_context_default(), G_PRIORITY_DEFAULT_IDLE,
  142. (GSourceFunc)set_preview, image, NULL);
  143. }
  144. static void transform_centered(cairo_t *cr, uint32_t dst_width, uint32_t dst_height,
  145. int src_width, int src_height)
  146. {
  147. cairo_translate(cr, dst_width / 2, dst_height / 2);
  148. double scale = MIN(dst_width / (double)src_width, dst_height / (double)src_height);
  149. cairo_scale(cr, scale, scale);
  150. cairo_translate(cr, -src_width / 2, -src_height / 2);
  151. }
  152. void
  153. draw_surface_scaled_centered(cairo_t *cr, uint32_t dst_width, uint32_t dst_height,
  154. cairo_surface_t *surface)
  155. {
  156. cairo_save(cr);
  157. int width = cairo_image_surface_get_width(surface);
  158. int height = cairo_image_surface_get_height(surface);
  159. transform_centered(cr, dst_width, dst_height, width, height);
  160. cairo_set_source_surface(cr, surface, 0, 0);
  161. cairo_paint(cr);
  162. cairo_restore(cr);
  163. }
  164. struct capture_completed_args {
  165. cairo_surface_t *thumb;
  166. char *fname;
  167. };
  168. static bool
  169. capture_completed(struct capture_completed_args *args)
  170. {
  171. strncpy(last_path, args->fname, 259);
  172. gtk_image_set_from_surface(GTK_IMAGE(thumb_last), args->thumb);
  173. gtk_spinner_stop(GTK_SPINNER(process_spinner));
  174. gtk_stack_set_visible_child(GTK_STACK(open_last_stack), thumb_last);
  175. cairo_surface_destroy(args->thumb);
  176. g_free(args->fname);
  177. return false;
  178. }
  179. void
  180. mp_main_capture_completed(cairo_surface_t *thumb, const char *fname)
  181. {
  182. struct capture_completed_args *args = malloc(sizeof(struct capture_completed_args));
  183. args->thumb = thumb;
  184. args->fname = g_strdup(fname);
  185. g_main_context_invoke_full(g_main_context_default(), G_PRIORITY_DEFAULT_IDLE,
  186. (GSourceFunc)capture_completed, args, free);
  187. }
  188. static void
  189. draw_controls()
  190. {
  191. cairo_t *cr;
  192. char iso[6];
  193. int temp;
  194. char shutterangle[6];
  195. if (exposure_is_manual) {
  196. temp = (int)((float)exposure / (float)camera->capture_mode.height *
  197. 360);
  198. sprintf(shutterangle, "%d\u00b0", temp);
  199. } else {
  200. sprintf(shutterangle, "auto");
  201. }
  202. if (gain_is_manual) {
  203. temp = remap(gain - 1, 0, gain_max, camera->iso_min,
  204. camera->iso_max);
  205. sprintf(iso, "%d", temp);
  206. } else {
  207. sprintf(iso, "auto");
  208. }
  209. if (status_surface)
  210. cairo_surface_destroy(status_surface);
  211. // Make a service to show status of controls, 32px high
  212. if (gtk_widget_get_window(preview) == NULL) {
  213. return;
  214. }
  215. status_surface =
  216. gdk_window_create_similar_surface(gtk_widget_get_window(preview),
  217. CAIRO_CONTENT_COLOR_ALPHA,
  218. preview_width, 32);
  219. cr = cairo_create(status_surface);
  220. cairo_set_source_rgba(cr, 0, 0, 0, 0.0);
  221. cairo_paint(cr);
  222. // Draw the outlines for the headings
  223. cairo_select_font_face(cr, "sans-serif", CAIRO_FONT_SLANT_NORMAL,
  224. CAIRO_FONT_WEIGHT_BOLD);
  225. cairo_set_font_size(cr, 9);
  226. cairo_set_source_rgba(cr, 0, 0, 0, 1);
  227. cairo_move_to(cr, 16, 16);
  228. cairo_text_path(cr, "ISO");
  229. cairo_stroke(cr);
  230. cairo_move_to(cr, 60, 16);
  231. cairo_text_path(cr, "Shutter");
  232. cairo_stroke(cr);
  233. // Draw the fill for the headings
  234. cairo_set_source_rgba(cr, 1, 1, 1, 1);
  235. cairo_move_to(cr, 16, 16);
  236. cairo_show_text(cr, "ISO");
  237. cairo_move_to(cr, 60, 16);
  238. cairo_show_text(cr, "Shutter");
  239. // Draw the outlines for the values
  240. cairo_select_font_face(cr, "sans-serif", CAIRO_FONT_SLANT_NORMAL,
  241. CAIRO_FONT_WEIGHT_NORMAL);
  242. cairo_set_font_size(cr, 11);
  243. cairo_set_source_rgba(cr, 0, 0, 0, 1);
  244. cairo_move_to(cr, 16, 26);
  245. cairo_text_path(cr, iso);
  246. cairo_stroke(cr);
  247. cairo_move_to(cr, 60, 26);
  248. cairo_text_path(cr, shutterangle);
  249. cairo_stroke(cr);
  250. // Draw the fill for the values
  251. cairo_set_source_rgba(cr, 1, 1, 1, 1);
  252. cairo_move_to(cr, 16, 26);
  253. cairo_show_text(cr, iso);
  254. cairo_move_to(cr, 60, 26);
  255. cairo_show_text(cr, shutterangle);
  256. cairo_destroy(cr);
  257. gtk_widget_queue_draw_area(preview, 0, 0, preview_width, 32);
  258. }
  259. static gboolean
  260. preview_draw(GtkWidget *widget, cairo_t *cr, gpointer data)
  261. {
  262. if (!camera_is_initialized) {
  263. return FALSE;
  264. }
  265. // Clear preview area with black
  266. cairo_paint(cr);
  267. if (surface) {
  268. // Draw camera preview
  269. cairo_save(cr);
  270. int width = cairo_image_surface_get_width(surface);
  271. int height = cairo_image_surface_get_height(surface);
  272. transform_centered(cr, preview_width, preview_height, width, height);
  273. cairo_set_source_surface(cr, surface, 0, 0);
  274. cairo_paint(cr);
  275. // Draw zbar image
  276. if (zbar_result) {
  277. for (uint8_t i = 0; i < zbar_result->size; ++i) {
  278. MPZBarCode *code = &zbar_result->codes[i];
  279. cairo_set_line_width(cr, 3.0);
  280. cairo_set_source_rgba(cr, 0, 0.5, 1, 0.75);
  281. cairo_new_path(cr);
  282. cairo_move_to(cr, code->bounds_x[0], code->bounds_y[0]);
  283. for (uint8_t i = 0; i < 4; ++i) {
  284. cairo_line_to(cr, code->bounds_x[i], code->bounds_y[i]);
  285. }
  286. cairo_close_path(cr);
  287. cairo_stroke(cr);
  288. cairo_save(cr);
  289. cairo_translate(cr, code->bounds_x[0], code->bounds_y[0]);
  290. cairo_show_text(cr, code->data);
  291. cairo_restore(cr);
  292. }
  293. }
  294. cairo_restore(cr);
  295. }
  296. // Draw control overlay
  297. cairo_set_source_surface(cr, status_surface, 0, 0);
  298. cairo_paint(cr);
  299. return FALSE;
  300. }
  301. static gboolean
  302. preview_configure(GtkWidget *widget, GdkEventConfigure *event)
  303. {
  304. int new_preview_width = gtk_widget_get_allocated_width(widget);
  305. int new_preview_height = gtk_widget_get_allocated_height(widget);
  306. if (preview_width != new_preview_width ||
  307. preview_height != new_preview_height) {
  308. preview_width = new_preview_width;
  309. preview_height = new_preview_height;
  310. update_io_pipeline();
  311. }
  312. draw_controls();
  313. return TRUE;
  314. }
  315. void
  316. on_open_last_clicked(GtkWidget *widget, gpointer user_data)
  317. {
  318. char uri[275];
  319. GError *error = NULL;
  320. if (strlen(last_path) == 0) {
  321. return;
  322. }
  323. sprintf(uri, "file://%s", last_path);
  324. if (!g_app_info_launch_default_for_uri(uri, NULL, &error)) {
  325. g_printerr("Could not launch image viewer for '%s': %s\n", uri, error->message);
  326. }
  327. }
  328. void
  329. on_open_directory_clicked(GtkWidget *widget, gpointer user_data)
  330. {
  331. char uri[270];
  332. GError *error = NULL;
  333. sprintf(uri, "file://%s", g_get_user_special_dir(G_USER_DIRECTORY_PICTURES));
  334. if (!g_app_info_launch_default_for_uri(uri, NULL, &error)) {
  335. g_printerr("Could not launch image viewer: %s\n", error->message);
  336. }
  337. }
  338. void
  339. on_shutter_clicked(GtkWidget *widget, gpointer user_data)
  340. {
  341. gtk_spinner_start(GTK_SPINNER(process_spinner));
  342. gtk_stack_set_visible_child(GTK_STACK(open_last_stack), process_spinner);
  343. mp_io_pipeline_capture();
  344. }
  345. void
  346. on_capture_shortcut(void)
  347. {
  348. on_shutter_clicked(NULL, NULL);
  349. }
  350. static bool
  351. check_point_inside_bounds(int x, int y, int *bounds_x, int *bounds_y)
  352. {
  353. bool right = false, left = false, top = false, bottom = false;
  354. for (int i = 0; i < 4; ++i) {
  355. if (x <= bounds_x[i])
  356. left = true;
  357. if (x >= bounds_x[i])
  358. right = true;
  359. if (y <= bounds_y[i])
  360. top = true;
  361. if (y >= bounds_y[i])
  362. bottom = true;
  363. }
  364. return right && left && top && bottom;
  365. }
  366. static void
  367. on_zbar_code_tapped(GtkWidget *widget, const MPZBarCode *code)
  368. {
  369. GtkWidget *dialog;
  370. GtkDialogFlags flags = GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT;
  371. bool data_is_url = g_uri_is_valid(
  372. code->data, G_URI_FLAGS_PARSE_RELAXED, NULL);
  373. char* data = strdup(code->data);
  374. if (data_is_url) {
  375. dialog = gtk_message_dialog_new(
  376. GTK_WINDOW(gtk_widget_get_toplevel(widget)),
  377. flags,
  378. GTK_MESSAGE_QUESTION,
  379. GTK_BUTTONS_NONE,
  380. "Found a URL '%s' encoded in a %s code.",
  381. code->data,
  382. code->type);
  383. gtk_dialog_add_buttons(
  384. GTK_DIALOG(dialog),
  385. "_Open URL",
  386. GTK_RESPONSE_YES,
  387. NULL);
  388. } else {
  389. dialog = gtk_message_dialog_new(
  390. GTK_WINDOW(gtk_widget_get_toplevel(widget)),
  391. flags,
  392. GTK_MESSAGE_QUESTION,
  393. GTK_BUTTONS_NONE,
  394. "Found '%s' encoded in a %s code.",
  395. code->data,
  396. code->type);
  397. }
  398. gtk_dialog_add_buttons(
  399. GTK_DIALOG(dialog),
  400. "_Copy",
  401. GTK_RESPONSE_ACCEPT,
  402. "_Cancel",
  403. GTK_RESPONSE_CANCEL,
  404. NULL);
  405. int result = gtk_dialog_run(GTK_DIALOG(dialog));
  406. GError *error = NULL;
  407. switch (result) {
  408. case GTK_RESPONSE_YES:
  409. if (!g_app_info_launch_default_for_uri(data,
  410. NULL, &error)) {
  411. g_printerr("Could not launch application: %s\n",
  412. error->message);
  413. }
  414. case GTK_RESPONSE_ACCEPT:
  415. gtk_clipboard_set_text(
  416. gtk_clipboard_get(GDK_SELECTION_PRIMARY),
  417. data, -1);
  418. case GTK_RESPONSE_CANCEL:
  419. break;
  420. default:
  421. g_printerr("Wrong dialog result: %d\n", result);
  422. }
  423. gtk_widget_destroy(dialog);
  424. }
  425. void
  426. on_preview_tap(GtkWidget *widget, GdkEventButton *event, gpointer user_data)
  427. {
  428. if (event->type != GDK_BUTTON_PRESS)
  429. return;
  430. // Handle taps on the controls
  431. if (event->y < 32) {
  432. if (gtk_widget_is_visible(control_box)) {
  433. gtk_widget_hide(control_box);
  434. return;
  435. } else {
  436. gtk_widget_show(control_box);
  437. }
  438. if (event->x < 60) {
  439. // ISO
  440. current_control = USER_CONTROL_ISO;
  441. gtk_label_set_text(GTK_LABEL(control_name), "ISO");
  442. gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(control_auto),
  443. !gain_is_manual);
  444. gtk_adjustment_set_lower(control_slider, 0.0);
  445. gtk_adjustment_set_upper(control_slider, (float)gain_max);
  446. gtk_adjustment_set_value(control_slider, (double)gain);
  447. } else if (event->x > 60 && event->x < 120) {
  448. // Shutter angle
  449. current_control = USER_CONTROL_SHUTTER;
  450. gtk_label_set_text(GTK_LABEL(control_name), "Shutter");
  451. gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(control_auto),
  452. !exposure_is_manual);
  453. gtk_adjustment_set_lower(control_slider, 1.0);
  454. gtk_adjustment_set_upper(control_slider, 360.0);
  455. gtk_adjustment_set_value(control_slider, (double)exposure);
  456. }
  457. return;
  458. }
  459. // Tapped zbar result
  460. if (zbar_result) {
  461. // Transform the event coordinates to the image
  462. int width = cairo_image_surface_get_width(surface);
  463. int height = cairo_image_surface_get_height(surface);
  464. double scale = MIN(preview_width / (double)width, preview_height / (double)height);
  465. int x = (event->x - preview_width / 2) / scale + width / 2;
  466. int y = (event->y - preview_height / 2) / scale + height / 2;
  467. for (uint8_t i = 0; i < zbar_result->size; ++i) {
  468. MPZBarCode *code = &zbar_result->codes[i];
  469. if (check_point_inside_bounds(x, y, code->bounds_x, code->bounds_y)) {
  470. on_zbar_code_tapped(widget, code);
  471. return;
  472. }
  473. }
  474. }
  475. // Tapped preview image itself, try focussing
  476. if (has_auto_focus_start) {
  477. mp_io_pipeline_focus();
  478. }
  479. }
  480. void
  481. on_error_close_clicked(GtkWidget *widget, gpointer user_data)
  482. {
  483. gtk_widget_hide(error_box);
  484. }
  485. void
  486. on_camera_switch_clicked(GtkWidget *widget, gpointer user_data)
  487. {
  488. size_t next_index = camera->index + 1;
  489. const struct mp_camera_config *next_camera =
  490. mp_get_camera_config(next_index);
  491. if (!next_camera) {
  492. next_index = 0;
  493. next_camera = mp_get_camera_config(next_index);
  494. }
  495. camera = next_camera;
  496. update_io_pipeline();
  497. }
  498. void
  499. on_settings_btn_clicked(GtkWidget *widget, gpointer user_data)
  500. {
  501. gtk_stack_set_visible_child_name(GTK_STACK(main_stack), "settings");
  502. }
  503. void
  504. on_back_clicked(GtkWidget *widget, gpointer user_data)
  505. {
  506. gtk_stack_set_visible_child_name(GTK_STACK(main_stack), "main");
  507. }
  508. void
  509. on_control_auto_toggled(GtkToggleButton *widget, gpointer user_data)
  510. {
  511. bool is_manual = gtk_toggle_button_get_active(widget) ? false : true;
  512. bool has_changed;
  513. switch (current_control) {
  514. case USER_CONTROL_ISO:
  515. if (gain_is_manual != is_manual) {
  516. gain_is_manual = is_manual;
  517. has_changed = true;
  518. }
  519. break;
  520. case USER_CONTROL_SHUTTER:
  521. if (exposure_is_manual != is_manual) {
  522. exposure_is_manual = is_manual;
  523. has_changed = true;
  524. }
  525. break;
  526. }
  527. if (has_changed) {
  528. // The slider might have been moved while Auto mode is active. When entering
  529. // Manual mode, first read the slider value to sync with those changes.
  530. double value = gtk_adjustment_get_value(control_slider);
  531. switch (current_control) {
  532. case USER_CONTROL_ISO:
  533. if (value != gain) {
  534. gain = (int)value;
  535. }
  536. break;
  537. case USER_CONTROL_SHUTTER: {
  538. // So far all sensors use exposure time in number of sensor rows
  539. int new_exposure =
  540. (int)(value / 360.0 * camera->capture_mode.height);
  541. if (new_exposure != exposure) {
  542. exposure = new_exposure;
  543. }
  544. break;
  545. }
  546. }
  547. update_io_pipeline();
  548. draw_controls();
  549. }
  550. }
  551. void
  552. on_control_slider_changed(GtkAdjustment *widget, gpointer user_data)
  553. {
  554. double value = gtk_adjustment_get_value(widget);
  555. bool has_changed = false;
  556. switch (current_control) {
  557. case USER_CONTROL_ISO:
  558. if (value != gain) {
  559. gain = (int)value;
  560. has_changed = true;
  561. }
  562. break;
  563. case USER_CONTROL_SHUTTER: {
  564. // So far all sensors use exposure time in number of sensor rows
  565. int new_exposure =
  566. (int)(value / 360.0 * camera->capture_mode.height);
  567. if (new_exposure != exposure) {
  568. exposure = new_exposure;
  569. has_changed = true;
  570. }
  571. break;
  572. }
  573. }
  574. if (has_changed) {
  575. update_io_pipeline();
  576. draw_controls();
  577. }
  578. }
  579. static void
  580. on_realize(GtkWidget *window, gpointer *data)
  581. {
  582. mp_process_pipeline_init_gl(gtk_widget_get_window(window));
  583. }
  584. int
  585. main(int argc, char *argv[])
  586. {
  587. if (!mp_load_config())
  588. return 1;
  589. setenv("LC_NUMERIC", "C", 1);
  590. gtk_init(&argc, &argv);
  591. g_object_set(gtk_settings_get_default(), "gtk-application-prefer-dark-theme",
  592. TRUE, NULL);
  593. GtkBuilder *builder = gtk_builder_new_from_resource(
  594. "/org/postmarketos/Megapixels/camera.glade");
  595. GtkWidget *window = GTK_WIDGET(gtk_builder_get_object(builder, "window"));
  596. GtkWidget *shutter = GTK_WIDGET(gtk_builder_get_object(builder, "shutter"));
  597. GtkWidget *switch_btn =
  598. GTK_WIDGET(gtk_builder_get_object(builder, "switch_camera"));
  599. GtkWidget *settings_btn =
  600. GTK_WIDGET(gtk_builder_get_object(builder, "settings"));
  601. GtkWidget *settings_back =
  602. GTK_WIDGET(gtk_builder_get_object(builder, "settings_back"));
  603. GtkWidget *error_close =
  604. GTK_WIDGET(gtk_builder_get_object(builder, "error_close"));
  605. GtkWidget *open_last =
  606. GTK_WIDGET(gtk_builder_get_object(builder, "open_last"));
  607. GtkWidget *open_directory =
  608. GTK_WIDGET(gtk_builder_get_object(builder, "open_directory"));
  609. preview = GTK_WIDGET(gtk_builder_get_object(builder, "preview"));
  610. error_box = GTK_WIDGET(gtk_builder_get_object(builder, "error_box"));
  611. error_message = GTK_WIDGET(gtk_builder_get_object(builder, "error_message"));
  612. main_stack = GTK_WIDGET(gtk_builder_get_object(builder, "main_stack"));
  613. open_last_stack = GTK_WIDGET(gtk_builder_get_object(builder, "open_last_stack"));
  614. thumb_last = GTK_WIDGET(gtk_builder_get_object(builder, "thumb_last"));
  615. process_spinner = GTK_WIDGET(gtk_builder_get_object(builder, "process_spinner"));
  616. control_box = GTK_WIDGET(gtk_builder_get_object(builder, "control_box"));
  617. control_name = GTK_WIDGET(gtk_builder_get_object(builder, "control_name"));
  618. control_slider =
  619. GTK_ADJUSTMENT(gtk_builder_get_object(builder, "control_adj"));
  620. control_auto = GTK_WIDGET(gtk_builder_get_object(builder, "control_auto"));
  621. g_signal_connect(window, "realize", G_CALLBACK(on_realize), NULL);
  622. g_signal_connect(window, "destroy", G_CALLBACK(gtk_main_quit), NULL);
  623. g_signal_connect(shutter, "clicked", G_CALLBACK(on_shutter_clicked), NULL);
  624. g_signal_connect(error_close, "clicked", G_CALLBACK(on_error_close_clicked),
  625. NULL);
  626. g_signal_connect(switch_btn, "clicked", G_CALLBACK(on_camera_switch_clicked),
  627. NULL);
  628. g_signal_connect(settings_btn, "clicked",
  629. G_CALLBACK(on_settings_btn_clicked), NULL);
  630. g_signal_connect(settings_back, "clicked", G_CALLBACK(on_back_clicked),
  631. NULL);
  632. g_signal_connect(open_last, "clicked", G_CALLBACK(on_open_last_clicked),
  633. NULL);
  634. g_signal_connect(open_directory, "clicked",
  635. G_CALLBACK(on_open_directory_clicked), NULL);
  636. g_signal_connect(preview, "draw", G_CALLBACK(preview_draw), NULL);
  637. g_signal_connect(preview, "configure-event", G_CALLBACK(preview_configure),
  638. NULL);
  639. gtk_widget_set_events(preview, gtk_widget_get_events(preview) |
  640. GDK_BUTTON_PRESS_MASK |
  641. GDK_POINTER_MOTION_MASK);
  642. g_signal_connect(preview, "button-press-event", G_CALLBACK(on_preview_tap),
  643. NULL);
  644. g_signal_connect(control_auto, "toggled",
  645. G_CALLBACK(on_control_auto_toggled), NULL);
  646. g_signal_connect(control_slider, "value-changed",
  647. G_CALLBACK(on_control_slider_changed), NULL);
  648. GtkCssProvider *provider = gtk_css_provider_new();
  649. if (access("camera.css", F_OK) != -1) {
  650. gtk_css_provider_load_from_path(provider, "camera.css", NULL);
  651. } else {
  652. gtk_css_provider_load_from_resource(
  653. provider, "/org/postmarketos/Megapixels/camera.css");
  654. }
  655. GtkStyleContext *context = gtk_widget_get_style_context(error_box);
  656. gtk_style_context_add_provider(context, GTK_STYLE_PROVIDER(provider),
  657. GTK_STYLE_PROVIDER_PRIORITY_USER);
  658. context = gtk_widget_get_style_context(control_box);
  659. gtk_style_context_add_provider(context, GTK_STYLE_PROVIDER(provider),
  660. GTK_STYLE_PROVIDER_PRIORITY_USER);
  661. GClosure* capture_shortcut = g_cclosure_new(on_capture_shortcut, 0, 0);
  662. GtkAccelGroup* accel_group = gtk_accel_group_new();
  663. gtk_accel_group_connect(accel_group,
  664. GDK_KEY_space,
  665. 0,
  666. 0,
  667. capture_shortcut);
  668. gtk_window_add_accel_group(GTK_WINDOW(window), accel_group);
  669. mp_io_pipeline_start();
  670. camera = mp_get_camera_config(0);
  671. update_io_pipeline();
  672. gtk_widget_show(window);
  673. gtk_main();
  674. mp_io_pipeline_stop();
  675. return 0;
  676. }