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