1 /*
2  * x11 helper functions -- blit frames to the screen
3  *
4  */
5 
6 #include "config.h"
7 
8 #include <stdio.h>
9 #include <stdlib.h>
10 #include <strings.h>
11 #include <errno.h>
12 #include <assert.h>
13 #include <pthread.h>
14 #include <sys/ipc.h>
15 #include <sys/shm.h>
16 
17 #include <X11/Xlib.h>
18 #include <X11/Intrinsic.h>
19 #include <X11/extensions/XShm.h>
20 #ifdef HAVE_LIBXV
21 # include <X11/extensions/Xv.h>
22 # include <X11/extensions/Xvlib.h>
23 #endif
24 
25 #if HAVE_GL
26 # include <GL/gl.h>
27 # include <GL/glx.h>
28 #endif
29 
30 #include "grab-ng.h"
31 #include "blit.h"
32 
33 /* ------------------------------------------------------------------------ */
34 
35 extern XtAppContext    app_context;
36 extern int             debug;
37 
38 unsigned int           x11_dpy_fmtid;
39 
40 static int             display_bits = 0;
41 static unsigned int    display_bytes = 0;
42 static unsigned int    pixmap_bytes = 0;
43 static int             x11_byteswap = 0;
44 static int             no_mitshm = 0;
45 static int             gl_error = 0;
46 
47 #if HAVE_LIBXV
48 static int             ver, rel, req, ev, err;
49 static int             formats;
50 static int             adaptors;
51 static XvImageFormatValues  *fo;
52 static XvAdaptorInfo        *ai;
53 #endif
54 
55 static unsigned int    im_adaptor,im_port = UNSET;
56 static unsigned int    im_formats[VIDEO_FMT_COUNT];
57 
58 static struct SEARCHFORMAT {
59     unsigned int   depth;
60     int            order;
61     unsigned long  red;
62     unsigned long  green;
63     unsigned long  blue;
64     unsigned int   format;
65 } fmt[] = {
66     { 2, MSBFirst, 0x7c00,     0x03e0,     0x001f,     VIDEO_RGB15_BE },
67     { 2, MSBFirst, 0xf800,     0x07e0,     0x001f,     VIDEO_RGB16_BE },
68     { 2, LSBFirst, 0x7c00,     0x03e0,     0x001f,     VIDEO_RGB15_LE },
69     { 2, LSBFirst, 0xf800,     0x07e0,     0x001f,     VIDEO_RGB16_LE },
70 
71     { 3, LSBFirst, 0x00ff0000, 0x0000ff00, 0x000000ff, VIDEO_BGR24    },
72     { 3, LSBFirst, 0x000000ff, 0x0000ff00, 0x00ff0000, VIDEO_RGB24    },
73     { 3, MSBFirst, 0x00ff0000, 0x0000ff00, 0x000000ff, VIDEO_RGB24    },
74     { 3, MSBFirst, 0x000000ff, 0x0000ff00, 0x00ff0000, VIDEO_BGR24    },
75 
76     { 4, LSBFirst, 0x00ff0000, 0x0000ff00, 0x000000ff, VIDEO_BGR32    },
77     { 4, LSBFirst, 0x0000ff00, 0x00ff0000, 0xff000000, VIDEO_RGB32    },
78     { 4, MSBFirst, 0x00ff0000, 0x0000ff00, 0x000000ff, VIDEO_RGB32    },
79     { 4, MSBFirst, 0x0000ff00, 0x00ff0000, 0xff000000, VIDEO_BGR32    },
80 
81     { 2, -1,       0,          0,          0,          VIDEO_LUT2     },
82     { 4, -1,       0,          0,          0,          VIDEO_LUT4     },
83     { 0 /* END OF LIST */ },
84 };
85 
86 static int
catch_no_mitshm(Display * dpy,XErrorEvent * event)87 catch_no_mitshm(Display * dpy, XErrorEvent * event)
88 {
89     no_mitshm++;
90     return 0;
91 }
92 
93 static int
catch_gl_error(Display * dpy,XErrorEvent * event)94 catch_gl_error(Display * dpy, XErrorEvent * event)
95 {
96     fprintf(stderr,"WARNING: Your OpenGL setup is broken.\n");
97     gl_error++;
98     return 0;
99 }
100 
101 /* ------------------------------------------------------------------------ */
102 /* plain X11 stuff                                                          */
103 
104 Visual*
x11_find_visual(Display * dpy)105 x11_find_visual(Display *dpy)
106 {
107     XVisualInfo  *info, template;
108     Visual*      vi = CopyFromParent;
109     int          found,i;
110     char         *class;
111 
112     template.screen = XDefaultScreen(dpy);
113     info = XGetVisualInfo(dpy, VisualScreenMask,&template,&found);
114     for (i = 0; i < found; i++) {
115 	switch (info[i].class) {
116 	case StaticGray:   class = "StaticGray";  break;
117 	case GrayScale:    class = "GrayScale";   break;
118 	case StaticColor:  class = "StaticColor"; break;
119 	case PseudoColor:  class = "PseudoColor"; break;
120 	case TrueColor:    class = "TrueColor";   break;
121 	case DirectColor:  class = "DirectColor"; break;
122 	default:           class = "UNKNOWN";     break;
123 	}
124 	if (debug)
125 	    fprintf(stderr,"visual: id=0x%lx class=%d (%s), depth=%d\n",
126 		    info[i].visualid,info[i].class,class,info[i].depth);
127     }
128     for (i = 0; vi == CopyFromParent && i < found; i++)
129 	if (info[i].class == TrueColor && info[i].depth >= 15)
130 	    vi = info[i].visual;
131     for (i = 0; vi == CopyFromParent && i < found; i++)
132 	if (info[i].class == StaticGray && info[i].depth == 8)
133 	    vi = info[i].visual;
134     return vi;
135 }
136 
137 void
x11_init_visual(Display * dpy,XVisualInfo * vinfo)138 x11_init_visual(Display *dpy, XVisualInfo *vinfo)
139 {
140     XPixmapFormatValues *pf;
141     int                  i,n;
142     int                  format = 0;
143 
144     if (!XShmQueryExtension(dpy))
145 	no_mitshm = 1;
146 
147     display_bits = vinfo->depth;
148     display_bytes = (display_bits+7)/8;
149 
150     pf = XListPixmapFormats(dpy,&n);
151     for (i = 0; i < n; i++)
152 	if (pf[i].depth == display_bits)
153 	    pixmap_bytes = pf[i].bits_per_pixel/8;
154 
155     if (debug) {
156 	fprintf(stderr,"x11: color depth: "
157 		"%d bits, %d bytes - pixmap: %d bytes\n",
158 		display_bits,display_bytes,pixmap_bytes);
159 	if (vinfo->class == TrueColor || vinfo->class == DirectColor)
160 	    fprintf(stderr, "x11: color masks: "
161 		    "red=0x%08lx green=0x%08lx blue=0x%08lx\n",
162 		    vinfo->red_mask, vinfo->green_mask, vinfo->blue_mask);
163 	fprintf(stderr,"x11: server byte order: %s\n",
164 		ImageByteOrder(dpy)==LSBFirst ? "little endian":"big endian");
165 	fprintf(stderr,"x11: client byte order: %s\n",
166 		BYTE_ORDER==LITTLE_ENDIAN ? "little endian":"big endian");
167     }
168     if (ImageByteOrder(dpy)==LSBFirst && BYTE_ORDER!=LITTLE_ENDIAN)
169 	x11_byteswap=1;
170     if (ImageByteOrder(dpy)==MSBFirst && BYTE_ORDER!=BIG_ENDIAN)
171 	x11_byteswap=1;
172     if (vinfo->class == TrueColor /* || vinfo->class == DirectColor */) {
173 	/* pixmap format */
174 	for (i = 0; fmt[i].depth > 0; i++) {
175 	    if (fmt[i].depth  == pixmap_bytes                               &&
176 		(fmt[i].order == ImageByteOrder(dpy) || fmt[i].order == -1) &&
177 		(fmt[i].red   == vinfo->red_mask     || fmt[i].red   == 0)  &&
178 		(fmt[i].green == vinfo->green_mask   || fmt[i].green == 0)  &&
179 		(fmt[i].blue  == vinfo->blue_mask    || fmt[i].blue  == 0)) {
180 		x11_dpy_fmtid = fmt[i].format;
181 		break;
182 	    }
183 	}
184 	if (fmt[i].depth == 0) {
185 	    fprintf(stderr, "Huh?\n");
186 	    exit(1);
187 	}
188 	ng_lut_init(vinfo->red_mask, vinfo->green_mask, vinfo->blue_mask,
189 		    x11_dpy_fmtid,x11_byteswap);
190 	/* guess physical screen format */
191 	if (ImageByteOrder(dpy) == MSBFirst) {
192 	    switch (pixmap_bytes) {
193 	    case 2: format = (display_bits==15) ?
194 			VIDEO_RGB15_BE : VIDEO_RGB16_BE; break;
195 	    case 3: format = VIDEO_RGB24; break;
196 	    case 4: format = VIDEO_RGB32; break;
197 	    }
198 	} else {
199 	    switch (pixmap_bytes) {
200 	    case 2: format = (display_bits==15) ?
201 			VIDEO_RGB15_LE : VIDEO_RGB16_LE; break;
202 	    case 3: format = VIDEO_BGR24; break;
203 	    case 4: format = VIDEO_BGR32; break;
204 	    }
205 	}
206     }
207     if (vinfo->class == StaticGray && vinfo->depth == 8) {
208 	format = VIDEO_GRAY;
209     }
210     if (0 == format) {
211 	if (vinfo->class == PseudoColor && vinfo->depth == 8) {
212 	    fprintf(stderr,
213 "\n"
214 "8-bit Pseudocolor Visual (256 colors) is *not* supported.\n"
215 "You can startup X11 either with 15 bpp (or more)...\n"
216 "	xinit -- -bpp 16\n"
217 "... or with StaticGray visual:\n"
218 "	xinit -- -cc StaticGray\n"
219 	    );
220 	} else {
221 	    fprintf(stderr, "Sorry, I can't handle your strange display\n");
222 	}
223 	exit(1);
224     }
225     x11_dpy_fmtid = format;
226 }
227 
228 XImage*
x11_create_ximage(Display * dpy,XVisualInfo * vinfo,int width,int height,XShmSegmentInfo ** shm)229 x11_create_ximage(Display *dpy, XVisualInfo *vinfo,
230 		  int width, int height, XShmSegmentInfo **shm)
231 {
232     XImage          *ximage = NULL;
233     unsigned char   *ximage_data;
234     XShmSegmentInfo *shminfo = NULL;
235     void            *old_handler;
236 
237     if (no_mitshm)
238 	goto no_mitshm;
239 
240     assert(width > 0 && height > 0);
241 
242     old_handler = XSetErrorHandler(catch_no_mitshm);
243     shminfo = malloc(sizeof(XShmSegmentInfo));
244     memset(shminfo, 0, sizeof(XShmSegmentInfo));
245     ximage = XShmCreateImage(dpy,vinfo->visual,vinfo->depth,
246 			     ZPixmap, NULL,
247 			     shminfo, width, height);
248     if (NULL == ximage)
249 	goto shm_error;
250     shminfo->shmid = shmget(IPC_PRIVATE,
251 			    ximage->bytes_per_line * ximage->height,
252 			    IPC_CREAT | 0777);
253     if (-1 == shminfo->shmid) {
254 	perror("shmget [x11]");
255 	goto shm_error;
256     }
257     shminfo->shmaddr = (char *) shmat(shminfo->shmid, 0, 0);
258     if ((void *)-1 == shminfo->shmaddr) {
259 	perror("shmat");
260 	goto shm_error;
261     }
262     ximage->data = shminfo->shmaddr;
263     shminfo->readOnly = False;
264 
265     XShmAttach(dpy, shminfo);
266     XSync(dpy, False);
267     if (no_mitshm)
268 	goto shm_error;
269     shmctl(shminfo->shmid, IPC_RMID, 0);
270     XSetErrorHandler(old_handler);
271     *shm = shminfo;
272     return ximage;
273 
274  shm_error:
275     if (ximage) {
276 	XDestroyImage(ximage);
277 	ximage = NULL;
278     }
279     if ((void *)-1 != shminfo->shmaddr  &&  NULL != shminfo->shmaddr)
280 	shmdt(shminfo->shmaddr);
281     free(shminfo);
282     XSetErrorHandler(old_handler);
283     no_mitshm = 1;
284 
285  no_mitshm:
286     *shm = NULL;
287     if (NULL == (ximage_data = malloc(width * height * pixmap_bytes))) {
288 	fprintf(stderr,"out of memory\n");
289 	exit(1);
290     }
291     ximage = XCreateImage(dpy, vinfo->visual, vinfo->depth,
292 			  ZPixmap, 0, ximage_data,
293 			  width, height,
294 			  8, 0);
295     memset(ximage->data, 0, ximage->bytes_per_line * ximage->height);
296     return ximage;
297 }
298 
299 void
x11_destroy_ximage(Display * dpy,XImage * ximage,XShmSegmentInfo * shm)300 x11_destroy_ximage(Display *dpy, XImage *ximage, XShmSegmentInfo *shm)
301 {
302     if (shm && !no_mitshm) {
303 	XShmDetach(dpy, shm);
304 	XDestroyImage(ximage);
305 	shmdt(shm->shmaddr);
306 	free(shm);
307     } else
308 	XDestroyImage(ximage);
309 }
310 
x11_blit(Display * dpy,Drawable dr,GC gc,XImage * xi,int a,int b,int c,int d,int w,int h)311 void x11_blit(Display *dpy, Drawable dr, GC gc, XImage *xi,
312 	      int a, int b, int c, int d, int w, int h)
313 {
314     if (no_mitshm)
315 	XPutImage(dpy,dr,gc,xi,a,b,c,d,w,h);
316     else
317 	XShmPutImage(dpy,dr,gc,xi,a,b,c,d,w,h,True);
318 }
319 
320 Pixmap
x11_create_pixmap(Display * dpy,XVisualInfo * vinfo,struct ng_video_buf * buf)321 x11_create_pixmap(Display *dpy, XVisualInfo *vinfo, struct ng_video_buf *buf)
322 {
323     Pixmap          pixmap;
324     XImage          *ximage;
325     GC              gc;
326     XShmSegmentInfo *shm;
327     Screen          *scr = DefaultScreenOfDisplay(dpy);
328 
329     pixmap = XCreatePixmap(dpy,RootWindowOfScreen(scr),
330 			   buf->fmt.width, buf->fmt.height, vinfo->depth);
331 
332     gc = XCreateGC(dpy, pixmap, 0, NULL);
333 
334     if (NULL == (ximage = x11_create_ximage(dpy, vinfo, buf->fmt.width,
335 					    buf->fmt.height, &shm))) {
336 	XFreePixmap(dpy, pixmap);
337 	XFreeGC(dpy, gc);
338 	return 0;
339     }
340     memcpy(ximage->data,buf->data,buf->size);
341     x11_blit(dpy, pixmap, gc, ximage, 0, 0, 0, 0,
342 	     buf->fmt.width, buf->fmt.height);
343     x11_destroy_ximage(dpy, ximage, shm);
344     XFreeGC(dpy, gc);
345     return pixmap;
346 }
347 
348 /* ------------------------------------------------------------------------ */
349 /* XVideo extention code                                                    */
350 
351 #ifdef HAVE_LIBXV
xv_image_init(Display * dpy)352 void xv_image_init(Display *dpy)
353 {
354     int i;
355 
356     if (Success != XvQueryExtension(dpy,&ver,&rel,&req,&ev,&err)) {
357 	if (debug)
358 	    fprintf(stderr,"Xvideo: Server has no Xvideo extention support\n");
359 	return;
360     }
361     if (Success != XvQueryAdaptors(dpy,DefaultRootWindow(dpy),&adaptors,&ai)) {
362 	fprintf(stderr,"Xvideo: XvQueryAdaptors failed");
363 	return;
364     }
365     for (i = 0; i < adaptors; i++) {
366 	if ((ai[i].type & XvInputMask) &&
367 	    (ai[i].type & XvImageMask) &&
368 	    (im_port == UNSET)) {
369 	    im_port = ai[i].base_id;
370 	    im_adaptor = i;
371 	}
372     }
373     if (UNSET == im_port)
374 	return;
375 
376     fo = XvListImageFormats(dpy, im_port, &formats);
377     for(i = 0; i < formats; i++) {
378 	if (debug)
379 	    fprintf(stderr, "blit: xv: 0x%x (%c%c%c%c) %s",
380 		    fo[i].id,
381 		    (fo[i].id)       & 0xff,
382 		    (fo[i].id >>  8) & 0xff,
383 		    (fo[i].id >> 16) & 0xff,
384 		    (fo[i].id >> 24) & 0xff,
385 		    (fo[i].format == XvPacked) ? "packed" : "planar");
386 	if (0x32595559 == fo[i].id) {
387 	    im_formats[VIDEO_YUYV] = fo[i].id;
388 	    if (debug)
389 		fprintf(stderr," [ok: %s]",ng_vfmt_to_desc[VIDEO_YUYV]);
390 	}
391 	if (0x59565955 == fo[i].id) {
392 	    im_formats[VIDEO_UYVY] = fo[i].id;
393 	    if (debug)
394 		fprintf(stderr," [ok: %s]",ng_vfmt_to_desc[VIDEO_UYVY]);
395 	}
396 	if (0x30323449 == fo[i].id) {
397 	    im_formats[VIDEO_YUV420P] = fo[i].id;
398 	    if (debug)
399 		fprintf(stderr," [ok: %s]",ng_vfmt_to_desc[VIDEO_YUV420P]);
400 	}
401 	if (debug)
402 	    fprintf(stderr,"\n");
403     }
404 }
405 
406 XvImage*
xv_create_ximage(Display * dpy,int width,int height,int format,XShmSegmentInfo ** shm)407 xv_create_ximage(Display *dpy, int width, int height, int format,
408 		 XShmSegmentInfo **shm)
409 {
410     XvImage         *xvimage = NULL;
411     unsigned char   *ximage_data;
412     XShmSegmentInfo *shminfo = NULL;
413     void            *old_handler;
414 
415     if (no_mitshm)
416 	goto no_mitshm;
417 
418     old_handler = XSetErrorHandler(catch_no_mitshm);
419     shminfo = malloc(sizeof(XShmSegmentInfo));
420     memset(shminfo, 0, sizeof(XShmSegmentInfo));
421     xvimage = XvShmCreateImage(dpy, im_port, format, 0,
422 			       width, height, shminfo);
423     if (NULL == xvimage)
424 	goto shm_error;
425     shminfo->shmid = shmget(IPC_PRIVATE, xvimage->data_size,
426 			    IPC_CREAT | 0777);
427     if (-1 == shminfo->shmid) {
428 	perror("shmget [xv]");
429 	goto shm_error;
430     }
431     shminfo->shmaddr = (char *) shmat(shminfo->shmid, 0, 0);
432     if ((void *)-1 == shminfo->shmaddr) {
433 	perror("shmat");
434 	goto shm_error;
435     }
436     xvimage->data = shminfo->shmaddr;
437     shminfo->readOnly = False;
438 
439     XShmAttach(dpy, shminfo);
440     XSync(dpy, False);
441     if (no_mitshm)
442 	goto shm_error;
443     shmctl(shminfo->shmid, IPC_RMID, 0);
444     XSetErrorHandler(old_handler);
445     *shm = shminfo;
446     return xvimage;
447 
448 shm_error:
449     if (xvimage) {
450 	XFree(xvimage);
451 	xvimage = NULL;
452     }
453     if ((void *)-1 != shminfo->shmaddr  &&  NULL != shminfo->shmaddr)
454 	shmdt(shminfo->shmaddr);
455     free(shminfo);
456     XSetErrorHandler(old_handler);
457     no_mitshm = 1;
458 
459  no_mitshm:
460     *shm = NULL;
461     if (NULL == (ximage_data = malloc(width * height * 2))) {
462 	fprintf(stderr,"out of memory\n");
463 	exit(1);
464     }
465     xvimage = XvCreateImage(dpy, im_port, format, ximage_data,
466 			    width, height);
467     return xvimage;
468 }
469 
470 void
xv_destroy_ximage(Display * dpy,XvImage * xvimage,XShmSegmentInfo * shm)471 xv_destroy_ximage(Display *dpy, XvImage * xvimage, XShmSegmentInfo *shm)
472 {
473     if (shm && !no_mitshm) {
474 	XShmDetach(dpy, shm);
475 	XFree(xvimage);
476 	shmdt(shm->shmaddr);
477 	free(shm);
478     } else
479 	XFree(xvimage);
480 }
481 
xv_blit(Display * dpy,Drawable dr,GC gc,XvImage * xi,int a,int b,int c,int d,int x,int y,int w,int h)482 void xv_blit(Display *dpy, Drawable dr, GC gc, XvImage *xi,
483 	     int a, int b, int c, int d, int x, int y, int w, int h)
484 {
485     if (no_mitshm)
486 	XvPutImage(dpy,im_port,dr,gc,xi,a,b,c,d,x,y,w,h);
487     else
488 	XvShmPutImage(dpy,im_port,dr,gc,xi,a,b,c,d,x,y,w,h,True);
489 }
490 #endif
491 
492 /* ------------------------------------------------------------------------ */
493 /* OpenGL code                                                              */
494 
495 #if HAVE_GL
496 static int have_gl,max_gl;
497 static int gl_attrib[] = { GLX_RGBA,
498 			   GLX_RED_SIZE, 1,
499 			   GLX_GREEN_SIZE, 1,
500 			   GLX_BLUE_SIZE, 1,
501 			   GLX_DOUBLEBUFFER,
502 			   None };
503 
504 struct {
505     int  fmt;
506     int  type;
507     char *ext;
508 } gl_formats[VIDEO_FMT_COUNT] = {
509     [ VIDEO_RGB24 ] = {
510 	.fmt =  GL_RGB,
511 	.type = GL_UNSIGNED_BYTE,
512     },
513 #ifdef GL_EXT_bgra
514     [ VIDEO_BGR24 ] = {
515 	.fmt =  GL_BGR_EXT,
516 	.type = GL_UNSIGNED_BYTE,
517 	.ext =  "GL_EXT_bgra",
518     },
519     [ VIDEO_BGR32 ] = {
520 	.fmt =  GL_BGRA_EXT,
521 	.type = GL_UNSIGNED_BYTE,
522 	.ext =  "GL_EXT_bgra",
523     },
524 #endif
525 };
526 
gl_init(Widget widget)527 static int gl_init(Widget widget)
528 {
529     void *old_handler;
530     XVisualInfo *visinfo;
531     GLXContext ctx;
532 
533     if (debug)
534 	fprintf(stderr,"blit: gl: init\n");
535     visinfo = glXChooseVisual(XtDisplay(widget),
536 			      XScreenNumberOfScreen(XtScreen(widget)),
537 			      gl_attrib);
538     if (!visinfo) {
539 	if (debug)
540 	    fprintf(stderr,"blit: gl: can't get visual (rgb,db)\n");
541 	return -1;
542     }
543     ctx = glXCreateContext(XtDisplay(widget), visinfo, NULL, True);
544     if (!ctx) {
545 	if (debug)
546 	    fprintf(stderr,"blit: gl: can't create context\n");
547 	return -1;
548     }
549 
550     /* there is no point in using OpenGL for image scaling if it
551      * isn't hardware accelerated ... */
552     if (debug)
553 	fprintf(stderr, "blit: gl: DRI=%s\n",
554 		glXIsDirect(XtDisplay(widget), ctx) ? "Yes" : "No");
555     if (!glXIsDirect(XtDisplay(widget), ctx))
556 	return -1;
557 
558     old_handler = XSetErrorHandler(catch_gl_error);
559     glXMakeCurrent(XtDisplay(widget),XtWindow(widget),ctx);
560     XSync(XtDisplay(widget), False);
561     XSetErrorHandler(old_handler);
562     if (gl_error)
563 	return -1;
564 
565     have_gl = 1;
566     glGetIntegerv(GL_MAX_TEXTURE_SIZE,&max_gl);
567     if (debug)
568 	fprintf(stderr,"blit: gl: texture max size: %d\n",max_gl);
569     return 0;
570 }
571 
gl_ext(GLubyte * find)572 static int gl_ext(GLubyte *find)
573 {
574     int len = strlen(find);
575     const GLubyte *ext;
576     GLubyte *pos;
577 
578     ext = glGetString(GL_EXTENSIONS);
579     if (NULL == ext)
580 	return 0;
581     if (NULL == (pos = strstr(ext,find)))
582 	return 0;
583     if (pos != ext && pos[-1] != ' ')
584 	return 0;
585     if (pos[len] != ' ' && pos[len] != '\0')
586 	return 0;
587     if (debug)
588 	fprintf(stderr,"blit: gl: extention %s is available\n",find);
589     return 1;
590 }
591 
gl_resize(int iw,int ih,int ww,int wh,GLint * tex,int * tw,int * th,int fmt,int type)592 static int gl_resize(int iw, int ih, int ww, int wh,
593 		     GLint *tex, int *tw, int *th, int fmt, int type)
594 {
595     char *dummy;
596     int i;
597 
598     /* check against max size */
599     if (iw > max_gl)
600 	return -1;
601     if (ih > max_gl)
602 	return -1;
603 
604     /* textures have power-of-two x,y dimensions */
605     for (i = 0; iw >= (1 << i); i++)
606 	;
607     *tw = (1 << i);
608     for (i = 0; ih >= (1 << i); i++)
609 	;
610     *th = (1 << i);
611     if (debug)
612 	fprintf(stderr,"blit: gl: frame=%dx%d, texture=%dx%d\n",
613 		iw,ih,*tw,*th);
614 
615     glClearColor (0.0, 0.0, 0.0, 0.0);
616     glShadeModel(GL_FLAT);
617     glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
618 
619     glViewport(0, 0, ww, wh);
620     glMatrixMode(GL_PROJECTION);
621     glLoadIdentity();
622     glOrtho(0.0, ww, 0.0, wh, -1, 1);
623     glMatrixMode(GL_MODELVIEW);
624     glLoadIdentity();
625 
626     glGenTextures(1,tex);
627     glBindTexture(GL_TEXTURE_2D,*tex);
628     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
629     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
630     dummy = malloc((*tw)*(*th)*3);
631     memset(dummy,128,(*tw)*(*th)*3);
632     glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB,*tw,*th,0,
633 		 fmt,type,dummy);
634     free(dummy);
635 
636     return 0;
637 }
638 
gl_cleanup(GLint tex)639 static void gl_cleanup(GLint tex)
640 {
641     /* FIXME: del texture */
642 }
643 
gl_blit(Widget widget,char * rgbbuf,int iw,int ih,int ww,int wh,GLint tex,int tw,int th,int fmt,int type)644 static void gl_blit(Widget widget, char *rgbbuf,
645 		    int iw, int ih, int ww, int wh,
646 		    GLint tex, int tw, int th, int fmt, int type)
647 {
648     float x,y;
649 
650     glTexSubImage2D(GL_TEXTURE_2D, 0, 0,0,iw,ih, fmt,type,rgbbuf);
651     x = (float)iw/tw;
652     y = (float)ih/th;
653 
654     glEnable(GL_TEXTURE_2D);
655     glTexEnvf(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_DECAL);
656     glBegin(GL_QUADS);
657     glTexCoord2f(0,y);  glVertex3f(0,0,0);
658     glTexCoord2f(0,0);  glVertex3f(0,wh,0);
659     glTexCoord2f(x,0);  glVertex3f(ww,wh,0);
660     glTexCoord2f(x,y);  glVertex3f(ww,0,0);
661     glEnd();
662     glXSwapBuffers(XtDisplay(widget), XtWindow(widget));
663     glDisable(GL_TEXTURE_2D);
664 }
665 #endif
666 
667 /* ------------------------------------------------------------------------ */
668 /* video frame blitter                                                      */
669 
670 enum blit_status {
671     STATUS_UNKNOWN = 0,
672     STATUS_BROKEN  = 1,
673     STATUS_CONVERT = 2,
674     STATUS_XVIDEO  = 3,
675     STATUS_OPENGL  = 4,
676 };
677 
678 struct blit_state {
679     enum blit_status          status;
680     Widget                    widget;
681     Dimension                 win_width, win_height;
682     int                       wx,wy,ww,wh;
683     GC                        gc;
684     XVisualInfo               *vinfo;
685     struct ng_video_fmt       fmt;
686     struct ng_video_buf       buf;
687     struct ng_video_conv      *conv;
688     struct ng_convert_handle  *chandle;
689     XShmSegmentInfo           *shm;
690     XImage                    *ximage;
691 #ifdef HAVE_LIBXV
692     XvImage                   *xvimage;
693 #endif
694 #if HAVE_GL
695     GLint                     tex;
696     int                       tw,th;
697 #endif
698 };
699 
700 struct blit_state*
blit_init(Widget widget,XVisualInfo * vinfo,int use_gl)701 blit_init(Widget widget, XVisualInfo *vinfo, int use_gl)
702 {
703     struct blit_state *st;
704 
705     if (debug)
706 	fprintf(stderr,"blit: init\n");
707     BUG_ON(0 == XtWindow(widget), "no blit window");
708 
709     st = malloc(sizeof(*st));
710     memset(st,0,sizeof(*st));
711 
712     st->widget = widget;
713     st->vinfo  = vinfo;
714     st->gc     = XCreateGC(XtDisplay(st->widget),XtWindow(st->widget),0,NULL);
715 #ifdef HAVE_GL
716     if (use_gl)
717 	gl_init(st->widget);
718 #endif
719 
720     return st;
721 }
722 
blit_get_formats(struct blit_state * st,int * fmtids,int max)723 void blit_get_formats(struct blit_state *st, int *fmtids, int max)
724 {
725     struct ng_video_conv *conv;
726     int i, n=0;
727 
728     BUG_ON(NULL == st, "blit handle is NULL");
729 
730     /* Xvideo extention */
731 #ifdef HAVE_LIBXV
732     for (i = 0; i < VIDEO_FMT_COUNT; i++) {
733 	if (0 != im_formats[i])
734 	    fmtids[n++] = i;
735 	if (n == max)
736 	    return;
737     }
738 #endif
739 
740 #if HAVE_GL
741     /* OpenGL */
742     if (have_gl) {
743 	for (i = 0; i < VIDEO_FMT_COUNT; i++) {
744 	    if (0 != gl_formats[i].fmt  &&
745 		(NULL == gl_formats[i].ext || gl_ext(gl_formats[i].ext)))
746 		fmtids[n++] = i;
747 	    if (n == max)
748 		return;
749 	}
750     }
751 #endif
752 
753     /* plain X11 */
754     fmtids[n++] = x11_dpy_fmtid;
755     if (n == max)
756 	return;
757     for (i = 0;;) {
758 	conv = ng_conv_find_to(x11_dpy_fmtid, &i);
759 	if (NULL == conv)
760 	    break;
761 	fmtids[n++] = conv->fmtid_in;
762 	if (n == max)
763 	    return;
764     }
765     for (; n < max; n++)
766 	fmtids[n] = 0;
767 }
768 
blit_resize(struct blit_state * st,Dimension width,Dimension height)769 void blit_resize(struct blit_state *st, Dimension width, Dimension height)
770 {
771     if (debug)
772 	fprintf(stderr,"blit: resize %dx%d\n",width,height);
773     st->win_width  = width;
774     st->win_height = height;
775 
776     st->wx = 0;
777     st->wy = 0;
778     st->ww = st->win_width;
779     st->wh = st->win_height;
780     ng_ratio_fixup(&st->ww, &st->wh, &st->wx, &st->wy);
781 
782     blit_fini_frame(st);
783 }
784 
blit_init_frame(struct blit_state * st,struct ng_video_fmt * fmt)785 void blit_init_frame(struct blit_state *st, struct ng_video_fmt *fmt)
786 {
787     struct ng_video_conv *conv;
788     int i;
789 
790     /* Xvideo extention */
791 #ifdef HAVE_LIBXV
792     if (0 != im_formats[fmt->fmtid]) {
793 	st->xvimage = xv_create_ximage(XtDisplay(st->widget),
794 				       fmt->width, fmt->height,
795 				       im_formats[fmt->fmtid],
796 				       &st->shm);
797 	st->buf.fmt = *fmt;
798 	st->status  = STATUS_XVIDEO;
799 	if (debug)
800 	    fprintf(stderr,"blit: %dx%d/[%s] => Xvideo\n",
801 		    fmt->width, fmt->height, ng_vfmt_to_desc[fmt->fmtid]);
802 	return;
803     }
804 #endif
805 
806 #if HAVE_GL
807     /* OpenGL */
808     if (have_gl  &&  0 != gl_formats[fmt->fmtid].fmt  &&
809 	(NULL == gl_formats[fmt->fmtid].ext ||
810 	 gl_ext(gl_formats[fmt->fmtid].ext)) &&
811 	0 == gl_resize(fmt->width,fmt->height,
812 		       st->win_width,st->win_height,
813 		       &st->tex,&st->tw,&st->th,
814 		       gl_formats[fmt->fmtid].fmt,
815 		       gl_formats[fmt->fmtid].type)) {
816 	st->buf.fmt = *fmt;
817 	st->status  = STATUS_OPENGL;
818 	if (debug)
819 	    fprintf(stderr,"blit: %dx%d/[%s] => OpenGL\n",
820 		    fmt->width, fmt->height, ng_vfmt_to_desc[fmt->fmtid]);
821 	return;
822     }
823 #endif
824 
825     /* plain X11 */
826     st->ximage = x11_create_ximage(XtDisplay(st->widget), st->vinfo,
827 				   fmt->width, fmt->height,
828 				   &st->shm);
829     st->buf.data = st->ximage->data;
830     if (x11_dpy_fmtid == fmt->fmtid) {
831 	st->buf.fmt = *fmt;
832 	st->status  = STATUS_CONVERT;
833 	if (debug)
834 	    fprintf(stderr,"blit: %dx%d/[%s] => X11 direct\n",
835 		    fmt->width, fmt->height, ng_vfmt_to_desc[fmt->fmtid]);
836 	return;
837     }
838     for (i = 0;;) {
839 	conv = ng_conv_find_to(x11_dpy_fmtid, &i);
840 	if (NULL == conv) {
841 	    st->status = STATUS_BROKEN;
842 	    if (debug)
843 		fprintf(stderr,"blit: %dx%d/[%s] => can't display\n",
844 			fmt->width, fmt->height, ng_vfmt_to_desc[fmt->fmtid]);
845 	    return;
846 	}
847 	if (debug)
848 	    fprintf(stderr,"blit test: %s\n",ng_vfmt_to_desc[conv->fmtid_in]);
849 	if (conv->fmtid_in != fmt->fmtid)
850 	    continue;
851 	break;
852     }
853     st->buf.fmt = *fmt;
854     st->status  = STATUS_CONVERT;
855     st->conv    = conv;
856     st->buf.fmt.fmtid  = x11_dpy_fmtid;
857     st->buf.fmt.bytesperline = 0;
858     st->chandle = ng_convert_alloc(st->conv,fmt,&st->buf.fmt);
859     ng_convert_init(st->chandle);
860     if (debug)
861 	fprintf(stderr,"blit: %dx%d/[%s] => X11 via [%s]\n",
862 		fmt->width, fmt->height, ng_vfmt_to_desc[fmt->fmtid],
863 		ng_vfmt_to_desc[st->buf.fmt.fmtid]);
864     return;
865 }
866 
blit_fini_frame(struct blit_state * st)867 void blit_fini_frame(struct blit_state *st)
868 {
869     switch (st->status) {
870     case STATUS_CONVERT:
871 	if (st->chandle) {
872 	    ng_convert_fini(st->chandle);
873 	    st->chandle = NULL;
874 	}
875 	if (st->ximage) {
876 	    x11_destroy_ximage(XtDisplay(st->widget),st->ximage,st->shm);
877 	    st->ximage = NULL;
878 	}
879 	break;
880 
881 #if HAVE_LIBXV
882     case STATUS_XVIDEO:
883 	if (st->xvimage) {
884 	    xv_destroy_ximage(XtDisplay(st->widget),st->xvimage,st->shm);
885 	    st->xvimage = NULL;
886 	}
887 	XvStopVideo(XtDisplay(st->widget), im_port, XtWindow(st->widget));
888 	break;
889 #endif
890 
891 #if HAVE_GL
892     case STATUS_OPENGL:
893 	gl_cleanup(st->tex);
894 	break;
895 #endif
896 
897     case STATUS_UNKNOWN:
898     case STATUS_BROKEN:
899 	break;
900     }
901     memset(&st->fmt,0,sizeof(st->fmt));
902     memset(&st->buf,0,sizeof(st->buf));
903     st->status = STATUS_UNKNOWN;
904 }
905 
blit_fini(struct blit_state * st)906 void blit_fini(struct blit_state *st)
907 {
908     free(st);
909 }
910 
blit_putframe(struct blit_state * st,struct ng_video_buf * buf)911 void blit_putframe(struct blit_state *st, struct ng_video_buf *buf)
912 {
913     if (st->fmt.fmtid  != buf->fmt.fmtid &&
914 	st->fmt.width  != buf->fmt.width &&
915 	st->fmt.height != buf->fmt.height) {
916 	blit_fini_frame(st);
917 	blit_init_frame(st,&buf->fmt);
918 	st->fmt = buf->fmt;
919     }
920 
921     if (debug > 1)
922 	fprintf(stderr,"blit: putframe\n");
923     switch (st->status) {
924     case STATUS_CONVERT:
925 	if (NULL == st->chandle) {
926 	    memcpy(st->ximage->data,buf->data,buf->size);
927 	    ng_release_video_buf(buf);
928 	} else {
929 	    buf = ng_convert_frame(st->chandle,&st->buf,buf);
930 	}
931 	x11_blit(XtDisplay(st->widget), XtWindow(st->widget),
932 		 st->gc,st->ximage,0,0,
933 		 (st->win_width  - st->buf.fmt.width)  >> 1,
934 		 (st->win_height - st->buf.fmt.height) >> 1,
935 		 st->buf.fmt.width, st->buf.fmt.height);
936 	break;
937 
938 #ifdef HAVE_LIBXV
939     case STATUS_XVIDEO:
940 	memcpy(st->xvimage->data,buf->data,
941 	    buf->size < st->xvimage->data_size ? buf->size : st->xvimage->data_size);
942 	ng_release_video_buf(buf);
943 	xv_blit(XtDisplay(st->widget), XtWindow(st->widget),
944 		st->gc, st->xvimage,
945 		0, 0,  st->buf.fmt.width, st->buf.fmt.height,
946 		st->wx, st->wy, st->ww, st->wh);
947 	break;
948 #endif
949 
950 #if HAVE_GL
951     case STATUS_OPENGL:
952 	gl_blit(st->widget,buf->data,
953 		st->buf.fmt.width, st->buf.fmt.height,
954 		st->win_width, st->win_height,
955 		st->tex, st->tw, st->th,
956 		gl_formats[buf->fmt.fmtid].fmt,
957 		gl_formats[buf->fmt.fmtid].type);
958 	ng_release_video_buf(buf);
959 	break;
960 #endif
961 
962     case STATUS_UNKNOWN:
963     case STATUS_BROKEN:
964 	if (debug > 1)
965 	    fprintf(stderr,"blit: putframe: oops: status = %d\n",st->status);
966 	ng_release_video_buf(buf);
967 	break;
968     }
969 }
970