git.jasLogic.tech
initial commit
[photobox.git] / src / photobox.c
1 #include <stdio.h>
2 #include <stdlib.h>
3 #include <time.h>
4
5 #include <gtk/gtk.h>
6 #include <glib.h>
7
8 #include <mipea/gpio.h>
9 #include <curl/curl.h>
10 #include <qrencode.h>
11
12 #include "photobox_photo.h"
13 #include "apikey.h"
14
15 // fehlt in glib??
16 #define G_SOURCE_FUNC(f) ((GSourceFunc) (void (*)(void)) (f))
17
18 void pb_exit(void);
19
20 void pb_show_main(void);
21 void pb_show_send(void);
22 void pb_show_qr(void);
23
24 gboolean pb_poll_buttton(void);
25 gboolean pb_countdown(void);
26 void pb_takepic(void);
27
28 int pb_cp_dp(char *id);
29
30 static GtkWidget *img;
31 static GtkWidget *button_take;
32 static GtkWidget *label_countdown;
33
34 static GtkWidget *button_upload;
35 static GtkWidget *button_cancel;
36
37 static GtkWidget *qr_img;
38 static GtkWidget *qr_label;
39 static GtkWidget *qr_button_back;
40
41 static const unsigned int IMG_WIDTH = 512;
42 static const unsigned int IMG_HEIGHT = 384;
43 static const unsigned int QR_SIZE = 350;
44
45 static unsigned int button_pin = 26;
46
47 int main(int argc, char *argv[])
48 {
49 if (pb_ph_init() != 0) {
50 fprintf(stderr, "ERROR: pb_ph_init\n");
51 return 1;
52 }
53 if (gpio_map() == NULL) {
54 fprintf(stderr, "ERROR: gpio_map\n");
55 return 1;
56 }
57 gtk_init(&argc, &argv);
58
59 signal(SIGCHLD, SIG_IGN);
60
61 // GPIO
62 gpio_inp(button_pin);
63 gpio_pud(button_pin, PUD_UP);
64
65 // WINDOW
66 GtkWidget *window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
67 gtk_window_set_title(GTK_WINDOW(window), "Photobox");
68 gtk_window_fullscreen(GTK_WINDOW(window));
69 //g_signal_connect(window, "delete-event", G_CALLBACK(pb_gui_on_delete_main), NULL);
70 g_signal_connect(window, "destroy", G_CALLBACK(pb_exit), NULL);
71 // hide cursor
72 gtk_widget_realize(window);
73 GdkCursor* cursor_blank = gdk_cursor_new(GDK_BLANK_CURSOR);
74 gdk_window_set_cursor(gtk_widget_get_window(window), cursor_blank);
75
76 // BOX VERTICAL
77 GtkWidget *box_v = gtk_box_new(GTK_ORIENTATION_VERTICAL, 0);
78 gtk_container_add(GTK_CONTAINER(window), box_v);
79 gtk_widget_show(box_v);
80
81 // BOX HORIZONTAL
82 GtkWidget *box_h = gtk_box_new(GTK_ORIENTATION_HORIZONTAL, 0);
83 gtk_box_pack_end(GTK_BOX(box_v), box_h, TRUE, FALSE, 0);
84 gtk_widget_show(box_h);
85
86 // IMAGE
87 img = gtk_image_new_from_file("no_signal.png");
88 gtk_box_pack_end(GTK_BOX(box_v), img, TRUE, FALSE, 0);
89
90 // BUTTON: TAKE PICTURE
91 button_take = gtk_button_new();
92 // label
93 GtkWidget *button_take_label = gtk_label_new(NULL);
94 gtk_label_set_markup(GTK_LABEL(button_take_label), "<span font='50'>Bild aufnehmen</span>");
95 gtk_container_add(GTK_CONTAINER(button_take), button_take_label);
96 gtk_widget_show(button_take_label);
97 // /label
98 gtk_box_pack_start(GTK_BOX(box_h), button_take, TRUE, FALSE, 0);
99 g_signal_connect(button_take, "clicked", G_CALLBACK(pb_countdown), NULL);
100
101 // COUNTDOWN LABEL
102 label_countdown = gtk_label_new(NULL);
103 gtk_box_pack_end(GTK_BOX(box_v), label_countdown, TRUE, FALSE, 0);
104
105 // BUTTON: UPLOAD
106 button_upload = gtk_button_new_with_label("Hochladen");
107 //g_signal_connect(button_upload, "clicked", G_CALLBACK(pb_exit), NULL);
108 g_signal_connect(button_upload, "clicked", G_CALLBACK(pb_show_qr), NULL);
109 gtk_box_pack_start(GTK_BOX(box_h), button_upload, TRUE, FALSE, 0);
110
111 // BUTTON: CANCEL
112 button_cancel = gtk_button_new_with_label("Abbrechen");
113 //g_signal_connect(button_cancel, "clicked", G_CALLBACK(pb_exit), NULL);
114 g_signal_connect(button_cancel, "clicked", G_CALLBACK(pb_show_main), NULL);
115 gtk_box_pack_start(GTK_BOX(box_h), button_cancel, TRUE, FALSE, 0);
116
117 // QR CODE IMAGE
118 qr_img = gtk_image_new_from_file("no_signal.png");
119 gtk_box_pack_end(GTK_BOX(box_v), qr_img, TRUE, FALSE, 0);
120
121 // QR CODE LABEL
122 qr_label = gtk_label_new(NULL);
123 gtk_label_set_justify(GTK_LABEL(qr_label), GTK_JUSTIFY_CENTER);
124 gtk_label_set_line_wrap(GTK_LABEL(qr_label), TRUE);
125 gtk_box_pack_end(GTK_BOX(box_v), qr_label, TRUE, FALSE, 0);
126
127 // QR BUTTON BACK
128 qr_button_back = gtk_button_new_with_label("Zurück");
129 g_signal_connect(qr_button_back, "clicked", G_CALLBACK(pb_show_main), NULL);
130 //g_signal_connect(qr_button_back, "clicked", G_CALLBACK(pb_exit), NULL);
131 gtk_box_pack_start(GTK_BOX(box_h), qr_button_back, TRUE, FALSE, 0);
132
133 pb_show_main();
134 gtk_widget_show(window);
135
136 gtk_main();
137
138 return 0;
139 }
140
141 void pb_exit(void)
142 {
143 gpio_unmap();
144 gtk_main_quit();
145 }
146
147 void pb_show_main(void)
148 {
149 gtk_widget_hide(img);
150 gtk_widget_hide(button_upload);
151 gtk_widget_hide(button_cancel);
152 gtk_widget_hide(qr_img);
153 gtk_widget_hide(qr_label);
154 gtk_widget_hide(qr_button_back);
155
156 gtk_widget_show(button_take);
157
158 // take picture with button
159 g_idle_add(G_SOURCE_FUNC(pb_poll_buttton), NULL);
160 }
161
162 void pb_show_send(void)
163 {
164 gtk_widget_show(img);
165 gtk_widget_show(button_upload);
166 gtk_widget_show(button_cancel);
167
168 gtk_widget_hide(label_countdown);
169 }
170
171 void pb_show_qr(void)
172 {
173 gtk_widget_hide(img);
174 gtk_widget_hide(button_upload);
175 gtk_widget_hide(button_cancel);
176
177 gtk_widget_show(qr_img);
178 gtk_widget_show(qr_label);
179 gtk_widget_show(qr_button_back);
180
181 // id from time
182 char id[7];
183 strftime(id, 7, "%H%M%S", localtime(&(time_t) {time(NULL)}));
184
185 pid_t pid = fork();
186 if (pid == 0) {
187 // child -> copy and upload
188 if (pb_cp_dp(id) < 0) {
189 fprintf(stderr, "Error: pb_cp_dp\n");
190 exit(EXIT_FAILURE);
191 } else {
192 exit(EXIT_SUCCESS);
193 }
194 } else if (pid < 0) {
195 fprintf(stderr, "FORK ERROR!!!!!\n");
196 // TODO: dont show qr
197 }
198
199 // url qr should point to
200 // example: https://www.dropbox.com/sh/3pq0pwednyuig86/AACx0_vjn-liY5_pP_C3nJD8a?dl=0&preview=c1b052c5.jpg
201 char url[] = "https://www.dropbox.com/sh/3pq0pwednyuig86/AACx0_vjn-liY5_pP_C3nJD8a?dl=0&preview=xxxxxx.jpg";
202 for (unsigned int i = 0; i < 6; ++i) {
203 url[i + 82] = id[i];
204 }
205
206 // generate qr code
207 QRcode *qr = QRcode_encodeString(url, 0, QR_ECLEVEL_H, QR_MODE_8, 1);
208 if (qr == NULL) {
209 perror("Failed to generate QR-Code");
210 pb_exit();
211 }
212
213 unsigned int size = qr->width;
214 char values[10];
215 sprintf(values, "%d %d 2 1", size, size);
216
217 const char *xface[3 + size];
218 xface[0] = values;
219 xface[1] = "a c #ffffff";
220 xface[2] = "b c #000000";
221
222 char qrcode_char[size][size + 1];
223 for (unsigned int i = 0; i < size; ++i) {
224 qrcode_char[i][size] = '\0';
225 for (unsigned int j = 0; j < size; ++j) {
226 qrcode_char[i][j] = qr->data[j + i * size] & 1 ? 'b' : 'a';
227 }
228 xface[3 + i] = &qrcode_char[i][0];
229 }
230 QRcode_free(qr);
231
232 GdkPixbuf *pb = gdk_pixbuf_new_from_xpm_data(xface);
233
234 pb = gdk_pixbuf_scale_simple(pb, QR_SIZE, QR_SIZE, GDK_INTERP_NEAREST);
235 gtk_image_set_from_pixbuf((GtkImage *)qr_img, pb);
236 g_clear_object(&pb);
237
238 char text[] = "<span font='12'>QR-Code scannen oder auf <b>https://www.dropbox.com/sh/3pq0pwednyuig86/AACx0_vjn-liY5_pP_C3nJD8a</b> gehen. <small>Der Upload kann einige Minuten dauern.</small></span>";
239 gtk_label_set_markup(GTK_LABEL(qr_label), text);
240 }
241
242 gboolean pb_poll_buttton(void)
243 {
244 if (gpio_tst(button_pin) == 0) {
245 pb_countdown();
246 return G_SOURCE_REMOVE;
247 } else {
248 return G_SOURCE_CONTINUE;
249 }
250 }
251
252 gboolean pb_countdown(void)
253 {
254 static int num = 0;
255
256 switch(num) {
257 case 0:
258 num = 5;
259
260 g_idle_remove_by_data(NULL);
261
262 gtk_label_set_markup(GTK_LABEL(label_countdown),
263 "<span font='300' color='red'>5</span>");
264 gtk_widget_hide(button_take);
265 gtk_widget_show(label_countdown);
266
267 g_timeout_add(1000, G_SOURCE_FUNC(pb_countdown), NULL);
268 return G_SOURCE_CONTINUE;
269 case 5:
270 num = 4;
271 gtk_label_set_markup(GTK_LABEL(label_countdown),
272 "<span font='300' color='red'>4</span>");
273 return G_SOURCE_CONTINUE;
274 case 4:
275 num = 3;
276 gtk_label_set_markup(GTK_LABEL(label_countdown),
277 "<span font='300' color='red'>3</span>");
278 return G_SOURCE_CONTINUE;
279 case 3:
280 num = 2;
281 gtk_label_set_markup(GTK_LABEL(label_countdown),
282 "<span font='300' color='red'>2</span>");
283 return G_SOURCE_CONTINUE;
284 case 2:
285 num = 1;
286 gtk_label_set_markup(GTK_LABEL(label_countdown),
287 "<span font='300' color='red'>1</span>");
288 return G_SOURCE_CONTINUE;
289 case 1:
290 num = 0;
291 gtk_label_set_markup(GTK_LABEL(label_countdown),
292 "<span font='300' color='red'>0</span>");
293
294 // 0 zeigen bevor der anfängt zu rechnen
295 while (gtk_events_pending()) {
296 gtk_main_iteration();
297 }
298
299 // bild aufnehmen
300 pb_takepic();
301 pb_show_send();
302 return G_SOURCE_REMOVE;
303 }
304 // wenn das passiert ist was schief gelaufen
305 printf("etwas ist sehr schief gelaufen\n");
306 return G_SOURCE_REMOVE;
307 }
308
309 void pb_takepic(void)
310 {
311 char *fn = "tmp.jpg";
312 if (pb_ph_capture_file(fn) != 0) {
313 fprintf(stderr, "ERROR: pb_ph_capture_file\n");
314 gtk_main_quit();
315 return;
316 }
317
318 GdkPixbuf *pb = gdk_pixbuf_new_from_file(fn, NULL);
319 pb = gdk_pixbuf_scale_simple(pb, IMG_WIDTH, IMG_HEIGHT, GDK_INTERP_BILINEAR);
320 gtk_image_set_from_pixbuf(GTK_IMAGE(img), pb);
321 g_clear_object(&pb);
322 }
323
324 int pb_cp_dp(char *id)
325 {
326 char buffer[4096];
327 size_t num_read;
328 size_t dest_fsize;
329
330 char src[] = "tmp.jpg";
331 char dest[] = "bilder/xxxxxx.jpg";
332 char dropbox_arg[] = "Dropbox-API-Arg: {\"path\": \"/bilder/xxxxxx.jpg\",\"mode\": \"add\",\"autorename\": true,\"mute\": false,\"strict_conflict\": false}";
333
334 for (unsigned int i = 0; i < 6; ++i) {
335 dest[i + 7] = id[i];
336 dropbox_arg[i + 35] = id[i];
337 }
338
339 FILE *src_file = fopen(src, "rb");
340 if (src_file == NULL) {
341 perror("Error opening \"tmp.jpg\"");
342 return -1;
343 }
344 FILE *dest_file = fopen(dest, "wb+");
345 if (dest_file == NULL) {
346 fclose(src_file);
347 perror("Error opening destination file");
348 return -1;
349 }
350
351 do {
352 num_read = fread(buffer, 1, 4096, src_file);
353 dest_fsize += num_read;
354 if (fwrite(buffer, 1, num_read, dest_file) != num_read && ferror(dest_file) != 0) {
355 fprintf(stderr, "Error writing to destination file");
356 goto error_file;
357 }
358 } while(num_read == 4096);
359 if (ferror(src_file) != 0) {
360 fprintf(stderr, "Error reading from \"tmp.jpg\"");
361 goto error_file;
362 }
363
364 fclose(src_file);
365 rewind(dest_file); // from beginning for upload
366
367 // upload
368 curl_global_init(CURL_GLOBAL_ALL);
369 CURL *curl = curl_easy_init();
370 if (curl == NULL) {
371 fprintf(stderr, "ERROR: curl_easy_init\n");
372 curl_global_cleanup();
373 goto error_file;
374 }
375
376 struct curl_slist *header = NULL;
377 header = curl_slist_append(header, "Authorization: Bearer " APIKEY);
378 header = curl_slist_append(header, dropbox_arg);
379 header = curl_slist_append(header, "Content-Type: application/octet-stream");
380 curl_easy_setopt(curl, CURLOPT_HTTPHEADER, header);
381
382 curl_easy_setopt(curl, CURLOPT_URL, "https://content.dropboxapi.com/2/files/upload");
383
384 curl_easy_setopt(curl, CURLOPT_POST, 1L); // post request
385 curl_easy_setopt(curl, CURLOPT_POSTFIELDSIZE, dest_fsize); // file size
386 curl_easy_setopt(curl, CURLOPT_POSTFIELDS, NULL); // use read callback
387 curl_easy_setopt(curl, CURLOPT_READDATA, dest_file); // file pointer
388 curl_easy_setopt(curl, CURLOPT_READFUNCTION, NULL); // default callback
389
390 curl_easy_setopt(curl, CURLOPT_WRITEDATA, NULL); // disable output
391
392 CURLcode ret = curl_easy_perform(curl);
393 if (ret != CURLE_OK) {
394 fprintf(stderr, "ERROR: curl_easy_perform: %s\n", curl_easy_strerror(ret));
395 goto error_curl;
396 }
397
398 curl_slist_free_all(header);
399 curl_easy_cleanup(curl);
400
401 curl_global_cleanup();
402
403 fclose(dest_file);
404
405 return 0;
406
407 error_curl:
408 curl_slist_free_all(header);
409 curl_easy_cleanup(curl);
410
411 curl_global_cleanup();
412
413 error_file:
414 fclose(dest_file);
415
416 return -1;
417 }