1 /*****************************************************************************
2 * intf_x11.c: X11 interface
3 *****************************************************************************
4 * Copyright (C) 1999, 2000 VideoLAN
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, write to the Free Software
20 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA.
21 *****************************************************************************/
23 /*****************************************************************************
25 *****************************************************************************/
28 #include <errno.h> /* ENOMEM */
29 #include <stdlib.h> /* free() */
30 #include <string.h> /* strerror() */
31 #include <sys/types.h> /* on BSD, uio.h needs types.h */
32 #include <sys/uio.h> /* for input.h */
35 #include <X11/Xutil.h>
36 #include <X11/keysym.h>
46 #include "video_output.h"
49 #include "interface.h"
53 /*****************************************************************************
54 * intf_sys_t: description and status of X11 interface
55 *****************************************************************************/
56 typedef struct intf_sys_s
58 /* X11 generic properties */
59 Display * p_display; /* X11 display pointer */
60 int i_screen; /* X11 screen */
62 Atom wm_delete_window;
64 /* Main window properties */
65 Window window; /* main window */
66 GC gc; /* graphic context for main window */
67 int i_width; /* width of main window */
68 int i_height; /* height of main window */
69 Colormap colormap; /* colormap used (8bpp only) */
71 /* Screen saver properties */
72 int i_ss_count; /* enabling/disabling count */
73 int i_ss_timeout; /* timeout */
74 int i_ss_interval; /* interval between changes */
75 int i_ss_blanking; /* blanking mode */
76 int i_ss_exposure; /* exposure mode */
78 /* Mouse pointer properties */
79 boolean_t b_mouse; /* is the mouse pointer displayed ? */
83 /*****************************************************************************
85 *****************************************************************************/
86 static int X11CreateWindow ( intf_thread_t *p_intf );
87 static void X11DestroyWindow ( intf_thread_t *p_intf );
88 static void X11ManageWindow ( intf_thread_t *p_intf );
89 static void X11EnableScreenSaver ( intf_thread_t *p_intf );
90 static void X11DisableScreenSaver ( intf_thread_t *p_intf );
91 static void X11TogglePointer ( intf_thread_t *p_intf );
93 /*****************************************************************************
94 * intf_X11Create: initialize and create window
95 *****************************************************************************/
96 int intf_X11Create( intf_thread_t *p_intf )
100 /* Allocate instance and initialize some members */
101 p_intf->p_sys = malloc( sizeof( intf_sys_t ) );
102 if( p_intf->p_sys == NULL )
104 intf_ErrMsg("error: %s\n", strerror(ENOMEM));
108 /* Open display, unsing 'vlc_display' or DISPLAY environment variable */
109 psz_display = XDisplayName( main_GetPszVariable( VOUT_DISPLAY_VAR, NULL ) );
110 p_intf->p_sys->p_display = XOpenDisplay( psz_display );
111 if( !p_intf->p_sys->p_display ) /* error */
113 intf_ErrMsg("error: can't open display %s\n", psz_display );
114 free( p_intf->p_sys );
117 p_intf->p_sys->i_screen = DefaultScreen( p_intf->p_sys->p_display );
119 /* Spawn base window - this window will include the video output window,
120 * but also command buttons, subtitles and other indicators */
121 if( X11CreateWindow( p_intf ) )
123 intf_ErrMsg("error: can't create interface window\n" );
124 XCloseDisplay( p_intf->p_sys->p_display );
125 free( p_intf->p_sys );
129 /* Spawn video output thread */
130 if( p_main->b_video )
132 p_intf->p_vout = vout_CreateThread( psz_display, p_intf->p_sys->window,
133 p_intf->p_sys->i_width,
134 p_intf->p_sys->i_height, NULL, 0,
135 (void *)&p_intf->p_sys->colormap );
137 if( p_intf->p_vout == NULL ) /* error */
139 intf_ErrMsg("error: can't create video output thread\n" );
140 X11DestroyWindow( p_intf );
141 XCloseDisplay( p_intf->p_sys->p_display );
142 free( p_intf->p_sys );
147 p_intf->p_sys->b_mouse = 1;
149 /* Disable screen saver and return */
150 p_intf->p_sys->i_ss_count = 1;
151 X11DisableScreenSaver( p_intf );
155 /*****************************************************************************
156 * intf_X11Destroy: destroy interface window
157 *****************************************************************************/
158 void intf_X11Destroy( intf_thread_t *p_intf )
160 /* Enable screen saver */
161 X11EnableScreenSaver( p_intf );
163 /* Close input thread, if any (blocking) */
164 if( p_intf->p_input )
166 input_DestroyThread( p_intf->p_input, NULL );
169 /* Close video output thread, if any (blocking) */
172 vout_DestroyThread( p_intf->p_vout, NULL );
175 /* Close main window and display */
176 X11DestroyWindow( p_intf );
177 XCloseDisplay( p_intf->p_sys->p_display );
179 /* Destroy structure */
180 free( p_intf->p_sys );
184 /*****************************************************************************
185 * intf_X11Manage: event loop
186 *****************************************************************************/
187 void intf_X11Manage( intf_thread_t *p_intf )
189 /* Manage main window */
190 X11ManageWindow( p_intf );
193 /* following functions are local */
195 /*****************************************************************************
196 * X11CreateWindow: open and set-up X11 main window
197 *****************************************************************************/
198 static int X11CreateWindow( intf_thread_t *p_intf )
200 XSizeHints xsize_hints;
201 XSetWindowAttributes xwindow_attributes;
205 boolean_t b_configure_notify;
206 boolean_t b_map_notify;
208 /* Set main window's size */
209 p_intf->p_sys->i_width = main_GetIntVariable( VOUT_WIDTH_VAR,
210 VOUT_WIDTH_DEFAULT );
211 p_intf->p_sys->i_height = main_GetIntVariable( VOUT_HEIGHT_VAR,
212 VOUT_HEIGHT_DEFAULT );
214 /* Prepare window manager hints and properties */
215 xsize_hints.base_width = p_intf->p_sys->i_width;
216 xsize_hints.base_height = p_intf->p_sys->i_height;
217 xsize_hints.flags = PSize;
218 p_intf->p_sys->wm_protocols = XInternAtom( p_intf->p_sys->p_display,
219 "WM_PROTOCOLS", True );
220 p_intf->p_sys->wm_delete_window = XInternAtom( p_intf->p_sys->p_display,
221 "WM_DELETE_WINDOW", True );
223 /* Prepare window attributes */
224 xwindow_attributes.backing_store = Always; /* save the hidden part */
225 xwindow_attributes.background_pixel = WhitePixel( p_intf->p_sys->p_display,
226 p_intf->p_sys->i_screen );
228 xwindow_attributes.event_mask = ExposureMask | StructureNotifyMask;
230 /* Create the window and set hints - the window must receive ConfigureNotify
231 * events, and, until it is displayed, Expose and MapNotify events. */
232 p_intf->p_sys->window =
233 XCreateWindow( p_intf->p_sys->p_display,
234 DefaultRootWindow( p_intf->p_sys->p_display ),
236 p_intf->p_sys->i_width, p_intf->p_sys->i_height, 1,
238 CWBackingStore | CWBackPixel | CWEventMask,
239 &xwindow_attributes );
241 /* Set window manager hints and properties: size hints, command,
242 * window's name, and accepted protocols */
243 XSetWMNormalHints( p_intf->p_sys->p_display, p_intf->p_sys->window,
245 XSetCommand( p_intf->p_sys->p_display, p_intf->p_sys->window,
246 p_main->ppsz_argv, p_main->i_argc );
247 XStoreName( p_intf->p_sys->p_display, p_intf->p_sys->window, VOUT_TITLE );
249 if( (p_intf->p_sys->wm_protocols == None) /* use WM_DELETE_WINDOW */
250 || (p_intf->p_sys->wm_delete_window == None)
251 || !XSetWMProtocols( p_intf->p_sys->p_display, p_intf->p_sys->window,
252 &p_intf->p_sys->wm_delete_window, 1 ) )
254 /* WM_DELETE_WINDOW is not supported by window manager */
255 intf_Msg("error: missing or bad window manager - please exit program kindly.\n");
258 /* Creation of a graphic context that doesn't generate a GraphicsExpose
259 * event when using functions like XCopyArea */
260 xgcvalues.graphics_exposures = False;
261 p_intf->p_sys->gc = XCreateGC( p_intf->p_sys->p_display, p_intf->p_sys->window,
262 GCGraphicsExposures, &xgcvalues);
264 /* Send orders to server, and wait until window is displayed - three
265 * events must be received: a MapNotify event, an Expose event allowing
266 * drawing in the window, and a ConfigureNotify to get the window
267 * dimensions. Once those events have been received, only ConfigureNotify
268 * events need to be received. */
270 b_configure_notify = 0;
272 XMapWindow( p_intf->p_sys->p_display, p_intf->p_sys->window);
275 XNextEvent( p_intf->p_sys->p_display, &xevent);
276 if( (xevent.type == Expose)
277 && (xevent.xexpose.window == p_intf->p_sys->window) )
281 else if( (xevent.type == MapNotify)
282 && (xevent.xmap.window == p_intf->p_sys->window) )
286 else if( (xevent.type == ConfigureNotify)
287 && (xevent.xconfigure.window == p_intf->p_sys->window) )
289 b_configure_notify = 1;
290 p_intf->p_sys->i_width = xevent.xconfigure.width;
291 p_intf->p_sys->i_height = xevent.xconfigure.height;
293 } while( !( b_expose && b_configure_notify && b_map_notify ) );
295 XSelectInput( p_intf->p_sys->p_display, p_intf->p_sys->window,
296 StructureNotifyMask | KeyPressMask | ButtonPressMask );
298 if( XDefaultDepth(p_intf->p_sys->p_display, p_intf->p_sys->i_screen) == 8 )
300 /* Allocate a new palette */
301 p_intf->p_sys->colormap = XCreateColormap( p_intf->p_sys->p_display,
302 DefaultRootWindow( p_intf->p_sys->p_display ),
303 DefaultVisual( p_intf->p_sys->p_display,
304 p_intf->p_sys->i_screen ),
307 xwindow_attributes.colormap = p_intf->p_sys->colormap;
308 XChangeWindowAttributes( p_intf->p_sys->p_display,
309 p_intf->p_sys->window,
310 CWColormap, &xwindow_attributes );
313 /* At this stage, the window is open, displayed, and ready to receive data */
317 /*****************************************************************************
318 * X11DestroyWindow: destroy X11 main window
319 *****************************************************************************/
320 static void X11DestroyWindow( intf_thread_t *p_intf )
322 XUnmapWindow( p_intf->p_sys->p_display, p_intf->p_sys->window );
323 XFreeGC( p_intf->p_sys->p_display, p_intf->p_sys->gc );
324 XDestroyWindow( p_intf->p_sys->p_display, p_intf->p_sys->window );
327 /*****************************************************************************
328 * X11ManageWindow: manage X11 main window
329 *****************************************************************************/
330 static void X11ManageWindow( intf_thread_t *p_intf )
332 XEvent xevent; /* X11 event */
333 boolean_t b_resized; /* window has been resized */
334 char i_key; /* ISO Latin-1 key */
336 /* Handle X11 events: ConfigureNotify events are parsed to know if the
337 * output window's size changed, MapNotify and UnmapNotify to know if the
338 * window is mapped (and if the display is useful), and ClientMessages
339 * to intercept window destruction requests */
341 while( XCheckWindowEvent( p_intf->p_sys->p_display, p_intf->p_sys->window,
342 StructureNotifyMask | KeyPressMask |
343 ButtonPressMask, &xevent ) == True )
345 /* ConfigureNotify event: prepare */
346 if( (xevent.type == ConfigureNotify)
347 && ((xevent.xconfigure.width != p_intf->p_sys->i_width)
348 || (xevent.xconfigure.height != p_intf->p_sys->i_height)) )
350 /* Update dimensions */
352 p_intf->p_sys->i_width = xevent.xconfigure.width;
353 p_intf->p_sys->i_height = xevent.xconfigure.height;
355 /* MapNotify event: change window status and disable screen saver */
356 else if( xevent.type == MapNotify)
358 if( (p_intf->p_vout != NULL) && !p_intf->p_vout->b_active )
360 X11DisableScreenSaver( p_intf );
361 p_intf->p_vout->b_active = 1;
364 /* UnmapNotify event: change window status and enable screen saver */
365 else if( xevent.type == UnmapNotify )
367 if( (p_intf->p_vout != NULL) && p_intf->p_vout->b_active )
369 X11EnableScreenSaver( p_intf );
370 p_intf->p_vout->b_active = 0;
374 else if( xevent.type == KeyPress )
376 if( XLookupString( &xevent.xkey, &i_key, 1, NULL, NULL ) )
378 if( intf_ProcessKey( p_intf, i_key ) )
380 intf_DbgMsg("unhandled key '%c' (%i)\n", (char) i_key, i_key );
385 else if( xevent.type == ButtonPress )
387 switch( ((XButtonEvent *)&xevent)->button )
390 /* in this part we will eventually manage
391 * clicks for DVD navigation for instance */
395 X11TogglePointer( p_intf );
399 vlc_mutex_lock( &p_intf->p_vout->change_lock );
400 p_intf->p_vout->b_interface = !p_intf->p_vout->b_interface;
401 p_intf->p_vout->i_changes |= VOUT_INTF_CHANGE;
402 vlc_mutex_unlock( &p_intf->p_vout->change_lock );
410 intf_DbgMsg( "%p -> unhandled event type %d received\n",
411 p_intf, xevent.type );
416 /* ClientMessage event - only WM_PROTOCOLS with WM_DELETE_WINDOW data
417 * are handled - according to the man pages, the format is always 32
419 while( XCheckTypedEvent( p_intf->p_sys->p_display,
420 ClientMessage, &xevent ) )
422 if( (xevent.xclient.message_type == p_intf->p_sys->wm_protocols)
423 && (xevent.xclient.data.l[0] == p_intf->p_sys->wm_delete_window ) )
429 intf_DbgMsg( "%p -> unhandled ClientMessage received\n", p_intf );
434 * Handle vout or interface windows resizing
436 if( p_intf->p_vout != NULL )
440 /* If interface window has been resized, change vout size */
441 intf_DbgMsg( "resizing output window\n" );
442 vlc_mutex_lock( &p_intf->p_vout->change_lock );
443 p_intf->p_vout->i_width = p_intf->p_sys->i_width;
444 p_intf->p_vout->i_height = p_intf->p_sys->i_height;
445 p_intf->p_vout->i_changes |= VOUT_SIZE_CHANGE;
446 vlc_mutex_unlock( &p_intf->p_vout->change_lock );
448 else if( (p_intf->p_vout->i_width != p_intf->p_sys->i_width) ||
449 (p_intf->p_vout->i_height != p_intf->p_sys->i_height) )
451 /* If video output size has changed, change interface window size */
452 intf_DbgMsg( "resizing output window\n" );
453 p_intf->p_sys->i_width = p_intf->p_vout->i_width;
454 p_intf->p_sys->i_height = p_intf->p_vout->i_height;
455 XResizeWindow( p_intf->p_sys->p_display, p_intf->p_sys->window,
456 p_intf->p_sys->i_width, p_intf->p_sys->i_height );
461 /*****************************************************************************
462 * X11EnableScreenSaver: enable screen saver
463 *****************************************************************************
464 * This function enable the screen saver on a display after it had been
465 * disabled by XDisableScreenSaver. Both functions use a counter mechanism to
466 * know wether the screen saver can be activated or not: if n successive calls
467 * are made to XDisableScreenSaver, n successive calls to XEnableScreenSaver
468 * will be required before the screen saver could effectively be activated.
469 *****************************************************************************/
470 void X11EnableScreenSaver( intf_thread_t *p_intf )
472 if( p_intf->p_sys->i_ss_count++ == 0 )
474 intf_Msg( "Enabling screen saver\n" );
475 XSetScreenSaver( p_intf->p_sys->p_display, p_intf->p_sys->i_ss_timeout,
476 p_intf->p_sys->i_ss_interval, p_intf->p_sys->i_ss_blanking,
477 p_intf->p_sys->i_ss_exposure );
481 /*****************************************************************************
482 * X11DisableScreenSaver: disable screen saver
483 *****************************************************************************
484 * See XEnableScreenSaver
485 *****************************************************************************/
486 void X11DisableScreenSaver( intf_thread_t *p_intf )
488 if( --p_intf->p_sys->i_ss_count == 0 )
490 /* Save screen saver informations */
491 XGetScreenSaver( p_intf->p_sys->p_display, &p_intf->p_sys->i_ss_timeout,
492 &p_intf->p_sys->i_ss_interval, &p_intf->p_sys->i_ss_blanking,
493 &p_intf->p_sys->i_ss_exposure );
495 /* Disable screen saver */
496 intf_Msg("Disabling screen saver\n");
497 XSetScreenSaver( p_intf->p_sys->p_display, 0,
498 p_intf->p_sys->i_ss_interval, p_intf->p_sys->i_ss_blanking,
499 p_intf->p_sys->i_ss_exposure );
503 /*****************************************************************************
504 * X11TogglePointer: hide or show the mouse pointer
505 *****************************************************************************
506 * This function hides the X pointer if it is visible by putting it at
507 * coordinates (32,32) and setting the pointer sprite to a blank one. To
508 * show it again, we disable the sprite and restore the original coordinates.
509 *****************************************************************************/
510 void X11TogglePointer( intf_thread_t *p_intf )
512 static Cursor cursor;
513 static boolean_t b_cursor = 0;
515 if( p_intf->p_sys->b_mouse )
517 p_intf->p_sys->b_mouse = 0;
522 Pixmap blank = XCreatePixmap( p_intf->p_sys->p_display,
523 DefaultRootWindow(p_intf->p_sys->p_display),
526 XParseColor( p_intf->p_sys->p_display,
527 XCreateColormap( p_intf->p_sys->p_display,
529 p_intf->p_sys->p_display ),
531 p_intf->p_sys->p_display,
532 p_intf->p_sys->i_screen ),
536 cursor = XCreatePixmapCursor( p_intf->p_sys->p_display,
537 blank, blank, &color, &color, 1, 1 );
541 XDefineCursor( p_intf->p_sys->p_display,
542 p_intf->p_sys->window, cursor );
546 p_intf->p_sys->b_mouse = 1;
548 XUndefineCursor( p_intf->p_sys->p_display, p_intf->p_sys->window );