Improved scaling, correct aspect ratio even in full screen mode.
[vlc.git] / plugins / beos / vout_beos.cpp
1 /*****************************************************************************
2  * vout_beos.cpp: beos video output display method
3  *****************************************************************************
4  * Copyright (C) 2000, 2001 VideoLAN
5  * $Id: vout_beos.cpp,v 1.47 2002/03/20 10:33:42 tcastley Exp $
6  *
7  * Authors: Jean-Marc Dressler <polux@via.ecp.fr>
8  *          Samuel Hocevar <sam@zoy.org>
9  *          Tony Castley <tcastley@mail.powerup.com.au>
10  *          Richard Shepherd <richard@rshepherd.demon.co.uk>
11  *
12  * This program is free software; you can redistribute it and/or modify
13  * it under the terms of the GNU General Public License as published by
14  * the Free Software Foundation; either version 2 of the License, or
15  * (at your option) any later version.
16  * 
17  * This program is distributed in the hope that it will be useful,
18  * but WITHOUT ANY WARRANTY; without even the implied warranty of
19  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
20  * GNU General Public License for more details.
21  *
22  * You should have received a copy of the GNU General Public License
23  * along with this program; if not, write to the Free Software
24  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111, USA.
25  *****************************************************************************/
26
27 /*****************************************************************************
28  * Preamble
29  *****************************************************************************/
30 #include <errno.h>                                                 /* ENOMEM */
31 #include <stdlib.h>                                                /* free() */
32 #include <stdio.h>
33 #include <string.h>                                            /* strerror() */
34 #include <InterfaceKit.h>
35 #include <DirectWindow.h>
36 #include <Application.h>
37 #include <Bitmap.h>
38
39 extern "C"
40 {
41 #include <videolan/vlc.h>
42
43 #include "video.h"
44 #include "video_output.h"
45
46 #include "interface.h"
47 }
48
49 #include "VideoWindow.h"
50
51 #define BITS_PER_PLANE  16
52 #define BYTES_PER_PIXEL 2
53
54 /*****************************************************************************
55  * vout_sys_t: BeOS video output method descriptor
56  *****************************************************************************
57  * This structure is part of the video output thread descriptor.
58  * It describes the BeOS specific properties of an output thread.
59  *****************************************************************************/
60 typedef struct vout_sys_s
61 {
62     VideoWindow *  p_window;
63
64     s32 i_width;
65     s32 i_height;
66
67     u8 *pp_buffer[2];
68     int i_index;
69
70 } vout_sys_t;
71
72
73 /*****************************************************************************
74  * beos_GetAppWindow : retrieve a BWindow pointer from the window name
75  *****************************************************************************/
76 BWindow *beos_GetAppWindow(char *name)
77 {
78     int32       index;
79     BWindow     *window;
80     
81     for (index = 0 ; ; index++)
82     {
83         window = be_app->WindowAt(index);
84         if (window == NULL)
85             break;
86         if (window->LockWithTimeout(20000) == B_OK)
87         {
88             if (strcmp(window->Name(), name) == 0)
89             {
90                 window->Unlock();
91                 break;
92             }
93             window->Unlock();
94         }
95     }
96     return window; 
97 }
98
99 /**************************************************************************** 
100  * DrawingThread : thread that really does the drawing 
101  ****************************************************************************/ 
102 int32 Draw(void *data) 
103
104     VideoWindow* p_win; 
105     p_win = (VideoWindow *) data; 
106
107     if ( p_win-> voutWindow-> LockLooper() ) 
108     {
109         if (p_win->vsync)
110         {
111             BScreen *screen;
112             screen = new BScreen(p_win->voutWindow);
113             screen-> WaitForRetrace(22000);
114             delete screen; 
115         }
116         if (p_win->resized)
117         {
118             p_win->resized = false;
119             p_win-> view-> FillRect(p_win-> voutWindow-> Bounds());
120         }  
121         p_win-> view-> DrawBitmap( p_win-> bitmap[p_win-> i_buffer],  
122                                    BRect(p_win->out_left, p_win->out_top,
123                                          p_win->out_left + p_win->out_width, 
124                                          p_win->out_top + p_win->out_height) );  
125         p_win-> voutWindow-> UnlockLooper(); 
126     } 
127     return B_OK; 
128 }
129
130 /*****************************************************************************
131  * bitmapWindow : This is the bitmap window output
132  *****************************************************************************/
133 bitmapWindow::bitmapWindow(BRect frame, VideoWindow *theOwner)
134         : BWindow( frame, NULL, B_TITLED_WINDOW, 
135                     B_NOT_CLOSABLE | B_NOT_MINIMIZABLE )
136 {
137     owner = theOwner;
138     SetTitle(VOUT_TITLE " (BBitmap output)");
139 }
140
141 bitmapWindow::~bitmapWindow()
142 {
143 }
144
145 void bitmapWindow::FrameMoved(BPoint origin) 
146 {
147     owner->FrameMoved(origin);
148 }
149
150 void bitmapWindow::FrameResized( float width, float height )
151 {
152     owner->FrameResized(width, height);
153 }
154
155 void bitmapWindow::Zoom(BPoint origin, float width, float height )
156 {
157     owner->Zoom( origin, width, height );
158 }
159
160 void bitmapWindow::ScreenChanged(BRect frame, color_space mode)
161 {
162     owner->ScreenChanged(frame, mode);
163 }
164
165 void bitmapWindow::drawBuffer(int bufferIndex)
166 {
167     status_t status;
168
169     owner->i_buffer = bufferIndex; 
170     owner->fDrawThreadID = spawn_thread(Draw, "drawing_thread",
171                                         B_DISPLAY_PRIORITY, (void*) owner);
172     wait_for_thread(owner->fDrawThreadID, &status);
173     
174 }
175
176 /*****************************************************************************
177  * directWindow : This is the bitmap window output
178  *****************************************************************************/
179 directWindow::directWindow(BRect frame, VideoWindow *theOwner)
180         : BDirectWindow( frame, NULL, B_TITLED_WINDOW, 
181                     B_NOT_CLOSABLE | B_NOT_MINIMIZABLE )
182 {
183     owner = theOwner;
184     SetTitle(VOUT_TITLE " (DirectWindow output)");
185 }
186
187 directWindow::~directWindow()
188 {
189 }
190
191 void directWindow::DirectConnected(direct_buffer_info *info)
192 {
193 }
194
195 void directWindow::FrameMoved(BPoint origin) 
196 {
197     owner->FrameMoved(origin);
198 }
199
200 void directWindow::FrameResized( float width, float height )
201 {
202     owner->FrameResized(width, height);
203 }
204
205 void directWindow::Zoom(BPoint origin, float width, float height )
206 {
207     owner->Zoom( origin, width, height );
208 }
209
210 void directWindow::ScreenChanged(BRect frame, color_space mode)
211 {
212     owner->ScreenChanged(frame, mode);
213 }
214
215 void directWindow::drawBuffer(int bufferIndex)
216 {
217     status_t status;
218
219     owner->i_buffer = bufferIndex; 
220     owner->fDrawThreadID = spawn_thread(Draw, "drawing_thread",
221                                         B_DISPLAY_PRIORITY, (void*) owner);
222     wait_for_thread(owner->fDrawThreadID, &status);
223 }
224
225 /*****************************************************************************
226  * VideoWindow constructor and destructor
227  *****************************************************************************/
228 VideoWindow::VideoWindow( int width, int height, 
229                           vout_thread_t *p_video_output )
230 {
231     if ( BDirectWindow::SupportsWindowMode() )
232     { 
233         voutWindow = new directWindow( BRect( 80, 50, 
234                                           80 + width, 50 + height ), this );
235         mode = DIRECT;
236     }
237     else
238     {
239         voutWindow = new bitmapWindow( BRect( 80, 50, 
240                                           80 + width, 50 + height ), this );
241         mode = BITMAP;
242     }
243
244     /* set the VideoWindow variables */
245     teardownwindow = false;
246     is_zoomed = false;
247     resized = true;
248     /* call ScreenChanged to set vsync correctly */
249     BScreen *screen;
250     screen = new BScreen(voutWindow);
251     ScreenChanged(screen->Frame(), screen->ColorSpace());
252     delete screen;
253     
254     /* create the view to do the display */
255     view = new VLCView( voutWindow->Bounds() );
256     voutWindow->AddChild(view);
257     
258     /* Bitmap mode overlay not available, set the system to 32bits
259      * and let BeOS do all the work */
260     bitmap[0] = new BBitmap( voutWindow->Bounds(), B_RGB32);
261     bitmap[1] = new BBitmap( voutWindow->Bounds(), B_RGB32);
262     memset(bitmap[0]->Bits(), 0, bitmap[0]->BitsLength());
263     memset(bitmap[1]->Bits(), 0, bitmap[1]->BitsLength());
264
265     i_width = bitmap[0]->Bounds().IntegerWidth();
266     i_height = bitmap[0]->Bounds().IntegerHeight();
267     
268     winSize = voutWindow->Frame();
269     out_top = 0;
270     out_left = 0;
271     out_height = winSize.Height();
272     out_width = winSize.Width();
273     width_scale = out_width / i_width;
274     height_scale = out_height / i_height;
275
276
277     voutWindow->Show();
278 }
279
280 VideoWindow::~VideoWindow()
281 {
282     int32 result;
283
284     teardownwindow = true;
285     wait_for_thread(fDrawThreadID, &result);
286     voutWindow->Hide();
287     voutWindow->Sync();
288     voutWindow->Lock();
289     voutWindow->Quit();
290     delete bitmap[0];
291     delete bitmap[1];
292 }
293
294 void VideoWindow::drawBuffer(int bufferIndex)
295 {
296     switch( mode )
297     {
298         case DIRECT:
299         {
300             directWindow *dW = (directWindow*)voutWindow;
301             dW->drawBuffer(bufferIndex);
302             break;
303         }
304         case BITMAP:
305         default:
306         {
307             bitmapWindow *bW = (bitmapWindow*)voutWindow;
308             bW->drawBuffer(bufferIndex);
309             break;
310         }
311     }
312 }
313
314 void VideoWindow::Zoom(BPoint origin, float width, float height )
315 {
316     if(is_zoomed)
317     {
318         is_zoomed = !is_zoomed;
319         voutWindow->MoveTo(winSize.left, winSize.top);
320         voutWindow->ResizeTo(winSize.IntegerWidth(), winSize.IntegerHeight());
321         width_scale = winSize.IntegerWidth() / i_width;
322         height_scale = winSize.IntegerHeight() / i_height;
323         be_app->ShowCursor();
324     }
325     else
326     {
327         is_zoomed = !is_zoomed;
328         BScreen *screen;
329         screen = new BScreen(voutWindow);
330         BRect rect = screen->Frame();
331         delete screen;
332         voutWindow->MoveTo(0,0);
333         voutWindow->ResizeTo(rect.IntegerWidth(), rect.IntegerHeight());
334         width_scale = rect.IntegerWidth() / i_width;
335         height_scale = rect.IntegerHeight() / i_height;
336         be_app->HideCursor();
337     }
338     resized = true;
339 }
340
341 void VideoWindow::FrameMoved(BPoint origin) 
342 {
343         if (is_zoomed) return ;
344     winSize = voutWindow->Frame();
345     resized = true;
346 }
347
348 void VideoWindow::FrameResized( float width, float height )
349 {
350     width_scale = width / i_width;
351     height_scale = height / i_height;
352     if (width_scale <= height_scale)
353     {
354         out_width = i_width * width_scale;
355         out_height = i_height * width_scale;
356         out_left = 0; 
357         out_top = (voutWindow->Frame().Height() - out_height) / 2;
358     }
359     else   /* if the height is proportionally smaller */
360     {
361         out_width = i_width * height_scale;
362         out_height = i_height * height_scale;
363         out_top = 0;
364         out_left = (voutWindow->Frame().Width() - out_width) /2;
365     }
366
367         if (is_zoomed) return ;
368     winSize = voutWindow->Frame();
369     width_scale = width / i_width;
370     height_scale = height / i_height;
371     resized = true;
372 }
373
374 void VideoWindow::ScreenChanged(BRect frame, color_space mode)
375 {
376     BScreen *screen;
377     float refresh;
378     
379     screen = new BScreen(voutWindow);
380     display_mode disp_mode; 
381     
382     screen-> GetMode(&disp_mode); 
383     refresh = 
384          (disp_mode.timing.pixel_clock * 1000)/((disp_mode.timing.h_total)* 
385          (disp_mode.timing.v_total)); 
386     if (refresh  < 61) 
387     { 
388         vsync = true; 
389     } 
390 }
391
392 /*****************************************************************************
393  * VLCView::VLCView
394  *****************************************************************************/
395 VLCView::VLCView(BRect bounds) : BView(bounds, "", B_FOLLOW_ALL, B_WILL_DRAW)
396 {
397     SetViewColor(B_TRANSPARENT_32_BIT);
398 }
399
400 /*****************************************************************************
401  * VLCView::~VLCView
402  *****************************************************************************/
403 VLCView::~VLCView()
404 {
405 }
406
407 /*****************************************************************************
408  * VLCVIew::MouseDown
409  *****************************************************************************/
410 void VLCView::MouseDown(BPoint point)
411 {
412     BWindow *win = Window();
413     win->Zoom();
414 }
415
416 /*****************************************************************************
417  * VLCVIew::Draw
418  *****************************************************************************/
419 void VLCView::Draw(BRect updateRect) 
420 {
421     FillRect(updateRect);
422 }
423
424
425 extern "C"
426 {
427
428 /*****************************************************************************
429  * Local prototypes
430  *****************************************************************************/
431 static int  vout_Create     ( vout_thread_t * );
432 static int  vout_Init       ( vout_thread_t * );
433 static void vout_End        ( vout_thread_t * );
434 static void vout_Destroy    ( vout_thread_t * );
435 static int  vout_Manage     ( vout_thread_t * );
436 static void vout_Display    ( vout_thread_t *, picture_t * );
437 static void vout_Render     ( vout_thread_t *, picture_t * );
438
439 static int  BeosOpenDisplay ( vout_thread_t *p_vout );
440 static void BeosCloseDisplay( vout_thread_t *p_vout );
441
442 /*****************************************************************************
443  * Functions exported as capabilities. They are declared as static so that
444  * we don't pollute the namespace too much.
445  *****************************************************************************/
446 void _M( vout_getfunctions )( function_list_t * p_function_list )
447 {
448     p_function_list->functions.vout.pf_create     = vout_Create;
449     p_function_list->functions.vout.pf_init       = vout_Init;
450     p_function_list->functions.vout.pf_end        = vout_End;
451     p_function_list->functions.vout.pf_destroy    = vout_Destroy;
452     p_function_list->functions.vout.pf_manage     = vout_Manage;
453     p_function_list->functions.vout.pf_display    = vout_Display;
454     p_function_list->functions.vout.pf_render     = vout_Render;
455 }
456
457 /*****************************************************************************
458  * vout_Create: allocates BeOS video thread output method
459  *****************************************************************************
460  * This function allocates and initializes a BeOS vout method.
461  *****************************************************************************/
462 int vout_Create( vout_thread_t *p_vout )
463 {
464     /* Allocate structure */
465     p_vout->p_sys = (vout_sys_t*) malloc( sizeof( vout_sys_t ) );
466     if( p_vout->p_sys == NULL )
467     {
468         intf_ErrMsg( "error: %s", strerror(ENOMEM) );
469         return( 1 );
470     }
471
472     p_vout->p_sys->i_width = p_vout->render.i_width;
473     p_vout->p_sys->i_height = p_vout->render.i_height;
474       
475     return( 0 );
476 }
477
478 /*****************************************************************************
479  * vout_Init: initialize BeOS video thread output method
480  *****************************************************************************/
481 int vout_Init( vout_thread_t *p_vout )
482 {
483     int i_index;
484     picture_t *p_pic;
485
486     I_OUTPUTPICTURES = 0;
487
488     /* Open and initialize device */
489     if( BeosOpenDisplay( p_vout ) )
490     {
491         intf_ErrMsg("vout error: can't open display");
492         return 0;
493     }
494     /* Set the buffers */
495     p_vout->p_sys->pp_buffer[0] = (u8*)p_vout->p_sys->p_window->bitmap[0]->Bits();
496     p_vout->p_sys->pp_buffer[1] = (u8*)p_vout->p_sys->p_window->bitmap[1]->Bits();
497
498     p_vout->output.i_width  = p_vout->p_sys->i_width;
499     p_vout->output.i_height = p_vout->p_sys->i_height;
500     p_vout->output.i_aspect = p_vout->p_sys->i_width
501                                * VOUT_ASPECT_FACTOR / p_vout->p_sys->i_height;
502     p_vout->output.i_chroma = FOURCC_RV32;
503
504     p_vout->output.i_rmask  = 0x00ff0000;
505     p_vout->output.i_gmask  = 0x0000ff00;
506     p_vout->output.i_bmask  = 0x000000ff;
507
508     p_pic = NULL;
509
510     /* Find an empty picture slot */
511     for( i_index = 0 ; i_index < VOUT_MAX_PICTURES ; i_index++ )
512     {
513         if( p_vout->p_picture[ i_index ].i_status == FREE_PICTURE )
514         {
515             p_pic = p_vout->p_picture + i_index;
516             break;
517         }
518     }
519
520     if( p_pic == NULL )
521     {
522         return 0;
523     }
524
525     p_vout->p_sys->i_index = 0;
526     p_pic->p->p_pixels = p_vout->p_sys->pp_buffer[0];
527     p_pic->p->i_pixel_bytes = 4;
528     p_pic->p->i_lines = p_vout->p_sys->i_height;
529     p_pic->p->b_margin = 0;
530     p_pic->p->i_pitch = 4 * p_vout->p_sys->i_width;
531
532     p_pic->i_planes = 1;
533
534     p_pic->i_status = DESTROYED_PICTURE;
535     p_pic->i_type   = DIRECT_PICTURE;
536
537     PP_OUTPUTPICTURE[ I_OUTPUTPICTURES ] = p_pic;
538
539     I_OUTPUTPICTURES++;
540
541     return( 0 );
542 }
543
544 /*****************************************************************************
545  * vout_End: terminate BeOS video thread output method
546  *****************************************************************************/
547 void vout_End( vout_thread_t *p_vout )
548 {
549     BeosCloseDisplay( p_vout );
550 }
551
552 /*****************************************************************************
553  * vout_Destroy: destroy BeOS video thread output method
554  *****************************************************************************
555  * Terminate an output method created by DummyCreateOutputMethod
556  *****************************************************************************/
557 void vout_Destroy( vout_thread_t *p_vout )
558 {
559     free( p_vout->p_sys );
560 }
561
562 /*****************************************************************************
563  * vout_Manage: handle BeOS events
564  *****************************************************************************
565  * This function should be called regularly by video output thread. It manages
566  * console events. It returns a non null value on error.
567  *****************************************************************************/
568 int vout_Manage( vout_thread_t *p_vout )
569 {
570                           
571     return( 0 );
572 }
573
574 /*****************************************************************************
575  * vout_Render: render previously calculated output
576  *****************************************************************************/
577 void vout_Render( vout_thread_t *p_vout, picture_t *p_pic )
578 {
579     ;
580 }
581
582 /*****************************************************************************
583  * vout_Display: displays previously rendered output
584  *****************************************************************************
585  * This function send the currently rendered image to BeOS image, waits until
586  * it is displayed and switch the two rendering buffers, preparing next frame.
587  *****************************************************************************/
588 void vout_Display( vout_thread_t *p_vout, picture_t *p_pic )
589 {
590     VideoWindow * p_win = p_vout->p_sys->p_window;
591
592     /* draw buffer if required */    
593     if (!p_win->teardownwindow)
594     {
595        p_win->drawBuffer(p_vout->p_sys->i_index);
596     }
597     /* change buffer */
598     p_vout->p_sys->i_index = ++p_vout->p_sys->i_index & 1;
599     p_pic->p->p_pixels = p_vout->p_sys->pp_buffer[p_vout->p_sys->i_index];
600 }
601
602 /* following functions are local */
603
604 /*****************************************************************************
605  * BeosOpenDisplay: open and initialize BeOS device
606  *****************************************************************************/
607 static int BeosOpenDisplay( vout_thread_t *p_vout )
608
609     p_vout->p_sys->p_window = new VideoWindow( p_vout->p_sys->i_width - 1,
610                                                p_vout->p_sys->i_height - 1,
611                                                p_vout );
612
613     if( p_vout->p_sys->p_window == NULL )
614     {
615         intf_ErrMsg( "error: cannot allocate memory for VideoWindow" );
616         return( 1 );
617     }   
618     
619     return( 0 );
620 }
621
622 /*****************************************************************************
623  * BeosDisplay: close and reset BeOS device
624  *****************************************************************************
625  * Returns all resources allocated by BeosOpenDisplay and restore the original
626  * state of the device.
627  *****************************************************************************/
628 static void BeosCloseDisplay( vout_thread_t *p_vout )
629 {    
630     /* Destroy the video window */
631     delete p_vout->p_sys->p_window;
632 }
633
634 } /* extern "C" */