1 /*
2  * OS/2 video output driver
3  *
4  * Copyright (c) 2007-2009 by KO Myung-Hun (komh@chollian.net)
5  *
6  * This file is part of MPlayer.
7  *
8  * MPlayer is free software; you can redistribute it and/or modify
9  * it under the terms of the GNU General Public License as published by
10  * the Free Software Foundation; either version 2 of the License, or
11  * (at your option) any later version.
12  *
13  * MPlayer 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 General Public License for more details.
17  *
18  * You should have received a copy of the GNU General Public License along
19  * with MPlayer; if not, write to the Free Software Foundation, Inc.,
20  * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
21  */
22 
23 #define INCL_WIN
24 #define INCL_GPI
25 #define INCL_DOS
26 #include <os2.h>
27 
28 #include <mmioos2.h>
29 #include <fourcc.h>
30 
31 #include <stdio.h>
32 #include <stdlib.h>
33 #include <string.h>
34 #include <float.h>
35 
36 #include <kva.h>
37 
38 #include "config.h"
39 #include "mp_msg.h"
40 #include "help_mp.h"
41 #include "video_out.h"
42 #define NO_DRAW_FRAME
43 #include "video_out_internal.h"
44 #include "libmpcodecs/vf.h"
45 #include "aspect.h"
46 
47 #include "fastmemcpy.h"
48 #include "mp_fifo.h"
49 #include "osdep/keycodes.h"
50 #include "input/input.h"
51 #include "input/mouse.h"
52 #include "subopt-helper.h"
53 #include "sub/sub.h"
54 
55 #include "cpudetect.h"
56 #include "libswscale/swscale.h"
57 #include "libmpcodecs/vf_scale.h"
58 
59 static const vo_info_t info = {
60     "SNAP/WarpOverlay!/VMAN/DIVE video output",
61     "kva",
62     "KO Myung-Hun <komh@chollian.net>",
63     ""
64 };
65 
66 const LIBVO_EXTERN(kva)
67 
68 #define WC_MPLAYER  "WC_MPLAYER"
69 
70 #define SRC_WIDTH   m_int.kvas.szlSrcSize.cx
71 #define SRC_HEIGHT  m_int.kvas.szlSrcSize.cy
72 
73 #define HWNDFROMWINID(wid)    ((wid) + 0x80000000UL)
74 
75 static const struct mp_keymap m_vk_map[] = {
76     {VK_NEWLINE, KEY_ENTER}, {VK_TAB, KEY_TAB}, {VK_SPACE, ' '},
77 
78     // control keys
79     {VK_CTRL,   KEY_CTRL},    {VK_BACKSPACE, KEY_BS},
80     {VK_DELETE, KEY_DELETE},  {VK_INSERT,    KEY_INSERT},
81     {VK_HOME,   KEY_HOME},    {VK_END,       KEY_END},
82     {VK_PAGEUP, KEY_PAGE_UP}, {VK_PAGEDOWN,  KEY_PAGE_DOWN},
83     {VK_ESC,    KEY_ESC},
84 
85     // cursor keys
86     {VK_RIGHT, KEY_RIGHT}, {VK_LEFT, KEY_LEFT},
87     {VK_DOWN,  KEY_DOWN},  {VK_UP,   KEY_UP},
88 
89     // function keys
90     {VK_F1, KEY_F+1}, {VK_F2,  KEY_F+2},  {VK_F3,  KEY_F+3},  {VK_F4,  KEY_F+4},
91     {VK_F5, KEY_F+5}, {VK_F6,  KEY_F+6},  {VK_F7,  KEY_F+7},  {VK_F8,  KEY_F+8},
92     {VK_F9, KEY_F+9}, {VK_F10, KEY_F+10}, {VK_F11, KEY_F+11}, {VK_F12, KEY_F+12},
93 
94     {0, 0}
95 };
96 
97 static const struct mp_keymap m_keypad_map[] = {
98     // keypad keys
99     {0x52, KEY_KP0}, {0x4F, KEY_KP1}, {0x50, KEY_KP2},   {0x51, KEY_KP3},
100     {0x4B, KEY_KP4}, {0x4C, KEY_KP5}, {0x4D, KEY_KP6},   {0x47, KEY_KP7},
101     {0x48, KEY_KP8}, {0x49, KEY_KP9}, {0x53, KEY_KPDEC}, {0x5A, KEY_KPENTER},
102 
103     {0, 0}
104 };
105 
106 static const struct mp_keymap m_mouse_map[] = {
107     {WM_BUTTON1DOWN,   MOUSE_BTN0},
108     {WM_BUTTON3DOWN,   MOUSE_BTN1},
109     {WM_BUTTON2DOWN,   MOUSE_BTN2},
110     {WM_BUTTON1DBLCLK, MOUSE_BTN0_DBL},
111     {WM_BUTTON3DBLCLK, MOUSE_BTN1_DBL},
112     {WM_BUTTON2DBLCLK, MOUSE_BTN2_DBL},
113 
114     {0, 0}
115 };
116 
117 struct {
118     HAB         hab;
119     HMQ         hmq;
120     HWND        hwndFrame;
121     HWND        hwndClient;
122     HWND        hwndSysMenu;
123     HWND        hwndTitleBar;
124     HWND        hwndMinMax;
125     FOURCC      fcc;
126     int         iImageFormat;
127     int         nChromaShift;
128     KVASETUP    kvas;
129     KVACAPS     kvac;
130     RECTL       rclDst;
131     int         bpp;
132     LONG        lStride;
133     PBYTE       pbImage;
134     BOOL        fFixT23;
135     PFNWP       pfnwpOldFrame;
136     uint8_t    *planes[MP_MAX_PLANES];     // y = 0, u = 1, v = 2
137     int         stride[MP_MAX_PLANES];
138     BOOL        fHWAccel;
139     RECTL       rclParent;
140     struct SwsContext *sws;
141 } m_int;
142 
setAspectRatio(ULONG ulRatio)143 static inline void setAspectRatio(ULONG ulRatio)
144 {
145     ULONG ulValue;
146     int   i;
147 
148     m_int.kvas.ulRatio = ulRatio;
149     kvaSetup(&m_int.kvas);
150 
151     // Setup initializes all attributes, so need to restore them.
152     for (i = 0; i < KVAA_LAST; i++) {
153         kvaQueryAttr(i, &ulValue);
154         kvaSetAttr(i, &ulValue);
155     }
156 }
157 
query_format_info(int format,PBOOL pfHWAccel,PFOURCC pfcc,int * pbpp,int * pnChromaShift)158 static int query_format_info(int format, PBOOL pfHWAccel, PFOURCC pfcc,
159                              int *pbpp, int *pnChromaShift)
160 {
161     BOOL    fHWAccel;
162     FOURCC  fcc;
163     INT     bpp;
164     INT     nChromaShift;
165 
166     switch (format) {
167     case IMGFMT_YV12:
168         fHWAccel        = m_int.kvac.ulInputFormatFlags & KVAF_YV12;
169         fcc             = FOURCC_YV12;
170         bpp             = 1;
171         nChromaShift    = 1;
172         break;
173 
174     case IMGFMT_YUY2:
175         fHWAccel        = m_int.kvac.ulInputFormatFlags & KVAF_YUY2;
176         fcc             = FOURCC_Y422;
177         bpp             = 2;
178         nChromaShift    = 0;
179         break;
180 
181     case IMGFMT_YVU9:
182         fHWAccel        = m_int.kvac.ulInputFormatFlags & KVAF_YVU9;
183         fcc             = FOURCC_YVU9;
184         bpp             = 1;
185         nChromaShift    = 2;
186         break;
187 
188     case IMGFMT_BGR32:
189         fHWAccel        = m_int.kvac.ulInputFormatFlags & KVAF_BGR32;
190         fcc             = FOURCC_BGR4;
191         bpp             = 4;
192         nChromaShift    = 0;
193         break;
194 
195     case IMGFMT_BGR24:
196         fHWAccel        = m_int.kvac.ulInputFormatFlags & KVAF_BGR24;
197         fcc             = FOURCC_BGR3;
198         bpp             = 3;
199         nChromaShift    = 0;
200         break;
201 
202     case IMGFMT_BGR16:
203         fHWAccel        = m_int.kvac.ulInputFormatFlags & KVAF_BGR16;
204         fcc             = FOURCC_R565;
205         bpp             = 2;
206         nChromaShift    = 0;
207         break;
208 
209     case IMGFMT_BGR15:
210         fHWAccel        = m_int.kvac.ulInputFormatFlags & KVAF_BGR15;
211         fcc             = FOURCC_R555;
212         bpp             = 2;
213         nChromaShift    = 0;
214         break;
215 
216     default:
217         return 1;
218     }
219 
220     if (pfHWAccel)
221         *pfHWAccel = fHWAccel;
222 
223     if (pfcc)
224         *pfcc = fcc;
225 
226     if (pbpp)
227         *pbpp = bpp;
228 
229     if (pnChromaShift)
230         *pnChromaShift = nChromaShift;
231 
232     return 0;
233 }
234 
imgCreate(void)235 static void imgCreate(void)
236 {
237     int size = SRC_HEIGHT * m_int.lStride;;
238 
239     switch (m_int.iImageFormat) {
240     case IMGFMT_YV12:
241         size += size / 2;
242         break;
243 
244     case IMGFMT_YVU9:
245         size += size / 8;
246         break;
247     }
248 
249     m_int.pbImage = malloc(size);
250 
251     memset(m_int.planes, 0, sizeof(m_int.planes));
252     memset(m_int.stride, 0, sizeof(m_int.stride));
253     m_int.planes[0] = m_int.pbImage;
254     m_int.stride[0] = m_int.lStride;
255 
256     // YV12 or YVU9 ?
257     if (m_int.nChromaShift) {
258         m_int.planes[1] = m_int.planes[0] + SRC_HEIGHT * m_int.stride[0];
259         m_int.stride[1] = m_int.stride[0] >> m_int.nChromaShift;
260 
261         m_int.planes[2] = m_int.planes[1] +
262                           (SRC_HEIGHT >> m_int.nChromaShift) * m_int.stride[1];
263         m_int.stride[2] = m_int.stride[1];
264     }
265 }
266 
imgFree(void)267 static void imgFree(void)
268 {
269     free(m_int.pbImage);
270 
271     m_int.pbImage = NULL;
272 }
273 
imgDisplay(void)274 static void imgDisplay(void)
275 {
276     PVOID pBuffer;
277     ULONG ulBPL;
278 
279     if (!kvaLockBuffer(&pBuffer, &ulBPL)) {
280         uint8_t *dst[MP_MAX_PLANES] = {NULL};
281         int      dstStride[MP_MAX_PLANES] = {0};
282 
283         // Get packed or Y
284         dst[0]       = pBuffer;
285         dstStride[0] = ulBPL;
286 
287         // YV12 or YVU9 ?
288         if (m_int.nChromaShift) {
289             // Get V
290             dst[2]       = dst[0] + SRC_HEIGHT * dstStride[0];
291             dstStride[2] = dstStride[0] >> m_int.nChromaShift;
292 
293             // Get U
294             dst[1]       = dst[2] +
295                            (SRC_HEIGHT >> m_int.nChromaShift ) * dstStride[2];
296             dstStride[1] = dstStride[2];
297         }
298 
299         if (m_int.fHWAccel) {
300             int w, h;
301 
302             w = m_int.stride[0];
303             h = SRC_HEIGHT;
304 
305             // Copy packed or Y
306             mem2agpcpy_pic(dst[0], m_int.planes[0], w, h,
307                            dstStride[0], m_int.stride[0]);
308 
309             // YV12 or YVU9 ?
310             if (m_int.nChromaShift) {
311                 w >>= m_int.nChromaShift; h >>= m_int.nChromaShift;
312 
313                 // Copy U
314                 mem2agpcpy_pic(dst[1], m_int.planes[1], w, h,
315                                dstStride[1], m_int.stride[1]);
316 
317                 // Copy V
318                 mem2agpcpy_pic(dst[2], m_int.planes[2], w, h,
319                                dstStride[2], m_int.stride[2]);
320             }
321         } else {
322             sws_scale(m_int.sws, m_int.planes, m_int.stride, 0, SRC_HEIGHT,
323                       dst, dstStride);
324         }
325 
326         kvaUnlockBuffer();
327     }
328 }
329 
330 // Frame window procedure to work around T23 laptop with S3 video card,
331 // which supports upscaling only.
NewFrameWndProc(HWND hwnd,ULONG msg,MPARAM mp1,MPARAM mp2)332 static MRESULT EXPENTRY NewFrameWndProc(HWND hwnd, ULONG msg, MPARAM mp1,
333                                         MPARAM mp2)
334 {
335     switch (msg) {
336     case WM_QUERYTRACKINFO:
337         {
338         PTRACKINFO  pti = (PTRACKINFO)mp2;
339         RECTL       rcl;
340 
341         if (vo_fs)
342             break;
343 
344         m_int.pfnwpOldFrame(hwnd, msg, mp1, mp2);
345 
346         rcl.xLeft   = 0;
347         rcl.yBottom = 0;
348         rcl.xRight  = SRC_WIDTH  + 1;
349         rcl.yTop    = SRC_HEIGHT + 1;
350 
351         WinCalcFrameRect(hwnd, &rcl, FALSE);
352 
353         pti->ptlMinTrackSize.x = rcl.xRight - rcl.xLeft;
354         pti->ptlMinTrackSize.y = rcl.yTop   - rcl.yBottom;
355 
356         pti->ptlMaxTrackSize.x = vo_screenwidth;
357         pti->ptlMaxTrackSize.y = vo_screenheight;
358 
359         return (MRESULT)TRUE;
360         }
361 
362     case WM_ADJUSTWINDOWPOS:
363         {
364         PSWP    pswp = (PSWP)mp1;
365         RECTL   rcl;
366 
367         if (vo_fs)
368             break;
369 
370         if (pswp->fl & SWP_SIZE) {
371             rcl.xLeft   = pswp->x;
372             rcl.yBottom = pswp->y;
373             rcl.xRight  = rcl.xLeft   + pswp->cx;
374             rcl.yTop    = rcl.yBottom + pswp->cy;
375 
376             WinCalcFrameRect(hwnd, &rcl, TRUE);
377 
378             if (rcl.xRight - rcl.xLeft <= SRC_WIDTH)
379                 rcl.xRight = rcl.xLeft + (SRC_WIDTH + 1);
380 
381             if (rcl.yTop - rcl.yBottom <= SRC_HEIGHT)
382                 rcl.yTop = rcl.yBottom + (SRC_HEIGHT + 1);
383 
384             WinCalcFrameRect(hwnd, &rcl, FALSE);
385 
386             if (rcl.xRight - rcl.xLeft > vo_screenwidth) {
387                 rcl.xLeft  = 0;
388                 rcl.xRight = vo_screenwidth;
389             }
390 
391             if (rcl.yTop - rcl.yBottom > vo_screenheight) {
392                 rcl.yBottom = 0;
393                 rcl.yTop    = vo_screenheight;
394             }
395 
396             pswp->fl |= SWP_MOVE;
397             pswp->x   = rcl.xLeft;
398             pswp->y   = rcl.yBottom;
399             pswp->cx  = rcl.xRight - rcl.xLeft;
400             pswp->cy  = rcl.yTop   - rcl.yBottom;
401         }
402         break;
403         }
404     }
405 
406     return m_int.pfnwpOldFrame(hwnd, msg, mp1, mp2);
407 }
408 
WndProc(HWND hwnd,ULONG msg,MPARAM mp1,MPARAM mp2)409 static MRESULT EXPENTRY WndProc(HWND hwnd, ULONG msg, MPARAM mp1, MPARAM mp2)
410 {
411     // if slave mode, ignore mouse events and deliver them to a parent window
412     if (WinID != -1 &&
413         ((msg >= WM_MOUSEFIRST    && msg <= WM_MOUSELAST) ||
414          (msg >= WM_EXTMOUSEFIRST && msg <= WM_EXTMOUSELAST))) {
415         WinPostMsg(HWNDFROMWINID(WinID), msg, mp1, mp2);
416 
417         return (MRESULT)TRUE;
418     }
419 
420     switch (msg) {
421     case WM_CLOSE:
422         mplayer_put_key(KEY_CLOSE_WIN);
423 
424         return 0;
425 
426     case WM_CHAR:
427         {
428         USHORT fsFlags = SHORT1FROMMP(mp1);
429         UCHAR  uchScan =  CHAR4FROMMP(mp1);
430         USHORT usCh    = SHORT1FROMMP(mp2);
431         USHORT usVk    = SHORT2FROMMP(mp2);
432         int    mpkey;
433 
434         if (fsFlags & KC_KEYUP)
435             break;
436 
437         if (fsFlags & KC_SCANCODE) {
438             mpkey = lookup_keymap_table(m_keypad_map, uchScan);
439             if (mpkey) {
440                 // distinguish KEY_KP0 and KEY_KPINS
441                 if (mpkey == KEY_KP0 && usCh != '0')
442                     mpkey = KEY_KPINS;
443 
444                 // distinguish KEY_KPDEC and KEY_KPDEL
445                 if (mpkey == KEY_KPDEC && usCh != '.')
446                     mpkey = KEY_KPDEL;
447 
448                 mplayer_put_key(mpkey);
449 
450                 return (MRESULT)TRUE;
451             }
452         }
453 
454         if (fsFlags & KC_VIRTUALKEY) {
455             mpkey = lookup_keymap_table(m_vk_map, usVk);
456             if (mpkey) {
457                 mplayer_put_key(mpkey);
458 
459                 return (MRESULT)TRUE;
460             }
461         }
462 
463         if ((fsFlags & KC_CHAR) && !HIBYTE(usCh))
464             mplayer_put_key(usCh);
465 
466         return (MRESULT)TRUE;
467         }
468 
469     case WM_SIZE:
470         {
471         RECTL rcl;
472         struct vo_rect src_rect;
473         struct vo_rect dst_rect;
474 
475         WinQueryWindowRect(hwnd, &rcl);
476 
477         vo_dwidth  = rcl.xRight - rcl.xLeft;
478         vo_dheight = rcl.yTop - rcl.yBottom;
479 
480         calc_src_dst_rects(SRC_WIDTH, SRC_HEIGHT, &src_rect, &dst_rect,
481                            NULL, NULL);
482 
483         m_int.kvas.rclSrcRect.xLeft   = src_rect.left;
484         m_int.kvas.rclSrcRect.yTop    = src_rect.top;
485         m_int.kvas.rclSrcRect.xRight  = src_rect.right;
486         m_int.kvas.rclSrcRect.yBottom = src_rect.bottom;
487         m_int.kvas.rclDstRect.xLeft   = dst_rect.left;
488         m_int.kvas.rclDstRect.yTop    = dst_rect.top;
489         m_int.kvas.rclDstRect.xRight  = dst_rect.right;
490         m_int.kvas.rclDstRect.yBottom = dst_rect.bottom;
491 
492         // setup to resize
493         setAspectRatio((vo_fs || vo_keepaspect) ? KVAR_FORCEANY : KVAR_NONE);
494 
495         return 0;
496         }
497 
498     case WM_BUTTON1DOWN:
499     case WM_BUTTON3DOWN:
500     case WM_BUTTON2DOWN:
501     case WM_BUTTON1DBLCLK:
502     case WM_BUTTON3DBLCLK:
503     case WM_BUTTON2DBLCLK:
504         if (WinQueryFocus(HWND_DESKTOP) != hwnd)
505             WinSetFocus(HWND_DESKTOP, hwnd);
506         else if (!vo_nomouse_input)
507             mplayer_put_key(lookup_keymap_table(m_mouse_map, msg));
508 
509         return (MRESULT)TRUE;
510 
511     case WM_MOUSEMOVE:
512         {
513         int x = SHORT1FROMMP(mp1);
514         int y = SHORT2FROMMP(mp1);
515 
516         // invert Y
517         y = (vo_dheight - 1) - y;
518 
519         vo_mouse_movement(x, y);
520 
521         break;
522         }
523 
524     case WM_PAINT:
525         {
526         HPS     hps;
527         RECTL   rcl, rclDst;
528         PRECTL  prcl = NULL;
529         HRGN    hrgn, hrgnDst;
530         RGNRECT rgnCtl;
531 
532         // get a current movie area
533         kvaAdjustDstRect(&m_int.kvas.rclSrcRect, &rclDst);
534 
535         // get a current invalidated area
536         hps = WinBeginPaint(hwnd, NULLHANDLE, &rcl);
537 
538         // create a region for an invalidated area
539         hrgn    = GpiCreateRegion(hps, 1, &rcl);
540         // create a region for a movie area
541         hrgnDst = GpiCreateRegion(hps, 1, &rclDst);
542 
543         // exclude a movie area from an invalidated area
544         GpiCombineRegion(hps, hrgn, hrgn, hrgnDst, CRGN_DIFF);
545 
546         // get rectangles from the region
547         rgnCtl.ircStart     = 1;
548         rgnCtl.ulDirection  = RECTDIR_LFRT_TOPBOT;
549         GpiQueryRegionRects(hps, hrgn, NULL, &rgnCtl, NULL);
550 
551         if (rgnCtl.crcReturned > 0) {
552             rgnCtl.crc = rgnCtl.crcReturned;
553             prcl       = malloc(sizeof(RECTL) * rgnCtl.crcReturned);
554         }
555 
556         // draw black bar if needed
557         if (prcl && GpiQueryRegionRects(hps, hrgn, NULL, &rgnCtl, prcl)) {
558             int i;
559 
560             for (i = 0; i < rgnCtl.crcReturned; i++)
561                 WinFillRect(hps, &prcl[i], CLR_BLACK);
562         }
563 
564         free(prcl);
565 
566         GpiDestroyRegion(hps, hrgnDst);
567         GpiDestroyRegion(hps, hrgn);
568 
569         WinEndPaint(hps);
570 
571         return 0;
572         }
573     }
574 
575     return WinDefWindowProc(hwnd, msg, mp1, mp2);
576 }
577 
578 // Change process type from VIO to PM to use PM APIs.
morphToPM(void)579 static void morphToPM(void)
580 {
581     PPIB pib;
582 
583     DosGetInfoBlocks(NULL, &pib);
584 
585     // Change flag from VIO to PM:
586     if (pib->pib_ultype == 2)
587         pib->pib_ultype = 3;
588 }
589 
preinit(const char * arg)590 static int preinit(const char *arg)
591 {
592     HWND    hwndParent;
593     ULONG   flFrameFlags;
594     ULONG   kvaMode = 0;
595 
596     int     fUseSnap = 0;
597     int     fUseWO   = 0;
598     int     fUseVman = 0;
599     int     fUseDive = 0;
600     int     fFixT23  = 0;
601 
602     const opt_t subopts[] = {
603         {"snap", OPT_ARG_BOOL, &fUseSnap, NULL},
604         {"wo",   OPT_ARG_BOOL, &fUseWO,   NULL},
605         {"vman", OPT_ARG_BOOL, &fUseVman, NULL},
606         {"dive", OPT_ARG_BOOL, &fUseDive, NULL},
607         {"t23",  OPT_ARG_BOOL, &fFixT23,  NULL},
608         {NULL,              0, NULL,      NULL}
609     };
610 
611     PCSZ pcszVideoModeStr[] = {"UNKNOWN", "DIVE", "WarpOverlay!", "SNAP",
612                                "VMAN"};
613     int nVideoModeStr = sizeof(pcszVideoModeStr) / sizeof(*pcszVideoModeStr);
614 
615     if (subopt_parse(arg, subopts) != 0)
616         return -1;
617 
618     morphToPM();
619 
620     memset(&m_int, 0, sizeof(m_int));
621 
622     m_int.hab = WinInitialize(0);
623     m_int.hmq = WinCreateMsgQueue(m_int.hab, 0);
624 
625     WinRegisterClass(m_int.hab,
626                      WC_MPLAYER,
627                      WndProc,
628                      CS_SIZEREDRAW | CS_MOVENOTIFY,
629                      sizeof(PVOID));
630 
631     if (WinID == -1) {
632         hwndParent   = HWND_DESKTOP;
633         flFrameFlags = FCF_SYSMENU    | FCF_TITLEBAR | FCF_MINMAX |
634                        FCF_SIZEBORDER | FCF_TASKLIST;
635     } else {
636         ULONG ulStyle;
637 
638         hwndParent   = HWNDFROMWINID(WinID);
639         flFrameFlags = 0;
640 
641         // Prevent a parent window from painting over our window
642         ulStyle = WinQueryWindowULong(hwndParent, QWL_STYLE);
643         WinSetWindowULong(hwndParent, QWL_STYLE, ulStyle | WS_CLIPCHILDREN);
644     }
645 
646     m_int.hwndFrame =
647         WinCreateStdWindow(hwndParent,          // parent window handle
648                            WS_VISIBLE,          // frame window style
649                            &flFrameFlags,       // window style
650                            WC_MPLAYER,          // class name
651                            "",                  // window title
652                            0L,                  // default client style
653                            NULLHANDLE,          // resource in exe file
654                            1,                   // frame window id
655                            &m_int.hwndClient);  // client window handle
656 
657     if (m_int.hwndFrame == NULLHANDLE)
658         return -1;
659 
660     m_int.hwndSysMenu  = WinWindowFromID(m_int.hwndFrame, FID_SYSMENU);
661     m_int.hwndTitleBar = WinWindowFromID(m_int.hwndFrame, FID_TITLEBAR);
662     m_int.hwndMinMax   = WinWindowFromID(m_int.hwndFrame, FID_MINMAX);
663 
664     m_int.fFixT23 = fFixT23;
665 
666     if (m_int.fFixT23)
667         m_int.pfnwpOldFrame = WinSubclassWindow(m_int.hwndFrame,
668                                                 NewFrameWndProc);
669 
670     if (!!fUseSnap + !!fUseWO + !!fUseVman + !!fUseDive > 1)
671         mp_msg(MSGT_VO, MSGL_WARN,"KVA: Multiple mode specified!!!\n");
672 
673     if (fUseSnap)
674         kvaMode = KVAM_SNAP;
675     else if (fUseWO)
676         kvaMode = KVAM_WO;
677     else if (fUseVman)
678         kvaMode = KVAM_VMAN;
679     else if (fUseDive)
680         kvaMode = KVAM_DIVE;
681     else
682         kvaMode = KVAM_AUTO;
683 
684     if (kvaInit(kvaMode, m_int.hwndClient, vo_colorkey)) {
685         mp_msg(MSGT_VO, MSGL_ERR, "KVA: Init failed!!!\n");
686 
687         return -1;
688     }
689 
690     kvaCaps(&m_int.kvac);
691 
692     mp_msg(MSGT_VO, MSGL_V, "KVA: Selected video mode = %s\n",
693            pcszVideoModeStr[m_int.kvac.ulMode >= nVideoModeStr ?
694                                 0 : m_int.kvac.ulMode]);
695 
696     kvaDisableScreenSaver();
697 
698     // Might cause PM DLLs to be loaded which incorrectly enable SIG_FPE,
699     // so mask off all floating-point exceptions.
700     _control87(MCW_EM, MCW_EM);
701 
702     return 0;
703 }
704 
uninit(void)705 static void uninit(void)
706 {
707     kvaEnableScreenSaver();
708 
709     imgFree();
710 
711     sws_freeContext(m_int.sws);
712 
713     if (m_int.hwndFrame != NULLHANDLE) {
714         kvaResetAttr();
715         kvaDone();
716 
717         if (m_int.fFixT23)
718             WinSubclassWindow(m_int.hwndFrame, m_int.pfnwpOldFrame);
719 
720         WinDestroyWindow(m_int.hwndFrame);
721     }
722 
723     WinDestroyMsgQueue(m_int.hmq);
724     WinTerminate(m_int.hab);
725 }
726 
config(uint32_t width,uint32_t height,uint32_t d_width,uint32_t d_height,uint32_t flags,char * title,uint32_t format)727 static int config(uint32_t width, uint32_t height,
728                   uint32_t d_width, uint32_t d_height,
729                   uint32_t flags, char *title, uint32_t format)
730 {
731     RECTL   rcl;
732 
733     if (vo_wintitle)
734         title = vo_wintitle;
735 
736     mp_msg(MSGT_VO, MSGL_V,
737            "KVA: Using 0x%X (%s) image format, vo_config_count = %d\n",
738            format, vo_format_name(format), vo_config_count);
739 
740     imgFree();
741 
742     if (query_format_info(format, &m_int.fHWAccel, &m_int.fcc, &m_int.bpp,
743                           &m_int.nChromaShift))
744         return 1;
745 
746     m_int.iImageFormat = format;
747 
748     // if there is no hw accel for given format,
749     // try any format supported by hw accel
750     if (!m_int.fHWAccel) {
751         int dstFormat = 0;
752 
753         sws_freeContext(m_int.sws);
754 
755         if (m_int.kvac.ulInputFormatFlags & KVAF_YV12)
756             dstFormat = IMGFMT_YV12;
757         else if (m_int.kvac.ulInputFormatFlags & KVAF_YUY2)
758             dstFormat = IMGFMT_YUY2;
759         else if (m_int.kvac.ulInputFormatFlags & KVAF_YVU9)
760             dstFormat = IMGFMT_YVU9;
761         else if (m_int.kvac.ulInputFormatFlags & KVAF_BGR32)
762             dstFormat = IMGFMT_BGR32;
763         else if (m_int.kvac.ulInputFormatFlags & KVAF_BGR24)
764             dstFormat = IMGFMT_BGR24;
765         else if (m_int.kvac.ulInputFormatFlags & KVAF_BGR16)
766             dstFormat = IMGFMT_BGR16;
767         else if (m_int.kvac.ulInputFormatFlags & KVAF_BGR15)
768             dstFormat = IMGFMT_BGR15;
769 
770         if (query_format_info(dstFormat, NULL, &m_int.fcc, NULL, NULL))
771             return 1;
772 
773         m_int.sws = sws_getContextFromCmdLine(width, height, format,
774                                               width, height, dstFormat);
775     }
776 
777     mp_msg(MSGT_VO, MSGL_V, "KVA: Selected FOURCC = %.4s\n", (char *)&m_int.fcc);
778 
779     m_int.kvas.ulLength           = sizeof(KVASETUP);
780     m_int.kvas.szlSrcSize.cx      = width;
781     m_int.kvas.szlSrcSize.cy      = height;
782     m_int.kvas.rclSrcRect.xLeft   = 0;
783     m_int.kvas.rclSrcRect.yTop    = 0;
784     m_int.kvas.rclSrcRect.xRight  = width;
785     m_int.kvas.rclSrcRect.yBottom = height;
786     m_int.kvas.ulRatio            = vo_keepaspect ? KVAR_FORCEANY : KVAR_NONE;
787     m_int.kvas.ulAspectWidth      = d_width;
788     m_int.kvas.ulAspectHeight     = d_height;
789     m_int.kvas.fccSrcColor        = m_int.fcc;
790     m_int.kvas.fDither            = TRUE;
791 
792     if (kvaSetup(&m_int.kvas)) {
793         mp_msg(MSGT_VO, MSGL_ERR, "KVA: Setup failed!!!\n");
794 
795         return 1;
796     }
797 
798     m_int.lStride = width * m_int.bpp;
799 
800     imgCreate();
801 
802     if (WinID == -1) {
803         WinSetWindowText(m_int.hwndFrame, title);
804 
805         // initialize 'vo_fs' only once at first config() call
806         if (vo_config_count == 0)
807             vo_fs = flags & VOFLAG_FULLSCREEN;
808 
809         // workaround for T23 laptop with S3 Video by Franz Bakan
810         if (!vo_fs && m_int.fFixT23) {
811             d_width++;
812             d_height++;
813         }
814 
815         vo_dx = (vo_screenwidth - d_width) / 2;
816         vo_dy = (vo_screenheight - d_height ) / 2;
817         geometry(&vo_dx, &vo_dy, &d_width, &d_height,
818                  vo_screenwidth, vo_screenheight);
819 
820         m_int.rclDst.xLeft   = vo_dx;
821         // invert Y
822         m_int.rclDst.yBottom = vo_screenheight - (vo_dy + d_height);
823         m_int.rclDst.xRight  = m_int.rclDst.xLeft   + d_width;
824         m_int.rclDst.yTop    = m_int.rclDst.yBottom + d_height;
825 
826         if (vo_fs) {
827             vo_dx = 0;
828             vo_dy = 0;
829 
830             d_width  = vo_screenwidth;
831             d_height = vo_screenheight;
832 
833             // when -fs option is used without this, title bar is not highlighted
834             WinSetActiveWindow(HWND_DESKTOP, m_int.hwndFrame);
835 
836             WinSetParent(m_int.hwndSysMenu,  HWND_OBJECT, FALSE);
837             WinSetParent(m_int.hwndTitleBar, HWND_OBJECT, FALSE);
838             WinSetParent(m_int.hwndMinMax,   HWND_OBJECT, FALSE);
839 
840             setAspectRatio(KVAR_FORCEANY);
841         }
842 
843         rcl.xLeft   = vo_dx;
844         // invert Y
845         rcl.yBottom = vo_screenheight - (vo_dy + d_height);
846         rcl.xRight  = rcl.xLeft   + d_width;
847         rcl.yTop    = rcl.yBottom + d_height;
848     } else {
849         vo_fs = 0;
850 
851         WinQueryWindowRect(HWNDFROMWINID(WinID), &m_int.rclDst);
852         rcl = m_int.rclDst;
853     }
854 
855     // trick to setup image parameters in WM_SIZE
856     // if new sizes of a window are same as old ones,
857     // WM_SIZE is not called
858     WinSetWindowPos(m_int.hwndFrame, NULLHANDLE, 0, 0, 0, 0, SWP_SIZE);
859 
860     WinCalcFrameRect(m_int.hwndFrame, &rcl, FALSE);
861 
862     WinSetWindowPos(m_int.hwndFrame, HWND_TOP,
863                     rcl.xLeft, rcl.yBottom,
864                     rcl.xRight - rcl.xLeft, rcl.yTop - rcl.yBottom,
865                     SWP_SIZE | SWP_MOVE | SWP_ZORDER | SWP_SHOW |
866                     (WinID == -1 ? SWP_ACTIVATE : 0));
867 
868     WinInvalidateRect(m_int.hwndFrame, NULL, TRUE);
869 
870     return 0;
871 }
872 
get_image(mp_image_t * mpi)873 static uint32_t get_image(mp_image_t *mpi)
874 {
875     if (m_int.iImageFormat != mpi->imgfmt)
876         return VO_FALSE;
877 
878     if (mpi->type == MP_IMGTYPE_STATIC || mpi->type == MP_IMGTYPE_TEMP) {
879         if (mpi->flags & MP_IMGFLAG_PLANAR) {
880             mpi->planes[1] = m_int.planes[1];
881             mpi->planes[2] = m_int.planes[2];
882 
883             mpi->stride[1] = m_int.stride[1];
884             mpi->stride[2] = m_int.stride[2];
885         }
886 
887         mpi->planes[0] = m_int.planes[0];
888         mpi->stride[0] = m_int.stride[0];
889         mpi->flags    |= MP_IMGFLAG_DIRECT;
890 
891         return VO_TRUE;
892     }
893 
894     return VO_FALSE;
895 }
896 
draw_image(mp_image_t * mpi)897 static uint32_t draw_image(mp_image_t *mpi)
898 {
899     // if -dr or -slices then do nothing:
900     if (mpi->flags & (MP_IMGFLAG_DIRECT | MP_IMGFLAG_DRAW_CALLBACK))
901         return VO_TRUE;
902 
903     draw_slice(mpi->planes, mpi->stride, mpi->w, mpi->h, mpi->x, mpi->y);
904 
905     return VO_TRUE;
906 }
907 
query_format(uint32_t format)908 static int query_format(uint32_t format)
909 {
910     BOOL fHWAccel;
911     int  res;
912 
913     if (query_format_info(format, &fHWAccel, NULL, NULL, NULL))
914         return 0;
915 
916     res = VFCAP_CSP_SUPPORTED | VFCAP_OSD;
917     if (fHWAccel) {
918         res |= VFCAP_CSP_SUPPORTED_BY_HW | VFCAP_HWSCALE_UP;
919 
920         if (!m_int.fFixT23)
921             res |= VFCAP_HWSCALE_DOWN;
922     }
923 
924     return res;
925 }
926 
fs_toggle(void)927 static int fs_toggle(void)
928 {
929     RECTL   rcl;
930 
931     vo_fs = !vo_fs;
932 
933     if (vo_fs) {
934         SWP swp;
935 
936         WinQueryWindowPos(m_int.hwndFrame, &swp);
937         m_int.rclDst.xLeft   = swp.x;
938         m_int.rclDst.yBottom = swp.y;
939         m_int.rclDst.xRight  = m_int.rclDst.xLeft   + swp.cx;
940         m_int.rclDst.yTop    = m_int.rclDst.yBottom + swp.cy;
941         WinCalcFrameRect(m_int.hwndFrame, &m_int.rclDst, TRUE);
942 
943         if (WinID != -1)
944             WinSetParent(m_int.hwndFrame, HWND_DESKTOP, FALSE);
945 
946         WinSetParent(m_int.hwndSysMenu,  HWND_OBJECT, FALSE);
947         WinSetParent(m_int.hwndTitleBar, HWND_OBJECT, FALSE);
948         WinSetParent(m_int.hwndMinMax,   HWND_OBJECT, FALSE);
949 
950         rcl.xLeft   = 0;
951         rcl.yBottom = 0;
952         rcl.xRight  = vo_screenwidth;
953         rcl.yTop    = vo_screenheight;
954 
955         setAspectRatio(KVAR_FORCEANY);
956     } else {
957         if (WinID != -1)
958             WinSetParent(m_int.hwndFrame, HWNDFROMWINID(WinID), TRUE);
959 
960         WinSetParent(m_int.hwndSysMenu,  m_int.hwndFrame, FALSE);
961         WinSetParent(m_int.hwndTitleBar, m_int.hwndFrame, FALSE);
962         WinSetParent(m_int.hwndMinMax,   m_int.hwndFrame, FALSE);
963 
964         rcl = m_int.rclDst;
965 
966         setAspectRatio(vo_keepaspect ? KVAR_FORCEANY : KVAR_NONE);
967     }
968 
969     WinCalcFrameRect(m_int.hwndFrame, &rcl, FALSE);
970 
971     WinSetWindowPos(m_int.hwndFrame, HWND_TOP,
972                     rcl.xLeft, rcl.yBottom,
973                     rcl.xRight - rcl.xLeft, rcl.yTop - rcl.yBottom,
974                     SWP_SIZE | SWP_MOVE | SWP_ZORDER | SWP_SHOW |
975                     (WinID == -1 ? SWP_ACTIVATE : 0));
976 
977     return VO_TRUE;
978 }
979 
color_ctrl_set(char * what,int value)980 static int color_ctrl_set(char *what, int value)
981 {
982     ULONG   ulAttr;
983     ULONG   ulValue;
984 
985     if (!strcmp(what, "brightness"))
986         ulAttr = KVAA_BRIGHTNESS;
987     else if (!strcmp(what, "contrast"))
988         ulAttr = KVAA_CONTRAST;
989     else if (!strcmp(what, "hue"))
990         ulAttr = KVAA_HUE;
991     else if (!strcmp(what, "saturation"))
992         ulAttr = KVAA_SATURATION;
993     else
994         return VO_NOTIMPL;
995 
996     ulValue = (value + 100) * 255 / 200;
997 
998     if (kvaSetAttr(ulAttr, &ulValue))
999         return VO_NOTIMPL;
1000 
1001     return VO_TRUE;
1002 }
1003 
color_ctrl_get(char * what,int * value)1004 static int color_ctrl_get(char *what, int *value)
1005 {
1006     ULONG   ulAttr;
1007     ULONG   ulValue;
1008 
1009     if (!strcmp(what, "brightness"))
1010         ulAttr = KVAA_BRIGHTNESS;
1011     else if (!strcmp(what, "contrast"))
1012         ulAttr = KVAA_CONTRAST;
1013     else if (!strcmp(what, "hue"))
1014         ulAttr = KVAA_HUE;
1015     else if (!strcmp(what, "saturation"))
1016         ulAttr = KVAA_SATURATION;
1017     else
1018         return VO_NOTIMPL;
1019 
1020     if (kvaQueryAttr(ulAttr, &ulValue))
1021         return VO_NOTIMPL;
1022 
1023     // add 1 to adjust range
1024     *value = ((ulValue + 1) * 200 / 255) - 100;
1025 
1026     return VO_TRUE;
1027 }
1028 
control(uint32_t request,void * data)1029 static int control(uint32_t request, void *data)
1030 {
1031     switch (request) {
1032     case VOCTRL_GET_IMAGE:
1033         return get_image(data);
1034 
1035     case VOCTRL_DRAW_IMAGE:
1036         return draw_image(data);
1037 
1038     case VOCTRL_QUERY_FORMAT:
1039         return query_format(*(uint32_t *)data);
1040 
1041     case VOCTRL_FULLSCREEN:
1042         return fs_toggle();
1043 
1044     case VOCTRL_SET_EQUALIZER:
1045         {
1046         vf_equalizer_t *eq=data;
1047         return color_ctrl_set(eq->item, eq->value);
1048         }
1049 
1050     case VOCTRL_GET_EQUALIZER:
1051         {
1052         vf_equalizer_t *eq=data;
1053         return color_ctrl_get(eq->item, &eq->value);
1054         }
1055 
1056     case VOCTRL_UPDATE_SCREENINFO:
1057         vo_screenwidth  = m_int.kvac.cxScreen;
1058         vo_screenheight = m_int.kvac.cyScreen;
1059 
1060         aspect_save_screenres(vo_screenwidth, vo_screenheight);
1061 
1062         return VO_TRUE;
1063     }
1064 
1065     return VO_NOTIMPL;
1066 }
1067 
draw_slice(uint8_t * src[],int stride[],int w,int h,int x,int y)1068 static int draw_slice(uint8_t *src[], int stride[], int w, int h, int x, int y)
1069 {
1070     uint8_t *s;
1071     uint8_t *d;
1072 
1073     // copy packed or Y
1074     d = m_int.planes[0] + m_int.stride[0] * y + x;
1075     s = src[0];
1076     mem2agpcpy_pic(d, s, w * m_int.bpp, h, m_int.stride[0], stride[0]);
1077 
1078     // YV12 or YVU9
1079     if (m_int.nChromaShift) {
1080         w >>= m_int.nChromaShift; h >>= m_int.nChromaShift;
1081         x >>= m_int.nChromaShift; y >>= m_int.nChromaShift;
1082 
1083         // copy U
1084         d = m_int.planes[1] + m_int.stride[1] * y + x;
1085         s = src[1];
1086         mem2agpcpy_pic(d, s, w, h, m_int.stride[1], stride[1]);
1087 
1088         // copy V
1089         d = m_int.planes[2] + m_int.stride[2] * y + x;
1090         s = src[2];
1091         mem2agpcpy_pic(d, s, w, h, m_int.stride[2], stride[2]);
1092     }
1093 
1094     return 0;
1095 }
1096 
1097 #define vo_draw_alpha(imgfmt) \
1098     vo_draw_alpha_##imgfmt(w, h, src, srca, stride, \
1099                            m_int.planes[0] + m_int.stride[0] * y0 + m_int.bpp * x0, \
1100                            m_int.stride[0])
1101 
draw_alpha(int x0,int y0,int w,int h,unsigned char * src,unsigned char * srca,int stride)1102 static void draw_alpha(int x0, int y0, int w, int h,
1103                        unsigned char *src, unsigned char *srca, int stride)
1104 {
1105     switch (m_int.iImageFormat) {
1106     case IMGFMT_YV12:
1107     case IMGFMT_YVU9:
1108         vo_draw_alpha(yv12);
1109         break;
1110 
1111     case IMGFMT_YUY2:
1112         vo_draw_alpha(yuy2);
1113         break;
1114 
1115     case IMGFMT_BGR24:
1116         vo_draw_alpha(rgb24);
1117         break;
1118 
1119     case IMGFMT_BGR16:
1120         vo_draw_alpha(rgb16);
1121         break;
1122 
1123     case IMGFMT_BGR15:
1124         vo_draw_alpha(rgb15);
1125         break;
1126     }
1127 }
1128 
draw_osd(void)1129 static void draw_osd(void)
1130 {
1131     vo_draw_text(SRC_WIDTH, SRC_HEIGHT, draw_alpha);
1132 }
1133 
flip_page(void)1134 static void flip_page(void)
1135 {
1136     imgDisplay();
1137 }
1138 
check_events(void)1139 static void check_events(void)
1140 {
1141     QMSG    qm;
1142 
1143     // On slave mode, we need to change our window size according to a
1144     // parent window size
1145     if (WinID != -1) {
1146         RECTL rcl;
1147 
1148         WinQueryWindowRect(HWNDFROMWINID(WinID), &rcl);
1149 
1150         if (rcl.xLeft   != m_int.rclParent.xLeft   ||
1151             rcl.yBottom != m_int.rclParent.yBottom ||
1152             rcl.xRight  != m_int.rclParent.xRight  ||
1153             rcl.yTop    != m_int.rclParent.yTop) {
1154             WinSetWindowPos(m_int.hwndFrame, NULLHANDLE,
1155                             rcl.xLeft, rcl.yBottom,
1156                             rcl.xRight - rcl.xLeft, rcl.yTop - rcl.yBottom,
1157                             SWP_SIZE | SWP_MOVE);
1158 
1159             m_int.rclParent = rcl;
1160         }
1161     }
1162 
1163     while (WinPeekMsg(m_int.hab, &qm, NULLHANDLE, 0, 0, PM_REMOVE))
1164         WinDispatchMsg(m_int.hab, &qm);
1165 }
1166