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