377ab0b25a1b0f53797c9f46db2baaf10abd53f8
[vlc.git] / modules / gui / beos / VideoOutput.cpp
1 /*****************************************************************************
2  * vout_beos.cpp: beos video output display method
3  *****************************************************************************
4  * Copyright (C) 2000, 2001 VideoLAN
5  * $Id: VideoOutput.cpp,v 1.11 2003/02/01 12:01:11 stippi 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  *          Stephan Aßmus <stippi@yellowbites.com>
12  *
13  * This program is free software; you can redistribute it and/or modify
14  * it under the terms of the GNU General Public License as published by
15  * the Free Software Foundation; either version 2 of the License, or
16  * (at your option) any later version.
17  * 
18  * This program is distributed in the hope that it will be useful,
19  * but WITHOUT ANY WARRANTY; without even the implied warranty of
20  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
21  * GNU General Public License for more details.
22  *
23  * You should have received a copy of the GNU General Public License
24  * along with this program; if not, write to the Free Software
25  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111, USA.
26  *****************************************************************************/
27
28 /*****************************************************************************
29  * Preamble
30  *****************************************************************************/
31 #include <errno.h>                                                 /* ENOMEM */
32 #include <stdlib.h>                                                /* free() */
33 #include <stdio.h>
34 #include <string.h>                                            /* strerror() */
35
36 #include <Application.h>
37 #include <BitmapStream.h>
38 #include <Bitmap.h>
39 #include <DirectWindow.h>
40 #include <File.h>
41 #include <InterfaceKit.h>
42 #include <NodeInfo.h>
43 #include <String.h>
44 #include <TranslatorRoster.h>
45
46 /* VLC headers */
47 #include <vlc/vlc.h>
48 #include <vlc/intf.h>
49 #include <vlc/vout.h>
50
51 #include "InterfaceWindow.h"    // for load/save_settings()
52 #include "DrawingTidbits.h"
53 #include "MsgVals.h"
54
55 #include "VideoWindow.h"
56
57 /*****************************************************************************
58  * vout_sys_t: BeOS video output method descriptor
59  *****************************************************************************
60  * This structure is part of the video output thread descriptor.
61  * It describes the BeOS specific properties of an output thread.
62  *****************************************************************************/
63 struct vout_sys_t
64 {
65     VideoWindow *  p_window;
66
67     s32 i_width;
68     s32 i_height;
69
70 //    u8 *pp_buffer[3];
71     u32 source_chroma;
72     int i_index;
73
74 };
75
76 #define MOUSE_IDLE_TIMEOUT 2000000      // two seconds
77 #define MIN_AUTO_VSYNC_REFRESH 61       // Hz
78 #define DEFAULT_SCREEN_SHOT_FORMAT 'PNG '
79 #define DEFAULT_SCREEN_SHOT_PATH "/boot/home/vlc screenshot"
80
81 /*****************************************************************************
82  * beos_GetAppWindow : retrieve a BWindow pointer from the window name
83  *****************************************************************************/
84 BWindow*
85 beos_GetAppWindow(char *name)
86 {
87     int32       index;
88     BWindow     *window;
89     
90     for (index = 0 ; ; index++)
91     {
92         window = be_app->WindowAt(index);
93         if (window == NULL)
94             break;
95         if (window->LockWithTimeout(20000) == B_OK)
96         {
97             if (strcmp(window->Name(), name) == 0)
98             {
99                 window->Unlock();
100                 break;
101             }
102             window->Unlock();
103         }
104     }
105     return window; 
106 }
107
108 /*****************************************************************************
109  * get_interface_window
110  *****************************************************************************/
111 BWindow*
112 get_interface_window()
113 {
114         return beos_GetAppWindow(VOUT_TITLE);
115 }
116
117 class BackgroundView : public BView
118 {
119  public:
120                                                         BackgroundView(BRect frame, VLCView* view)
121                                                         : BView(frame, "background",
122                                                                         B_FOLLOW_ALL, B_FULL_UPDATE_ON_RESIZE),
123                                                           fVideoView(view)
124                                                         {
125                                                                 SetViewColor(kBlack);
126                                                         }
127         virtual                                 ~BackgroundView() {}
128
129         virtual void                    MouseDown(BPoint where)
130                                                         {
131                                                                 // convert coordinates
132                                                                 where = fVideoView->ConvertFromParent(where);
133                                                                 // let him handle it
134                                                                 fVideoView->MouseDown(where);
135                                                         }
136         virtual void                    MouseMoved(BPoint where, uint32 transit,
137                                                                            const BMessage* dragMessage)
138                                                         {
139                                                                 // convert coordinates
140                                                                 where = fVideoView->ConvertFromParent(where);
141                                                                 // let him handle it
142                                                                 fVideoView->MouseMoved(where, transit, dragMessage);
143                                                                 // notice: It might look like transit should be
144                                                                 // B_OUTSIDE_VIEW regardless, but leave it like this,
145                                                                 // otherwise, unwanted things will happen!
146                                                         }
147
148  private:
149         VLCView*                                fVideoView;
150 };
151
152
153 /*****************************************************************************
154  * VideoSettings constructor and destructor
155  *****************************************************************************/
156 VideoSettings::VideoSettings()
157         : fVideoSize( SIZE_100 ),
158           fFlags( FLAG_CORRECT_RATIO ),
159           fSettings( new BMessage( 'sett' ) )
160 {
161         // read settings from disk
162         status_t ret = load_settings( fSettings, "video_settings", "VideoLAN Client" );
163         if ( ret == B_OK )
164         {
165                 uint32 flags;
166                 if ( fSettings->FindInt32( "flags", (int32*)&flags ) == B_OK )
167                         SetFlags( flags );
168                 uint32 size;
169                 if ( fSettings->FindInt32( "video size", (int32*)&size ) == B_OK )
170                         SetVideoSize( size );
171         }
172         else
173         {
174                 fprintf( stderr, "error loading video settings: %s\n", strerror( ret ) );
175                 
176                 // figure out if we should use vertical sync by default
177                 BScreen screen(B_MAIN_SCREEN_ID);
178                 if (screen.IsValid())
179                 {
180                         display_mode mode; 
181                         screen.GetMode(&mode); 
182                         float refresh = (mode.timing.pixel_clock * 1000)
183                                                         / ((mode.timing.h_total)* (mode.timing.v_total)); 
184                         if (refresh < MIN_AUTO_VSYNC_REFRESH)
185                                 AddFlags(FLAG_SYNC_RETRACE);
186                 }
187         }
188 }
189
190 VideoSettings::VideoSettings( const VideoSettings& clone )
191         : fVideoSize( clone.VideoSize() ),
192           fFlags( clone.Flags() ),
193           fSettings( NULL )
194 {
195 }
196
197
198 VideoSettings::~VideoSettings()
199 {
200         if ( fSettings )
201         {
202                 // we are the default settings
203                 // and write our settings to disk
204                 if (fSettings->ReplaceInt32( "video size", VideoSize() ) != B_OK)
205                         fSettings->AddInt32( "video size", VideoSize() );
206                 if (fSettings->ReplaceInt32( "flags", Flags() ) != B_OK)
207                         fSettings->AddInt32( "flags", Flags() );
208
209                 status_t ret = save_settings( fSettings, "video_settings", "VideoLAN Client" );
210                 if ( ret != B_OK )
211                         fprintf( stderr, "error saving video settings: %s\n", strerror( ret ) );
212                 delete fSettings;
213         }
214         else
215         {
216                 // we are just a clone of the default settings
217                 fDefaultSettings.SetVideoSize( VideoSize() );
218                 fDefaultSettings.SetFlags( Flags() );
219         }
220 }
221
222 /*****************************************************************************
223  * VideoSettings::DefaultSettings
224  *****************************************************************************/
225 VideoSettings*
226 VideoSettings::DefaultSettings()
227 {
228         return &fDefaultSettings;
229 }
230
231 /*****************************************************************************
232  * VideoSettings::SetVideoSize
233  *****************************************************************************/
234 void
235 VideoSettings::SetVideoSize( uint32 mode )
236 {
237         fVideoSize = mode;
238 }
239
240 // static variable initialization
241 VideoSettings
242 VideoSettings::fDefaultSettings;
243
244
245 /*****************************************************************************
246  * VideoWindow constructor and destructor
247  *****************************************************************************/
248 VideoWindow::VideoWindow(int v_width, int v_height, BRect frame,
249                          vout_thread_t *p_videoout)
250         : BWindow(frame, NULL, B_TITLED_WINDOW, B_NOT_CLOSABLE | B_NOT_MINIMIZABLE),
251           i_width(frame.IntegerWidth()),
252           i_height(frame.IntegerHeight()),
253           winSize(frame),
254           i_buffer(0),
255           teardownwindow(false),
256           fTrueWidth(v_width),
257           fTrueHeight(v_height),
258           fCachedFeel(B_NORMAL_WINDOW_FEEL),
259           fInterfaceShowing(false),
260           fInitStatus(B_ERROR),
261           fSettings(new VideoSettings(*VideoSettings::DefaultSettings()))
262 {
263     p_vout = p_videoout;
264     
265     // create the view to do the display
266     view = new VLCView( Bounds(), p_vout );
267
268         // create background view
269     BView *mainView =  new BackgroundView( Bounds(), view );
270     AddChild(mainView);
271     mainView->AddChild(view);
272
273         // allocate bitmap buffers
274         for (int32 i = 0; i < 3; i++)
275                 bitmap[i] = NULL;
276         fInitStatus = _AllocateBuffers(v_width, v_height, &mode);
277
278         // make sure we layout the view correctly
279     FrameResized(i_width, i_height);
280
281     if (fInitStatus >= B_OK && mode == OVERLAY)
282     {
283        overlay_restrictions r;
284
285        bitmap[1]->GetOverlayRestrictions(&r);
286        SetSizeLimits((i_width * r.min_width_scale), i_width * r.max_width_scale,
287                      (i_height * r.min_height_scale), i_height * r.max_height_scale);
288     }
289     
290         // vlc settings override settings from disk
291         if (config_GetInt(p_vout, "fullscreen"))
292                 fSettings->AddFlags(VideoSettings::FLAG_FULL_SCREEN);
293
294     _SetToSettings();
295 }
296
297 VideoWindow::~VideoWindow()
298 {
299     int32 result;
300
301     teardownwindow = true;
302     wait_for_thread(fDrawThreadID, &result);
303     _FreeBuffers();
304         delete fSettings;
305 }
306
307 /*****************************************************************************
308  * VideoWindow::MessageReceived
309  *****************************************************************************/
310 void
311 VideoWindow::MessageReceived( BMessage *p_message )
312 {
313         switch( p_message->what )
314         {
315                 case TOGGLE_FULL_SCREEN:
316                         BWindow::Zoom();
317                         break;
318                 case RESIZE_50:
319                 case RESIZE_100:
320                 case RESIZE_200:
321                         if (IsFullScreen())
322                                 BWindow::Zoom();
323                         _SetVideoSize(p_message->what);
324                         break;
325                 case VERT_SYNC:
326                         SetSyncToRetrace(!IsSyncedToRetrace());
327                         break;
328                 case WINDOW_FEEL:
329                         {
330                                 window_feel winFeel;
331                                 if (p_message->FindInt32("WinFeel", (int32*)&winFeel) == B_OK)
332                                 {
333                                         SetFeel(winFeel);
334                                         fCachedFeel = winFeel;
335                                         if (winFeel == B_FLOATING_ALL_WINDOW_FEEL)
336                                                 fSettings->AddFlags(VideoSettings::FLAG_ON_TOP_ALL);
337                                         else
338                                                 fSettings->ClearFlags(VideoSettings::FLAG_ON_TOP_ALL);
339                                 }
340                         }
341                         break;
342                 case ASPECT_CORRECT:
343                         SetCorrectAspectRatio(!CorrectAspectRatio());
344                         break;
345                 case SCREEN_SHOT:
346                         // save a screen shot
347                         if ( BBitmap* current = bitmap[i_buffer] )
348                         {
349 // the following line might be tempting, but does not work for some overlay bitmaps!!!
350 //                              BBitmap* temp = new BBitmap( current );
351 // so we clone the bitmap ourselves
352 // however, we need to take care of potentially different padding!
353 // memcpy() is slow when reading from grafix memory, but what the heck...
354                                 BBitmap* temp = new BBitmap( current->Bounds(), current->ColorSpace() );
355                                 if ( temp && temp->IsValid() )
356                                 {
357                                         int32 height = (int32)current->Bounds().Height();
358                                         uint8* dst = (uint8*)temp->Bits();
359                                         uint8* src = (uint8*)current->Bits();
360                                         int32 dstBpr = temp->BytesPerRow();
361                                         int32 srcBpr = current->BytesPerRow();
362                                         int32 validBytes = dstBpr > srcBpr ? srcBpr : dstBpr;
363                                         for ( int32 y = 0; y < height; y++ )
364                                         {
365                                                 memcpy( dst, src, validBytes );
366                                                 dst += dstBpr;
367                                                 src += srcBpr;
368                                         }
369                                         _SaveScreenShot( temp,
370                                                                          strdup( DEFAULT_SCREEN_SHOT_PATH ),
371                                                                          DEFAULT_SCREEN_SHOT_FORMAT );
372                                 }
373                                 else
374                                 {
375                                         delete temp;
376                                 }
377                         }
378                         break;
379                 default:
380                         BWindow::MessageReceived( p_message );
381                         break;
382         }
383 }
384
385 /*****************************************************************************
386  * VideoWindow::Zoom
387  *****************************************************************************/
388 void
389 VideoWindow::Zoom(BPoint origin, float width, float height )
390 {
391         ToggleFullScreen();
392 }
393
394 /*****************************************************************************
395  * VideoWindow::FrameMoved
396  *****************************************************************************/
397 void
398 VideoWindow::FrameMoved(BPoint origin) 
399 {
400         if (IsFullScreen())
401                 return ;
402         winSize = Frame();
403 }
404
405 /*****************************************************************************
406  * VideoWindow::FrameResized
407  *****************************************************************************/
408 void
409 VideoWindow::FrameResized( float width, float height )
410 {
411         int32 useWidth = CorrectAspectRatio() ? i_width : fTrueWidth;
412         int32 useHeight = CorrectAspectRatio() ? i_height : fTrueHeight;
413     float out_width, out_height;
414     float out_left, out_top;
415     float width_scale = width / useWidth;
416     float height_scale = height / useHeight;
417
418     if (width_scale <= height_scale)
419     {
420         out_width = (useWidth * width_scale);
421         out_height = (useHeight * width_scale);
422         out_left = 0; 
423         out_top = (height - out_height) / 2;
424     }
425     else   /* if the height is proportionally smaller */
426     {
427         out_width = (useWidth * height_scale);
428         out_height = (useHeight * height_scale);
429         out_top = 0;
430         out_left = (width - out_width) / 2;
431     }
432     view->MoveTo(out_left,out_top);
433     view->ResizeTo(out_width, out_height);
434
435         if (!IsFullScreen())
436         winSize = Frame();
437 }
438
439 /*****************************************************************************
440  * VideoWindow::ScreenChanged
441  *****************************************************************************/
442 void
443 VideoWindow::ScreenChanged(BRect frame, color_space format)
444 {
445         BScreen screen(this);
446         display_mode mode; 
447         screen.GetMode(&mode); 
448         float refresh = (mode.timing.pixel_clock * 1000)
449                                         / ((mode.timing.h_total) * (mode.timing.v_total)); 
450         SetSyncToRetrace(refresh < MIN_AUTO_VSYNC_REFRESH);
451 }
452
453 /*****************************************************************************
454  * VideoWindow::Activate
455  *****************************************************************************/
456 void
457 VideoWindow::WindowActivated(bool active)
458 {
459 }
460
461 /*****************************************************************************
462  * VideoWindow::drawBuffer
463  *****************************************************************************/
464 void
465 VideoWindow::drawBuffer(int bufferIndex)
466 {
467     i_buffer = bufferIndex;
468
469     // sync to the screen if required
470     if (IsSyncedToRetrace())
471     {
472         BScreen screen(this);
473         screen.WaitForRetrace(22000);
474     }
475     if (fInitStatus >= B_OK && LockLooper())
476     {
477        // switch the overlay bitmap
478        if (mode == OVERLAY)
479        {
480           rgb_color key;
481           view->SetViewOverlay(bitmap[i_buffer], 
482                             bitmap[i_buffer]->Bounds() ,
483                             view->Bounds(),
484                             &key, B_FOLLOW_ALL,
485                                                         B_OVERLAY_FILTER_HORIZONTAL|B_OVERLAY_FILTER_VERTICAL|
486                                     B_OVERLAY_TRANSFER_CHANNEL);
487                    view->SetViewColor(key);
488            }
489        else
490        {
491          // switch the bitmap
492          view->DrawBitmap(bitmap[i_buffer], view->Bounds() );
493        }
494        UnlockLooper();
495     }
496 }
497
498 /*****************************************************************************
499  * VideoWindow::SetInterfaceShowing
500  *****************************************************************************/
501 void
502 VideoWindow::ToggleInterfaceShowing()
503 {
504         SetInterfaceShowing(!fInterfaceShowing);
505 }
506
507 /*****************************************************************************
508  * VideoWindow::SetInterfaceShowing
509  *****************************************************************************/
510 void
511 VideoWindow::SetInterfaceShowing(bool showIt)
512 {
513         BWindow* window = get_interface_window();
514         if (window)
515         {
516                 if (showIt)
517                 {
518                         if (fCachedFeel != B_NORMAL_WINDOW_FEEL)
519                                 SetFeel(B_NORMAL_WINDOW_FEEL);
520                         window->Activate(true);
521                         SendBehind(window);
522                 }
523                 else
524                 {
525                         SetFeel(fCachedFeel);
526                         Activate(true);
527                         window->SendBehind(this);
528                 }
529                 fInterfaceShowing = showIt;
530         }
531 }
532
533 /*****************************************************************************
534  * VideoWindow::SetCorrectAspectRatio
535  *****************************************************************************/
536 void
537 VideoWindow::SetCorrectAspectRatio(bool doIt)
538 {
539         if (CorrectAspectRatio() != doIt)
540         {
541                 if (doIt)
542                         fSettings->AddFlags(VideoSettings::FLAG_CORRECT_RATIO);
543                 else
544                         fSettings->ClearFlags(VideoSettings::FLAG_CORRECT_RATIO);
545                 FrameResized(Bounds().Width(), Bounds().Height());
546         }
547 }
548
549 /*****************************************************************************
550  * VideoWindow::CorrectAspectRatio
551  *****************************************************************************/
552 bool
553 VideoWindow::CorrectAspectRatio() const
554 {
555         return fSettings->HasFlags(VideoSettings::FLAG_CORRECT_RATIO);
556 }
557
558 /*****************************************************************************
559  * VideoWindow::ToggleFullScreen
560  *****************************************************************************/
561 void
562 VideoWindow::ToggleFullScreen()
563 {
564         SetFullScreen(!IsFullScreen());
565 }
566
567 /*****************************************************************************
568  * VideoWindow::SetFullScreen
569  *****************************************************************************/
570 void
571 VideoWindow::SetFullScreen(bool doIt)
572 {
573         if (doIt)
574         {
575                 BScreen screen(this);
576                 BRect rect = screen.Frame();
577                 Activate();
578                 MoveTo(0.0, 0.0);
579                 ResizeTo(rect.IntegerWidth(), rect.IntegerHeight());
580                 be_app->ObscureCursor();
581                 fInterfaceShowing = false;
582                 fSettings->AddFlags(VideoSettings::FLAG_FULL_SCREEN);
583         }
584         else
585         {
586                 MoveTo(winSize.left, winSize.top);
587                 ResizeTo(winSize.IntegerWidth(), winSize.IntegerHeight());
588                 be_app->ShowCursor();
589                 fInterfaceShowing = true;
590                 fSettings->ClearFlags(VideoSettings::FLAG_FULL_SCREEN);
591         }
592 }
593
594 /*****************************************************************************
595  * VideoWindow::IsFullScreen
596  *****************************************************************************/
597 bool
598 VideoWindow::IsFullScreen() const
599 {
600         return fSettings->HasFlags(VideoSettings::FLAG_FULL_SCREEN);
601 }
602
603 /*****************************************************************************
604  * VideoWindow::SetSyncToRetrace
605  *****************************************************************************/
606 void
607 VideoWindow::SetSyncToRetrace(bool doIt)
608 {
609         if (doIt)
610                 fSettings->AddFlags(VideoSettings::FLAG_SYNC_RETRACE);
611         else
612                 fSettings->ClearFlags(VideoSettings::FLAG_SYNC_RETRACE);
613 }
614
615 /*****************************************************************************
616  * VideoWindow::IsSyncedToRetrace
617  *****************************************************************************/
618 bool
619 VideoWindow::IsSyncedToRetrace() const
620 {
621         return fSettings->HasFlags(VideoSettings::FLAG_SYNC_RETRACE);
622 }
623
624
625 /*****************************************************************************
626  * VideoWindow::_AllocateBuffers
627  *****************************************************************************/
628 status_t
629 VideoWindow::_AllocateBuffers(int width, int height, int* mode)
630 {
631         // clear any old buffers
632         _FreeBuffers();
633         // set default mode
634         *mode = BITMAP;
635
636         BRect bitmapFrame( 0, 0, width, height );
637         // read from config, if we are supposed to use overlay at all
638     int noOverlay = !config_GetInt( p_vout, "overlay" );
639         // test for overlay capability
640     for (int i = 0; i < COLOR_COUNT; i++)
641     {
642         if (noOverlay) break;
643         bitmap[0] = new BBitmap ( bitmapFrame, 
644                                   B_BITMAP_WILL_OVERLAY |
645                                   B_BITMAP_RESERVE_OVERLAY_CHANNEL,
646                                   colspace[i].colspace);
647
648         if(bitmap[0] && bitmap[0]->InitCheck() == B_OK) 
649         {
650             colspace_index = i;
651
652             bitmap[1] = new BBitmap( bitmapFrame, B_BITMAP_WILL_OVERLAY,
653                                      colspace[colspace_index].colspace);
654             bitmap[2] = new BBitmap( bitmapFrame, B_BITMAP_WILL_OVERLAY,
655                                      colspace[colspace_index].colspace);
656             if ( (bitmap[2] && bitmap[2]->InitCheck() == B_OK) )
657             {
658                *mode = OVERLAY;
659                rgb_color key;
660                view->SetViewOverlay(bitmap[0], 
661                                     bitmap[0]->Bounds() ,
662                                     view->Bounds(),
663                                     &key, B_FOLLOW_ALL,
664                                             B_OVERLAY_FILTER_HORIZONTAL|B_OVERLAY_FILTER_VERTICAL);
665                        view->SetViewColor(key);
666                SetTitle(VOUT_TITLE " (Overlay)");
667                break;
668             }
669             else
670             {
671                _FreeBuffers();
672                *mode = BITMAP; // might want to try again with normal bitmaps
673             }
674         }
675         else
676             delete bitmap[0];
677         }
678
679     if (*mode == BITMAP)
680         {
681         // fallback to RGB
682         colspace_index = DEFAULT_COL;   // B_RGB32
683         SetTitle( VOUT_TITLE " (Bitmap)" );
684         bitmap[0] = new BBitmap( bitmapFrame, colspace[colspace_index].colspace );
685         bitmap[1] = new BBitmap( bitmapFrame, colspace[colspace_index].colspace );
686         bitmap[2] = new BBitmap( bitmapFrame, colspace[colspace_index].colspace );
687     }
688     // see if everything went well
689     status_t status = B_ERROR;
690     for (int32 i = 0; i < 3; i++)
691     {
692         if (bitmap[i])
693                 status = bitmap[i]->InitCheck();
694                 if (status < B_OK)
695                         break;
696     }
697     if (status >= B_OK)
698     {
699             // clear bitmaps to black
700             for (int32 i = 0; i < 3; i++)
701                 _BlankBitmap(bitmap[i]);
702     }
703     return status;
704 }
705
706 /*****************************************************************************
707  * VideoWindow::_FreeBuffers
708  *****************************************************************************/
709 void
710 VideoWindow::_FreeBuffers()
711 {
712         delete bitmap[0];
713         bitmap[0] = NULL;
714         delete bitmap[1];
715         bitmap[1] = NULL;
716         delete bitmap[2];
717         bitmap[2] = NULL;
718         fInitStatus = B_ERROR;
719 }
720
721 /*****************************************************************************
722  * VideoWindow::_BlankBitmap
723  *****************************************************************************/
724 void
725 VideoWindow::_BlankBitmap(BBitmap* bitmap) const
726 {
727         // no error checking (we do that earlier on and since it's a private function...
728
729         // YCbCr: 
730         // Loss/Saturation points are Y 16-235 (absoulte); Cb/Cr 16-240 (center 128)
731
732         // YUV: 
733         // Extrema points are Y 0 - 207 (absolute) U -91 - 91 (offset 128) V -127 - 127 (offset 128)
734
735         // we only handle weird colorspaces with special care
736         switch (bitmap->ColorSpace()) {
737                 case B_YCbCr422: {
738                         // Y0[7:0]  Cb0[7:0]  Y1[7:0]  Cr0[7:0]  Y2[7:0]  Cb2[7:0]  Y3[7:0]  Cr2[7:0]
739                         int32 height = bitmap->Bounds().IntegerHeight() + 1;
740                         uint8* bits = (uint8*)bitmap->Bits();
741                         int32 bpr = bitmap->BytesPerRow();
742                         for (int32 y = 0; y < height; y++) {
743                                 // handle 2 bytes at a time
744                                 for (int32 i = 0; i < bpr; i += 2) {
745                                         // offset into line
746                                         bits[i] = 16;
747                                         bits[i + 1] = 128;
748                                 }
749                                 // next line
750                                 bits += bpr;
751                         }
752                         break;
753                 }
754                 case B_YCbCr420: {
755 // TODO: untested!!
756                         // Non-interlaced only, Cb0  Y0  Y1  Cb2 Y2  Y3  on even scan lines ...
757                         // Cr0  Y0  Y1  Cr2 Y2  Y3  on odd scan lines
758                         int32 height = bitmap->Bounds().IntegerHeight() + 1;
759                         uint8* bits = (uint8*)bitmap->Bits();
760                         int32 bpr = bitmap->BytesPerRow();
761                         for (int32 y = 0; y < height; y += 1) {
762                                 // handle 3 bytes at a time
763                                 for (int32 i = 0; i < bpr; i += 3) {
764                                         // offset into line
765                                         bits[i] = 128;
766                                         bits[i + 1] = 16;
767                                         bits[i + 2] = 16;
768                                 }
769                                 // next line
770                                 bits += bpr;
771                         }
772                         break;
773                 }
774                 case B_YUV422: {
775 // TODO: untested!!
776                         // U0[7:0]  Y0[7:0]   V0[7:0]  Y1[7:0]  U2[7:0]  Y2[7:0]   V2[7:0]  Y3[7:0]
777                         int32 height = bitmap->Bounds().IntegerHeight() + 1;
778                         uint8* bits = (uint8*)bitmap->Bits();
779                         int32 bpr = bitmap->BytesPerRow();
780                         for (int32 y = 0; y < height; y += 1) {
781                                 // handle 2 bytes at a time
782                                 for (int32 i = 0; i < bpr; i += 2) {
783                                         // offset into line
784                                         bits[i] = 128;
785                                         bits[i + 1] = 0;
786                                 }
787                                 // next line
788                                 bits += bpr;
789                         }
790                         break;
791                 }
792                 default:
793                         memset(bitmap->Bits(), 0, bitmap->BitsLength());
794                         break;
795         }
796 }
797
798 /*****************************************************************************
799  * VideoWindow::_SetVideoSize
800  *****************************************************************************/
801 void
802 VideoWindow::_SetVideoSize(uint32 mode)
803 {
804         // let size depend on aspect correction
805         int32 width = CorrectAspectRatio() ? i_width : fTrueWidth;
806         int32 height = CorrectAspectRatio() ? i_height : fTrueHeight;
807         switch (mode)
808         {
809                 case RESIZE_50:
810                         width /= 2;
811                         height /= 2;
812                         break;
813                 case RESIZE_200:
814                         width *= 2;
815                         height *= 2;
816                         break;
817                 case RESIZE_100:
818                 default:
819                 break;
820         }
821         fSettings->ClearFlags(VideoSettings::FLAG_FULL_SCREEN);
822         ResizeTo(width, height);
823 }
824
825 /*****************************************************************************
826  * VideoWindow::_SetToSettings
827  *****************************************************************************/
828 void
829 VideoWindow::_SetToSettings()
830 {
831         // adjust dimensions
832         uint32 mode = RESIZE_100;
833         switch (fSettings->VideoSize())
834         {
835                 case VideoSettings::SIZE_50:
836                         mode = RESIZE_50;
837                         break;
838                 case VideoSettings::SIZE_200:
839                         mode = RESIZE_200;
840                         break;
841                 case VideoSettings::SIZE_100:
842                 case VideoSettings::SIZE_OTHER:
843                 default:
844                         break;
845         }
846         bool fullscreen = IsFullScreen();       // remember settings
847         _SetVideoSize(mode);                            // because this will reset settings
848         // the fullscreen status is reflected in the settings,
849         // but not yet in the windows state
850         if (fullscreen)
851                 SetFullScreen(true);
852         if (fSettings->HasFlags(VideoSettings::FLAG_ON_TOP_ALL))
853                 fCachedFeel = B_FLOATING_ALL_WINDOW_FEEL;
854         else
855                 fCachedFeel = B_NORMAL_WINDOW_FEEL;
856         SetFeel(fCachedFeel);
857 }
858
859 /*****************************************************************************
860  * VideoWindow::_SaveScreenShot
861  *****************************************************************************/
862 void
863 VideoWindow::_SaveScreenShot( BBitmap* bitmap, char* path,
864                                                   uint32 translatorID ) const
865 {
866         // make the info object from the parameters
867         screen_shot_info* info = new screen_shot_info;
868         info->bitmap = bitmap;
869         info->path = path;
870         info->translatorID = translatorID;
871         info->width = CorrectAspectRatio() ? i_width : fTrueWidth;
872         info->height = CorrectAspectRatio() ? i_height : fTrueHeight;
873         // spawn a new thread to take care of the actual saving to disk
874         thread_id thread = spawn_thread( _save_screen_shot,
875                                                                          "screen shot saver",
876                                                                          B_LOW_PRIORITY, (void*)info );
877         // start thread or do the job ourself if something went wrong
878         if ( thread < B_OK || resume_thread( thread ) < B_OK )
879                 _save_screen_shot( (void*)info );
880 }
881
882 /*****************************************************************************
883  * VideoWindow::_save_screen_shot
884  *****************************************************************************/
885 int32
886 VideoWindow::_save_screen_shot( void* cookie )
887 {
888         screen_shot_info* info = (screen_shot_info*)cookie;
889         if ( info && info->bitmap && info->bitmap->IsValid() && info->path )
890         {
891                 // try to be as quick as possible creating the file (the user might have
892                 // taken the next screen shot already!)
893                 // make sure we have a unique name for the screen shot
894                 BString path( info->path );
895                 BEntry entry( path.String() );
896                 int32 appendedNumber = 0;
897                 if ( entry.Exists() && !entry.IsSymLink() )
898                 {
899                         // we would clobber an existing entry
900                         bool foundUniqueName = false;
901                         appendedNumber = 1;
902                         while ( !foundUniqueName ) {
903                                 BString newName( info->path );
904                                 newName << " " << appendedNumber;
905                                 BEntry possiblyClobberedEntry( newName.String() );
906                                 if ( possiblyClobberedEntry.Exists()
907                                         && !possiblyClobberedEntry.IsSymLink() )
908                                         appendedNumber++;
909                                 else
910                                         foundUniqueName = true;
911                         }
912                 }
913                 if ( appendedNumber > 0 )
914                         path << " " << appendedNumber;
915                 // there is still a slight chance to clobber an existing
916                 // file (if it was created in the "meantime"), but we take it...
917                 BFile outFile( path.String(),
918                                            B_CREATE_FILE | B_WRITE_ONLY | B_ERASE_FILE );
919
920                 // make colorspace converted copy of bitmap
921                 BBitmap* converted = new BBitmap( BRect( 0.0, 0.0, info->width, info->height ),
922                                                                                   B_RGB32 );
923 //              if ( converted->IsValid() )
924 //                      memset( converted->Bits(), 0, converted->BitsLength() );
925                 status_t status = convert_bitmap( info->bitmap, converted );
926                 if ( status == B_OK )
927                 {
928                         BTranslatorRoster* roster = BTranslatorRoster::Default();
929                         uint32 imageFormat = 0;
930                         translator_id translator = 0;
931                         bool found = false;
932
933                         // find suitable translator
934                         translator_id* ids = NULL;
935                         int32 count = 0;
936                 
937                         status = roster->GetAllTranslators( &ids, &count );
938                         if ( status >= B_OK )
939                         {
940                                 for ( int tix = 0; tix < count; tix++ )
941                                 { 
942                                         const translation_format *formats = NULL; 
943                                         int32 num_formats = 0; 
944                                         bool ok = false; 
945                                         status = roster->GetInputFormats( ids[tix],
946                                                                                                           &formats, &num_formats );
947                                         if (status >= B_OK)
948                                         {
949                                                 for ( int iix = 0; iix < num_formats; iix++ )
950                                                 { 
951                                                         if ( formats[iix].type == B_TRANSLATOR_BITMAP )
952                                                         { 
953                                                                 ok = true; 
954                                                                 break; 
955                                                         }
956                                                 }
957                                         }
958                                         if ( !ok )
959                                                 continue; 
960                                         status = roster->GetOutputFormats( ids[tix],
961                                                                                                            &formats, &num_formats); 
962                                         if ( status >= B_OK )
963                                         {
964                                                 for ( int32 oix = 0; oix < num_formats; oix++ )
965                                                 {
966                                                         if ( formats[oix].type != B_TRANSLATOR_BITMAP )
967                                                         {
968                                                                 if ( formats[oix].type == info->translatorID )
969                                                                 {
970                                                                         found = true;
971                                                                         imageFormat = formats[oix].type;
972                                                                         translator = ids[tix];
973                                                                         break;
974                                                                 }
975                                                         }
976                                                 }
977                                         }
978                                 }
979                         }
980                         delete[] ids;
981                         if ( found )
982                         {
983                                 // make bitmap stream
984                                 BBitmapStream outStream( converted );
985
986                                 status = outFile.InitCheck();
987                                 if (status == B_OK) {
988                                         status = roster->Translate( &outStream, NULL, NULL,
989                                                                                                 &outFile, imageFormat );
990                                         if ( status == B_OK )
991                                         {
992                                                 BNodeInfo nodeInfo( &outFile );
993                                                 if ( nodeInfo.InitCheck() == B_OK )
994                                                 {
995                                                         translation_format* formats; 
996                                                         int32 count;
997                                                         status = roster->GetOutputFormats( translator,
998                                                                                                                            (const translation_format **) &formats,
999                                                                                                                            &count);
1000                                                         if ( status >= B_OK )
1001                                                         {
1002                                                                 const char * mime = NULL; 
1003                                                                 for ( int ix = 0; ix < count; ix++ ) {
1004                                                                         if ( formats[ix].type == imageFormat ) {
1005                                                                                 mime = formats[ix].MIME;
1006                                                                                 break;
1007                                                                         }
1008                                                                 } 
1009                                                                 if ( mime )
1010                                                                         nodeInfo.SetType( mime );
1011                                                         }
1012                                                 }
1013                                         }
1014                                 }
1015                                 outStream.DetachBitmap( &converted );
1016                                 outFile.Unset();
1017                         }
1018                 }
1019                 delete converted;
1020         }
1021         if ( info )
1022         {
1023                 delete info->bitmap;
1024                 delete[] info->path;
1025         }
1026         delete info;
1027         return B_OK;
1028 }
1029
1030
1031 /*****************************************************************************
1032  * VLCView::VLCView
1033  *****************************************************************************/
1034 VLCView::VLCView(BRect bounds, vout_thread_t *p_vout_instance )
1035         : BView(bounds, "video view", B_FOLLOW_NONE, B_WILL_DRAW | B_PULSE_NEEDED),
1036           fLastMouseMovedTime(system_time()),
1037           fCursorHidden(false),
1038           fCursorInside(false),
1039           fIgnoreDoubleClick(false)
1040 {
1041     p_vout = p_vout_instance;
1042     SetViewColor(B_TRANSPARENT_32_BIT);
1043 }
1044
1045 /*****************************************************************************
1046  * VLCView::~VLCView
1047  *****************************************************************************/
1048 VLCView::~VLCView()
1049 {
1050 }
1051
1052 /*****************************************************************************
1053  * VLCVIew::AttachedToWindow
1054  *****************************************************************************/
1055 void
1056 VLCView::AttachedToWindow()
1057 {
1058         // in order to get keyboard events
1059         MakeFocus(true);
1060         // periodically check if we want to hide the pointer
1061         Window()->SetPulseRate(1000000);
1062 }
1063
1064 /*****************************************************************************
1065  * VLCVIew::MouseDown
1066  *****************************************************************************/
1067 void
1068 VLCView::MouseDown(BPoint where)
1069 {
1070         VideoWindow* videoWindow = dynamic_cast<VideoWindow*>(Window());
1071         BMessage* msg = Window()->CurrentMessage();
1072         int32 clicks;
1073         uint32 buttons;
1074         msg->FindInt32("clicks", &clicks);
1075         msg->FindInt32("buttons", (int32*)&buttons);
1076
1077         if (videoWindow)
1078         {
1079                 if (buttons & B_PRIMARY_MOUSE_BUTTON)
1080                 {
1081                         if (clicks == 2 && !fIgnoreDoubleClick)
1082                                 Window()->Zoom();
1083                         else
1084                                 videoWindow->ToggleInterfaceShowing();
1085                         fIgnoreDoubleClick = false;
1086                 }
1087             else
1088             {
1089                         if (buttons & B_SECONDARY_MOUSE_BUTTON) 
1090                         {
1091                                 // clicks will be 2 next time (if interval short enough)
1092                                 // even if the first click and the second
1093                                 // have not been made with the same mouse button
1094                                 fIgnoreDoubleClick = true;
1095                                 // launch popup menu
1096                                 BPopUpMenu *menu = new BPopUpMenu("context menu");
1097                                 menu->SetRadioMode(false);
1098                                 // Resize to 50%
1099                                 BMenuItem *halfItem = new BMenuItem("50%", new BMessage(RESIZE_50));
1100                                 menu->AddItem(halfItem);
1101                                 // Resize to 100%
1102                                 BMenuItem *origItem = new BMenuItem("100%", new BMessage(RESIZE_100));
1103                                 menu->AddItem(origItem);
1104                                 // Resize to 200%
1105                                 BMenuItem *doubleItem = new BMenuItem("200%", new BMessage(RESIZE_200));
1106                                 menu->AddItem(doubleItem);
1107                                 // Toggle FullScreen
1108                                 BMenuItem *zoomItem = new BMenuItem("Fullscreen", new BMessage(TOGGLE_FULL_SCREEN));
1109                                 zoomItem->SetMarked(videoWindow->IsFullScreen());
1110                                 menu->AddItem(zoomItem);
1111         
1112                                 menu->AddSeparatorItem();
1113         
1114                                 // Toggle vSync
1115                                 BMenuItem *vsyncItem = new BMenuItem("Vertical Sync", new BMessage(VERT_SYNC));
1116                                 vsyncItem->SetMarked(videoWindow->IsSyncedToRetrace());
1117                                 menu->AddItem(vsyncItem);
1118                                 // Correct Aspect Ratio
1119                                 BMenuItem *aspectItem = new BMenuItem("Correct Aspect Ratio", new BMessage(ASPECT_CORRECT));
1120                                 aspectItem->SetMarked(videoWindow->CorrectAspectRatio());
1121                                 menu->AddItem(aspectItem);
1122         
1123                                 menu->AddSeparatorItem();
1124         
1125                                 // Windwo Feel Items
1126 /*                              BMessage *winNormFeel = new BMessage(WINDOW_FEEL);
1127                                 winNormFeel->AddInt32("WinFeel", (int32)B_NORMAL_WINDOW_FEEL);
1128                                 BMenuItem *normWindItem = new BMenuItem("Normal Window", winNormFeel);
1129                                 normWindItem->SetMarked(videoWindow->Feel() == B_NORMAL_WINDOW_FEEL);
1130                                 menu->AddItem(normWindItem);
1131                                 
1132                                 BMessage *winFloatFeel = new BMessage(WINDOW_FEEL);
1133                                 winFloatFeel->AddInt32("WinFeel", (int32)B_FLOATING_APP_WINDOW_FEEL);
1134                                 BMenuItem *onTopWindItem = new BMenuItem("App Top", winFloatFeel);
1135                                 onTopWindItem->SetMarked(videoWindow->Feel() == B_FLOATING_APP_WINDOW_FEEL);
1136                                 menu->AddItem(onTopWindItem);
1137                                 
1138                                 BMessage *winAllFeel = new BMessage(WINDOW_FEEL);
1139                                 winAllFeel->AddInt32("WinFeel", (int32)B_FLOATING_ALL_WINDOW_FEEL);
1140                                 BMenuItem *allSpacesWindItem = new BMenuItem("On Top All Workspaces", winAllFeel);
1141                                 allSpacesWindItem->SetMarked(videoWindow->Feel() == B_FLOATING_ALL_WINDOW_FEEL);
1142                                 menu->AddItem(allSpacesWindItem);*/
1143
1144                                 BMessage *windowFeelMsg = new BMessage( WINDOW_FEEL );
1145                                 bool onTop = videoWindow->Feel() == B_FLOATING_ALL_WINDOW_FEEL;
1146                                 window_feel feel = onTop ? B_NORMAL_WINDOW_FEEL : B_FLOATING_ALL_WINDOW_FEEL;
1147                                 windowFeelMsg->AddInt32( "WinFeel", (int32)feel );
1148                                 BMenuItem *windowFeelItem = new BMenuItem( "Stay On Top", windowFeelMsg );
1149                                 windowFeelItem->SetMarked( onTop );
1150                                 menu->AddItem( windowFeelItem );
1151
1152                                 menu->AddSeparatorItem();
1153
1154                                 BMenuItem* screenShotItem = new BMenuItem( "Take Screen Shot",
1155                                                                                                                    new BMessage( SCREEN_SHOT ) );
1156                                 menu->AddItem( screenShotItem );
1157
1158                                 menu->SetTargetForItems( this );
1159                                 ConvertToScreen( &where );
1160                                 menu->Go( where, true, false, true );
1161                 }
1162                 }
1163         }
1164         fLastMouseMovedTime = system_time();
1165         fCursorHidden = false;
1166 }
1167
1168 /*****************************************************************************
1169  * VLCVIew::MouseUp
1170  *****************************************************************************/
1171 void
1172 VLCView::MouseUp( BPoint where )
1173 {
1174     vlc_value_t val;
1175     val.b_bool = VLC_TRUE;
1176     var_Set( p_vout, "mouse-clicked", val );
1177 }
1178
1179 /*****************************************************************************
1180  * VLCVIew::MouseMoved
1181  *****************************************************************************/
1182 void
1183 VLCView::MouseMoved(BPoint point, uint32 transit, const BMessage* dragMessage)
1184 {
1185         fLastMouseMovedTime = system_time();
1186         fCursorHidden = false;
1187         fCursorInside = (transit == B_INSIDE_VIEW || transit == B_ENTERED_VIEW);
1188         /* DVD navigation */
1189         unsigned int i_width, i_height, i_x, i_y;
1190     vout_PlacePicture( p_vout, (unsigned int)Bounds().Width(),
1191                        (unsigned int)Bounds().Height(),
1192                        &i_x, &i_y, &i_width, &i_height );
1193         vlc_value_t val;
1194         val.i_int = ( (int)point.x - i_x ) * p_vout->render.i_width / i_width;
1195         var_Set( p_vout, "mouse-x", val );
1196         val.i_int = ( (int)point.y - i_y ) * p_vout->render.i_height / i_height;
1197         var_Set( p_vout, "mouse-y", val );
1198         val.b_bool = VLC_TRUE;
1199     var_Set( p_vout, "mouse-moved", val );
1200 }
1201
1202 /*****************************************************************************
1203  * VLCVIew::Pulse
1204  *****************************************************************************/
1205 void 
1206 VLCView::Pulse()
1207 {
1208         // We are getting the pulse messages no matter if the mouse is over
1209         // this view. If we are in full screen mode, we want to hide the cursor
1210         // even if it is not.
1211         if (!fCursorHidden)
1212         {
1213                 if (fCursorInside
1214                         && system_time() - fLastMouseMovedTime > MOUSE_IDLE_TIMEOUT)
1215                 {
1216                         be_app->ObscureCursor();
1217                         fCursorHidden = true;
1218                         VideoWindow *videoWindow = dynamic_cast<VideoWindow*>(Window());
1219                         // hide the interface window as well if full screen
1220                         if (videoWindow && videoWindow->IsFullScreen())
1221                                 videoWindow->SetInterfaceShowing(false);
1222                 }
1223         }
1224 }
1225
1226 /*****************************************************************************
1227  * VLCVIew::KeyDown
1228  *****************************************************************************/
1229 void
1230 VLCView::KeyDown(const char *bytes, int32 numBytes)
1231 {
1232     VideoWindow *videoWindow = dynamic_cast<VideoWindow*>(Window());
1233     BWindow* interfaceWindow = get_interface_window();
1234         if (videoWindow && numBytes > 0) {
1235                 uint32 mods = modifiers();
1236                 switch (*bytes) {
1237                         case B_TAB:
1238                                 // toggle window and full screen mode
1239                                 // not passing on the tab key to the default KeyDown()
1240                                 // implementation also avoids loosing the keyboard focus
1241                                 videoWindow->PostMessage(TOGGLE_FULL_SCREEN);
1242                                 break;
1243                         case B_ESCAPE:
1244                                 // go back to window mode
1245                                 if (videoWindow->IsFullScreen())
1246                                         videoWindow->PostMessage(TOGGLE_FULL_SCREEN);
1247                                 break;
1248                         case B_SPACE:
1249                                 // toggle playback
1250                                 if (interfaceWindow)
1251                                         interfaceWindow->PostMessage(PAUSE_PLAYBACK);
1252                                 break;
1253                         case B_RIGHT_ARROW:
1254                                 if (interfaceWindow)
1255                                 {
1256                                         if (mods & B_SHIFT_KEY)
1257                                                 // next title
1258                                                 interfaceWindow->PostMessage(NEXT_TITLE);
1259                                         else
1260                                                 // next chapter
1261                                                 interfaceWindow->PostMessage(NEXT_CHAPTER);
1262                                 }
1263                                 break;
1264                         case B_LEFT_ARROW:
1265                                 if (interfaceWindow)
1266                                 {
1267                                         if (mods & B_SHIFT_KEY)
1268                                                 // previous title
1269                                                 interfaceWindow->PostMessage(PREV_TITLE);
1270                                         else
1271                                                 // previous chapter
1272                                                 interfaceWindow->PostMessage(PREV_CHAPTER);
1273                                 }
1274                                 break;
1275                         case B_UP_ARROW:
1276                                 // previous file in playlist
1277                                 interfaceWindow->PostMessage(PREV_FILE);
1278                                 break;
1279                         case B_DOWN_ARROW:
1280                                 // next file in playlist
1281                                 interfaceWindow->PostMessage(NEXT_FILE);
1282                                 break;
1283                         case B_PRINT_KEY:
1284                         case 's':
1285                         case 'S':
1286                                 videoWindow->PostMessage( SCREEN_SHOT );
1287                                 break;
1288                         default:
1289                                 BView::KeyDown(bytes, numBytes);
1290                                 break;
1291                 }
1292         }
1293 }
1294
1295 /*****************************************************************************
1296  * VLCVIew::Draw
1297  *****************************************************************************/
1298 void
1299 VLCView::Draw(BRect updateRect) 
1300 {
1301         VideoWindow* window = dynamic_cast<VideoWindow*>( Window() );
1302         if ( window && window->mode == BITMAP )
1303                 FillRect( updateRect );
1304 }
1305
1306 /*****************************************************************************
1307  * Local prototypes
1308  *****************************************************************************/
1309 static int  Init       ( vout_thread_t * );
1310 static void End        ( vout_thread_t * );
1311 // static int  Manage     ( vout_thread_t * );
1312 static void Display    ( vout_thread_t *, picture_t * );
1313
1314 static int  BeosOpenDisplay ( vout_thread_t *p_vout );
1315 static void BeosCloseDisplay( vout_thread_t *p_vout );
1316
1317 /*****************************************************************************
1318  * OpenVideo: allocates BeOS video thread output method
1319  *****************************************************************************
1320  * This function allocates and initializes a BeOS vout method.
1321  *****************************************************************************/
1322 int E_(OpenVideo) ( vlc_object_t *p_this )
1323 {
1324     vout_thread_t * p_vout = (vout_thread_t *)p_this;
1325
1326     /* Allocate structure */
1327     p_vout->p_sys = (vout_sys_t*) malloc( sizeof( vout_sys_t ) );
1328     if( p_vout->p_sys == NULL )
1329     {
1330         msg_Err( p_vout, "out of memory" );
1331         return( 1 );
1332     }
1333     p_vout->p_sys->i_width = p_vout->render.i_width;
1334     p_vout->p_sys->i_height = p_vout->render.i_height;
1335     p_vout->p_sys->source_chroma = p_vout->render.i_chroma;
1336
1337     p_vout->pf_init = Init;
1338     p_vout->pf_end = End;
1339     p_vout->pf_manage = NULL;
1340     p_vout->pf_render = NULL;
1341     p_vout->pf_display = Display;
1342
1343     return( 0 );
1344 }
1345
1346 /*****************************************************************************
1347  * Init: initialize BeOS video thread output method
1348  *****************************************************************************/
1349 int Init( vout_thread_t *p_vout )
1350 {
1351     int i_index;
1352     picture_t *p_pic;
1353
1354     I_OUTPUTPICTURES = 0;
1355
1356     /* Open and initialize device */
1357     if( BeosOpenDisplay( p_vout ) )
1358     {
1359         msg_Err(p_vout, "vout error: can't open display");
1360         return 0;
1361     }
1362     p_vout->output.i_width  = p_vout->render.i_width;
1363     p_vout->output.i_height = p_vout->render.i_height;
1364
1365     /* Assume we have square pixels */
1366     p_vout->output.i_aspect = p_vout->p_sys->i_width
1367                                * VOUT_ASPECT_FACTOR / p_vout->p_sys->i_height;
1368     p_vout->output.i_chroma = colspace[p_vout->p_sys->p_window->colspace_index].chroma;
1369     p_vout->p_sys->i_index = 0;
1370
1371     p_vout->b_direct = 1;
1372
1373     p_vout->output.i_rmask  = 0x00ff0000;
1374     p_vout->output.i_gmask  = 0x0000ff00;
1375     p_vout->output.i_bmask  = 0x000000ff;
1376
1377     for (int buffer_index = 0 ; buffer_index < 3; buffer_index++)
1378     {
1379        p_pic = NULL;
1380        /* Find an empty picture slot */
1381        for( i_index = 0 ; i_index < VOUT_MAX_PICTURES ; i_index++ )
1382        {
1383            p_pic = NULL;
1384            if( p_vout->p_picture[ i_index ].i_status == FREE_PICTURE )
1385            {
1386                p_pic = p_vout->p_picture + i_index;
1387                break;
1388            }
1389        }
1390
1391        if( p_pic == NULL )
1392        {
1393            return 0;
1394        }
1395        p_pic->p->p_pixels = (u8*)p_vout->p_sys->p_window->bitmap[buffer_index]->Bits();
1396        p_pic->p->i_lines = p_vout->p_sys->i_height;
1397
1398        p_pic->p->i_pixel_pitch = colspace[p_vout->p_sys->p_window->colspace_index].pixel_bytes;
1399        p_pic->i_planes = colspace[p_vout->p_sys->p_window->colspace_index].planes;
1400        p_pic->p->i_pitch = p_vout->p_sys->p_window->bitmap[buffer_index]->BytesPerRow(); 
1401        p_pic->p->i_visible_pitch = p_pic->p->i_pixel_pitch * ( p_vout->p_sys->p_window->bitmap[buffer_index]->Bounds().IntegerWidth() + 1 );
1402
1403        p_pic->i_status = DESTROYED_PICTURE;
1404        p_pic->i_type   = DIRECT_PICTURE;
1405  
1406        PP_OUTPUTPICTURE[ I_OUTPUTPICTURES ] = p_pic;
1407
1408        I_OUTPUTPICTURES++;
1409     }
1410
1411     return( 0 );
1412 }
1413
1414 /*****************************************************************************
1415  * End: terminate BeOS video thread output method
1416  *****************************************************************************/
1417 void End( vout_thread_t *p_vout )
1418 {
1419     BeosCloseDisplay( p_vout );
1420 }
1421
1422 /*****************************************************************************
1423  * CloseVideo: destroy BeOS video thread output method
1424  *****************************************************************************
1425  * Terminate an output method created by DummyCreateOutputMethod
1426  *****************************************************************************/
1427 void E_(CloseVideo) ( vlc_object_t *p_this )
1428 {
1429     vout_thread_t * p_vout = (vout_thread_t *)p_this;
1430
1431     free( p_vout->p_sys );
1432 }
1433
1434 /*****************************************************************************
1435  * Display: displays previously rendered output
1436  *****************************************************************************
1437  * This function send the currently rendered image to BeOS image, waits until
1438  * it is displayed and switch the two rendering buffers, preparing next frame.
1439  *****************************************************************************/
1440 void Display( vout_thread_t *p_vout, picture_t *p_pic )
1441 {
1442     VideoWindow * p_win = p_vout->p_sys->p_window;
1443
1444     /* draw buffer if required */    
1445     if (!p_win->teardownwindow)
1446     { 
1447        p_win->drawBuffer(p_vout->p_sys->i_index);
1448     }
1449     /* change buffer */
1450     p_vout->p_sys->i_index = ++p_vout->p_sys->i_index % 3;
1451     p_pic->p->p_pixels = (u8*)p_vout->p_sys->p_window->bitmap[p_vout->p_sys->i_index]->Bits();
1452 }
1453
1454 /* following functions are local */
1455
1456 /*****************************************************************************
1457  * BeosOpenDisplay: open and initialize BeOS device
1458  *****************************************************************************/
1459 static int BeosOpenDisplay( vout_thread_t *p_vout )
1460
1461
1462     p_vout->p_sys->p_window = new VideoWindow( p_vout->p_sys->i_width - 1,
1463                                                p_vout->p_sys->i_height - 1,
1464                                                BRect( 20, 50,
1465                                                       20 + p_vout->i_window_width - 1, 
1466                                                       50 + p_vout->i_window_height - 1 ),
1467                                                p_vout );
1468     if( p_vout->p_sys->p_window == NULL )
1469     {
1470         msg_Err( p_vout, "cannot allocate VideoWindow" );
1471         return( 1 );
1472     }
1473     else
1474     {
1475         p_vout->p_sys->p_window->Show();
1476     }
1477     
1478     return( 0 );
1479 }
1480
1481 /*****************************************************************************
1482  * BeosDisplay: close and reset BeOS device
1483  *****************************************************************************
1484  * Returns all resources allocated by BeosOpenDisplay and restore the original
1485  * state of the device.
1486  *****************************************************************************/
1487 static void BeosCloseDisplay( vout_thread_t *p_vout )
1488 {    
1489     VideoWindow * p_win = p_vout->p_sys->p_window;
1490     /* Destroy the video window */
1491     if( p_win != NULL && !p_win->teardownwindow)
1492     {
1493         p_win->Lock();
1494         p_win->teardownwindow = true;
1495         p_win->Hide();
1496         p_win->Quit();
1497     }
1498     p_win = NULL;
1499 }