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