]> git.sesse.net Git - vlc/blob - modules/video_output/ggi.c
Don't include config.h from the headers - refs #297.
[vlc] / modules / video_output / ggi.c
1 /*****************************************************************************
2  * ggi.c : GGI plugin for vlc
3  *****************************************************************************
4  * Copyright (C) 2000, 2001 the VideoLAN team
5  * $Id$
6  *
7  * Authors: Vincent Seguin <seguin@via.ecp.fr>
8  *          Samuel Hocevar <sam@zoy.org>
9  *
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.
14  *
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.
19  *
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., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
23  *****************************************************************************/
24
25 /*****************************************************************************
26  * Preamble
27  *****************************************************************************/
28 #include <errno.h>                                                 /* ENOMEM */
29
30 #include <ggi/ggi.h>
31
32 #ifdef HAVE_CONFIG_H
33 # include "config.h"
34 #endif
35
36 #include <vlc/vlc.h>
37 #include <vlc_interface.h>
38 #include <vlc_vout.h>
39
40 /*****************************************************************************
41  * Local prototypes
42  *****************************************************************************/
43 static int  Create    ( vlc_object_t * );
44 static void Destroy   ( vlc_object_t * );
45
46 static int  Init      ( vout_thread_t * );
47 static void End       ( vout_thread_t * );
48 static int  Manage    ( vout_thread_t * );
49 static void Display   ( vout_thread_t *, picture_t * );
50
51 static int  OpenDisplay    ( vout_thread_t * );
52 static void CloseDisplay   ( vout_thread_t * );
53 static void SetPalette     ( vout_thread_t *, uint16_t *, uint16_t *, uint16_t * );
54
55 /*****************************************************************************
56  * Module descriptor
57  *****************************************************************************/
58 #define DISPLAY_TEXT N_("X11 display")
59 #define DISPLAY_LONGTEXT N_( \
60             "X11 hardware display to use.\n" \
61             "By default, VLC will use the value of the DISPLAY " \
62             "environment variable.")
63
64 vlc_module_begin();
65     add_string( "ggi-display", NULL, NULL, DISPLAY_TEXT, DISPLAY_LONGTEXT, VLC_TRUE );
66     set_description( "General Graphics Interface video output" );
67     set_capability( "video output", 30 );
68     set_callbacks( Create, Destroy );
69 vlc_module_end();
70
71 /*****************************************************************************
72  * vout_sys_t: video output GGI method descriptor
73  *****************************************************************************
74  * This structure is part of the video output thread descriptor.
75  * It describes the GGI specific properties of an output thread.
76  *****************************************************************************/
77 struct vout_sys_t
78 {
79     /* GGI system information */
80     ggi_visual_t        p_display;                         /* display device */
81
82     ggi_mode            mode;                             /* mode descriptor */
83     int                 i_bits_per_pixel;
84
85     /* Buffer information */
86     ggi_directbuffer *  pp_buffer[2];                             /* buffers */
87     int                 i_index;
88
89     vlc_bool_t          b_must_acquire;   /* must be acquired before writing */
90 };
91
92 /*****************************************************************************
93  * Create: allocate GGI video thread output method
94  *****************************************************************************
95  * This function allocate and initialize a GGI vout method. It uses some of the
96  * vout properties to choose the correct mode, and change them according to the
97  * mode actually used.
98  *****************************************************************************/
99 static int Create( vlc_object_t *p_this )
100 {
101     vout_thread_t *p_vout = (vout_thread_t *)p_this;
102
103     /* Allocate structure */
104     p_vout->p_sys = malloc( sizeof( vout_sys_t ) );
105     if( p_vout->p_sys == NULL )
106     {
107         msg_Err( p_vout, "out of memory" );
108         return( 1 );
109     }
110
111     /* Open and initialize device */
112     if( OpenDisplay( p_vout ) )
113     {
114         msg_Err( p_vout, "cannot initialize GGI display" );
115         free( p_vout->p_sys );
116         return( 1 );
117     }
118
119     p_vout->pf_init = Init;
120     p_vout->pf_end = End;
121     p_vout->pf_manage = Manage;
122     p_vout->pf_render = NULL;
123     p_vout->pf_display = Display;
124
125     return( 0 );
126 }
127
128 /*****************************************************************************
129  * Init: initialize GGI video thread output method
130  *****************************************************************************
131  * This function initialize the GGI display device.
132  *****************************************************************************/
133 static int Init( vout_thread_t *p_vout )
134 {
135 #define p_b p_vout->p_sys->pp_buffer
136     int i_index;
137     picture_t *p_pic;
138
139     I_OUTPUTPICTURES = 0;
140
141     p_vout->output.i_width  = p_vout->p_sys->mode.visible.x;
142     p_vout->output.i_height = p_vout->p_sys->mode.visible.y;
143     p_vout->output.i_aspect = p_vout->p_sys->mode.visible.x
144                                * VOUT_ASPECT_FACTOR
145                                / p_vout->p_sys->mode.visible.y;
146
147     switch( p_vout->p_sys->i_bits_per_pixel )
148     {
149         case 8:
150             p_vout->output.i_chroma = VLC_FOURCC('R','G','B','2');
151             p_vout->output.pf_setpalette = SetPalette;
152             break;
153         case 15:
154             p_vout->output.i_chroma = VLC_FOURCC('R','V','1','5'); break;
155         case 16:
156             p_vout->output.i_chroma = VLC_FOURCC('R','V','1','6'); break;
157         case 24:
158             p_vout->output.i_chroma = VLC_FOURCC('R','V','2','4'); break;
159         case 32:
160             p_vout->output.i_chroma = VLC_FOURCC('R','V','3','2'); break;
161         default:
162             msg_Err( p_vout, "unknown screen depth %i",
163                      p_vout->p_sys->i_bits_per_pixel );
164             return 0;
165     }
166
167     /* Only useful for bits_per_pixel != 8 */
168     p_vout->output.i_rmask = p_b[ 0 ]->buffer.plb.pixelformat->red_mask;
169     p_vout->output.i_gmask = p_b[ 0 ]->buffer.plb.pixelformat->green_mask;
170     p_vout->output.i_bmask = p_b[ 0 ]->buffer.plb.pixelformat->blue_mask;
171
172     p_pic = NULL;
173
174     /* Find an empty picture slot */
175     for( i_index = 0 ; i_index < VOUT_MAX_PICTURES ; i_index++ )
176     {
177         if( p_vout->p_picture[ i_index ].i_status == FREE_PICTURE )
178         {
179             p_pic = p_vout->p_picture + i_index;
180             break;
181         }
182     }
183
184     if( p_pic == NULL )
185     {
186         return 0;
187     }
188
189     /* We know the chroma, allocate a buffer which will be used
190      * directly by the decoder */
191     p_vout->p_sys->i_index = 0;
192     p_pic->p->p_pixels = p_b[ 0 ]->write;
193     p_pic->p->i_pixel_pitch = p_b[ 0 ]->buffer.plb.pixelformat->size / 8;
194     p_pic->p->i_lines = p_vout->p_sys->mode.visible.y;
195     p_pic->p->i_visible_lines = p_vout->p_sys->mode.visible.y;
196
197     p_pic->p->i_pitch = p_b[ 0 ]->buffer.plb.stride;
198
199     if( p_b[ 0 ]->buffer.plb.pixelformat->size / 8
200          * p_vout->p_sys->mode.visible.x
201         != p_b[ 0 ]->buffer.plb.stride )
202     {
203         p_pic->p->i_visible_pitch = p_b[ 0 ]->buffer.plb.pixelformat->size
204                                      / 8 * p_vout->p_sys->mode.visible.x;
205     }
206     else
207     {
208         p_pic->p->i_visible_pitch = p_b[ 0 ]->buffer.plb.stride;
209     }
210
211     p_pic->i_planes = 1;
212
213     p_pic->i_status = DESTROYED_PICTURE;
214     p_pic->i_type   = DIRECT_PICTURE;
215
216     PP_OUTPUTPICTURE[ I_OUTPUTPICTURES ] = p_pic;
217
218     I_OUTPUTPICTURES++;
219
220     /* Acquire first buffer */
221     if( p_vout->p_sys->b_must_acquire )
222     {
223         ggiResourceAcquire( p_b[ 0 ]->resource,
224                             GGI_ACTYPE_WRITE );
225     }
226
227     /* Listen to the keyboard and the mouse buttons */
228     ggiSetEventMask( p_vout->p_sys->p_display,
229                      emKeyboard | emPtrButtonPress | emPtrButtonRelease );
230
231     /* Set asynchronous display mode -- usually quite faster */
232     ggiAddFlags( p_vout->p_sys->p_display, GGIFLAG_ASYNC );
233
234     return( 0 );
235 #undef p_b
236 }
237
238 /*****************************************************************************
239  * End: terminate GGI video thread output method
240  *****************************************************************************
241  * Terminate an output method created by Create
242  *****************************************************************************/
243 static void End( vout_thread_t *p_vout )
244 {
245 #define p_b p_vout->p_sys->pp_buffer
246     /* Release buffer */
247     if( p_vout->p_sys->b_must_acquire )
248     {
249         ggiResourceRelease( p_b[ p_vout->p_sys->i_index ]->resource );
250     }
251 #undef p_b
252 }
253
254 /*****************************************************************************
255  * Destroy: destroy GGI video thread output method
256  *****************************************************************************
257  * Terminate an output method created by Create
258  *****************************************************************************/
259 static void Destroy( vlc_object_t *p_this )
260 {
261     vout_thread_t *p_vout = (vout_thread_t *)p_this;
262
263     CloseDisplay( p_vout );
264
265     free( p_vout->p_sys );
266 }
267
268 /*****************************************************************************
269  * Manage: handle GGI events
270  *****************************************************************************
271  * This function should be called regularly by video output thread. It returns
272  * a non null value if an error occurred.
273  *****************************************************************************/
274 static int Manage( vout_thread_t *p_vout )
275 {
276     struct timeval tv = { 0, 1000 };                        /* 1 millisecond */
277     gii_event_mask mask;
278     gii_event      event;
279     vlc_value_t    val;
280
281     mask = emKeyboard | emPtrButtonPress | emPtrButtonRelease;
282
283     ggiEventPoll( p_vout->p_sys->p_display, mask, &tv );
284
285     while( ggiEventsQueued( p_vout->p_sys->p_display, mask) )
286     {
287         ggiEventRead( p_vout->p_sys->p_display, &event, mask);
288
289         switch( event.any.type )
290         {
291             case evKeyRelease:
292
293                 switch( event.key.sym )
294                 {
295                     case 'q':
296                     case 'Q':
297                     case GIIUC_Escape:
298                         vlc_object_kill( p_vout->p_libvlc );
299                         break;
300
301                     default:
302                         break;
303                 }
304                 break;
305
306             case evPtrButtonRelease:
307
308                 switch( event.pbutton.button )
309                 {
310                     case GII_PBUTTON_LEFT:
311                         val.b_bool = VLC_TRUE;
312                         var_Set( p_vout, "mouse-clicked", val );
313                         break;
314
315                     case GII_PBUTTON_RIGHT:
316                         {
317                             intf_thread_t *p_intf;
318                             p_intf = vlc_object_find( p_vout, VLC_OBJECT_INTF,
319                                                               FIND_ANYWHERE );
320                             if( p_intf )
321                             {
322                                 p_intf->b_menu_change = 1;
323                                 vlc_object_release( p_intf );
324                             }
325                         }
326                         break;
327                 }
328                 break;
329
330             default:
331                 break;
332         }
333     }
334
335     return( 0 );
336 }
337
338 /*****************************************************************************
339  * Display: displays previously rendered output
340  *****************************************************************************/
341 static void Display( vout_thread_t *p_vout, picture_t *p_pic )
342 {
343 #define p_b p_vout->p_sys->pp_buffer
344     p_pic->p->p_pixels = p_b[ p_vout->p_sys->i_index ]->write;
345
346     /* Change display frame */
347     if( p_vout->p_sys->b_must_acquire )
348     {
349         ggiResourceRelease( p_b[ p_vout->p_sys->i_index ]->resource );
350     }
351     ggiSetDisplayFrame( p_vout->p_sys->p_display,
352                         p_b[ p_vout->p_sys->i_index ]->frame );
353
354     /* Swap buffers and change write frame */
355     p_vout->p_sys->i_index ^= 1;
356     p_pic->p->p_pixels = p_b[ p_vout->p_sys->i_index ]->write;
357
358     if( p_vout->p_sys->b_must_acquire )
359     {
360         ggiResourceAcquire( p_b[ p_vout->p_sys->i_index ]->resource,
361                             GGI_ACTYPE_WRITE );
362     }
363     ggiSetWriteFrame( p_vout->p_sys->p_display,
364                       p_b[ p_vout->p_sys->i_index ]->frame );
365
366     /* Flush the output so that it actually displays */
367     ggiFlush( p_vout->p_sys->p_display );
368 #undef p_b
369 }
370
371 /* following functions are local */
372
373 /*****************************************************************************
374  * OpenDisplay: open and initialize GGI device
375  *****************************************************************************
376  * Open and initialize display according to preferences specified in the vout
377  * thread fields.
378  *****************************************************************************/
379 static int OpenDisplay( vout_thread_t *p_vout )
380 {
381 #define p_b p_vout->p_sys->pp_buffer
382     ggi_color   col_fg;                                  /* foreground color */
383     ggi_color   col_bg;                                  /* background color */
384     int         i_index;                               /* all purposes index */
385     char        *psz_display;
386
387     /* Initialize library */
388     if( ggiInit() )
389     {
390         msg_Err( p_vout, "cannot initialize GGI library" );
391         return( 1 );
392     }
393
394     /* Open display */
395     psz_display = config_GetPsz( p_vout, "ggi_display" );
396
397     p_vout->p_sys->p_display = ggiOpen( psz_display, NULL );
398     if( psz_display ) free( psz_display );
399
400     if( p_vout->p_sys->p_display == NULL )
401     {
402         msg_Err( p_vout, "cannot open GGI default display" );
403         ggiExit();
404         return( 1 );
405     }
406
407     /* Find most appropriate mode */
408     p_vout->p_sys->mode.frames =    2;                          /* 2 buffers */
409     p_vout->p_sys->mode.visible.x = config_GetInt( p_vout, "width" );
410     p_vout->p_sys->mode.visible.y = config_GetInt( p_vout, "height" );
411     p_vout->p_sys->mode.virt.x =    GGI_AUTO;
412     p_vout->p_sys->mode.virt.y =    GGI_AUTO;
413     p_vout->p_sys->mode.size.x =    GGI_AUTO;
414     p_vout->p_sys->mode.size.y =    GGI_AUTO;
415     p_vout->p_sys->mode.graphtype = GT_15BIT;        /* minimum usable depth */
416     p_vout->p_sys->mode.dpp.x =     GGI_AUTO;
417     p_vout->p_sys->mode.dpp.y =     GGI_AUTO;
418     ggiCheckMode( p_vout->p_sys->p_display, &p_vout->p_sys->mode );
419
420     /* FIXME: Check that returned mode has some minimum properties */
421
422     /* Set mode */
423     if( ggiSetMode( p_vout->p_sys->p_display, &p_vout->p_sys->mode ) )
424     {
425         msg_Err( p_vout, "cannot set GGI mode" );
426         ggiClose( p_vout->p_sys->p_display );
427         ggiExit();
428         return( 1 );
429     }
430
431     /* Check buffers properties */
432     p_vout->p_sys->b_must_acquire = 0;
433     for( i_index = 0; i_index < 2; i_index++ )
434     {
435         /* Get buffer address */
436         p_vout->p_sys->pp_buffer[ i_index ] =
437             (ggi_directbuffer *)ggiDBGetBuffer( p_vout->p_sys->p_display,
438                                                 i_index );
439         if( p_b[ i_index ] == NULL )
440         {
441             msg_Err( p_vout, "double buffering is not possible" );
442             ggiClose( p_vout->p_sys->p_display );
443             ggiExit();
444             return( 1 );
445         }
446
447         /* Check buffer properties */
448         if( ! ( p_b[ i_index ]->type & GGI_DB_SIMPLE_PLB )
449            || ( p_b[ i_index ]->page_size != 0 )
450            || ( p_b[ i_index ]->write == NULL )
451            || ( p_b[ i_index ]->noaccess != 0 )
452            || ( p_b[ i_index ]->align != 0 ) )
453         {
454             msg_Err( p_vout, "incorrect video memory type" );
455             ggiClose( p_vout->p_sys->p_display );
456             ggiExit();
457             return( 1 );
458         }
459
460         /* Check if buffer needs to be acquired before write */
461         if( ggiResourceMustAcquire( p_b[ i_index ]->resource ) )
462         {
463             p_vout->p_sys->b_must_acquire = 1;
464         }
465     }
466
467     /* Set graphic context colors */
468     col_fg.r = col_fg.g = col_fg.b = -1;
469     col_bg.r = col_bg.g = col_bg.b = 0;
470     if( ggiSetGCForeground(p_vout->p_sys->p_display,
471                            ggiMapColor(p_vout->p_sys->p_display,&col_fg)) ||
472         ggiSetGCBackground(p_vout->p_sys->p_display,
473                            ggiMapColor(p_vout->p_sys->p_display,&col_bg)) )
474     {
475         msg_Err( p_vout, "cannot set colors" );
476         ggiClose( p_vout->p_sys->p_display );
477         ggiExit();
478         return( 1 );
479     }
480
481     /* Set clipping for text */
482     if( ggiSetGCClipping( p_vout->p_sys->p_display, 0, 0,
483                           p_vout->p_sys->mode.visible.x,
484                           p_vout->p_sys->mode.visible.y ) )
485     {
486         msg_Err( p_vout, "cannot set clipping" );
487         ggiClose( p_vout->p_sys->p_display );
488         ggiExit();
489         return( 1 );
490     }
491
492     /* FIXME: set palette in 8bpp */
493     p_vout->p_sys->i_bits_per_pixel = p_b[ 0 ]->buffer.plb.pixelformat->depth;
494
495     return( 0 );
496 #undef p_b
497 }
498
499 /*****************************************************************************
500  * CloseDisplay: close and reset GGI device
501  *****************************************************************************
502  * This function returns all resources allocated by OpenDisplay and restore
503  * the original state of the device.
504  *****************************************************************************/
505 static void CloseDisplay( vout_thread_t *p_vout )
506 {
507     /* Restore original mode and close display */
508     ggiClose( p_vout->p_sys->p_display );
509
510     /* Exit library */
511     ggiExit();
512 }
513
514 /*****************************************************************************
515  * SetPalette: sets an 8 bpp palette
516  *****************************************************************************/
517 static void SetPalette( vout_thread_t *p_vout,
518                         uint16_t *red, uint16_t *green, uint16_t *blue )
519 {
520     ggi_color colors[256];
521     int i;
522
523     /* Fill colors with color information */
524     for( i = 0; i < 256; i++ )
525     {
526         colors[ i ].r = red[ i ];
527         colors[ i ].g = green[ i ];
528         colors[ i ].b = blue[ i ];
529         colors[ i ].a = 0;
530     }
531
532     /* Set palette */
533     if( ggiSetPalette( p_vout->p_sys->p_display, 0, 256, colors ) < 0 )
534     {
535         msg_Err( p_vout, "failed to set palette" );
536     }
537 }
538