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