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