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