Propagate the reason of box deletion
[platform/framework/web/livebox-viewer.git] / src / fb.c
1 /*
2  * Copyright 2013  Samsung Electronics Co., Ltd
3  *
4  * Licensed under the Flora License, Version 1.1 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://floralicense.org/license/
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */
16
17 #include <stdio.h>
18 #include <errno.h>
19 #include <sys/mman.h>
20 #include <stdlib.h>
21 #include <string.h>
22 #include <sys/stat.h>
23 #include <fcntl.h>
24 #include <sys/types.h>
25 #include <unistd.h>
26 #include <stddef.h>
27 #include <sys/shm.h>
28 #include <sys/ipc.h>
29
30 #include <X11/Xlib.h>
31 #include <X11/extensions/XShm.h>
32 #include <X11/Xutil.h>
33
34 #include <dlog.h>
35 #include <livebox-errno.h> /* For error code */
36
37 #include "debug.h"
38 #include "util.h"
39 #include "fb.h"
40 #include "critical_log.h"
41
42 int errno;
43
44 struct fb_info {
45         char *id;
46         int w;
47         int h;
48         int bufsz;
49         void *buffer;
50
51         int handle;
52 };
53
54 struct buffer { /*!< Must has to be sync with slave & provider */
55         enum {
56                 CREATED = 0x00beef00,
57                 DESTROYED = 0x00dead00
58         } state;
59         enum buffer_type type;
60         int refcnt;
61         void *info;
62         char data[];
63 };
64
65 static struct {
66         Display *disp;
67         int screen;
68         int depth;
69         Visual *visual;
70         int disp_is_opened;
71 } s_info = {
72         .disp = NULL,
73         .disp_is_opened = 0,
74         .screen = -1,
75         .depth = 0,
76         .visual = NULL,
77 };
78
79 int fb_init(void *disp)
80 {
81         s_info.disp = disp;
82         if (s_info.disp) {
83                 Screen *screen;
84
85                 screen = DefaultScreenOfDisplay(s_info.disp);
86
87                 s_info.screen = DefaultScreen(s_info.disp);
88                 s_info.visual = DefaultVisualOfScreen(screen);
89         }
90
91         s_info.depth = sizeof(int); //DefaultDepthOfScreen(screen);
92         return 0;
93 }
94
95 int fb_fini(void)
96 {
97         if (s_info.disp_is_opened && s_info.disp) {
98                 XCloseDisplay(s_info.disp);
99         }
100
101         s_info.disp = NULL;
102         s_info.disp_is_opened = 0;
103         s_info.visual = NULL;
104         s_info.screen = -1;
105         s_info.depth = 0;
106         return 0;
107 }
108
109 static inline void update_fb_size(struct fb_info *info)
110 {
111         info->bufsz = info->w * info->h * s_info.depth;
112 }
113
114 static inline int sync_for_file(struct fb_info *info)
115 {
116         int fd;
117         struct buffer *buffer;
118
119         buffer = info->buffer;
120
121         if (!buffer) { /* Ignore this sync request */
122                 return LB_STATUS_SUCCESS;
123         }
124
125         if (buffer->state != CREATED) {
126                 ErrPrint("Invalid state of a FB\n");
127                 return LB_STATUS_ERROR_INVALID;
128         }
129
130         if (buffer->type != BUFFER_TYPE_FILE) {
131                 ErrPrint("Invalid buffer\n");
132                 return LB_STATUS_SUCCESS;
133         }
134
135         fd = open(util_uri_to_path(info->id), O_RDONLY);
136         if (fd < 0) {
137                 ErrPrint("Failed to open a file (%s) because of (%s)\n",
138                                         util_uri_to_path(info->id), strerror(errno));
139
140                 /*!
141                  * \note
142                  * But return ZERO, even if we couldn't get a buffer file,
143                  * the viewer can draw empty screen.
144                  *
145                  * and then update it after it gots update events
146                  */
147                 return LB_STATUS_SUCCESS;
148         }
149
150         if (read(fd, buffer->data, info->bufsz) != info->bufsz) {
151                 ErrPrint("read: %s\n", strerror(errno));
152                 if (close(fd) < 0) {
153                         ErrPrint("close: %s\n", strerror(errno));
154                 }
155
156                 /*!
157                  * \note
158                  * But return ZERO, even if we couldn't get a buffer file,
159                  * the viewer can draw empty screen.
160                  *
161                  * and then update it after it gots update events
162                  */
163                 return LB_STATUS_SUCCESS;
164         }
165
166         if (close(fd) < 0) {
167                 ErrPrint("close: %s\n", strerror(errno));
168         }
169         return LB_STATUS_SUCCESS;
170 }
171
172 static inline __attribute__((always_inline)) int sync_for_pixmap(struct fb_info *info)
173 {
174         struct buffer *buffer;
175         XShmSegmentInfo si;
176         XImage *xim;
177
178         buffer = info->buffer;
179         if (!buffer) { /*!< Ignore this sync request */
180                 return LB_STATUS_SUCCESS;
181         }
182
183         if (buffer->state != CREATED) {
184                 ErrPrint("Invalid state of a FB\n");
185                 return LB_STATUS_ERROR_INVALID;
186         }
187
188         if (buffer->type != BUFFER_TYPE_PIXMAP) {
189                 ErrPrint("Invalid buffer\n");
190                 return LB_STATUS_SUCCESS;
191         }
192
193         if (!s_info.disp) {
194                 s_info.disp = XOpenDisplay(NULL);
195                 if (s_info.disp) {
196                         Screen *screen;
197
198                         s_info.disp_is_opened = 1;
199
200                         screen = DefaultScreenOfDisplay(s_info.disp);
201
202                         s_info.screen = DefaultScreen(s_info.disp);
203                         s_info.visual = DefaultVisualOfScreen(screen);
204                 } else {
205                         ErrPrint("Failed to open a display\n");
206                         return LB_STATUS_ERROR_FAULT;
207                 }
208         }
209
210         if (info->handle == 0) {
211                 ErrPrint("Pixmap ID is not valid\n");
212                 return LB_STATUS_ERROR_INVALID;
213         }
214
215         if (info->bufsz == 0) {
216                 /*!
217                  * If the client does not acquire the buffer,
218                  * This function will do nothing.
219                  * It will work only if the buffer is acquired.
220                  * To sync its contents.
221                  */
222                 DbgPrint("Nothing can be sync\n");
223                 return LB_STATUS_SUCCESS;
224         }
225
226         si.shmid = shmget(IPC_PRIVATE, info->bufsz, IPC_CREAT | 0666);
227         if (si.shmid < 0) {
228                 ErrPrint("shmget: %s\n", strerror(errno));
229                 return LB_STATUS_ERROR_FAULT;
230         }
231
232         si.readOnly = False;
233         si.shmaddr = shmat(si.shmid, NULL, 0);
234         if (si.shmaddr == (void *)-1) {
235                 if (shmctl(si.shmid, IPC_RMID, 0) < 0) {
236                         ErrPrint("shmctl: %s\n", strerror(errno));
237                 }
238
239                 return LB_STATUS_ERROR_FAULT;
240         }
241
242         /*!
243          * \NOTE
244          * Use the 24 bits Pixmap for Video player
245          */
246         xim = XShmCreateImage(s_info.disp, s_info.visual,
247                                 (s_info.depth << 3), ZPixmap, NULL,
248                                 &si,
249                                 info->w, info->h);
250         if (xim == NULL) {
251                 if (shmdt(si.shmaddr) < 0) {
252                         ErrPrint("shmdt: %s\n", strerror(errno));
253                 }
254
255                 if (shmctl(si.shmid, IPC_RMID, 0) < 0) {
256                         ErrPrint("shmctl: %s\n", strerror(errno));
257                 }
258
259                 return LB_STATUS_ERROR_FAULT;
260         }
261
262         xim->data = si.shmaddr;
263         XShmAttach(s_info.disp, &si);
264
265         XShmGetImage(s_info.disp, info->handle, xim, 0, 0, 0xFFFFFFFF);
266         XSync(s_info.disp, False);
267
268         memcpy(buffer->data, xim->data, info->bufsz);
269
270         XShmDetach(s_info.disp, &si);
271         XDestroyImage(xim);
272
273         if (shmdt(si.shmaddr) < 0) {
274                 ErrPrint("shmdt: %s\n", strerror(errno));
275         }
276
277         if (shmctl(si.shmid, IPC_RMID, 0) < 0) {
278                 ErrPrint("shmctl: %s\n", strerror(errno));
279         }
280
281         return LB_STATUS_SUCCESS;
282 }
283
284 int fb_sync(struct fb_info *info)
285 {
286         if (!info) {
287                 ErrPrint("FB Handle is not valid\n");
288                 return LB_STATUS_ERROR_INVALID;
289         }
290
291         if (!info->id || info->id[0] == '\0') {
292                 DbgPrint("Ingore sync\n");
293                 return LB_STATUS_SUCCESS;
294         }
295
296         if (!strncasecmp(info->id, SCHEMA_FILE, strlen(SCHEMA_FILE))) {
297                 return sync_for_file(info);
298         } else if (!strncasecmp(info->id, SCHEMA_PIXMAP, strlen(SCHEMA_PIXMAP))) {
299                 return sync_for_pixmap(info);
300         } else if (!strncasecmp(info->id, SCHEMA_SHM, strlen(SCHEMA_SHM))) {
301                 /* No need to do sync */ 
302                 return LB_STATUS_SUCCESS;
303         }
304
305         return LB_STATUS_ERROR_INVALID;
306 }
307
308 struct fb_info *fb_create(const char *id, int w, int h)
309 {
310         struct fb_info *info;
311
312         if (!id || id[0] == '\0') {
313                 ErrPrint("Invalid ID\n");
314                 return NULL;
315         }
316
317         info = calloc(1, sizeof(*info));
318         if (!info) {
319                 CRITICAL_LOG("Heap: %s\n", strerror(errno));
320                 return NULL;
321         }
322
323         info->id = strdup(id);
324         if (!info->id) {
325                 CRITICAL_LOG("Heap: %s\n", strerror(errno));
326                 free(info);
327                 return NULL;
328         }
329
330         if (sscanf(info->id, SCHEMA_SHM "%d", &info->handle) == 1) {
331                 DbgPrint("SHMID: %d is gotten\n", info->handle);
332         } else if (sscanf(info->id, SCHEMA_PIXMAP "%d", &info->handle) == 1) {
333                 DbgPrint("PIXMAP-SHMID: %d is gotten\n", info->handle);
334         } else {
335                 info->handle = LB_STATUS_ERROR_INVALID;
336         }
337
338         info->bufsz = 0;
339         info->buffer = NULL;
340         info->w = w;
341         info->h = h;
342
343         return info;
344 }
345
346 int fb_destroy(struct fb_info *info)
347 {
348         if (!info) {
349                 ErrPrint("Handle is not valid\n");
350                 return LB_STATUS_ERROR_INVALID;
351         }
352
353         if (info->buffer) {
354                 struct buffer *buffer;
355                 buffer = info->buffer;
356
357                 buffer->info = NULL;
358         }
359
360         free(info->id);
361         free(info);
362         return LB_STATUS_SUCCESS;
363 }
364
365 int fb_is_created(struct fb_info *info)
366 {
367         if (!info) {
368                 ErrPrint("Handle is not valid\n");
369                 return 0;
370         }
371
372         if (!strncasecmp(info->id, SCHEMA_PIXMAP, strlen(SCHEMA_PIXMAP)) && info->handle != 0) {
373                 return 1;
374         } else if (!strncasecmp(info->id, SCHEMA_SHM, strlen(SCHEMA_SHM)) && info->handle > 0) {
375                 return 1;
376         } else {
377                 const char *path;
378                 path = util_uri_to_path(info->id);
379                 if (path && access(path, F_OK | R_OK) == 0) {
380                         return 1;
381                 } else {
382                         ErrPrint("access: %s (%s)\n", strerror(errno), path);
383                 }
384         }
385
386         return 0;
387 }
388
389 void *fb_acquire_buffer(struct fb_info *info)
390 {
391         struct buffer *buffer;
392
393         if (!info) {
394                 ErrPrint("info == NIL\n");
395                 return NULL;
396         }
397
398         if (!info->buffer) {
399                 if (!strncasecmp(info->id, SCHEMA_PIXMAP, strlen(SCHEMA_PIXMAP))) {
400                         update_fb_size(info);
401
402                         buffer = calloc(1, sizeof(*buffer) + info->bufsz);
403                         if (!buffer) {
404                                 CRITICAL_LOG("Heap: %s\n", strerror(errno));
405                                 info->bufsz = 0;
406                                 return NULL;
407                         }
408
409                         buffer->type = BUFFER_TYPE_PIXMAP;
410                         buffer->refcnt = 0;
411                         buffer->state = CREATED;
412                         buffer->info = info;
413                         info->buffer = buffer;
414
415                         /*!
416                          * \note
417                          * Just update from here.
418                          */
419                         sync_for_pixmap(info);
420                 } else if (!strncasecmp(info->id, SCHEMA_FILE, strlen(SCHEMA_FILE))) {
421                         update_fb_size(info);
422
423                         buffer = calloc(1, sizeof(*buffer) + info->bufsz);
424                         if (!buffer) {
425                                 CRITICAL_LOG("Heap: %s\n", strerror(errno));
426                                 info->bufsz = 0;
427                                 return NULL;
428                         }
429
430                         buffer->type = BUFFER_TYPE_FILE;
431                         buffer->refcnt = 0;
432                         buffer->state = CREATED;
433                         buffer->info = info;
434                         info->buffer = buffer;
435
436                         sync_for_file(info);
437                 } else if (!strncasecmp(info->id, SCHEMA_SHM, strlen(SCHEMA_SHM))) {
438                         buffer = shmat(info->handle, NULL, 0);
439                         if (buffer == (void *)-1) {
440                                 ErrPrint("shmat: %s\n", strerror(errno));
441                                 return NULL;
442                         }
443
444                         return buffer->data;
445                 } else {
446                         ErrPrint("Buffer is not created (%s)\n", info->id);
447                         return NULL;
448                 }
449         }
450
451         buffer = info->buffer;
452
453         switch (buffer->type) {
454         case BUFFER_TYPE_PIXMAP:
455                 buffer->refcnt++;
456                 break;
457         case BUFFER_TYPE_FILE:
458                 buffer->refcnt++;
459                 break;
460         default:
461                 DbgPrint("Unknwon FP: %d\n", buffer->type);
462                 break;
463         }
464
465         return buffer->data;
466 }
467
468 int fb_release_buffer(void *data)
469 {
470         struct buffer *buffer;
471
472         if (!data) {
473                 ErrPrint("buffer data == NIL\n");
474                 return LB_STATUS_ERROR_INVALID;
475         }
476
477         buffer = container_of(data, struct buffer, data);
478
479         if (buffer->state != CREATED) {
480                 ErrPrint("Invalid handle\n");
481                 return LB_STATUS_ERROR_INVALID;
482         }
483
484         switch (buffer->type) {
485         case BUFFER_TYPE_SHM:
486                 if (shmdt(buffer) < 0) {
487                         ErrPrint("shmdt: %s\n", strerror(errno));
488                 }
489                 break;
490         case BUFFER_TYPE_PIXMAP:
491                 buffer->refcnt--;
492                 if (buffer->refcnt == 0) {
493                         struct fb_info *info;
494                         info = buffer->info;
495
496                         buffer->state = DESTROYED;
497                         free(buffer);
498                 
499                         if (info && info->buffer == buffer) {
500                                 info->buffer = NULL;
501                         }
502                 }
503                 break;
504         case BUFFER_TYPE_FILE:
505                 buffer->refcnt--;
506                 if (buffer->refcnt == 0) {
507                         struct fb_info *info;
508                         info = buffer->info;
509
510                         buffer->state = DESTROYED;
511                         free(buffer);
512
513                         if (info && info->buffer == buffer) {
514                                 info->buffer = NULL;
515                         }
516                 }
517                 break;
518         default:
519                 ErrPrint("Unknwon buffer type\n");
520                 break;
521         }
522
523         return LB_STATUS_SUCCESS;
524 }
525
526 int fb_refcnt(void *data)
527 {
528         struct buffer *buffer;
529         struct shmid_ds buf;
530         int ret;
531
532         if (!data) {
533                 return LB_STATUS_ERROR_INVALID;
534         }
535
536         buffer = container_of(data, struct buffer, data);
537
538         if (buffer->state != CREATED) {
539                 ErrPrint("Invalid handle\n");
540                 return LB_STATUS_ERROR_INVALID;
541         }
542
543         switch (buffer->type) {
544         case BUFFER_TYPE_SHM:
545                 if (shmctl(buffer->refcnt, IPC_STAT, &buf) < 0) {
546                         ErrPrint("Error: %s\n", strerror(errno));
547                         return LB_STATUS_ERROR_FAULT;
548                 }
549
550                 ret = buf.shm_nattch;
551                 break;
552         case BUFFER_TYPE_PIXMAP:
553                 ret = buffer->refcnt;
554                 break;
555         case BUFFER_TYPE_FILE:
556                 ret = buffer->refcnt;
557                 break;
558         default:
559                 ret = LB_STATUS_ERROR_INVALID;
560                 break;
561         }
562
563         return ret;
564 }
565
566 const char *fb_id(struct fb_info *info)
567 {
568         return info ? info->id : NULL;
569 }
570
571 int fb_get_size(struct fb_info *info, int *w, int *h)
572 {
573         if (!info) {
574                 ErrPrint("Handle is not valid\n");
575                 return LB_STATUS_ERROR_INVALID;
576         }
577
578         *w = info->w;
579         *h = info->h;
580         return LB_STATUS_SUCCESS;
581 }
582
583 int fb_size(struct fb_info *info)
584 {
585         if (!info) {
586                 return 0;
587         }
588
589         update_fb_size(info);
590
591         return info->bufsz;
592 }
593
594 /* End of a file */