change global variable position
[framework/uifw/cbhm.git] / src / scrcapture.c
1 #include "common.h"
2 #include "cbhm_main.h"
3 #include "xcnphandler.h"
4 #include "scrcapture.h"
5 #include "clipdrawer.h"
6
7 #include <sys/ipc.h>
8 #include <sys/shm.h>
9
10 #include <Ecore.h>
11 #include <Ecore_X.h>
12 #include <Ecore_Input.h>
13 #include <utilX.h>
14
15 #define IMAGE_SAVE_DIR "/opt/media/Images and videos/My photo clips"
16 #define IMAGE_SAVE_FILE_TYPE ".png"
17 #define CAPTURE_SOUND_FILE "/usr/share/cbhm/sounds/14_screen_capture.wav"
18 #define CAPTURE_SOUND_TIMEOUT_SEC 2
19                         
20 #include <mmf/mm_sound_private.h>
21 #include <pthread.h>
22 #include <errno.h>
23 #include <sys/time.h>
24
25 static pthread_mutex_t g_sound_lock = PTHREAD_MUTEX_INITIALIZER;
26 static pthread_cond_t g_sound_cond = PTHREAD_COND_INITIALIZER;
27 static Eina_Bool g_shot = EINA_FALSE;
28
29 typedef struct tag_captureimginfo
30 {
31         char filename[256];
32         Evas_Object *eo;
33         char *imgdata;
34 } captureimginfo_t;
35
36 static Eina_Bool get_image_filename_with_date(char *dstr)
37 {
38         time_t tim = time(NULL);
39         struct tm *now = localtime(&tim);
40         struct timeval tv;
41         gettimeofday(&tv, NULL);
42         sprintf(dstr, "%s/screen-%d%02d%02d%02d%02d%02d%0ld%s", 
43                         IMAGE_SAVE_DIR,
44                         now->tm_year+1900, now->tm_mon+1, now->tm_mday,
45                         now->tm_hour, now->tm_min,
46                         now->tm_sec, tv.tv_usec,
47                         IMAGE_SAVE_FILE_TYPE);
48         return EINA_TRUE;
49 }
50
51 static void _sound_callback(void *data)
52 {
53                 DTRACE("_sound_callback\n");
54         pthread_cond_broadcast(&g_sound_cond);
55         return;
56 }
57
58 static void _play_capture_sound()
59 {
60         int ret, step, handle;
61         MMSoundParamType pparam = {0,};
62         Eina_Bool sync = EINA_FALSE;
63         struct timespec timeout;
64         struct timeval tv;
65         ret = mm_sound_set_path(MM_SOUND_GAIN_CAMERA, MM_SOUND_PATH_SPK, MM_SOUND_PATH_NONE, MM_SOUND_PATH_OPTION_NONE);         
66         if (ret != MM_ERROR_NONE)
67         {
68                 DTRACE("mm_sound_set_path is failed\n");
69                 return;
70         }
71         ret = mm_sound_volume_get_step(VOLUME_TYPE_FIXED, &step);
72         if (ret != MM_ERROR_NONE)
73         {
74                 DTRACE("mm_sound_volume_get_step is failed\n");
75                 return;
76         }
77         if (pthread_mutex_trylock(&g_sound_lock) == EBUSY)
78         {
79                 DTRACE("trylock is fail - g_sound_lock\n");
80                 return;
81         }
82         pparam.filename = CAPTURE_SOUND_FILE;
83         pparam.loop = 1;
84         pparam.volume = step-1;
85         pparam.callback = _sound_callback;
86
87         if (mm_sound_play_loud_solo_sound(CAPTURE_SOUND_FILE, 
88                                                                           VOLUME_TYPE_FIXED, _sound_callback, 
89                                                                           NULL, &handle) 
90                 == MM_ERROR_NONE)
91         {
92                 if(sync)
93                 {
94                         gettimeofday(&tv, NULL);
95                         timeout.tv_sec = tv.tv_sec + CAPTURE_SOUND_TIMEOUT_SEC;
96                         timeout.tv_nsec = tv.tv_usec;
97                         if(ETIMEDOUT == pthread_cond_timedwait(&g_sound_cond, &g_sound_lock, &timeout))
98                         {
99                                 if(handle>0)
100                                         mm_sound_stop_sound(handle);
101                         }
102                 }
103         }
104         else
105         {
106                 DTRACE("effect sound play failed\n");
107                 pthread_mutex_unlock(&g_sound_lock);
108                 return;
109         }
110         pthread_mutex_unlock(&g_sound_lock);
111         DTRACE("sound play success\n");
112 }
113
114 static Eina_Bool _scrcapture_capture_postprocess(void* data)
115 {
116         captureimginfo_t *capimginfo = data;
117
118         DTIME("start capture postprocess - %s\n", capimginfo->filename);
119
120         if (!evas_object_image_save(capimginfo->eo, capimginfo->filename, NULL, "compress=1")) 
121         {
122                 DTRACE("screen capture save fail\n");
123                 g_shot = EINA_FALSE;
124                 return EINA_FALSE;
125         }
126
127         DTIME("end capture postprocess - %s\n", capimginfo->filename);
128
129         char *imgpath = NULL;
130         imgpath = malloc(strlen(capimginfo->filename)+strlen("file://")+2);
131         snprintf(imgpath, strlen(capimginfo->filename)+strlen("file://")+1,
132                          "%s%s", "file://", capimginfo->filename);
133         DTRACE("add to image history = %s\n", imgpath+strlen("file://"));
134         clipdrawer_add_item(imgpath+strlen("file://"), GI_IMAGE);
135         free(imgpath);
136         
137         evas_object_del(capimginfo->eo);
138         free(capimginfo->imgdata);
139         free(capimginfo);
140
141         DTIME("end current capture\n");
142
143         _play_capture_sound();
144
145         g_shot = EINA_FALSE;
146
147         return EINA_FALSE;
148 }
149
150 Eina_Bool capture_current_screen(void *data)
151 {
152         struct appdata *ad = data;
153
154         if (g_shot)
155         {
156                 DTRACE("too early to capture current screen\n");
157                 return EINA_FALSE;
158         }
159         g_shot = EINA_TRUE;
160
161         DTIME("start current capture\n");
162
163         captureimginfo_t *capimginfo = NULL;
164         capimginfo = malloc(sizeof(captureimginfo_t) * 1);
165         get_image_filename_with_date(capimginfo->filename);
166         DTRACE("capture current screen\n");
167
168         int width, height;
169         width = ad->root_w;
170         height = ad->root_h;
171         capimginfo->imgdata = malloc(sizeof(char) * (width*height*4) + 1);
172         capimginfo->eo = evas_object_image_add(ad->evas);
173
174         char *scrimage = NULL;
175         scrimage = scrcapture_capture_screen_by_xv_ext(width, height);
176         if (scrimage)
177                 memcpy(capimginfo->imgdata, scrimage, width*height*4);
178         scrcapture_release_screen_by_xv_ext(scrimage);
179
180         if (scrimage == NULL || capimginfo->eo == NULL || capimginfo->imgdata == NULL) 
181         {
182                 DTRACE("screen capture fail\n");
183                 free(capimginfo->imgdata);
184                 if (capimginfo->eo)
185                         evas_object_del(capimginfo->eo);
186                 free(capimginfo);
187                 g_shot = EINA_FALSE;
188                 return EINA_FALSE;
189         }
190
191         DTRACE("screen capture prepared\n");
192
193         evas_object_image_data_set(capimginfo->eo, NULL);
194         evas_object_image_size_set(capimginfo->eo, width, height);
195         evas_object_image_data_set(capimginfo->eo, capimginfo->imgdata);
196         evas_object_image_data_update_add(capimginfo->eo, 0, 0, width, height);
197         evas_object_resize(capimginfo->eo, width, height);
198
199         ecore_idler_add(_scrcapture_capture_postprocess, capimginfo);
200
201         return EINA_TRUE;
202 }
203
204 static Eina_Bool scrcapture_keydown_cb(void *data, int type, void *event)
205 {
206         struct appdata *ad = data;
207         Ecore_Event_Key *ev = event;
208
209         if (!strcmp(ev->keyname, KEY_END))
210                 clipdrawer_lower_view(ad);
211
212         return ECORE_CALLBACK_PASS_ON;
213 }
214
215 int init_scrcapture(void *data)
216 {
217         struct appdata *ad = data;
218
219         int result = 0;
220
221         /* Key Grab */
222         Ecore_X_Display *xdisp = ecore_x_display_get();
223         Ecore_X_Window xwin = (Ecore_X_Window)ecore_evas_window_get(ecore_evas_ecore_evas_get(ad->evas));
224
225         ecore_event_handler_add(ECORE_EVENT_KEY_DOWN, scrcapture_keydown_cb, ad);
226
227         pthread_mutex_init(&g_sound_lock, NULL);
228         pthread_cond_init(&g_sound_cond, NULL);
229
230         return 0;
231 }
232
233 void close_scrcapture(void *data)
234 {
235         struct appdata *ad = data;
236
237         Ecore_X_Display *xdisp = ecore_x_display_get();
238         Ecore_X_Window xwin = (Ecore_X_Window)ecore_evas_window_get(ecore_evas_ecore_evas_get(ad->evas));
239
240         pthread_mutex_destroy(&g_sound_lock);
241         pthread_cond_destroy(&g_sound_cond);
242 }
243
244
245 inline static Ecore_X_Display *get_display(void)
246 {
247         return ecore_x_display_get();
248 }
249
250 static int get_window_attribute(Window id, int *depth, Visual **visual, int *width, int *height)
251 {
252 //      assert(id);
253         XWindowAttributes attr;
254
255         DTRACE("XGetWindowAttributes\n");
256         if (!XGetWindowAttributes(get_display(), id, &attr)) 
257         {
258                 return -1;
259         }
260
261         if (attr.map_state == IsViewable && attr.class == InputOutput) 
262         {
263                 *depth = attr.depth;
264                 *width = attr.width;
265                 *height= attr.height;
266                 *visual= attr.visual;
267         }
268
269         return 0;
270 }
271
272 static Window _get_parent_window( Window id )
273 {
274         Window root;
275         Window parent;
276         Window* children;
277         unsigned int num;
278
279         DTRACE("XQeuryTree\n");
280
281         if (!XQueryTree(get_display(), id, &root, &parent, &children, &num)) 
282         {
283                 return 0;
284         }
285
286         if (children) 
287         {
288                 DTRACE("XFree\n");
289                 XFree(children);
290         }
291
292         return parent;
293 }
294
295 static Window find_capture_available_window( Window id, Visual** visual, int* depth, int* width, int* height) 
296 {
297         XWindowAttributes attr;
298         Window parent = id;
299         Window orig_id = id;
300         
301         if (id == 0) 
302         {
303                 return (Window) -1;
304         }
305
306         do 
307         {
308                 id = parent;    
309                 DTRACE("find_capture - XGetWindowAttributes\n");
310
311                 if (!XGetWindowAttributes(get_display(), id, &attr)) 
312                 {
313                         return (Window) -1;
314                 }
315                 
316                 parent = _get_parent_window( id );
317
318                 if (attr.map_state == IsViewable
319                                 && attr.override_redirect == True
320                                 && attr.class == InputOutput && parent == attr.root )
321         {
322                         *depth = attr.depth;
323                         *width = attr.width;
324                         *height = attr.height;
325                         *visual = attr.visual;
326                         return id;
327                 }
328         } while( parent != attr.root && parent != 0 ); //Failed finding a redirected window 
329         
330
331         DTRACE( "find_capture - cannot find id\n");
332         XGetWindowAttributes (get_display(), orig_id, &attr);
333         *depth = attr.depth;
334         *width = attr.width;
335         *height = attr.height;
336         *visual = attr.visual;
337                 
338         return (Window) 0;
339         
340 }
341
342 char *scrcapture_screen_capture(Window oid, int *size)
343 {
344         XImage *xim;
345         XShmSegmentInfo si;
346         Pixmap pix;
347         int depth;
348         int width;
349         int height;
350         Visual *visual;
351         char *captured_image;
352         Window id;
353         
354         id = find_capture_available_window(ecore_x_window_focus_get(), &visual, &depth, &width, &height);
355
356         if (id == 0 || id == -1 || id == oid)
357         {
358                 DTRACE("Window : 0x%lX\n", id);
359                 if (get_window_attribute(id, &depth, &visual, &width, &height) < 0) 
360                 {
361                         DTRACE("Failed to get the attributes from 0x%x\n", (unsigned int)id);
362                         return NULL;
363                 }
364         }
365
366         DTRACE("WxH : %dx%d\n", width, height);
367         DTRACE("Depth : %d\n", depth >> 3);
368
369         // NOTE: just add one more depth....
370         si.shmid = shmget(IPC_PRIVATE, width * height * ((depth >> 3)+1), IPC_CREAT | 0666);
371         if (si.shmid < 0) 
372         {
373                 DTRACE("error at shmget\n");
374                 return NULL;
375         }
376         si.readOnly = False;
377         si.shmaddr = shmat(si.shmid, NULL, 0);
378         if (si.shmaddr == (char*)-1) 
379         {
380                 shmdt(si.shmaddr);
381                 shmctl(si.shmid, IPC_RMID, 0);
382                 DTRACE("can't get shmat\n");
383                 return NULL;
384         }
385
386 /*
387         if (!need_redirecting) 
388         {
389                 Window border;
390                 if (get_border_window(id, &border) < 0) 
391                 {
392                         need_redirecting = 1;
393                         printf("Failed to find a border, forcely do redirecting\n");
394                 } 
395                 else 
396                 {
397                         id = border;
398                         printf("Border window is found, use it : 0x%X\n", (unsigned int)id);
399                 }
400         }
401
402         if (need_redirecting) 
403         {
404                 printf("XCompositeRedirectWindow");
405                 XCompositeRedirectWindow(get_display(), id, CompositeRedirectManual);
406         }
407 */
408
409
410         DTRACE("XShmCreateImage\n");
411         xim = XShmCreateImage(get_display(), visual, depth, ZPixmap, NULL, &si, width, height);
412         if (!xim) 
413         {
414                 shmdt(si.shmaddr);
415                 shmctl(si.shmid, IPC_RMID, 0);
416
417 /*
418                 if (need_redirecting) 
419                 {
420                         printf("XCompositeUnredirectWindow");
421                         XCompositeUnredirectWindow(get_display(), id, CompositeRedirectManual);
422                 }
423 */
424                 return NULL;
425         }
426
427         *size = xim->bytes_per_line * xim->height;
428         xim->data = si.shmaddr;
429
430         DTRACE("XCompositeNameWindowPixmap\n");
431         pix = XCompositeNameWindowPixmap(get_display(), id);
432
433         DTRACE("XShmAttach\n");
434         XShmAttach(get_display(), &si);
435
436         DTRACE("XShmGetImage\n");
437         XShmGetImage(get_display(), pix, xim, 0, 0, 0xFFFFFFFF);
438
439         //XUnmapWindow(disp, id);
440         //XMapWindow(disp, id);
441         DTRACE("XSync\n");
442         XSync(get_display(), False);
443
444         //sleep(1);
445         // We can optimize this!
446         captured_image = calloc(1, *size);
447         if (captured_image) 
448         {
449                 memcpy(captured_image, xim->data, *size);
450         } 
451         else 
452         {
453                 DTRACE("calloc error");
454         }
455
456         DTRACE("XShmDetach");
457         XShmDetach(get_display(), &si);
458         DTRACE("XFreePixmap\n");
459         XFreePixmap(get_display(), pix);
460         DTRACE("XDestroyImage\n");
461         XDestroyImage(xim);
462
463 /*
464         if (need_redirecting) {
465                 printf("XCompositeUnredirectWindow");
466                 XCompositeUnredirectWindow(get_display(), id, CompositeRedirectManual);
467         }
468 */
469
470         shmdt(si.shmaddr);
471         shmctl(si.shmid, IPC_RMID, 0);
472         return captured_image;
473 }
474
475 char *scrcapture_capture_screen_by_x11(Window xid, int *size)
476 {
477         XImage *xim;
478         int depth;
479         int width;
480         int height;
481         Visual *visual;
482         char *captured_image;
483
484
485         DTRACE("Window : 0x%lX\n", xid);
486         if (get_window_attribute(xid, &depth, &visual, &width, &height) < 0) 
487         {
488                 DTRACE("Failed to get the attributes from 0x%x\n", (unsigned int)xid);
489                 return NULL;
490         }
491
492         DTRACE("WxH : %dx%d\n", width, height);
493         DTRACE("Depth : %d\n", depth >> 3);
494
495         xim = XGetImage(get_display(), xid, 0, 0,
496                                          width, height, AllPlanes, ZPixmap);
497
498         *size = xim->bytes_per_line * xim->height;
499
500         captured_image = calloc(1, *size);
501         if (captured_image) 
502         {
503                 memcpy(captured_image, xim->data, *size);
504         } 
505         else 
506         {
507                 DTRACE("calloc error");
508         }
509
510         return captured_image;
511 }
512
513 char *scrcapture_capture_screen_by_xv_ext(int w, int h)
514 {
515         return createScreenShot(w, h);
516 }
517
518 void scrcapture_release_screen_by_xv_ext(const char *s)
519 {
520         releaseScreenShot(s);
521 }