1 /*****************************************************************************
2 * intf_mga.c: MGA 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 */
70 /* Screen saver properties */
71 int i_ss_count; /* enabling/disabling count */
72 int i_ss_timeout; /* timeout */
73 int i_ss_interval; /* interval between changes */
74 int i_ss_blanking; /* blanking mode */
75 int i_ss_exposure; /* exposure mode */
77 /* Mouse pointer properties */
78 boolean_t b_mouse; /* is the mouse pointer displayed ? */
82 /*****************************************************************************
84 *****************************************************************************/
85 static int X11CreateWindow ( intf_thread_t *p_intf );
86 static void X11DestroyWindow ( intf_thread_t *p_intf );
87 static void X11ManageWindow ( intf_thread_t *p_intf );
88 static void X11EnableScreenSaver ( intf_thread_t *p_intf );
89 static void X11DisableScreenSaver ( intf_thread_t *p_intf );
90 static void X11TogglePointer ( intf_thread_t *p_intf );
92 /*****************************************************************************
93 * intf_MGACreate: initialize and create window
94 *****************************************************************************/
95 int intf_MGACreate( intf_thread_t *p_intf )
99 /* Allocate instance and initialize some members */
100 p_intf->p_sys = malloc( sizeof( intf_sys_t ) );
101 if( p_intf->p_sys == NULL )
103 intf_ErrMsg("error: %s\n", strerror(ENOMEM));
107 /* Open display, unsing 'vlc_display' or DISPLAY environment variable */
108 psz_display = XDisplayName( main_GetPszVariable( VOUT_DISPLAY_VAR, NULL ) );
109 p_intf->p_sys->p_display = XOpenDisplay( psz_display );
110 if( !p_intf->p_sys->p_display ) /* error */
112 intf_ErrMsg("error: can't open display %s\n", psz_display );
113 free( p_intf->p_sys );
116 p_intf->p_sys->i_screen = DefaultScreen( p_intf->p_sys->p_display );
118 /* Spawn base window - this window will include the video output window,
119 * but also command buttons, subtitles and other indicators */
120 if( X11CreateWindow( p_intf ) )
122 intf_ErrMsg("error: can't create interface window\n" );
123 XCloseDisplay( p_intf->p_sys->p_display );
124 free( p_intf->p_sys );
128 /* Spawn video output thread */
129 if( p_main->b_video )
131 p_intf->p_vout = vout_CreateThread( psz_display, p_intf->p_sys->window,
132 p_intf->p_sys->i_width,
133 p_intf->p_sys->i_height, NULL, 0, NULL );
134 if( p_intf->p_vout == NULL ) /* error */
136 intf_ErrMsg("error: can't create video output thread\n" );
137 X11DestroyWindow( p_intf );
138 XCloseDisplay( p_intf->p_sys->p_display );
139 free( p_intf->p_sys );
144 p_intf->p_sys->b_mouse = 1;
146 /* Disable screen saver and return */
147 p_intf->p_sys->i_ss_count = 1;
148 X11DisableScreenSaver( p_intf );
152 /*****************************************************************************
153 * intf_MGADestroy: destroy interface window
154 *****************************************************************************/
155 void intf_MGADestroy( intf_thread_t *p_intf )
157 /* Enable screen saver */
158 X11EnableScreenSaver( p_intf );
160 /* Close input thread, if any (blocking) */
161 if( p_intf->p_input )
163 input_DestroyThread( p_intf->p_input, NULL );
166 /* Close video output thread, if any (blocking) */
169 vout_DestroyThread( p_intf->p_vout, NULL );
172 /* Close main window and display */
173 X11DestroyWindow( p_intf );
174 XCloseDisplay( p_intf->p_sys->p_display );
176 /* Destroy structure */
177 free( p_intf->p_sys );
181 /*****************************************************************************
182 * intf_MGAManage: event loop
183 *****************************************************************************/
184 void intf_MGAManage( intf_thread_t *p_intf )
186 /* Manage main window */
187 X11ManageWindow( p_intf );
190 /* following functions are local */
192 /*****************************************************************************
193 * X11CreateWindow: open and set-up X11 main window
194 *****************************************************************************/
195 static int X11CreateWindow( intf_thread_t *p_intf )
197 XSizeHints xsize_hints;
198 XSetWindowAttributes xwindow_attributes;
202 boolean_t b_configure_notify;
203 boolean_t b_map_notify;
205 /* Set main window's size */
206 p_intf->p_sys->i_width = main_GetIntVariable( VOUT_WIDTH_VAR, VOUT_WIDTH_DEFAULT );
207 p_intf->p_sys->i_height = main_GetIntVariable( VOUT_HEIGHT_VAR, VOUT_HEIGHT_DEFAULT );
209 /* Prepare window manager hints and properties */
210 xsize_hints.base_width = p_intf->p_sys->i_width;
211 xsize_hints.base_height = p_intf->p_sys->i_height;
212 xsize_hints.flags = PSize;
213 p_intf->p_sys->wm_protocols = XInternAtom( p_intf->p_sys->p_display,
214 "WM_PROTOCOLS", True );
215 p_intf->p_sys->wm_delete_window = XInternAtom( p_intf->p_sys->p_display,
216 "WM_DELETE_WINDOW", True );
218 /* Prepare window attributes */
219 xwindow_attributes.backing_store = Always; /* save the hidden part */
220 xwindow_attributes.background_pixel = WhitePixel( p_intf->p_sys->p_display,
221 p_intf->p_sys->i_screen );
223 /* Create the window and set hints - the window must receive ConfigureNotify
224 * events, and, until it is displayed, Expose and MapNotify events. */
225 p_intf->p_sys->window = XCreateSimpleWindow( p_intf->p_sys->p_display,
226 DefaultRootWindow( p_intf->p_sys->p_display ),
228 p_intf->p_sys->i_width, p_intf->p_sys->i_height,
230 XSelectInput( p_intf->p_sys->p_display, p_intf->p_sys->window,
231 ExposureMask | StructureNotifyMask );
232 XChangeWindowAttributes( p_intf->p_sys->p_display, p_intf->p_sys->window,
233 CWBackingStore | CWBackPixel, &xwindow_attributes);
235 /* Set window manager hints and properties: size hints, command, window's name,
236 * and accepted protocols */
237 XSetWMNormalHints( p_intf->p_sys->p_display, p_intf->p_sys->window, &xsize_hints );
238 XSetCommand( p_intf->p_sys->p_display, p_intf->p_sys->window,
239 p_main->ppsz_argv, p_main->i_argc );
240 XStoreName( p_intf->p_sys->p_display, p_intf->p_sys->window, VOUT_TITLE );
241 if( (p_intf->p_sys->wm_protocols == None) /* use WM_DELETE_WINDOW */
242 || (p_intf->p_sys->wm_delete_window == None)
243 || !XSetWMProtocols( p_intf->p_sys->p_display, p_intf->p_sys->window,
244 &p_intf->p_sys->wm_delete_window, 1 ) )
246 /* WM_DELETE_WINDOW is not supported by window manager */
247 intf_Msg("error: missing or bad window manager - please exit program kindly.\n");
250 /* Creation of a graphic context that doesn't generate a GraphicsExpose event
251 when using functions like XCopyArea */
252 xgcvalues.graphics_exposures = False;
253 p_intf->p_sys->gc = XCreateGC( p_intf->p_sys->p_display, p_intf->p_sys->window,
254 GCGraphicsExposures, &xgcvalues);
256 /* Send orders to server, and wait until window is displayed - three events
257 * must be received: a MapNotify event, an Expose event allowing drawing in the
258 * window, and a ConfigureNotify to get the window dimensions. Once those events
259 * have been received, only ConfigureNotify events need to be received. */
261 b_configure_notify = 0;
263 XMapWindow( p_intf->p_sys->p_display, p_intf->p_sys->window);
266 XNextEvent( p_intf->p_sys->p_display, &xevent);
267 if( (xevent.type == Expose)
268 && (xevent.xexpose.window == p_intf->p_sys->window) )
272 else if( (xevent.type == MapNotify)
273 && (xevent.xmap.window == p_intf->p_sys->window) )
277 else if( (xevent.type == ConfigureNotify)
278 && (xevent.xconfigure.window == p_intf->p_sys->window) )
280 b_configure_notify = 1;
281 p_intf->p_sys->i_width = xevent.xconfigure.width;
282 p_intf->p_sys->i_height = xevent.xconfigure.height;
285 while( !( b_expose && b_configure_notify && b_map_notify ) );
286 XSelectInput( p_intf->p_sys->p_display, p_intf->p_sys->window,
287 StructureNotifyMask | KeyPressMask | ButtonPressMask );
289 /* At this stage, the window is openned, displayed, and ready to receive data */
293 /*****************************************************************************
294 * X11DestroyWindow: destroy X11 main window
295 *****************************************************************************/
296 static void X11DestroyWindow( intf_thread_t *p_intf )
298 XUnmapWindow( p_intf->p_sys->p_display, p_intf->p_sys->window );
299 XFreeGC( p_intf->p_sys->p_display, p_intf->p_sys->gc );
300 XDestroyWindow( p_intf->p_sys->p_display, p_intf->p_sys->window );
303 /*****************************************************************************
304 * X11ManageWindow: manage X11 main window
305 *****************************************************************************/
306 static void X11ManageWindow( intf_thread_t *p_intf )
308 XEvent xevent; /* X11 event */
309 boolean_t b_resized; /* window has been resized */
310 char i_key; /* ISO Latin-1 key */
312 /* Handle X11 events: ConfigureNotify events are parsed to know if the
313 * output window's size changed, MapNotify and UnmapNotify to know if the
314 * window is mapped (and if the display is useful), and ClientMessages
315 * to intercept window destruction requests */
317 while( XCheckWindowEvent( p_intf->p_sys->p_display, p_intf->p_sys->window,
318 StructureNotifyMask | KeyPressMask |
319 ButtonPressMask, &xevent ) == True )
321 /* ConfigureNotify event: prepare */
322 if( (xevent.type == ConfigureNotify)
323 && ((xevent.xconfigure.width != p_intf->p_sys->i_width)
324 || (xevent.xconfigure.height != p_intf->p_sys->i_height)) )
326 /* Update dimensions */
328 p_intf->p_sys->i_width = xevent.xconfigure.width;
329 p_intf->p_sys->i_height = xevent.xconfigure.height;
331 /* MapNotify event: change window status and disable screen saver */
332 else if( xevent.type == MapNotify)
334 if( (p_intf->p_vout != NULL) && !p_intf->p_vout->b_active )
336 X11DisableScreenSaver( p_intf );
337 p_intf->p_vout->b_active = 1;
340 /* UnmapNotify event: change window status and enable screen saver */
341 else if( xevent.type == UnmapNotify )
343 if( (p_intf->p_vout != NULL) && p_intf->p_vout->b_active )
345 X11EnableScreenSaver( p_intf );
346 p_intf->p_vout->b_active = 0;
349 /* DestroyNotify event: window has been destroyed */
350 else if( xevent.type == DestroyNotify )
352 intf_ErrMsg( "vout: window destroyed !\n");
355 else if( xevent.type == KeyPress )
357 if( XLookupString( &xevent.xkey, &i_key, 1, NULL, NULL ) )
359 if( intf_ProcessKey( p_intf, i_key ) )
361 intf_DbgMsg("unhandled key '%c' (%i)\n", (char) i_key, i_key );
366 else if( xevent.type == ButtonPress )
368 switch( ((XButtonEvent *)&xevent)->button )
371 /* in this part we will eventually manage
372 * clicks for DVD navigation for instance */
376 X11TogglePointer( p_intf );
380 vlc_mutex_lock( &p_intf->p_vout->change_lock );
381 p_intf->p_vout->b_interface = !p_intf->p_vout->b_interface;
382 p_intf->p_vout->i_changes |= VOUT_INTF_CHANGE;
383 vlc_mutex_unlock( &p_intf->p_vout->change_lock );
387 /* ClientMessage event - only WM_PROTOCOLS with WM_DELETE_WINDOW data
388 * are handled - according to the man pages, the format is always 32
390 else if( (xevent.type == ClientMessage)
391 && (xevent.xclient.message_type == p_intf->p_sys->wm_protocols)
392 && (xevent.xclient.data.l[0] == p_intf->p_sys->wm_delete_window ) )
394 /* FIXME: this never happens :( how to receive wm messages ?? */
395 intf_DbgMsg("ClientMessage received\n");
401 intf_DbgMsg("%p -> unhandled event type %d received\n", p_intf, xevent.type );
407 * Handle vout or interface windows resizing
409 if( p_intf->p_vout != NULL )
413 /* If interface window has been resized, change vout size */
414 intf_DbgMsg("resizing output window\n");
415 vlc_mutex_lock( &p_intf->p_vout->change_lock );
416 p_intf->p_vout->i_width = p_intf->p_sys->i_width;
417 p_intf->p_vout->i_height = p_intf->p_sys->i_height;
418 p_intf->p_vout->i_changes |= VOUT_SIZE_CHANGE;
419 vlc_mutex_unlock( &p_intf->p_vout->change_lock );
421 else if( (p_intf->p_vout->i_width != p_intf->p_sys->i_width) ||
422 (p_intf->p_vout->i_height != p_intf->p_sys->i_height) )
424 /* If video output size has changed, change interface window size */
425 intf_DbgMsg("resizing interface window\n");
426 p_intf->p_sys->i_width = p_intf->p_vout->i_width;
427 p_intf->p_sys->i_height = p_intf->p_vout->i_height;
428 XResizeWindow( p_intf->p_sys->p_display, p_intf->p_sys->window,
429 p_intf->p_sys->i_width, p_intf->p_sys->i_height );
434 /*****************************************************************************
435 * X11EnableScreenSaver: enable screen saver
436 *****************************************************************************
437 * This function enable the screen saver on a display after it had been
438 * disabled by XDisableScreenSaver. Both functions use a counter mechanism to
439 * know wether the screen saver can be activated or not: if n successive calls
440 * are made to XDisableScreenSaver, n successive calls to XEnableScreenSaver
441 * will be required before the screen saver could effectively be activated.
442 *****************************************************************************/
443 void X11EnableScreenSaver( intf_thread_t *p_intf )
445 if( p_intf->p_sys->i_ss_count++ == 0 )
447 intf_Msg("Enabling screen saver\n");
448 XSetScreenSaver( p_intf->p_sys->p_display, p_intf->p_sys->i_ss_timeout,
449 p_intf->p_sys->i_ss_interval, p_intf->p_sys->i_ss_blanking,
450 p_intf->p_sys->i_ss_exposure );
454 /*****************************************************************************
455 * X11DisableScreenSaver: disable screen saver
456 *****************************************************************************
457 * See XEnableScreenSaver
458 *****************************************************************************/
459 void X11DisableScreenSaver( intf_thread_t *p_intf )
461 if( --p_intf->p_sys->i_ss_count == 0 )
463 /* Save screen saver informations */
464 XGetScreenSaver( p_intf->p_sys->p_display, &p_intf->p_sys->i_ss_timeout,
465 &p_intf->p_sys->i_ss_interval, &p_intf->p_sys->i_ss_blanking,
466 &p_intf->p_sys->i_ss_exposure );
468 /* Disable screen saver */
469 intf_Msg("Disabling screen saver\n");
470 XSetScreenSaver( p_intf->p_sys->p_display, 0,
471 p_intf->p_sys->i_ss_interval, p_intf->p_sys->i_ss_blanking,
472 p_intf->p_sys->i_ss_exposure );
476 /*****************************************************************************
477 * X11TogglePointer: hide or show the mouse pointer
478 *****************************************************************************
479 * This function hides the X pointer if it is visible by putting it at
480 * coordinates (32,32) and setting the pointer sprite to a blank one. To
481 * show it again, we disable the sprite and restore the original coordinates.
482 *****************************************************************************/
483 void X11TogglePointer( intf_thread_t *p_intf )
485 static Cursor cursor;
486 static boolean_t b_cursor = 0;
488 if( p_intf->p_sys->b_mouse )
490 p_intf->p_sys->b_mouse = 0;
495 Pixmap blank = XCreatePixmap( p_intf->p_sys->p_display,
496 DefaultRootWindow(p_intf->p_sys->p_display),
499 XParseColor( p_intf->p_sys->p_display,
500 XCreateColormap( p_intf->p_sys->p_display,
502 p_intf->p_sys->p_display ),
504 p_intf->p_sys->p_display,
505 p_intf->p_sys->i_screen ),
509 cursor = XCreatePixmapCursor( p_intf->p_sys->p_display,
510 blank, blank, &color, &color, 1, 1 );
514 XDefineCursor( p_intf->p_sys->p_display,
515 p_intf->p_sys->window, cursor );
519 p_intf->p_sys->b_mouse = 1;
521 XUndefineCursor( p_intf->p_sys->p_display, p_intf->p_sys->window );