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