A bit of headers cleanup
[vlc.git] / modules / video_output / qte / qte.cpp
1 /*****************************************************************************
2  * qte.cpp : QT Embedded plugin for vlc
3  *****************************************************************************
4  * Copyright (C) 1998-2003 the VideoLAN team
5  * $Id$
6  *
7  * Authors: Gerald Hansink <gerald.hansink@ordina.nl>
8  *          Jean-Paul Saman <jpsaman _at_ videolan _dot_ org>
9  *
10  * This program is free software; you can redistribute it and/or modify
11  * it under the terms of the GNU General Public License as published by
12  * the Free Software Foundation; either version 2 of the License, or
13  * (at your option) any later version.
14  *
15  * This program is distributed in the hope that it will be useful,
16  * but WITHOUT ANY WARRANTY; without even the implied warranty of
17  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
18  * GNU General Public License for more details.
19  *
20  * You should have received a copy of the GNU General Public License
21  * along with this program; if not, write to the Free Software
22  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
23  *****************************************************************************/
24
25 /*****************************************************************************
26  * Preamble
27  *****************************************************************************/
28
29 /*****************************************************************************
30  * notes:
31  * - written for ipaq, so hardcoded assumptions specific for ipaq...
32  * - runs full screen
33  * - no "mouse events" handling
34  * - etc.
35  *****************************************************************************/
36
37 extern "C"
38 {
39 #include <errno.h>                                                 /* ENOMEM */
40 #include <stdlib.h>                                                /* free() */
41 #include <string.h>                                                /* strerror() */
42
43 #include <vlc/vlc.h>
44 #include <vlc_interface.h>
45 #include <vlc_vout.h>
46
47 #ifdef HAVE_MACHINE_PARAM_H
48     /* BSD */
49 #   include <machine/param.h>
50 #   include <sys/types.h>                                  /* typedef ushort */
51 #   include <sys/ipc.h>
52 #endif
53
54 #ifndef WIN32
55 #   include <netinet/in.h>                            /* BSD: struct in_addr */
56 #endif
57
58 #ifdef HAVE_SYS_SHM_H
59 #   include <sys/shm.h>                                /* shmget(), shmctl() */
60 #endif
61 } /* extern "C" */
62
63 #include <qapplication.h>
64 #include <qpainter.h>
65
66 #ifdef Q_WS_QWS
67 #   define USE_DIRECT_PAINTER
68 #   include <qdirectpainter_qws.h>
69 #   include <qgfxraster_qws.h>
70 #endif
71
72 extern "C"
73 {
74 #include "qte.h"
75
76 /*****************************************************************************
77  * Module descriptor
78  *****************************************************************************/
79 #define DISPLAY_TEXT N_("QT Embedded display")
80 #define DISPLAY_LONGTEXT N_( \
81     "Qt Embedded hardware display to use. " \
82     "By default VLC will use the value of the DISPLAY environment variable.")
83
84 /*****************************************************************************
85  * Local prototypes
86  *****************************************************************************/
87 static int  Open      ( vlc_object_t * );
88 static void Close     ( vlc_object_t * );
89 static void Render    ( vout_thread_t *, picture_t * );
90 static void Display   ( vout_thread_t *, picture_t * );
91 static int  Manage    ( vout_thread_t * );
92 static int  Init      ( vout_thread_t * );
93 static void End       ( vout_thread_t * );
94
95 static int  OpenDisplay ( vout_thread_t * );
96 static void CloseDisplay( vout_thread_t * );
97
98 static int  NewPicture     ( vout_thread_t *, picture_t * );
99 static void FreePicture    ( vout_thread_t *, picture_t * );
100
101 static void ToggleFullScreen      ( vout_thread_t * );
102
103 static void RunQtThread( event_thread_t *p_event );
104 } /* extern "C" */
105
106 /*****************************************************************************
107 * Exported prototypes
108 *****************************************************************************/
109 extern "C"
110 {
111
112 vlc_module_begin();
113     set_category( CAT_VIDEO );
114     set_subcategory( SUBCAT_VIDEO_VOUT );
115 //    add_category_hint( N_("QT Embedded"), NULL );
116 //    add_string( "qte-display", "landscape", NULL, DISPLAY_TEXT, DISPLAY_LONGTEXT);
117     set_description( _("QT Embedded video output") );
118     set_capability( "video output", 70 );
119     add_shortcut( "qte" );
120     set_callbacks( Open, Close);
121 vlc_module_end();
122
123 } /* extern "C" */
124
125 /*****************************************************************************
126  * Seeking function TODO: put this in a generic location !
127  *****************************************************************************/
128 static inline void vout_Seek( off_t i_seek )
129 {
130 }
131
132 /*****************************************************************************
133  * Open: allocate video thread output method
134  *****************************************************************************/
135 static int Open( vlc_object_t *p_this )
136 {
137     vout_thread_t * p_vout = (vout_thread_t *)p_this;
138
139     /* Allocate structure */
140     p_vout->p_sys = (struct vout_sys_t*) malloc( sizeof( struct vout_sys_t ) );
141
142     if( p_vout->p_sys == NULL )
143     {
144         msg_Err( p_vout, "out of memory" );
145         return( 1 );
146     }
147
148     p_vout->pf_init    = Init;
149     p_vout->pf_end     = End;
150     p_vout->pf_manage  = Manage;
151     p_vout->pf_render  = NULL; //Render;
152     p_vout->pf_display = Display;
153
154 #ifdef NEED_QTE_MAIN
155     p_vout->p_sys->p_qte_main =
156         module_Need( p_this, "gui-helper", "qte", VLC_TRUE );
157     if( p_vout->p_sys->p_qte_main == NULL )
158     {
159         free( p_vout->p_sys );
160         return VLC_ENOMOD;
161     }
162 #endif
163
164     if (OpenDisplay(p_vout))
165     {
166         msg_Err( p_vout, "Cannot set up qte video output" );
167         Close(p_this);
168         return( -1 );
169     }
170     return( 0 );
171 }
172
173 /*****************************************************************************
174  * CloseVideo: destroy Sys video thread output method
175  *****************************************************************************
176  * Terminate an output method created by Open
177  *****************************************************************************/
178 static void Close ( vlc_object_t *p_this )
179 {
180     vout_thread_t * p_vout = (vout_thread_t *)p_this;
181
182     msg_Dbg( p_vout, "close" );
183     if( p_vout->p_sys->p_event )
184     {
185         vlc_object_detach( p_vout->p_sys->p_event );
186
187         /* Kill RunQtThread */
188         p_vout->p_sys->p_event->b_die = VLC_TRUE;
189         CloseDisplay(p_vout);
190
191         vlc_thread_join( p_vout->p_sys->p_event );
192         vlc_object_destroy( p_vout->p_sys->p_event );
193     }
194
195 #ifdef NEED_QTE_MAIN
196     msg_Dbg( p_vout, "releasing gui-helper" );
197     module_Unneed( p_vout, p_vout->p_sys->p_qte_main );
198 #endif
199
200     if( p_vout->p_sys )
201     {
202         free( p_vout->p_sys );
203         p_vout->p_sys = NULL;
204     }
205 }
206
207 /*****************************************************************************
208  * Init: initialize video thread output method
209  *****************************************************************************
210  * This function create the buffers needed by the output thread. It is called
211  * at the beginning of the thread, but also each time the window is resized.
212  *****************************************************************************/
213 static int Init( vout_thread_t *p_vout )
214 {
215     int         i_index;
216     picture_t*  p_pic;
217     int         dd = QPixmap::defaultDepth();
218
219     I_OUTPUTPICTURES = 0;
220
221     p_vout->output.i_chroma = (dd == 16) ? VLC_FOURCC('R','V','1','6'): VLC_FOURCC('R','V','3','2');
222     p_vout->output.i_rmask  = 0xf800;
223     p_vout->output.i_gmask  = 0x07e0;
224     p_vout->output.i_bmask  = 0x001f;
225
226     /* All we have is an RGB image with square pixels */
227     p_vout->output.i_width  = p_vout->p_sys->i_width;
228     p_vout->output.i_height = p_vout->p_sys->i_height;
229     if( !p_vout->b_fullscreen )
230     {
231         p_vout->output.i_aspect = p_vout->output.i_width
232                                    * VOUT_ASPECT_FACTOR
233                                    / p_vout->output.i_height;
234     }
235     else
236     {
237         p_vout->output.i_aspect = p_vout->render.i_aspect;
238     }
239 #if 0
240     msg_Dbg( p_vout, "Init (h=%d,w=%d,aspect=%d)",p_vout->output.i_height,p_vout->output.i_width,p_vout->output.i_aspect );
241 #endif
242     /* Try to initialize MAX_DIRECTBUFFERS direct buffers */
243     while( I_OUTPUTPICTURES < QTE_MAX_DIRECTBUFFERS )
244     {
245         p_pic = NULL;
246
247         /* Find an empty picture slot */
248         for( i_index = 0 ; i_index < VOUT_MAX_PICTURES ; i_index++ )
249         {
250             if( p_vout->p_picture[ i_index ].i_status == FREE_PICTURE )
251             {
252                 p_pic = p_vout->p_picture + i_index;
253                 break;
254             }
255         }
256
257         /* Allocate the picture */
258         if( p_pic == NULL ||  NewPicture( p_vout, p_pic ) )
259         {
260             break;
261         }
262
263         p_pic->i_status = DESTROYED_PICTURE;
264         p_pic->i_type   = DIRECT_PICTURE;
265
266         PP_OUTPUTPICTURE[ I_OUTPUTPICTURES ] = p_pic;
267
268         I_OUTPUTPICTURES++;
269     }
270
271     return( 0 );
272 }
273
274
275 /*****************************************************************************
276  * Render: render previously calculated output
277  *****************************************************************************/
278 static void Render( vout_thread_t *p_vout, picture_t *p_pic )
279 {
280     ;
281 }
282
283 /*****************************************************************************
284  * Display: displays previously rendered output
285  *****************************************************************************
286  * This function sends the currently rendered image to screen.
287  *****************************************************************************/
288 static void Display( vout_thread_t *p_vout, picture_t *p_pic )
289 {
290     unsigned int x, y, w, h;
291
292     vout_PlacePicture( p_vout, p_vout->output.i_width, p_vout->output.i_height,
293                        &x, &y, &w, &h );
294 #if 0
295     msg_Dbg(p_vout, "+qte::Display( p_vout, i_width=%d, i_height=%d, x=%u, y=%u, w=%u, h=%u",
296         p_vout->output.i_width, p_vout->output.i_height, x, y, w, h );
297 #endif
298
299     if(p_vout->p_sys->p_VideoWidget)
300     {
301 // shameless borrowed from opie mediaplayer....
302 #ifndef USE_DIRECT_PAINTER
303         msg_Dbg(p_vout, "not using direct painter");
304         QPainter p(p_vout->p_sys->p_VideoWidget);
305
306         /* rotate frame */
307         int dd      = QPixmap::defaultDepth();
308         int bytes   = ( dd == 16 ) ? 2 : 4;
309         int rw = h, rh = w;
310
311         QImage rotatedFrame( rw, rh, bytes << 3 );
312
313         ushort* in  = (ushort*)p_pic->p_sys->pQImage->bits();
314         ushort* out = (ushort*)rotatedFrame.bits();
315
316         int spl = rotatedFrame.bytesPerLine() / bytes;
317         for (int x=0; x<h; x++)
318         {
319             if ( bytes == 2 )
320             {
321                 ushort* lout = out++ + (w - 1)*spl;
322                 for (int y=0; y<w; y++)
323                 {
324                     *lout=*in++;
325                     lout-=spl;
326                 }
327             }
328             else
329             {
330                 ulong* lout = ((ulong *)out)++ + (w - 1)*spl;
331                 for (int y=0; y<w; y++)
332                 {
333                     *lout=*((ulong*)in)++;
334                     lout-=spl;
335                 }
336             }
337         }
338
339         p.drawImage( x, y, rotatedFrame, 0, 0, rw, rh );
340 #else
341         QDirectPainter p(p_vout->p_sys->p_VideoWidget);
342         p.transformOrientation();
343         // just copy the image to the frame buffer...
344         memcpy(p.frameBuffer(), (p_pic->p_sys->pQImage->jumpTable())[0], h * p.lineStep());
345 #endif
346     }
347 }
348
349 /*****************************************************************************
350  * Manage: handle Qte events
351  *****************************************************************************
352  * This function should be called regularly by video output thread. It manages
353  * Qte events and allows window resizing. It returns a non null value on
354  * error.
355  *****************************************************************************/
356 static int Manage( vout_thread_t *p_vout )
357 {
358 //    msg_Dbg( p_vout, "Manage" );
359
360     /* Fullscreen change */
361     if( p_vout->i_changes & VOUT_FULLSCREEN_CHANGE )
362     {
363         p_vout->b_fullscreen = ! p_vout->b_fullscreen;
364
365 //        p_vout->p_sys->b_cursor_autohidden = 0;
366 //        SDL_ShowCursor( p_vout->p_sys->b_cursor &&
367 //                        ! p_vout->p_sys->b_cursor_autohidden );
368
369         p_vout->i_changes &= ~VOUT_FULLSCREEN_CHANGE;
370         p_vout->i_changes |= VOUT_SIZE_CHANGE;
371     }
372
373     /*
374      * Size change
375      */
376     if( p_vout->i_changes & VOUT_SIZE_CHANGE )
377     {
378         msg_Dbg( p_vout, "video display resized (%dx%d)",
379                  p_vout->p_sys->i_width, p_vout->p_sys->i_height );
380
381         CloseDisplay( p_vout );
382         OpenDisplay( p_vout );
383
384         /* We don't need to signal the vout thread about the size change if
385          * we can handle rescaling ourselves */
386         p_vout->i_changes &= ~VOUT_SIZE_CHANGE;
387     }
388
389     /* Pointer change */
390 //    if( ! p_vout->p_sys->b_cursor_autohidden &&
391 //        ( mdate() - p_vout->p_sys->i_lastmoved > 2000000 ) )
392 //    {
393 //        /* Hide the mouse automatically */
394 //        p_vout->p_sys->b_cursor_autohidden = 1;
395 //        SDL_ShowCursor( 0 );
396 //    }
397 //
398 //    if( p_vout->p_libvlc->b_die )
399 //        p_vout->p_sys->bRunning = FALSE;
400
401     return 0;
402 }
403
404 /*****************************************************************************
405  * End: terminate video thread output method
406  *****************************************************************************
407  * Destroy the buffers created by vout_Init. It is called at the end of
408  * the thread, but also each time the window is resized.
409  *****************************************************************************/
410 static void End( vout_thread_t *p_vout )
411 {
412     int i_index;
413
414     /* Free the direct buffers we allocated */
415     for( i_index = I_OUTPUTPICTURES ; i_index ; )
416     {
417         i_index--;
418         FreePicture( p_vout, PP_OUTPUTPICTURE[ i_index ] );
419     }
420 }
421
422
423 /*****************************************************************************
424  * NewPicture: allocate a picture
425  *****************************************************************************
426  * Returns 0 on success, -1 otherwise
427  *****************************************************************************/
428 static int NewPicture( vout_thread_t *p_vout, picture_t *p_pic )
429 {
430     int dd = QPixmap::defaultDepth();
431
432     p_pic->p_sys = (picture_sys_t*) malloc( sizeof( picture_sys_t ) );
433     if( p_pic->p_sys == NULL )
434     {
435         return -1;
436     }
437
438     /* Create the display */
439     p_pic->p_sys->pQImage = new QImage(p_vout->output.i_width,
440                                        p_vout->output.i_height, dd );
441
442     if(p_pic->p_sys->pQImage == NULL)
443     {
444         return -1;
445     }
446
447     switch( dd )
448     {
449         case 8:
450             p_pic->p->i_pixel_pitch = 1;
451             break;
452         case 15:
453         case 16:
454             p_pic->p->i_pixel_pitch = 2;
455             break;
456         case 24:
457         case 32:
458             p_pic->p->i_pixel_pitch = 4;
459             break;
460         default:
461             return( -1 );
462     }
463
464     p_pic->p->p_pixels = (p_pic->p_sys->pQImage->jumpTable())[0];
465     p_pic->p->i_pitch = p_pic->p_sys->pQImage->bytesPerLine();
466     p_pic->p->i_lines = p_vout->output.i_height;
467     p_pic->p->i_visible_lines = p_vout->output.i_height;
468     p_pic->p->i_visible_pitch =
469             p_pic->p->i_pixel_pitch * p_vout->output.i_width;
470
471     p_pic->i_planes = 1;
472
473     return 0;
474 }
475
476 /*****************************************************************************
477  * FreePicture: destroy a picture allocated with NewPicture
478  *****************************************************************************/
479 static void FreePicture( vout_thread_t *p_vout, picture_t *p_pic )
480 {
481     delete p_pic->p_sys->pQImage;
482 }
483
484 /*****************************************************************************
485  * ToggleFullScreen: Enable or disable full screen mode
486  *****************************************************************************
487  * This function will switch between fullscreen and window mode.
488  *
489  *****************************************************************************/
490 static void ToggleFullScreen ( vout_thread_t *p_vout )
491 {
492     if ( p_vout->b_fullscreen )
493        p_vout->p_sys->p_VideoWidget->showFullScreen();
494     else
495        p_vout->p_sys->p_VideoWidget->showNormal();
496
497     p_vout->b_fullscreen = !p_vout->b_fullscreen;
498 }
499
500 /*****************************************************************************
501  * OpenDisplay: create qte applicaton / window
502  *****************************************************************************
503  * Create a window according to video output given size, and set other
504  * properties according to the display properties.
505  *****************************************************************************/
506 static int OpenDisplay( vout_thread_t *p_vout )
507 {
508     /* for displaying the vout in a qt window we need the QtApplication */
509     p_vout->p_sys->p_QApplication = NULL;
510     p_vout->p_sys->p_VideoWidget = NULL;
511
512     p_vout->p_sys->p_event = (event_thread_t*) vlc_object_create( p_vout, sizeof(event_thread_t) );
513     p_vout->p_sys->p_event->p_vout = p_vout;
514
515     /* Initializations */
516 #if 1 /* FIXME: I need an event queue to handle video output size changes. */
517     p_vout->b_fullscreen = VLC_TRUE;
518 #endif
519
520     /* Set main window's size */
521     QWidget *desktop = p_vout->p_sys->p_QApplication->desktop();
522     p_vout->p_sys->i_width = p_vout->b_fullscreen ? desktop->height() :
523                                                     p_vout->i_window_width;
524     p_vout->p_sys->i_height = p_vout->b_fullscreen ? desktop->width() :
525                                                      p_vout->i_window_height;
526
527 #if 0 /* FIXME: I need an event queue to handle video output size changes. */
528     /* Update dimensions */
529     p_vout->i_changes |= VOUT_SIZE_CHANGE;
530     p_vout->i_window_width = p_vout->p_sys->i_width;
531     p_vout->i_window_height = p_vout->p_sys->i_height;
532 #endif
533
534     msg_Dbg( p_vout, "opening display (h=%d,w=%d)",p_vout->p_sys->i_height,p_vout->p_sys->i_width);
535
536     /* create thread to exec the qpe application */
537     if ( vlc_thread_create( p_vout->p_sys->p_event, "QT Embedded Thread",
538                             RunQtThread,
539                             VLC_THREAD_PRIORITY_OUTPUT, VLC_TRUE) )
540     {
541         msg_Err( p_vout, "cannot create QT Embedded Thread" );
542         vlc_object_destroy( p_vout->p_sys->p_event );
543         p_vout->p_sys->p_event = NULL;
544         return -1;
545     }
546
547     if( p_vout->p_sys->p_event->b_error )
548     {
549         msg_Err( p_vout, "RunQtThread failed" );
550         return -1;
551     }
552
553     vlc_object_attach( p_vout->p_sys->p_event, p_vout );
554     msg_Dbg( p_vout, "RunQtThread running" );
555
556     // just wait until the crew is complete...
557     while(p_vout->p_sys->p_VideoWidget == NULL)
558     {
559         msleep(1);
560     }
561     return VLC_SUCCESS;
562 }
563
564
565 /*****************************************************************************
566  * CloseDisplay: destroy the window
567  *****************************************************************************/
568 static void CloseDisplay( vout_thread_t *p_vout )
569 {
570     // quit qt application loop
571     msg_Dbg( p_vout, "destroying Qt Window" );
572 #ifdef NEED_QTE_MAIN
573     if(p_vout->p_sys->p_QApplication)
574     {
575         p_vout->p_sys->bRunning = FALSE;
576         while(p_vout->p_sys->p_VideoWidget)
577         {
578             msleep(1);
579         }
580     }
581 #else
582     if (p_vout->p_sys->p_QApplication)
583        p_vout->p_sys->p_QApplication->quit();
584 #endif
585 }
586
587 /*****************************************************************************
588  * main loop of qtapplication
589  *****************************************************************************/
590 static void RunQtThread(event_thread_t *p_event)
591 {
592     msg_Dbg( p_event->p_vout, "RunQtThread starting" );
593
594 #ifdef NEED_QTE_MAIN
595     if (qApp)
596     {
597         p_event->p_vout->p_sys->p_QApplication = qApp;
598         p_event->p_vout->p_sys->bOwnsQApp = FALSE;
599         p_event->p_vout->p_sys->p_VideoWidget = qApp->mainWidget();
600         msg_Dbg( p_event->p_vout, "RunQtThread applicaton attached" );
601     }
602 #else
603     if (qApp==NULL)
604     {
605         int argc = 0;
606         QApplication* pApp = new QApplication(argc, NULL);
607         if(pApp)
608         {
609             p_event->p_vout->p_sys->p_QApplication = pApp;
610             p_event->p_vout->p_sys->bOwnsQApp = TRUE;
611         }
612         QWidget* pWidget = new QWidget();
613         if (pWidget)
614             {
615             p_event->p_vout->p_sys->p_VideoWidget = pWidget;
616         }
617     }
618 #endif
619     /* signal the creation of the window */
620     vlc_thread_ready( p_event );
621     msg_Dbg( p_event->p_vout, "RunQtThread ready" );
622
623     if (p_event->p_vout->p_sys->p_QApplication)
624     {
625         /* Set default window width and heigh to exactly preferred size. */
626             QWidget *desktop = p_event->p_vout->p_sys->p_QApplication->desktop();
627             p_event->p_vout->p_sys->p_VideoWidget->setMinimumWidth( 10 );
628              p_event->p_vout->p_sys->p_VideoWidget->setMinimumHeight( 10 );
629             p_event->p_vout->p_sys->p_VideoWidget->setBaseSize( p_event->p_vout->p_sys->i_width,
630             p_event->p_vout->p_sys->i_height );
631         p_event->p_vout->p_sys->p_VideoWidget->setMaximumWidth( desktop->width() );
632         p_event->p_vout->p_sys->p_VideoWidget->setMaximumHeight( desktop->height() );
633         /* Check on fullscreen */
634         if (p_event->p_vout->b_fullscreen)
635                   p_event->p_vout->p_sys->p_VideoWidget->showFullScreen();
636         else
637                 p_event->p_vout->p_sys->p_VideoWidget->showNormal();
638
639         p_event->p_vout->p_sys->p_VideoWidget->show();
640         p_event->p_vout->p_sys->bRunning = TRUE;
641
642 #ifdef NEED_QTE_MAIN
643         while(!p_event->b_die && p_event->p_vout->p_sys->bRunning)
644               {
645                /* Check if we are asked to exit */
646            if( p_event->b_die )
647                break;
648
649                msleep(100);
650             }
651 #else
652         // run the main loop of qtapplication until someone says: 'quit'
653         p_event->p_vout->p_sys->pcQApplication->exec();
654 #endif
655     }
656
657 #ifndef NEED_QTE_MAIN
658     if(p_event->p_vout->p_sys->p_QApplication)
659     {
660         delete p_event->p_vout->p_sys->p_VideoWidget;
661         p_event->p_vout->p_sys->p_VideoWidget = NULL;
662         delete p_event->p_vout->p_sys->p_QApplication;
663         p_event->p_vout->p_sys->p_QApplication = NULL;
664     }
665 #else
666     p_event->p_vout->p_sys->p_VideoWidget = NULL;
667 #endif
668
669     msg_Dbg( p_event->p_vout, "RunQtThread terminating" );
670 }
671