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