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