main.c 26 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958
  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. // start_capturing(int fd)
  148. // {
  149. // for (int i = 0; i < n_buffers; ++i) {
  150. // struct v4l2_buffer buf = {
  151. // .type = current.type,
  152. // .memory = V4L2_MEMORY_MMAP,
  153. // .index = i,
  154. // };
  155. // if(current.type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) {
  156. // buf.m.planes = buf_planes;
  157. // buf.length = 1;
  158. // }
  159. // if (xioctl(fd, VIDIOC_QBUF, &buf) == -1) {
  160. // errno_exit("VIDIOC_QBUF");
  161. // }
  162. // }
  163. // if (xioctl(fd, VIDIOC_STREAMON, &current.type) == -1) {
  164. // errno_exit("VIDIOC_STREAMON");
  165. // }
  166. // ready = 1;
  167. // }
  168. // static void
  169. // stop_capturing(int fd)
  170. // {
  171. // int i;
  172. // ready = 0;
  173. // printf("Stopping capture\n");
  174. // if (xioctl(fd, VIDIOC_STREAMOFF, &current.type) == -1) {
  175. // errno_exit("VIDIOC_STREAMOFF");
  176. // }
  177. // for (i = 0; i < n_buffers; ++i) {
  178. // munmap(buffers[i].start, buffers[i].length);
  179. // }
  180. // }
  181. // static void
  182. // init_mmap(int fd)
  183. // {
  184. // struct v4l2_requestbuffers req = {
  185. // .count = 4,
  186. // .type = current.type,
  187. // .memory = V4L2_MEMORY_MMAP,
  188. // };
  189. // if (xioctl(fd, VIDIOC_REQBUFS, &req) == -1) {
  190. // if (errno == EINVAL) {
  191. // fprintf(stderr, "%s does not support memory mapping",
  192. // current.dev_name);
  193. // exit(EXIT_FAILURE);
  194. // } else {
  195. // errno_exit("VIDIOC_REQBUFS");
  196. // }
  197. // }
  198. // if (req.count < 2) {
  199. // fprintf(stderr, "Insufficient buffer memory on %s\n",
  200. // current.dev_name);
  201. // exit(EXIT_FAILURE);
  202. // }
  203. // buffers = calloc(req.count, sizeof(buffers[0]));
  204. // if (!buffers) {
  205. // fprintf(stderr, "Out of memory\\n");
  206. // exit(EXIT_FAILURE);
  207. // }
  208. // for (n_buffers = 0; n_buffers < req.count; ++n_buffers) {
  209. // struct v4l2_buffer buf = {
  210. // .type = current.type,
  211. // .memory = V4L2_MEMORY_MMAP,
  212. // .index = n_buffers,
  213. // };
  214. // if (current.type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) {
  215. // buf.m.planes = buf_planes;
  216. // buf.length = 1;
  217. // }
  218. // if (xioctl(fd, VIDIOC_QUERYBUF, &buf) == -1) {
  219. // errno_exit("VIDIOC_QUERYBUF");
  220. // }
  221. // if (current.type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) {
  222. // buffers[n_buffers].length = buf.m.planes[0].length;
  223. // buffers[n_buffers].start = mmap(NULL /* start anywhere */,
  224. // buf.m.planes[0].length,
  225. // PROT_READ | PROT_WRITE /* required */,
  226. // MAP_SHARED /* recommended */,
  227. // fd, buf.m.planes[0].m.mem_offset);
  228. // } else {
  229. // buffers[n_buffers].length = buf.length;
  230. // buffers[n_buffers].start = mmap(NULL /* start anywhere */,
  231. // buf.length,
  232. // PROT_READ | PROT_WRITE /* required */,
  233. // MAP_SHARED /* recommended */,
  234. // fd, buf.m.offset);
  235. // }
  236. // if (MAP_FAILED == buffers[n_buffers].start) {
  237. // errno_exit("mmap");
  238. // }
  239. // }
  240. // }
  241. static void
  242. draw_controls()
  243. {
  244. cairo_t *cr;
  245. char iso[6];
  246. int temp;
  247. char shutterangle[6];
  248. if (exposure_is_manual) {
  249. temp = (int)((float)exposure / (float)camera->capture_mode.height * 360);
  250. sprintf(shutterangle, "%d\u00b0", temp);
  251. } else {
  252. sprintf(shutterangle, "auto");
  253. }
  254. if (gain_is_manual) {
  255. temp = remap(gain - 1, 0, gain_max, camera->iso_min, camera->iso_max);
  256. sprintf(iso, "%d", temp);
  257. } else {
  258. sprintf(iso, "auto");
  259. }
  260. if (status_surface)
  261. cairo_surface_destroy(status_surface);
  262. // Make a service to show status of controls, 32px high
  263. if (gtk_widget_get_window(preview) == NULL) {
  264. return;
  265. }
  266. status_surface = gdk_window_create_similar_surface(gtk_widget_get_window(preview),
  267. CAIRO_CONTENT_COLOR_ALPHA,
  268. preview_width, 32);
  269. cr = cairo_create(status_surface);
  270. cairo_set_source_rgba(cr, 0, 0, 0, 0.0);
  271. cairo_paint(cr);
  272. // Draw the outlines for the headings
  273. cairo_select_font_face(cr, "sans-serif", CAIRO_FONT_SLANT_NORMAL, CAIRO_FONT_WEIGHT_BOLD);
  274. cairo_set_font_size(cr, 9);
  275. cairo_set_source_rgba(cr, 0, 0, 0, 1);
  276. cairo_move_to(cr, 16, 16);
  277. cairo_text_path(cr, "ISO");
  278. cairo_stroke(cr);
  279. cairo_move_to(cr, 60, 16);
  280. cairo_text_path(cr, "Shutter");
  281. cairo_stroke(cr);
  282. // Draw the fill for the headings
  283. cairo_set_source_rgba(cr, 1, 1, 1, 1);
  284. cairo_move_to(cr, 16, 16);
  285. cairo_show_text(cr, "ISO");
  286. cairo_move_to(cr, 60, 16);
  287. cairo_show_text(cr, "Shutter");
  288. // Draw the outlines for the values
  289. cairo_select_font_face(cr, "sans-serif", CAIRO_FONT_SLANT_NORMAL, CAIRO_FONT_WEIGHT_NORMAL);
  290. cairo_set_font_size(cr, 11);
  291. cairo_set_source_rgba(cr, 0, 0, 0, 1);
  292. cairo_move_to(cr, 16, 26);
  293. cairo_text_path(cr, iso);
  294. cairo_stroke(cr);
  295. cairo_move_to(cr, 60, 26);
  296. cairo_text_path(cr, shutterangle);
  297. cairo_stroke(cr);
  298. // Draw the fill for the values
  299. cairo_set_source_rgba(cr, 1, 1, 1, 1);
  300. cairo_move_to(cr, 16, 26);
  301. cairo_show_text(cr, iso);
  302. cairo_move_to(cr, 60, 26);
  303. cairo_show_text(cr, shutterangle);
  304. cairo_destroy(cr);
  305. gtk_widget_queue_draw_area(preview, 0, 0, preview_width, 32);
  306. }
  307. // static void
  308. // init_sensor(char *fn, int width, int height, int mbus, int rate)
  309. // {
  310. // int fd;
  311. // struct v4l2_subdev_frame_interval interval = {};
  312. // struct v4l2_subdev_format fmt = {};
  313. // fd = open(fn, O_RDWR);
  314. // g_print("Setting sensor rate to %d\n", rate);
  315. // interval.pad = 0;
  316. // interval.interval.numerator = 1;
  317. // interval.interval.denominator = rate;
  318. // if (xioctl(fd, VIDIOC_SUBDEV_S_FRAME_INTERVAL, &interval) == -1) {
  319. // errno_exit("VIDIOC_SUBDEV_S_FRAME_INTERVAL");
  320. // }
  321. // if (interval.interval.numerator != 1 || interval.interval.denominator != rate)
  322. // g_printerr("Driver chose %d/%d instead\n",
  323. // interval.interval.numerator, interval.interval.denominator);
  324. // g_print("Setting sensor to %dx%d fmt %d\n",
  325. // width, height, mbus);
  326. // fmt.pad = 0;
  327. // fmt.which = V4L2_SUBDEV_FORMAT_ACTIVE;
  328. // fmt.format.code = mbus;
  329. // fmt.format.width = width;
  330. // fmt.format.height = height;
  331. // fmt.format.field = V4L2_FIELD_ANY;
  332. // if (xioctl(fd, VIDIOC_SUBDEV_S_FMT, &fmt) == -1) {
  333. // errno_exit("VIDIOC_SUBDEV_S_FMT");
  334. // }
  335. // if (fmt.format.width != width || fmt.format.height != height || fmt.format.code != mbus)
  336. // g_printerr("Driver chose %dx%d fmt %d instead\n",
  337. // fmt.format.width, fmt.format.height,
  338. // fmt.format.code);
  339. // // Trigger continuous auto focus if the sensor supports it
  340. // if (v4l2_has_control(fd, V4L2_CID_FOCUS_AUTO)) {
  341. // current.has_af_c = 1;
  342. // v4l2_ctrl_set(fd, V4L2_CID_FOCUS_AUTO, 1);
  343. // }
  344. // if (v4l2_has_control(fd, V4L2_CID_AUTO_FOCUS_START)) {
  345. // current.has_af_s = 1;
  346. // }
  347. // if (v4l2_has_control(fd, V4L2_CID_GAIN)) {
  348. // current.gain_ctrl = V4L2_CID_GAIN;
  349. // current.gain_max = v4l2_ctrl_get_max(fd, V4L2_CID_GAIN);
  350. // }
  351. // if (v4l2_has_control(fd, V4L2_CID_ANALOGUE_GAIN)) {
  352. // current.gain_ctrl = V4L2_CID_ANALOGUE_GAIN;
  353. // current.gain_max = v4l2_ctrl_get_max(fd, V4L2_CID_ANALOGUE_GAIN);
  354. // }
  355. // auto_exposure = 1;
  356. // auto_gain = 1;
  357. // draw_controls();
  358. // close(current.fd);
  359. // current.fd = fd;
  360. // }
  361. // static void
  362. // init_media_entity(char *fn, int width, int height, int mbus)
  363. // {
  364. // int fd;
  365. // struct v4l2_subdev_format fmt = {};
  366. // fd = open(fn, O_RDWR);
  367. // // Apply mode to v4l2 subdev
  368. // g_print("Setting node to %dx%d fmt %d\n",
  369. // width, height, mbus);
  370. // fmt.pad = 0;
  371. // fmt.which = V4L2_SUBDEV_FORMAT_ACTIVE;
  372. // fmt.format.code = mbus;
  373. // fmt.format.width = width;
  374. // fmt.format.height = height;
  375. // fmt.format.field = V4L2_FIELD_ANY;
  376. // if (xioctl(fd, VIDIOC_SUBDEV_S_FMT, &fmt) == -1) {
  377. // errno_exit("VIDIOC_SUBDEV_S_FMT");
  378. // }
  379. // if (fmt.format.width != width || fmt.format.height != height || fmt.format.code != mbus)
  380. // g_printerr("Driver chose %dx%d fmt %d instead\n",
  381. // fmt.format.width, fmt.format.height,
  382. // fmt.format.code);
  383. // }
  384. // static int
  385. // init_device(int fd)
  386. // {
  387. // struct v4l2_capability cap;
  388. // if (xioctl(fd, VIDIOC_QUERYCAP, &cap) == -1) {
  389. // if (errno == EINVAL) {
  390. // fprintf(stderr, "%s is no V4L2 device\n",
  391. // current.dev_name);
  392. // exit(EXIT_FAILURE);
  393. // } else {
  394. // errno_exit("VIDIOC_QUERYCAP");
  395. // }
  396. // }
  397. // // Detect buffer format for the interface node, preferring normal video capture
  398. // if (cap.capabilities & V4L2_CAP_VIDEO_CAPTURE) {
  399. // current.type = V4L2_BUF_TYPE_VIDEO_CAPTURE;
  400. // } else if (cap.capabilities & V4L2_CAP_VIDEO_CAPTURE_MPLANE) {
  401. // printf("[%s] Using the MPLANE buffer format\n", current.cfg_name);
  402. // current.type = V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE;
  403. // } else {
  404. // fprintf(stderr, "%s is no video capture device\n",
  405. // current.dev_name);
  406. // exit(EXIT_FAILURE);
  407. // }
  408. // if (!(cap.capabilities & V4L2_CAP_STREAMING)) {
  409. // fprintf(stderr, "%s does not support streaming i/o\n",
  410. // current.dev_name);
  411. // exit(EXIT_FAILURE);
  412. // }
  413. // /* Select video input, video standard and tune here. */
  414. // struct v4l2_cropcap cropcap = {
  415. // .type = current.type,
  416. // };
  417. // struct v4l2_crop crop = {0};
  418. // if (xioctl(fd, VIDIOC_CROPCAP, &cropcap) == 0) {
  419. // crop.type = current.type;
  420. // crop.c = cropcap.defrect; /* reset to default */
  421. // if (xioctl(fd, VIDIOC_S_CROP, &crop) == -1) {
  422. // switch (errno) {
  423. // case EINVAL:
  424. // /* Cropping not supported. */
  425. // break;
  426. // default:
  427. // /* Errors ignored. */
  428. // break;
  429. // }
  430. // }
  431. // } else {
  432. // /* Errors ignored. */
  433. // }
  434. // // Request a video format
  435. // struct v4l2_format fmt = {
  436. // .type = current.type,
  437. // };
  438. // if (current.width > 0) {
  439. // g_print("Setting camera to %dx%d fmt %d\n",
  440. // current.width, current.height, current.fmt);
  441. // if (current.type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) {
  442. // fmt.fmt.pix_mp.width = current.width;
  443. // fmt.fmt.pix_mp.height = current.height;
  444. // fmt.fmt.pix_mp.pixelformat = current.fmt;
  445. // fmt.fmt.pix_mp.field = V4L2_FIELD_ANY;
  446. // } else {
  447. // fmt.fmt.pix.width = current.width;
  448. // fmt.fmt.pix.height = current.height;
  449. // fmt.fmt.pix.pixelformat = current.fmt;
  450. // fmt.fmt.pix.field = V4L2_FIELD_ANY;
  451. // }
  452. // if (xioctl(fd, VIDIOC_S_FMT, &fmt) == -1) {
  453. // g_printerr("VIDIOC_S_FMT failed");
  454. // show_error("Could not set camera mode");
  455. // return -1;
  456. // }
  457. // if (current.type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE
  458. // && (fmt.fmt.pix_mp.width != current.width ||
  459. // fmt.fmt.pix_mp.height != current.height ||
  460. // fmt.fmt.pix_mp.pixelformat != current.fmt))
  461. // g_printerr("Driver returned %dx%d fmt %d\n",
  462. // fmt.fmt.pix_mp.width, fmt.fmt.pix_mp.height,
  463. // fmt.fmt.pix_mp.pixelformat);
  464. // if (current.type == V4L2_BUF_TYPE_VIDEO_CAPTURE
  465. // && (fmt.fmt.pix.width != current.width ||
  466. // fmt.fmt.pix.height != current.height ||
  467. // fmt.fmt.pix.pixelformat != current.fmt))
  468. // g_printerr("Driver returned %dx%d fmt %d\n",
  469. // fmt.fmt.pix.width, fmt.fmt.pix.height,
  470. // fmt.fmt.pix.pixelformat);
  471. // /* Note VIDIOC_S_FMT may change width and height. */
  472. // } else {
  473. // if (xioctl(fd, VIDIOC_G_FMT, &fmt) == -1) {
  474. // errno_exit("VIDIOC_G_FMT");
  475. // }
  476. // if (current.type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) {
  477. // g_print("Got %dx%d fmt %d from the driver\n",
  478. // fmt.fmt.pix_mp.width, fmt.fmt.pix_mp.height,
  479. // fmt.fmt.pix_mp.pixelformat);
  480. // current.width = fmt.fmt.pix.width;
  481. // current.height = fmt.fmt.pix.height;
  482. // } else {
  483. // g_print("Got %dx%d fmt %d from the driver\n",
  484. // fmt.fmt.pix.width, fmt.fmt.pix.height,
  485. // fmt.fmt.pix.pixelformat);
  486. // current.width = fmt.fmt.pix_mp.width;
  487. // current.height = fmt.fmt.pix_mp.height;
  488. // }
  489. // }
  490. // if (current.type == V4L2_BUF_TYPE_VIDEO_CAPTURE_MPLANE) {
  491. // current.fmt = fmt.fmt.pix_mp.pixelformat;
  492. // } else {
  493. // current.fmt = fmt.fmt.pix.pixelformat;
  494. // }
  495. // init_mmap(fd);
  496. // return 0;
  497. // }
  498. static gboolean
  499. preview_draw(GtkWidget *widget, cairo_t *cr, gpointer data)
  500. {
  501. if (!camera_is_initialized) {
  502. return FALSE;
  503. }
  504. if (surface) {
  505. cairo_save(cr);
  506. cairo_translate(cr, preview_width / 2, preview_height / 2);
  507. int width = cairo_image_surface_get_width(surface);
  508. int height = cairo_image_surface_get_height(surface);
  509. double scale = MIN(preview_width / (double) width, preview_height / (double) height);
  510. cairo_scale(cr, scale, scale);
  511. cairo_translate(cr, -width / 2, -height / 2);
  512. cairo_set_source_surface(cr, surface, 0, 0);
  513. cairo_paint(cr);
  514. cairo_restore(cr);
  515. }
  516. cairo_set_source_surface(cr, status_surface, 0, 0);
  517. cairo_paint(cr);
  518. return FALSE;
  519. }
  520. static gboolean
  521. preview_configure(GtkWidget *widget, GdkEventConfigure *event)
  522. {
  523. int new_preview_width = gtk_widget_get_allocated_width(widget);
  524. int new_preview_height = gtk_widget_get_allocated_height(widget);
  525. if (preview_width != new_preview_width || preview_height != new_preview_height)
  526. {
  527. preview_width = new_preview_width;
  528. preview_height = new_preview_height;
  529. update_io_pipeline();
  530. }
  531. draw_controls();
  532. return TRUE;
  533. }
  534. // int
  535. // setup_camera(int cid)
  536. // {
  537. // struct media_link_desc link = {0};
  538. // // Kill existing links for cameras in the same graph
  539. // for(int i=0; i<NUM_CAMERAS; i++) {
  540. // if(!cameras[i].exists)
  541. // continue;
  542. // if(i == cid)
  543. // continue;
  544. // if(strcmp(cameras[i].media_dev_fname, cameras[cid].media_dev_fname) != 0)
  545. // continue;
  546. // // Disable the interface<->front link
  547. // link.flags = 0;
  548. // link.source.entity = cameras[i].entity_id;
  549. // link.source.index = 0;
  550. // link.sink.entity = cameras[i].interface_entity_id;
  551. // link.sink.index = 0;
  552. // if (xioctl(cameras[cid].media_fd, MEDIA_IOC_SETUP_LINK, &link) < 0) {
  553. // g_printerr("Could not disable [%s] camera link\n", cameras[i].cfg_name);
  554. // return -1;
  555. // }
  556. // }
  557. // // Enable the interface<->sensor link
  558. // link.flags = MEDIA_LNK_FL_ENABLED;
  559. // link.source.entity = cameras[cid].entity_id;
  560. // link.source.index = 0;
  561. // link.sink.entity = cameras[cid].interface_entity_id;
  562. // link.sink.index = 0;
  563. // current = cameras[cid];
  564. // if (xioctl(cameras[cid].media_fd, MEDIA_IOC_SETUP_LINK, &link) < 0) {
  565. // g_printerr("[%s] Could not enable direct sensor->if link\n", cameras[cid].cfg_name);
  566. // for(int i=0;i<NUM_LINKS; i++) {
  567. // if (!cameras[cid].media_links[i].valid)
  568. // continue;
  569. // if (cameras[cid].media_links[i].source_entity_id < 1) {
  570. // g_printerr("[%s] media entry [%s] not found\n",
  571. // cameras[cid].cfg_name,
  572. // cameras[cid].media_links[i].source_name);
  573. // }
  574. // if (cameras[cid].media_links[i].target_entity_id < 1) {
  575. // g_printerr("[%s] media entry [%s] not found\n",
  576. // cameras[cid].cfg_name,
  577. // cameras[cid].media_links[i].target_name);
  578. // }
  579. // link.flags = MEDIA_LNK_FL_ENABLED;
  580. // link.source.entity = cameras[cid].media_links[i].source_entity_id;
  581. // link.source.index = cameras[cid].media_links[i].source_port;
  582. // link.sink.entity = cameras[cid].media_links[i].target_entity_id;
  583. // link.sink.index = cameras[cid].media_links[i].target_port;
  584. // if (xioctl(cameras[cid].media_fd, MEDIA_IOC_SETUP_LINK, &link) < 0) {
  585. // g_printerr("[%s] Could not link [%s:%d] -> [%s:%d]\n",
  586. // cameras[cid].cfg_name,
  587. // cameras[cid].media_links[i].source_name,
  588. // cameras[cid].media_links[i].source_port,
  589. // cameras[cid].media_links[i].target_name,
  590. // cameras[cid].media_links[i].target_port);
  591. // }
  592. // init_media_entity(cameras[cid].media_links[i].source_fname, current.width, current.height, current.mbus);
  593. // init_media_entity(cameras[cid].media_links[i].target_fname, current.width, current.height, current.mbus);
  594. // }
  595. // }
  596. // // Find camera node
  597. // init_sensor(current.dev_fname, current.width, current.height, current.mbus, current.rate);
  598. // return 0;
  599. // }
  600. void
  601. on_open_last_clicked(GtkWidget *widget, gpointer user_data)
  602. {
  603. char uri[270];
  604. GError *error = NULL;
  605. if(strlen(last_path) == 0) {
  606. return;
  607. }
  608. sprintf(uri, "file://%s", last_path);
  609. if(!g_app_info_launch_default_for_uri(uri, NULL, &error)){
  610. g_printerr("Could not launch image viewer: %s\n", error->message);
  611. }
  612. }
  613. void
  614. on_open_directory_clicked(GtkWidget *widget, gpointer user_data)
  615. {
  616. char uri[270];
  617. GError *error = NULL;
  618. sprintf(uri, "file://%s/Pictures", getenv("HOME"));
  619. if(!g_app_info_launch_default_for_uri(uri, NULL, &error)){
  620. g_printerr("Could not launch image viewer: %s\n", error->message);
  621. }
  622. }
  623. void
  624. on_shutter_clicked(GtkWidget *widget, gpointer user_data)
  625. {
  626. mp_io_pipeline_capture();
  627. }
  628. void
  629. on_preview_tap(GtkWidget *widget, GdkEventButton *event, gpointer user_data)
  630. {
  631. if (event->type != GDK_BUTTON_PRESS)
  632. return;
  633. // Handle taps on the controls
  634. if (event->y < 32) {
  635. if (gtk_widget_is_visible(control_box)) {
  636. gtk_widget_hide(control_box);
  637. return;
  638. } else {
  639. gtk_widget_show(control_box);
  640. }
  641. if (event->x < 60 ) {
  642. // ISO
  643. current_control = USER_CONTROL_ISO;
  644. gtk_label_set_text(GTK_LABEL(control_name), "ISO");
  645. gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(control_auto), !gain_is_manual);
  646. gtk_adjustment_set_lower(control_slider, 0.0);
  647. gtk_adjustment_set_upper(control_slider, (float)gain_max);
  648. gtk_adjustment_set_value(control_slider, (double)gain);
  649. } else if (event->x > 60 && event->x < 120) {
  650. // Shutter angle
  651. current_control = USER_CONTROL_SHUTTER;
  652. gtk_label_set_text(GTK_LABEL(control_name), "Shutter");
  653. gtk_toggle_button_set_active(GTK_TOGGLE_BUTTON(control_auto), !exposure_is_manual);
  654. gtk_adjustment_set_lower(control_slider, 1.0);
  655. gtk_adjustment_set_upper(control_slider, 360.0);
  656. gtk_adjustment_set_value(control_slider, (double)exposure);
  657. }
  658. return;
  659. }
  660. // Tapped preview image itself, try focussing
  661. if (has_auto_focus_start) {
  662. mp_io_pipeline_focus();
  663. }
  664. }
  665. void
  666. on_error_close_clicked(GtkWidget *widget, gpointer user_data)
  667. {
  668. gtk_widget_hide(error_box);
  669. }
  670. void
  671. on_camera_switch_clicked(GtkWidget *widget, gpointer user_data)
  672. {
  673. size_t next_index = camera->index + 1;
  674. const struct mp_camera_config *next_camera = mp_get_camera_config(next_index);
  675. if (!next_camera) {
  676. next_index = 0;
  677. next_camera = mp_get_camera_config(next_index);
  678. }
  679. camera = next_camera;
  680. update_io_pipeline();
  681. }
  682. void
  683. on_settings_btn_clicked(GtkWidget *widget, gpointer user_data)
  684. {
  685. gtk_stack_set_visible_child_name(GTK_STACK(main_stack), "settings");
  686. }
  687. void
  688. on_back_clicked(GtkWidget *widget, gpointer user_data)
  689. {
  690. gtk_stack_set_visible_child_name(GTK_STACK(main_stack), "main");
  691. }
  692. void
  693. on_control_auto_toggled(GtkToggleButton *widget, gpointer user_data)
  694. {
  695. bool is_manual = gtk_toggle_button_get_active(widget) ? false : true;
  696. bool has_changed;
  697. switch (current_control) {
  698. case USER_CONTROL_ISO:
  699. if (gain_is_manual != is_manual) {
  700. gain_is_manual = is_manual;
  701. has_changed = true;
  702. }
  703. break;
  704. case USER_CONTROL_SHUTTER:
  705. if (exposure_is_manual != is_manual) {
  706. exposure_is_manual = is_manual;
  707. has_changed = true;
  708. }
  709. break;
  710. }
  711. if (has_changed) {
  712. update_io_pipeline();
  713. draw_controls();
  714. }
  715. }
  716. void
  717. on_control_slider_changed(GtkAdjustment *widget, gpointer user_data)
  718. {
  719. double value = gtk_adjustment_get_value(widget);
  720. bool has_changed = false;
  721. switch (current_control) {
  722. case USER_CONTROL_ISO:
  723. if (value != gain) {
  724. gain = (int)value;
  725. has_changed = true;
  726. }
  727. break;
  728. case USER_CONTROL_SHUTTER:
  729. {
  730. // So far all sensors use exposure time in number of sensor rows
  731. int new_exposure = (int)(value / 360.0 * camera->capture_mode.height);
  732. if (new_exposure != exposure) {
  733. exposure = new_exposure;
  734. has_changed = true;
  735. }
  736. break;
  737. }
  738. }
  739. if (has_changed) {
  740. update_io_pipeline();
  741. draw_controls();
  742. }
  743. }
  744. int
  745. main(int argc, char *argv[])
  746. {
  747. if (!mp_load_config())
  748. return 1;
  749. setenv("LC_NUMERIC", "C", 1);
  750. gtk_init(&argc, &argv);
  751. g_object_set(gtk_settings_get_default(), "gtk-application-prefer-dark-theme", TRUE, NULL);
  752. GtkBuilder *builder = gtk_builder_new_from_resource("/org/postmarketos/Megapixels/camera.glade");
  753. GtkWidget *window = GTK_WIDGET(gtk_builder_get_object(builder, "window"));
  754. GtkWidget *shutter = GTK_WIDGET(gtk_builder_get_object(builder, "shutter"));
  755. GtkWidget *switch_btn = GTK_WIDGET(gtk_builder_get_object(builder, "switch_camera"));
  756. GtkWidget *settings_btn = GTK_WIDGET(gtk_builder_get_object(builder, "settings"));
  757. GtkWidget *settings_back = GTK_WIDGET(gtk_builder_get_object(builder, "settings_back"));
  758. GtkWidget *error_close = GTK_WIDGET(gtk_builder_get_object(builder, "error_close"));
  759. GtkWidget *open_last = GTK_WIDGET(gtk_builder_get_object(builder, "open_last"));
  760. GtkWidget *open_directory = GTK_WIDGET(gtk_builder_get_object(builder, "open_directory"));
  761. preview = GTK_WIDGET(gtk_builder_get_object(builder, "preview"));
  762. error_box = GTK_WIDGET(gtk_builder_get_object(builder, "error_box"));
  763. error_message = GTK_WIDGET(gtk_builder_get_object(builder, "error_message"));
  764. main_stack = GTK_WIDGET(gtk_builder_get_object(builder, "main_stack"));
  765. thumb_last = GTK_WIDGET(gtk_builder_get_object(builder, "thumb_last"));
  766. control_box = GTK_WIDGET(gtk_builder_get_object(builder, "control_box"));
  767. control_name = GTK_WIDGET(gtk_builder_get_object(builder, "control_name"));
  768. control_slider = GTK_ADJUSTMENT(gtk_builder_get_object(builder, "control_adj"));
  769. control_auto = GTK_WIDGET(gtk_builder_get_object(builder, "control_auto"));
  770. g_signal_connect(window, "destroy", G_CALLBACK(gtk_main_quit), NULL);
  771. g_signal_connect(shutter, "clicked", G_CALLBACK(on_shutter_clicked), NULL);
  772. g_signal_connect(error_close, "clicked", G_CALLBACK(on_error_close_clicked), NULL);
  773. g_signal_connect(switch_btn, "clicked", G_CALLBACK(on_camera_switch_clicked), NULL);
  774. g_signal_connect(settings_btn, "clicked", G_CALLBACK(on_settings_btn_clicked), NULL);
  775. g_signal_connect(settings_back, "clicked", G_CALLBACK(on_back_clicked), NULL);
  776. g_signal_connect(open_last, "clicked", G_CALLBACK(on_open_last_clicked), NULL);
  777. g_signal_connect(open_directory, "clicked", G_CALLBACK(on_open_directory_clicked), NULL);
  778. g_signal_connect(preview, "draw", G_CALLBACK(preview_draw), NULL);
  779. g_signal_connect(preview, "configure-event", G_CALLBACK(preview_configure), NULL);
  780. gtk_widget_set_events(preview, gtk_widget_get_events(preview) |
  781. GDK_BUTTON_PRESS_MASK | GDK_POINTER_MOTION_MASK);
  782. g_signal_connect(preview, "button-press-event", G_CALLBACK(on_preview_tap), NULL);
  783. g_signal_connect(control_auto, "toggled", G_CALLBACK(on_control_auto_toggled), NULL);
  784. g_signal_connect(control_slider, "value-changed", G_CALLBACK(on_control_slider_changed), NULL);
  785. GtkCssProvider *provider = gtk_css_provider_new();
  786. if (access("camera.css", F_OK) != -1) {
  787. gtk_css_provider_load_from_path(provider, "camera.css", NULL);
  788. } else {
  789. gtk_css_provider_load_from_resource(provider, "/org/postmarketos/Megapixels/camera.css");
  790. }
  791. GtkStyleContext *context = gtk_widget_get_style_context(error_box);
  792. gtk_style_context_add_provider(context,
  793. GTK_STYLE_PROVIDER(provider),
  794. GTK_STYLE_PROVIDER_PRIORITY_USER);
  795. context = gtk_widget_get_style_context(control_box);
  796. gtk_style_context_add_provider(context,
  797. GTK_STYLE_PROVIDER(provider),
  798. GTK_STYLE_PROVIDER_PRIORITY_USER);
  799. mp_io_pipeline_start();
  800. camera = mp_get_camera_config(0);
  801. update_io_pipeline();
  802. gtk_widget_show(window);
  803. gtk_main();
  804. mp_io_pipeline_stop();
  805. return 0;
  806. }