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