1 /*****************************************************************************
2 * intf_x11.c: X11 interface
3 *****************************************************************************
4 * Copyright (C) 1999, 2000 VideoLAN
5 * $Id: intf_x11.c,v 1.7 2001/01/15 06:18:23 sam Exp $
9 * This program is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; either version 2 of the License, or
12 * (at your option) any later version.
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
19 * You should have received a copy of the GNU General Public License
20 * along with this program; if not, write to the Free Software
21 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA.
22 *****************************************************************************/
24 /*****************************************************************************
26 *****************************************************************************/
29 #include <errno.h> /* ENOMEM */
30 #include <stdlib.h> /* free() */
31 #include <string.h> /* strerror() */
34 #include <X11/Xutil.h>
35 #include <X11/keysym.h>
43 #include "stream_control.h"
44 #include "input_ext-intf.h"
47 #include "video_output.h"
50 #include "interface.h"
54 /*****************************************************************************
55 * intf_sys_t: description and status of X11 interface
56 *****************************************************************************/
57 typedef struct intf_sys_s
59 /* X11 generic properties */
60 Display * p_display; /* X11 display pointer */
61 int i_screen; /* X11 screen */
63 Atom wm_delete_window;
65 /* Main window properties */
66 Window window; /* main window */
67 GC gc; /* graphic context for main window */
68 int i_width; /* width of main window */
69 int i_height; /* height of main window */
70 Colormap colormap; /* colormap used (8bpp only) */
72 /* Screen saver properties */
73 int i_ss_count; /* enabling/disabling count */
74 int i_ss_timeout; /* timeout */
75 int i_ss_interval; /* interval between changes */
76 int i_ss_blanking; /* blanking mode */
77 int i_ss_exposure; /* exposure mode */
79 /* Mouse pointer properties */
80 boolean_t b_mouse; /* is the mouse pointer displayed ? */
84 /*****************************************************************************
86 *****************************************************************************/
87 static int X11CreateWindow ( intf_thread_t *p_intf );
88 static void X11DestroyWindow ( intf_thread_t *p_intf );
89 static void X11ManageWindow ( intf_thread_t *p_intf );
90 static void X11EnableScreenSaver ( intf_thread_t *p_intf );
91 static void X11DisableScreenSaver ( intf_thread_t *p_intf );
92 static void X11TogglePointer ( intf_thread_t *p_intf );
94 /*****************************************************************************
95 * intf_X11Create: initialize and create window
96 *****************************************************************************/
97 int intf_X11Create( intf_thread_t *p_intf )
101 /* Allocate instance and initialize some members */
102 p_intf->p_sys = malloc( sizeof( intf_sys_t ) );
103 if( p_intf->p_sys == NULL )
105 intf_ErrMsg("error: %s", strerror(ENOMEM));
109 /* Open display, unsing 'vlc_display' or DISPLAY environment variable */
110 psz_display = XDisplayName( main_GetPszVariable( VOUT_DISPLAY_VAR, NULL ) );
111 p_intf->p_sys->p_display = XOpenDisplay( psz_display );
112 if( !p_intf->p_sys->p_display ) /* error */
114 intf_ErrMsg("error: can't open display %s", psz_display );
115 free( p_intf->p_sys );
118 p_intf->p_sys->i_screen = DefaultScreen( p_intf->p_sys->p_display );
120 /* Spawn base window - this window will include the video output window,
121 * but also command buttons, subtitles and other indicators */
122 if( X11CreateWindow( p_intf ) )
124 intf_ErrMsg("error: can't create interface window" );
125 XCloseDisplay( p_intf->p_sys->p_display );
126 free( p_intf->p_sys );
130 /* Spawn video output thread */
131 if( p_main->b_video )
133 p_intf->p_vout = vout_CreateThread( psz_display, p_intf->p_sys->window,
134 p_intf->p_sys->i_width,
135 p_intf->p_sys->i_height, NULL, 0,
136 (void *)&p_intf->p_sys->colormap );
138 if( p_intf->p_vout == NULL ) /* error */
140 intf_ErrMsg("error: can't create video output thread" );
141 X11DestroyWindow( p_intf );
142 XCloseDisplay( p_intf->p_sys->p_display );
143 free( p_intf->p_sys );
148 p_intf->p_sys->b_mouse = 1;
151 intf_AssignNormalKeys( p_intf );
153 /* Disable screen saver and return */
154 p_intf->p_sys->i_ss_count = 1;
155 X11DisableScreenSaver( p_intf );
159 /*****************************************************************************
160 * intf_X11Destroy: destroy interface window
161 *****************************************************************************/
162 void intf_X11Destroy( intf_thread_t *p_intf )
164 /* Enable screen saver */
165 X11EnableScreenSaver( p_intf );
167 /* Close input thread, if any (blocking) */
168 if( p_intf->p_input )
170 input_DestroyThread( p_intf->p_input, NULL );
173 /* Close video output thread, if any (blocking) */
176 vout_DestroyThread( p_intf->p_vout, NULL );
179 /* Close main window and display */
180 X11DestroyWindow( p_intf );
181 XCloseDisplay( p_intf->p_sys->p_display );
183 /* Destroy structure */
184 free( p_intf->p_sys );
188 /*****************************************************************************
189 * intf_X11Manage: event loop
190 *****************************************************************************/
191 void intf_X11Manage( intf_thread_t *p_intf )
193 /* Manage main window */
194 X11ManageWindow( p_intf );
197 /* following functions are local */
199 /*****************************************************************************
200 * X11CreateWindow: open and set-up X11 main window
201 *****************************************************************************/
202 static int X11CreateWindow( intf_thread_t *p_intf )
204 XSizeHints xsize_hints;
205 XSetWindowAttributes xwindow_attributes;
209 boolean_t b_configure_notify;
210 boolean_t b_map_notify;
212 /* Set main window's size */
213 p_intf->p_sys->i_width = main_GetIntVariable( VOUT_WIDTH_VAR,
214 VOUT_WIDTH_DEFAULT );
215 p_intf->p_sys->i_height = main_GetIntVariable( VOUT_HEIGHT_VAR,
216 VOUT_HEIGHT_DEFAULT );
218 /* Prepare window manager hints and properties */
219 xsize_hints.base_width = p_intf->p_sys->i_width;
220 xsize_hints.base_height = p_intf->p_sys->i_height;
221 xsize_hints.flags = PSize;
222 p_intf->p_sys->wm_protocols = XInternAtom( p_intf->p_sys->p_display,
223 "WM_PROTOCOLS", True );
224 p_intf->p_sys->wm_delete_window = XInternAtom( p_intf->p_sys->p_display,
225 "WM_DELETE_WINDOW", True );
227 /* Prepare window attributes */
228 xwindow_attributes.backing_store = Always; /* save the hidden part */
229 xwindow_attributes.background_pixel = WhitePixel( p_intf->p_sys->p_display,
230 p_intf->p_sys->i_screen );
232 xwindow_attributes.event_mask = ExposureMask | StructureNotifyMask;
234 /* Create the window and set hints - the window must receive ConfigureNotify
235 * events, and, until it is displayed, Expose and MapNotify events. */
236 p_intf->p_sys->window =
237 XCreateWindow( p_intf->p_sys->p_display,
238 DefaultRootWindow( p_intf->p_sys->p_display ),
240 p_intf->p_sys->i_width, p_intf->p_sys->i_height, 1,
242 CWBackingStore | CWBackPixel | CWEventMask,
243 &xwindow_attributes );
245 /* Set window manager hints and properties: size hints, command,
246 * window's name, and accepted protocols */
247 XSetWMNormalHints( p_intf->p_sys->p_display, p_intf->p_sys->window,
249 XSetCommand( p_intf->p_sys->p_display, p_intf->p_sys->window,
250 p_main->ppsz_argv, p_main->i_argc );
251 XStoreName( p_intf->p_sys->p_display, p_intf->p_sys->window, VOUT_TITLE );
253 if( (p_intf->p_sys->wm_protocols == None) /* use WM_DELETE_WINDOW */
254 || (p_intf->p_sys->wm_delete_window == None)
255 || !XSetWMProtocols( p_intf->p_sys->p_display, p_intf->p_sys->window,
256 &p_intf->p_sys->wm_delete_window, 1 ) )
258 /* WM_DELETE_WINDOW is not supported by window manager */
259 intf_Msg("intf error: missing or bad window manager - please exit program kindly.");
262 /* Creation of a graphic context that doesn't generate a GraphicsExpose
263 * event when using functions like XCopyArea */
264 xgcvalues.graphics_exposures = False;
265 p_intf->p_sys->gc = XCreateGC( p_intf->p_sys->p_display, p_intf->p_sys->window,
266 GCGraphicsExposures, &xgcvalues);
268 /* Send orders to server, and wait until window is displayed - three
269 * events must be received: a MapNotify event, an Expose event allowing
270 * drawing in the window, and a ConfigureNotify to get the window
271 * dimensions. Once those events have been received, only ConfigureNotify
272 * events need to be received. */
274 b_configure_notify = 0;
276 XMapWindow( p_intf->p_sys->p_display, p_intf->p_sys->window);
279 XNextEvent( p_intf->p_sys->p_display, &xevent);
280 if( (xevent.type == Expose)
281 && (xevent.xexpose.window == p_intf->p_sys->window) )
285 else if( (xevent.type == MapNotify)
286 && (xevent.xmap.window == p_intf->p_sys->window) )
290 else if( (xevent.type == ConfigureNotify)
291 && (xevent.xconfigure.window == p_intf->p_sys->window) )
293 b_configure_notify = 1;
294 p_intf->p_sys->i_width = xevent.xconfigure.width;
295 p_intf->p_sys->i_height = xevent.xconfigure.height;
297 } while( !( b_expose && b_configure_notify && b_map_notify ) );
299 XSelectInput( p_intf->p_sys->p_display, p_intf->p_sys->window,
300 StructureNotifyMask | KeyPressMask | ButtonPressMask );
302 if( XDefaultDepth(p_intf->p_sys->p_display, p_intf->p_sys->i_screen) == 8 )
304 /* Allocate a new palette */
305 p_intf->p_sys->colormap = XCreateColormap( p_intf->p_sys->p_display,
306 DefaultRootWindow( p_intf->p_sys->p_display ),
307 DefaultVisual( p_intf->p_sys->p_display,
308 p_intf->p_sys->i_screen ),
311 xwindow_attributes.colormap = p_intf->p_sys->colormap;
312 XChangeWindowAttributes( p_intf->p_sys->p_display,
313 p_intf->p_sys->window,
314 CWColormap, &xwindow_attributes );
317 /* At this stage, the window is open, displayed, and ready to receive data */
321 /*****************************************************************************
322 * X11DestroyWindow: destroy X11 main window
323 *****************************************************************************/
324 static void X11DestroyWindow( intf_thread_t *p_intf )
326 XUnmapWindow( p_intf->p_sys->p_display, p_intf->p_sys->window );
327 XFreeGC( p_intf->p_sys->p_display, p_intf->p_sys->gc );
328 XDestroyWindow( p_intf->p_sys->p_display, p_intf->p_sys->window );
331 /*****************************************************************************
332 * X11ManageWindow: manage X11 main window
333 *****************************************************************************/
334 static void X11ManageWindow( intf_thread_t *p_intf )
336 XEvent xevent; /* X11 event */
337 boolean_t b_resized; /* window has been resized */
338 char i_key; /* ISO Latin-1 key */
340 /* Handle X11 events: ConfigureNotify events are parsed to know if the
341 * output window's size changed, MapNotify and UnmapNotify to know if the
342 * window is mapped (and if the display is useful), and ClientMessages
343 * to intercept window destruction requests */
345 while( XCheckWindowEvent( p_intf->p_sys->p_display, p_intf->p_sys->window,
346 StructureNotifyMask | KeyPressMask |
347 ButtonPressMask, &xevent ) == True )
349 /* ConfigureNotify event: prepare */
350 if( (xevent.type == ConfigureNotify)
351 && ((xevent.xconfigure.width != p_intf->p_sys->i_width)
352 || (xevent.xconfigure.height != p_intf->p_sys->i_height)) )
354 /* Update dimensions */
356 p_intf->p_sys->i_width = xevent.xconfigure.width;
357 p_intf->p_sys->i_height = xevent.xconfigure.height;
359 /* MapNotify event: change window status and disable screen saver */
360 else if( xevent.type == MapNotify)
362 if( (p_intf->p_vout != NULL) && !p_intf->p_vout->b_active )
364 X11DisableScreenSaver( p_intf );
365 p_intf->p_vout->b_active = 1;
368 /* UnmapNotify event: change window status and enable screen saver */
369 else if( xevent.type == UnmapNotify )
371 if( (p_intf->p_vout != NULL) && p_intf->p_vout->b_active )
373 X11EnableScreenSaver( p_intf );
374 p_intf->p_vout->b_active = 0;
378 else if( xevent.type == KeyPress )
380 if( XLookupString( &xevent.xkey, &i_key, 1, NULL, NULL ) )
382 if( intf_ProcessKey( p_intf, i_key ) )
384 intf_DbgMsg("unhandled key '%c' (%i)", (char) i_key, i_key );
389 else if( xevent.type == ButtonPress )
391 switch( ((XButtonEvent *)&xevent)->button )
394 /* in this part we will eventually manage
395 * clicks for DVD navigation for instance */
399 X11TogglePointer( p_intf );
403 vlc_mutex_lock( &p_intf->p_vout->change_lock );
404 p_intf->p_vout->b_interface = !p_intf->p_vout->b_interface;
405 p_intf->p_vout->i_changes |= VOUT_INTF_CHANGE;
406 vlc_mutex_unlock( &p_intf->p_vout->change_lock );
414 intf_DbgMsg( "%p -> unhandled event type %d received",
415 p_intf, xevent.type );
420 /* ClientMessage event - only WM_PROTOCOLS with WM_DELETE_WINDOW data
421 * are handled - according to the man pages, the format is always 32
423 while( XCheckTypedEvent( p_intf->p_sys->p_display,
424 ClientMessage, &xevent ) )
426 if( (xevent.xclient.message_type == p_intf->p_sys->wm_protocols)
427 && (xevent.xclient.data.l[0] == p_intf->p_sys->wm_delete_window ) )
433 intf_DbgMsg( "%p -> unhandled ClientMessage received", p_intf );
438 * Handle vout or interface windows resizing
440 if( p_intf->p_vout != NULL )
444 /* If interface window has been resized, change vout size */
445 intf_DbgMsg( "resizing output window" );
446 vlc_mutex_lock( &p_intf->p_vout->change_lock );
447 p_intf->p_vout->i_width = p_intf->p_sys->i_width;
448 p_intf->p_vout->i_height = p_intf->p_sys->i_height;
449 p_intf->p_vout->i_changes |= VOUT_SIZE_CHANGE;
450 vlc_mutex_unlock( &p_intf->p_vout->change_lock );
452 else if( (p_intf->p_vout->i_width != p_intf->p_sys->i_width) ||
453 (p_intf->p_vout->i_height != p_intf->p_sys->i_height) )
455 /* If video output size has changed, change interface window size */
456 intf_DbgMsg( "resizing output window" );
457 p_intf->p_sys->i_width = p_intf->p_vout->i_width;
458 p_intf->p_sys->i_height = p_intf->p_vout->i_height;
459 XResizeWindow( p_intf->p_sys->p_display, p_intf->p_sys->window,
460 p_intf->p_sys->i_width, p_intf->p_sys->i_height );
465 /*****************************************************************************
466 * X11EnableScreenSaver: enable screen saver
467 *****************************************************************************
468 * This function enable the screen saver on a display after it had been
469 * disabled by XDisableScreenSaver. Both functions use a counter mechanism to
470 * know wether the screen saver can be activated or not: if n successive calls
471 * are made to XDisableScreenSaver, n successive calls to XEnableScreenSaver
472 * will be required before the screen saver could effectively be activated.
473 *****************************************************************************/
474 void X11EnableScreenSaver( intf_thread_t *p_intf )
476 if( p_intf->p_sys->i_ss_count++ == 0 )
478 intf_DbgMsg( "intf: enabling screen saver" );
479 XSetScreenSaver( p_intf->p_sys->p_display, p_intf->p_sys->i_ss_timeout,
480 p_intf->p_sys->i_ss_interval, p_intf->p_sys->i_ss_blanking,
481 p_intf->p_sys->i_ss_exposure );
485 /*****************************************************************************
486 * X11DisableScreenSaver: disable screen saver
487 *****************************************************************************
488 * See XEnableScreenSaver
489 *****************************************************************************/
490 void X11DisableScreenSaver( intf_thread_t *p_intf )
492 if( --p_intf->p_sys->i_ss_count == 0 )
494 /* Save screen saver informations */
495 XGetScreenSaver( p_intf->p_sys->p_display, &p_intf->p_sys->i_ss_timeout,
496 &p_intf->p_sys->i_ss_interval, &p_intf->p_sys->i_ss_blanking,
497 &p_intf->p_sys->i_ss_exposure );
499 /* Disable screen saver */
500 intf_DbgMsg("intf: disabling screen saver");
501 XSetScreenSaver( p_intf->p_sys->p_display, 0,
502 p_intf->p_sys->i_ss_interval, p_intf->p_sys->i_ss_blanking,
503 p_intf->p_sys->i_ss_exposure );
507 /*****************************************************************************
508 * X11TogglePointer: hide or show the mouse pointer
509 *****************************************************************************
510 * This function hides the X pointer if it is visible by putting it at
511 * coordinates (32,32) and setting the pointer sprite to a blank one. To
512 * show it again, we disable the sprite and restore the original coordinates.
513 *****************************************************************************/
514 void X11TogglePointer( intf_thread_t *p_intf )
516 static Cursor cursor;
517 static boolean_t b_cursor = 0;
519 if( p_intf->p_sys->b_mouse )
521 p_intf->p_sys->b_mouse = 0;
526 Pixmap blank = XCreatePixmap( p_intf->p_sys->p_display,
527 DefaultRootWindow(p_intf->p_sys->p_display),
530 XParseColor( p_intf->p_sys->p_display,
531 XCreateColormap( p_intf->p_sys->p_display,
533 p_intf->p_sys->p_display ),
535 p_intf->p_sys->p_display,
536 p_intf->p_sys->i_screen ),
540 cursor = XCreatePixmapCursor( p_intf->p_sys->p_display,
541 blank, blank, &color, &color, 1, 1 );
545 XDefineCursor( p_intf->p_sys->p_display,
546 p_intf->p_sys->window, cursor );
550 p_intf->p_sys->b_mouse = 1;
552 XUndefineCursor( p_intf->p_sys->p_display, p_intf->p_sys->window );