main.c 49 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411
  1. #include "main.h"
  2. #include "flash.h"
  3. #include "gl_util.h"
  4. #include "io_pipeline.h"
  5. #include "process_pipeline.h"
  6. #include "state.h"
  7. #include <asm/errno.h>
  8. #include <assert.h>
  9. #include <errno.h>
  10. #include <fcntl.h>
  11. #include <gtk/gtk.h>
  12. #define LIBFEEDBACK_USE_UNSTABLE_API
  13. #include <libfeedback.h>
  14. #ifdef GDK_WINDOWING_WAYLAND
  15. #include <gdk/wayland/gdkwayland.h>
  16. #include <wayland-client.h>
  17. #endif
  18. #ifdef GDK_WINDOWING_X11
  19. #include <X11/Xlib.h>
  20. #include <X11/extensions/Xrandr.h>
  21. #include <gdk/x11/gdkx.h>
  22. #endif
  23. #include <libmegapixels.h>
  24. #include <limits.h>
  25. #include <linux/kdev_t.h>
  26. #include <linux/media.h>
  27. #include <linux/v4l2-subdev.h>
  28. #include <linux/videodev2.h>
  29. #include <locale.h>
  30. #include <sys/ioctl.h>
  31. #include <sys/mman.h>
  32. #include <sys/stat.h>
  33. #include <sys/sysmacros.h>
  34. #include <time.h>
  35. #include <wordexp.h>
  36. #include <zbar.h>
  37. // #define RENDERDOC
  38. #ifdef RENDERDOC
  39. #include <dlfcn.h>
  40. #include <renderdoc/app.h>
  41. RENDERDOC_API_1_1_2 *rdoc_api = NULL;
  42. #endif
  43. mp_state_main state;
  44. static MPProcessPipelineBuffer *current_preview_buffer = NULL;
  45. static char last_path[260] = "";
  46. static MPZBarScanResult *zbar_result = NULL;
  47. // Widgets
  48. GtkWidget *window;
  49. GtkWidget *preview;
  50. GtkWidget *main_stack;
  51. GtkWidget *open_last_stack;
  52. GtkWidget *thumb_last;
  53. GtkWidget *process_spinner;
  54. GtkWidget *scanned_codes;
  55. GtkWidget *preview_top_box;
  56. GtkWidget *preview_bottom_box;
  57. GtkWidget *message_box;
  58. GtkWidget *message_label;
  59. GtkWidget *flash_button;
  60. GtkWidget *iso_button;
  61. GtkWidget *shutter_button;
  62. LfbEvent *capture_event;
  63. GSettings *settings;
  64. GSettings *fb_settings;
  65. int
  66. remap(int value, int input_min, int input_max, int output_min, int output_max)
  67. {
  68. const long long factor = 1000000000;
  69. long long output_spread = output_max - output_min;
  70. long long input_spread = input_max - input_min;
  71. long long zero_value = value - input_min;
  72. zero_value *= factor;
  73. long long percentage = zero_value / input_spread;
  74. long long zero_output = percentage * output_spread / factor;
  75. long long result = output_min + zero_output;
  76. return (int)result;
  77. }
  78. static void
  79. display_error(const char *message)
  80. {
  81. gtk_label_set_label(GTK_LABEL(message_label), message);
  82. gtk_widget_set_visible(message_box, true);
  83. }
  84. bool
  85. check_window_active()
  86. {
  87. return gtk_window_is_active(GTK_WINDOW(window));
  88. }
  89. static void
  90. update_io_pipeline()
  91. {
  92. mp_state_io new_state = {
  93. .camera = state.camera,
  94. .configuration = state.configuration,
  95. .burst_length = state.burst_length,
  96. .preview_width = state.preview_width,
  97. .preview_height = state.preview_height,
  98. .device_rotation = state.device_rotation,
  99. .gain.control = state.gain.control,
  100. .gain.auto_control = state.gain.auto_control,
  101. .gain.value = state.gain.value,
  102. .gain.value_req = state.gain.value_req,
  103. .gain.max = state.gain.max,
  104. .gain.manual = state.gain.manual,
  105. .gain.manual_req = state.gain.manual_req,
  106. .exposure.control = state.exposure.control,
  107. .exposure.auto_control = state.exposure.auto_control,
  108. .exposure.value = state.exposure.value,
  109. .exposure.value_req = state.exposure.value_req,
  110. .exposure.max = state.exposure.max,
  111. .exposure.manual = state.exposure.manual,
  112. .exposure.manual_req = state.exposure.manual_req,
  113. .focus.control = state.focus.control,
  114. .focus.auto_control = state.focus.auto_control,
  115. .focus.value = state.focus.value,
  116. .focus.value_req = state.focus.value_req,
  117. .focus.max = state.focus.max,
  118. .focus.manual = state.focus.manual,
  119. .focus.manual_req = state.focus.manual_req,
  120. .stats.exposure = state.stats.exposure,
  121. .stats.temp = state.stats.temp,
  122. .stats.tint = state.stats.tint,
  123. .stats.focus = state.stats.focus,
  124. };
  125. mp_io_pipeline_update_state(&new_state);
  126. }
  127. /*
  128. * State transfer from Process -> Main
  129. */
  130. static bool
  131. update_state(const mp_state_main *new_state)
  132. {
  133. state.gain.control = new_state->gain.control;
  134. state.gain.auto_control = new_state->gain.auto_control;
  135. state.gain.value = new_state->gain.value;
  136. state.gain.max = new_state->gain.max;
  137. state.gain.manual = new_state->gain.manual;
  138. state.exposure.control = new_state->exposure.control;
  139. state.exposure.auto_control = new_state->exposure.auto_control;
  140. state.exposure.value = new_state->exposure.value;
  141. state.exposure.max = new_state->exposure.max;
  142. state.exposure.manual = new_state->exposure.manual;
  143. state.focus.control = new_state->focus.control;
  144. state.focus.auto_control = new_state->focus.auto_control;
  145. state.focus.value = new_state->focus.value;
  146. state.focus.max = new_state->focus.max;
  147. state.focus.manual = new_state->focus.manual;
  148. state.has_auto_focus_continuous = new_state->has_auto_focus_continuous;
  149. state.has_auto_focus_start = new_state->has_auto_focus_start;
  150. state.preview_buffer_width = new_state->preview_buffer_width;
  151. state.preview_buffer_height = new_state->preview_buffer_height;
  152. state.stats.exposure = new_state->stats.exposure;
  153. state.stats.temp = new_state->stats.temp;
  154. state.stats.tint = new_state->stats.tint;
  155. state.stats.focus = new_state->stats.focus;
  156. // Make the right settings available for the camera
  157. gtk_widget_set_visible(flash_button, state.control_flash);
  158. gtk_widget_set_visible(iso_button, state.gain.control != 0);
  159. gtk_widget_set_visible(shutter_button, state.exposure.control != 0);
  160. update_io_pipeline();
  161. return false;
  162. }
  163. void
  164. mp_main_update_state(const mp_state_main *new_state)
  165. {
  166. mp_state_main *state_copy = malloc(sizeof(mp_state_main));
  167. *state_copy = *new_state;
  168. g_main_context_invoke_full(g_main_context_default(),
  169. G_PRIORITY_DEFAULT_IDLE,
  170. (GSourceFunc)update_state,
  171. state_copy,
  172. free);
  173. }
  174. static bool
  175. set_zbar_result(MPZBarScanResult *result)
  176. {
  177. if (zbar_result) {
  178. for (uint8_t i = 0; i < zbar_result->size; ++i) {
  179. free(zbar_result->codes[i].data);
  180. }
  181. free(zbar_result);
  182. }
  183. zbar_result = result;
  184. gtk_widget_queue_draw(preview);
  185. return false;
  186. }
  187. void
  188. mp_main_set_zbar_result(MPZBarScanResult *result)
  189. {
  190. g_main_context_invoke_full(g_main_context_default(),
  191. G_PRIORITY_DEFAULT_IDLE,
  192. (GSourceFunc)set_zbar_result,
  193. result,
  194. NULL);
  195. }
  196. static bool
  197. set_preview(MPProcessPipelineBuffer *buffer)
  198. {
  199. if (current_preview_buffer) {
  200. mp_process_pipeline_buffer_unref(current_preview_buffer);
  201. }
  202. current_preview_buffer = buffer;
  203. gtk_widget_queue_draw(preview);
  204. return false;
  205. }
  206. void
  207. mp_main_set_preview(MPProcessPipelineBuffer *buffer)
  208. {
  209. g_main_context_invoke_full(g_main_context_default(),
  210. G_PRIORITY_DEFAULT_IDLE,
  211. (GSourceFunc)set_preview,
  212. buffer,
  213. NULL);
  214. }
  215. struct capture_completed_args {
  216. GdkTexture *thumb;
  217. char *fname;
  218. };
  219. static bool
  220. capture_completed(struct capture_completed_args *args)
  221. {
  222. strncpy(last_path, args->fname, 259);
  223. gtk_image_set_from_paintable(GTK_IMAGE(thumb_last),
  224. GDK_PAINTABLE(args->thumb));
  225. gtk_spinner_stop(GTK_SPINNER(process_spinner));
  226. gtk_stack_set_visible_child(GTK_STACK(open_last_stack), thumb_last);
  227. g_object_unref(args->thumb);
  228. g_free(args->fname);
  229. return false;
  230. }
  231. void
  232. mp_main_capture_completed(GdkTexture *thumb, const char *fname)
  233. {
  234. struct capture_completed_args *args =
  235. malloc(sizeof(struct capture_completed_args));
  236. args->thumb = thumb;
  237. args->fname = g_strdup(fname);
  238. g_main_context_invoke_full(g_main_context_default(),
  239. G_PRIORITY_DEFAULT_IDLE,
  240. (GSourceFunc)capture_completed,
  241. args,
  242. free);
  243. }
  244. static GLuint blit_program;
  245. static GLuint blit_uniform_transform;
  246. static GLuint blit_uniform_texture;
  247. static GLuint solid_program;
  248. static GLuint solid_uniform_color;
  249. static GLuint quad;
  250. static void
  251. preview_realize(GtkGLArea *area)
  252. {
  253. gtk_gl_area_make_current(area);
  254. if (gtk_gl_area_get_error(area) != NULL) {
  255. return;
  256. }
  257. // Make a VAO for OpenGL
  258. if (!gtk_gl_area_get_use_es(area)) {
  259. GLuint vao;
  260. glGenVertexArrays(1, &vao);
  261. glBindVertexArray(vao);
  262. check_gl();
  263. }
  264. GLuint blit_shaders[] = {
  265. gl_util_load_shader("/org/postmarketos/Megapixels/blit.vert",
  266. GL_VERTEX_SHADER,
  267. NULL,
  268. 0),
  269. gl_util_load_shader("/org/postmarketos/Megapixels/blit.frag",
  270. GL_FRAGMENT_SHADER,
  271. NULL,
  272. 0),
  273. };
  274. blit_program = gl_util_link_program(blit_shaders, 2);
  275. glBindAttribLocation(blit_program, GL_UTIL_VERTEX_ATTRIBUTE, "vert");
  276. glBindAttribLocation(blit_program, GL_UTIL_TEX_COORD_ATTRIBUTE, "tex_coord");
  277. check_gl();
  278. blit_uniform_transform = glGetUniformLocation(blit_program, "transform");
  279. blit_uniform_texture = glGetUniformLocation(blit_program, "texture");
  280. GLuint solid_shaders[] = {
  281. gl_util_load_shader("/org/postmarketos/Megapixels/solid.vert",
  282. GL_VERTEX_SHADER,
  283. NULL,
  284. 0),
  285. gl_util_load_shader("/org/postmarketos/Megapixels/solid.frag",
  286. GL_FRAGMENT_SHADER,
  287. NULL,
  288. 0),
  289. };
  290. solid_program = gl_util_link_program(solid_shaders, 2);
  291. glBindAttribLocation(solid_program, GL_UTIL_VERTEX_ATTRIBUTE, "vert");
  292. check_gl();
  293. solid_uniform_color = glGetUniformLocation(solid_program, "color");
  294. quad = gl_util_new_quad();
  295. }
  296. static void
  297. position_preview(float *offset_x, float *offset_y, float *size_x, float *size_y)
  298. {
  299. int buffer_width, buffer_height;
  300. if (state.device_rotation == 0 || state.device_rotation == 180) {
  301. buffer_width = state.preview_buffer_width;
  302. buffer_height = state.preview_buffer_height;
  303. } else {
  304. buffer_width = state.preview_buffer_height;
  305. buffer_height = state.preview_buffer_width;
  306. }
  307. int scale_factor = gtk_widget_get_scale_factor(preview);
  308. int top_height = gtk_widget_get_height(preview_top_box) * scale_factor;
  309. int bottom_height = gtk_widget_get_height(preview_bottom_box) * scale_factor;
  310. int inner_height = state.preview_height - top_height - bottom_height;
  311. float scale = (float)MIN(state.preview_width / (float)buffer_width,
  312. state.preview_height / (float)buffer_height);
  313. *size_x = scale * (float)buffer_width;
  314. *size_y = scale * (float)buffer_height;
  315. *offset_x = ((float)state.preview_width - *size_x) / 2.0f;
  316. if (*size_y > (float)inner_height) {
  317. *offset_y = ((float)state.preview_height - *size_y) / 2.0f;
  318. } else {
  319. *offset_y =
  320. (float)top_height + ((float)inner_height - *size_y) / 2.0f;
  321. }
  322. }
  323. static gboolean
  324. preview_draw(GtkGLArea *area, GdkGLContext *ctx, gpointer data)
  325. {
  326. if (gtk_gl_area_get_error(area) != NULL) {
  327. return FALSE;
  328. }
  329. if (current_preview_buffer == NULL) {
  330. return FALSE;
  331. }
  332. #ifdef RENDERDOC
  333. if (rdoc_api) {
  334. rdoc_api->StartFrameCapture(NULL, NULL);
  335. }
  336. #endif
  337. glClearColor(0, 0, 0, 1);
  338. glClear(GL_COLOR_BUFFER_BIT);
  339. float offset_x, offset_y, size_x, size_y;
  340. position_preview(&offset_x, &offset_y, &size_x, &size_y);
  341. glViewport(
  342. offset_x, state.preview_height - size_y - offset_y, size_x, size_y);
  343. if (current_preview_buffer) {
  344. glUseProgram(blit_program);
  345. GLfloat rotation_list[4] = { 0, -1, 0, 1 };
  346. int rotation_index = state.device_rotation / 90;
  347. GLfloat sin_rot = rotation_list[rotation_index];
  348. GLfloat cos_rot = rotation_list[(4 + rotation_index - 1) % 4];
  349. GLfloat matrix[9] = {
  350. // clang-format off
  351. cos_rot, sin_rot, 0,
  352. -sin_rot, cos_rot, 0,
  353. 0, 0, 1,
  354. // clang-format on
  355. };
  356. glUniformMatrix3fv(blit_uniform_transform, 1, GL_FALSE, matrix);
  357. check_gl();
  358. glActiveTexture(GL_TEXTURE0);
  359. glBindTexture(GL_TEXTURE_2D,
  360. mp_process_pipeline_buffer_get_texture_id(
  361. current_preview_buffer));
  362. glUniform1i(blit_uniform_texture, 0);
  363. check_gl();
  364. gl_util_bind_quad(quad);
  365. gl_util_draw_quad(quad);
  366. }
  367. if (zbar_result) {
  368. GLuint buffer;
  369. if (!gtk_gl_area_get_use_es(area)) {
  370. glGenBuffers(1, &buffer);
  371. glBindBuffer(GL_ARRAY_BUFFER, buffer);
  372. check_gl();
  373. }
  374. glUseProgram(solid_program);
  375. glEnable(GL_BLEND);
  376. glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
  377. glUniform4f(solid_uniform_color, 1, 0, 0, 0.5);
  378. for (uint8_t i = 0; i < zbar_result->size; ++i) {
  379. MPZBarCode *code = &zbar_result->codes[i];
  380. GLfloat vertices[] = {
  381. code->bounds_x[0], code->bounds_y[0],
  382. code->bounds_x[1], code->bounds_y[1],
  383. code->bounds_x[3], code->bounds_y[3],
  384. code->bounds_x[2], code->bounds_y[2],
  385. };
  386. for (int i = 0; i < 4; ++i) {
  387. vertices[i * 2] =
  388. 2 * vertices[i * 2] /
  389. state.preview_buffer_width -
  390. 1.0;
  391. vertices[i * 2 + 1] =
  392. 1.0 - 2 * vertices[i * 2 + 1] /
  393. state.preview_buffer_height;
  394. }
  395. if (gtk_gl_area_get_use_es(area)) {
  396. glVertexAttribPointer(GL_UTIL_VERTEX_ATTRIBUTE,
  397. 2,
  398. GL_FLOAT,
  399. 0,
  400. 0,
  401. vertices);
  402. check_gl();
  403. glEnableVertexAttribArray(GL_UTIL_VERTEX_ATTRIBUTE);
  404. check_gl();
  405. } else {
  406. glBufferData(GL_ARRAY_BUFFER,
  407. sizeof(vertices),
  408. vertices,
  409. GL_STREAM_DRAW);
  410. check_gl();
  411. glVertexAttribPointer(GL_UTIL_VERTEX_ATTRIBUTE,
  412. 2,
  413. GL_FLOAT,
  414. GL_FALSE,
  415. 0,
  416. 0);
  417. glEnableVertexAttribArray(GL_UTIL_VERTEX_ATTRIBUTE);
  418. check_gl();
  419. }
  420. glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
  421. check_gl();
  422. }
  423. glDisable(GL_BLEND);
  424. glBindBuffer(GL_ARRAY_BUFFER, 0);
  425. }
  426. glFlush();
  427. #ifdef RENDERDOC
  428. if (rdoc_api) {
  429. rdoc_api->EndFrameCapture(NULL, NULL);
  430. }
  431. #endif
  432. return FALSE;
  433. }
  434. static gboolean
  435. preview_resize(GtkWidget *widget, int width, int height, gpointer data)
  436. {
  437. if (state.preview_width != width || state.preview_height != height) {
  438. state.preview_width = width;
  439. state.preview_height = height;
  440. if (state.configuration->count == 0)
  441. return TRUE;
  442. update_io_pipeline();
  443. }
  444. return TRUE;
  445. }
  446. void
  447. run_open_last_action(GSimpleAction *action, GVariant *param, gpointer user_data)
  448. {
  449. char uri[275];
  450. g_autoptr(GError) error = NULL;
  451. if (strlen(last_path) == 0) {
  452. return;
  453. }
  454. sprintf(uri, "file://%s", last_path);
  455. if (!g_app_info_launch_default_for_uri(uri, NULL, &error)) {
  456. g_printerr("Could not launch image viewer for '%s': %s\n",
  457. uri,
  458. error->message);
  459. }
  460. }
  461. void
  462. run_open_photos_action(GSimpleAction *action, GVariant *param, gpointer user_data)
  463. {
  464. char uri[270];
  465. g_autoptr(GError) error = NULL;
  466. sprintf(uri, "file://%s", g_get_user_special_dir(G_USER_DIRECTORY_PICTURES));
  467. if (!g_app_info_launch_default_for_uri(uri, NULL, &error)) {
  468. g_printerr("Could not launch image viewer: %s\n", error->message);
  469. }
  470. }
  471. void
  472. run_capture_action(GSimpleAction *action, GVariant *param, gpointer user_data)
  473. {
  474. gtk_spinner_start(GTK_SPINNER(process_spinner));
  475. gtk_stack_set_visible_child(GTK_STACK(open_last_stack), process_spinner);
  476. if (capture_event)
  477. lfb_event_trigger_feedback_async(capture_event, NULL, NULL, NULL);
  478. mp_io_pipeline_capture();
  479. }
  480. void
  481. run_about_action(GSimpleAction *action, GVariant *param, GApplication *app)
  482. {
  483. GtkWindow *parent = gtk_application_get_active_window(GTK_APPLICATION(app));
  484. gtk_show_about_dialog(parent,
  485. "program-name",
  486. "Megapixels",
  487. "title",
  488. "Megapixels",
  489. "logo-icon-name",
  490. "me.gapixels.Megapixels",
  491. "comments",
  492. "The postmarketOS camera application",
  493. "website",
  494. "https://gitlab.com/postmarketOS/megapixels",
  495. "version",
  496. VERSION,
  497. "license-type",
  498. GTK_LICENSE_GPL_3_0_ONLY,
  499. NULL);
  500. }
  501. void
  502. run_quit_action(GSimpleAction *action, GVariant *param, GApplication *app)
  503. {
  504. g_application_quit(app);
  505. }
  506. static bool
  507. check_point_inside_bounds(int x, int y, const int *bounds_x, const int *bounds_y)
  508. {
  509. bool right = false, left = false, top = false, bottom = false;
  510. for (int i = 0; i < 4; ++i) {
  511. if (x <= bounds_x[i])
  512. left = true;
  513. if (x >= bounds_x[i])
  514. right = true;
  515. if (y <= bounds_y[i])
  516. top = true;
  517. if (y >= bounds_y[i])
  518. bottom = true;
  519. }
  520. return right && left && top && bottom;
  521. }
  522. static void
  523. on_zbar_dialog_response(GtkDialog *dialog, int response, char *data)
  524. {
  525. g_autoptr(GError) error = NULL;
  526. switch (response) {
  527. case GTK_RESPONSE_YES:
  528. if (!g_app_info_launch_default_for_uri(data, NULL, &error)) {
  529. g_printerr("Could not launch application: %s\n",
  530. error->message);
  531. }
  532. case GTK_RESPONSE_ACCEPT: {
  533. GdkDisplay *display = gtk_widget_get_display(GTK_WIDGET(dialog));
  534. gdk_clipboard_set_text(gdk_display_get_clipboard(display), data);
  535. }
  536. case GTK_RESPONSE_CANCEL:
  537. break;
  538. default:
  539. g_printerr("Wrong dialog response: %d\n", response);
  540. }
  541. g_free(data);
  542. gtk_window_destroy(GTK_WINDOW(dialog));
  543. }
  544. static void
  545. on_zbar_code_tapped(GtkWidget *widget, const MPZBarCode *code)
  546. {
  547. GtkWidget *dialog;
  548. GtkDialogFlags flags = GTK_DIALOG_MODAL | GTK_DIALOG_DESTROY_WITH_PARENT;
  549. bool data_is_url =
  550. g_uri_is_valid(code->data, G_URI_FLAGS_PARSE_RELAXED, NULL);
  551. char *data = strdup(code->data);
  552. if (data_is_url) {
  553. dialog = gtk_message_dialog_new(
  554. GTK_WINDOW(gtk_widget_get_root(widget)),
  555. flags,
  556. GTK_MESSAGE_QUESTION,
  557. GTK_BUTTONS_NONE,
  558. "Found a URL '%s' encoded in a %s.",
  559. code->data,
  560. code->type);
  561. gtk_dialog_add_buttons(
  562. GTK_DIALOG(dialog), "_Open URL", GTK_RESPONSE_YES, NULL);
  563. } else {
  564. dialog = gtk_message_dialog_new(
  565. GTK_WINDOW(gtk_widget_get_root(widget)),
  566. flags,
  567. GTK_MESSAGE_QUESTION,
  568. GTK_BUTTONS_NONE,
  569. "Found data encoded in a %s.",
  570. code->type);
  571. gtk_message_dialog_format_secondary_markup(
  572. GTK_MESSAGE_DIALOG(dialog), "<small>%s</small>", code->data);
  573. }
  574. gtk_dialog_add_buttons(GTK_DIALOG(dialog),
  575. "_Copy",
  576. GTK_RESPONSE_ACCEPT,
  577. "_Cancel",
  578. GTK_RESPONSE_CANCEL,
  579. NULL);
  580. g_signal_connect(
  581. dialog, "response", G_CALLBACK(on_zbar_dialog_response), data);
  582. gtk_widget_set_visible(GTK_WIDGET(dialog), true);
  583. }
  584. static void
  585. preview_pressed(GtkGestureClick *gesture, int n_press, double x, double y)
  586. {
  587. GtkWidget *widget =
  588. gtk_event_controller_get_widget(GTK_EVENT_CONTROLLER(gesture));
  589. int scale_factor = gtk_widget_get_scale_factor(widget);
  590. // Tapped zbar result
  591. if (zbar_result) {
  592. // Transform the event coordinates to the image
  593. float offset_x, offset_y, size_x, size_y;
  594. position_preview(&offset_x, &offset_y, &size_x, &size_y);
  595. int zbar_x = (x - offset_x) * scale_factor / size_x *
  596. state.preview_buffer_width;
  597. int zbar_y = (y - offset_y) * scale_factor / size_y *
  598. state.preview_buffer_height;
  599. for (uint8_t i = 0; i < zbar_result->size; ++i) {
  600. MPZBarCode *code = &zbar_result->codes[i];
  601. if (check_point_inside_bounds(zbar_x,
  602. zbar_y,
  603. code->bounds_x,
  604. code->bounds_y)) {
  605. on_zbar_code_tapped(widget, code);
  606. return;
  607. }
  608. }
  609. }
  610. // Tapped preview image itself, try focussing
  611. if (state.has_auto_focus_start) {
  612. mp_io_pipeline_focus();
  613. }
  614. }
  615. static void
  616. run_camera_switch_action(GSimpleAction *action, GVariant *param, gpointer user_data)
  617. {
  618. int new_index = state.camera->index + 1;
  619. if (new_index > state.configuration->count - 1 || new_index < 0) {
  620. new_index = 0;
  621. }
  622. state.camera = state.configuration->cameras[new_index];
  623. // TODO: allow setting burst length in the config
  624. state.burst_length = 5;
  625. update_io_pipeline();
  626. }
  627. static void
  628. run_open_settings_action(GSimpleAction *action, GVariant *param, gpointer user_data)
  629. {
  630. gtk_stack_set_visible_child_name(GTK_STACK(main_stack), "settings");
  631. }
  632. static void
  633. run_close_settings_action(GSimpleAction *action, GVariant *param, gpointer user_data)
  634. {
  635. gtk_stack_set_visible_child_name(GTK_STACK(main_stack), "main");
  636. }
  637. static void
  638. on_controls_scale_changed(GtkAdjustment *adjustment, void (*set_fn)(double))
  639. {
  640. set_fn(gtk_adjustment_get_value(adjustment));
  641. }
  642. static void
  643. update_value(GtkAdjustment *adjustment, GtkLabel *label)
  644. {
  645. char buf[12];
  646. snprintf(buf, 12, "%.0f", gtk_adjustment_get_value(adjustment));
  647. gtk_label_set_label(label, buf);
  648. }
  649. static void
  650. on_auto_controls_toggled(GtkToggleButton *button, void (*set_auto_fn)(bool))
  651. {
  652. set_auto_fn(gtk_toggle_button_get_active(button));
  653. }
  654. static void
  655. update_scale(GtkToggleButton *button, GtkScale *scale)
  656. {
  657. gtk_widget_set_sensitive(GTK_WIDGET(scale),
  658. !gtk_toggle_button_get_active(button));
  659. }
  660. static void
  661. open_controls(GtkWidget *parent,
  662. const char *title_name,
  663. double min_value,
  664. double max_value,
  665. double current,
  666. bool auto_enabled,
  667. void (*set_fn)(double),
  668. void (*set_auto_fn)(bool))
  669. {
  670. GtkBuilder *builder = gtk_builder_new_from_resource(
  671. "/org/postmarketos/Megapixels/controls-popover.ui");
  672. GtkPopover *popover =
  673. GTK_POPOVER(gtk_builder_get_object(builder, "controls"));
  674. GtkScale *scale = GTK_SCALE(gtk_builder_get_object(builder, "scale"));
  675. GtkLabel *title = GTK_LABEL(gtk_builder_get_object(builder, "title"));
  676. GtkLabel *value_label =
  677. GTK_LABEL(gtk_builder_get_object(builder, "value-label"));
  678. GtkToggleButton *auto_button =
  679. GTK_TOGGLE_BUTTON(gtk_builder_get_object(builder, "auto-button"));
  680. gtk_label_set_label(title, title_name);
  681. GtkAdjustment *adjustment = gtk_range_get_adjustment(GTK_RANGE(scale));
  682. gtk_adjustment_set_lower(adjustment, min_value);
  683. gtk_adjustment_set_upper(adjustment, max_value);
  684. gtk_adjustment_set_value(adjustment, current);
  685. update_value(adjustment, value_label);
  686. gtk_toggle_button_set_active(auto_button, auto_enabled);
  687. update_scale(auto_button, scale);
  688. g_signal_connect(adjustment,
  689. "value-changed",
  690. G_CALLBACK(on_controls_scale_changed),
  691. set_fn);
  692. g_signal_connect(
  693. adjustment, "value-changed", G_CALLBACK(update_value), value_label);
  694. g_signal_connect(auto_button,
  695. "toggled",
  696. G_CALLBACK(on_auto_controls_toggled),
  697. set_auto_fn);
  698. g_signal_connect(auto_button, "toggled", G_CALLBACK(update_scale), scale);
  699. gtk_widget_set_parent(GTK_WIDGET(popover), parent);
  700. gtk_popover_popup(popover);
  701. // g_object_unref(popover);
  702. }
  703. static void
  704. set_gain(double value)
  705. {
  706. if (state.gain.value != (int)value) {
  707. state.gain.value_req = (int)value;
  708. update_io_pipeline();
  709. }
  710. }
  711. static void
  712. set_gain_auto(bool is_auto)
  713. {
  714. if (state.gain.manual != !is_auto) {
  715. state.gain.manual_req = !is_auto;
  716. update_io_pipeline();
  717. }
  718. }
  719. static void
  720. open_iso_controls(GtkWidget *button, gpointer user_data)
  721. {
  722. open_controls(button,
  723. "ISO",
  724. 0,
  725. state.gain.max,
  726. state.gain.value,
  727. !state.gain.manual,
  728. set_gain,
  729. set_gain_auto);
  730. }
  731. static void
  732. set_shutter(double value)
  733. {
  734. int new_exposure = (int)(value / 360.0 * state.camera->current_mode->height);
  735. if (new_exposure != state.exposure.value) {
  736. state.exposure.value_req = new_exposure;
  737. update_io_pipeline();
  738. }
  739. }
  740. static void
  741. set_shutter_auto(bool is_auto)
  742. {
  743. if (state.exposure.manual != !is_auto) {
  744. state.exposure.manual_req = !is_auto;
  745. update_io_pipeline();
  746. }
  747. }
  748. static void
  749. open_shutter_controls(GtkWidget *button, gpointer user_data)
  750. {
  751. float value =
  752. ((float)state.exposure.value / (float)state.exposure.max) * 360.0f;
  753. open_controls(button,
  754. "Shutter",
  755. 1.0,
  756. 360.0,
  757. value,
  758. !state.exposure.manual,
  759. set_shutter,
  760. set_shutter_auto);
  761. }
  762. static void
  763. flash_button_clicked(GtkWidget *button, gpointer user_data)
  764. {
  765. state.flash_enabled = !state.flash_enabled;
  766. update_io_pipeline();
  767. const char *icon_name = state.flash_enabled ? "flash-enabled-symbolic" :
  768. "flash-disabled-symbolic";
  769. gtk_button_set_icon_name(GTK_BUTTON(button), icon_name);
  770. }
  771. static void
  772. on_realize(GtkWidget *window, gpointer *data)
  773. {
  774. if (state.configuration->count == 0) {
  775. return;
  776. }
  777. GtkNative *native = gtk_widget_get_native(window);
  778. mp_process_pipeline_init_gl(gtk_native_get_surface(native));
  779. state.camera = state.configuration->cameras[0];
  780. update_io_pipeline();
  781. }
  782. static GSimpleAction *
  783. create_simple_action(GtkApplication *app, const char *name, GCallback callback)
  784. {
  785. GSimpleAction *action = g_simple_action_new(name, NULL);
  786. g_signal_connect(action, "activate", callback, app);
  787. g_action_map_add_action(G_ACTION_MAP(app), G_ACTION(action));
  788. return action;
  789. }
  790. static void
  791. update_ui_rotation()
  792. {
  793. if (state.device_rotation == 0 || state.device_rotation == 180) {
  794. // Portrait
  795. gtk_widget_set_halign(preview_top_box, GTK_ALIGN_FILL);
  796. gtk_orientable_set_orientation(GTK_ORIENTABLE(preview_top_box),
  797. GTK_ORIENTATION_VERTICAL);
  798. gtk_widget_set_halign(preview_bottom_box, GTK_ALIGN_FILL);
  799. gtk_orientable_set_orientation(GTK_ORIENTABLE(preview_bottom_box),
  800. GTK_ORIENTATION_HORIZONTAL);
  801. if (state.device_rotation == 0) {
  802. gtk_widget_set_valign(preview_top_box, GTK_ALIGN_START);
  803. gtk_widget_set_valign(preview_bottom_box, GTK_ALIGN_END);
  804. } else {
  805. gtk_widget_set_valign(preview_top_box, GTK_ALIGN_END);
  806. gtk_widget_set_valign(preview_bottom_box, GTK_ALIGN_START);
  807. }
  808. } else {
  809. // Landscape
  810. gtk_widget_set_valign(preview_top_box, GTK_ALIGN_FILL);
  811. gtk_orientable_set_orientation(GTK_ORIENTABLE(preview_top_box),
  812. GTK_ORIENTATION_HORIZONTAL);
  813. gtk_widget_set_valign(preview_bottom_box, GTK_ALIGN_FILL);
  814. gtk_orientable_set_orientation(GTK_ORIENTABLE(preview_bottom_box),
  815. GTK_ORIENTATION_VERTICAL);
  816. if (state.device_rotation == 90) {
  817. gtk_widget_set_halign(preview_top_box, GTK_ALIGN_END);
  818. gtk_widget_set_halign(preview_bottom_box, GTK_ALIGN_START);
  819. } else {
  820. gtk_widget_set_halign(preview_top_box, GTK_ALIGN_START);
  821. gtk_widget_set_halign(preview_bottom_box, GTK_ALIGN_END);
  822. }
  823. }
  824. }
  825. char *
  826. munge_app_id(const char *app_id)
  827. {
  828. char *id = g_strdup(app_id);
  829. int i;
  830. if (g_str_has_suffix(id, ".desktop")) {
  831. char *c = g_strrstr(id, ".desktop");
  832. if (c)
  833. *c = '\0';
  834. }
  835. g_strcanon(id,
  836. "0123456789"
  837. "abcdefghijklmnopqrstuvwxyz"
  838. "ABCDEFGHIJKLMNOPQRSTUVWXYZ"
  839. "-",
  840. '-');
  841. for (i = 0; id[i] != '\0'; i++)
  842. id[i] = g_ascii_tolower(id[i]);
  843. return id;
  844. }
  845. /* Verbatim from feedbackd */
  846. #define FEEDBACKD_SCHEMA_ID "org.sigxcpu.feedbackd"
  847. #define FEEDBACKD_KEY_PROFILE "profile"
  848. #define FEEDBACKD_APP_SCHEMA FEEDBACKD_SCHEMA_ID ".application"
  849. #define FEEDBACKD_APP_PREFIX "/org/sigxcpu/feedbackd/application/"
  850. static gboolean
  851. fb_profile_to_state(GValue *value, GVariant *variant, gpointer user_data)
  852. {
  853. const gchar *name;
  854. gboolean fb_state = FALSE;
  855. name = g_variant_get_string(variant, NULL);
  856. if (g_strcmp0(name, "full") == 0)
  857. fb_state = TRUE;
  858. g_value_set_boolean(value, fb_state);
  859. return TRUE;
  860. }
  861. static GVariant *
  862. state_to_fb_profile(const GValue *value,
  863. const GVariantType *expected_type,
  864. gpointer user_data)
  865. {
  866. gboolean fb_state = g_value_get_boolean(value);
  867. return g_variant_new_string(fb_state ? "full" : "silent");
  868. }
  869. static void
  870. setup_fb_switch(GtkBuilder *builder)
  871. {
  872. g_autofree char *path = NULL;
  873. g_autofree char *munged_id = NULL;
  874. g_autoptr(GSettingsSchema) schema = NULL;
  875. GSettingsSchemaSource *schema_source =
  876. g_settings_schema_source_get_default();
  877. GtkWidget *shutter_sound_switch =
  878. GTK_WIDGET(gtk_builder_get_object(builder, "shutter-sound-switch"));
  879. GtkWidget *feedback_box =
  880. GTK_WIDGET(gtk_builder_get_object(builder, "feedback-box"));
  881. schema = g_settings_schema_source_lookup(
  882. schema_source, FEEDBACKD_APP_SCHEMA, TRUE);
  883. if (schema == NULL) {
  884. gtk_widget_set_sensitive(feedback_box, FALSE);
  885. return;
  886. }
  887. munged_id = munge_app_id(APP_ID);
  888. path = g_strconcat(FEEDBACKD_APP_PREFIX, munged_id, "/", NULL);
  889. fb_settings = g_settings_new_with_path(FEEDBACKD_APP_SCHEMA, path);
  890. g_settings_bind_with_mapping(fb_settings,
  891. FEEDBACKD_KEY_PROFILE,
  892. shutter_sound_switch,
  893. "active",
  894. G_SETTINGS_BIND_DEFAULT,
  895. fb_profile_to_state,
  896. state_to_fb_profile,
  897. NULL,
  898. NULL);
  899. }
  900. #ifdef GDK_WINDOWING_WAYLAND
  901. static void
  902. wl_handle_geometry(void *data,
  903. struct wl_output *wl_output,
  904. int32_t x,
  905. int32_t y,
  906. int32_t physical_width,
  907. int32_t physical_height,
  908. int32_t subpixel,
  909. const char *make,
  910. const char *model,
  911. int32_t transform)
  912. {
  913. assert(transform < 4);
  914. int new_rotation = transform * 90;
  915. if (new_rotation != state.device_rotation) {
  916. state.device_rotation = new_rotation;
  917. update_io_pipeline();
  918. update_ui_rotation();
  919. }
  920. }
  921. static void
  922. wl_handle_mode(void *data,
  923. struct wl_output *wl_output,
  924. uint32_t flags,
  925. int32_t width,
  926. int32_t height,
  927. int32_t refresh)
  928. {
  929. // Do nothing
  930. }
  931. static const struct wl_output_listener output_listener = {
  932. .geometry = wl_handle_geometry,
  933. .mode = wl_handle_mode
  934. };
  935. static void
  936. wl_handle_global(void *data,
  937. struct wl_registry *wl_registry,
  938. uint32_t name,
  939. const char *interface,
  940. uint32_t version)
  941. {
  942. if (strcmp(interface, wl_output_interface.name) == 0) {
  943. struct wl_output *output =
  944. wl_registry_bind(wl_registry, name, &wl_output_interface, 1);
  945. wl_output_add_listener(output, &output_listener, NULL);
  946. }
  947. }
  948. static void
  949. wl_handle_global_remove(void *data, struct wl_registry *wl_registry, uint32_t name)
  950. {
  951. // Do nothing
  952. }
  953. static const struct wl_registry_listener registry_listener = {
  954. .global = wl_handle_global,
  955. .global_remove = wl_handle_global_remove
  956. };
  957. #endif // GDK_WINDOWING_WAYLAND
  958. #ifdef GDK_WINDOWING_X11
  959. static gboolean
  960. xevent_handler(GdkDisplay *display, XEvent *xevent, gpointer data)
  961. {
  962. Display *xdisplay = gdk_x11_display_get_xdisplay(display);
  963. int event_base, error_base;
  964. XRRQueryExtension(xdisplay, &event_base, &error_base);
  965. if (xevent->type - event_base == RRScreenChangeNotify) {
  966. Rotation xrotation =
  967. ((XRRScreenChangeNotifyEvent *)xevent)->rotation;
  968. int new_rotation = 0;
  969. switch (xrotation) {
  970. case RR_Rotate_0:
  971. new_rotation = 0;
  972. break;
  973. case RR_Rotate_90:
  974. new_rotation = 90;
  975. break;
  976. case RR_Rotate_180:
  977. new_rotation = 180;
  978. break;
  979. case RR_Rotate_270:
  980. new_rotation = 270;
  981. break;
  982. }
  983. if (new_rotation != state.device_rotation) {
  984. state.device_rotation = new_rotation;
  985. update_io_pipeline();
  986. update_ui_rotation();
  987. }
  988. }
  989. // The return value of this function should always be FALSE; if it's
  990. // TRUE, we prevent GTK/GDK from handling the event.
  991. return FALSE;
  992. }
  993. #endif // GDK_WINDOWING_X11
  994. static void
  995. activate(GtkApplication *app, gpointer data)
  996. {
  997. g_object_set(gtk_settings_get_default(),
  998. "gtk-application-prefer-dark-theme",
  999. TRUE,
  1000. NULL);
  1001. GdkDisplay *display = gdk_display_get_default();
  1002. GtkIconTheme *icon_theme = gtk_icon_theme_get_for_display(display);
  1003. gtk_icon_theme_add_resource_path(icon_theme, "/org/postmarketos/Megapixels");
  1004. GtkCssProvider *provider = gtk_css_provider_new();
  1005. gtk_css_provider_load_from_resource(
  1006. provider, "/org/postmarketos/Megapixels/camera.css");
  1007. gtk_style_context_add_provider_for_display(
  1008. display,
  1009. GTK_STYLE_PROVIDER(provider),
  1010. GTK_STYLE_PROVIDER_PRIORITY_APPLICATION);
  1011. GtkBuilder *builder = gtk_builder_new_from_resource(
  1012. "/org/postmarketos/Megapixels/camera.ui");
  1013. window = GTK_WIDGET(gtk_builder_get_object(builder, "window"));
  1014. iso_button =
  1015. GTK_WIDGET(gtk_builder_get_object(builder, "iso-controls-button"));
  1016. shutter_button = GTK_WIDGET(
  1017. gtk_builder_get_object(builder, "shutter-controls-button"));
  1018. flash_button =
  1019. GTK_WIDGET(gtk_builder_get_object(builder, "flash-controls-button"));
  1020. GtkWidget *setting_dng_button =
  1021. GTK_WIDGET(gtk_builder_get_object(builder, "setting-raw"));
  1022. GtkWidget *setting_postprocessor_combo =
  1023. GTK_WIDGET(gtk_builder_get_object(builder, "setting-processor"));
  1024. GtkListStore *setting_postprocessor_list = GTK_LIST_STORE(
  1025. gtk_builder_get_object(builder, "list-postprocessors"));
  1026. preview = GTK_WIDGET(gtk_builder_get_object(builder, "preview"));
  1027. main_stack = GTK_WIDGET(gtk_builder_get_object(builder, "main_stack"));
  1028. open_last_stack =
  1029. GTK_WIDGET(gtk_builder_get_object(builder, "open_last_stack"));
  1030. thumb_last = GTK_WIDGET(gtk_builder_get_object(builder, "thumb_last"));
  1031. process_spinner =
  1032. GTK_WIDGET(gtk_builder_get_object(builder, "process_spinner"));
  1033. scanned_codes = GTK_WIDGET(gtk_builder_get_object(builder, "scanned-codes"));
  1034. preview_top_box = GTK_WIDGET(gtk_builder_get_object(builder, "top-box"));
  1035. preview_bottom_box =
  1036. GTK_WIDGET(gtk_builder_get_object(builder, "bottom-box"));
  1037. message_box = GTK_WIDGET(gtk_builder_get_object(builder, "message-box"));
  1038. message_label = GTK_WIDGET(gtk_builder_get_object(builder, "message-label"));
  1039. g_signal_connect(window, "realize", G_CALLBACK(on_realize), NULL);
  1040. g_signal_connect(preview, "realize", G_CALLBACK(preview_realize), NULL);
  1041. g_signal_connect(preview, "render", G_CALLBACK(preview_draw), NULL);
  1042. g_signal_connect(preview, "resize", G_CALLBACK(preview_resize), NULL);
  1043. GtkGesture *click = gtk_gesture_click_new();
  1044. g_signal_connect(click, "pressed", G_CALLBACK(preview_pressed), NULL);
  1045. gtk_widget_add_controller(preview, GTK_EVENT_CONTROLLER(click));
  1046. g_signal_connect(iso_button, "clicked", G_CALLBACK(open_iso_controls), NULL);
  1047. g_signal_connect(
  1048. shutter_button, "clicked", G_CALLBACK(open_shutter_controls), NULL);
  1049. g_signal_connect(
  1050. flash_button, "clicked", G_CALLBACK(flash_button_clicked), NULL);
  1051. setup_fb_switch(builder);
  1052. // Setup actions
  1053. create_simple_action(app, "capture", G_CALLBACK(run_capture_action));
  1054. create_simple_action(
  1055. app, "switch-camera", G_CALLBACK(run_camera_switch_action));
  1056. create_simple_action(
  1057. app, "open-settings", G_CALLBACK(run_open_settings_action));
  1058. create_simple_action(
  1059. app, "close-settings", G_CALLBACK(run_close_settings_action));
  1060. create_simple_action(app, "open-last", G_CALLBACK(run_open_last_action));
  1061. create_simple_action(app, "open-photos", G_CALLBACK(run_open_photos_action));
  1062. create_simple_action(app, "about", G_CALLBACK(run_about_action));
  1063. create_simple_action(app, "quit", G_CALLBACK(run_quit_action));
  1064. // Setup shortcuts
  1065. const char *capture_accels[] = { "space", NULL };
  1066. gtk_application_set_accels_for_action(app, "app.capture", capture_accels);
  1067. const char *quit_accels[] = { "<Ctrl>q", "<Ctrl>w", NULL };
  1068. gtk_application_set_accels_for_action(app, "app.quit", quit_accels);
  1069. // Setup settings
  1070. settings = g_settings_new(APP_ID);
  1071. char *setting_postproc = g_settings_get_string(settings, "postprocessor");
  1072. // Initialize the postprocessing gsetting to the old processor if
  1073. // it was not set yet
  1074. if (setting_postproc == NULL || setting_postproc[0] == '\0') {
  1075. printf("Initializing postprocessor gsetting\n");
  1076. setting_postproc = malloc(512);
  1077. if (!mp_process_find_processor(setting_postproc)) {
  1078. printf("No processor found\n");
  1079. exit(1);
  1080. }
  1081. g_settings_set_string(settings, "postprocessor", setting_postproc);
  1082. printf("Initialized postprocessor to %s\n", setting_postproc);
  1083. }
  1084. // Find all postprocessors for the settings list
  1085. mp_process_find_all_processors(setting_postprocessor_list);
  1086. // Bind settings widgets to the actual settings
  1087. g_settings_bind(settings,
  1088. "save-raw",
  1089. setting_dng_button,
  1090. "active",
  1091. G_SETTINGS_BIND_DEFAULT);
  1092. g_settings_bind(settings,
  1093. "postprocessor",
  1094. setting_postprocessor_combo,
  1095. "active-id",
  1096. G_SETTINGS_BIND_DEFAULT);
  1097. #ifdef GDK_WINDOWING_WAYLAND
  1098. // Listen for Wayland rotation
  1099. if (GDK_IS_WAYLAND_DISPLAY(display)) {
  1100. struct wl_display *wl_display =
  1101. gdk_wayland_display_get_wl_display(display);
  1102. struct wl_registry *wl_registry =
  1103. wl_display_get_registry(wl_display);
  1104. // The registry listener will bind to our wl_output and add our
  1105. // listeners
  1106. wl_registry_add_listener(wl_registry, &registry_listener, NULL);
  1107. // GTK will take care of dispatching wayland events for us.
  1108. // Wayland sends us a geometry event as soon as we bind to the
  1109. // wl_output, so we don't need to manually check the initial
  1110. // rotation here.
  1111. }
  1112. #endif
  1113. #ifdef GDK_WINDOWING_X11
  1114. // Listen for X rotation
  1115. if (GDK_IS_X11_DISPLAY(display)) {
  1116. g_signal_connect(
  1117. display, "xevent", G_CALLBACK(xevent_handler), NULL);
  1118. // Set initial rotation
  1119. Display *xdisplay = gdk_x11_display_get_xdisplay(display);
  1120. int screen =
  1121. XScreenNumberOfScreen(gdk_x11_display_get_xscreen(display));
  1122. Rotation xrotation;
  1123. XRRRotations(xdisplay, screen, &xrotation);
  1124. int new_rotation = 0;
  1125. switch (xrotation) {
  1126. case RR_Rotate_0:
  1127. new_rotation = 0;
  1128. break;
  1129. case RR_Rotate_90:
  1130. new_rotation = 90;
  1131. break;
  1132. case RR_Rotate_180:
  1133. new_rotation = 180;
  1134. break;
  1135. case RR_Rotate_270:
  1136. new_rotation = 270;
  1137. break;
  1138. }
  1139. if (new_rotation != state.device_rotation) {
  1140. state.device_rotation = new_rotation;
  1141. update_ui_rotation();
  1142. }
  1143. }
  1144. #endif
  1145. // Initialize display flash
  1146. GDBusConnection *conn =
  1147. g_application_get_dbus_connection(G_APPLICATION(app));
  1148. mp_flash_gtk_init(conn);
  1149. if (state.configuration->count > 0) {
  1150. mp_io_pipeline_start();
  1151. } else {
  1152. display_error("No camera found");
  1153. }
  1154. gtk_application_add_window(app, GTK_WINDOW(window));
  1155. gtk_widget_set_visible(window, true);
  1156. }
  1157. static void
  1158. startup(GApplication *app, gpointer data)
  1159. {
  1160. g_autoptr(GError) err = NULL;
  1161. if (lfb_init(APP_ID, &err))
  1162. capture_event = lfb_event_new("camera-shutter");
  1163. else
  1164. g_warning("Failed to init libfeedback: %s", err->message);
  1165. }
  1166. static void
  1167. shutdown(GApplication *app, gpointer data)
  1168. {
  1169. // Only do cleanup in development, let the OS clean up otherwise
  1170. #ifdef DEBUG
  1171. mp_io_pipeline_stop();
  1172. mp_flash_gtk_clean();
  1173. g_clear_object(&fb_settings);
  1174. g_clear_object(&capture_event);
  1175. lfb_uninit();
  1176. #endif
  1177. }
  1178. int
  1179. main(int argc, char *argv[])
  1180. {
  1181. #ifdef RENDERDOC
  1182. {
  1183. void *mod = dlopen("librenderdoc.so", RTLD_NOW | RTLD_NOLOAD);
  1184. if (mod) {
  1185. pRENDERDOC_GetAPI RENDERDOC_GetAPI =
  1186. (pRENDERDOC_GetAPI)dlsym(mod, "RENDERDOC_GetAPI");
  1187. int ret = RENDERDOC_GetAPI(eRENDERDOC_API_Version_1_1_2,
  1188. (void **)&rdoc_api);
  1189. assert(ret == 1);
  1190. } else {
  1191. printf("Renderdoc not found\n");
  1192. }
  1193. }
  1194. #endif
  1195. char configfile[PATH_MAX];
  1196. libmegapixels_init(&state.configuration);
  1197. if (libmegapixels_find_config(configfile)) {
  1198. if (!libmegapixels_load_file(state.configuration, configfile)) {
  1199. fprintf(stderr, "Could not load config\n");
  1200. return 1;
  1201. }
  1202. } else {
  1203. if (!libmegapixels_load_uvc(state.configuration)) {
  1204. fprintf(stderr, "No config found\n");
  1205. return 1;
  1206. }
  1207. }
  1208. setenv("LC_NUMERIC", "C", 1);
  1209. GtkApplication *app = gtk_application_new(APP_ID, 0);
  1210. g_signal_connect(app, "startup", G_CALLBACK(startup), NULL);
  1211. g_signal_connect(app, "activate", G_CALLBACK(activate), NULL);
  1212. g_signal_connect(app, "shutdown", G_CALLBACK(shutdown), NULL);
  1213. g_application_run(G_APPLICATION(app), argc, argv);
  1214. return 0;
  1215. }