38cf06d89855409f45ef33252e6f3a490df2480c
[vlc.git] / modules / video_output / directx / events.c
1 /*****************************************************************************
2  * events.c: Windows DirectX video output events handler
3  *****************************************************************************
4  * Copyright (C) 2001 VideoLAN
5  * $Id: events.c,v 1.15 2003/05/17 14:36:19 gbazin Exp $
6  *
7  * Authors: Gildas Bazin <gbazin@netcourrier.com>
8  *
9  * This program is free software; you can redistribute it and/or modify
10  * it under the terms of the GNU General Public License as published by
11  * the Free Software Foundation; either version 2 of the License, or
12  * (at your option) any later version.
13  *
14  * This program is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
17  * GNU General Public License for more details.
18  *
19  * You should have received a copy of the GNU General Public License
20  * along with this program; if not, write to the Free Software
21  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111, USA.
22  *****************************************************************************/
23
24
25 /*****************************************************************************
26  * Preamble: This file contains the functions related to the creation of
27  *             a window and the handling of its messages (events).
28  *****************************************************************************/
29 #include <errno.h>                                                 /* ENOMEM */
30 #include <stdlib.h>                                                /* free() */
31 #include <string.h>                                            /* strerror() */
32
33 #include <vlc/vlc.h>
34 #include <vlc/intf.h>
35 #include <vlc/input.h>
36 #include <vlc/vout.h>
37
38 #include <windows.h>
39 #include <windowsx.h>
40 #include <shellapi.h>
41
42 #include <ddraw.h>
43
44 #include "vout.h"
45
46 /*****************************************************************************
47  * Local prototypes.
48  *****************************************************************************/
49 static int  DirectXCreateWindow( vout_thread_t *p_vout );
50 static void DirectXCloseWindow ( vout_thread_t *p_vout );
51 static long FAR PASCAL DirectXEventProc ( HWND hwnd, UINT message,
52                                           WPARAM wParam, LPARAM lParam );
53
54 /*****************************************************************************
55  * DirectXEventThread: Create video window & handle its messages
56  *****************************************************************************
57  * This function creates a video window and then enters an infinite loop
58  * that handles the messages sent to that window.
59  * The main goal of this thread is to isolate the Win32 PeekMessage function
60  * because this one can block for a long time.
61  *****************************************************************************/
62 void DirectXEventThread( event_thread_t *p_event )
63 {
64     MSG msg;
65     POINT old_mouse_pos = {0,0};
66     vlc_value_t val;
67     int i_width, i_height, i_x, i_y;
68
69     /* Initialisation */
70
71     /* Create a window for the video */
72     /* Creating a window under Windows also initializes the thread's event
73      * message queue */
74     if( DirectXCreateWindow( p_event->p_vout ) )
75     {
76         msg_Err( p_event, "out of memory" );
77         p_event->b_dead = VLC_TRUE;
78     }
79
80     /* signal the creation of the window */
81     vlc_thread_ready( p_event );
82
83     /* Main loop */
84     /* GetMessage will sleep if there's no message in the queue */
85     while( !p_event->b_die
86            && GetMessage( &msg, p_event->p_vout->p_sys->hwnd, 0, 0 ) )
87     {
88         /* Check if we are asked to exit */
89         if( p_event->b_die )
90             break;
91
92         switch( msg.message )
93         {
94
95         case WM_NCMOUSEMOVE:
96         case WM_MOUSEMOVE:
97             vout_PlacePicture( p_event->p_vout,
98                                p_event->p_vout->p_sys->i_window_width,
99                                p_event->p_vout->p_sys->i_window_height,
100                                &i_x, &i_y, &i_width, &i_height );
101
102             val.i_int = ( GET_X_LPARAM(msg.lParam) - i_x )
103                          * p_event->p_vout->render.i_width / i_width;
104             var_Set( p_event->p_vout, "mouse-x", val );
105             val.i_int = ( GET_Y_LPARAM(msg.lParam) - i_y )
106                          * p_event->p_vout->render.i_height / i_height;
107             var_Set( p_event->p_vout, "mouse-y", val );
108
109             val.b_bool = VLC_TRUE;
110             var_Set( p_event->p_vout, "mouse-moved", val );
111
112             if( (abs(GET_X_LPARAM(msg.lParam) - old_mouse_pos.x) > 2 ||
113                 (abs(GET_Y_LPARAM(msg.lParam) - old_mouse_pos.y)) > 2 ) )
114             {
115                 GetCursorPos( &old_mouse_pos );
116                 p_event->p_vout->p_sys->i_lastmoved = mdate();
117
118                 if( p_event->p_vout->p_sys->b_cursor_hidden )
119                 {
120                     p_event->p_vout->p_sys->b_cursor_hidden = 0;
121                     ShowCursor( TRUE );
122                 }
123             }
124             break;
125
126         case WM_VLC_HIDE_MOUSE:
127             GetCursorPos( &old_mouse_pos );
128             ShowCursor( FALSE );
129             break;
130
131         case WM_LBUTTONDOWN:
132             var_Get( p_event->p_vout, "mouse-button-down", &val );
133             val.i_int |= 1;
134             var_Set( p_event->p_vout, "mouse-button-down", val );
135             break;
136
137         case WM_LBUTTONUP:
138             var_Get( p_event->p_vout, "mouse-button-down", &val );
139             val.i_int &= ~1;
140             var_Set( p_event->p_vout, "mouse-button-down", val );
141
142             val.b_bool = VLC_TRUE;
143             var_Set( p_event->p_vout, "mouse-clicked", val );
144             break;
145
146         case WM_LBUTTONDBLCLK:
147             p_event->p_vout->p_sys->i_changes |= VOUT_FULLSCREEN_CHANGE;
148             break;
149
150         case WM_MBUTTONDOWN:
151             var_Get( p_event->p_vout, "mouse-button-down", &val );
152             val.i_int |= 2;
153             var_Set( p_event->p_vout, "mouse-button-down", val );
154             break;
155
156         case WM_MBUTTONUP:
157             var_Get( p_event->p_vout, "mouse-button-down", &val );
158             val.i_int &= ~2;
159             var_Set( p_event->p_vout, "mouse-button-down", val );
160             break;
161
162         case WM_RBUTTONDOWN:
163             var_Get( p_event->p_vout, "mouse-button-down", &val );
164             val.i_int |= 4;
165             var_Set( p_event->p_vout, "mouse-button-down", val );
166             break;
167
168         case WM_RBUTTONUP:
169             var_Get( p_event->p_vout, "mouse-button-down", &val );
170             val.i_int &= ~4;
171             var_Set( p_event->p_vout, "mouse-button-down", val );
172             {
173                 intf_thread_t *p_intf;
174                 p_intf = vlc_object_find( p_event, VLC_OBJECT_INTF,
175                                                    FIND_ANYWHERE );
176                 if( p_intf )
177                 {
178                     p_intf->b_menu_change = 1;
179                     vlc_object_release( p_intf );
180                 }
181             }
182             break;
183
184         case WM_KEYDOWN:
185             /* the key events are first processed here. The next
186              * message processed by this main message loop will be the
187              * char translation of the key event */
188             msg_Dbg( p_event, "WM_KEYDOWN" );
189             switch( msg.wParam )
190             {
191             case VK_ESCAPE:
192                 if( p_event->p_vout->b_fullscreen )
193                 {
194                     p_event->p_vout->i_changes |= VOUT_FULLSCREEN_CHANGE;
195                 }
196                 else
197                 {
198                     /* exit application */
199                     p_event->p_vout->p_vlc->b_die = VLC_TRUE;
200                 }
201                 break;
202
203             case VK_MENU:
204                 {
205                     intf_thread_t *p_intf;
206                     p_intf = vlc_object_find( p_event->p_vout, VLC_OBJECT_INTF,
207                                               FIND_ANYWHERE );
208                     if( p_intf )
209                     {
210                         p_intf->b_menu_change = 1;
211                         vlc_object_release( p_intf );
212                     }
213                 }
214                 break;
215
216             case VK_LEFT:
217                 input_Seek( p_event->p_vout, -5,
218                             INPUT_SEEK_SECONDS | INPUT_SEEK_CUR );
219                 break;
220             case VK_RIGHT:
221                 input_Seek( p_event->p_vout, 5,
222                             INPUT_SEEK_SECONDS | INPUT_SEEK_CUR );
223                 break;
224             case VK_UP:
225                 input_Seek( p_event->p_vout, 60,
226                             INPUT_SEEK_SECONDS | INPUT_SEEK_CUR );
227                 break;
228             case VK_DOWN:
229                 input_Seek( p_event->p_vout, -60,
230                             INPUT_SEEK_SECONDS | INPUT_SEEK_CUR );
231                 break;
232             case VK_HOME:
233                 input_Seek( p_event->p_vout, 0,
234                             INPUT_SEEK_BYTES | INPUT_SEEK_SET );
235                 break;
236             case VK_END:
237                 input_Seek( p_event->p_vout, 0,
238                             INPUT_SEEK_BYTES | INPUT_SEEK_END );
239                 break;
240             case VK_PRIOR:
241                 input_Seek( p_event->p_vout, 900,
242                             INPUT_SEEK_SECONDS | INPUT_SEEK_CUR );
243                 break;
244             case VK_NEXT:
245                 input_Seek( p_event->p_vout, -900,
246                             INPUT_SEEK_SECONDS | INPUT_SEEK_CUR );
247                 break;
248             case VK_SPACE:
249                 input_SetStatus( p_event->p_vout, INPUT_STATUS_PAUSE );
250                 break;
251             }
252             TranslateMessage(&msg);
253             break;
254
255         case WM_CHAR:
256             switch( msg.wParam )
257             {
258             case 'q':
259             case 'Q':
260                 /* exit application */
261                 p_event->p_vlc->b_die = VLC_TRUE;
262                 break;
263
264             case 'f':                            /* switch to fullscreen */
265             case 'F':
266                 p_event->p_vout->p_sys->i_changes |= VOUT_FULLSCREEN_CHANGE;
267                 break;
268
269             default:
270                 break;
271             }
272
273         default:
274             /* Messages we don't handle directly are dispatched to the
275              * window procedure */
276             TranslateMessage(&msg);
277             DispatchMessage(&msg);
278             break;
279
280         } /* End Switch */
281
282     } /* End Main loop */
283
284     if( msg.message == WM_QUIT )
285     {
286         msg_Warn( p_event, "WM_QUIT... should not happen!!" );
287         p_event->p_vout->p_sys->hwnd = NULL; /* Window already destroyed */
288     }
289
290     msg_Dbg( p_event, "DirectXEventThread terminating" );
291
292     /* clear the changes formerly signaled */
293     p_event->p_vout->p_sys->i_changes = 0;
294
295     DirectXCloseWindow( p_event->p_vout );
296 }
297
298
299 /* following functions are local */
300
301 /*****************************************************************************
302  * DirectXCreateWindow: create a window for the video.
303  *****************************************************************************
304  * Before creating a direct draw surface, we need to create a window in which
305  * the video will be displayed. This window will also allow us to capture the
306  * events.
307  *****************************************************************************/
308 static int DirectXCreateWindow( vout_thread_t *p_vout )
309 {
310     HINSTANCE  hInstance;
311     COLORREF   colorkey;
312     HDC        hdc;
313     HMENU      hMenu;
314     RECT       rect_window;
315
316     vlc_value_t val;
317
318     msg_Dbg( p_vout, "DirectXCreateWindow" );
319
320     /* Get this module's instance */
321     hInstance = GetModuleHandle(NULL);
322
323     /* Create a BRUSH that will be used by Windows to paint the window
324      * background.
325      * This window background is important for us as it will be used by the
326      * graphics card to display the overlay.
327      * This is why we carefully choose the color for this background, the goal
328      * being to choose a color which isn't complete black but nearly. We
329      * obviously don't want to use black as a colorkey for the overlay because
330      * black is one of the most used color and thus would give us undesirable
331      * effects */
332     /* the first step is to find the colorkey we want to use. The difficulty
333      * comes from the potential dithering (depends on the display depth)
334      * because we need to know the real RGB value of the chosen colorkey */
335     hdc = GetDC( NULL );
336     for( colorkey = 0x0a; colorkey < 0xff /* all shades of red */; colorkey++ )
337     {
338         if( colorkey == GetNearestColor( hdc, colorkey ) )
339         {
340             break;
341         }
342     }
343     msg_Dbg( p_vout, "background color: %i", colorkey );
344
345     /* Create the actual brush */
346     p_vout->p_sys->hbrush = CreateSolidBrush(colorkey);
347     p_vout->p_sys->i_rgb_colorkey = (int)colorkey;
348
349     /* Get the current size of the display and its colour depth */
350     p_vout->p_sys->rect_display.right = GetDeviceCaps( hdc, HORZRES );
351     p_vout->p_sys->rect_display.bottom = GetDeviceCaps( hdc, VERTRES );
352     p_vout->p_sys->i_display_depth = GetDeviceCaps( hdc, BITSPIXEL );
353     msg_Dbg( p_vout, "screen dimensions %ix%i colour depth %i",
354                       p_vout->p_sys->rect_display.right,
355                       p_vout->p_sys->rect_display.bottom,
356                       p_vout->p_sys->i_display_depth );
357
358     ReleaseDC( NULL, hdc );
359
360     /* If an external window was specified, we'll draw in it. */
361     var_Get( p_vout->p_vlc, "drawable", &val );
362     p_vout->p_sys->hparent = p_vout->p_sys->hwnd =
363              val.i_int ?  (void*)(ptrdiff_t) val.i_int : NULL;
364
365     if( p_vout->p_sys->hparent )
366     {
367         msg_Dbg( p_vout, "using external window %p\n", p_vout->p_sys->hwnd );
368
369         /* Set stuff in the window that we can not put directly in
370          * a class (see below). */
371         SetClassLong( p_vout->p_sys->hwnd,
372                       GCL_STYLE, CS_DBLCLKS );
373         SetClassLong( p_vout->p_sys->hwnd,
374                       GCL_HBRBACKGROUND, (LONG)p_vout->p_sys->hbrush );
375         SetClassLong( p_vout->p_sys->hwnd,
376                       GCL_HCURSOR, (LONG)LoadCursor(NULL, IDC_ARROW) );
377         /* Store a p_vout pointer into the window local storage (for later
378          * use in DirectXEventProc). */
379         SetWindowLong( p_vout->p_sys->hwnd, GWL_USERDATA, (LONG)p_vout );
380
381         p_vout->p_sys->pf_wndproc =
382                (WNDPROC)SetWindowLong( p_vout->p_sys->hwnd,
383                                        GWL_WNDPROC, (LONG)DirectXEventProc );
384
385         /* Blam! Erase everything that might have been there. */
386         RedrawWindow( p_vout->p_sys->hwnd, NULL, NULL,
387                       RDW_INVALIDATE | RDW_ERASE );
388     }
389     else
390     {
391         WNDCLASSEX wc;                            /* window class components */
392         HICON      vlc_icon = NULL;
393         char       vlc_path[MAX_PATH+1];
394
395         /* Get the Icon from the main app */
396         vlc_icon = NULL;
397         if( GetModuleFileName( NULL, vlc_path, MAX_PATH ) )
398         {
399             vlc_icon = ExtractIcon( hInstance, vlc_path, 0 );
400         }
401
402         /* Fill in the window class structure */
403         wc.cbSize        = sizeof(WNDCLASSEX);
404         wc.style         = CS_DBLCLKS;                   /* style: dbl click */
405         wc.lpfnWndProc   = (WNDPROC)DirectXEventProc;       /* event handler */
406         wc.cbClsExtra    = 0;                         /* no extra class data */
407         wc.cbWndExtra    = 0;                        /* no extra window data */
408         wc.hInstance     = hInstance;                            /* instance */
409         wc.hIcon         = vlc_icon;                /* load the vlc big icon */
410         wc.hCursor       = LoadCursor(NULL, IDC_ARROW);    /* default cursor */
411         wc.hbrBackground = p_vout->p_sys->hbrush;        /* background color */
412         wc.lpszMenuName  = NULL;                                  /* no menu */
413         wc.lpszClassName = "VLC DirectX";             /* use a special class */
414         wc.hIconSm       = vlc_icon;              /* load the vlc small icon */
415
416         /* Register the window class */
417         if( !RegisterClassEx(&wc) )
418         {
419             WNDCLASS wndclass;
420
421             /* Free window background brush */
422             if( p_vout->p_sys->hbrush )
423             {
424                 DeleteObject( p_vout->p_sys->hbrush );
425                 p_vout->p_sys->hbrush = NULL;
426             }
427
428             if( vlc_icon )
429             {
430                 DestroyIcon( vlc_icon );
431             }
432
433             /* Check why it failed. If it's because one already exists
434              * then fine, otherwise return with an error. */
435             if( !GetClassInfo( hInstance, "VLC DirectX", &wndclass ) )
436             {
437                 msg_Err( p_vout, "DirectXCreateWindow RegisterClass FAILED" );
438                 return VLC_EGENERIC;
439             }
440         }
441
442         /* When you create a window you give the dimensions you wish it to
443          * have. Unfortunatly these dimensions will include the borders and
444          * titlebar. We use the following function to find out the size of
445          * the window corresponding to the useable surface we want */
446         rect_window.top    = 10;
447         rect_window.left   = 10;
448         rect_window.right  = rect_window.left + p_vout->p_sys->i_window_width;
449         rect_window.bottom = rect_window.top + p_vout->p_sys->i_window_height;
450         AdjustWindowRect( &rect_window, WS_OVERLAPPEDWINDOW|WS_SIZEBOX, 0 );
451
452         /* Create the window */
453         p_vout->p_sys->hwnd =
454             CreateWindow( "VLC DirectX",             /* name of window class */
455                     VOUT_TITLE " (DirectX Output)", /* window title bar text */
456                     WS_OVERLAPPEDWINDOW | WS_SIZEBOX,        /* window style */
457                     CW_USEDEFAULT,                   /* default X coordinate */
458                     0,                               /* default Y coordinate */
459                     rect_window.right - rect_window.left,    /* window width */
460                     rect_window.bottom - rect_window.top,   /* window height */
461                     NULL,                                /* no parent window */
462                     NULL,                          /* no menu in this window */
463                     hInstance,            /* handle of this program instance */
464                     (LPVOID)p_vout );            /* send p_vout to WM_CREATE */
465
466         if( !p_vout->p_sys->hwnd )
467         {
468             msg_Warn( p_vout, "DirectXCreateWindow create window FAILED" );
469             return VLC_EGENERIC;
470         }
471     }
472
473     /* Append a "Always On Top" entry in the system menu */
474     hMenu = GetSystemMenu( p_vout->p_sys->hwnd, FALSE );
475     AppendMenu( hMenu, MF_SEPARATOR, 0, "" );
476     AppendMenu( hMenu, MF_STRING | MF_UNCHECKED,
477                        IDM_TOGGLE_ON_TOP, "Always on &Top" );
478
479     /* Now display the window */
480     ShowWindow( p_vout->p_sys->hwnd, SW_SHOW );
481
482     return VLC_SUCCESS;
483 }
484
485 /*****************************************************************************
486  * DirectXCloseWindow: close the window created by DirectXCreateWindow
487  *****************************************************************************
488  * This function returns all resources allocated by DirectXCreateWindow.
489  *****************************************************************************/
490 static void DirectXCloseWindow( vout_thread_t *p_vout )
491 {
492     msg_Dbg( p_vout, "DirectXCloseWindow" );
493
494     if( p_vout->p_sys->hwnd && !p_vout->p_sys->hparent )
495     {
496         DestroyWindow( p_vout->p_sys->hwnd );
497     }
498
499     p_vout->p_sys->hwnd = NULL;
500
501     /* We don't unregister the Window Class because it could lead to race
502      * conditions and it will be done anyway by the system when the app will
503      * exit */
504 }
505
506 /*****************************************************************************
507  * DirectXUpdateRects: update clipping rectangles
508  *****************************************************************************
509  * This function is called when the window position or size are changed, and
510  * its job is to update the source and destination RECTs used to display the
511  * picture.
512  *****************************************************************************/
513 void DirectXUpdateRects( vout_thread_t *p_vout, vlc_bool_t b_force )
514 {
515 #define rect_src p_vout->p_sys->rect_src
516 #define rect_src_clipped p_vout->p_sys->rect_src_clipped
517 #define rect_dest p_vout->p_sys->rect_dest
518 #define rect_dest_clipped p_vout->p_sys->rect_dest_clipped
519
520     int i_width, i_height, i_x, i_y;
521
522     RECT  rect;
523     POINT point;
524
525     /* Retrieve the window size */
526     GetClientRect( p_vout->p_sys->hwnd, &rect );
527
528     /* Retrieve the window position */
529     point.x = point.y = 0;
530     ClientToScreen( p_vout->p_sys->hwnd, &point );
531
532     /* If nothing changed, we can return */
533     if( !b_force
534          && p_vout->p_sys->i_window_width == rect.right
535          && p_vout->p_sys->i_window_height == rect.bottom
536          && p_vout->p_sys->i_window_x == point.x
537          && p_vout->p_sys->i_window_y == point.y )
538     {
539         return;
540     }
541
542     /* Update the window position and size */
543     p_vout->p_sys->i_window_x = point.x;
544     p_vout->p_sys->i_window_y = point.y;
545     p_vout->p_sys->i_window_width = rect.right;
546     p_vout->p_sys->i_window_height = rect.bottom;
547
548     vout_PlacePicture( p_vout, rect.right, rect.bottom,
549                        &i_x, &i_y, &i_width, &i_height );
550
551     /* Destination image position and dimensions */
552     rect_dest.left = point.x + i_x;
553     rect_dest.right = rect_dest.left + i_width;
554     rect_dest.top = point.y + i_y;
555     rect_dest.bottom = rect_dest.top + i_height;
556
557
558     /* UpdateOverlay directdraw function doesn't automatically clip to the
559      * display size so we need to do it otherwise it will fail */
560
561     /* Clip the destination window */
562     IntersectRect( &rect_dest_clipped,
563                    &rect_dest,
564                    &p_vout->p_sys->rect_display );
565
566 #if 0
567     msg_Dbg( p_vout, "DirectXUpdateRects image_dst_clipped coords:"
568                      " %i,%i,%i,%i",
569                      rect_dest_clipped.left, rect_dest_clipped.top,
570                      rect_dest_clipped.right, rect_dest_clipped.bottom );
571 #endif
572
573     /* the 2 following lines are to fix a bug when clicking on the desktop */
574     if( (rect_dest_clipped.right - rect_dest_clipped.left)==0 ||
575         (rect_dest_clipped.bottom - rect_dest_clipped.top)==0 )
576     {
577         SetRectEmpty( &rect_src_clipped );
578         return;
579     }
580
581     /* src image dimensions */
582     rect_src.left = 0;
583     rect_src.top = 0;
584     rect_src.right = p_vout->render.i_width;
585     rect_src.bottom = p_vout->render.i_height;
586
587     /* Clip the source image */
588     rect_src_clipped.left = (rect_dest_clipped.left - rect_dest.left) *
589       p_vout->render.i_width / (rect_dest.right - rect_dest.left);
590     rect_src_clipped.right = p_vout->render.i_width -
591       (rect_dest.right - rect_dest_clipped.right) * p_vout->render.i_width /
592       (rect_dest.right - rect_dest.left);
593     rect_src_clipped.top = (rect_dest_clipped.top - rect_dest.top) *
594       p_vout->render.i_height / (rect_dest.bottom - rect_dest.top);
595     rect_src_clipped.bottom = p_vout->render.i_height -
596       (rect_dest.bottom - rect_dest_clipped.bottom) * p_vout->render.i_height /
597       (rect_dest.bottom - rect_dest.top);
598
599 #if 0
600     msg_Dbg( p_vout, "DirectXUpdateRects image_src_clipped"
601                      " coords: %i,%i,%i,%i",
602                      rect_src_clipped.left, rect_src_clipped.top,
603                      rect_src_clipped.right, rect_src_clipped.bottom );
604 #endif
605
606     /* Signal the size change */
607     if( !p_vout->p_sys->p_event->b_die )
608     {
609         if( p_vout->p_sys->b_using_overlay )
610         {
611             DirectXUpdateOverlay( p_vout );
612         }
613         else
614         {
615             p_vout->p_sys->i_changes |= VOUT_SIZE_CHANGE;
616         }
617     }
618
619 #undef rect_src
620 #undef rect_src_clipped
621 #undef rect_dest
622 #undef rect_dest_clipped
623 }
624
625 /*****************************************************************************
626  * DirectXEventProc: This is the window event processing function.
627  *****************************************************************************
628  * On Windows, when you create a window you have to attach an event processing
629  * function to it. The aim of this function is to manage "Queued Messages" and
630  * "Nonqueued Messages".
631  * Queued Messages are those picked up and retransmitted by vout_Manage
632  * (using the GetMessage and DispatchMessage functions).
633  * Nonqueued Messages are those that Windows will send directly to this
634  * procedure (like WM_DESTROY, WM_WINDOWPOSCHANGED...)
635  *****************************************************************************/
636 static long FAR PASCAL DirectXEventProc( HWND hwnd, UINT message,
637                                          WPARAM wParam, LPARAM lParam )
638 {
639     vout_thread_t *p_vout;
640
641     if( message == WM_CREATE )
642     {
643         /* Store p_vout for future use */
644         p_vout = (vout_thread_t *)((CREATESTRUCT *)lParam)->lpCreateParams;
645         SetWindowLong( hwnd, GWL_USERDATA, (LONG)p_vout );
646     }
647     else
648     {
649         p_vout = (vout_thread_t *)GetWindowLong( hwnd, GWL_USERDATA );
650     }
651
652     switch( message )
653     {
654
655     case WM_WINDOWPOSCHANGED:
656         DirectXUpdateRects( p_vout, VLC_TRUE );
657         return 0;
658
659     /* the user wants to close the window */
660     case WM_CLOSE:
661         msg_Dbg( p_vout, "WinProc WM_CLOSE" );
662         /* exit application */
663         p_vout->p_vlc->b_die = VLC_TRUE;
664         return 0;
665
666     /* the window has been closed so shut down everything now */
667     case WM_DESTROY:
668         msg_Dbg( p_vout, "WinProc WM_DESTROY" );
669         /* just destroy the window */
670         PostQuitMessage( 0 );
671         return 0;
672
673     case WM_SYSCOMMAND:
674         switch (wParam)
675         {
676             case SC_SCREENSAVE:                     /* catch the screensaver */
677             case SC_MONITORPOWER:              /* catch the monitor turn-off */
678                 msg_Dbg( p_vout, "WinProc WM_SYSCOMMAND" );
679                 return 0;                  /* this stops them from happening */
680             case IDM_TOGGLE_ON_TOP:            /* toggle the "on top" status */
681             {
682                 vlc_value_t val;
683                 msg_Dbg( p_vout, "WinProc WM_SYSCOMMAND: IDM_TOGGLE_ON_TOP");
684
685                 /* Get the current value... */
686                 if( var_Get( p_vout, "directx-on-top", &val ) < 0 )
687                     return 0;
688                 /* ...and change it */
689                 val.b_bool = !val.b_bool;
690                 var_Set( p_vout, "directx-on-top", val );
691                 return 0;
692                 break;
693             }
694         }
695         break;
696
697     case WM_ERASEBKGND:
698         if( !p_vout->p_sys->b_using_overlay )
699         {
700             /* We want to eliminate unnecessary background redraws which create
701              * an annoying flickering */
702             int i_width, i_height, i_x, i_y;
703             RECT rect_temp;
704             GetClipBox( (HDC)wParam, &rect_temp );
705 #if 0
706             msg_Dbg( p_vout, "WinProc WM_ERASEBKGND %i,%i,%i,%i",
707                           rect_temp.left, rect_temp.top,
708                           rect_temp.right, rect_temp.bottom );
709 #endif
710             vout_PlacePicture( p_vout, p_vout->p_sys->i_window_width,
711                                p_vout->p_sys->i_window_height,
712                                &i_x, &i_y, &i_width, &i_height );
713             ExcludeClipRect( (HDC)wParam, i_x, i_y,
714                              i_x + i_width, i_y + i_height );
715         }
716         break;
717
718     default:
719         //msg_Dbg( p_vout, "WinProc WM Default %i", message );
720         break;
721     }
722
723     return DefWindowProc(hwnd, message, wParam, lParam);
724 }
725