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