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