* X11 output works again. It's just the output, but it works; I now need
[vlc.git] / plugins / x11 / vout_x11.c
blob5457b2d8522cffddb45171144a1f9ea1d9333866
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.11 2001/02/15 03:01:20 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 /*****************************************************************************
26 * Preamble
27 *****************************************************************************/
28 #include "defs.h"
30 #include <errno.h> /* ENOMEM */
31 #include <stdlib.h> /* free() */
32 #include <string.h> /* strerror() */
34 #ifdef HAVE_MACHINE_PARAM_H
35 /* BSD */
36 #include <machine/param.h>
37 #include <sys/types.h> /* typedef ushort */
38 #include <sys/ipc.h>
39 #endif
41 #include <sys/shm.h> /* shmget(), shmctl() */
42 #include <X11/Xlib.h>
43 #include <X11/Xutil.h>
44 #include <X11/keysym.h>
45 #include <X11/extensions/XShm.h>
47 #include "config.h"
48 #include "common.h"
49 #include "threads.h"
50 #include "mtime.h"
51 #include "tests.h"
52 #include "modules.h"
54 #include "video.h"
55 #include "video_output.h"
57 #include "interface.h"
58 #include "intf_msg.h"
60 #include "main.h"
62 /*****************************************************************************
63 * vout_sys_t: video output X11 method descriptor
64 *****************************************************************************
65 * This structure is part of the video output thread descriptor.
66 * It describes the X11 specific properties of an output thread. X11 video
67 * output is performed through regular resizable windows. Windows can be
68 * dynamically resized to adapt to the size of the streams.
69 *****************************************************************************/
70 typedef struct vout_sys_s
72 /* User settings */
73 boolean_t b_shm; /* shared memory extension flag */
75 /* Internal settings and properties */
76 Display * p_display; /* display pointer */
77 Visual * p_visual; /* visual pointer */
78 int i_screen; /* screen number */
79 Window window; /* root window */
80 GC gc; /* graphic context instance handler */
81 Colormap colormap; /* colormap used (8bpp only) */
83 /* Display buffers and shared memory information */
84 XImage * p_ximage[2]; /* XImage pointer */
85 XShmSegmentInfo shm_info[2]; /* shared memory zone information */
87 /* X11 generic properties */
88 Atom wm_protocols;
89 Atom wm_delete_window;
91 int i_width; /* width of main window */
92 int i_height; /* height of main window */
94 /* Screen saver properties */
95 int i_ss_count; /* enabling/disabling count */
96 int i_ss_timeout; /* timeout */
97 int i_ss_interval; /* interval between changes */
98 int i_ss_blanking; /* blanking mode */
99 int i_ss_exposure; /* exposure mode */
101 /* Mouse pointer properties */
102 boolean_t b_mouse; /* is the mouse pointer displayed ? */
104 } vout_sys_t;
106 /*****************************************************************************
107 * Local prototypes
108 *****************************************************************************/
109 static int vout_Probe ( probedata_t *p_data );
110 static int vout_Create ( struct vout_thread_s * );
111 static int vout_Init ( struct vout_thread_s * );
112 static void vout_End ( struct vout_thread_s * );
113 static void vout_Destroy ( struct vout_thread_s * );
114 static int vout_Manage ( struct vout_thread_s * );
115 static void vout_Display ( struct vout_thread_s * );
116 static void vout_SetPalette( struct vout_thread_s *, u16*, u16*, u16*, u16* );
118 static int X11OpenDisplay ( vout_thread_t *p_vout, char *psz_display );
119 static int X11CreateWindowVOUT ( vout_thread_t *p_vout );
120 static int X11CreateImage ( vout_thread_t *p_vout, XImage **pp_ximage );
121 static void X11DestroyImage ( XImage *p_ximage );
122 static int X11CreateShmImage ( vout_thread_t *p_vout, XImage **pp_ximage,
123 XShmSegmentInfo *p_shm_info );
124 static void X11DestroyShmImage ( vout_thread_t *p_vout, XImage *p_ximage,
125 XShmSegmentInfo *p_shm_info );
127 static int X11CreateWindow ( vout_thread_t *p_vout );
128 static void X11ManageWindow ( vout_thread_t *p_vout );
129 static void X11EnableScreenSaver ( vout_thread_t *p_vout );
130 static void X11DisableScreenSaver ( vout_thread_t *p_vout );
131 static void X11TogglePointer ( vout_thread_t *p_vout );
133 /*****************************************************************************
134 * Functions exported as capabilities. They are declared as static so that
135 * we don't pollute the namespace too much.
136 *****************************************************************************/
137 void vout_getfunctions( function_list_t * p_function_list )
139 p_function_list->pf_probe = vout_Probe;
140 p_function_list->functions.vout.pf_create = vout_Create;
141 p_function_list->functions.vout.pf_init = vout_Init;
142 p_function_list->functions.vout.pf_end = vout_End;
143 p_function_list->functions.vout.pf_destroy = vout_Destroy;
144 p_function_list->functions.vout.pf_manage = vout_Manage;
145 p_function_list->functions.vout.pf_display = vout_Display;
146 p_function_list->functions.vout.pf_setpalette = vout_SetPalette;
149 /*****************************************************************************
150 * vout_Probe: probe the video driver and return a score
151 *****************************************************************************
152 * This function tries to initialize SDL and returns a score to the
153 * plugin manager so that it can select the best plugin.
154 *****************************************************************************/
155 static int vout_Probe( probedata_t *p_data )
157 if( TestMethod( VOUT_METHOD_VAR, "x11" ) )
159 return( 999 );
162 return( 50 );
165 /*****************************************************************************
166 * vout_Create: allocate X11 video thread output method
167 *****************************************************************************
168 * This function allocate and initialize a X11 vout method. It uses some of the
169 * vout properties to choose the window size, and change them according to the
170 * actual properties of the display.
171 *****************************************************************************/
172 static int vout_Create( vout_thread_t *p_vout )
174 char *psz_display;
176 /* Allocate structure */
177 p_vout->p_sys = malloc( sizeof( vout_sys_t ) );
178 if( p_vout->p_sys == NULL )
180 intf_ErrMsg("error: %s", strerror(ENOMEM) );
181 return( 1 );
184 /* Open display, unsing 'vlc_display' or DISPLAY environment variable */
185 psz_display = XDisplayName( main_GetPszVariable( VOUT_DISPLAY_VAR, NULL ) );
186 p_vout->p_sys->p_display = XOpenDisplay( psz_display );
188 if( !p_vout->p_sys->p_display ) /* error */
190 intf_ErrMsg("error: can't open display %s\n", psz_display );
191 free( p_vout->p_sys );
192 return( 1 );
194 p_vout->p_sys->i_screen = DefaultScreen( p_vout->p_sys->p_display );
196 /* Spawn base window - this window will include the video output window,
197 * but also command buttons, subtitles and other indicators */
198 if( X11CreateWindow( p_vout ) )
200 intf_ErrMsg("error: can't create interface window\n" );
201 XCloseDisplay( p_vout->p_sys->p_display );
202 free( p_vout->p_sys );
203 return( 1 );
206 /* Open and initialize device. This function issues its own error messages.
207 * Since XLib is usually not thread-safe, we can't use the same display
208 * pointer than the interface or another thread. However, the root window
209 * id is still valid. */
210 if( X11OpenDisplay( p_vout, psz_display ) )
212 intf_ErrMsg("error: can't initialize X11 display" );
213 XCloseDisplay( p_vout->p_sys->p_display );
214 free( p_vout->p_sys );
215 return( 1 );
218 p_vout->p_sys->b_mouse = 1;
220 /* Disable screen saver and return */
221 p_vout->p_sys->i_ss_count = 1;
222 X11DisableScreenSaver( p_vout );
224 return( 0 );
227 /*****************************************************************************
228 * vout_Init: initialize X11 video thread output method
229 *****************************************************************************
230 * This function create the XImages needed by the output thread. It is called
231 * at the beginning of the thread, but also each time the window is resized.
232 *****************************************************************************/
233 static int vout_Init( vout_thread_t *p_vout )
235 int i_err;
237 /* Create XImages using XShm extension - on failure, fall back to regular
238 * way (and destroy the first image if it was created successfully) */
239 if( p_vout->p_sys->b_shm )
241 /* Create first image */
242 i_err = X11CreateShmImage( p_vout, &p_vout->p_sys->p_ximage[0],
243 &p_vout->p_sys->shm_info[0] );
244 if( !i_err ) /* first image has been created */
246 /* Create second image */
247 if( X11CreateShmImage( p_vout, &p_vout->p_sys->p_ximage[1],
248 &p_vout->p_sys->shm_info[1] ) )
249 { /* error creating the second image */
250 X11DestroyShmImage( p_vout, p_vout->p_sys->p_ximage[0],
251 &p_vout->p_sys->shm_info[0] );
252 i_err = 1;
255 if( i_err ) /* an error occured */
257 intf_Msg("vout: XShm video sextension deactivated" );
258 p_vout->p_sys->b_shm = 0;
262 /* Create XImages without XShm extension */
263 if( !p_vout->p_sys->b_shm )
265 if( X11CreateImage( p_vout, &p_vout->p_sys->p_ximage[0] ) )
267 intf_ErrMsg("error: can't create images");
268 p_vout->p_sys->p_ximage[0] = NULL;
269 p_vout->p_sys->p_ximage[1] = NULL;
270 return( 1 );
272 if( X11CreateImage( p_vout, &p_vout->p_sys->p_ximage[1] ) )
274 intf_ErrMsg("error: can't create images");
275 X11DestroyImage( p_vout->p_sys->p_ximage[0] );
276 p_vout->p_sys->p_ximage[0] = NULL;
277 p_vout->p_sys->p_ximage[1] = NULL;
278 return( 1 );
282 /* Set bytes per line and initialize buffers */
283 p_vout->i_bytes_per_line = p_vout->p_sys->p_ximage[0]->bytes_per_line;
284 vout_SetBuffers( p_vout, p_vout->p_sys->p_ximage[ 0 ]->data,
285 p_vout->p_sys->p_ximage[ 1 ]->data );
286 return( 0 );
289 /*****************************************************************************
290 * vout_End: terminate X11 video thread output method
291 *****************************************************************************
292 * Destroy the X11 XImages created by vout_Init. It is called at the end of
293 * the thread, but also each time the window is resized.
294 *****************************************************************************/
295 static void vout_End( vout_thread_t *p_vout )
297 if( p_vout->p_sys->b_shm ) /* Shm XImages... */
299 X11DestroyShmImage( p_vout, p_vout->p_sys->p_ximage[0],
300 &p_vout->p_sys->shm_info[0] );
301 X11DestroyShmImage( p_vout, p_vout->p_sys->p_ximage[1],
302 &p_vout->p_sys->shm_info[1] );
304 else /* ...or regular XImages */
306 X11DestroyImage( p_vout->p_sys->p_ximage[0] );
307 X11DestroyImage( p_vout->p_sys->p_ximage[1] );
311 /*****************************************************************************
312 * vout_Destroy: destroy X11 video thread output method
313 *****************************************************************************
314 * Terminate an output method created by vout_CreateOutputMethod
315 *****************************************************************************/
316 static void vout_Destroy( vout_thread_t *p_vout )
318 /* Enable screen saver */
319 X11EnableScreenSaver( p_vout );
321 /* Destroy colormap */
322 if( p_vout->i_screen_depth == 8 )
324 XFreeColormap( p_vout->p_sys->p_display, p_vout->p_sys->colormap );
327 /* Destroy window */
328 XUnmapWindow( p_vout->p_sys->p_display, p_vout->p_sys->window );
329 XFreeGC( p_vout->p_sys->p_display, p_vout->p_sys->gc );
330 XDestroyWindow( p_vout->p_sys->p_display, p_vout->p_sys->window );
332 XCloseDisplay( p_vout->p_sys->p_display );
334 /* Destroy structure */
335 free( p_vout->p_sys );
338 /*****************************************************************************
339 * vout_Manage: handle X11 events
340 *****************************************************************************
341 * This function should be called regularly by video output thread. It manages
342 * X11 events and allows window resizing. It returns a non null value on
343 * error.
344 *****************************************************************************/
345 static int vout_Manage( vout_thread_t *p_vout )
348 * Color/Grayscale or gamma change: in 8bpp, just change the colormap
350 if( (p_vout->i_changes & VOUT_GRAYSCALE_CHANGE)
351 && (p_vout->i_screen_depth == 8) )
353 /* FIXME: clear flags ?? */
357 * Size change
359 if( p_vout->i_changes & VOUT_SIZE_CHANGE )
361 intf_DbgMsg("resizing window");
362 p_vout->i_changes &= ~VOUT_SIZE_CHANGE;
364 /* Resize window */
365 XResizeWindow( p_vout->p_sys->p_display, p_vout->p_sys->window,
366 p_vout->i_width, p_vout->i_height );
368 /* Destroy XImages to change their size */
369 vout_End( p_vout );
371 /* Recreate XImages. If SysInit failed, the thread can't go on. */
372 if( vout_Init( p_vout ) )
374 intf_ErrMsg("error: can't resize display");
375 return( 1 );
378 /* Tell the video output thread that it will need to rebuild YUV
379 * tables. This is needed since conversion buffer size may have
380 * changed */
381 p_vout->i_changes |= VOUT_YUV_CHANGE;
382 intf_Msg("vout: video display resized (%dx%d)", p_vout->i_width, p_vout->i_height);
385 X11ManageWindow( p_vout );
387 return 0;
390 /*****************************************************************************
391 * vout_Display: displays previously rendered output
392 *****************************************************************************
393 * This function send the currently rendered image to X11 server, wait until
394 * it is displayed and switch the two rendering buffer, preparing next frame.
395 *****************************************************************************/
396 static void vout_Display( vout_thread_t *p_vout )
398 if( p_vout->p_sys->b_shm) /* XShm is used */
400 /* Display rendered image using shared memory extension */
401 XShmPutImage(p_vout->p_sys->p_display, p_vout->p_sys->window, p_vout->p_sys->gc,
402 p_vout->p_sys->p_ximage[ p_vout->i_buffer_index ],
403 0, 0, 0, 0,
404 p_vout->p_sys->p_ximage[ p_vout->i_buffer_index ]->width,
405 p_vout->p_sys->p_ximage[ p_vout->i_buffer_index ]->height, True);
407 /* Send the order to the X server */
408 XSync(p_vout->p_sys->p_display, False);
410 else /* regular X11 capabilities are used */
412 XPutImage(p_vout->p_sys->p_display, p_vout->p_sys->window, p_vout->p_sys->gc,
413 p_vout->p_sys->p_ximage[ p_vout->i_buffer_index ],
414 0, 0, 0, 0,
415 p_vout->p_sys->p_ximage[ p_vout->i_buffer_index ]->width,
416 p_vout->p_sys->p_ximage[ p_vout->i_buffer_index ]->height);
418 /* Send the order to the X server */
419 XSync(p_vout->p_sys->p_display, False);
423 /*****************************************************************************
424 * vout_SetPalette: sets an 8 bpp palette
425 *****************************************************************************
426 * This function sets the palette given as an argument. It does not return
427 * anything, but could later send information on which colors it was unable
428 * to set.
429 *****************************************************************************/
430 static void vout_SetPalette( p_vout_thread_t p_vout,
431 u16 *red, u16 *green, u16 *blue, u16 *transp )
433 int i;
434 XColor color[255];
436 intf_DbgMsg( "Palette change called" );
438 /* allocate palette */
439 for( i = 0; i < 255; i++ )
441 /* kludge: colors are indexed reversely because color 255 seems
442 * to be reserved for black even if we try to set it to white */
443 color[i].pixel = 255-i;
444 color[i].pad = 0;
445 color[i].flags = DoRed|DoGreen|DoBlue;
446 color[i].red = red[255-i];
447 color[i].blue = blue[255-i];
448 color[i].green = green[255-i];
451 XStoreColors( p_vout->p_sys->p_display, p_vout->p_sys->colormap, color, 256 );
454 /* following functions are local */
456 /*****************************************************************************
457 * X11OpenDisplay: open and initialize X11 device
458 *****************************************************************************
459 * Create a window according to video output given size, and set other
460 * properties according to the display properties.
461 *****************************************************************************/
462 static int X11OpenDisplay( vout_thread_t *p_vout, char *psz_display )
464 XPixmapFormatValues * p_xpixmap_format; /* pixmap formats */
465 XVisualInfo * p_xvisual; /* visuals informations */
466 XVisualInfo xvisual_template; /* visual template */
467 int i_count; /* array size */
469 /* Open display */
470 p_vout->p_sys->p_display = XOpenDisplay( psz_display );
471 if( p_vout->p_sys->p_display == NULL )
473 intf_ErrMsg("error: can't open display %s", psz_display );
474 return( 1 );
477 /* Initialize structure */
478 p_vout->p_sys->b_shm = (XShmQueryExtension(p_vout->p_sys->p_display) == True);
479 p_vout->p_sys->i_screen = DefaultScreen( p_vout->p_sys->p_display );
480 if( !p_vout->p_sys->b_shm )
482 intf_Msg("vout: XShm video extension is not available");
485 /* Get screen depth */
486 p_vout->i_screen_depth = XDefaultDepth( p_vout->p_sys->p_display, p_vout->p_sys->i_screen );
487 switch( p_vout->i_screen_depth )
489 case 8:
491 * Screen depth is 8bpp. Use PseudoColor visual with private colormap.
493 xvisual_template.screen = p_vout->p_sys->i_screen;
494 xvisual_template.class = DirectColor;
495 p_xvisual = XGetVisualInfo( p_vout->p_sys->p_display, VisualScreenMask | VisualClassMask,
496 &xvisual_template, &i_count );
497 if( p_xvisual == NULL )
499 intf_ErrMsg("error: no PseudoColor visual available");
500 XCloseDisplay( p_vout->p_sys->p_display );
501 return( 1 );
503 p_vout->i_bytes_per_pixel = 1;
504 break;
505 case 15:
506 case 16:
507 case 24:
508 default:
510 * Screen depth is higher than 8bpp. TrueColor visual is used.
512 xvisual_template.screen = p_vout->p_sys->i_screen;
513 xvisual_template.class = TrueColor;
514 p_xvisual = XGetVisualInfo( p_vout->p_sys->p_display, VisualScreenMask | VisualClassMask,
515 &xvisual_template, &i_count );
516 if( p_xvisual == NULL )
518 intf_ErrMsg("error: no TrueColor visual available");
519 XCloseDisplay( p_vout->p_sys->p_display );
520 return( 1 );
522 p_vout->i_red_mask = p_xvisual->red_mask;
523 p_vout->i_green_mask = p_xvisual->green_mask;
524 p_vout->i_blue_mask = p_xvisual->blue_mask;
526 /* There is no difference yet between 3 and 4 Bpp. The only way to find
527 * the actual number of bytes per pixel is to list supported pixmap
528 * formats. */
529 p_xpixmap_format = XListPixmapFormats( p_vout->p_sys->p_display, &i_count );
532 /* Under XFree4.0, the list contains pixmap formats available through
533 * all video depths ; so we have to check against current depth. */
534 p_vout->i_bytes_per_pixel = 0;
535 for( ; i_count-- ; p_xpixmap_format++ )
537 if( p_xpixmap_format->depth == p_vout->i_screen_depth )
539 if( p_xpixmap_format->bits_per_pixel / 8 > p_vout->i_bytes_per_pixel )
541 p_vout->i_bytes_per_pixel = p_xpixmap_format->bits_per_pixel / 8;
545 break;
547 p_vout->p_sys->p_visual = p_xvisual->visual;
548 XFree( p_xvisual );
550 /* Create a window */
551 if( X11CreateWindowVOUT( p_vout ) )
553 intf_ErrMsg("error: can't open a window");
554 XCloseDisplay( p_vout->p_sys->p_display );
555 return( 1 );
557 return( 0 );
560 /*****************************************************************************
561 * X11CreateWindowVOUT: create X11 vout window
562 *****************************************************************************
563 * The video output window will be created. Normally, this window is wether
564 * full screen or part of a parent window. Therefore, it does not need a
565 * title or other hints. Thery are still supplied in case the window would be
566 * spawned as a standalone one by the interface.
567 *****************************************************************************/
568 static int X11CreateWindowVOUT( vout_thread_t *p_vout )
570 XSetWindowAttributes xwindow_attributes; /* window attributes */
571 XGCValues xgcvalues; /* graphic context configuration */
572 XEvent xevent; /* first events */
573 boolean_t b_expose; /* 'expose' event received */
574 boolean_t b_map_notify; /* 'map_notify' event received */
576 /* Prepare window attributes */
577 xwindow_attributes.backing_store = Always; /* save the hidden part */
579 /* Create the window and set hints */
580 p_vout->p_sys->window = XCreateSimpleWindow( p_vout->p_sys->p_display,
581 p_vout->p_sys->window,
582 0, 0,
583 p_vout->i_width, p_vout->i_height,
584 0, 0, 0);
585 XSelectInput( p_vout->p_sys->p_display, p_vout->p_sys->window,
586 ExposureMask | StructureNotifyMask );
587 XChangeWindowAttributes( p_vout->p_sys->p_display, p_vout->p_sys->window,
588 CWBackingStore, &xwindow_attributes);
590 /* Creation of a graphic context that doesn't generate a GraphicsExpose event
591 when using functions like XCopyArea */
592 xgcvalues.graphics_exposures = False;
593 p_vout->p_sys->gc = XCreateGC( p_vout->p_sys->p_display, p_vout->p_sys->window,
594 GCGraphicsExposures, &xgcvalues);
596 /* Send orders to server, and wait until window is displayed - two events
597 * must be received: a MapNotify event, an Expose event allowing drawing in the
598 * window */
599 b_expose = 0;
600 b_map_notify = 0;
601 XMapWindow( p_vout->p_sys->p_display, p_vout->p_sys->window);
604 XNextEvent( p_vout->p_sys->p_display, &xevent);
605 if( (xevent.type == Expose)
606 && (xevent.xexpose.window == p_vout->p_sys->window) )
608 b_expose = 1;
610 else if( (xevent.type == MapNotify)
611 && (xevent.xmap.window == p_vout->p_sys->window) )
613 b_map_notify = 1;
616 while( !( b_expose && b_map_notify ) );
617 XSelectInput( p_vout->p_sys->p_display, p_vout->p_sys->window, 0 );
619 /* At this stage, the window is open, displayed, and ready to receive
620 * data */
621 return( 0 );
624 /*****************************************************************************
625 * X11CreateImage: create an XImage
626 *****************************************************************************
627 * Create a simple XImage used as a buffer.
628 *****************************************************************************/
629 static int X11CreateImage( vout_thread_t *p_vout, XImage **pp_ximage )
631 byte_t * pb_data; /* image data storage zone */
632 int i_quantum; /* XImage quantum (see below) */
634 /* Allocate memory for image */
635 p_vout->i_bytes_per_line = p_vout->i_width * p_vout->i_bytes_per_pixel;
636 pb_data = (byte_t *) malloc( p_vout->i_bytes_per_line * p_vout->i_height );
637 if( !pb_data ) /* error */
639 intf_ErrMsg("error: %s", strerror(ENOMEM));
640 return( 1 );
643 /* Optimize the quantum of a scanline regarding its size - the quantum is
644 a diviser of the number of bits between the start of two scanlines. */
645 if( !(( p_vout->i_bytes_per_line ) % 32) )
647 i_quantum = 32;
649 else
651 if( !(( p_vout->i_bytes_per_line ) % 16) )
653 i_quantum = 16;
655 else
657 i_quantum = 8;
661 /* Create XImage */
662 *pp_ximage = XCreateImage( p_vout->p_sys->p_display, p_vout->p_sys->p_visual,
663 p_vout->i_screen_depth, ZPixmap, 0, pb_data,
664 p_vout->i_width, p_vout->i_height, i_quantum, 0);
665 if(! *pp_ximage ) /* error */
667 intf_ErrMsg( "error: XCreateImage() failed" );
668 free( pb_data );
669 return( 1 );
672 return 0;
675 /*****************************************************************************
676 * X11CreateShmImage: create an XImage using shared memory extension
677 *****************************************************************************
678 * Prepare an XImage for DisplayX11ShmImage function.
679 * The order of the operations respects the recommandations of the mit-shm
680 * document by J.Corbet and K.Packard. Most of the parameters were copied from
681 * there.
682 *****************************************************************************/
683 static int X11CreateShmImage( vout_thread_t *p_vout, XImage **pp_ximage,
684 XShmSegmentInfo *p_shm_info)
686 /* Create XImage */
687 *pp_ximage = XShmCreateImage( p_vout->p_sys->p_display, p_vout->p_sys->p_visual,
688 p_vout->i_screen_depth, ZPixmap, 0,
689 p_shm_info, p_vout->i_width, p_vout->i_height );
690 if(! *pp_ximage ) /* error */
692 intf_ErrMsg("error: XShmCreateImage() failed");
693 return( 1 );
696 /* Allocate shared memory segment - 0777 set the access permission
697 * rights (like umask), they are not yet supported by X servers */
698 p_shm_info->shmid = shmget( IPC_PRIVATE,
699 (*pp_ximage)->bytes_per_line * (*pp_ximage)->height,
700 IPC_CREAT | 0777);
701 if( p_shm_info->shmid < 0) /* error */
703 intf_ErrMsg("error: can't allocate shared image data (%s)",
704 strerror(errno));
705 XDestroyImage( *pp_ximage );
706 return( 1 );
709 /* Attach shared memory segment to process (read/write) */
710 p_shm_info->shmaddr = (*pp_ximage)->data = shmat(p_shm_info->shmid, 0, 0);
711 if(! p_shm_info->shmaddr )
712 { /* error */
713 intf_ErrMsg("error: can't attach shared memory (%s)",
714 strerror(errno));
715 shmctl( p_shm_info->shmid, IPC_RMID, 0 ); /* free shared memory */
716 XDestroyImage( *pp_ximage );
717 return( 1 );
720 /* Mark the shm segment to be removed when there will be no more
721 * attachements, so it is automatic on process exit or after shmdt */
722 shmctl( p_shm_info->shmid, IPC_RMID, 0 );
724 /* Attach shared memory segment to X server (read only) */
725 p_shm_info->readOnly = True;
726 if( XShmAttach( p_vout->p_sys->p_display, p_shm_info ) == False ) /* error */
728 intf_ErrMsg("error: can't attach shared memory to X11 server");
729 shmdt( p_shm_info->shmaddr ); /* detach shared memory from process
730 * and automatic free */
731 XDestroyImage( *pp_ximage );
732 return( 1 );
735 /* Send image to X server. This instruction is required, since having
736 * built a Shm XImage and not using it causes an error on XCloseDisplay */
737 XFlush( p_vout->p_sys->p_display );
738 return( 0 );
741 /*****************************************************************************
742 * X11DestroyImage: destroy an XImage
743 *****************************************************************************
744 * Destroy XImage AND associated data. If pointer is NULL, the image won't be
745 * destroyed (see vout_ManageOutputMethod())
746 *****************************************************************************/
747 static void X11DestroyImage( XImage *p_ximage )
749 if( p_ximage != NULL )
751 XDestroyImage( p_ximage ); /* no free() required */
755 /*****************************************************************************
756 * X11DestroyShmImage
757 *****************************************************************************
758 * Destroy XImage AND associated data. Detach shared memory segment from
759 * server and process, then free it. If pointer is NULL, the image won't be
760 * destroyed (see vout_ManageOutputMethod())
761 *****************************************************************************/
762 static void X11DestroyShmImage( vout_thread_t *p_vout, XImage *p_ximage,
763 XShmSegmentInfo *p_shm_info )
765 /* If pointer is NULL, do nothing */
766 if( p_ximage == NULL )
768 return;
771 XShmDetach( p_vout->p_sys->p_display, p_shm_info ); /* detach from server */
772 XDestroyImage( p_ximage );
773 if( shmdt( p_shm_info->shmaddr ) ) /* detach shared memory from process */
774 { /* also automatic freeing... */
775 intf_ErrMsg( "error: can't detach shared memory (%s)",
776 strerror(errno) );
781 /* WAZAAAAAAAAAAA */
783 /*****************************************************************************
784 * X11CreateWindow: open and set-up X11 main window
785 *****************************************************************************/
786 static int X11CreateWindow( vout_thread_t *p_vout )
788 XSizeHints xsize_hints;
789 XSetWindowAttributes xwindow_attributes;
790 XGCValues xgcvalues;
791 XEvent xevent;
792 boolean_t b_expose;
793 boolean_t b_configure_notify;
794 boolean_t b_map_notify;
796 /* Set main window's size */
797 p_vout->p_sys->i_width = main_GetIntVariable( VOUT_WIDTH_VAR,
798 VOUT_WIDTH_DEFAULT );
799 p_vout->p_sys->i_height = main_GetIntVariable( VOUT_HEIGHT_VAR,
800 VOUT_HEIGHT_DEFAULT );
802 /* Prepare window manager hints and properties */
803 xsize_hints.base_width = p_vout->p_sys->i_width;
804 xsize_hints.base_height = p_vout->p_sys->i_height;
805 xsize_hints.flags = PSize;
806 p_vout->p_sys->wm_protocols = XInternAtom( p_vout->p_sys->p_display,
807 "WM_PROTOCOLS", True );
808 p_vout->p_sys->wm_delete_window = XInternAtom( p_vout->p_sys->p_display,
809 "WM_DELETE_WINDOW", True );
811 /* Prepare window attributes */
812 xwindow_attributes.backing_store = Always; /* save the hidden part */
813 xwindow_attributes.background_pixel = WhitePixel( p_vout->p_sys->p_display,
814 p_vout->p_sys->i_screen );
816 xwindow_attributes.event_mask = ExposureMask | StructureNotifyMask;
818 /* Create the window and set hints - the window must receive ConfigureNotify
819 * events, and, until it is displayed, Expose and MapNotify events. */
820 p_vout->p_sys->window =
821 XCreateWindow( p_vout->p_sys->p_display,
822 DefaultRootWindow( p_vout->p_sys->p_display ),
823 0, 0,
824 p_vout->p_sys->i_width, p_vout->p_sys->i_height, 1,
825 0, InputOutput, 0,
826 CWBackingStore | CWBackPixel | CWEventMask,
827 &xwindow_attributes );
829 /* Set window manager hints and properties: size hints, command,
830 * window's name, and accepted protocols */
831 XSetWMNormalHints( p_vout->p_sys->p_display, p_vout->p_sys->window,
832 &xsize_hints );
833 XSetCommand( p_vout->p_sys->p_display, p_vout->p_sys->window,
834 p_main->ppsz_argv, p_main->i_argc );
835 XStoreName( p_vout->p_sys->p_display, p_vout->p_sys->window, VOUT_TITLE );
837 if( (p_vout->p_sys->wm_protocols == None) /* use WM_DELETE_WINDOW */
838 || (p_vout->p_sys->wm_delete_window == None)
839 || !XSetWMProtocols( p_vout->p_sys->p_display, p_vout->p_sys->window,
840 &p_vout->p_sys->wm_delete_window, 1 ) )
842 /* WM_DELETE_WINDOW is not supported by window manager */
843 intf_Msg("intf error: missing or bad window manager - please exit program kindly.");
846 /* Creation of a graphic context that doesn't generate a GraphicsExpose
847 * event when using functions like XCopyArea */
848 xgcvalues.graphics_exposures = False;
849 p_vout->p_sys->gc = XCreateGC( p_vout->p_sys->p_display, p_vout->p_sys->window,
850 GCGraphicsExposures, &xgcvalues);
852 /* Send orders to server, and wait until window is displayed - three
853 * events must be received: a MapNotify event, an Expose event allowing
854 * drawing in the window, and a ConfigureNotify to get the window
855 * dimensions. Once those events have been received, only ConfigureNotify
856 * events need to be received. */
857 b_expose = 0;
858 b_configure_notify = 0;
859 b_map_notify = 0;
860 XMapWindow( p_vout->p_sys->p_display, p_vout->p_sys->window);
863 XNextEvent( p_vout->p_sys->p_display, &xevent);
864 if( (xevent.type == Expose)
865 && (xevent.xexpose.window == p_vout->p_sys->window) )
867 b_expose = 1;
869 else if( (xevent.type == MapNotify)
870 && (xevent.xmap.window == p_vout->p_sys->window) )
872 b_map_notify = 1;
874 else if( (xevent.type == ConfigureNotify)
875 && (xevent.xconfigure.window == p_vout->p_sys->window) )
877 b_configure_notify = 1;
878 p_vout->p_sys->i_width = xevent.xconfigure.width;
879 p_vout->p_sys->i_height = xevent.xconfigure.height;
881 } while( !( b_expose && b_configure_notify && b_map_notify ) );
883 XSelectInput( p_vout->p_sys->p_display, p_vout->p_sys->window,
884 StructureNotifyMask | KeyPressMask | ButtonPressMask );
886 if( XDefaultDepth(p_vout->p_sys->p_display, p_vout->p_sys->i_screen) == 8 )
888 /* Allocate a new palette */
889 p_vout->p_sys->colormap = XCreateColormap( p_vout->p_sys->p_display,
890 DefaultRootWindow( p_vout->p_sys->p_display ),
891 DefaultVisual( p_vout->p_sys->p_display,
892 p_vout->p_sys->i_screen ),
893 AllocAll );
895 xwindow_attributes.colormap = p_vout->p_sys->colormap;
896 XChangeWindowAttributes( p_vout->p_sys->p_display,
897 p_vout->p_sys->window,
898 CWColormap, &xwindow_attributes );
901 /* At this stage, the window is open, displayed, and ready to receive data */
902 return( 0 );
905 /*****************************************************************************
906 * X11ManageWindow: manage X11 main window
907 *****************************************************************************/
908 static void X11ManageWindow( vout_thread_t *p_vout )
910 XEvent xevent; /* X11 event */
911 boolean_t b_resized; /* window has been resized */
912 char i_key; /* ISO Latin-1 key */
914 /* Handle X11 events: ConfigureNotify events are parsed to know if the
915 * output window's size changed, MapNotify and UnmapNotify to know if the
916 * window is mapped (and if the display is useful), and ClientMessages
917 * to intercept window destruction requests */
918 b_resized = 0;
919 while( XCheckWindowEvent( p_vout->p_sys->p_display, p_vout->p_sys->window,
920 StructureNotifyMask | KeyPressMask |
921 ButtonPressMask, &xevent ) == True )
923 /* ConfigureNotify event: prepare */
924 if( (xevent.type == ConfigureNotify)
925 && ((xevent.xconfigure.width != p_vout->p_sys->i_width)
926 || (xevent.xconfigure.height != p_vout->p_sys->i_height)) )
928 /* Update dimensions */
929 b_resized = 1;
930 p_vout->p_sys->i_width = xevent.xconfigure.width;
931 p_vout->p_sys->i_height = xevent.xconfigure.height;
933 /* MapNotify event: change window status and disable screen saver */
934 else if( xevent.type == MapNotify)
936 if( (p_vout != NULL) && !p_vout->b_active )
938 X11DisableScreenSaver( p_vout );
939 p_vout->b_active = 1;
942 /* UnmapNotify event: change window status and enable screen saver */
943 else if( xevent.type == UnmapNotify )
945 if( (p_vout != NULL) && p_vout->b_active )
947 X11EnableScreenSaver( p_vout );
948 p_vout->b_active = 0;
951 /* Keyboard event */
952 else if( xevent.type == KeyPress )
954 if( XLookupString( &xevent.xkey, &i_key, 1, NULL, NULL ) )
956 /* if( intf_ProcessKey( p_vout, i_key ) )
958 intf_DbgMsg("unhandled key '%c' (%i)", (char) i_key, i_key );
962 /* Mouse click */
963 else if( xevent.type == ButtonPress )
965 switch( ((XButtonEvent *)&xevent)->button )
967 case Button1:
968 /* in this part we will eventually manage
969 * clicks for DVD navigation for instance */
970 break;
972 case Button2:
973 X11TogglePointer( p_vout );
974 break;
976 case Button3:
977 vlc_mutex_lock( &p_vout->change_lock );
978 p_vout->b_interface = !p_vout->b_interface;
979 p_vout->i_changes |= VOUT_INTF_CHANGE;
980 vlc_mutex_unlock( &p_vout->change_lock );
981 break;
984 #ifdef DEBUG
985 /* Other event */
986 else
988 intf_DbgMsg( "%p -> unhandled event type %d received",
989 p_vout, xevent.type );
991 #endif
994 /* ClientMessage event - only WM_PROTOCOLS with WM_DELETE_WINDOW data
995 * are handled - according to the man pages, the format is always 32
996 * in this case */
997 while( XCheckTypedEvent( p_vout->p_sys->p_display,
998 ClientMessage, &xevent ) )
1000 if( (xevent.xclient.message_type == p_vout->p_sys->wm_protocols)
1001 && (xevent.xclient.data.l[0] == p_vout->p_sys->wm_delete_window ) )
1003 p_vout->b_die = 1;
1005 else
1007 intf_DbgMsg( "%p -> unhandled ClientMessage received", p_vout );
1012 * Handle vout or interface windows resizing
1014 if( p_vout != NULL )
1016 if( b_resized )
1018 /* If interface window has been resized, change vout size */
1019 intf_DbgMsg( "resizing output window" );
1020 vlc_mutex_lock( &p_vout->change_lock );
1021 p_vout->i_width = p_vout->p_sys->i_width;
1022 p_vout->i_height = p_vout->p_sys->i_height;
1023 p_vout->i_changes |= VOUT_SIZE_CHANGE;
1024 vlc_mutex_unlock( &p_vout->change_lock );
1026 else if( (p_vout->i_width != p_vout->p_sys->i_width) ||
1027 (p_vout->i_height != p_vout->p_sys->i_height) )
1029 /* If video output size has changed, change interface window size */
1030 intf_DbgMsg( "resizing output window" );
1031 p_vout->p_sys->i_width = p_vout->i_width;
1032 p_vout->p_sys->i_height = p_vout->i_height;
1033 XResizeWindow( p_vout->p_sys->p_display, p_vout->p_sys->window,
1034 p_vout->p_sys->i_width, p_vout->p_sys->i_height );
1039 /*****************************************************************************
1040 * X11EnableScreenSaver: enable screen saver
1041 *****************************************************************************
1042 * This function enable the screen saver on a display after it had been
1043 * disabled by XDisableScreenSaver. Both functions use a counter mechanism to
1044 * know wether the screen saver can be activated or not: if n successive calls
1045 * are made to XDisableScreenSaver, n successive calls to XEnableScreenSaver
1046 * will be required before the screen saver could effectively be activated.
1047 *****************************************************************************/
1048 void X11EnableScreenSaver( vout_thread_t *p_vout )
1050 if( p_vout->p_sys->i_ss_count++ == 0 )
1052 intf_DbgMsg( "intf: enabling screen saver" );
1053 XSetScreenSaver( p_vout->p_sys->p_display, p_vout->p_sys->i_ss_timeout,
1054 p_vout->p_sys->i_ss_interval, p_vout->p_sys->i_ss_blanking,
1055 p_vout->p_sys->i_ss_exposure );
1059 /*****************************************************************************
1060 * X11DisableScreenSaver: disable screen saver
1061 *****************************************************************************
1062 * See XEnableScreenSaver
1063 *****************************************************************************/
1064 void X11DisableScreenSaver( vout_thread_t *p_vout )
1066 if( --p_vout->p_sys->i_ss_count == 0 )
1068 /* Save screen saver informations */
1069 XGetScreenSaver( p_vout->p_sys->p_display, &p_vout->p_sys->i_ss_timeout,
1070 &p_vout->p_sys->i_ss_interval, &p_vout->p_sys->i_ss_blanking,
1071 &p_vout->p_sys->i_ss_exposure );
1073 /* Disable screen saver */
1074 intf_DbgMsg("intf: disabling screen saver");
1075 XSetScreenSaver( p_vout->p_sys->p_display, 0,
1076 p_vout->p_sys->i_ss_interval, p_vout->p_sys->i_ss_blanking,
1077 p_vout->p_sys->i_ss_exposure );
1081 /*****************************************************************************
1082 * X11TogglePointer: hide or show the mouse pointer
1083 *****************************************************************************
1084 * This function hides the X pointer if it is visible by putting it at
1085 * coordinates (32,32) and setting the pointer sprite to a blank one. To
1086 * show it again, we disable the sprite and restore the original coordinates.
1087 *****************************************************************************/
1088 void X11TogglePointer( vout_thread_t *p_vout )
1090 static Cursor cursor;
1091 static boolean_t b_cursor = 0;
1093 if( p_vout->p_sys->b_mouse )
1095 p_vout->p_sys->b_mouse = 0;
1097 if( !b_cursor )
1099 XColor color;
1100 Pixmap blank = XCreatePixmap( p_vout->p_sys->p_display,
1101 DefaultRootWindow(p_vout->p_sys->p_display),
1102 1, 1, 1 );
1104 XParseColor( p_vout->p_sys->p_display,
1105 XCreateColormap( p_vout->p_sys->p_display,
1106 DefaultRootWindow(
1107 p_vout->p_sys->p_display ),
1108 DefaultVisual(
1109 p_vout->p_sys->p_display,
1110 p_vout->p_sys->i_screen ),
1111 AllocNone ),
1112 "black", &color );
1114 cursor = XCreatePixmapCursor( p_vout->p_sys->p_display,
1115 blank, blank, &color, &color, 1, 1 );
1117 b_cursor = 1;
1119 XDefineCursor( p_vout->p_sys->p_display,
1120 p_vout->p_sys->window, cursor );
1122 else
1124 p_vout->p_sys->b_mouse = 1;
1126 XUndefineCursor( p_vout->p_sys->p_display, p_vout->p_sys->window );