]> git.sesse.net Git - vlc/blob - modules/gui/beos/VideoOutput.cpp
* ./modules/*: moved plugins to the new tree. Yet untested builds include
[vlc] / modules / gui / beos / VideoOutput.cpp
1 /*****************************************************************************
2  * vout.cpp: beos video output display method
3  *****************************************************************************
4  * Copyright (C) 2000, 2001 VideoLAN
5  * $Id: VideoOutput.cpp,v 1.1 2002/08/04 17:23:43 sam 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  * Local prototypes
465  *****************************************************************************/
466 static int  Init       ( vout_thread_t * );
467 static void End        ( vout_thread_t * );
468 static int  Manage     ( vout_thread_t * );
469 static void Display    ( vout_thread_t *, picture_t * );
470
471 static int  BeosOpenDisplay ( vout_thread_t *p_vout );
472 static void BeosCloseDisplay( vout_thread_t *p_vout );
473
474 /*****************************************************************************
475  * OpenVideo: allocates BeOS video thread output method
476  *****************************************************************************
477  * This function allocates and initializes a BeOS vout method.
478  *****************************************************************************/
479 int E_(OpenVideo) ( vlc_object_t *p_this )
480 {
481     vout_thread_t * p_vout = (vout_thread_t *)p_this;
482
483     /* Allocate structure */
484     p_vout->p_sys = (vout_sys_t*) malloc( sizeof( vout_sys_t ) );
485     if( p_vout->p_sys == NULL )
486     {
487         msg_Err( p_vout, "out of memory" );
488         return( 1 );
489     }
490     p_vout->p_sys->i_width = p_vout->render.i_width;
491     p_vout->p_sys->i_height = p_vout->render.i_height;
492     p_vout->p_sys->source_chroma = p_vout->render.i_chroma;
493
494     p_vout->pf_init = Init;
495     p_vout->pf_end = End;
496     p_vout->pf_manage = NULL;
497     p_vout->pf_render = NULL;
498     p_vout->pf_display = Display;
499
500     return( 0 );
501 }
502
503 /*****************************************************************************
504  * Init: initialize BeOS video thread output method
505  *****************************************************************************/
506 int Init( vout_thread_t *p_vout )
507 {
508     int i_index;
509     picture_t *p_pic;
510
511     I_OUTPUTPICTURES = 0;
512
513     /* Open and initialize device */
514     if( BeosOpenDisplay( p_vout ) )
515     {
516         msg_Err(p_vout, "vout error: can't open display");
517         return 0;
518     }
519     p_vout->output.i_width  = p_vout->render.i_width;
520     p_vout->output.i_height = p_vout->render.i_height;
521
522     /* Assume we have square pixels */
523     p_vout->output.i_aspect = p_vout->p_sys->i_width
524                                * VOUT_ASPECT_FACTOR / p_vout->p_sys->i_height;
525     p_vout->output.i_chroma = colspace[p_vout->p_sys->p_window->colspace_index].chroma;
526     p_vout->p_sys->i_index = 0;
527
528     p_vout->b_direct = 1;
529
530     p_vout->output.i_rmask  = 0x00ff0000;
531     p_vout->output.i_gmask  = 0x0000ff00;
532     p_vout->output.i_bmask  = 0x000000ff;
533
534     for (int buffer_index = 0 ; buffer_index < 3; buffer_index++)
535     {
536        p_pic = NULL;
537        /* Find an empty picture slot */
538        for( i_index = 0 ; i_index < VOUT_MAX_PICTURES ; i_index++ )
539        {
540            p_pic = NULL;
541            if( p_vout->p_picture[ i_index ].i_status == FREE_PICTURE )
542            {
543                p_pic = p_vout->p_picture + i_index;
544                break;
545            }
546        }
547
548        if( p_pic == NULL )
549        {
550            return 0;
551        }
552        p_pic->p->p_pixels = (u8*)p_vout->p_sys->p_window->bitmap[buffer_index]->Bits();
553        p_pic->p->i_lines = p_vout->p_sys->i_height;
554
555        p_pic->p->i_pixel_pitch = colspace[p_vout->p_sys->p_window->colspace_index].pixel_bytes;
556        p_pic->i_planes = colspace[p_vout->p_sys->p_window->colspace_index].planes;
557        p_pic->p->i_pitch = p_vout->p_sys->p_window->bitmap[buffer_index]->BytesPerRow(); 
558        p_pic->p->i_visible_pitch = p_pic->p->i_pixel_pitch * ( p_vout->p_sys->p_window->bitmap[buffer_index]->Bounds().IntegerWidth() + 1 );
559
560        p_pic->i_status = DESTROYED_PICTURE;
561        p_pic->i_type   = DIRECT_PICTURE;
562  
563        PP_OUTPUTPICTURE[ I_OUTPUTPICTURES ] = p_pic;
564
565        I_OUTPUTPICTURES++;
566     }
567
568     return( 0 );
569 }
570
571 /*****************************************************************************
572  * End: terminate BeOS video thread output method
573  *****************************************************************************/
574 void End( vout_thread_t *p_vout )
575 {
576     BeosCloseDisplay( p_vout );
577 }
578
579 /*****************************************************************************
580  * CloseVideo: destroy BeOS video thread output method
581  *****************************************************************************
582  * Terminate an output method created by DummyCreateOutputMethod
583  *****************************************************************************/
584 void E_(CloseVideo) ( vlc_object_t *p_this )
585 {
586     vout_thread_t * p_vout = (vout_thread_t *)p_this;
587
588     free( p_vout->p_sys );
589 }
590
591 /*****************************************************************************
592  * Display: displays previously rendered output
593  *****************************************************************************
594  * This function send the currently rendered image to BeOS image, waits until
595  * it is displayed and switch the two rendering buffers, preparing next frame.
596  *****************************************************************************/
597 void Display( vout_thread_t *p_vout, picture_t *p_pic )
598 {
599     VideoWindow * p_win = p_vout->p_sys->p_window;
600
601     /* draw buffer if required */    
602     if (!p_win->teardownwindow)
603     { 
604        p_win->drawBuffer(p_vout->p_sys->i_index);
605     }
606     /* change buffer */
607     p_vout->p_sys->i_index = ++p_vout->p_sys->i_index % 3;
608     p_pic->p->p_pixels = (u8*)p_vout->p_sys->p_window->bitmap[p_vout->p_sys->i_index]->Bits();
609 }
610
611 /* following functions are local */
612
613 /*****************************************************************************
614  * BeosOpenDisplay: open and initialize BeOS device
615  *****************************************************************************/
616 static int BeosOpenDisplay( vout_thread_t *p_vout )
617
618
619     p_vout->p_sys->p_window = new VideoWindow( p_vout->p_sys->i_width - 1,
620                                                p_vout->p_sys->i_height - 1,
621                                                BRect( 20, 50,
622                                                       20 + p_vout->i_window_width - 1, 
623                                                       50 + p_vout->i_window_height - 1 ));
624
625     if( p_vout->p_sys->p_window == NULL )
626     {
627         msg_Err( p_vout, "cannot allocate VideoWindow" );
628         return( 1 );
629     }   
630     
631     return( 0 );
632 }
633
634 /*****************************************************************************
635  * BeosDisplay: close and reset BeOS device
636  *****************************************************************************
637  * Returns all resources allocated by BeosOpenDisplay and restore the original
638  * state of the device.
639  *****************************************************************************/
640 static void BeosCloseDisplay( vout_thread_t *p_vout )
641 {    
642     VideoWindow * p_win = p_vout->p_sys->p_window;
643     /* Destroy the video window */
644     if( p_win != NULL && !p_win->teardownwindow)
645     {
646         p_win->Lock();
647         p_win->teardownwindow = true;
648         p_win->Hide();
649         p_win->Quit();
650     }
651     p_win = NULL;
652 }
653