1 /*****************************************************************************
2 * vout_x11.c: X11 video output display method
3 *****************************************************************************
4 * Copyright (C) 1998, 1999, 2000 VideoLAN
5 * $Id: vout_x11.c,v 1.10 2001/02/14 07:48:18 sam Exp $
7 * Authors: Vincent Seguin <seguin@via.ecp.fr>
8 * Samuel Hocevar <sam@zoy.org>
10 * This program is free software; you can redistribute it and/or modify
11 * it under the terms of the GNU General Public License as published by
12 * the Free Software Foundation; either version 2 of the License, or
13 * (at your option) any later version.
15 * This program is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU General Public License for more details.
20 * You should have received a copy of the GNU General Public License
21 * along with this program; if not, write to the Free Software
22 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA.
23 *****************************************************************************/
25 /*****************************************************************************
27 *****************************************************************************/
30 #include <errno.h> /* ENOMEM */
31 #include <stdlib.h> /* free() */
32 #include <string.h> /* strerror() */
34 #ifdef HAVE_MACHINE_PARAM_H
36 #include <machine/param.h>
37 #include <sys/types.h> /* typedef ushort */
41 #include <sys/shm.h> /* shmget(), shmctl() */
43 #include <X11/Xutil.h>
44 #include <X11/keysym.h>
45 #include <X11/extensions/XShm.h>
55 #include "video_output.h"
57 #include "interface.h"
60 /*****************************************************************************
61 * vout_sys_t: video output X11 method descriptor
62 *****************************************************************************
63 * This structure is part of the video output thread descriptor.
64 * It describes the X11 specific properties of an output thread. X11 video
65 * output is performed through regular resizable windows. Windows can be
66 * dynamically resized to adapt to the size of the streams.
67 *****************************************************************************/
68 typedef struct vout_sys_s
71 boolean_t b_shm; /* shared memory extension flag */
73 /* Internal settings and properties */
74 Display * p_display; /* display pointer */
75 Visual * p_visual; /* visual pointer */
76 int i_screen; /* screen number */
77 Window root_window; /* root window */
78 Window window; /* window instance handler */
79 GC gc; /* graphic context instance handler */
80 Colormap colormap; /* colormap used (8bpp only) */
82 /* Display buffers and shared memory information */
83 XImage * p_ximage[2]; /* XImage pointer */
84 XShmSegmentInfo shm_info[2]; /* shared memory zone information */
86 /* X11 generic properties */
88 Atom wm_delete_window;
90 int i_width; /* width of main window */
91 int i_height; /* height of main window */
93 /* Screen saver properties */
94 int i_ss_count; /* enabling/disabling count */
95 int i_ss_timeout; /* timeout */
96 int i_ss_interval; /* interval between changes */
97 int i_ss_blanking; /* blanking mode */
98 int i_ss_exposure; /* exposure mode */
100 /* Mouse pointer properties */
101 boolean_t b_mouse; /* is the mouse pointer displayed ? */
105 /*****************************************************************************
107 *****************************************************************************/
108 static int vout_Probe ( probedata_t *p_data );
109 static int vout_Create ( struct vout_thread_s * );
110 static int vout_Init ( struct vout_thread_s * );
111 static void vout_End ( struct vout_thread_s * );
112 static void vout_Destroy ( struct vout_thread_s * );
113 static int vout_Manage ( struct vout_thread_s * );
114 static void vout_Display ( struct vout_thread_s * );
115 static void vout_SetPalette( struct vout_thread_s *, u16*, u16*, u16*, u16* );
117 static int X11OpenDisplay ( vout_thread_t *p_vout );
118 static void X11CloseDisplay ( vout_thread_t *p_vout );
119 static int X11CreateWindow ( vout_thread_t *p_vout );
120 static void X11DestroyWindow ( vout_thread_t *p_vout );
121 static int X11CreateImage ( vout_thread_t *p_vout, XImage **pp_ximage );
122 static void X11DestroyImage ( XImage *p_ximage );
123 static int X11CreateShmImage ( vout_thread_t *p_vout, XImage **pp_ximage,
124 XShmSegmentInfo *p_shm_info );
125 static void X11DestroyShmImage ( vout_thread_t *p_vout, XImage *p_ximage,
126 XShmSegmentInfo *p_shm_info );
128 /*****************************************************************************
129 * Functions exported as capabilities. They are declared as static so that
130 * we don't pollute the namespace too much.
131 *****************************************************************************/
132 void vout_getfunctions( function_list_t * p_function_list )
134 p_function_list->pf_probe = vout_Probe;
135 p_function_list->functions.vout.pf_create = vout_Create;
136 p_function_list->functions.vout.pf_init = vout_Init;
137 p_function_list->functions.vout.pf_end = vout_End;
138 p_function_list->functions.vout.pf_destroy = vout_Destroy;
139 p_function_list->functions.vout.pf_manage = vout_Manage;
140 p_function_list->functions.vout.pf_display = vout_Display;
141 p_function_list->functions.vout.pf_setpalette = vout_SetPalette;
144 /*****************************************************************************
145 * vout_Probe: probe the video driver and return a score
146 *****************************************************************************
147 * This function tries to initialize SDL and returns a score to the
148 * plugin manager so that it can select the best plugin.
149 *****************************************************************************/
150 static int vout_Probe( probedata_t *p_data )
152 if( TestMethod( VOUT_METHOD_VAR, "x11" ) )
160 /*****************************************************************************
161 * vout_Create: allocate X11 video thread output method
162 *****************************************************************************
163 * This function allocate and initialize a X11 vout method. It uses some of the
164 * vout properties to choose the window size, and change them according to the
165 * actual properties of the display.
166 *****************************************************************************/
167 static int vout_Create( vout_thread_t *p_vout )
169 /* Allocate structure */
170 p_vout->p_sys = malloc( sizeof( vout_sys_t ) );
171 if( p_vout->p_sys == NULL )
173 intf_ErrMsg("error: %s", strerror(ENOMEM) );
177 /* Open and initialize device. This function issues its own error messages.
178 * Since XLib is usually not thread-safe, we can't use the same display
179 * pointer than the interface or another thread. However, the root window
180 * id is still valid. */
181 if( X11OpenDisplay( p_vout ) )
183 intf_ErrMsg("error: can't initialize X11 display" );
184 free( p_vout->p_sys );
191 /*****************************************************************************
192 * vout_Init: initialize X11 video thread output method
193 *****************************************************************************
194 * This function create the XImages needed by the output thread. It is called
195 * at the beginning of the thread, but also each time the window is resized.
196 *****************************************************************************/
197 static int vout_Init( vout_thread_t *p_vout )
201 /* Create XImages using XShm extension - on failure, fall back to regular
202 * way (and destroy the first image if it was created successfully) */
203 if( p_vout->p_sys->b_shm )
205 /* Create first image */
206 i_err = X11CreateShmImage( p_vout, &p_vout->p_sys->p_ximage[0],
207 &p_vout->p_sys->shm_info[0] );
208 if( !i_err ) /* first image has been created */
210 /* Create second image */
211 if( X11CreateShmImage( p_vout, &p_vout->p_sys->p_ximage[1],
212 &p_vout->p_sys->shm_info[1] ) )
213 { /* error creating the second image */
214 X11DestroyShmImage( p_vout, p_vout->p_sys->p_ximage[0],
215 &p_vout->p_sys->shm_info[0] );
219 if( i_err ) /* an error occured */
221 intf_Msg("vout: XShm video sextension deactivated" );
222 p_vout->p_sys->b_shm = 0;
226 /* Create XImages without XShm extension */
227 if( !p_vout->p_sys->b_shm )
229 if( X11CreateImage( p_vout, &p_vout->p_sys->p_ximage[0] ) )
231 intf_ErrMsg("error: can't create images");
232 p_vout->p_sys->p_ximage[0] = NULL;
233 p_vout->p_sys->p_ximage[1] = NULL;
236 if( X11CreateImage( p_vout, &p_vout->p_sys->p_ximage[1] ) )
238 intf_ErrMsg("error: can't create images");
239 X11DestroyImage( p_vout->p_sys->p_ximage[0] );
240 p_vout->p_sys->p_ximage[0] = NULL;
241 p_vout->p_sys->p_ximage[1] = NULL;
246 /* Set bytes per line and initialize buffers */
247 p_vout->i_bytes_per_line = p_vout->p_sys->p_ximage[0]->bytes_per_line;
248 vout_SetBuffers( p_vout, p_vout->p_sys->p_ximage[ 0 ]->data,
249 p_vout->p_sys->p_ximage[ 1 ]->data );
253 /*****************************************************************************
254 * vout_End: terminate X11 video thread output method
255 *****************************************************************************
256 * Destroy the X11 XImages created by vout_Init. It is called at the end of
257 * the thread, but also each time the window is resized.
258 *****************************************************************************/
259 static void vout_End( vout_thread_t *p_vout )
261 if( p_vout->p_sys->b_shm ) /* Shm XImages... */
263 X11DestroyShmImage( p_vout, p_vout->p_sys->p_ximage[0],
264 &p_vout->p_sys->shm_info[0] );
265 X11DestroyShmImage( p_vout, p_vout->p_sys->p_ximage[1],
266 &p_vout->p_sys->shm_info[1] );
268 else /* ...or regular XImages */
270 X11DestroyImage( p_vout->p_sys->p_ximage[0] );
271 X11DestroyImage( p_vout->p_sys->p_ximage[1] );
275 /*****************************************************************************
276 * vout_Destroy: destroy X11 video thread output method
277 *****************************************************************************
278 * Terminate an output method created by vout_CreateOutputMethod
279 *****************************************************************************/
280 static void vout_Destroy( vout_thread_t *p_vout )
282 X11CloseDisplay( p_vout );
283 free( p_vout->p_sys );
286 /*****************************************************************************
287 * vout_Manage: handle X11 events
288 *****************************************************************************
289 * This function should be called regularly by video output thread. It manages
290 * X11 events and allows window resizing. It returns a non null value on
292 *****************************************************************************/
293 static int vout_Manage( vout_thread_t *p_vout )
296 * Color/Grayscale or gamma change: in 8bpp, just change the colormap
298 if( (p_vout->i_changes & VOUT_GRAYSCALE_CHANGE)
299 && (p_vout->i_screen_depth == 8) )
301 /* FIXME: clear flags ?? */
307 if( p_vout->i_changes & VOUT_SIZE_CHANGE )
309 intf_DbgMsg("resizing window");
310 p_vout->i_changes &= ~VOUT_SIZE_CHANGE;
313 XResizeWindow( p_vout->p_sys->p_display, p_vout->p_sys->window,
314 p_vout->i_width, p_vout->i_height );
316 /* Destroy XImages to change their size */
319 /* Recreate XImages. If SysInit failed, the thread can't go on. */
320 if( vout_Init( p_vout ) )
322 intf_ErrMsg("error: can't resize display");
326 /* Tell the video output thread that it will need to rebuild YUV
327 * tables. This is needed since conversion buffer size may have
329 p_vout->i_changes |= VOUT_YUV_CHANGE;
330 intf_Msg("vout: video display resized (%dx%d)", p_vout->i_width, p_vout->i_height);
336 /*****************************************************************************
337 * vout_Display: displays previously rendered output
338 *****************************************************************************
339 * This function send the currently rendered image to X11 server, wait until
340 * it is displayed and switch the two rendering buffer, preparing next frame.
341 *****************************************************************************/
342 static void vout_Display( vout_thread_t *p_vout )
344 if( p_vout->p_sys->b_shm) /* XShm is used */
346 /* Display rendered image using shared memory extension */
347 XShmPutImage(p_vout->p_sys->p_display, p_vout->p_sys->window, p_vout->p_sys->gc,
348 p_vout->p_sys->p_ximage[ p_vout->i_buffer_index ],
350 p_vout->p_sys->p_ximage[ p_vout->i_buffer_index ]->width,
351 p_vout->p_sys->p_ximage[ p_vout->i_buffer_index ]->height, True);
353 /* Send the order to the X server */
354 XSync(p_vout->p_sys->p_display, False);
356 else /* regular X11 capabilities are used */
358 XPutImage(p_vout->p_sys->p_display, p_vout->p_sys->window, p_vout->p_sys->gc,
359 p_vout->p_sys->p_ximage[ p_vout->i_buffer_index ],
361 p_vout->p_sys->p_ximage[ p_vout->i_buffer_index ]->width,
362 p_vout->p_sys->p_ximage[ p_vout->i_buffer_index ]->height);
364 /* Send the order to the X server */
365 XSync(p_vout->p_sys->p_display, False);
369 /*****************************************************************************
370 * vout_SetPalette: sets an 8 bpp palette
371 *****************************************************************************
372 * This function sets the palette given as an argument. It does not return
373 * anything, but could later send information on which colors it was unable
375 *****************************************************************************/
376 static void vout_SetPalette( p_vout_thread_t p_vout,
377 u16 *red, u16 *green, u16 *blue, u16 *transp )
382 intf_DbgMsg( "Palette change called" );
384 /* allocate palette */
385 for( i = 0; i < 255; i++ )
387 /* kludge: colors are indexed reversely because color 255 seems
388 * to be reserved for black even if we try to set it to white */
389 color[i].pixel = 255-i;
391 color[i].flags = DoRed|DoGreen|DoBlue;
392 color[i].red = red[255-i];
393 color[i].blue = blue[255-i];
394 color[i].green = green[255-i];
397 XStoreColors( p_vout->p_sys->p_display, p_vout->p_sys->colormap, color, 256 );
400 /* following functions are local */
402 /*****************************************************************************
403 * X11OpenDisplay: open and initialize X11 device
404 *****************************************************************************
405 * Create a window according to video output given size, and set other
406 * properties according to the display properties.
407 *****************************************************************************/
408 static int X11OpenDisplay( vout_thread_t *p_vout )
410 XPixmapFormatValues * p_xpixmap_format; /* pixmap formats */
411 XVisualInfo * p_xvisual; /* visuals informations */
412 XVisualInfo xvisual_template; /* visual template */
413 int i_count; /* array size */
416 p_vout->p_sys->p_display = XOpenDisplay( psz_display );
417 if( p_vout->p_sys->p_display == NULL )
419 intf_ErrMsg("error: can't open display %s", psz_display );
423 /* Initialize structure */
424 p_vout->p_sys->root_window = root_window;
425 p_vout->p_sys->b_shm = (XShmQueryExtension(p_vout->p_sys->p_display) == True);
426 p_vout->p_sys->i_screen = DefaultScreen( p_vout->p_sys->p_display );
427 if( !p_vout->p_sys->b_shm )
429 intf_Msg("vout: XShm video extension is not available");
432 /* Get screen depth */
433 p_vout->i_screen_depth = XDefaultDepth( p_vout->p_sys->p_display, p_vout->p_sys->i_screen );
434 switch( p_vout->i_screen_depth )
438 * Screen depth is 8bpp. Use PseudoColor visual with private colormap.
440 xvisual_template.screen = p_vout->p_sys->i_screen;
441 xvisual_template.class = DirectColor;
442 p_xvisual = XGetVisualInfo( p_vout->p_sys->p_display, VisualScreenMask | VisualClassMask,
443 &xvisual_template, &i_count );
444 if( p_xvisual == NULL )
446 intf_ErrMsg("error: no PseudoColor visual available");
447 XCloseDisplay( p_vout->p_sys->p_display );
450 p_vout->i_bytes_per_pixel = 1;
452 /* put the colormap in place */
453 p_vout->p_sys->colormap = *(Colormap *)p_data;
460 * Screen depth is higher than 8bpp. TrueColor visual is used.
462 xvisual_template.screen = p_vout->p_sys->i_screen;
463 xvisual_template.class = TrueColor;
464 p_xvisual = XGetVisualInfo( p_vout->p_sys->p_display, VisualScreenMask | VisualClassMask,
465 &xvisual_template, &i_count );
466 if( p_xvisual == NULL )
468 intf_ErrMsg("error: no TrueColor visual available");
469 XCloseDisplay( p_vout->p_sys->p_display );
472 p_vout->i_red_mask = p_xvisual->red_mask;
473 p_vout->i_green_mask = p_xvisual->green_mask;
474 p_vout->i_blue_mask = p_xvisual->blue_mask;
476 /* There is no difference yet between 3 and 4 Bpp. The only way to find
477 * the actual number of bytes per pixel is to list supported pixmap
479 p_xpixmap_format = XListPixmapFormats( p_vout->p_sys->p_display, &i_count );
482 /* Under XFree4.0, the list contains pixmap formats available through
483 * all video depths ; so we have to check against current depth. */
484 p_vout->i_bytes_per_pixel = 0;
485 for( ; i_count-- ; p_xpixmap_format++ )
487 if( p_xpixmap_format->depth == p_vout->i_screen_depth )
489 if( p_xpixmap_format->bits_per_pixel / 8 > p_vout->i_bytes_per_pixel )
491 p_vout->i_bytes_per_pixel = p_xpixmap_format->bits_per_pixel / 8;
497 p_vout->p_sys->p_visual = p_xvisual->visual;
500 /* Create a window */
501 if( X11CreateWindow( p_vout ) )
503 intf_ErrMsg("error: can't open a window");
504 XCloseDisplay( p_vout->p_sys->p_display );
510 /*****************************************************************************
511 * X11CloseDisplay: close X11 device
512 *****************************************************************************
513 * Returns all resources allocated by X11OpenDisplay and restore the original
514 * state of the display.
515 *****************************************************************************/
516 static void X11CloseDisplay( vout_thread_t *p_vout )
518 /* Destroy colormap */
519 if( p_vout->i_screen_depth == 8 )
521 XFreeColormap( p_vout->p_sys->p_display, p_vout->p_sys->colormap );
525 X11DestroyWindow( p_vout );
527 /* FIXME: We should close the display here, but X returns an error. */
528 //XCloseDisplay( p_vout->p_sys->p_display );
531 /*****************************************************************************
532 * X11CreateWindow: create X11 vout window
533 *****************************************************************************
534 * The video output window will be created. Normally, this window is wether
535 * full screen or part of a parent window. Therefore, it does not need a
536 * title or other hints. Thery are still supplied in case the window would be
537 * spawned as a standalone one by the interface.
538 *****************************************************************************/
539 static int X11CreateWindow( vout_thread_t *p_vout )
541 XSetWindowAttributes xwindow_attributes; /* window attributes */
542 XGCValues xgcvalues; /* graphic context configuration */
543 XEvent xevent; /* first events */
544 boolean_t b_expose; /* 'expose' event received */
545 boolean_t b_map_notify; /* 'map_notify' event received */
547 /* Prepare window attributes */
548 xwindow_attributes.backing_store = Always; /* save the hidden part */
550 /* Create the window and set hints */
551 p_vout->p_sys->window = XCreateSimpleWindow( p_vout->p_sys->p_display,
552 p_vout->p_sys->root_window,
554 p_vout->i_width, p_vout->i_height,
556 XSelectInput( p_vout->p_sys->p_display, p_vout->p_sys->window,
557 ExposureMask | StructureNotifyMask );
558 XChangeWindowAttributes( p_vout->p_sys->p_display, p_vout->p_sys->window,
559 CWBackingStore, &xwindow_attributes);
561 /* Creation of a graphic context that doesn't generate a GraphicsExpose event
562 when using functions like XCopyArea */
563 xgcvalues.graphics_exposures = False;
564 p_vout->p_sys->gc = XCreateGC( p_vout->p_sys->p_display, p_vout->p_sys->window,
565 GCGraphicsExposures, &xgcvalues);
567 /* Send orders to server, and wait until window is displayed - two events
568 * must be received: a MapNotify event, an Expose event allowing drawing in the
572 XMapWindow( p_vout->p_sys->p_display, p_vout->p_sys->window);
575 XNextEvent( p_vout->p_sys->p_display, &xevent);
576 if( (xevent.type == Expose)
577 && (xevent.xexpose.window == p_vout->p_sys->window) )
581 else if( (xevent.type == MapNotify)
582 && (xevent.xmap.window == p_vout->p_sys->window) )
587 while( !( b_expose && b_map_notify ) );
588 XSelectInput( p_vout->p_sys->p_display, p_vout->p_sys->window, 0 );
590 /* At this stage, the window is open, displayed, and ready to receive
595 /*****************************************************************************
596 * X11DestroyWindow: destroy X11 window
597 *****************************************************************************
598 * Destroy an X11 window created by vout_CreateWindow
599 *****************************************************************************/
600 static void X11DestroyWindow( vout_thread_t *p_vout )
602 XUnmapWindow( p_vout->p_sys->p_display, p_vout->p_sys->window );
603 XFreeGC( p_vout->p_sys->p_display, p_vout->p_sys->gc );
604 XDestroyWindow( p_vout->p_sys->p_display, p_vout->p_sys->window );
607 /*****************************************************************************
608 * X11CreateImage: create an XImage
609 *****************************************************************************
610 * Create a simple XImage used as a buffer.
611 *****************************************************************************/
612 static int X11CreateImage( vout_thread_t *p_vout, XImage **pp_ximage )
614 byte_t * pb_data; /* image data storage zone */
615 int i_quantum; /* XImage quantum (see below) */
617 /* Allocate memory for image */
618 p_vout->i_bytes_per_line = p_vout->i_width * p_vout->i_bytes_per_pixel;
619 pb_data = (byte_t *) malloc( p_vout->i_bytes_per_line * p_vout->i_height );
620 if( !pb_data ) /* error */
622 intf_ErrMsg("error: %s", strerror(ENOMEM));
626 /* Optimize the quantum of a scanline regarding its size - the quantum is
627 a diviser of the number of bits between the start of two scanlines. */
628 if( !(( p_vout->i_bytes_per_line ) % 32) )
634 if( !(( p_vout->i_bytes_per_line ) % 16) )
645 *pp_ximage = XCreateImage( p_vout->p_sys->p_display, p_vout->p_sys->p_visual,
646 p_vout->i_screen_depth, ZPixmap, 0, pb_data,
647 p_vout->i_width, p_vout->i_height, i_quantum, 0);
648 if(! *pp_ximage ) /* error */
650 intf_ErrMsg( "error: XCreateImage() failed" );
658 /*****************************************************************************
659 * X11CreateShmImage: create an XImage using shared memory extension
660 *****************************************************************************
661 * Prepare an XImage for DisplayX11ShmImage function.
662 * The order of the operations respects the recommandations of the mit-shm
663 * document by J.Corbet and K.Packard. Most of the parameters were copied from
665 *****************************************************************************/
666 static int X11CreateShmImage( vout_thread_t *p_vout, XImage **pp_ximage,
667 XShmSegmentInfo *p_shm_info)
670 *pp_ximage = XShmCreateImage( p_vout->p_sys->p_display, p_vout->p_sys->p_visual,
671 p_vout->i_screen_depth, ZPixmap, 0,
672 p_shm_info, p_vout->i_width, p_vout->i_height );
673 if(! *pp_ximage ) /* error */
675 intf_ErrMsg("error: XShmCreateImage() failed");
679 /* Allocate shared memory segment - 0777 set the access permission
680 * rights (like umask), they are not yet supported by X servers */
681 p_shm_info->shmid = shmget( IPC_PRIVATE,
682 (*pp_ximage)->bytes_per_line * (*pp_ximage)->height,
684 if( p_shm_info->shmid < 0) /* error */
686 intf_ErrMsg("error: can't allocate shared image data (%s)",
688 XDestroyImage( *pp_ximage );
692 /* Attach shared memory segment to process (read/write) */
693 p_shm_info->shmaddr = (*pp_ximage)->data = shmat(p_shm_info->shmid, 0, 0);
694 if(! p_shm_info->shmaddr )
696 intf_ErrMsg("error: can't attach shared memory (%s)",
698 shmctl( p_shm_info->shmid, IPC_RMID, 0 ); /* free shared memory */
699 XDestroyImage( *pp_ximage );
703 /* Mark the shm segment to be removed when there will be no more
704 * attachements, so it is automatic on process exit or after shmdt */
705 shmctl( p_shm_info->shmid, IPC_RMID, 0 );
707 /* Attach shared memory segment to X server (read only) */
708 p_shm_info->readOnly = True;
709 if( XShmAttach( p_vout->p_sys->p_display, p_shm_info ) == False ) /* error */
711 intf_ErrMsg("error: can't attach shared memory to X11 server");
712 shmdt( p_shm_info->shmaddr ); /* detach shared memory from process
713 * and automatic free */
714 XDestroyImage( *pp_ximage );
718 /* Send image to X server. This instruction is required, since having
719 * built a Shm XImage and not using it causes an error on XCloseDisplay */
720 XFlush( p_vout->p_sys->p_display );
724 /*****************************************************************************
725 * X11DestroyImage: destroy an XImage
726 *****************************************************************************
727 * Destroy XImage AND associated data. If pointer is NULL, the image won't be
728 * destroyed (see vout_ManageOutputMethod())
729 *****************************************************************************/
730 static void X11DestroyImage( XImage *p_ximage )
732 if( p_ximage != NULL )
734 XDestroyImage( p_ximage ); /* no free() required */
738 /*****************************************************************************
740 *****************************************************************************
741 * Destroy XImage AND associated data. Detach shared memory segment from
742 * server and process, then free it. If pointer is NULL, the image won't be
743 * destroyed (see vout_ManageOutputMethod())
744 *****************************************************************************/
745 static void X11DestroyShmImage( vout_thread_t *p_vout, XImage *p_ximage,
746 XShmSegmentInfo *p_shm_info )
748 /* If pointer is NULL, do nothing */
749 if( p_ximage == NULL )
754 XShmDetach( p_vout->p_sys->p_display, p_shm_info ); /* detach from server */
755 XDestroyImage( p_ximage );
756 if( shmdt( p_shm_info->shmaddr ) ) /* detach shared memory from process */
757 { /* also automatic freeing... */
758 intf_ErrMsg( "error: can't detach shared memory (%s)",
763 /*****************************************************************************
765 *****************************************************************************/
766 static int X11CreateWindow ( intf_thread_t *p_intf );
767 static void X11DestroyWindow ( intf_thread_t *p_intf );
768 static void X11ManageWindow ( intf_thread_t *p_intf );
769 static void X11EnableScreenSaver ( intf_thread_t *p_intf );
770 static void X11DisableScreenSaver ( intf_thread_t *p_intf );
771 static void X11TogglePointer ( intf_thread_t *p_intf );
773 /*****************************************************************************
774 * intf_X11Create: initialize and create window
775 *****************************************************************************/
776 int intf_X11Create( intf_thread_t *p_intf )
780 /* Allocate instance and initialize some members */
781 p_intf->p_sys = malloc( sizeof( intf_sys_t ) );
782 if( p_intf->p_sys == NULL )
784 intf_ErrMsg("error: %s", strerror(ENOMEM));
788 /* Open display, unsing 'vlc_display' or DISPLAY environment variable */
789 psz_display = XDisplayName( main_GetPszVariable( VOUT_DISPLAY_VAR, NULL ) );
790 p_intf->p_sys->p_display = XOpenDisplay( psz_display );
791 if( !p_intf->p_sys->p_display ) /* error */
793 intf_ErrMsg("error: can't open display %s", psz_display );
794 free( p_intf->p_sys );
797 p_intf->p_sys->i_screen = DefaultScreen( p_intf->p_sys->p_display );
799 /* Spawn base window - this window will include the video output window,
800 * but also command buttons, subtitles and other indicators */
801 if( X11CreateWindow( p_intf ) )
803 intf_ErrMsg("error: can't create interface window" );
804 XCloseDisplay( p_intf->p_sys->p_display );
805 free( p_intf->p_sys );
809 /* Spawn video output thread */
810 if( p_main->b_video )
812 p_intf->p_vout = vout_CreateThread( psz_display, p_intf->p_sys->window,
813 p_intf->p_sys->i_width,
814 p_intf->p_sys->i_height, NULL, 0,
815 (void *)&p_intf->p_sys->colormap );
817 if( p_intf->p_vout == NULL ) /* error */
819 intf_ErrMsg("error: can't create video output thread" );
820 X11DestroyWindow( p_intf );
821 XCloseDisplay( p_intf->p_sys->p_display );
822 free( p_intf->p_sys );
827 p_intf->p_sys->b_mouse = 1;
830 intf_AssignNormalKeys( p_intf );
832 /* Disable screen saver and return */
833 p_intf->p_sys->i_ss_count = 1;
834 X11DisableScreenSaver( p_intf );
838 /*****************************************************************************
839 * intf_X11Destroy: destroy interface window
840 *****************************************************************************/
841 void intf_X11Destroy( intf_thread_t *p_intf )
843 /* Enable screen saver */
844 X11EnableScreenSaver( p_intf );
846 /* Close input thread, if any (blocking) */
847 if( p_intf->p_input )
849 input_DestroyThread( p_intf->p_input, NULL );
852 /* Close video output thread, if any (blocking) */
855 vout_DestroyThread( p_intf->p_vout, NULL );
858 /* Close main window and display */
859 X11DestroyWindow( p_intf );
860 XCloseDisplay( p_intf->p_sys->p_display );
862 /* Destroy structure */
863 free( p_intf->p_sys );
867 /*****************************************************************************
868 * intf_X11Manage: event loop
869 *****************************************************************************/
870 void intf_X11Manage( intf_thread_t *p_intf )
872 /* Manage main window */
873 X11ManageWindow( p_intf );
876 /* following functions are local */
878 /*****************************************************************************
879 * X11CreateWindow: open and set-up X11 main window
880 *****************************************************************************/
881 static int X11CreateWindow( intf_thread_t *p_intf )
883 XSizeHints xsize_hints;
884 XSetWindowAttributes xwindow_attributes;
888 boolean_t b_configure_notify;
889 boolean_t b_map_notify;
891 /* Set main window's size */
892 p_intf->p_sys->i_width = main_GetIntVariable( VOUT_WIDTH_VAR,
893 VOUT_WIDTH_DEFAULT );
894 p_intf->p_sys->i_height = main_GetIntVariable( VOUT_HEIGHT_VAR,
895 VOUT_HEIGHT_DEFAULT );
897 /* Prepare window manager hints and properties */
898 xsize_hints.base_width = p_intf->p_sys->i_width;
899 xsize_hints.base_height = p_intf->p_sys->i_height;
900 xsize_hints.flags = PSize;
901 p_intf->p_sys->wm_protocols = XInternAtom( p_intf->p_sys->p_display,
902 "WM_PROTOCOLS", True );
903 p_intf->p_sys->wm_delete_window = XInternAtom( p_intf->p_sys->p_display,
904 "WM_DELETE_WINDOW", True );
906 /* Prepare window attributes */
907 xwindow_attributes.backing_store = Always; /* save the hidden part */
908 xwindow_attributes.background_pixel = WhitePixel( p_intf->p_sys->p_display,
909 p_intf->p_sys->i_screen );
911 xwindow_attributes.event_mask = ExposureMask | StructureNotifyMask;
913 /* Create the window and set hints - the window must receive ConfigureNotify
914 * events, and, until it is displayed, Expose and MapNotify events. */
915 p_intf->p_sys->window =
916 XCreateWindow( p_intf->p_sys->p_display,
917 DefaultRootWindow( p_intf->p_sys->p_display ),
919 p_intf->p_sys->i_width, p_intf->p_sys->i_height, 1,
921 CWBackingStore | CWBackPixel | CWEventMask,
922 &xwindow_attributes );
924 /* Set window manager hints and properties: size hints, command,
925 * window's name, and accepted protocols */
926 XSetWMNormalHints( p_intf->p_sys->p_display, p_intf->p_sys->window,
928 XSetCommand( p_intf->p_sys->p_display, p_intf->p_sys->window,
929 p_main->ppsz_argv, p_main->i_argc );
930 XStoreName( p_intf->p_sys->p_display, p_intf->p_sys->window, VOUT_TITLE );
932 if( (p_intf->p_sys->wm_protocols == None) /* use WM_DELETE_WINDOW */
933 || (p_intf->p_sys->wm_delete_window == None)
934 || !XSetWMProtocols( p_intf->p_sys->p_display, p_intf->p_sys->window,
935 &p_intf->p_sys->wm_delete_window, 1 ) )
937 /* WM_DELETE_WINDOW is not supported by window manager */
938 intf_Msg("intf error: missing or bad window manager - please exit program kindly.");
941 /* Creation of a graphic context that doesn't generate a GraphicsExpose
942 * event when using functions like XCopyArea */
943 xgcvalues.graphics_exposures = False;
944 p_intf->p_sys->gc = XCreateGC( p_intf->p_sys->p_display, p_intf->p_sys->window,
945 GCGraphicsExposures, &xgcvalues);
947 /* Send orders to server, and wait until window is displayed - three
948 * events must be received: a MapNotify event, an Expose event allowing
949 * drawing in the window, and a ConfigureNotify to get the window
950 * dimensions. Once those events have been received, only ConfigureNotify
951 * events need to be received. */
953 b_configure_notify = 0;
955 XMapWindow( p_intf->p_sys->p_display, p_intf->p_sys->window);
958 XNextEvent( p_intf->p_sys->p_display, &xevent);
959 if( (xevent.type == Expose)
960 && (xevent.xexpose.window == p_intf->p_sys->window) )
964 else if( (xevent.type == MapNotify)
965 && (xevent.xmap.window == p_intf->p_sys->window) )
969 else if( (xevent.type == ConfigureNotify)
970 && (xevent.xconfigure.window == p_intf->p_sys->window) )
972 b_configure_notify = 1;
973 p_intf->p_sys->i_width = xevent.xconfigure.width;
974 p_intf->p_sys->i_height = xevent.xconfigure.height;
976 } while( !( b_expose && b_configure_notify && b_map_notify ) );
978 XSelectInput( p_intf->p_sys->p_display, p_intf->p_sys->window,
979 StructureNotifyMask | KeyPressMask | ButtonPressMask );
981 if( XDefaultDepth(p_intf->p_sys->p_display, p_intf->p_sys->i_screen) == 8 )
983 /* Allocate a new palette */
984 p_intf->p_sys->colormap = XCreateColormap( p_intf->p_sys->p_display,
985 DefaultRootWindow( p_intf->p_sys->p_display ),
986 DefaultVisual( p_intf->p_sys->p_display,
987 p_intf->p_sys->i_screen ),
990 xwindow_attributes.colormap = p_intf->p_sys->colormap;
991 XChangeWindowAttributes( p_intf->p_sys->p_display,
992 p_intf->p_sys->window,
993 CWColormap, &xwindow_attributes );
996 /* At this stage, the window is open, displayed, and ready to receive data */
1000 /*****************************************************************************
1001 * X11DestroyWindow: destroy X11 main window
1002 *****************************************************************************/
1003 static void X11DestroyWindow( intf_thread_t *p_intf )
1005 XUnmapWindow( p_intf->p_sys->p_display, p_intf->p_sys->window );
1006 XFreeGC( p_intf->p_sys->p_display, p_intf->p_sys->gc );
1007 XDestroyWindow( p_intf->p_sys->p_display, p_intf->p_sys->window );
1010 /*****************************************************************************
1011 * X11ManageWindow: manage X11 main window
1012 *****************************************************************************/
1013 static void X11ManageWindow( intf_thread_t *p_intf )
1015 XEvent xevent; /* X11 event */
1016 boolean_t b_resized; /* window has been resized */
1017 char i_key; /* ISO Latin-1 key */
1019 /* Handle X11 events: ConfigureNotify events are parsed to know if the
1020 * output window's size changed, MapNotify and UnmapNotify to know if the
1021 * window is mapped (and if the display is useful), and ClientMessages
1022 * to intercept window destruction requests */
1024 while( XCheckWindowEvent( p_intf->p_sys->p_display, p_intf->p_sys->window,
1025 StructureNotifyMask | KeyPressMask |
1026 ButtonPressMask, &xevent ) == True )
1028 /* ConfigureNotify event: prepare */
1029 if( (xevent.type == ConfigureNotify)
1030 && ((xevent.xconfigure.width != p_intf->p_sys->i_width)
1031 || (xevent.xconfigure.height != p_intf->p_sys->i_height)) )
1033 /* Update dimensions */
1035 p_intf->p_sys->i_width = xevent.xconfigure.width;
1036 p_intf->p_sys->i_height = xevent.xconfigure.height;
1038 /* MapNotify event: change window status and disable screen saver */
1039 else if( xevent.type == MapNotify)
1041 if( (p_intf->p_vout != NULL) && !p_intf->p_vout->b_active )
1043 X11DisableScreenSaver( p_intf );
1044 p_intf->p_vout->b_active = 1;
1047 /* UnmapNotify event: change window status and enable screen saver */
1048 else if( xevent.type == UnmapNotify )
1050 if( (p_intf->p_vout != NULL) && p_intf->p_vout->b_active )
1052 X11EnableScreenSaver( p_intf );
1053 p_intf->p_vout->b_active = 0;
1056 /* Keyboard event */
1057 else if( xevent.type == KeyPress )
1059 if( XLookupString( &xevent.xkey, &i_key, 1, NULL, NULL ) )
1061 if( intf_ProcessKey( p_intf, i_key ) )
1063 intf_DbgMsg("unhandled key '%c' (%i)", (char) i_key, i_key );
1068 else if( xevent.type == ButtonPress )
1070 switch( ((XButtonEvent *)&xevent)->button )
1073 /* in this part we will eventually manage
1074 * clicks for DVD navigation for instance */
1078 X11TogglePointer( p_intf );
1082 vlc_mutex_lock( &p_intf->p_vout->change_lock );
1083 p_intf->p_vout->b_interface = !p_intf->p_vout->b_interface;
1084 p_intf->p_vout->i_changes |= VOUT_INTF_CHANGE;
1085 vlc_mutex_unlock( &p_intf->p_vout->change_lock );
1093 intf_DbgMsg( "%p -> unhandled event type %d received",
1094 p_intf, xevent.type );
1099 /* ClientMessage event - only WM_PROTOCOLS with WM_DELETE_WINDOW data
1100 * are handled - according to the man pages, the format is always 32
1102 while( XCheckTypedEvent( p_intf->p_sys->p_display,
1103 ClientMessage, &xevent ) )
1105 if( (xevent.xclient.message_type == p_intf->p_sys->wm_protocols)
1106 && (xevent.xclient.data.l[0] == p_intf->p_sys->wm_delete_window ) )
1112 intf_DbgMsg( "%p -> unhandled ClientMessage received", p_intf );
1117 * Handle vout or interface windows resizing
1119 if( p_intf->p_vout != NULL )
1123 /* If interface window has been resized, change vout size */
1124 intf_DbgMsg( "resizing output window" );
1125 vlc_mutex_lock( &p_intf->p_vout->change_lock );
1126 p_intf->p_vout->i_width = p_intf->p_sys->i_width;
1127 p_intf->p_vout->i_height = p_intf->p_sys->i_height;
1128 p_intf->p_vout->i_changes |= VOUT_SIZE_CHANGE;
1129 vlc_mutex_unlock( &p_intf->p_vout->change_lock );
1131 else if( (p_intf->p_vout->i_width != p_intf->p_sys->i_width) ||
1132 (p_intf->p_vout->i_height != p_intf->p_sys->i_height) )
1134 /* If video output size has changed, change interface window size */
1135 intf_DbgMsg( "resizing output window" );
1136 p_intf->p_sys->i_width = p_intf->p_vout->i_width;
1137 p_intf->p_sys->i_height = p_intf->p_vout->i_height;
1138 XResizeWindow( p_intf->p_sys->p_display, p_intf->p_sys->window,
1139 p_intf->p_sys->i_width, p_intf->p_sys->i_height );
1144 /*****************************************************************************
1145 * X11EnableScreenSaver: enable screen saver
1146 *****************************************************************************
1147 * This function enable the screen saver on a display after it had been
1148 * disabled by XDisableScreenSaver. Both functions use a counter mechanism to
1149 * know wether the screen saver can be activated or not: if n successive calls
1150 * are made to XDisableScreenSaver, n successive calls to XEnableScreenSaver
1151 * will be required before the screen saver could effectively be activated.
1152 *****************************************************************************/
1153 void X11EnableScreenSaver( intf_thread_t *p_intf )
1155 if( p_intf->p_sys->i_ss_count++ == 0 )
1157 intf_DbgMsg( "intf: enabling screen saver" );
1158 XSetScreenSaver( p_intf->p_sys->p_display, p_intf->p_sys->i_ss_timeout,
1159 p_intf->p_sys->i_ss_interval, p_intf->p_sys->i_ss_blanking,
1160 p_intf->p_sys->i_ss_exposure );
1164 /*****************************************************************************
1165 * X11DisableScreenSaver: disable screen saver
1166 *****************************************************************************
1167 * See XEnableScreenSaver
1168 *****************************************************************************/
1169 void X11DisableScreenSaver( intf_thread_t *p_intf )
1171 if( --p_intf->p_sys->i_ss_count == 0 )
1173 /* Save screen saver informations */
1174 XGetScreenSaver( p_intf->p_sys->p_display, &p_intf->p_sys->i_ss_timeout,
1175 &p_intf->p_sys->i_ss_interval, &p_intf->p_sys->i_ss_blanking,
1176 &p_intf->p_sys->i_ss_exposure );
1178 /* Disable screen saver */
1179 intf_DbgMsg("intf: disabling screen saver");
1180 XSetScreenSaver( p_intf->p_sys->p_display, 0,
1181 p_intf->p_sys->i_ss_interval, p_intf->p_sys->i_ss_blanking,
1182 p_intf->p_sys->i_ss_exposure );
1186 /*****************************************************************************
1187 * X11TogglePointer: hide or show the mouse pointer
1188 *****************************************************************************
1189 * This function hides the X pointer if it is visible by putting it at
1190 * coordinates (32,32) and setting the pointer sprite to a blank one. To
1191 * show it again, we disable the sprite and restore the original coordinates.
1192 *****************************************************************************/
1193 void X11TogglePointer( intf_thread_t *p_intf )
1195 static Cursor cursor;
1196 static boolean_t b_cursor = 0;
1198 if( p_intf->p_sys->b_mouse )
1200 p_intf->p_sys->b_mouse = 0;
1205 Pixmap blank = XCreatePixmap( p_intf->p_sys->p_display,
1206 DefaultRootWindow(p_intf->p_sys->p_display),
1209 XParseColor( p_intf->p_sys->p_display,
1210 XCreateColormap( p_intf->p_sys->p_display,
1212 p_intf->p_sys->p_display ),
1214 p_intf->p_sys->p_display,
1215 p_intf->p_sys->i_screen ),
1219 cursor = XCreatePixmapCursor( p_intf->p_sys->p_display,
1220 blank, blank, &color, &color, 1, 1 );
1224 XDefineCursor( p_intf->p_sys->p_display,
1225 p_intf->p_sys->window, cursor );
1229 p_intf->p_sys->b_mouse = 1;
1231 XUndefineCursor( p_intf->p_sys->p_display, p_intf->p_sys->window );