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