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