]> git.sesse.net Git - vlc/blob - modules/video_output/kva.c
DirectDraw: remove unused include
[vlc] / modules / video_output / kva.c
1 /*****************************************************************************
2  * kva.c: KVA video output plugin for vlc
3  *****************************************************************************
4  * Copyright (C) 2010, 2011, 2012 VLC authors and VideoLAN
5  *
6  * Authors: KO Myung-Hun <komh@chollian.net>
7  *
8  * This program is free software; you can redistribute it and/or modify it
9  * under the terms of the GNU Lesser General Public License as published by
10  * the Free Software Foundation; either version 2.1 of the License, or
11  * (at your option) any later version.
12  *
13  * This program is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16  * GNU Lesser General Public License for more details.
17  *
18  * You should have received a copy of the GNU Lesser General Public License
19  * along with this program; if not, write to the Free Software Foundation,
20  * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
21  *****************************************************************************/
22
23 /*****************************************************************************
24  * Preamble
25  *****************************************************************************/
26 #ifdef HAVE_CONFIG_H
27 # include "config.h"
28 #endif
29
30 #include <vlc_common.h>
31 #include <vlc_plugin.h>
32 #include <vlc_vout_display.h>
33 #include <vlc_picture_pool.h>
34
35 #include <ctype.h>
36 #include <float.h>
37 #include <assert.h>
38
39 #include <fourcc.h>
40
41 #include <kva.h>
42
43 /*****************************************************************************
44  * Module descriptor
45  *****************************************************************************/
46 static int  Open ( vlc_object_t * );
47 static void Close( vlc_object_t * );
48
49 #define KVA_FIXT23_TEXT N_( \
50     "Enable a workaround for T23" )
51 #define KVA_FIXT23_LONGTEXT N_( \
52     "Enable this option if the diagonal stripes are displayed " \
53     "when the window size is equal to or smaller than the movie size." )
54 #define KVA_VIDEO_MODE_TEXT N_( \
55     "Video mode" )
56 #define KVA_VIDEO_MODE_LONGTEXT N_( \
57     "Select a proper video mode to be used by KVA." )
58
59 static const char *const ppsz_kva_video_mode[] = {
60     "auto", "snap", "wo", "vman", "dive" };
61 static const char *const ppsz_kva_video_mode_text[] = {
62     N_("Auto"), N_("SNAP"), N_("WarpOverlay!"), N_("VMAN"), N_("DIVE") };
63
64 vlc_module_begin ()
65     set_shortname( "KVA" )
66     set_category( CAT_VIDEO )
67     set_subcategory( SUBCAT_VIDEO_VOUT )
68     add_string( "kva-video-mode", ppsz_kva_video_mode[0], KVA_VIDEO_MODE_TEXT,
69                 KVA_VIDEO_MODE_LONGTEXT, false )
70         change_string_list( ppsz_kva_video_mode, ppsz_kva_video_mode_text )
71     add_bool( "kva-fixt23", false, KVA_FIXT23_TEXT, KVA_FIXT23_LONGTEXT, true )
72     set_description( N_("K Video Acceleration video output") )
73     set_capability( "vout display", 100 )
74     add_shortcut( "kva" )
75     set_callbacks( Open, Close )
76 vlc_module_end ()
77
78 /*****************************************************************************
79  * vout_display_sys_t: video output method descriptor
80  *****************************************************************************
81  * This structure is part of the video output thread descriptor.
82  * It describes the module specific properties of an output thread.
83  *****************************************************************************/
84 struct vout_display_sys_t
85 {
86     TID                tid;
87     HEV                ack_event;
88     int                i_result;
89     HAB                hab;
90     HMQ                hmq;
91     HWND               frame;
92     HWND               client;
93     KVASETUP           kvas;
94     KVACAPS            kvac;
95     LONG               i_screen_width;
96     LONG               i_screen_height;
97     bool               b_fixt23;
98     PFNWP              p_old_frame;
99     RECTL              client_rect;
100     vout_window_t     *parent_window;
101     HWND               parent;
102     RECTL              parent_rect;
103     picture_pool_t    *pool;
104     unsigned           button_pressed;
105     bool               is_mouse_hidden;
106     bool               is_on_top;
107 };
108
109 struct picture_sys_t
110 {
111     int i_chroma_shift;
112 };
113
114 /*****************************************************************************
115  * Local prototypes
116  *****************************************************************************/
117 static picture_pool_t *Pool   (vout_display_t *, unsigned);
118 static void            Display(vout_display_t *, picture_t *, subpicture_t * );
119 static int             Control(vout_display_t *, int, va_list);
120 static void            Manage (vout_display_t *);
121
122 static int  OpenDisplay ( vout_display_t *, video_format_t * );
123 static void CloseDisplay( vout_display_t * );
124
125 static int  KVALock( picture_t * );
126 static void KVAUnlock( picture_t * );
127
128 static void             MorphToPM     ( void );
129 static int              ConvertKey    ( USHORT );
130 static MRESULT EXPENTRY MyFrameWndProc( HWND, ULONG, MPARAM, MPARAM );
131 static MRESULT EXPENTRY WndProc       ( HWND, ULONG, MPARAM, MPARAM );
132
133 #define WC_VLC_KVA "WC_VLC_KVA"
134
135 #define COLOR_KEY 0x0F0F0F
136
137 #define WM_VLC_MANAGE               ( WM_USER + 1 )
138 #define WM_VLC_FULLSCREEN_CHANGE    ( WM_USER + 2 )
139 #define WM_VLC_SIZE_CHANGE          ( WM_USER + 3 )
140
141 static const char *psz_video_mode[ 4 ] = {"DIVE", "WarpOverlay!", "SNAP",
142                                           "VMAN"};
143
144 static void PMThread( void *arg )
145 {
146     vout_display_t *vd = ( vout_display_t * )arg;
147     vout_display_sys_t * sys = vd->sys;
148     ULONG i_frame_flags;
149     QMSG qm;
150     char *psz_mode;
151     ULONG i_kva_mode;
152
153     /* */
154     video_format_t fmt = vd->fmt;
155
156     /* */
157     vout_display_info_t info = vd->info;
158     info.is_slow = false;
159     info.has_double_click = true;
160     info.has_hide_mouse = false;
161     info.has_pictures_invalid = false;
162
163     MorphToPM();
164
165     sys->hab = WinInitialize( 0 );
166     sys->hmq = WinCreateMsgQueue( sys->hab, 0);
167
168     WinRegisterClass( sys->hab,
169                       WC_VLC_KVA,
170                       WndProc,
171                       CS_SIZEREDRAW | CS_MOVENOTIFY,
172                       sizeof( PVOID ));
173
174     sys->b_fixt23 = var_CreateGetBool( vd, "kva-fixt23");
175
176     if( !sys->b_fixt23 )
177     {
178         vout_window_cfg_t wnd_cfg;
179
180         wnd_cfg.is_standalone = false;
181         wnd_cfg.type          = VOUT_WINDOW_TYPE_HWND;
182         wnd_cfg.x             = var_InheritInteger(vd, "video-x");
183         wnd_cfg.y             = var_InheritInteger(vd, "video-y");
184         wnd_cfg.width         = vd->cfg->display.width;
185         wnd_cfg.height        = vd->cfg->display.height;
186
187         /* If an external window was specified, we'll draw in it. */
188         sys->parent_window =
189             vout_display_NewWindow( vd, &wnd_cfg );
190     }
191
192     if( sys->parent_window )
193     {
194         sys->parent = ( HWND )sys->parent_window->handle.hwnd;
195
196         ULONG i_style = WinQueryWindowULong( sys->parent, QWL_STYLE );
197         WinSetWindowULong( sys->parent, QWL_STYLE,
198                            i_style | WS_CLIPCHILDREN );
199
200         i_frame_flags = FCF_TITLEBAR;
201     }
202     else
203     {
204         sys->parent = HWND_DESKTOP;
205
206         i_frame_flags = FCF_SYSMENU    | FCF_TITLEBAR | FCF_MINMAX |
207                         FCF_SIZEBORDER | FCF_TASKLIST;
208     }
209
210     sys->frame =
211         WinCreateStdWindow( sys->parent,      /* parent window handle */
212                             WS_VISIBLE,       /* frame window style */
213                             &i_frame_flags,   /* window style */
214                             WC_VLC_KVA,       /* class name */
215                             "",               /* window title */
216                             0L,               /* default client style */
217                             NULLHANDLE,       /* resource in exe file */
218                             1,                /* frame window id */
219                             &sys->client );   /* client window handle */
220
221     if( sys->frame == NULLHANDLE )
222     {
223         msg_Err( vd, "cannot create a frame window");
224
225         goto exit_frame;
226     }
227
228     WinSetWindowPtr( sys->client, 0, vd );
229
230     if( !sys->parent_window )
231     {
232         WinSetWindowPtr( sys->frame, 0, vd );
233         sys->p_old_frame = WinSubclassWindow( sys->frame, MyFrameWndProc );
234     }
235
236     psz_mode = var_CreateGetString( vd, "kva-video-mode" );
237
238     i_kva_mode = KVAM_AUTO;
239     if( strcmp( psz_mode, "snap" ) == 0 )
240         i_kva_mode = KVAM_SNAP;
241     else if( strcmp( psz_mode, "wo" ) == 0 )
242         i_kva_mode = KVAM_WO;
243     else if( strcmp( psz_mode, "vman" ) == 0 )
244         i_kva_mode = KVAM_VMAN;
245     else if( strcmp( psz_mode, "dive" ) == 0 )
246         i_kva_mode = KVAM_DIVE;
247
248     free( psz_mode );
249
250     if( kvaInit( i_kva_mode, sys->client, COLOR_KEY ))
251     {
252         msg_Err( vd, "cannot initialize KVA");
253
254         goto exit_kva_init;
255     }
256
257     kvaCaps( &sys->kvac );
258
259     msg_Dbg( vd, "selected video mode = %s",
260              psz_video_mode[ sys->kvac.ulMode - 1 ]);
261
262     if( OpenDisplay( vd, &fmt ) )
263     {
264         msg_Err( vd, "cannot open display");
265
266         goto exit_open_display;
267     }
268
269     if( vd->cfg->is_fullscreen )
270     {
271         if( sys->parent_window )
272             vout_window_SetFullScreen(sys->parent_window, true);
273         else
274             WinPostMsg( sys->client, WM_VLC_FULLSCREEN_CHANGE,
275                         MPFROMLONG( true ), 0 );
276     }
277
278     kvaDisableScreenSaver();
279
280     /* Setup vout_display now that everything is fine */
281     vd->fmt     = fmt;
282     vd->info    = info;
283
284     vd->pool    = Pool;
285     vd->prepare = NULL;
286     vd->display = Display;
287     vd->control = Control;
288     vd->manage  = Manage;
289
290     /* Prevent SIG_FPE */
291     _control87(MCW_EM, MCW_EM);
292
293     sys->i_result = VLC_SUCCESS;
294     DosPostEventSem( sys->ack_event );
295
296     if( !sys->parent_window )
297         WinSetVisibleRegionNotify( sys->frame, TRUE );
298
299     while( WinGetMsg( sys->hab, &qm, NULLHANDLE, 0, 0 ))
300         WinDispatchMsg( sys->hab, &qm );
301
302     if( !sys->parent_window )
303         WinSetVisibleRegionNotify( sys->frame, FALSE );
304
305     kvaEnableScreenSaver();
306
307     CloseDisplay( vd );
308
309     /* fall through */
310
311 exit_open_display :
312     kvaDone();
313
314 exit_kva_init :
315     if( !sys->parent_window )
316         WinSubclassWindow( sys->frame, sys->p_old_frame );
317
318     WinDestroyWindow( sys->frame );
319
320 exit_frame :
321     vout_display_DeleteWindow( vd, sys->parent_window );
322
323     if( sys->is_mouse_hidden )
324         WinShowPointer( HWND_DESKTOP, TRUE );
325
326     WinDestroyMsgQueue( sys->hmq );
327     WinTerminate( sys->hab );
328
329     sys->i_result = VLC_EGENERIC;
330     DosPostEventSem( sys->ack_event );
331 }
332
333 /**
334  * This function initializes KVA vout method.
335  */
336 static int Open ( vlc_object_t *object )
337 {
338     vout_display_t *vd = (vout_display_t *)object;
339     vout_display_sys_t *sys;
340
341     vd->sys = sys = calloc( 1, sizeof( *sys ));
342     if( !sys )
343         return VLC_ENOMEM;
344
345     DosCreateEventSem( NULL, &sys->ack_event, 0, FALSE );
346
347     sys->tid = _beginthread( PMThread, NULL, 1024 * 1024, vd );
348     DosWaitEventSem( sys->ack_event, SEM_INDEFINITE_WAIT );
349
350     if( sys->i_result != VLC_SUCCESS )
351     {
352         DosCloseEventSem( sys->ack_event );
353
354         free( sys );
355
356         return VLC_EGENERIC;
357     }
358
359     return VLC_SUCCESS;
360 }
361
362 /*****************************************************************************
363  * Close: destroy KVA video thread output method
364  *****************************************************************************
365  * Terminate an output method created by Open
366  *****************************************************************************/
367 static void Close ( vlc_object_t *object )
368 {
369     vout_display_t * vd = (vout_display_t *)object;
370     vout_display_sys_t * sys = vd->sys;
371
372     WinPostQueueMsg( sys->hmq, WM_QUIT, 0, 0 );
373
374     DosWaitThread( &sys->tid, DCWW_WAIT );
375
376     if( sys->pool )
377         picture_pool_Delete( sys->pool );
378
379     DosCloseEventSem( sys->ack_event );
380
381     free( sys );
382 }
383
384 /**
385  * Return a pool of direct buffers
386  */
387 static picture_pool_t *Pool(vout_display_t *vd, unsigned count)
388 {
389     vout_display_sys_t *sys = vd->sys;
390     VLC_UNUSED(count);
391
392     return sys->pool;
393 }
394
395 /*****************************************************************************
396  * Display: displays previously rendered output
397  *****************************************************************************
398  * This function sends the currently rendered image to the display.
399  *****************************************************************************/
400 static void Display( vout_display_t *vd, picture_t *picture,
401                      subpicture_t *subpicture )
402 {
403     VLC_UNUSED( vd );
404     VLC_UNUSED( subpicture );
405
406     picture_Release( picture );
407 }
408
409 /*****************************************************************************
410  * Manage: handle Sys events
411  *****************************************************************************
412  * This function should be called regularly by video output thread. It returns
413  * a non null value if an error occurred.
414  *****************************************************************************/
415 static void Manage( vout_display_t *vd )
416 {
417     vout_display_sys_t * sys = vd->sys;
418
419     /* Let a window procedure manage instead because if resizing a frame window
420      * here, WM_SIZE is not sent to its child window.
421      * Maybe, is this due to the different threads ? */
422     WinPostMsg( sys->client, WM_VLC_MANAGE, 0, 0 );
423 }
424
425 /*****************************************************************************
426  * Control: control facility for the vout
427  *****************************************************************************/
428 static int Control( vout_display_t *vd, int query, va_list args )
429 {
430     vout_display_sys_t *sys = vd->sys;
431
432     switch (query)
433     {
434     case VOUT_DISPLAY_HIDE_MOUSE:
435     {
436         POINTL ptl;
437
438         WinQueryPointerPos( HWND_DESKTOP, &ptl );
439         if( !sys->is_mouse_hidden &&
440             WinWindowFromPoint( HWND_DESKTOP, &ptl, TRUE ) == sys->client )
441         {
442             WinShowPointer( HWND_DESKTOP, FALSE );
443             sys->is_mouse_hidden = true;
444         }
445
446         return VLC_SUCCESS;
447     }
448
449     case VOUT_DISPLAY_CHANGE_FULLSCREEN:
450     {
451         vout_display_cfg_t cfg = *va_arg(args, const vout_display_cfg_t *);
452
453         if( sys->parent_window )
454             vout_window_SetFullScreen(sys->parent_window, cfg.is_fullscreen);
455         else
456             WinPostMsg( sys->client, WM_VLC_FULLSCREEN_CHANGE,
457                         MPFROMLONG( cfg.is_fullscreen ), 0 );
458
459         return VLC_SUCCESS;
460     }
461
462     case VOUT_DISPLAY_CHANGE_WINDOW_STATE:
463     {
464         const unsigned state = va_arg( args, unsigned );
465         const bool is_on_top = (state & VOUT_WINDOW_STATE_ABOVE) != 0;
466
467         if( sys->parent_window )
468         {
469             if( vout_window_SetState( sys->parent_window, state ))
470                 return VLC_EGENERIC;
471         }
472         else if( is_on_top )
473             WinSetWindowPos( sys->frame, HWND_TOP, 0, 0, 0, 0, SWP_ZORDER );
474
475         sys->is_on_top = is_on_top;
476
477         return VLC_SUCCESS;
478     }
479
480     case VOUT_DISPLAY_CHANGE_DISPLAY_SIZE:
481     {
482         const vout_display_cfg_t *cfg = va_arg(args, const vout_display_cfg_t *);
483         bool  is_forced = va_arg(args, int);
484
485         if( is_forced )
486         {
487             if( sys->parent_window )
488             {
489                 vout_window_SetSize(sys->parent_window,
490                                     cfg->display.width, cfg->display.height);
491             }
492             else
493                 WinPostMsg( sys->client, WM_VLC_SIZE_CHANGE,
494                             MPFROMLONG( cfg->display.width ),
495                             MPFROMLONG( cfg->display.height ));
496         }
497
498         return VLC_SUCCESS;
499     }
500
501     case VOUT_DISPLAY_CHANGE_SOURCE_ASPECT:
502     case VOUT_DISPLAY_CHANGE_SOURCE_CROP:
503     {
504         const video_format_t *source = va_arg(args, const video_format_t *);
505
506         if( query == VOUT_DISPLAY_CHANGE_SOURCE_ASPECT )
507         {
508             sys->kvas.ulAspectWidth  = ( int64_t )source->i_width *
509                                        source->i_sar_num / source->i_sar_den;
510             sys->kvas.ulAspectHeight = source->i_height;
511         }
512         else
513         {
514             sys->kvas.rclSrcRect.xLeft   = source->i_x_offset;
515             sys->kvas.rclSrcRect.yTop    = source->i_y_offset;
516             sys->kvas.rclSrcRect.xRight  = source->i_x_offset +
517                                            source->i_visible_width;
518             sys->kvas.rclSrcRect.yBottom = source->i_y_offset +
519                                            source->i_visible_height;
520         }
521
522         kvaSetup( &sys->kvas );
523
524         return VLC_SUCCESS;
525     }
526
527     case VOUT_DISPLAY_RESET_PICTURES:
528     case VOUT_DISPLAY_CHANGE_DISPLAY_FILLED:
529     case VOUT_DISPLAY_CHANGE_ZOOM:
530     case VOUT_DISPLAY_GET_OPENGL:
531         /* TODO */
532         break;
533     }
534
535     msg_Err(vd, "Unsupported query(=%d) in vout display KVA", query);
536     return VLC_EGENERIC;
537 }
538
539 /* following functions are local */
540
541 /*****************************************************************************
542  * OpenDisplay: open and initialize KVA device
543  *****************************************************************************
544  * Open and initialize display according to preferences specified in the vout
545  * thread fields.
546  *****************************************************************************/
547 static int OpenDisplay( vout_display_t *vd, video_format_t *fmt )
548 {
549     vout_display_sys_t * sys = vd->sys;
550     const vlc_fourcc_t *fallback;
551     bool b_hw_accel = 0;
552     FOURCC i_kva_fourcc;
553     int i_chroma_shift;
554     char sz_title[ 256 ];
555     RECTL rcl;
556     int w, h;
557
558     msg_Dbg( vd, "render chroma = %4.4s", ( const char * )&fmt->i_chroma );
559
560     for( int pass = 0; pass < 2 && !b_hw_accel; pass++ )
561     {
562         fallback = ( pass == 0 ) ? vlc_fourcc_GetYUVFallback( fmt->i_chroma ) :
563                                    vlc_fourcc_GetRGBFallback( fmt->i_chroma );
564
565         for( int i = 0; fallback[ i ]; i++ )
566         {
567             switch( fallback[ i ])
568             {
569                 case VLC_CODEC_YV12:
570                     b_hw_accel = sys->kvac.ulInputFormatFlags & KVAF_YV12;
571                     i_kva_fourcc = FOURCC_YV12;
572                     i_chroma_shift = 1;
573                     break;
574
575                 case VLC_CODEC_YUYV:
576                     b_hw_accel = sys->kvac.ulInputFormatFlags & KVAF_YUY2;
577                     i_kva_fourcc = FOURCC_Y422;
578                     i_chroma_shift = 0;
579                     break;
580
581                 case VLC_CODEC_YV9:
582                     b_hw_accel = sys->kvac.ulInputFormatFlags & KVAF_YVU9;
583                     i_kva_fourcc = FOURCC_YVU9;
584                     i_chroma_shift = 2;
585                     break;
586
587                 case VLC_CODEC_RGB32:
588                     b_hw_accel = sys->kvac.ulInputFormatFlags & KVAF_BGR32;
589                     i_kva_fourcc = FOURCC_BGR4;
590                     i_chroma_shift = 0;
591                     break;
592
593                 case VLC_CODEC_RGB24:
594                     b_hw_accel = sys->kvac.ulInputFormatFlags & KVAF_BGR24;
595                     i_kva_fourcc = FOURCC_BGR3;
596                     i_chroma_shift = 0;
597                     break;
598
599                 case VLC_CODEC_RGB16:
600                     b_hw_accel = sys->kvac.ulInputFormatFlags & KVAF_BGR16;
601                     i_kva_fourcc = FOURCC_R565;
602                     i_chroma_shift = 0;
603                     break;
604
605                 case VLC_CODEC_RGB15:
606                     b_hw_accel = sys->kvac.ulInputFormatFlags & KVAF_BGR15;
607                     i_kva_fourcc = FOURCC_R555;
608                     i_chroma_shift = 0;
609                     break;
610             }
611
612             if( b_hw_accel )
613             {
614                 fmt->i_chroma = fallback[ i ];
615                 break;
616             }
617         }
618     }
619
620     if( !b_hw_accel )
621     {
622         msg_Err( vd, "Ooops. There is no fourcc supported by KVA at all.");
623
624         return VLC_EGENERIC;
625     }
626
627     /* Set the RGB masks */
628     fmt->i_rmask = sys->kvac.ulRMask;
629     fmt->i_gmask = sys->kvac.ulGMask;
630     fmt->i_bmask = sys->kvac.ulBMask;
631
632     msg_Dbg( vd, "output chroma = %4.4s", ( const char * )&fmt->i_chroma );
633     msg_Dbg( vd, "KVA chroma = %4.4s", ( const char * )&i_kva_fourcc );
634
635     w = vd->source.i_width;
636     h = vd->source.i_height;
637
638     sys->kvas.ulLength           = sizeof( KVASETUP );
639     sys->kvas.szlSrcSize.cx      = w;
640     sys->kvas.szlSrcSize.cy      = h;
641     sys->kvas.rclSrcRect.xLeft   = 0;
642     sys->kvas.rclSrcRect.yTop    = 0;
643     sys->kvas.rclSrcRect.xRight  = w;
644     sys->kvas.rclSrcRect.yBottom = h;
645     sys->kvas.ulRatio            = KVAR_FORCEANY;
646     sys->kvas.ulAspectWidth      = w;
647     sys->kvas.ulAspectHeight     = h;
648     sys->kvas.fccSrcColor        = i_kva_fourcc;
649     sys->kvas.fDither            = TRUE;
650
651     if( kvaSetup( &sys->kvas ))
652     {
653         msg_Err( vd, "cannot set up KVA");
654
655         return VLC_EGENERIC;
656     }
657
658     /* Create the associated picture */
659     picture_sys_t *picsys = malloc( sizeof( *picsys ) );
660     if( picsys == NULL )
661         return VLC_ENOMEM;
662     picsys->i_chroma_shift = i_chroma_shift;
663
664     picture_resource_t resource = { .p_sys = picsys };
665     picture_t *picture = picture_NewFromResource( fmt, &resource );
666     if( !picture )
667     {
668         free( picsys );
669         return VLC_ENOMEM;
670     }
671
672     /* Wrap it into a picture pool */
673     picture_pool_configuration_t pool_cfg;
674     memset( &pool_cfg, 0, sizeof( pool_cfg ));
675     pool_cfg.picture_count = 1;
676     pool_cfg.picture       = &picture;
677     pool_cfg.lock          = KVALock;
678     pool_cfg.unlock        = KVAUnlock;
679
680     sys->pool = picture_pool_NewExtended( &pool_cfg );
681     if( !sys->pool )
682     {
683         picture_Release( picture );
684         return VLC_ENOMEM;
685     }
686
687     if (vd->cfg->display.title)
688         snprintf( sz_title, sizeof( sz_title ), "%s", vd->cfg->display.title );
689     else
690         snprintf( sz_title, sizeof( sz_title ),
691                   "%s (%4.4s to %4.4s - %s mode KVA output)",
692                   VOUT_TITLE,
693                   ( char * )&vd->fmt.i_chroma,
694                   ( char * )&sys->kvas.fccSrcColor,
695                   psz_video_mode[ sys->kvac.ulMode - 1 ]);
696     WinSetWindowText( sys->frame, sz_title );
697
698     sys->i_screen_width  = WinQuerySysValue( HWND_DESKTOP, SV_CXSCREEN );
699     sys->i_screen_height = WinQuerySysValue( HWND_DESKTOP, SV_CYSCREEN );
700
701     if( sys->parent_window )
702         WinQueryWindowRect( sys->parent, &sys->client_rect );
703     else
704     {
705         sys->client_rect.xLeft   = ( sys->i_screen_width  - w ) / 2;
706         sys->client_rect.yBottom = ( sys->i_screen_height - h ) / 2 ;
707         sys->client_rect.xRight  = sys->client_rect.xLeft   + w;
708         sys->client_rect.yTop    = sys->client_rect.yBottom + h;
709     }
710
711     rcl = sys->client_rect;
712
713     WinCalcFrameRect( sys->frame, &rcl, FALSE);
714
715     WinSetWindowPos( sys->frame, HWND_TOP,
716                      rcl.xLeft, rcl.yBottom,
717                      rcl.xRight - rcl.xLeft, rcl.yTop - rcl.yBottom,
718                      SWP_MOVE | SWP_SIZE | SWP_ZORDER | SWP_SHOW |
719                      SWP_ACTIVATE );
720
721     return VLC_SUCCESS;
722 }
723
724 /*****************************************************************************
725  * CloseDisplay: close and reset KVA device
726  *****************************************************************************
727  * This function returns all resources allocated by OpenDisplay and restore
728  * the original state of the device.
729  *****************************************************************************/
730 static void CloseDisplay( vout_display_t *vd )
731 {
732     VLC_UNUSED( vd );
733 }
734
735 static int KVALock( picture_t *picture )
736 {
737     picture_sys_t *picsys = picture->p_sys;
738     PVOID kva_buffer;
739     ULONG kva_bpl;
740
741     if( kvaLockBuffer( &kva_buffer, &kva_bpl ))
742         return VLC_EGENERIC;
743
744     /* Packed or Y plane */
745     picture->p->p_pixels = ( uint8_t * )kva_buffer;
746     picture->p->i_pitch  = kva_bpl;
747     picture->p->i_lines  = picture->format.i_height;
748
749     /* Other planes */
750     for( int n = 1; n < picture->i_planes; n++ )
751     {
752         const plane_t *o = &picture->p[n-1];
753         plane_t *p = &picture->p[n];
754
755         p->p_pixels = o->p_pixels + o->i_lines * o->i_pitch;
756         p->i_pitch  = kva_bpl >> picsys->i_chroma_shift;
757         p->i_lines  = picture->format.i_height >> picsys->i_chroma_shift;
758     }
759
760     return VLC_SUCCESS;
761 }
762
763 static void KVAUnlock( picture_t *picture )
764 {
765     VLC_UNUSED( picture );
766
767     kvaUnlockBuffer();
768 }
769
770 static void MorphToPM( void )
771 {
772     PPIB pib;
773
774     DosGetInfoBlocks(NULL, &pib);
775
776     /* Change flag from VIO to PM */
777     if (pib->pib_ultype == 2)
778         pib->pib_ultype = 3;
779 }
780
781 /*****************************************************************************
782  * Key events handling
783  *****************************************************************************/
784 static const struct
785 {
786     USHORT i_pmkey;
787     int    i_vlckey;
788 } pmkeys_to_vlckeys[] =
789 {
790     { VK_LEFT, KEY_LEFT },
791     { VK_RIGHT, KEY_RIGHT },
792     { VK_UP, KEY_UP },
793     { VK_DOWN, KEY_DOWN },
794     { VK_SPACE, ' ' },
795     { VK_NEWLINE, KEY_ENTER },
796     { VK_ENTER, KEY_ENTER },
797     { VK_F1, KEY_F1 },
798     { VK_F2, KEY_F2 },
799     { VK_F3, KEY_F3 },
800     { VK_F4, KEY_F4 },
801     { VK_F5, KEY_F5 },
802     { VK_F6, KEY_F6 },
803     { VK_F7, KEY_F7 },
804     { VK_F8, KEY_F8 },
805     { VK_F9, KEY_F9 },
806     { VK_F10, KEY_F10 },
807     { VK_F11, KEY_F11 },
808     { VK_F12, KEY_F12 },
809     { VK_HOME, KEY_HOME },
810     { VK_END, KEY_END },
811     { VK_INSERT, KEY_INSERT },
812     { VK_DELETE, KEY_DELETE },
813 /*
814     Not supported
815     {, KEY_MENU },
816 */
817     { VK_ESC, KEY_ESC },
818     { VK_PAGEUP, KEY_PAGEUP },
819     { VK_PAGEDOWN, KEY_PAGEDOWN },
820     { VK_TAB, KEY_TAB },
821     { VK_BACKSPACE, KEY_BACKSPACE },
822 /*
823     Not supported
824     {, KEY_MOUSEWHEELUP },
825     {, KEY_MOUSEWHEELDOWN },
826     {, KEY_MOUSEWHEELLEFT },
827     {, KEY_MOUSEWHEELRIGHT },
828
829     {, KEY_BROWSER_BACK },
830     {, KEY_BROWSER_FORWARD },
831     {, KEY_BROWSER_REFRESH },
832     {, KEY_BROWSER_STOP },
833     {, KEY_BROWSER_SEARCH },
834     {, KEY_BROWSER_FAVORITES },
835     {, KEY_BROWSER_HOME },
836     {, KEY_VOLUME_MUTE },
837     {, KEY_VOLUME_DOWN },
838     {, KEY_VOLUME_UP },
839     {, KEY_MEDIA_NEXT_TRACK },
840     {, KEY_MEDIA_PREV_TRACK },
841     {, KEY_MEDIA_STOP },
842     {, KEY_MEDIA_PLAY_PAUSE },
843 */
844
845     { 0, 0 }
846 };
847
848 static int ConvertKey( USHORT i_pmkey )
849 {
850     int i;
851     for( i = 0; pmkeys_to_vlckeys[ i ].i_pmkey != 0; i++ )
852     {
853         if( pmkeys_to_vlckeys[ i ].i_pmkey == i_pmkey )
854             return pmkeys_to_vlckeys[ i ].i_vlckey;
855     }
856     return 0;
857 }
858
859 static MRESULT EXPENTRY MyFrameWndProc( HWND hwnd, ULONG msg, MPARAM mp1,
860                                         MPARAM mp2 )
861 {
862     vout_display_t *vd = WinQueryWindowPtr( hwnd, 0 );
863     vout_display_sys_t *sys = vd->sys;
864
865     switch( msg )
866     {
867         case WM_QUERYTRACKINFO :
868         {
869             MRESULT mr;
870
871             mr = sys->p_old_frame( hwnd, msg, mp1, mp2 );
872             if( !sys->b_fixt23 )
873                 return mr;
874
875             PTRACKINFO pti = ( PTRACKINFO )mp2;
876             RECTL      rcl;
877
878             pti->rclBoundary.xLeft   = 0;
879             pti->rclBoundary.yBottom = 0;
880             pti->rclBoundary.xRight  = sys->i_screen_width;
881             pti->rclBoundary.yTop    = sys->i_screen_height;
882
883             WinCalcFrameRect( hwnd, &pti->rclBoundary, FALSE );
884
885             pti->ptlMaxTrackSize.x = pti->rclBoundary.xRight -
886                                      pti->rclBoundary.xLeft;
887             pti->ptlMaxTrackSize.y = pti->rclBoundary.yTop -
888                                      pti->rclBoundary.yBottom;
889
890             rcl.xLeft   = 0;
891             rcl.yBottom = 0;
892             rcl.xRight  = sys->kvas.szlSrcSize.cx + 1;
893             rcl.yTop    = sys->kvas.szlSrcSize.cy + 1;
894
895             WinCalcFrameRect( hwnd, &rcl, FALSE );
896
897             pti->ptlMinTrackSize.x = rcl.xRight - rcl.xLeft;
898             pti->ptlMinTrackSize.y = rcl.yTop - rcl.yBottom;
899
900             return MRFROMLONG( TRUE );
901         }
902
903         case WM_ADJUSTWINDOWPOS :
904         {
905             if( !sys->b_fixt23 )
906                 break;
907
908             PSWP  pswp = ( PSWP )mp1;
909
910             if( pswp->fl & SWP_SIZE )
911             {
912                 RECTL rcl;
913
914                 rcl.xLeft   = pswp->x;
915                 rcl.yBottom = pswp->y;
916                 rcl.xRight  = rcl.xLeft + pswp->cx;
917                 rcl.yTop    = rcl.yBottom + pswp->cy;
918
919                 WinCalcFrameRect( hwnd, &rcl, TRUE );
920
921                 if( rcl.xRight - rcl.xLeft <= sys->kvas.szlSrcSize.cx )
922                     rcl.xRight = rcl.xLeft + ( sys->kvas.szlSrcSize.cx + 1 );
923
924                 if( rcl.yTop - rcl.yBottom <= sys->kvas.szlSrcSize.cy )
925                     rcl.yTop = rcl.yBottom + ( sys->kvas.szlSrcSize.cy + 1 );
926
927                 if( rcl.xRight - rcl.xLeft > sys->i_screen_width )
928                 {
929                     rcl.xLeft  = 0;
930                     rcl.xRight = sys->i_screen_width;
931                 }
932
933                 if( rcl.yTop - rcl.yBottom > sys->i_screen_height )
934                 {
935                     rcl.yBottom = 0;
936                     rcl.yTop    = sys->i_screen_height;
937                 }
938
939                 WinCalcFrameRect( hwnd, &rcl, FALSE );
940
941                 if( pswp->x != rcl.xLeft || pswp->y != rcl.yBottom )
942                     pswp->fl |= SWP_MOVE;
943
944                 pswp->x = rcl.xLeft;
945                 pswp->y = rcl.yBottom;
946
947                 pswp->cx = rcl.xRight - rcl.xLeft;
948                 pswp->cy = rcl.yTop - rcl.yBottom;
949             }
950
951             break;
952         }
953
954         //case WM_VRNDISABLED :
955         case WM_VRNENABLED :
956             if( !vd->cfg->is_fullscreen && sys->is_on_top )
957                 WinSetWindowPos( hwnd, HWND_TOP, 0, 0, 0, 0, SWP_ZORDER );
958             break;
959     }
960
961     return sys->p_old_frame( hwnd, msg, mp1, mp2 );
962 }
963
964 static void MousePressed( vout_display_t *vd, HWND hwnd, unsigned button )
965 {
966     if( WinQueryFocus( HWND_DESKTOP ) != hwnd )
967         WinSetFocus( HWND_DESKTOP, hwnd );
968
969     if( !vd->sys->button_pressed )
970         WinSetCapture( HWND_DESKTOP, hwnd );
971
972     vd->sys->button_pressed |= 1 << button;
973
974     vout_display_SendEventMousePressed( vd, button );
975 }
976
977 static void MouseReleased( vout_display_t *vd, unsigned button )
978 {
979     vd->sys->button_pressed &= ~(1 << button);
980     if( !vd->sys->button_pressed )
981         WinSetCapture( HWND_DESKTOP, NULLHANDLE );
982
983     vout_display_SendEventMouseReleased( vd, button );
984 }
985
986 #define WM_MOUSELEAVE   0x41F
987
988 static MRESULT EXPENTRY WndProc( HWND hwnd, ULONG msg, MPARAM mp1, MPARAM mp2 )
989 {
990     vout_display_t * vd = WinQueryWindowPtr( hwnd, 0 );
991     MRESULT result = ( MRESULT )TRUE;
992
993     if ( !vd )
994         return WinDefWindowProc( hwnd, msg, mp1, mp2 );
995
996     vout_display_sys_t * sys = vd->sys;
997     RECTL rcl;
998     SWP   swp;
999
1000     if ( sys->is_mouse_hidden &&
1001          ((msg >= WM_MOUSEFIRST    && msg <= WM_MOUSELAST) ||
1002           (msg >= WM_EXTMOUSEFIRST && msg <= WM_EXTMOUSELAST) ||
1003            msg == WM_MOUSELEAVE))
1004     {
1005         WinShowPointer(HWND_DESKTOP, TRUE);
1006         sys->is_mouse_hidden = false;
1007     }
1008
1009     switch( msg )
1010     {
1011         /* the user wants to close the window */
1012         case WM_CLOSE:
1013             vout_display_SendEventClose(vd);
1014             result = 0;
1015             break;
1016
1017         case WM_MOUSEMOVE :
1018         {
1019             SHORT i_mouse_x = SHORT1FROMMP( mp1 );
1020             SHORT i_mouse_y = SHORT2FROMMP( mp1 );
1021             RECTL movie_rect;
1022             int   i_movie_width, i_movie_height;
1023             int   i_src_width, i_src_height;
1024
1025             /* Get a current movie area */
1026             kvaAdjustDstRect( &sys->kvas.rclSrcRect, &movie_rect );
1027             i_movie_width = movie_rect.xRight - movie_rect.xLeft;
1028             i_movie_height = movie_rect.yTop - movie_rect.yBottom;
1029
1030             i_src_width =  sys->kvas.rclSrcRect.xRight -
1031                            sys->kvas.rclSrcRect.xLeft;
1032             i_src_height = sys->kvas.rclSrcRect.yBottom -
1033                            sys->kvas.rclSrcRect.yTop;
1034
1035             int x = ( i_mouse_x - movie_rect.xLeft ) *
1036                     i_src_width / i_movie_width +
1037                     sys->kvas.rclSrcRect.xLeft;
1038             int y = ( i_mouse_y - movie_rect.yBottom ) *
1039                     i_src_height / i_movie_height;
1040
1041             /* Invert Y coordinate and add y offset */
1042             y = ( i_src_height - y ) + sys->kvas.rclSrcRect.yTop;;
1043
1044             vout_display_SendEventMouseMoved(vd, x, y);
1045
1046             result = WinDefWindowProc( hwnd, msg, mp1,mp2 );
1047             break;
1048         }
1049
1050         case WM_BUTTON1DOWN :
1051             MousePressed( vd, hwnd, MOUSE_BUTTON_LEFT );
1052             break;
1053
1054         case WM_BUTTON2DOWN :
1055             MousePressed( vd, hwnd, MOUSE_BUTTON_RIGHT );
1056             break;
1057
1058         case WM_BUTTON3DOWN :
1059             MousePressed( vd, hwnd, MOUSE_BUTTON_CENTER );
1060             break;
1061
1062         case WM_BUTTON1UP :
1063             MouseReleased( vd, MOUSE_BUTTON_LEFT );
1064             break;
1065
1066         case WM_BUTTON2UP :
1067             MouseReleased( vd, MOUSE_BUTTON_RIGHT );
1068             break;
1069
1070         case WM_BUTTON3UP :
1071             MouseReleased( vd, MOUSE_BUTTON_CENTER );
1072             break;
1073
1074         case WM_BUTTON1DBLCLK :
1075             vout_display_SendEventMouseDoubleClick(vd);
1076             break;
1077
1078         case WM_TRANSLATEACCEL :
1079             /* We have no accelerator table at all */
1080             result = ( MRESULT )FALSE;
1081             break;
1082
1083         case WM_CHAR :
1084         {
1085             USHORT i_flags = SHORT1FROMMP( mp1 );
1086             USHORT i_ch    = SHORT1FROMMP( mp2 );
1087             USHORT i_vk    = SHORT2FROMMP( mp2 );
1088             int    i_key   = 0;
1089
1090             /* If embedded window, let the parent process keys */
1091             if( sys->parent_window )
1092             {
1093                 WinPostMsg( sys->parent, msg, mp1, mp2 );
1094                 break;
1095             }
1096
1097             if( !( i_flags & KC_KEYUP ))
1098             {
1099                 if( i_flags & KC_VIRTUALKEY )
1100                     /* convert the key if possible */
1101                     i_key = ConvertKey( i_vk );
1102                 else if(( i_flags & KC_CHAR ) && !HIBYTE( i_ch ))
1103                     i_key = tolower( i_ch );
1104
1105                 if( i_key )
1106                 {
1107                     if( i_flags & KC_SHIFT )
1108                        i_key |= KEY_MODIFIER_SHIFT;
1109
1110                     if( i_flags & KC_CTRL )
1111                         i_key |= KEY_MODIFIER_CTRL;
1112
1113                     if( i_flags & KC_ALT )
1114                         i_key |= KEY_MODIFIER_ALT;
1115
1116                     vout_display_SendEventKey(vd, i_key);
1117                 }
1118             }
1119             break;
1120         }
1121
1122         /* Process Manage() call */
1123         case WM_VLC_MANAGE :
1124             break;
1125
1126         /* Fullscreen change */
1127         case WM_VLC_FULLSCREEN_CHANGE :
1128             if( LONGFROMMP( mp1 ))
1129             {
1130                 WinQueryWindowPos( sys->frame, &swp );
1131                 sys->client_rect.xLeft   = swp.x;
1132                 sys->client_rect.yBottom = swp.y;
1133                 sys->client_rect.xRight  = sys->client_rect.xLeft   + swp.cx;
1134                 sys->client_rect.yTop    = sys->client_rect.yBottom + swp.cy;
1135                 WinCalcFrameRect( sys->frame, &sys->client_rect, TRUE );
1136
1137                 rcl.xLeft   = 0;
1138                 rcl.yBottom = 0;
1139                 rcl.xRight  = sys->i_screen_width;
1140                 rcl.yTop    = sys->i_screen_height;
1141             }
1142             else
1143                 rcl = sys->client_rect;
1144
1145             WinCalcFrameRect( sys->frame, &rcl, FALSE );
1146
1147             WinSetWindowPos( sys->frame, HWND_TOP,
1148                              rcl.xLeft, rcl.yBottom,
1149                              rcl.xRight - rcl.xLeft, rcl.yTop - rcl.yBottom,
1150                              SWP_MOVE | SWP_SIZE | SWP_ZORDER | SWP_SHOW |
1151                              SWP_ACTIVATE );
1152             break;
1153
1154         /* Size change */
1155         case WM_VLC_SIZE_CHANGE :
1156             rcl.xLeft   = 0;
1157             rcl.yBottom = 0;
1158             rcl.xRight  = LONGFROMMP( mp1 );
1159             rcl.yTop    = LONGFROMMP( mp2 );
1160             WinCalcFrameRect( sys->frame, &rcl, FALSE );
1161
1162             WinSetWindowPos( sys->frame, NULLHANDLE,
1163                              0, 0,
1164                              rcl.xRight - rcl.xLeft, rcl.yTop - rcl.yBottom,
1165                              SWP_SIZE );
1166
1167             WinQueryWindowPos( sys->frame, &swp );
1168             sys->client_rect.xLeft   = swp.x;
1169             sys->client_rect.yBottom = swp.y;
1170             sys->client_rect.xRight  = sys->client_rect.xLeft   + swp.cx;
1171             sys->client_rect.yTop    = sys->client_rect.yBottom + swp.cy;
1172             WinCalcFrameRect( sys->frame, &sys->client_rect, TRUE );
1173             break;
1174
1175         default :
1176             return WinDefWindowProc( hwnd, msg, mp1, mp2 );
1177     }
1178
1179     /* If embedded window, we need to change our window size according to a
1180      * parent window size */
1181     if( sys->parent_window )
1182     {
1183         WinQueryWindowRect( sys->parent, &rcl );
1184
1185         if( rcl.xLeft   != sys->parent_rect.xLeft   ||
1186             rcl.yBottom != sys->parent_rect.yBottom ||
1187             rcl.xRight  != sys->parent_rect.xRight  ||
1188             rcl.yTop    != sys->parent_rect.yTop)
1189         {
1190             sys->parent_rect = rcl;
1191
1192             WinCalcFrameRect( sys->frame, &rcl, FALSE );
1193
1194             WinSetWindowPos( sys->frame, NULLHANDLE,
1195                              rcl.xLeft, rcl.yBottom,
1196                              rcl.xRight - rcl.xLeft, rcl.yTop - rcl.yBottom,
1197                              SWP_SIZE | SWP_MOVE );
1198         }
1199     }
1200
1201     return result;
1202 }