1 /*
2 * misc x11 functions: pixmap handling (incl. MIT SHMEM), event
3 * tracking for the TV widget.
4 *
5 * (c) 1998 Gerd Knorr <kraxel@goldbach.in-berlin.de>
6 *
7 */
8
9 #include "config.h"
10
11 #include <stdio.h>
12 #include <stdlib.h>
13 #include <strings.h>
14 #include <errno.h>
15 #include <pthread.h>
16 #include <sys/types.h>
17 #include <sys/time.h>
18 #include <sys/socket.h>
19 #include <sys/ipc.h>
20 #include <sys/shm.h>
21
22 #include <X11/Xlib.h>
23 #include <X11/Xutil.h>
24 #include <X11/Intrinsic.h>
25 #include <X11/StringDefs.h>
26 #include <X11/Shell.h>
27 #include <X11/extensions/XShm.h>
28 #ifdef HAVE_LIBXV
29 # include <X11/extensions/Xv.h>
30 # include <X11/extensions/Xvlib.h>
31 #endif
32
33 #include "grab-ng.h"
34 #include "capture.h"
35 #include "channel.h"
36 #include "x11.h"
37 #include "xv.h"
38 #include "commands.h"
39 #include "blit.h"
40
41 #define DEL_TIMER(proc) XtRemoveTimeOut(proc)
42 #define ADD_TIMER(proc) XtAppAddTimeOut(app_context,200,proc,NULL)
43
44 extern XtAppContext app_context;
45 extern XVisualInfo vinfo;
46
47 /* ------------------------------------------------------------------------ */
48
49 Pixmap
50 x11_capture_pixmap(Display *dpy, XVisualInfo *vinfo, Colormap colormap,
51 unsigned int width, unsigned int height)
52 {
53 struct ng_video_buf *buf;
54 struct ng_video_fmt fmt;
55 Pixmap pix = 0;
56
57 if (!(f_drv & CAN_CAPTURE))
58 return 0;
59
60 memset(&fmt,0,sizeof(fmt));
61 fmt.fmtid = x11_dpy_fmtid;
62 fmt.width = width ? width : cur_tv_width;
63 fmt.height = height ? height : cur_tv_height;
64 if (NULL == (buf = ng_grabber_get_image(&fmt)))
65 return 0;
66 buf = ng_filter_single(cur_filter,buf);
67 pix = x11_create_pixmap(dpy,vinfo,buf);
68 ng_release_video_buf(buf);
69 return pix;
70 }
71
72 void
73 x11_label_pixmap(Display *dpy, Colormap colormap, Pixmap pixmap,
74 int height, char *label)
75 {
76 static XFontStruct *font;
77 static XColor color,dummy;
78 XGCValues values;
79 GC gc;
80
81 if (!font) {
82 font = XLoadQueryFont(dpy,"fixed");
83 XAllocNamedColor(dpy,colormap,"yellow",&color,&dummy);
84 }
85 values.font = font->fid;
86 values.foreground = color.pixel;
87 gc = XCreateGC(dpy, pixmap, GCFont | GCForeground, &values);
88 XDrawString(dpy,pixmap,gc,5,height-5,label,strlen(label));
89 XFreeGC(dpy, gc);
90 }
91
92 static int
93 x11_error_dev_null(Display * dpy, XErrorEvent * event)
94 {
95 return 0;
96 }
97
98 /* ------------------------------------------------------------------------ */
99 /* video grabdisplay stuff */
100
101 struct video_handle {
102 Widget win;
103 Dimension width,height;
104 XtWorkProcId work_id;
105 int suspend; /* temporarely disabled */
106 int nw,nh; /* new size (suspend) */
107 struct ng_video_fmt best;
108 struct blit_state *blit;
109
110 /* image filtering */
111 struct ng_filter *filter;
112 void *fhandle;
113 struct ng_video_fmt ffmt;
114 };
115 struct video_handle vh;
116
117 void video_gd_init(Widget widget, int use_gl)
118 {
119 struct video_handle *h = &vh;
120
121 if (debug)
122 fprintf(stderr,"gd: init\n");
123 h->win = widget;
124 h->blit = blit_init(h->win,&vinfo, use_gl);
125 }
126
127 static struct ng_video_buf*
128 video_gd_filter(struct video_handle *h, struct ng_video_buf *buf)
129 {
130 if (NULL != h->filter &&
131 (cur_filter != h->filter ||
132 buf->fmt.fmtid != h->ffmt.fmtid ||
133 buf->fmt.width != h->ffmt.width ||
134 buf->fmt.height != h->ffmt.height)) {
135 h->filter->fini(h->fhandle);
136 h->filter = NULL;
137 h->fhandle = NULL;
138 memset(&h->ffmt,0,sizeof(h->ffmt));
139 }
140 if ((1 << buf->fmt.fmtid) & cur_filter->fmts) {
141 if (NULL == h->filter) {
142 h->filter = cur_filter;
143 h->fhandle = h->filter->init(&buf->fmt);
144 h->ffmt = buf->fmt;
145 }
146 buf = cur_filter->frame(h->fhandle,buf);
147 }
148 return buf;
149 }
150
151 int
152 video_gd_blitframe(struct video_handle *h, struct ng_video_buf *buf)
153 {
154 if (buf->fmt.width > cur_tv_width ||
155 buf->fmt.height > cur_tv_height)
156 return -1;
157
158 if (cur_filter)
159 buf = video_gd_filter(h,buf);
160 blit_putframe(h->blit,buf);
161 return 0;
162 }
163
164 static Boolean
165 video_gd_idle(XtPointer data)
166 {
167 struct video_handle *h = data;
168 struct ng_video_buf *buf;
169
170 if (!(f_drv & CAN_CAPTURE))
171 goto oops;
172
173 buf = ng_grabber_grab_image(0);
174 if (NULL != buf) {
175 video_gd_blitframe(h,buf);
176 } else {
177 goto oops;
178 }
179
180 if (debug) {
181 static long count,lastsec;
182 struct timeval t;
183 struct timezone tz;
184 gettimeofday(&t,&tz);
185 if (t.tv_sec != lastsec) {
186 if (lastsec == t.tv_sec-1)
187 fprintf(stderr,"%5ld fps \r", count);
188 lastsec = t.tv_sec;
189 count = 0;
190 }
191 count++;
192 }
193 return FALSE;
194
195 oops:
196 h->work_id = 0;
197 if (f_drv & CAN_CAPTURE)
198 drv->stopvideo(h_drv);
199 return TRUE;
200 }
201
202 void
203 video_gd_start(void)
204 {
205 struct video_handle *h = &vh;
206
207 if (debug)
208 fprintf(stderr,"gd: start [%d]\n",h->best.fmtid);
209 if (0 == h->best.fmtid)
210 return;
211 if (0 != ng_grabber_setformat(&h->best,0))
212 return;
213 drv->startvideo(h_drv,-1,2);
214 // drv->startvideo(h_drv,-1,4);
215 h->work_id = XtAppAddWorkProc(app_context, video_gd_idle, h);
216 }
217
218 void
219 video_gd_stop(void)
220 {
221 struct video_handle *h = &vh;
222
223 if (debug)
224 fprintf(stderr,"gd: stop\n");
225 if (h->work_id) {
226 drv->stopvideo(h_drv);
227 XtRemoveWorkProc(h->work_id);
228 h->work_id = 0;
229 blit_fini_frame(h->blit);
230 }
231 }
232
233 void
234 video_gd_suspend(void)
235 {
236 struct video_handle *h = &vh;
237
238 h->suspend = 1;
239 if (cur_capture != CAPTURE_GRABDISPLAY)
240 return;
241 do_va_cmd(2, "capture", "off");
242 }
243
244 void
245 video_gd_restart(void)
246 {
247 struct video_handle *h = &vh;
248
249 if (!h->suspend)
250 return;
251 h->suspend = 0;
252 if (h->nw && h->nh) {
253 video_gd_configure(h->nw,h->nh);
254 h->nw = 0;
255 h->nh = 0;
256 }
257 if (cur_capture != CAPTURE_OFF)
258 return;
259 do_va_cmd(2, "capture", "grab");
260 }
261
262 void
263 video_gd_configure(int width, int height)
264 {
265 struct video_handle *h = &vh;
266 unsigned int i,fmtids[2*VIDEO_FMT_COUNT];
267
268 if (!(f_drv & CAN_CAPTURE))
269 return;
270
271 blit_resize(h->blit,width,height);
272
273 if (h->suspend) {
274 if (debug)
275 fprintf(stderr,"gd: delay configure\n");
276 h->nw = width;
277 h->nh = height;
278 return;
279 }
280
281 if (debug)
282 fprintf(stderr,"gd: config %dx%d win=%lx\n",
283 width,height,XtWindow(h->win));
284
285 if (!XtWindow(h->win))
286 return;
287
288 cur_tv_width = width;
289 cur_tv_height = height;
290 h->best.width = width;
291 h->best.height = height;
292 h->best.bytesperline = 0;
293 ng_ratio_fixup(&cur_tv_width, &cur_tv_height, NULL, NULL);
294 ng_ratio_fixup(&h->best.width, &h->best.height, NULL, NULL);
295
296 if (0 == h->best.fmtid) {
297 blit_get_formats(h->blit,fmtids,sizeof(fmtids)/sizeof(int));
298 for (i = 0; i < sizeof(fmtids)/sizeof(int); i++) {
299 h->best.fmtid = fmtids[i];
300 if (0 == ng_grabber_setformat(&h->best,0))
301 goto done;
302 }
303 /* failed */
304 h->best.fmtid = 0;
305 }
306
307 done:
308 if (debug)
309 fprintf(stderr,"grabdisplay: using \"%s\"\n",
310 ng_vfmt_to_desc[h->best.fmtid]);
311 if (cur_capture == CAPTURE_GRABDISPLAY) {
312 do_va_cmd(2, "capture", "off");
313 do_va_cmd(2, "capture", "grab");
314 }
315 }
316
317 /* ------------------------------------------------------------------------ */
318 /* video overlay stuff */
319
320 unsigned int swidth,sheight; /* screen */
321 static int x11_overlay_fmtid;
322
323 /* window */
324 static Widget video,video_parent;
325 static int wx, wy, wmap;
326 static struct ng_video_fmt wfmt;
327
328 static XtIntervalId overlay_refresh;
329 static int did_refresh, oc_count;
330 static int visibility = VisibilityFullyObscured;
331 static int conf = 1, move = 1;
332 static int overlay_on = 0, overlay_enabled = 0;
333 static struct OVERLAY_CLIP oc[256];
334 static XtWorkProcId conf_id;
335
336 /* ------------------------------------------------------------------------ */
337
338 char *event_names[] = {
339 "", "1",
340 "KeyPress",
341 "KeyRelease",
342 "ButtonPress",
343 "ButtonRelease",
344 "MotionNotify",
345 "EnterNotify",
346 "LeaveNotify",
347 "FocusIn",
348 "FocusOut",
349 "KeymapNotify",
350 "Expose",
351 "GraphicsExpose",
352 "NoExpose",
353 "VisibilityNotify",
354 "CreateNotify",
355 "DestroyNotify",
356 "UnmapNotify",
357 "MapNotify",
358 "MapRequest",
359 "ReparentNotify",
360 "ConfigureNotify",
361 "ConfigureRequest",
362 "GravityNotify",
363 "ResizeRequest",
364 "CirculateNotify",
365 "CirculateRequest",
366 "PropertyNotify",
367 "SelectionClear",
368 "SelectionRequest",
369 "SelectionNotify",
370 "ColormapNotify",
371 "ClientMessage",
372 "MappingNotify"
373 };
374 const int nevent_names = sizeof(event_names)/sizeof(event_names[0]);
375
376 /* ------------------------------------------------------------------------ */
377
378 static void
379 add_clip(int x1, int y1, int x2, int y2)
380 {
381 if (oc[oc_count].x1 != x1 || oc[oc_count].y1 != y1 ||
382 oc[oc_count].x2 != x2 || oc[oc_count].y2 != y2) {
383 conf = 1;
384 }
385 oc[oc_count].x1 = x1;
386 oc[oc_count].y1 = y1;
387 oc[oc_count].x2 = x2;
388 oc[oc_count].y2 = y2;
389 oc_count++;
390 }
391
392 static void
393 get_clips(void)
394 {
395 int x1,y1,x2,y2,lastcount;
396 Display *dpy;
397 XWindowAttributes wts;
398 Window root, me, rroot, parent, *children;
399 uint nchildren, i;
400 void *old_handler = XSetErrorHandler(x11_error_dev_null);
401
402 if (debug > 1)
403 fprintf(stderr," getclips");
404 lastcount = oc_count;
405 oc_count = 0;
406 dpy = XtDisplay(video);
407
408 if (wx<0)
409 add_clip(0, 0, (uint)(-wx), wfmt.height);
410 if (wy<0)
411 add_clip(0, 0, wfmt.width, (uint)(-wy));
412 if ((wx+wfmt.width) > swidth)
413 add_clip(swidth-wx, 0, wfmt.width, wfmt.height);
414 if ((wy+wfmt.height) > sheight)
415 add_clip(0, sheight-wy, wfmt.width, wfmt.height);
416
417 root=DefaultRootWindow(dpy);
418 me = XtWindow(video);
419 for (;;) {
420 XQueryTree(dpy, me, &rroot, &parent, &children, &nchildren);
421 XFree((char *) children);
422 /* fprintf(stderr,"me=0x%x, parent=0x%x\n",me,parent); */
423 if (root == parent)
424 break;
425 me = parent;
426 }
427 XQueryTree(dpy, root, &rroot, &parent, &children, &nchildren);
428
429 for (i = 0; i < nchildren; i++)
430 if (children[i]==me)
431 break;
432
433 for (i++; i<nchildren; i++) {
434 XGetWindowAttributes(dpy, children[i], &wts);
435 if (!(wts.map_state & IsViewable))
436 continue;
437
438 x1=wts.x-wx;
439 y1=wts.y-wy;
440 x2=x1+wts.width+2*wts.border_width;
441 y2=y1+wts.height+2*wts.border_width;
442 if ((x2 < 0) || (x1 > (int)wfmt.width) ||
443 (y2 < 0) || (y1 > (int)wfmt.height))
444 continue;
445
446 if (x1<0) x1=0;
447 if (y1<0) y1=0;
448 if (x2>(int)wfmt.width) x2=wfmt.width;
449 if (y2>(int)wfmt.height) y2=wfmt.height;
450 add_clip(x1, y1, x2, y2);
451 }
452 XFree((char *) children);
453
454 if (lastcount != oc_count)
455 conf = 1;
456 XSetErrorHandler(old_handler);
457 }
458
459 static void
460 refresh_timer(XtPointer clientData, XtIntervalId *id)
461 {
462 Window win = RootWindowOfScreen(XtScreen(video));
463 Display *dpy = XtDisplay(video);
464 XSetWindowAttributes xswa;
465 unsigned long mask;
466 Window tmp;
467
468 if (!move && wmap && visibility == VisibilityUnobscured) {
469 if (debug > 1)
470 fprintf(stderr,"video: refresh skipped\n");
471 return;
472 }
473
474 if (debug > 1)
475 fprintf(stderr,"video: refresh\n");
476 overlay_refresh = 0;
477 if (wmap && visibility != VisibilityFullyObscured)
478 did_refresh = 1;
479
480 xswa.override_redirect = True;
481 xswa.backing_store = NotUseful;
482 xswa.save_under = False;
483 mask = (CWSaveUnder | CWBackingStore| CWOverrideRedirect );
484 tmp = XCreateWindow(dpy,win, 0,0, swidth,sheight, 0,
485 CopyFromParent, InputOutput, CopyFromParent,
486 mask, &xswa);
487 XMapWindow(dpy, tmp);
488 XUnmapWindow(dpy, tmp);
489 XDestroyWindow(dpy, tmp);
490 move = 0;
491 }
492
493 static Boolean
494 configure_delayed(XtPointer data)
495 {
496 if (debug > 1)
497 fprintf(stderr,"video: configure delayed");
498 if (wmap && visibility != VisibilityFullyObscured) {
499 if (visibility == VisibilityPartiallyObscured)
500 get_clips();
501 else
502 oc_count = 0;
503
504 if (debug > 1)
505 fprintf(stderr," %s\n",conf ? "yes" : "no");
506 if (conf) {
507 overlay_on = 1;
508 if (f_drv & CAN_OVERLAY)
509 drv->overlay(h_drv,&wfmt,wx,wy,oc,oc_count,1);
510 if (overlay_refresh)
511 DEL_TIMER(overlay_refresh);
512 overlay_refresh = ADD_TIMER(refresh_timer);
513 conf = 0;
514 }
515 } else {
516 if (debug > 1)
517 fprintf(stderr," off\n");
518 if (conf && overlay_on) {
519 overlay_on = 0;
520 if (f_drv & CAN_OVERLAY)
521 drv->overlay(h_drv,NULL,0,0,NULL,0,0);
522 if (overlay_refresh)
523 DEL_TIMER(overlay_refresh);
524 overlay_refresh = ADD_TIMER(refresh_timer);
525 conf = 0;
526 }
527 }
528 conf_id = 0;
529 return TRUE;
530 }
531
532 static void
533 configure_overlay(void)
534 {
535 if (!overlay_enabled)
536 return;
537
538 #ifdef HAVE_LIBXV
539 if (have_xv) {
540 if (wfmt.width && wfmt.height)
541 xv_video(XtWindow(video),wfmt.width,wfmt.height,1);
542 return;
543 }
544 #endif
545
546 if (0 == conf_id)
547 conf_id = XtAppAddWorkProc(app_context,configure_delayed,NULL);
548 }
549
550 void
551 video_new_size()
552 {
553 Dimension x,y,w,h;
554
555 XtVaGetValues(video_parent, XtNx, &x, XtNy, &y,
556 XtNwidth, &w, XtNheight, &h, NULL);
557 wx = x; if (wx > 32768) wx -= 65536;
558 wy = y; if (wy > 32768) wy -= 65536;
559 wfmt.width = w; if (wfmt.width > 32768) wfmt.width -= 65536;
560 wfmt.height = h; if (wfmt.height > 32768) wfmt.height -= 65536;
561 wfmt.fmtid = x11_overlay_fmtid;
562 if (debug > 1)
563 fprintf(stderr,"video: shell: size %dx%d+%d+%d\n",
564 wfmt.width,wfmt.height,wx,wy);
565
566 conf = 1;
567 move = 1;
568 configure_overlay();
569 }
570
571 /* ------------------------------------------------------------------------ */
572
573 static void
574 video_event(Widget widget, XtPointer client_data, XEvent *event, Boolean *d)
575 {
576 if (widget == video_parent) {
577 /* shell widget */
578 switch(event->type) {
579 case ConfigureNotify:
580 #if 0
581 wx = event->xconfigure.x;
582 wy = event->xconfigure.y;
583 wwidth = event->xconfigure.width;
584 wheight = event->xconfigure.height;
585 if (debug > 1)
586 fprintf(stderr,"video: shell: cfg %dx%d+%d+%d\n",
587 wwidth,wheight,wx,wy);
588 #endif
589 video_new_size();
590 break;
591 case MapNotify:
592 if (debug > 1)
593 fprintf(stderr,"video: shell: map\n");
594 wmap = 1;
595 conf = 1;
596 configure_overlay();
597 break;
598 case UnmapNotify:
599 if (debug > 1)
600 fprintf(stderr,"video: shell: unmap\n");
601 wmap = 0;
602 conf = 1;
603 configure_overlay();
604 break;
605 default:
606 if (debug > 1)
607 fprintf(stderr,"video: shell: %s\n",
608 event_names[event->type]);
609 }
610 return;
611
612 } else {
613 /* TV widget (+root window) */
614 switch(event->type) {
615 case Expose:
616 if (event->xvisibility.window == XtWindow(video)) {
617 /* tv */
618 if (!event->xexpose.count) {
619 if (did_refresh) {
620 did_refresh = 0;
621 if (debug > 1)
622 fprintf(stderr,"video: tv: last refresh expose\n");
623 } else {
624 if (debug > 1)
625 fprintf(stderr,"video: tv: expose\n");
626 conf = 1;
627 configure_overlay();
628 }
629 }
630 }
631 break;
632 case VisibilityNotify:
633 if (event->xvisibility.window == XtWindow(video)) {
634 /* tv */
635 visibility = event->xvisibility.state;
636 if (debug > 1)
637 fprintf(stderr,"video: tv: visibility %d%s\n",
638 event->xvisibility.state,
639 did_refresh?" (ignored)":"");
640 if (did_refresh) {
641 if (event->xvisibility.state != VisibilityFullyObscured)
642 did_refresh = 0;
643 } else {
644 conf = 1;
645 configure_overlay();
646 }
647 } else {
648 /* root */
649 if (debug > 1)
650 fprintf(stderr,"video: root: visibility\n");
651 }
652 break;
653 case MapNotify:
654 case UnmapNotify:
655 case ConfigureNotify:
656 if (event->xvisibility.window != XtWindow(video)) {
657 if (debug > 1)
658 fprintf(stderr,"video: root: %s%s\n",
659 event_names[event->type],did_refresh?" (ignored)":"");
660 if (!did_refresh)
661 configure_overlay();
662 }
663 break;
664 default:
665 if (debug > 1)
666 fprintf(stderr,"video: tv(+root): %s\n",
667 event_names[event->type]);
668 break;
669 }
670 }
671 }
672
673 void
674 video_overlay(int state)
675 {
676 if (state) {
677 conf = 1;
678 overlay_enabled = 1;
679 configure_overlay();
680 } else {
681 if (1 == overlay_enabled) {
682 #ifdef HAVE_LIBXV
683 if (have_xv) {
684 xv_video(XtWindow(video),0,0,0);
685 } else
686 #endif
687 {
688 overlay_on = 0;
689 if (f_drv & CAN_OVERLAY)
690 drv->overlay(h_drv,NULL,0,0,NULL,0,0);
691 overlay_refresh = ADD_TIMER(refresh_timer);
692 }
693 }
694 overlay_enabled = 0;
695 }
696 }
697
698 Widget
699 video_init(Widget parent, XVisualInfo *vinfo, WidgetClass class,
700 int args_bpp, int args_gl)
701 {
702 Window root = DefaultRootWindow(XtDisplay(parent));
703
704 swidth = XtScreen(parent)->width;
705 sheight = XtScreen(parent)->height;
706
707 x11_overlay_fmtid = x11_dpy_fmtid;
708 if (ImageByteOrder(XtDisplay(parent)) == MSBFirst) {
709 /* X-Server is BE */
710 switch(args_bpp) {
711 case 8: x11_overlay_fmtid = VIDEO_RGB08; break;
712 case 15: x11_overlay_fmtid = VIDEO_RGB15_BE; break;
713 case 16: x11_overlay_fmtid = VIDEO_RGB16_BE; break;
714 case 24: x11_overlay_fmtid = VIDEO_BGR24; break;
715 case 32: x11_overlay_fmtid = VIDEO_BGR32; break;
716 }
717 } else {
718 /* X-Server is LE */
719 switch(args_bpp) {
720 case 8: x11_overlay_fmtid = VIDEO_RGB08; break;
721 case 15: x11_overlay_fmtid = VIDEO_RGB15_LE; break;
722 case 16: x11_overlay_fmtid = VIDEO_RGB16_LE; break;
723 case 24: x11_overlay_fmtid = VIDEO_BGR24; break;
724 case 32: x11_overlay_fmtid = VIDEO_BGR32; break;
725 }
726 }
727
728 video_parent = parent;
729 video = XtVaCreateManagedWidget("tv",class,parent,NULL);
730
731 /* Shell widget -- need map, unmap, configure */
732 XtAddEventHandler(parent,
733 StructureNotifyMask,
734 True, video_event, NULL);
735
736 if (!have_xv) {
737 /* TV Widget -- need visibility, expose */
738 XtAddEventHandler(video,
739 VisibilityChangeMask |
740 StructureNotifyMask,
741 False, video_event, NULL);
742
743 /* root window -- need */
744 XSelectInput(XtDisplay(video),root,
745 VisibilityChangeMask |
746 SubstructureNotifyMask |
747 StructureNotifyMask);
748
749 XtRegisterDrawable(XtDisplay(video),root,video);
750 }
751
752 return video;
753 }
754
755 void
756 video_close(void)
757 {
758 Window root = DefaultRootWindow(XtDisplay(video));
759
760 if (overlay_refresh)
761 DEL_TIMER(overlay_refresh);
762 XSelectInput(XtDisplay(video),root,0);
763 XtUnregisterDrawable(XtDisplay(video),root);
764 }
765
|
This page was automatically generated by the
LXR engine.
|