Fix threaded function declaration.
[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
41 #ifdef HAVE_CONFIG_H
42 # include "config.h"
43 #endif
44
45 #include <vlc_common.h>
46 #include <vlc_plugin.h>
47 #include <vlc_interface.h>
48 #include <vlc_vout.h>
49
50 #ifdef HAVE_MACHINE_PARAM_H
51     /* BSD */
52 #   include <machine/param.h>
53 #   include <sys/types.h>                                  /* typedef ushort */
54 #   include <sys/ipc.h>
55 #endif
56
57 #ifndef WIN32
58 #   include <netinet/in.h>                            /* BSD: struct in_addr */
59 #endif
60
61 #ifdef HAVE_SYS_SHM_H
62 #   include <sys/shm.h>                                /* shmget(), shmctl() */
63 #endif
64 } /* extern "C" */
65
66 #include <qapplication.h>
67 #include <qpainter.h>
68
69 #ifdef Q_WS_QWS
70 #   define USE_DIRECT_PAINTER
71 #   include <qdirectpainter_qws.h>
72 #   include <qgfxraster_qws.h>
73 #endif
74
75 extern "C"
76 {
77 #include "qte.h"
78
79 /*****************************************************************************
80  * Module descriptor
81  *****************************************************************************/
82 #define DISPLAY_TEXT N_("QT Embedded display")
83 #define DISPLAY_LONGTEXT N_( \
84     "Qt Embedded hardware display to use. " \
85     "By default VLC will use the value of the DISPLAY environment variable.")
86
87 /*****************************************************************************
88  * Local prototypes
89  *****************************************************************************/
90 static int  Open      ( vlc_object_t * );
91 static void Close     ( vlc_object_t * );
92 static void Render    ( vout_thread_t *, picture_t * );
93 static void Display   ( vout_thread_t *, picture_t * );
94 static int  Manage    ( vout_thread_t * );
95 static int  Init      ( vout_thread_t * );
96 static void End       ( vout_thread_t * );
97
98 static int  OpenDisplay ( vout_thread_t * );
99 static void CloseDisplay( vout_thread_t * );
100
101 static int  NewPicture     ( vout_thread_t *, picture_t * );
102 static void FreePicture    ( vout_thread_t *, picture_t * );
103
104 static void ToggleFullScreen      ( vout_thread_t * );
105
106 static void* RunQtThread( vlc_object_t *p_this );
107 } /* extern "C" */
108
109 /*****************************************************************************
110 * Exported prototypes
111 *****************************************************************************/
112 extern "C"
113 {
114
115 vlc_module_begin();
116     set_category( CAT_VIDEO );
117     set_subcategory( SUBCAT_VIDEO_VOUT );
118 //    add_category_hint( N_("QT Embedded"), NULL );
119 //    add_string( "qte-display", "landscape", NULL, DISPLAY_TEXT, DISPLAY_LONGTEXT);
120     set_description( N_("QT Embedded video output") );
121     set_capability( "video output", 70 );
122     add_shortcut( "qte" );
123     set_callbacks( Open, Close);
124 vlc_module_end();
125
126 } /* extern "C" */
127
128 /*****************************************************************************
129  * Seeking function TODO: put this in a generic location !
130  *****************************************************************************/
131 static inline void vout_Seek( off_t i_seek )
132 {
133 }
134
135 /*****************************************************************************
136  * Open: allocate video thread output method
137  *****************************************************************************/
138 static int Open( vlc_object_t *p_this )
139 {
140     vout_thread_t * p_vout = (vout_thread_t *)p_this;
141
142     /* Allocate structure */
143     p_vout->p_sys = (struct vout_sys_t*) malloc( sizeof( struct vout_sys_t ) );
144
145     if( p_vout->p_sys == NULL )
146         return( 1 );
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", 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         vlc_object_kill( p_vout->p_sys->p_event );
189         CloseDisplay(p_vout);
190
191         vlc_thread_join( p_vout->p_sys->p_event );
192         vlc_object_release( 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 >
392 //            p_vout->p_sys->i_mouse_hide_timeout ) )
393 //    {
394 //        /* Hide the mouse automatically */
395 //        p_vout->p_sys->b_cursor_autohidden = 1;
396 //        SDL_ShowCursor( 0 );
397 //    }
398 //
399 //    if( !vlc_object_alive (p_vout->p_libvlc) )
400 //        p_vout->p_sys->bRunning = FALSE;
401
402     return 0;
403 }
404
405 /*****************************************************************************
406  * End: terminate video thread output method
407  *****************************************************************************
408  * Destroy the buffers created by vout_Init. It is called at the end of
409  * the thread, but also each time the window is resized.
410  *****************************************************************************/
411 static void End( vout_thread_t *p_vout )
412 {
413     int i_index;
414
415     /* Free the direct buffers we allocated */
416     for( i_index = I_OUTPUTPICTURES ; i_index ; )
417     {
418         i_index--;
419         FreePicture( p_vout, PP_OUTPUTPICTURE[ i_index ] );
420     }
421 }
422
423
424 /*****************************************************************************
425  * NewPicture: allocate a picture
426  *****************************************************************************
427  * Returns 0 on success, -1 otherwise
428  *****************************************************************************/
429 static int NewPicture( vout_thread_t *p_vout, picture_t *p_pic )
430 {
431     int dd = QPixmap::defaultDepth();
432
433     p_pic->p_sys = (picture_sys_t*) malloc( sizeof( picture_sys_t ) );
434     if( p_pic->p_sys == NULL )
435     {
436         return -1;
437     }
438
439     /* Create the display */
440     p_pic->p_sys->pQImage = new QImage(p_vout->output.i_width,
441                                        p_vout->output.i_height, dd );
442
443     if(p_pic->p_sys->pQImage == NULL)
444     {
445         return -1;
446     }
447
448     switch( dd )
449     {
450         case 8:
451             p_pic->p->i_pixel_pitch = 1;
452             break;
453         case 15:
454         case 16:
455             p_pic->p->i_pixel_pitch = 2;
456             break;
457         case 24:
458         case 32:
459             p_pic->p->i_pixel_pitch = 4;
460             break;
461         default:
462             return( -1 );
463     }
464
465     p_pic->p->p_pixels = (p_pic->p_sys->pQImage->jumpTable())[0];
466     p_pic->p->i_pitch = p_pic->p_sys->pQImage->bytesPerLine();
467     p_pic->p->i_lines = p_vout->output.i_height;
468     p_pic->p->i_visible_lines = p_vout->output.i_height;
469     p_pic->p->i_visible_pitch =
470             p_pic->p->i_pixel_pitch * p_vout->output.i_width;
471
472     p_pic->i_planes = 1;
473
474     return 0;
475 }
476
477 /*****************************************************************************
478  * FreePicture: destroy a picture allocated with NewPicture
479  *****************************************************************************/
480 static void FreePicture( vout_thread_t *p_vout, picture_t *p_pic )
481 {
482     delete p_pic->p_sys->pQImage;
483 }
484
485 /*****************************************************************************
486  * ToggleFullScreen: Enable or disable full screen mode
487  *****************************************************************************
488  * This function will switch between fullscreen and window mode.
489  *
490  *****************************************************************************/
491 static void ToggleFullScreen ( vout_thread_t *p_vout )
492 {
493     if ( p_vout->b_fullscreen )
494        p_vout->p_sys->p_VideoWidget->showFullScreen();
495     else
496        p_vout->p_sys->p_VideoWidget->showNormal();
497
498     p_vout->b_fullscreen = !p_vout->b_fullscreen;
499 }
500
501 /*****************************************************************************
502  * OpenDisplay: create qte applicaton / window
503  *****************************************************************************
504  * Create a window according to video output given size, and set other
505  * properties according to the display properties.
506  *****************************************************************************/
507 static int OpenDisplay( vout_thread_t *p_vout )
508 {
509     /* for displaying the vout in a qt window we need the QtApplication */
510     p_vout->p_sys->p_QApplication = NULL;
511     p_vout->p_sys->p_VideoWidget = NULL;
512
513     p_vout->p_sys->p_event = (event_thread_t*) vlc_object_create( p_vout, sizeof(event_thread_t) );
514     p_vout->p_sys->p_event->p_vout = p_vout;
515
516     /* Initializations */
517 #if 1 /* FIXME: I need an event queue to handle video output size changes. */
518     p_vout->b_fullscreen = true;
519 #endif
520
521     /* Set main window's size */
522     QWidget *desktop = p_vout->p_sys->p_QApplication->desktop();
523     p_vout->p_sys->i_width = p_vout->b_fullscreen ? desktop->height() :
524                                                     p_vout->i_window_width;
525     p_vout->p_sys->i_height = p_vout->b_fullscreen ? desktop->width() :
526                                                      p_vout->i_window_height;
527
528 #if 0 /* FIXME: I need an event queue to handle video output size changes. */
529     /* Update dimensions */
530     p_vout->i_changes |= VOUT_SIZE_CHANGE;
531     p_vout->i_window_width = p_vout->p_sys->i_width;
532     p_vout->i_window_height = p_vout->p_sys->i_height;
533 #endif
534
535     msg_Dbg( p_vout, "opening display (h=%d,w=%d)",p_vout->p_sys->i_height,p_vout->p_sys->i_width);
536
537     /* create thread to exec the qpe application */
538     if ( vlc_thread_create( p_vout->p_sys->p_event, "QT Embedded Thread",
539                             RunQtThread,
540                             VLC_THREAD_PRIORITY_OUTPUT, true) )
541     {
542         msg_Err( p_vout, "cannot create QT Embedded Thread" );
543         vlc_object_release( p_vout->p_sys->p_event );
544         p_vout->p_sys->p_event = NULL;
545         return -1;
546     }
547
548     if( p_vout->p_sys->p_event->b_error )
549     {
550         msg_Err( p_vout, "RunQtThread failed" );
551         return -1;
552     }
553
554     vlc_object_attach( p_vout->p_sys->p_event, p_vout );
555     msg_Dbg( p_vout, "RunQtThread running" );
556
557     // just wait until the crew is complete...
558     while(p_vout->p_sys->p_VideoWidget == NULL)
559     {
560         msleep(1);
561     }
562     return VLC_SUCCESS;
563 }
564
565
566 /*****************************************************************************
567  * CloseDisplay: destroy the window
568  *****************************************************************************/
569 static void CloseDisplay( vout_thread_t *p_vout )
570 {
571     // quit qt application loop
572     msg_Dbg( p_vout, "destroying Qt Window" );
573 #ifdef NEED_QTE_MAIN
574     if(p_vout->p_sys->p_QApplication)
575     {
576         p_vout->p_sys->bRunning = FALSE;
577         while(p_vout->p_sys->p_VideoWidget)
578         {
579             msleep(1);
580         }
581     }
582 #else
583     if (p_vout->p_sys->p_QApplication)
584        p_vout->p_sys->p_QApplication->quit();
585 #endif
586 }
587
588 /*****************************************************************************
589  * main loop of qtapplication
590  *****************************************************************************/
591 static void* RunQtThread( vlc_object_t *p_this )
592 {
593     event_thread_t *p_event = (event_thread_t *)p_this;
594     msg_Dbg( p_event->p_vout, "RunQtThread starting" );
595
596 #ifdef NEED_QTE_MAIN
597     if (qApp)
598     {
599         p_event->p_vout->p_sys->p_QApplication = qApp;
600         p_event->p_vout->p_sys->bOwnsQApp = FALSE;
601         p_event->p_vout->p_sys->p_VideoWidget = qApp->mainWidget();
602         msg_Dbg( p_event->p_vout, "RunQtThread applicaton attached" );
603     }
604 #else
605     if (qApp==NULL)
606     {
607         int argc = 0;
608         QApplication* pApp = new QApplication(argc, NULL);
609         if(pApp)
610         {
611             p_event->p_vout->p_sys->p_QApplication = pApp;
612             p_event->p_vout->p_sys->bOwnsQApp = TRUE;
613         }
614         QWidget* pWidget = new QWidget();
615         if (pWidget)
616             {
617             p_event->p_vout->p_sys->p_VideoWidget = pWidget;
618         }
619     }
620 #endif
621     /* signal the creation of the window */
622     vlc_thread_ready( p_event );
623     msg_Dbg( p_event->p_vout, "RunQtThread ready" );
624
625     if (p_event->p_vout->p_sys->p_QApplication)
626     {
627         /* Set default window width and heigh to exactly preferred size. */
628             QWidget *desktop = p_event->p_vout->p_sys->p_QApplication->desktop();
629             p_event->p_vout->p_sys->p_VideoWidget->setMinimumWidth( 10 );
630              p_event->p_vout->p_sys->p_VideoWidget->setMinimumHeight( 10 );
631             p_event->p_vout->p_sys->p_VideoWidget->setBaseSize( p_event->p_vout->p_sys->i_width,
632             p_event->p_vout->p_sys->i_height );
633         p_event->p_vout->p_sys->p_VideoWidget->setMaximumWidth( desktop->width() );
634         p_event->p_vout->p_sys->p_VideoWidget->setMaximumHeight( desktop->height() );
635         /* Check on fullscreen */
636         if (p_event->p_vout->b_fullscreen)
637                   p_event->p_vout->p_sys->p_VideoWidget->showFullScreen();
638         else
639                 p_event->p_vout->p_sys->p_VideoWidget->showNormal();
640
641         p_event->p_vout->p_sys->p_VideoWidget->show();
642         p_event->p_vout->p_sys->bRunning = TRUE;
643
644 #ifdef NEED_QTE_MAIN
645         while(vlc_object_alive (p_event) && p_event->p_vout->p_sys->bRunning)
646               {
647                /* Check if we are asked to exit */
648            if( !vlc_object_alive (p_event) )
649                break;
650
651                msleep(100);
652             }
653 #else
654         // run the main loop of qtapplication until someone says: 'quit'
655         p_event->p_vout->p_sys->pcQApplication->exec();
656 #endif
657     }
658
659 #ifndef NEED_QTE_MAIN
660     if(p_event->p_vout->p_sys->p_QApplication)
661     {
662         delete p_event->p_vout->p_sys->p_VideoWidget;
663         p_event->p_vout->p_sys->p_VideoWidget = NULL;
664         delete p_event->p_vout->p_sys->p_QApplication;
665         p_event->p_vout->p_sys->p_QApplication = NULL;
666     }
667 #else
668     p_event->p_vout->p_sys->p_VideoWidget = NULL;
669 #endif
670
671     msg_Dbg( p_event->p_vout, "RunQtThread terminating" );
672     return NULL;
673 }
674