Fixes for preventing multiple playlist windows
[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.59 2002/06/01 09:21:59 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
50 #include "VideoWindow.h"
51 #include "DrawingTidbits.h"
52
53 /*****************************************************************************
54  * vout_sys_t: BeOS video output method descriptor
55  *****************************************************************************
56  * This structure is part of the video output thread descriptor.
57  * It describes the BeOS specific properties of an output thread.
58  *****************************************************************************/
59 typedef struct vout_sys_s
60 {
61     VideoWindow *  p_window;
62
63     s32 i_width;
64     s32 i_height;
65
66     u8 *pp_buffer[3];
67     int i_index;
68
69 } vout_sys_t;
70
71 /*****************************************************************************
72  * beos_GetAppWindow : retrieve a BWindow pointer from the window name
73  *****************************************************************************/
74 BWindow *beos_GetAppWindow(char *name)
75 {
76     int32       index;
77     BWindow     *window;
78     
79     for (index = 0 ; ; index++)
80     {
81         window = be_app->WindowAt(index);
82         if (window == NULL)
83             break;
84         if (window->LockWithTimeout(20000) == B_OK)
85         {
86             if (strcmp(window->Name(), name) == 0)
87             {
88                 window->Unlock();
89                 break;
90             }
91             window->Unlock();
92         }
93     }
94     return window; 
95 }
96
97 /*****************************************************************************
98  * VideoWindow constructor and destructor
99  *****************************************************************************/
100 VideoWindow::VideoWindow( int v_width, int v_height, 
101                           BRect frame )
102             : BWindow( frame, NULL, B_TITLED_WINDOW, 
103                     B_NOT_CLOSABLE | B_NOT_MINIMIZABLE )
104 {
105     BView *mainView =  new BView( Bounds(), "mainView", 
106                                   B_FOLLOW_ALL, B_FULL_UPDATE_ON_RESIZE);
107     AddChild(mainView);
108     mainView->SetViewColor(kBlack);
109                                   
110     /* create the view to do the display */
111     view = new VLCView( Bounds() );
112     mainView->AddChild(view);
113
114     /* set the VideoWindow variables */
115     teardownwindow = false;
116     is_zoomed = false;
117     i_buffer = 0;
118
119     /* call ScreenChanged to set vsync correctly */
120     BScreen *screen;
121     display_mode disp_mode; 
122     float refresh;
123
124     screen = new BScreen(this);
125     
126     screen-> GetMode(&disp_mode); 
127     refresh = 
128          (disp_mode.timing.pixel_clock * 1000)/((disp_mode.timing.h_total)* 
129          (disp_mode.timing.v_total)); 
130     if (refresh  < 61) 
131     { 
132         vsync = true; 
133     } 
134     delete screen;
135     
136     // remember current settings
137     i_width = frame.IntegerWidth();
138     i_height = frame.IntegerHeight();
139     FrameResized(frame.IntegerWidth(), frame.IntegerHeight());
140
141     mode = SelectDrawingMode(v_width, v_height);
142     Show();
143 }
144
145 VideoWindow::~VideoWindow()
146 {
147     int32 result;
148
149     teardownwindow = true;
150     wait_for_thread(fDrawThreadID, &result);
151     delete bitmap[0];
152     delete bitmap[1];
153     delete bitmap[2];
154 }
155
156 bool VideoWindow::QuitRequested()
157 {
158     return true;
159 }
160
161 void VideoWindow::drawBuffer(int bufferIndex)
162 {
163     status_t status;
164
165     i_buffer = bufferIndex;
166     // sync to the screen if required
167     if (vsync)
168     {
169         BScreen *screen;
170         screen = new BScreen(this);
171         screen-> WaitForRetrace(22000);
172         delete screen;
173     }
174     if (LockLooper())
175     {
176        // switch the overlay bitmap
177        if (mode == OVERLAY)
178        {
179           rgb_color key;
180           view->SetViewOverlay(bitmap[i_buffer], 
181                             bitmap[i_buffer]->Bounds() ,
182                             view->Bounds(),
183                             &key, B_FOLLOW_ALL,
184                                     B_OVERLAY_FILTER_HORIZONTAL|B_OVERLAY_FILTER_VERTICAL|
185                                     B_OVERLAY_TRANSFER_CHANNEL);
186                    //view->SetViewColor(key);
187            }
188        else
189        {
190          // switch the bitmap
191          view-> DrawBitmap(bitmap[i_buffer], view->Bounds() );
192        }
193        UnlockLooper();
194     }
195 }
196
197 void VideoWindow::Zoom(BPoint origin, float width, float height )
198 {
199     if(is_zoomed)
200     {
201         is_zoomed = !is_zoomed;
202         MoveTo(winSize.left, winSize.top);
203         ResizeTo(winSize.IntegerWidth(), winSize.IntegerHeight());
204         be_app->ShowCursor();
205     }
206     else
207     {
208         is_zoomed = !is_zoomed;
209         BScreen *screen;
210         screen = new BScreen(this);
211         BRect rect = screen->Frame();
212         delete screen;
213         MoveTo(0,0);
214         ResizeTo(rect.IntegerWidth(), rect.IntegerHeight());
215         be_app->ObscureCursor();
216     }
217 }
218
219 void VideoWindow::FrameMoved(BPoint origin) 
220 {
221         if (is_zoomed) return ;
222     winSize = Frame();
223 }
224
225 void VideoWindow::FrameResized( float width, float height )
226 {
227     float out_width, out_height;
228     float out_left, out_top;
229     float width_scale = width / i_width;
230     float height_scale = height / i_height;
231
232     if (width_scale <= height_scale)
233     {
234         out_width = (i_width * width_scale);
235         out_height = (i_height * width_scale);
236         out_left = 0; 
237         out_top = (height - out_height) / 2;
238     }
239     else   /* if the height is proportionally smaller */
240     {
241         out_width = (i_width * height_scale);
242         out_height = (i_height * height_scale);
243         out_top = 0;
244         out_left = (width - out_width) /2;
245     }
246     view->MoveTo(out_left,out_top);
247     view->ResizeTo(out_width, out_height);
248         if (!is_zoomed)
249         {
250         winSize = Frame();
251     }
252 }
253
254 void VideoWindow::ScreenChanged(BRect frame, color_space mode)
255 {
256     BScreen *screen;
257     float refresh;
258     
259     screen = new BScreen(this);
260     display_mode disp_mode; 
261     
262     screen-> GetMode(&disp_mode); 
263     refresh = 
264          (disp_mode.timing.pixel_clock * 1000)/((disp_mode.timing.h_total)* 
265          (disp_mode.timing.v_total)); 
266     if (refresh  < 61) 
267     { 
268         vsync = true; 
269     } 
270     rgb_color key;
271     view->SetViewOverlay(bitmap[i_buffer], 
272                          bitmap[i_buffer]->Bounds() ,
273                          view->Bounds(),
274                          &key, B_FOLLOW_ALL,
275                          B_OVERLAY_FILTER_HORIZONTAL|B_OVERLAY_FILTER_VERTICAL);
276     view->SetViewColor(key);
277 }
278
279 void VideoWindow::WindowActivated(bool active)
280 {
281 }
282
283 int VideoWindow::SelectDrawingMode(int width, int height)
284 {
285     int drawingMode = BITMAP;
286
287     int noOverlay = !config_GetIntVariable( "overlay" );
288     for (int i = 0; i < COLOR_COUNT; i++)
289     {
290         if (noOverlay) break;
291         bitmap[0] = new BBitmap ( BRect( 0, 0, width, height ), 
292                                   B_BITMAP_WILL_OVERLAY,
293                                   colspace[i].colspace);
294
295         if(bitmap[0] && bitmap[0]->InitCheck() == B_OK) 
296         {
297             colspace_index = i;
298
299             bitmap[1] = new BBitmap( BRect( 0, 0, width, height ), B_BITMAP_WILL_OVERLAY,
300                                      colspace[colspace_index].colspace);
301             bitmap[2] = new BBitmap( BRect( 0, 0, width, height ), B_BITMAP_WILL_OVERLAY,
302                                      colspace[colspace_index].colspace);
303             if ( (bitmap[2] && bitmap[2]->InitCheck() == B_OK) )
304             {
305                drawingMode = OVERLAY;
306                rgb_color key;
307                view->SetViewOverlay(bitmap[0], 
308                                     bitmap[0]->Bounds() ,
309                                     view->Bounds(),
310                                     &key, B_FOLLOW_ALL,
311                                             B_OVERLAY_FILTER_HORIZONTAL|B_OVERLAY_FILTER_VERTICAL);
312                        view->SetViewColor(key);
313                SetTitle(VOUT_TITLE " (Overlay)");
314                break;
315             }
316             else
317             {
318                delete bitmap[0];
319                delete bitmap[1];
320                delete bitmap[2];
321             }
322         }
323         else
324         {
325             delete bitmap[0];
326         }        
327         }
328
329     if (drawingMode == BITMAP)
330         {
331         // fallback to RGB32
332         colspace_index = DEFAULT_COL;
333         SetTitle(VOUT_TITLE " (Bitmap)");
334         bitmap[0] = new BBitmap( BRect( 0, 0, width, height ), colspace[colspace_index].colspace);
335         bitmap[1] = new BBitmap( BRect( 0, 0, width, height ), colspace[colspace_index].colspace);
336         bitmap[2] = new BBitmap( BRect( 0, 0, width, height ), colspace[colspace_index].colspace);
337     }
338     return drawingMode;
339 }
340
341 /*****************************************************************************
342  * VLCView::VLCView
343  *****************************************************************************/
344 VLCView::VLCView(BRect bounds) : BView(bounds, "", B_FOLLOW_NONE,
345                                        B_WILL_DRAW)
346
347 {
348     SetViewColor(B_TRANSPARENT_32_BIT);
349 }
350
351 /*****************************************************************************
352  * VLCView::~VLCView
353  *****************************************************************************/
354 VLCView::~VLCView()
355 {
356 }
357
358 /*****************************************************************************
359  * VLCVIew::MouseDown
360  *****************************************************************************/
361 void VLCView::MouseDown(BPoint point)
362 {
363     BWindow *win = Window();
364     win->Zoom();
365 }
366
367 /*****************************************************************************
368  * VLCVIew::Draw
369  *****************************************************************************/
370 void VLCView::Draw(BRect updateRect) 
371 {
372     VideoWindow *win = (VideoWindow *) Window();
373     if (win->mode == BITMAP)
374       FillRect(updateRect);
375 }
376
377
378 extern "C"
379 {
380
381 /*****************************************************************************
382  * Local prototypes
383  *****************************************************************************/
384 static int  vout_Create     ( vout_thread_t * );
385 static int  vout_Init       ( vout_thread_t * );
386 static void vout_End        ( vout_thread_t * );
387 static void vout_Destroy    ( vout_thread_t * );
388 static int  vout_Manage     ( vout_thread_t * );
389 static void vout_Display    ( vout_thread_t *, picture_t * );
390 static void vout_Render     ( vout_thread_t *, picture_t * );
391
392 static int  BeosOpenDisplay ( vout_thread_t *p_vout );
393 static void BeosCloseDisplay( vout_thread_t *p_vout );
394
395 /*****************************************************************************
396  * Functions exported as capabilities. They are declared as static so that
397  * we don't pollute the namespace too much.
398  *****************************************************************************/
399 void _M( vout_getfunctions )( function_list_t * p_function_list )
400 {
401     p_function_list->functions.vout.pf_create     = vout_Create;
402     p_function_list->functions.vout.pf_init       = vout_Init;
403     p_function_list->functions.vout.pf_end        = vout_End;
404     p_function_list->functions.vout.pf_destroy    = vout_Destroy;
405     p_function_list->functions.vout.pf_manage     = vout_Manage;
406     p_function_list->functions.vout.pf_display    = vout_Display;
407     p_function_list->functions.vout.pf_render     = vout_Render;
408 }
409
410 /*****************************************************************************
411  * vout_Create: allocates BeOS video thread output method
412  *****************************************************************************
413  * This function allocates and initializes a BeOS vout method.
414  *****************************************************************************/
415 int vout_Create( vout_thread_t *p_vout )
416 {
417     /* Allocate structure */
418     p_vout->p_sys = (vout_sys_t*) malloc( sizeof( vout_sys_t ) );
419     if( p_vout->p_sys == NULL )
420     {
421         intf_ErrMsg( "error: %s", strerror(ENOMEM) );
422         return( 1 );
423     }
424     p_vout->p_sys->i_width = p_vout->render.i_width;
425     p_vout->p_sys->i_height = p_vout->render.i_height;
426
427     return( 0 );
428 }
429
430 /*****************************************************************************
431  * vout_Init: initialize BeOS video thread output method
432  *****************************************************************************/
433 int vout_Init( vout_thread_t *p_vout )
434 {
435     int i_index;
436     picture_t *p_pic;
437
438     I_OUTPUTPICTURES = 0;
439
440     /* Open and initialize device */
441     if( BeosOpenDisplay( p_vout ) )
442     {
443         intf_ErrMsg("vout error: can't open display");
444         return 0;
445     }
446     /* Set the buffers */
447     p_vout->p_sys->pp_buffer[0] = (u8*)p_vout->p_sys->p_window->bitmap[0]->Bits();
448     p_vout->p_sys->pp_buffer[1] = (u8*)p_vout->p_sys->p_window->bitmap[1]->Bits();
449     p_vout->p_sys->pp_buffer[2] = (u8*)p_vout->p_sys->p_window->bitmap[2]->Bits();
450     p_vout->output.i_width  = p_vout->render.i_width;
451     p_vout->output.i_height = p_vout->render.i_height;
452
453     /* Assume we have square pixels */
454     p_vout->output.i_aspect = p_vout->p_sys->i_width
455                                * VOUT_ASPECT_FACTOR / p_vout->p_sys->i_height;
456     p_vout->output.i_chroma = colspace[p_vout->p_sys->p_window->colspace_index].chroma;
457     p_vout->p_sys->i_index = 0;
458
459     p_vout->output.i_rmask  = 0x00ff0000;
460     p_vout->output.i_gmask  = 0x0000ff00;
461     p_vout->output.i_bmask  = 0x000000ff;
462
463     for (int buffer_index = 0 ; buffer_index < 3; buffer_index++)
464     {
465        p_pic = NULL;
466        /* Find an empty picture slot */
467        for( i_index = 0 ; i_index < VOUT_MAX_PICTURES ; i_index++ )
468        {
469            p_pic = NULL;
470            if( p_vout->p_picture[ i_index ].i_status == FREE_PICTURE )
471            {
472                p_pic = p_vout->p_picture + i_index;
473                break;
474            }
475        }
476
477        if( p_pic == NULL )
478        {
479            return 0;
480        }
481        p_pic->p->p_pixels = p_vout->p_sys->pp_buffer[0];
482        p_pic->p->i_lines = p_vout->p_sys->i_height;
483
484        p_pic->p->i_pixel_bytes = colspace[p_vout->p_sys->p_window->colspace_index].pixel_bytes;
485        p_pic->i_planes = colspace[p_vout->p_sys->p_window->colspace_index].planes;
486        p_pic->p->i_pitch = p_vout->p_sys->p_window->bitmap[0]->BytesPerRow(); 
487
488        if (p_vout->p_sys->p_window->mode == OVERLAY)
489        {
490           p_pic->p->i_visible_bytes = (p_vout->p_sys->p_window->bitmap[0]->Bounds().IntegerWidth()+1) 
491                                      * p_pic->p->i_pixel_bytes; 
492           p_pic->p->b_margin = 1;
493           p_pic->p->b_hidden = 0;
494        }
495        else
496        {
497           p_pic->p->b_margin = 0;
498           p_pic->p->i_visible_bytes = p_pic->p->i_pitch;
499        }
500
501        p_pic->i_status = DESTROYED_PICTURE;
502        p_pic->i_type   = DIRECT_PICTURE;
503  
504        PP_OUTPUTPICTURE[ I_OUTPUTPICTURES ] = p_pic;
505
506        I_OUTPUTPICTURES++;
507     }
508
509     return( 0 );
510 }
511
512 /*****************************************************************************
513  * vout_End: terminate BeOS video thread output method
514  *****************************************************************************/
515 void vout_End( vout_thread_t *p_vout )
516 {
517     BeosCloseDisplay( p_vout );
518 }
519
520 /*****************************************************************************
521  * vout_Destroy: destroy BeOS video thread output method
522  *****************************************************************************
523  * Terminate an output method created by DummyCreateOutputMethod
524  *****************************************************************************/
525 void vout_Destroy( vout_thread_t *p_vout )
526 {
527     free( p_vout->p_sys );
528 }
529
530 /*****************************************************************************
531  * vout_Manage: handle BeOS events
532  *****************************************************************************
533  * This function should be called regularly by video output thread. It manages
534  * console events. It returns a non null value on error.
535  *****************************************************************************/
536 int vout_Manage( vout_thread_t *p_vout )
537 {
538                           
539     return( 0 );
540 }
541
542 /*****************************************************************************
543  * vout_Render: render previously calculated output
544  *****************************************************************************/
545 void vout_Render( vout_thread_t *p_vout, picture_t *p_pic )
546 {
547     ;
548 }
549
550 /*****************************************************************************
551  * vout_Display: displays previously rendered output
552  *****************************************************************************
553  * This function send the currently rendered image to BeOS image, waits until
554  * it is displayed and switch the two rendering buffers, preparing next frame.
555  *****************************************************************************/
556 void vout_Display( vout_thread_t *p_vout, picture_t *p_pic )
557 {
558     VideoWindow * p_win = p_vout->p_sys->p_window;
559
560     /* draw buffer if required */    
561     if (!p_win->teardownwindow)
562     { 
563        p_win->drawBuffer(p_vout->p_sys->i_index);
564     }
565     /* change buffer */
566     p_vout->p_sys->i_index = ++p_vout->p_sys->i_index % 3;
567     p_pic->p->p_pixels = p_vout->p_sys->pp_buffer[p_vout->p_sys->i_index];
568 }
569
570 /* following functions are local */
571
572 /*****************************************************************************
573  * BeosOpenDisplay: open and initialize BeOS device
574  *****************************************************************************/
575 static int BeosOpenDisplay( vout_thread_t *p_vout )
576
577
578     p_vout->p_sys->p_window = new VideoWindow( p_vout->p_sys->i_width - 1,
579                                                p_vout->p_sys->i_height - 1,
580                                                BRect( 20, 50,
581                                                       20 + p_vout->i_window_width - 1, 
582                                                       50 + p_vout->i_window_height - 1 ));
583
584     if( p_vout->p_sys->p_window == NULL )
585     {
586         intf_ErrMsg( "error: cannot allocate memory for VideoWindow" );
587         return( 1 );
588     }   
589     
590     return( 0 );
591 }
592
593 /*****************************************************************************
594  * BeosDisplay: close and reset BeOS device
595  *****************************************************************************
596  * Returns all resources allocated by BeosOpenDisplay and restore the original
597  * state of the device.
598  *****************************************************************************/
599 static void BeosCloseDisplay( vout_thread_t *p_vout )
600 {    
601     VideoWindow * p_win = p_vout->p_sys->p_window;
602     /* Destroy the video window */
603     if( p_win != NULL && !p_win->teardownwindow)
604     {
605         p_win->Lock();
606         p_win->teardownwindow = true;
607         p_win->Hide();
608         p_win->Quit();
609     }
610     p_win = NULL;
611 }
612
613
614
615 } /* extern "C" */