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