]> git.sesse.net Git - vlc/blob - plugins/ggi/ggi.c
15eecf412839b6ade5438bc3c652308d9c6a664e
[vlc] / plugins / ggi / ggi.c
1 /*****************************************************************************
2  * ggi.c : GGI plugin for vlc
3  *****************************************************************************
4  * Copyright (C) 2000, 2001 VideoLAN
5  * $Id: ggi.c,v 1.14 2002/02/19 00:50:19 sam Exp $
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., 59 Temple Place - Suite 330, Boston, MA  02111, USA.
23  *****************************************************************************/
24
25 /*****************************************************************************
26  * Preamble
27  *****************************************************************************/
28 #include <stdlib.h>                                      /* malloc(), free() */
29 #include <string.h>
30 #include <errno.h>                                                 /* ENOMEM */
31
32 #include <ggi/ggi.h>
33
34 #include <videolan/vlc.h>
35
36 #include "video.h"
37 #include "video_output.h"
38
39 #include "intf_msg.h"
40 #include "interface.h"
41
42 /*****************************************************************************
43  * Local prototypes.
44  *****************************************************************************/
45 static void vout_getfunctions( function_list_t * p_function_list );
46
47 static int  vout_Create    ( vout_thread_t * );
48 static int  vout_Init      ( vout_thread_t * );
49 static void vout_End       ( vout_thread_t * );
50 static void vout_Destroy   ( vout_thread_t * );
51 static int  vout_Manage    ( vout_thread_t * );
52 static void vout_Render    ( vout_thread_t *, picture_t * );
53 static void vout_Display   ( vout_thread_t *, picture_t * );
54
55 static int  OpenDisplay    ( vout_thread_t * );
56 static void CloseDisplay   ( vout_thread_t * );
57
58 /*****************************************************************************
59  * Building configuration tree
60  *****************************************************************************/
61 MODULE_CONFIG_START
62 MODULE_CONFIG_STOP
63
64 MODULE_INIT_START
65     SET_DESCRIPTION( "General Graphics Interface video output" )
66     ADD_CAPABILITY( VOUT, 30 )
67     ADD_SHORTCUT( "ggi" )
68 MODULE_INIT_STOP
69
70 MODULE_ACTIVATE_START
71     vout_getfunctions( &p_module->p_functions->vout );
72 MODULE_ACTIVATE_STOP
73
74 MODULE_DEACTIVATE_START
75 MODULE_DEACTIVATE_STOP
76
77 /*****************************************************************************
78  * vout_sys_t: video output GGI method descriptor
79  *****************************************************************************
80  * This structure is part of the video output thread descriptor.
81  * It describes the GGI specific properties of an output thread.
82  *****************************************************************************/
83 typedef struct vout_sys_s
84 {
85     /* GGI system informations */
86     ggi_visual_t        p_display;                         /* display device */
87
88     ggi_mode            mode;                             /* mode descriptor */
89     int                 i_bits_per_pixel;
90
91     /* Buffer information */
92     ggi_directbuffer *  pp_buffer[2];                             /* buffers */
93     int                 i_index;
94
95     boolean_t           b_must_acquire;   /* must be acquired before writing */
96 } vout_sys_t;
97
98 /*****************************************************************************
99  * Functions exported as capabilities. They are declared as static so that
100  * we don't pollute the namespace too much.
101  *****************************************************************************/
102 static void vout_getfunctions( function_list_t * p_function_list )
103 {
104     p_function_list->functions.vout.pf_create  = vout_Create;
105     p_function_list->functions.vout.pf_init    = vout_Init;
106     p_function_list->functions.vout.pf_end     = vout_End;
107     p_function_list->functions.vout.pf_destroy = vout_Destroy;
108     p_function_list->functions.vout.pf_manage  = vout_Manage;
109     p_function_list->functions.vout.pf_render  = vout_Render;
110     p_function_list->functions.vout.pf_display = vout_Display;
111 }
112
113 /*****************************************************************************
114  * vout_Create: allocate GGI video thread output method
115  *****************************************************************************
116  * This function allocate and initialize a GGI vout method. It uses some of the
117  * vout properties to choose the correct mode, and change them according to the
118  * mode actually used.
119  *****************************************************************************/
120 int vout_Create( vout_thread_t *p_vout )
121 {
122     /* Allocate structure */
123     p_vout->p_sys = malloc( sizeof( vout_sys_t ) );
124     if( p_vout->p_sys == NULL )
125     {
126         intf_ErrMsg( "vout error: %s", strerror(ENOMEM) );
127         return( 1 );
128     }
129
130     /* Open and initialize device */
131     if( OpenDisplay( p_vout ) )
132     {
133         intf_ErrMsg( "vout error: can't initialize GGI display" );
134         free( p_vout->p_sys );
135         return( 1 );
136     }
137
138     return( 0 );
139 }
140
141 /*****************************************************************************
142  * vout_Init: initialize GGI video thread output method
143  *****************************************************************************
144  * This function initialize the GGI display device.
145  *****************************************************************************/
146 int vout_Init( vout_thread_t *p_vout )
147 {
148     int i_index;
149     picture_t *p_pic;
150
151     I_OUTPUTPICTURES = 0;
152
153     p_vout->output.i_width  = p_vout->p_sys->mode.visible.x;
154     p_vout->output.i_height = p_vout->p_sys->mode.visible.y;
155     p_vout->output.i_aspect = p_vout->p_sys->mode.visible.x
156                                * VOUT_ASPECT_FACTOR
157                                / p_vout->p_sys->mode.visible.y;
158
159     switch( p_vout->p_sys->i_bits_per_pixel )
160     {
161         case 8: /* FIXME: set the palette */
162             p_vout->output.i_chroma = FOURCC_BI_RGB; break;
163         case 15:
164             p_vout->output.i_chroma = FOURCC_RV15; break;
165         case 16:
166             p_vout->output.i_chroma = FOURCC_RV16; break;
167         case 24:
168             p_vout->output.i_chroma = FOURCC_BI_BITFIELDS; break;
169         case 32:
170             p_vout->output.i_chroma = FOURCC_BI_BITFIELDS; break;
171         default:
172             intf_ErrMsg( "vout error: unknown screen depth" );
173             return 0;
174     }
175
176     p_pic = NULL;
177
178     /* Find an empty picture slot */
179     for( i_index = 0 ; i_index < VOUT_MAX_PICTURES ; i_index++ )
180     {
181         if( p_vout->p_picture[ i_index ].i_status == FREE_PICTURE )
182         {
183             p_pic = p_vout->p_picture + i_index;
184             break;
185         }
186     }
187
188     if( p_pic == NULL )
189     {
190         return 0;
191     }
192
193 #define p_b p_vout->p_sys->pp_buffer
194     /* We know the chroma, allocate a buffer which will be used
195      * directly by the decoder */
196     p_vout->p_sys->i_index = 0;
197     p_pic->p->p_pixels = p_b[ 0 ]->write;
198     p_pic->p->i_pixel_bytes = p_b[ 0 ]->buffer.plb.pixelformat->size / 8;
199     p_pic->p->i_lines = p_vout->p_sys->mode.visible.y;
200
201     if( p_b[ 0 ]->buffer.plb.pixelformat->size / 8
202          * p_vout->p_sys->mode.visible.x
203         != p_b[ 0 ]->buffer.plb.stride )
204     {
205         p_pic->p->b_margin = 1;
206         p_pic->p->b_hidden = 1;
207         p_pic->p->i_pitch = p_b[ 0 ]->buffer.plb.stride;
208         p_pic->p->i_visible_bytes = p_b[ 0 ]->buffer.plb.pixelformat->size
209                                      / 8 * p_vout->p_sys->mode.visible.x;
210     }
211     else
212     {
213         p_pic->p->b_margin = 0;
214         p_pic->p->i_pitch = p_b[ 0 ]->buffer.plb.stride;
215     }
216
217     /* Only useful for bits_per_pixel != 8 */
218     p_pic->p->i_red_mask =   p_b[ 0 ]->buffer.plb.pixelformat->red_mask;
219     p_pic->p->i_green_mask = p_b[ 0 ]->buffer.plb.pixelformat->green_mask;
220     p_pic->p->i_blue_mask =  p_b[ 0 ]->buffer.plb.pixelformat->blue_mask;
221
222     p_pic->i_planes = 1;
223
224     p_pic->i_status = DESTROYED_PICTURE;
225     p_pic->i_type   = DIRECT_PICTURE;
226
227     PP_OUTPUTPICTURE[ I_OUTPUTPICTURES ] = p_pic;
228
229     I_OUTPUTPICTURES++;
230
231     /* Acquire first buffer */
232     if( p_vout->p_sys->b_must_acquire )
233     {
234         ggiResourceAcquire( p_b[ 0 ]->resource,
235                             GGI_ACTYPE_WRITE );
236     }
237
238     /* Listen to the keyboard and the mouse buttons */
239     ggiSetEventMask( p_vout->p_sys->p_display,
240                      emKeyboard | emPtrButtonPress | emPtrButtonRelease );
241
242     /* Set asynchronous display mode -- usually quite faster */
243     ggiAddFlags( p_vout->p_sys->p_display, GGIFLAG_ASYNC );
244
245     return( 0 );
246 #undef p_b
247 }
248
249 /*****************************************************************************
250  * vout_End: terminate GGI video thread output method
251  *****************************************************************************
252  * Terminate an output method created by vout_Create
253  *****************************************************************************/
254 void vout_End( vout_thread_t *p_vout )
255 {
256 #define p_b p_vout->p_sys->pp_buffer
257     /* Release buffer */
258     if( p_vout->p_sys->b_must_acquire )
259     {
260         ggiResourceRelease( p_b[ p_vout->p_sys->i_index ]->resource );
261     }
262 #undef p_b
263 }
264
265 /*****************************************************************************
266  * vout_Destroy: destroy GGI video thread output method
267  *****************************************************************************
268  * Terminate an output method created by vout_Create
269  *****************************************************************************/
270 void vout_Destroy( vout_thread_t *p_vout )
271 {
272     CloseDisplay( p_vout );
273
274     free( p_vout->p_sys );
275 }
276
277 /*****************************************************************************
278  * vout_Manage: handle GGI events
279  *****************************************************************************
280  * This function should be called regularly by video output thread. It returns
281  * a non null value if an error occured.
282  *****************************************************************************/
283 int vout_Manage( vout_thread_t *p_vout )
284 {
285     struct timeval tv = { 0, 1000 };                        /* 1 millisecond */
286     gii_event_mask mask;
287     gii_event      event;
288
289     mask = emKeyboard | emPtrButtonPress | emPtrButtonRelease;
290
291     ggiEventPoll( p_vout->p_sys->p_display, mask, &tv );
292     
293     while( ggiEventsQueued( p_vout->p_sys->p_display, mask) )
294     {
295         ggiEventRead( p_vout->p_sys->p_display, &event, mask);
296
297         switch( event.any.type )
298         {
299             case evKeyRelease:
300
301                 switch( event.key.sym )
302                 {
303                     case 'q':
304                     case 'Q':
305                     case GIIUC_Escape:
306                         /* FIXME pass message ! */
307                         p_main->p_intf->b_die = 1;
308                         break;
309
310                     default:
311                         break;
312                 }
313                 break;
314
315             case evPtrButtonRelease:
316
317                 switch( event.pbutton.button )
318                 {
319                     case GII_PBUTTON_RIGHT:
320                         /* FIXME: need locking ! */
321                         p_main->p_intf->b_menu_change = 1;
322                         break;
323                 }
324                 break;
325
326             default:
327                 break;
328         }
329     }
330
331     return( 0 );
332 }
333
334 /*****************************************************************************
335  * vout_Render: displays previously rendered output
336  *****************************************************************************/
337 void vout_Render( vout_thread_t *p_vout, picture_t *p_pic )
338 {
339     ;
340 }
341
342 /*****************************************************************************
343  * vout_Display: displays previously rendered output
344  *****************************************************************************/
345 void vout_Display( vout_thread_t *p_vout, picture_t *p_pic )
346 {
347 #define p_b p_vout->p_sys->pp_buffer
348     p_pic->p->p_pixels = p_b[ p_vout->p_sys->i_index ]->write;
349
350     /* Change display frame */
351     if( p_vout->p_sys->b_must_acquire )
352     {
353         ggiResourceRelease( p_b[ p_vout->p_sys->i_index ]->resource );
354     }
355     ggiSetDisplayFrame( p_vout->p_sys->p_display,
356                         p_b[ p_vout->p_sys->i_index ]->frame );
357
358     /* Swap buffers and change write frame */
359     p_vout->p_sys->i_index ^= 1;
360     p_pic->p->p_pixels = p_b[ p_vout->p_sys->i_index ]->write;
361
362     if( p_vout->p_sys->b_must_acquire )
363     {
364         ggiResourceAcquire( p_b[ p_vout->p_sys->i_index ]->resource,
365                             GGI_ACTYPE_WRITE );
366     }
367     ggiSetWriteFrame( p_vout->p_sys->p_display,
368                       p_b[ p_vout->p_sys->i_index ]->frame );
369
370     /* Flush the output so that it actually displays */
371     ggiFlush( p_vout->p_sys->p_display );
372 #undef p_b
373 }
374
375 /* following functions are local */
376
377 /*****************************************************************************
378  * OpenDisplay: open and initialize GGI device
379  *****************************************************************************
380  * Open and initialize display according to preferences specified in the vout
381  * thread fields.
382  *****************************************************************************/
383 static int OpenDisplay( vout_thread_t *p_vout )
384 {
385 #define p_b p_vout->p_sys->pp_buffer
386     ggi_color   col_fg;                                  /* foreground color */
387     ggi_color   col_bg;                                  /* background color */
388     int         i_index;                               /* all purposes index */
389     char        *psz_display;
390
391     /* Initialize library */
392     if( ggiInit() )
393     {
394         intf_ErrMsg( "vout error: can't initialize GGI library" );
395         return( 1 );
396     }
397
398     /* Open display */
399     psz_display = main_GetPszVariable( VOUT_DISPLAY_VAR, NULL );
400
401     p_vout->p_sys->p_display = ggiOpen( psz_display, NULL );
402
403     if( p_vout->p_sys->p_display == NULL )
404     {
405         intf_ErrMsg( "vout error: can't open GGI default display" );
406         ggiExit();
407         return( 1 );
408     }
409
410     /* Find most appropriate mode */
411     p_vout->p_sys->mode.frames =    2;                          /* 2 buffers */
412     p_vout->p_sys->mode.visible.x = main_GetIntVariable( VOUT_WIDTH_VAR,
413                                                          VOUT_WIDTH_DEFAULT );
414     p_vout->p_sys->mode.visible.y = main_GetIntVariable( VOUT_HEIGHT_VAR,
415                                                          VOUT_HEIGHT_DEFAULT );
416     p_vout->p_sys->mode.virt.x =    GGI_AUTO;
417     p_vout->p_sys->mode.virt.y =    GGI_AUTO;
418     p_vout->p_sys->mode.size.x =    GGI_AUTO;
419     p_vout->p_sys->mode.size.y =    GGI_AUTO;
420     p_vout->p_sys->mode.graphtype = GT_15BIT;        /* minimum usable depth */
421     p_vout->p_sys->mode.dpp.x =     GGI_AUTO;
422     p_vout->p_sys->mode.dpp.y =     GGI_AUTO;
423     ggiCheckMode( p_vout->p_sys->p_display, &p_vout->p_sys->mode );
424
425     /* FIXME: Check that returned mode has some minimum properties */
426
427     /* Set mode */
428     if( ggiSetMode( p_vout->p_sys->p_display, &p_vout->p_sys->mode ) )
429     {
430         intf_ErrMsg( "vout error: can't set GGI mode" );
431         ggiClose( p_vout->p_sys->p_display );
432         ggiExit();
433         return( 1 );
434     }
435
436     /* Check buffers properties */
437     p_vout->p_sys->b_must_acquire = 0;
438     for( i_index = 0; i_index < 2; i_index++ )
439     {
440         /* Get buffer address */
441         p_vout->p_sys->pp_buffer[ i_index ] =
442             (ggi_directbuffer *)ggiDBGetBuffer( p_vout->p_sys->p_display,
443                                                 i_index );
444         if( p_b[ i_index ] == NULL )
445         {
446             intf_ErrMsg( "vout error: double buffering is not possible" );
447             ggiClose( p_vout->p_sys->p_display );
448             ggiExit();
449             return( 1 );
450         }
451
452         /* Check buffer properties */
453         if( ! ( p_b[ i_index ]->type & GGI_DB_SIMPLE_PLB )
454            || ( p_b[ i_index ]->page_size != 0 )
455            || ( p_b[ i_index ]->write == NULL )
456            || ( p_b[ i_index ]->noaccess != 0 )
457            || ( p_b[ i_index ]->align != 0 ) )
458         {
459             intf_ErrMsg( "vout error: incorrect video memory type" );
460             ggiClose( p_vout->p_sys->p_display );
461             ggiExit();
462             return( 1 );
463         }
464
465         /* Check if buffer needs to be acquired before write */
466         if( ggiResourceMustAcquire( p_b[ i_index ]->resource ) )
467         {
468             p_vout->p_sys->b_must_acquire = 1;
469         }
470     }
471
472     /* Set graphic context colors */
473     col_fg.r = col_fg.g = col_fg.b = -1;
474     col_bg.r = col_bg.g = col_bg.b = 0;
475     if( ggiSetGCForeground(p_vout->p_sys->p_display,
476                            ggiMapColor(p_vout->p_sys->p_display,&col_fg)) ||
477         ggiSetGCBackground(p_vout->p_sys->p_display,
478                            ggiMapColor(p_vout->p_sys->p_display,&col_bg)) )
479     {
480         intf_ErrMsg( "vout error: can't set colors" );
481         ggiClose( p_vout->p_sys->p_display );
482         ggiExit();
483         return( 1 );
484     }
485
486     /* Set clipping for text */
487     if( ggiSetGCClipping( p_vout->p_sys->p_display, 0, 0,
488                           p_vout->p_sys->mode.visible.x,
489                           p_vout->p_sys->mode.visible.y ) )
490     {
491         intf_ErrMsg( "vout error: can't set clipping" );
492         ggiClose( p_vout->p_sys->p_display );
493         ggiExit();
494         return( 1 );
495     }
496
497     /* FIXME: set palette in 8bpp */
498     p_vout->p_sys->i_bits_per_pixel = p_b[ 0 ]->buffer.plb.pixelformat->depth;
499
500     return( 0 );
501 #undef p_b
502 }
503
504 /*****************************************************************************
505  * CloseDisplay: close and reset GGI device
506  *****************************************************************************
507  * This function returns all resources allocated by OpenDisplay and restore
508  * the original state of the device.
509  *****************************************************************************/
510 static void CloseDisplay( vout_thread_t *p_vout )
511 {
512     /* Restore original mode and close display */
513     ggiClose( p_vout->p_sys->p_display );
514
515     /* Exit library */
516     ggiExit();
517 }
518