Main Page | Modules | Class Hierarchy | Class List | Directories | File List | Class Members | File Members | Related Pages

glwin32.c

00001 /*****************************************************************************
00002  * glwin32.c: Windows OpenGL provider
00003  *****************************************************************************
00004  * Copyright (C) 2001-2004 the VideoLAN team
00005  * $Id: glwin32.c 13062 2005-10-31 21:14:27Z gbazin $
00006  *
00007  * Authors: Gildas Bazin <[email protected]>
00008  *
00009  * This program is free software; you can redistribute it and/or modify
00010  * it under the terms of the GNU General Public License as published by
00011  * the Free Software Foundation; either version 2 of the License, or
00012  * (at your option) any later version.
00013  *
00014  * This program is distributed in the hope that it will be useful,
00015  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00016  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00017  * GNU General Public License for more details.
00018  *
00019  * You should have received a copy of the GNU General Public License
00020  * along with this program; if not, write to the Free Software
00021  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111, USA.
00022  *****************************************************************************/
00023 
00024 #include <errno.h>                                                 /* ENOMEM */
00025 #include <stdlib.h>                                                /* free() */
00026 #include <string.h>                                            /* strerror() */
00027 
00028 #include <vlc/vlc.h>
00029 #include <vlc/intf.h>
00030 #include <vlc/vout.h>
00031 
00032 #include <windows.h>
00033 #include <ddraw.h>
00034 #include <commctrl.h>
00035 
00036 #include <multimon.h>
00037 #undef GetSystemMetrics
00038 
00039 #ifndef MONITOR_DEFAULTTONEAREST
00040 #   define MONITOR_DEFAULTTONEAREST 2
00041 #endif
00042 
00043 #include <GL/gl.h>
00044 
00045 #include "vout.h"
00046 
00047 /*****************************************************************************
00048  * Local prototypes.
00049  *****************************************************************************/
00050 static int  OpenVideo  ( vlc_object_t * );
00051 static void CloseVideo ( vlc_object_t * );
00052 
00053 static int  Init      ( vout_thread_t * );
00054 static void End       ( vout_thread_t * );
00055 static int  Manage    ( vout_thread_t * );
00056 static void GLSwapBuffers( vout_thread_t * );
00057 
00058 /*****************************************************************************
00059  * Module descriptor
00060  *****************************************************************************/
00061 vlc_module_begin();
00062     set_category( CAT_VIDEO );
00063     set_subcategory( SUBCAT_VIDEO_VOUT );
00064     set_shortname( "OpenGL" );
00065     set_description( _("OpenGL video output") );
00066     set_capability( "opengl provider", 100 );
00067     add_shortcut( "glwin32" );
00068     set_callbacks( OpenVideo, CloseVideo );
00069 
00070     /* FIXME: Hack to avoid unregistering our window class */
00071     linked_with_a_crap_library_which_uses_atexit( );
00072 vlc_module_end();
00073 
00074 #if 0 /* FIXME */
00075     /* check if we registered a window class because we need to
00076      * unregister it */
00077     WNDCLASS wndclass;
00078     if( GetClassInfo( GetModuleHandle(NULL), "VLC DirectX", &wndclass ) )
00079         UnregisterClass( "VLC DirectX", GetModuleHandle(NULL) );
00080 #endif
00081 
00082 /*****************************************************************************
00083  * OpenVideo: allocate OpenGL provider
00084  *****************************************************************************
00085  * This function creates and initializes a video window.
00086  *****************************************************************************/
00087 static int OpenVideo( vlc_object_t *p_this )
00088 {
00089     vout_thread_t * p_vout = (vout_thread_t *)p_this;
00090     vlc_value_t val;
00091 
00092     /* Allocate structure */
00093     p_vout->p_sys = malloc( sizeof( vout_sys_t ) );
00094     if( p_vout->p_sys == NULL )
00095     {
00096         msg_Err( p_vout, "out of memory" );
00097         return VLC_ENOMEM;
00098     }
00099     memset( p_vout->p_sys, 0, sizeof( vout_sys_t ) );
00100 
00101     /* Initialisations */
00102     p_vout->pf_init = Init;
00103     p_vout->pf_end = End;
00104     p_vout->pf_manage = Manage;
00105     p_vout->pf_swap = GLSwapBuffers;
00106 
00107     p_vout->p_sys->p_ddobject = NULL;
00108     p_vout->p_sys->p_display = NULL;
00109     p_vout->p_sys->p_current_surface = NULL;
00110     p_vout->p_sys->p_clipper = NULL;
00111     p_vout->p_sys->hwnd = p_vout->p_sys->hvideownd = NULL;
00112     p_vout->p_sys->hparent = p_vout->p_sys->hfswnd = NULL;
00113     p_vout->p_sys->i_changes = 0;
00114     p_vout->p_sys->b_wallpaper = 0;
00115     vlc_mutex_init( p_vout, &p_vout->p_sys->lock );
00116     SetRectEmpty( &p_vout->p_sys->rect_display );
00117     SetRectEmpty( &p_vout->p_sys->rect_parent );
00118 
00119     var_Create( p_vout, "video-title", VLC_VAR_STRING | VLC_VAR_DOINHERIT );
00120 
00121     p_vout->p_sys->b_cursor_hidden = 0;
00122     p_vout->p_sys->i_lastmoved = mdate();
00123 
00124     /* Set main window's size */
00125     p_vout->p_sys->i_window_width = p_vout->i_window_width;
00126     p_vout->p_sys->i_window_height = p_vout->i_window_height;
00127 
00128     /* Create the DirectXEventThread, this thread is created by us to isolate
00129      * the Win32 PeekMessage function calls. We want to do this because
00130      * Windows can stay blocked inside this call for a long time, and when
00131      * this happens it thus blocks vlc's video_output thread.
00132      * DirectXEventThread will take care of the creation of the video
00133      * window (because PeekMessage has to be called from the same thread which
00134      * created the window). */
00135     msg_Dbg( p_vout, "creating DirectXEventThread" );
00136     p_vout->p_sys->p_event =
00137         vlc_object_create( p_vout, sizeof(event_thread_t) );
00138     p_vout->p_sys->p_event->p_vout = p_vout;
00139     if( vlc_thread_create( p_vout->p_sys->p_event, "DirectX Events Thread",
00140                            E_(DirectXEventThread), 0, 1 ) )
00141     {
00142         msg_Err( p_vout, "cannot create DirectXEventThread" );
00143         vlc_object_destroy( p_vout->p_sys->p_event );
00144         p_vout->p_sys->p_event = NULL;
00145         goto error;
00146     }
00147 
00148     if( p_vout->p_sys->p_event->b_error )
00149     {
00150         msg_Err( p_vout, "DirectXEventThread failed" );
00151         goto error;
00152     }
00153 
00154     vlc_object_attach( p_vout->p_sys->p_event, p_vout );
00155 
00156     msg_Dbg( p_vout, "DirectXEventThread running" );
00157 
00158     /* Variable to indicate if the window should be on top of others */
00159     /* Trigger a callback right now */
00160     var_Get( p_vout, "video-on-top", &val );
00161     var_Set( p_vout, "video-on-top", val );
00162 
00163     return VLC_SUCCESS;
00164 
00165  error:
00166     CloseVideo( VLC_OBJECT(p_vout) );
00167     return VLC_EGENERIC;
00168 }
00169 
00170 /*****************************************************************************
00171  * Init: initialize video thread output method
00172  *****************************************************************************/
00173 static int Init( vout_thread_t *p_vout )
00174 {
00175     PIXELFORMATDESCRIPTOR pfd;
00176     int iFormat;
00177 
00178     /* Change the window title bar text */
00179     PostMessage( p_vout->p_sys->hwnd, WM_VLC_CHANGE_TEXT, 0, 0 );
00180 
00181     p_vout->p_sys->hGLDC = GetDC( p_vout->p_sys->hvideownd );
00182 
00183     /* Set the pixel format for the DC */
00184     memset( &pfd, 0, sizeof( pfd ) );
00185     pfd.nSize = sizeof( pfd );
00186     pfd.nVersion = 1;
00187     pfd.dwFlags = PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER;
00188     pfd.iPixelType = PFD_TYPE_RGBA;
00189     pfd.cColorBits = 24;
00190     pfd.cDepthBits = 16;
00191     pfd.iLayerType = PFD_MAIN_PLANE;
00192     iFormat = ChoosePixelFormat( p_vout->p_sys->hGLDC, &pfd );
00193     SetPixelFormat( p_vout->p_sys->hGLDC, iFormat, &pfd );
00194 
00195     /* Create and enable the render context */
00196     p_vout->p_sys->hGLRC = wglCreateContext( p_vout->p_sys->hGLDC );
00197     wglMakeCurrent( p_vout->p_sys->hGLDC, p_vout->p_sys->hGLRC );
00198 
00199     return VLC_SUCCESS;
00200 }
00201 
00202 /*****************************************************************************
00203  * End: terminate Sys video thread output method
00204  *****************************************************************************
00205  * Terminate an output method created by Create.
00206  * It is called at the end of the thread.
00207  *****************************************************************************/
00208 static void End( vout_thread_t *p_vout )
00209 {
00210     wglMakeCurrent( NULL, NULL );
00211     wglDeleteContext( p_vout->p_sys->hGLRC );
00212     ReleaseDC( p_vout->p_sys->hvideownd, p_vout->p_sys->hGLDC );
00213     return;
00214 }
00215 
00216 /*****************************************************************************
00217  * CloseVideo: destroy Sys video thread output method
00218  *****************************************************************************
00219  * Terminate an output method created by Create
00220  *****************************************************************************/
00221 static void CloseVideo( vlc_object_t *p_this )
00222 {
00223     vout_thread_t * p_vout = (vout_thread_t *)p_this;
00224 
00225     msg_Dbg( p_vout, "CloseVideo" );
00226 
00227     if( p_vout->p_sys->p_event )
00228     {
00229         vlc_object_detach( p_vout->p_sys->p_event );
00230 
00231         /* Kill DirectXEventThread */
00232         p_vout->p_sys->p_event->b_die = VLC_TRUE;
00233 
00234         /* we need to be sure DirectXEventThread won't stay stuck in
00235          * GetMessage, so we send a fake message */
00236         if( p_vout->p_sys->hwnd )
00237         {
00238             PostMessage( p_vout->p_sys->hwnd, WM_NULL, 0, 0);
00239         }
00240 
00241         vlc_thread_join( p_vout->p_sys->p_event );
00242         vlc_object_destroy( p_vout->p_sys->p_event );
00243     }
00244 
00245     vlc_mutex_destroy( &p_vout->p_sys->lock );
00246 
00247     if( p_vout->p_sys )
00248     {
00249         free( p_vout->p_sys );
00250         p_vout->p_sys = NULL;
00251     }
00252 }
00253 
00254 /*****************************************************************************
00255  * Manage: handle Sys events
00256  *****************************************************************************
00257  * This function should be called regularly by the video output thread.
00258  * It returns a non null value if an error occurred.
00259  *****************************************************************************/
00260 static int Manage( vout_thread_t *p_vout )
00261 {
00262     WINDOWPLACEMENT window_placement;
00263 
00264     int i_width = p_vout->p_sys->rect_dest.right -
00265         p_vout->p_sys->rect_dest.left;
00266     int i_height = p_vout->p_sys->rect_dest.bottom -
00267         p_vout->p_sys->rect_dest.top;
00268     glViewport( 0, 0, i_width, i_height );
00269 
00270     /* If we do not control our window, we check for geometry changes
00271      * ourselves because the parent might not send us its events. */
00272     vlc_mutex_lock( &p_vout->p_sys->lock );
00273     if( p_vout->p_sys->hparent && !p_vout->b_fullscreen )
00274     {
00275         RECT rect_parent;
00276         POINT point;
00277 
00278         vlc_mutex_unlock( &p_vout->p_sys->lock );
00279 
00280         GetClientRect( p_vout->p_sys->hparent, &rect_parent );
00281         point.x = point.y = 0;
00282         ClientToScreen( p_vout->p_sys->hparent, &point );
00283         OffsetRect( &rect_parent, point.x, point.y );
00284 
00285         if( !EqualRect( &rect_parent, &p_vout->p_sys->rect_parent ) )
00286         {
00287             p_vout->p_sys->rect_parent = rect_parent;
00288 
00289             /* This one is to force the update even if only
00290              * the position has changed */
00291             SetWindowPos( p_vout->p_sys->hwnd, 0, 1, 1,
00292                           rect_parent.right - rect_parent.left,
00293                           rect_parent.bottom - rect_parent.top, 0 );
00294 
00295             SetWindowPos( p_vout->p_sys->hwnd, 0, 0, 0,
00296                           rect_parent.right - rect_parent.left,
00297                           rect_parent.bottom - rect_parent.top, 0 );
00298         }
00299     }
00300     else
00301     {
00302         vlc_mutex_unlock( &p_vout->p_sys->lock );
00303     }
00304 
00305     /* Check for cropping / aspect changes */
00306     if( p_vout->i_changes & VOUT_CROP_CHANGE ||
00307         p_vout->i_changes & VOUT_ASPECT_CHANGE )
00308     {
00309         p_vout->i_changes &= ~VOUT_CROP_CHANGE;
00310         p_vout->i_changes &= ~VOUT_ASPECT_CHANGE;
00311 
00312         p_vout->fmt_out.i_x_offset = p_vout->fmt_in.i_x_offset;
00313         p_vout->fmt_out.i_y_offset = p_vout->fmt_in.i_y_offset;
00314         p_vout->fmt_out.i_visible_width = p_vout->fmt_in.i_visible_width;
00315         p_vout->fmt_out.i_visible_height = p_vout->fmt_in.i_visible_height;
00316         p_vout->fmt_out.i_aspect = p_vout->fmt_in.i_aspect;
00317         p_vout->fmt_out.i_sar_num = p_vout->fmt_in.i_sar_num;
00318         p_vout->fmt_out.i_sar_den = p_vout->fmt_in.i_sar_den;
00319         p_vout->output.i_aspect = p_vout->fmt_in.i_aspect;
00320         E_(DirectXUpdateRects)( p_vout, VLC_TRUE );
00321     }
00322 
00323     /* We used to call the Win32 PeekMessage function here to read the window
00324      * messages. But since window can stay blocked into this function for a
00325      * long time (for example when you move your window on the screen), I
00326      * decided to isolate PeekMessage in another thread. */
00327 
00328     /*
00329      * Fullscreen change
00330      */
00331     if( p_vout->i_changes & VOUT_FULLSCREEN_CHANGE
00332         || p_vout->p_sys->i_changes & VOUT_FULLSCREEN_CHANGE )
00333     {
00334         vlc_value_t val;
00335         HWND hwnd = (p_vout->p_sys->hparent && p_vout->p_sys->hfswnd) ?
00336             p_vout->p_sys->hfswnd : p_vout->p_sys->hwnd;
00337 
00338         p_vout->b_fullscreen = ! p_vout->b_fullscreen;
00339 
00340         /* We need to switch between Maximized and Normal sized window */
00341         window_placement.length = sizeof(WINDOWPLACEMENT);
00342         GetWindowPlacement( hwnd, &window_placement );
00343         if( p_vout->b_fullscreen )
00344         {
00345             /* Change window style, no borders and no title bar */
00346             int i_style = WS_CLIPCHILDREN | WS_VISIBLE;
00347             SetWindowLong( hwnd, GWL_STYLE, i_style );
00348 
00349             if( p_vout->p_sys->hparent )
00350             {
00351                 /* Retrieve current window position so fullscreen will happen
00352                  * on the right screen */
00353                 POINT point = {0,0};
00354                 RECT rect;
00355                 ClientToScreen( p_vout->p_sys->hwnd, &point );
00356                 GetClientRect( p_vout->p_sys->hwnd, &rect );
00357                 SetWindowPos( hwnd, 0, point.x, point.y,
00358                               rect.right, rect.bottom,
00359                               SWP_NOZORDER|SWP_FRAMECHANGED );
00360                 GetWindowPlacement( hwnd, &window_placement );
00361             }
00362 
00363             /* Maximize window */
00364             window_placement.showCmd = SW_SHOWMAXIMIZED;
00365             SetWindowPlacement( hwnd, &window_placement );
00366             SetWindowPos( hwnd, 0, 0, 0, 0, 0,
00367                           SWP_NOMOVE|SWP_NOSIZE|SWP_NOZORDER|SWP_FRAMECHANGED);
00368 
00369             if( p_vout->p_sys->hparent )
00370             {
00371                 RECT rect;
00372                 GetClientRect( hwnd, &rect );
00373                 SetParent( p_vout->p_sys->hwnd, hwnd );
00374                 SetWindowPos( p_vout->p_sys->hwnd, 0, 0, 0,
00375                               rect.right, rect.bottom,
00376                               SWP_NOZORDER|SWP_FRAMECHANGED );
00377             }
00378 
00379             SetForegroundWindow( hwnd );
00380         }
00381         else
00382         {
00383             /* Change window style, no borders and no title bar */
00384             SetWindowLong( hwnd, GWL_STYLE, p_vout->p_sys->i_window_style );
00385 
00386             /* Normal window */
00387             window_placement.showCmd = SW_SHOWNORMAL;
00388             SetWindowPlacement( hwnd, &window_placement );
00389             SetWindowPos( hwnd, 0, 0, 0, 0, 0,
00390                           SWP_NOMOVE|SWP_NOSIZE|SWP_NOZORDER|SWP_FRAMECHANGED);
00391 
00392             if( p_vout->p_sys->hparent )
00393             {
00394                 RECT rect;
00395                 GetClientRect( p_vout->p_sys->hparent, &rect );
00396                 SetParent( p_vout->p_sys->hwnd, p_vout->p_sys->hparent );
00397                 SetWindowPos( p_vout->p_sys->hwnd, 0, 0, 0,
00398                               rect.right, rect.bottom,
00399                               SWP_NOZORDER|SWP_FRAMECHANGED );
00400 
00401                 ShowWindow( hwnd, SW_HIDE );
00402                 SetForegroundWindow( p_vout->p_sys->hparent );
00403             }
00404 
00405             /* Make sure the mouse cursor is displayed */
00406             PostMessage( p_vout->p_sys->hwnd, WM_VLC_SHOW_MOUSE, 0, 0 );
00407         }
00408 
00409         /* Update the object variable and trigger callback */
00410         val.b_bool = p_vout->b_fullscreen;
00411         var_Set( p_vout, "fullscreen", val );
00412 
00413         p_vout->i_changes &= ~VOUT_FULLSCREEN_CHANGE;
00414         p_vout->p_sys->i_changes &= ~VOUT_FULLSCREEN_CHANGE;
00415     }
00416 
00417     /*
00418      * Pointer change
00419      */
00420     if( p_vout->b_fullscreen && !p_vout->p_sys->b_cursor_hidden &&
00421         (mdate() - p_vout->p_sys->i_lastmoved) > 5000000 )
00422     {
00423         POINT point;
00424         HWND hwnd;
00425 
00426         /* Hide the cursor only if it is inside our window */
00427         GetCursorPos( &point );
00428         hwnd = WindowFromPoint(point);
00429         if( hwnd == p_vout->p_sys->hwnd || hwnd == p_vout->p_sys->hvideownd )
00430         {
00431             PostMessage( p_vout->p_sys->hwnd, WM_VLC_HIDE_MOUSE, 0, 0 );
00432         }
00433         else
00434         {
00435             p_vout->p_sys->i_lastmoved = mdate();
00436         }
00437     }
00438 
00439     /*
00440      * "Always on top" status change
00441      */
00442     if( p_vout->p_sys->b_on_top_change )
00443     {
00444         vlc_value_t val;
00445         HMENU hMenu = GetSystemMenu( p_vout->p_sys->hwnd, FALSE );
00446 
00447         var_Get( p_vout, "video-on-top", &val );
00448 
00449         /* Set the window on top if necessary */
00450         if( val.b_bool && !( GetWindowLong( p_vout->p_sys->hwnd, GWL_EXSTYLE )
00451                            & WS_EX_TOPMOST ) )
00452         {
00453             CheckMenuItem( hMenu, IDM_TOGGLE_ON_TOP,
00454                            MF_BYCOMMAND | MFS_CHECKED );
00455             SetWindowPos( p_vout->p_sys->hwnd, HWND_TOPMOST, 0, 0, 0, 0,
00456                           SWP_NOSIZE | SWP_NOMOVE );
00457         }
00458         else
00459         /* The window shouldn't be on top */
00460         if( !val.b_bool && ( GetWindowLong( p_vout->p_sys->hwnd, GWL_EXSTYLE )
00461                            & WS_EX_TOPMOST ) )
00462         {
00463             CheckMenuItem( hMenu, IDM_TOGGLE_ON_TOP,
00464                            MF_BYCOMMAND | MFS_UNCHECKED );
00465             SetWindowPos( p_vout->p_sys->hwnd, HWND_NOTOPMOST, 0, 0, 0, 0,
00466                           SWP_NOSIZE | SWP_NOMOVE );
00467         }
00468 
00469         p_vout->p_sys->b_on_top_change = VLC_FALSE;
00470     }
00471 
00472     /* Check if the event thread is still running */
00473     if( p_vout->p_sys->p_event->b_die )
00474     {
00475         return VLC_EGENERIC; /* exit */
00476     }
00477 
00478     return VLC_SUCCESS;
00479 }
00480 
00481 /*****************************************************************************
00482  * GLSwapBuffers: swap front/back buffers
00483  *****************************************************************************/
00484 static void GLSwapBuffers( vout_thread_t *p_vout )
00485 {
00486     SwapBuffers( p_vout->p_sys->hGLDC );
00487 }
00488 
00489 int E_(DirectXUpdateOverlay)( vout_thread_t *p_vout )
00490 {
00491     return 1;
00492 }

Generated on Tue Dec 20 10:14:55 2005 for vlc-0.8.4a by  doxygen 1.4.2