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