configure: use cflag_check function for more cases
[mplayer/glamo.git] / libvo / vo_quartz.c
blob5c06adf437ed03a09eeb2e02195d301455e3809c
1 /*
2 * This file is part of MPlayer.
4 * MPlayer is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version.
9 * MPlayer is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
14 * You should have received a copy of the GNU General Public License along
15 * with MPlayer; if not, write to the Free Software Foundation, Inc.,
16 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
19 /**
20 \author Nicolas Plourde <nicolasplourde@gmail.com>
22 Copyright (c) Nicolas Plourde - April 2004
24 YUV support Copyright (C) 2004 Romain Dolbeau <romain@dolbeau.org>
26 \brief MPlayer Mac OSX Quartz video out module.
28 \todo: -screen overlay output
29 -fit osd in black bar when available
30 -fix RGB32
31 -(add sugestion here)
34 //SYS
35 #include <stdio.h>
37 //OSX
38 #include <Carbon/Carbon.h>
39 #include <QuickTime/QuickTime.h>
41 //MPLAYER
42 #include "config.h"
43 #include "fastmemcpy.h"
44 #include "video_out.h"
45 #include "video_out_internal.h"
46 #include "aspect.h"
47 #include "mp_msg.h"
48 #include "m_option.h"
49 #include "mp_fifo.h"
50 #include "mpbswap.h"
51 #include "sub.h"
53 #include "input/input.h"
54 #include "input/mouse.h"
56 #include "osx_common.h"
58 static const vo_info_t info =
60 "Mac OSX (Quartz)",
61 "quartz",
62 "Nicolas Plourde <nicolasplourde@hotmail.com>, Romain Dolbeau <romain@dolbeau.org>",
66 const LIBVO_EXTERN(quartz)
68 static uint32_t image_depth;
69 static uint32_t image_format;
70 static uint32_t image_size;
71 static uint32_t image_buffer_size;
72 static char *image_data;
74 static ImageSequence seqId;
75 static CodecType image_qtcodec;
76 static PlanarPixmapInfoYUV420 *P = NULL;
77 static struct
79 ImageDescriptionHandle desc;
80 Handle extension_colr;
81 Handle extension_fiel;
82 Handle extension_clap;
83 Handle extension_pasp;
84 } yuv_qt_stuff;
85 static MatrixRecord matrix;
86 static int EnterMoviesDone = 0;
87 static int get_image_done = 0;
89 static int vo_quartz_fs; // we are in fullscreen
91 static int winLevel = 1;
92 int levelList[] =
94 kCGDesktopWindowLevelKey,
95 kCGNormalWindowLevelKey,
96 kCGScreenSaverWindowLevelKey
99 static int int_pause = 0;
100 static float winAlpha = 1;
101 static int mouseHide = FALSE;
102 static float winSizeMult = 1;
104 static int device_id = 0;
106 static short fs_res_x = 0;
107 static short fs_res_y = 0;
109 static WindowRef theWindow = NULL;
110 static WindowGroupRef winGroup = NULL;
111 static CGRect bounds;
112 static CGDirectDisplayID displayId = 0;
113 static CFDictionaryRef originalMode = NULL;
115 static CGDataProviderRef dataProviderRef = NULL;
116 static CGImageRef image = NULL;
118 static Rect imgRect; // size of the original image (unscaled)
119 static Rect dstRect; // size of the displayed image (after scaling)
120 static Rect winRect; // size of the window containg the displayed image (include padding)
121 static Rect oldWinRect; // size of the window containg the displayed image (include padding) when NOT in FS mode
122 static Rect oldWinBounds;
124 static MenuRef windMenu;
125 static MenuRef movMenu;
126 static MenuRef aspectMenu;
128 static int lastScreensaverUpdate = 0;
129 static int lastMouseHide = 0;
131 enum
133 kQuitCmd = 1,
134 kHalfScreenCmd = 2,
135 kNormalScreenCmd = 3,
136 kDoubleScreenCmd = 4,
137 kFullScreenCmd = 5,
138 kKeepAspectCmd = 6,
139 kAspectOrgCmd = 7,
140 kAspectFullCmd = 8,
141 kAspectWideCmd = 9,
142 kPanScanCmd = 10
145 #include "osdep/keycodes.h"
147 //PROTOTYPE/////////////////////////////////////////////////////////////////
148 static OSStatus KeyEventHandler(EventHandlerCallRef nextHandler, EventRef event, void *userData);
149 static OSStatus MouseEventHandler(EventHandlerCallRef nextHandler, EventRef event, void *userData);
150 static OSStatus WindowEventHandler(EventHandlerCallRef nextHandler, EventRef event, void *userData);
151 void window_resized(void);
152 void window_ontop(void);
153 void window_fullscreen(void);
154 void window_panscan(void);
156 static void draw_alpha(int x0, int y0, int w, int h, unsigned char *src, unsigned char *srca, int stride)
158 switch (image_format)
160 case IMGFMT_RGB32:
161 vo_draw_alpha_rgb32(w, h, src, srca, stride, image_data + 4 * (y0 * imgRect.right + x0), 4 * imgRect.right);
162 break;
163 case IMGFMT_YV12:
164 case IMGFMT_IYUV:
165 case IMGFMT_I420:
166 vo_draw_alpha_yv12(w, h, src, srca, stride, ((char *)P) + be2me_32(P->componentInfoY.offset) + x0 + y0 * imgRect.right, imgRect.right);
167 break;
168 case IMGFMT_UYVY:
169 vo_draw_alpha_uyvy(w, h, src, srca, stride, ((char *)P) + (x0 + y0 * imgRect.right) * 2, imgRect.right * 2);
170 break;
171 case IMGFMT_YUY2:
172 vo_draw_alpha_yuy2(w, h, src, srca, stride, ((char *)P) + (x0 + y0 * imgRect.right) * 2, imgRect.right * 2);
173 break;
177 //default keyboard event handler
178 static OSStatus KeyEventHandler(EventHandlerCallRef nextHandler, EventRef event, void *userData)
180 OSStatus result = noErr;
181 UInt32 class = GetEventClass(event);
182 UInt32 kind = GetEventKind(event);
184 result = CallNextEventHandler(nextHandler, event);
186 if (class == kEventClassKeyboard)
188 char macCharCodes;
189 UInt32 macKeyCode;
190 UInt32 macKeyModifiers;
192 GetEventParameter(event, kEventParamKeyMacCharCodes, typeChar, NULL, sizeof(macCharCodes), NULL, &macCharCodes);
193 GetEventParameter(event, kEventParamKeyCode, typeUInt32, NULL, sizeof(macKeyCode), NULL, &macKeyCode);
194 GetEventParameter(event, kEventParamKeyModifiers, typeUInt32, NULL, sizeof(macKeyModifiers), NULL, &macKeyModifiers);
196 if (macKeyModifiers != 256)
198 if (kind == kEventRawKeyRepeat || kind == kEventRawKeyDown)
200 int key = convert_key(macKeyCode, macCharCodes);
202 if (key != -1)
203 mplayer_put_key(key);
206 else if (macKeyModifiers == 256)
208 switch (macCharCodes)
210 case '[': SetWindowAlpha(theWindow, winAlpha -= 0.05); break;
211 case ']': SetWindowAlpha(theWindow, winAlpha += 0.05); break;
214 else
215 result = eventNotHandledErr;
218 return result;
221 //default mouse event handler
222 static OSStatus MouseEventHandler(EventHandlerCallRef nextHandler, EventRef event, void *userData)
224 OSStatus result = noErr;
225 UInt32 class = GetEventClass(event);
226 UInt32 kind = GetEventKind(event);
228 result = CallNextEventHandler(nextHandler, event);
230 if (class == kEventClassMouse)
232 WindowPtr tmpWin;
233 Point mousePos;
234 Point winMousePos;
236 GetEventParameter(event, kEventParamMouseLocation, typeQDPoint, 0, sizeof(mousePos), 0, &mousePos);
237 GetEventParameter(event, kEventParamWindowMouseLocation, typeQDPoint, 0, sizeof(winMousePos), 0, &winMousePos);
239 switch (kind)
241 case kEventMouseMoved:
243 if (vo_quartz_fs)
245 CGDisplayShowCursor(displayId);
246 mouseHide = FALSE;
249 break;
251 case kEventMouseWheelMoved:
253 int wheel;
254 short part;
256 GetEventParameter(event, kEventParamMouseWheelDelta, typeSInt32, 0, sizeof(wheel), 0, &wheel);
258 part = FindWindow(mousePos, &tmpWin);
260 if (part == inContent)
262 if (wheel > 0)
263 mplayer_put_key(MOUSE_BTN3);
264 else
265 mplayer_put_key(MOUSE_BTN4);
268 break;
270 case kEventMouseDown:
271 case kEventMouseUp:
273 EventMouseButton button;
274 short part;
275 Rect bounds;
277 GetWindowPortBounds(theWindow, &bounds);
278 GetEventParameter(event, kEventParamMouseButton, typeMouseButton, 0, sizeof(button), 0, &button);
280 part = FindWindow(mousePos, &tmpWin);
281 if (kind == kEventMouseUp)
283 if (part != inContent)
284 break;
285 switch (button)
287 case kEventMouseButtonPrimary:
288 mplayer_put_key(MOUSE_BTN0);
289 break;
290 case kEventMouseButtonSecondary:
291 mplayer_put_key(MOUSE_BTN2);
292 break;
293 case kEventMouseButtonTertiary:
294 mplayer_put_key(MOUSE_BTN1);
295 break;
297 default:
298 result = eventNotHandledErr;
299 break;
301 break;
303 if (winMousePos.h > bounds.right - 15 && winMousePos.v > bounds.bottom)
305 if (!vo_quartz_fs)
307 Rect newSize;
309 ResizeWindow(theWindow, mousePos, NULL, &newSize);
312 else if (part == inMenuBar)
314 MenuSelect(mousePos);
315 HiliteMenu(0);
317 else if (part == inContent)
319 switch (button)
321 case kEventMouseButtonPrimary:
322 mplayer_put_key(MOUSE_BTN0 | MP_KEY_DOWN);
323 break;
324 case kEventMouseButtonSecondary:
325 mplayer_put_key(MOUSE_BTN2 | MP_KEY_DOWN);
326 break;
327 case kEventMouseButtonTertiary:
328 mplayer_put_key(MOUSE_BTN1 | MP_KEY_DOWN);
329 break;
331 default:
332 result = eventNotHandledErr;
333 break;
337 break;
339 case kEventMouseDragged:
340 break;
342 default:
343 result = eventNotHandledErr;
344 break;
348 return result;
351 static void set_winSizeMult(float mult)
353 int d_width, d_height;
354 aspect(&d_width, &d_height, A_NOZOOM);
356 if (vo_quartz_fs)
358 vo_fs = !vo_fs;
359 window_fullscreen();
362 winSizeMult = mult;
363 SizeWindow(theWindow, d_width * mult, d_height * mult, 1);
364 window_resized();
367 //default window event handler
368 static OSStatus WindowEventHandler(EventHandlerCallRef nextHandler, EventRef event, void *userData)
370 OSStatus result = noErr;
371 UInt32 class = GetEventClass(event);
372 UInt32 kind = GetEventKind(event);
374 result = CallNextEventHandler(nextHandler, event);
376 if (class == kEventClassCommand)
378 HICommand theHICommand;
380 GetEventParameter(event, kEventParamDirectObject, typeHICommand, NULL, sizeof(theHICommand), NULL, &theHICommand);
382 switch (theHICommand.commandID)
384 case kHICommandQuit:
385 mplayer_put_key(KEY_CLOSE_WIN);
386 break;
388 case kHalfScreenCmd:
389 set_winSizeMult(0.5);
390 break;
392 case kNormalScreenCmd:
393 set_winSizeMult(1);
394 break;
396 case kDoubleScreenCmd:
397 set_winSizeMult(2);
398 break;
400 case kFullScreenCmd:
401 vo_fs = !vo_fs;
402 window_fullscreen();
403 break;
405 case kKeepAspectCmd:
406 vo_keepaspect = !vo_keepaspect;
407 CheckMenuItem(aspectMenu, 1, vo_keepaspect);
408 window_resized();
409 break;
411 case kAspectOrgCmd:
412 change_movie_aspect(-1);
413 break;
415 case kAspectFullCmd:
416 change_movie_aspect(4.0 / 3.0);
417 break;
419 case kAspectWideCmd:
420 change_movie_aspect(16.0 / 9.0);
421 break;
423 case kPanScanCmd:
424 vo_panscan = !vo_panscan;
425 CheckMenuItem(aspectMenu, 2, vo_panscan);
426 window_panscan();
427 window_resized();
428 break;
430 default:
431 result = eventNotHandledErr;
432 break;
435 else if (class == kEventClassWindow)
437 WindowRef window;
438 Rect rectWindow = { 0, 0, 0, 0 };
440 GetEventParameter(event, kEventParamDirectObject, typeWindowRef, NULL, sizeof(window), NULL, &window);
442 if (window)
444 GetWindowBounds(window, kWindowGlobalPortRgn, &rectWindow);
447 switch (kind)
449 case kEventWindowClosed:
450 theWindow = NULL;
451 mplayer_put_key(KEY_CLOSE_WIN);
452 break;
454 // resize window
455 case kEventWindowZoomed:
456 case kEventWindowBoundsChanged:
457 window_resized();
458 flip_page();
459 window_resized();
460 break;
462 default:
463 result = eventNotHandledErr;
464 break;
468 return result;
471 static void quartz_CreateWindow(uint32_t d_width, uint32_t d_height, WindowAttributes windowAttrs)
473 CFStringRef titleKey;
474 CFStringRef windowTitle;
475 OSStatus result;
477 MenuItemIndex index;
478 CFStringRef movMenuTitle;
479 CFStringRef aspMenuTitle;
481 const EventTypeSpec win_events[] = {
482 {kEventClassWindow, kEventWindowClosed},
483 {kEventClassWindow, kEventWindowBoundsChanged},
484 {kEventClassCommand, kEventCommandProcess}
487 const EventTypeSpec key_events[] = {
488 {kEventClassKeyboard, kEventRawKeyDown},
489 {kEventClassKeyboard, kEventRawKeyRepeat}
492 const EventTypeSpec mouse_events[] = {
493 {kEventClassMouse, kEventMouseMoved},
494 {kEventClassMouse, kEventMouseWheelMoved},
495 {kEventClassMouse, kEventMouseDown},
496 {kEventClassMouse, kEventMouseUp},
497 {kEventClassMouse, kEventMouseDragged}
500 SetRect(&winRect, 0, 0, d_width, d_height);
501 SetRect(&oldWinRect, 0, 0, d_width, d_height);
502 SetRect(&dstRect, 0, 0, d_width, d_height);
504 // Clear Menu Bar
505 ClearMenuBar();
507 // Create Window Menu
508 CreateStandardWindowMenu(0, &windMenu);
509 InsertMenu(windMenu, 0);
511 // Create Movie Menu
512 CreateNewMenu(1004, 0, &movMenu);
513 movMenuTitle = CFSTR("Movie");
514 SetMenuTitleWithCFString(movMenu, movMenuTitle);
516 AppendMenuItemTextWithCFString(movMenu, CFSTR("Half Size"), 0, kHalfScreenCmd, &index);
517 SetMenuItemCommandKey(movMenu, index, 0, '0');
519 AppendMenuItemTextWithCFString(movMenu, CFSTR("Normal Size"), 0, kNormalScreenCmd, &index);
520 SetMenuItemCommandKey(movMenu, index, 0, '1');
522 AppendMenuItemTextWithCFString(movMenu, CFSTR("Double Size"), 0, kDoubleScreenCmd, &index);
523 SetMenuItemCommandKey(movMenu, index, 0, '2');
525 AppendMenuItemTextWithCFString(movMenu, CFSTR("Full Size"), 0, kFullScreenCmd, &index);
526 SetMenuItemCommandKey(movMenu, index, 0, 'F');
528 AppendMenuItemTextWithCFString(movMenu, NULL, kMenuItemAttrSeparator, 0, &index);
530 AppendMenuItemTextWithCFString(movMenu, CFSTR("Aspect Ratio"), 0, 0, &index);
532 //// Create Aspect Ratio Sub Menu
533 CreateNewMenu(0, 0, &aspectMenu);
534 aspMenuTitle = CFSTR("Aspect Ratio");
535 SetMenuTitleWithCFString(aspectMenu, aspMenuTitle);
536 SetMenuItemHierarchicalMenu(movMenu, 6, aspectMenu);
538 AppendMenuItemTextWithCFString(aspectMenu, CFSTR("Keep"), 0, kKeepAspectCmd, &index);
539 CheckMenuItem(aspectMenu, 1, vo_keepaspect);
540 AppendMenuItemTextWithCFString(aspectMenu, CFSTR("Pan-Scan"), 0, kPanScanCmd, &index);
541 CheckMenuItem(aspectMenu, 2, vo_panscan);
542 AppendMenuItemTextWithCFString(aspectMenu, NULL, kMenuItemAttrSeparator, 0, &index);
543 AppendMenuItemTextWithCFString(aspectMenu, CFSTR("Original"), 0, kAspectOrgCmd, &index);
544 AppendMenuItemTextWithCFString(aspectMenu, CFSTR("4:3"), 0, kAspectFullCmd, &index);
545 AppendMenuItemTextWithCFString(aspectMenu, CFSTR("16:9"), 0, kAspectWideCmd, &index);
547 InsertMenu(movMenu, GetMenuID(windMenu)); //insert before Window menu
549 DrawMenuBar();
551 // create window
552 CreateNewWindow(kDocumentWindowClass, windowAttrs, &winRect, &theWindow);
554 CreateWindowGroup(0, &winGroup);
555 SetWindowGroup(theWindow, winGroup);
557 // Set window title
558 titleKey = CFSTR("MPlayer - The Movie Player");
559 windowTitle = CFCopyLocalizedString(titleKey, NULL);
560 result = SetWindowTitleWithCFString(theWindow, windowTitle);
561 CFRelease(titleKey);
562 CFRelease(windowTitle);
564 // Install event handler
565 InstallApplicationEventHandler(NewEventHandlerUPP(KeyEventHandler), GetEventTypeCount(key_events), key_events, NULL, NULL);
566 InstallApplicationEventHandler(NewEventHandlerUPP(MouseEventHandler), GetEventTypeCount(mouse_events), mouse_events, NULL, NULL);
567 InstallWindowEventHandler(theWindow, NewEventHandlerUPP(WindowEventHandler), GetEventTypeCount(win_events), win_events, theWindow, NULL);
570 static void update_screen_info(void)
572 CGRect displayRect;
573 CGDisplayCount displayCount;
574 CGDirectDisplayID *displays;
575 // Display IDs might not be consecutive, get the list of all devices up to # device_id
576 displayCount = device_id + 1;
577 displays = malloc(sizeof(*displays) * displayCount);
578 if (kCGErrorSuccess != CGGetActiveDisplayList(displayCount, displays, &displayCount) || displayCount < device_id + 1) {
579 mp_msg(MSGT_VO, MSGL_FATAL, "Quartz error: Device ID %d do not exist, falling back to main device.\n", device_id);
580 displayId = kCGDirectMainDisplay;
581 device_id = 0;
583 else
585 displayId = displays[device_id];
587 free(displays);
589 displayRect = CGDisplayBounds(displayId);
590 xinerama_x = displayRect.origin.x;
591 xinerama_y = displayRect.origin.y;
592 vo_screenwidth = displayRect.size.width;
593 vo_screenheight = displayRect.size.height;
594 aspect_save_screenres(vo_screenwidth, vo_screenheight);
597 static void free_video_specific(void)
599 if (seqId) CDSequenceEnd(seqId);
600 seqId = 0;
601 free(image_data);
602 image_data = NULL;
603 free(P);
604 P = NULL;
605 CGDataProviderRelease(dataProviderRef);
606 dataProviderRef = NULL;
607 CGImageRelease(image);
608 image = NULL;
611 static int config(uint32_t width, uint32_t height, uint32_t d_width, uint32_t d_height, uint32_t flags, char *title, uint32_t format)
613 WindowAttributes windowAttrs;
614 OSErr qterr;
615 CGRect tmpBounds;
617 free_video_specific();
619 vo_dwidth = d_width *= winSizeMult;
620 vo_dheight = d_height *= winSizeMult;
621 config_movie_aspect((float)d_width / d_height);
623 // misc mplayer setup/////////////////////////////////////////////////////
624 SetRect(&imgRect, 0, 0, width, height);
625 switch (image_format)
627 case IMGFMT_RGB32:
628 image_depth = 32;
629 break;
630 case IMGFMT_YV12:
631 case IMGFMT_IYUV:
632 case IMGFMT_I420:
633 case IMGFMT_UYVY:
634 case IMGFMT_YUY2:
635 image_depth = 16;
636 break;
638 image_size = (imgRect.right * imgRect.bottom * image_depth + 7) / 8;
640 image_data = malloc(image_size);
642 // Create player window//////////////////////////////////////////////////
643 windowAttrs = kWindowStandardDocumentAttributes
644 | kWindowStandardHandlerAttribute
645 | kWindowLiveResizeAttribute;
647 windowAttrs &= ~kWindowResizableAttribute;
649 if (theWindow == NULL)
651 CGContextRef context;
653 quartz_CreateWindow(d_width, d_height, windowAttrs);
655 if (theWindow == NULL)
657 mp_msg(MSGT_VO, MSGL_FATAL, "Quartz error: Couldn't create window !!!!!\n");
658 return -1;
660 tmpBounds = CGRectMake(0, 0, winRect.right, winRect.bottom);
661 QDBeginCGContext(GetWindowPort(theWindow), &context);
662 CGContextFillRect(context, tmpBounds);
663 QDEndCGContext(GetWindowPort(theWindow), &context);
665 else
667 HideWindow(theWindow);
668 ChangeWindowAttributes(theWindow, ~windowAttrs, windowAttrs);
669 SetRect(&winRect, 0, 0, d_width, d_height);
670 SetRect(&oldWinRect, 0, 0, d_width, d_height);
671 SizeWindow(theWindow, d_width, d_height, 1);
674 switch (image_format)
676 case IMGFMT_RGB32:
678 CGContextRef context;
680 QDBeginCGContext(GetWindowPort(theWindow), &context);
682 dataProviderRef = CGDataProviderCreateWithData(0, image_data, imgRect.right * imgRect.bottom * 4, 0);
684 image = CGImageCreate(imgRect.right,
685 imgRect.bottom,
687 image_depth,
688 (imgRect.right * 32 + 7) / 8,
689 CGColorSpaceCreateDeviceRGB(),
690 kCGImageAlphaNoneSkipFirst,
691 dataProviderRef, 0, 1, kCGRenderingIntentDefault);
693 QDEndCGContext(GetWindowPort(theWindow), &context);
694 break;
697 case IMGFMT_YV12:
698 case IMGFMT_IYUV:
699 case IMGFMT_I420:
700 case IMGFMT_UYVY:
701 case IMGFMT_YUY2:
703 get_image_done = 0;
705 if (!EnterMoviesDone)
707 qterr = EnterMovies();
708 EnterMoviesDone = 1;
710 else
711 qterr = 0;
713 if (qterr)
715 mp_msg(MSGT_VO, MSGL_FATAL, "Quartz error: EnterMovies (%d)\n", qterr);
716 return -1;
720 SetIdentityMatrix(&matrix);
722 if (d_width != width || d_height != height)
724 ScaleMatrix(&matrix, FixDiv(Long2Fix(d_width), Long2Fix(width)), FixDiv(Long2Fix(d_height), Long2Fix(height)), 0, 0);
727 yuv_qt_stuff.desc = (ImageDescriptionHandle) NewHandleClear(sizeof(ImageDescription));
729 yuv_qt_stuff.extension_colr = NewHandleClear(sizeof(NCLCColorInfoImageDescriptionExtension));
730 ((NCLCColorInfoImageDescriptionExtension *) (*yuv_qt_stuff.extension_colr))->colorParamType = kVideoColorInfoImageDescriptionExtensionType;
731 ((NCLCColorInfoImageDescriptionExtension *) (*yuv_qt_stuff.extension_colr))->primaries = 2;
732 ((NCLCColorInfoImageDescriptionExtension *) (*yuv_qt_stuff.extension_colr))->transferFunction = 2;
733 ((NCLCColorInfoImageDescriptionExtension *) (*yuv_qt_stuff.extension_colr))->matrix = 2;
735 yuv_qt_stuff.extension_fiel = NewHandleClear(sizeof(FieldInfoImageDescriptionExtension));
736 ((FieldInfoImageDescriptionExtension *) (*yuv_qt_stuff.extension_fiel))->fieldCount = 1;
737 ((FieldInfoImageDescriptionExtension *) (*yuv_qt_stuff.extension_fiel))->fieldOrderings = 0;
739 yuv_qt_stuff.extension_clap = NewHandleClear(sizeof(CleanApertureImageDescriptionExtension));
740 ((CleanApertureImageDescriptionExtension *) (*yuv_qt_stuff.extension_clap))->cleanApertureWidthN = imgRect.right;
741 ((CleanApertureImageDescriptionExtension *) (*yuv_qt_stuff.extension_clap))->cleanApertureWidthD = 1;
742 ((CleanApertureImageDescriptionExtension *) (*yuv_qt_stuff.extension_clap))->cleanApertureHeightN = imgRect.bottom;
743 ((CleanApertureImageDescriptionExtension *) (*yuv_qt_stuff.extension_clap))->cleanApertureHeightD = 1;
744 ((CleanApertureImageDescriptionExtension *) (*yuv_qt_stuff.extension_clap))->horizOffN = 0;
745 ((CleanApertureImageDescriptionExtension *) (*yuv_qt_stuff.extension_clap))->horizOffD = 1;
746 ((CleanApertureImageDescriptionExtension *) (*yuv_qt_stuff.extension_clap))->vertOffN = 0;
747 ((CleanApertureImageDescriptionExtension *) (*yuv_qt_stuff.extension_clap))->vertOffD = 1;
749 yuv_qt_stuff.extension_pasp = NewHandleClear(sizeof(PixelAspectRatioImageDescriptionExtension));
750 ((PixelAspectRatioImageDescriptionExtension *) (*yuv_qt_stuff.extension_pasp))->hSpacing = 1;
751 ((PixelAspectRatioImageDescriptionExtension *) (*yuv_qt_stuff.extension_pasp))->vSpacing = 1;
753 (*yuv_qt_stuff.desc)->idSize = sizeof(ImageDescription);
754 (*yuv_qt_stuff.desc)->cType = image_qtcodec;
755 (*yuv_qt_stuff.desc)->version = 2;
756 (*yuv_qt_stuff.desc)->revisionLevel = 0;
757 (*yuv_qt_stuff.desc)->vendor = 'mpla';
758 (*yuv_qt_stuff.desc)->width = imgRect.right;
759 (*yuv_qt_stuff.desc)->height = imgRect.bottom;
760 (*yuv_qt_stuff.desc)->hRes = Long2Fix(72);
761 (*yuv_qt_stuff.desc)->vRes = Long2Fix(72);
762 (*yuv_qt_stuff.desc)->temporalQuality = 0;
763 (*yuv_qt_stuff.desc)->spatialQuality = codecLosslessQuality;
764 (*yuv_qt_stuff.desc)->frameCount = 1;
765 (*yuv_qt_stuff.desc)->dataSize = 0;
766 (*yuv_qt_stuff.desc)->depth = 24;
767 (*yuv_qt_stuff.desc)->clutID = -1;
769 qterr = AddImageDescriptionExtension(yuv_qt_stuff.desc, yuv_qt_stuff.extension_colr, kColorInfoImageDescriptionExtension);
770 if (qterr)
772 mp_msg(MSGT_VO, MSGL_ERR, "Quartz error: AddImageDescriptionExtension [colr] (%d)\n", qterr);
775 qterr = AddImageDescriptionExtension(yuv_qt_stuff.desc, yuv_qt_stuff.extension_fiel, kFieldInfoImageDescriptionExtension);
776 if (qterr)
778 mp_msg(MSGT_VO, MSGL_ERR, "Quartz error: AddImageDescriptionExtension [fiel] (%d)\n", qterr);
781 qterr = AddImageDescriptionExtension(yuv_qt_stuff.desc, yuv_qt_stuff.extension_clap, kCleanApertureImageDescriptionExtension);
782 if (qterr)
784 mp_msg(MSGT_VO, MSGL_ERR, "Quartz error: AddImageDescriptionExtension [clap] (%d)\n", qterr);
787 qterr = AddImageDescriptionExtension(yuv_qt_stuff.desc, yuv_qt_stuff.extension_pasp, kCleanApertureImageDescriptionExtension);
788 if (qterr)
790 mp_msg(MSGT_VO, MSGL_ERR, "Quartz error: AddImageDescriptionExtension [pasp] (%d)\n", qterr);
792 P = calloc(sizeof(PlanarPixmapInfoYUV420) + image_size, 1);
793 switch (image_format)
795 case IMGFMT_YV12:
796 case IMGFMT_IYUV:
797 case IMGFMT_I420:
798 P->componentInfoY.offset = be2me_32(sizeof(PlanarPixmapInfoYUV420));
799 P->componentInfoCb.offset = be2me_32(be2me_32(P->componentInfoY.offset) + image_size / 2);
800 P->componentInfoCr.offset = be2me_32(be2me_32(P->componentInfoCb.offset) + image_size / 4);
801 P->componentInfoY.rowBytes = be2me_32(imgRect.right);
802 P->componentInfoCb.rowBytes = be2me_32(imgRect.right / 2);
803 P->componentInfoCr.rowBytes = be2me_32(imgRect.right / 2);
804 image_buffer_size = image_size + sizeof(PlanarPixmapInfoYUV420);
805 break;
806 case IMGFMT_UYVY:
807 case IMGFMT_YUY2:
808 image_buffer_size = image_size;
809 break;
812 qterr = DecompressSequenceBeginS(&seqId,
813 yuv_qt_stuff.desc,
814 (char *)P,
815 image_buffer_size,
816 GetWindowPort(theWindow),
817 NULL,
818 NULL,
819 d_width != width || d_height != height ?
820 &matrix : NULL,
821 srcCopy,
822 NULL,
824 codecLosslessQuality,
825 bestSpeedCodec);
827 if (qterr)
829 mp_msg(MSGT_VO, MSGL_FATAL, "Quartz error: DecompressSequenceBeginS (%d)\n", qterr);
830 return -1;
833 break;
836 // Show window
837 RepositionWindow(theWindow, NULL, kWindowCenterOnMainScreen);
838 ShowWindow(theWindow);
840 if (vo_fs)
841 window_fullscreen();
843 if (vo_ontop)
844 window_ontop();
846 if (vo_rootwin)
848 vo_fs = TRUE;
849 winLevel = 0;
850 SetWindowGroupLevel(winGroup, CGWindowLevelForKey(levelList[winLevel]));
851 window_fullscreen();
854 window_resized();
856 return 0;
859 static void check_events(void)
861 EventRef theEvent;
862 EventTargetRef theTarget;
863 OSStatus theErr;
865 // Get event
866 theTarget = GetEventDispatcherTarget();
867 theErr = ReceiveNextEvent(0, 0, kEventDurationNoWait, true, &theEvent);
868 if (theErr == noErr && theEvent != NULL)
870 SendEventToEventTarget(theEvent, theTarget);
871 ReleaseEvent(theEvent);
875 static void draw_osd(void)
877 vo_draw_text(imgRect.right, imgRect.bottom, draw_alpha);
880 static void flip_page(void)
882 int curTime;
884 if (theWindow == NULL)
885 return;
887 switch (image_format)
889 case IMGFMT_RGB32:
891 CGContextRef context;
893 QDBeginCGContext(GetWindowPort(theWindow), &context);
894 CGContextDrawImage(context, bounds, image);
895 QDEndCGContext(GetWindowPort(theWindow), &context);
897 break;
899 case IMGFMT_YV12:
900 case IMGFMT_IYUV:
901 case IMGFMT_I420:
902 case IMGFMT_UYVY:
903 case IMGFMT_YUY2:
904 if (EnterMoviesDone)
906 OSErr qterr;
907 CodecFlags flags = 0;
909 qterr = DecompressSequenceFrameWhen(seqId,
910 (char *)P,
911 image_buffer_size,
912 0, //codecFlagUseImageBuffer,
913 &flags,
914 NULL,
915 NULL);
916 if (qterr)
918 mp_msg(MSGT_VO, MSGL_ERR, "Quartz error: DecompressSequenceFrameWhen in flip_page (%d) flags:0x%08x\n", qterr, flags);
921 break;
924 if (!vo_quartz_fs)
926 CGContextRef context;
928 QDBeginCGContext(GetWindowPort(theWindow), &context);
929 // render resize box
930 CGContextBeginPath(context);
931 CGContextSetAllowsAntialiasing(context, false);
932 //CGContextSaveGState(context);
934 // line white
935 CGContextSetRGBStrokeColor(context, 0.2, 0.2, 0.2, 0.5);
936 CGContextMoveToPoint(context, winRect.right - 1, 1); CGContextAddLineToPoint(context, winRect.right - 1, 1);
937 CGContextMoveToPoint(context, winRect.right - 1, 5); CGContextAddLineToPoint(context, winRect.right - 5, 1);
938 CGContextMoveToPoint(context, winRect.right - 1, 9); CGContextAddLineToPoint(context, winRect.right - 9, 1);
939 CGContextStrokePath(context);
941 // line gray
942 CGContextSetRGBStrokeColor(context, 0.4, 0.4, 0.4, 0.5);
943 CGContextMoveToPoint(context, winRect.right - 1, 2); CGContextAddLineToPoint(context, winRect.right - 2, 1);
944 CGContextMoveToPoint(context, winRect.right - 1, 6); CGContextAddLineToPoint(context, winRect.right - 6, 1);
945 CGContextMoveToPoint(context, winRect.right - 1, 10); CGContextAddLineToPoint(context, winRect.right - 10, 1);
946 CGContextStrokePath(context);
948 // line black
949 CGContextSetRGBStrokeColor(context, 0.6, 0.6, 0.6, 0.5);
950 CGContextMoveToPoint(context, winRect.right - 1, 3); CGContextAddLineToPoint(context, winRect.right - 3, 1);
951 CGContextMoveToPoint(context, winRect.right - 1, 7); CGContextAddLineToPoint(context, winRect.right - 7, 1);
952 CGContextMoveToPoint(context, winRect.right - 1, 11); CGContextAddLineToPoint(context, winRect.right - 11, 1);
953 CGContextStrokePath(context);
955 // CGContextRestoreGState( context );
956 CGContextFlush(context);
957 QDEndCGContext(GetWindowPort(theWindow), &context);
960 curTime = TickCount() / 60;
962 // auto hide mouse cursor (and future on-screen control?)
963 if (vo_quartz_fs && !mouseHide)
965 if (curTime - lastMouseHide >= 5 || lastMouseHide == 0)
967 CGDisplayHideCursor(displayId);
968 mouseHide = TRUE;
969 lastMouseHide = curTime;
972 // update activity every 30 seconds to prevent
973 // screensaver from starting up.
974 if (curTime - lastScreensaverUpdate >= 30 || lastScreensaverUpdate == 0)
976 UpdateSystemActivity(UsrActivity);
977 lastScreensaverUpdate = curTime;
981 static int draw_slice(uint8_t * src[], int stride[], int w, int h, int x, int y)
983 switch (image_format)
985 case IMGFMT_YV12:
986 case IMGFMT_I420:
987 memcpy_pic(((char *)P) + be2me_32(P->componentInfoY.offset) + x + imgRect.right * y, src[0], w, h, imgRect.right, stride[0]);
988 x=x/2;y=y/2;w=w/2;h=h/2;
990 memcpy_pic(((char *)P) + be2me_32(P->componentInfoCb.offset) + x + imgRect.right / 2 * y, src[1], w, h, imgRect.right / 2, stride[1]);
991 memcpy_pic(((char *)P) + be2me_32(P->componentInfoCr.offset) + x + imgRect.right / 2 * y, src[2], w, h, imgRect.right / 2, stride[2]);
992 return 0;
994 case IMGFMT_IYUV:
995 memcpy_pic(((char *)P) + be2me_32(P->componentInfoY.offset) + x + imgRect.right * y, src[0], w, h, imgRect.right, stride[0]);
996 x=x/2;y=y/2;w=w/2;h=h/2;
998 memcpy_pic(((char *)P) + be2me_32(P->componentInfoCr.offset) + x + imgRect.right / 2 * y, src[1], w, h, imgRect.right / 2, stride[1]);
999 memcpy_pic(((char *)P) + be2me_32(P->componentInfoCb.offset) + x + imgRect.right / 2 * y, src[2], w, h, imgRect.right / 2, stride[2]);
1000 return 0;
1002 return -1;
1005 static int draw_frame(uint8_t * src[])
1007 switch (image_format)
1009 case IMGFMT_RGB32:
1010 fast_memcpy(image_data, src[0], image_size);
1011 return 0;
1013 case IMGFMT_UYVY:
1014 case IMGFMT_YUY2:
1015 memcpy_pic(((char *)P), src[0], imgRect.right * 2, imgRect.bottom, imgRect.right * 2, imgRect.right * 2);
1016 return 0;
1018 return -1;
1021 static int query_format(uint32_t format)
1023 image_format = format;
1024 image_qtcodec = 0;
1026 if (format == IMGFMT_RGB32)
1028 return VFCAP_CSP_SUPPORTED | VFCAP_OSD | VFCAP_HWSCALE_UP | VFCAP_HWSCALE_DOWN;
1031 if (format == IMGFMT_YV12 || format == IMGFMT_IYUV || format == IMGFMT_I420)
1033 image_qtcodec = kMpegYUV420CodecType; //kYUV420CodecType ?;
1034 return VFCAP_CSP_SUPPORTED | VFCAP_OSD | VFCAP_HWSCALE_UP | VFCAP_HWSCALE_DOWN | VFCAP_ACCEPT_STRIDE;
1037 if (format == IMGFMT_YUY2)
1039 image_qtcodec = kComponentVideoUnsigned;
1040 return VFCAP_CSP_SUPPORTED | VFCAP_OSD | VFCAP_HWSCALE_UP | VFCAP_HWSCALE_DOWN;
1043 if (format == IMGFMT_UYVY)
1045 image_qtcodec = k422YpCbCr8CodecType;
1046 return VFCAP_CSP_SUPPORTED | VFCAP_OSD | VFCAP_HWSCALE_UP | VFCAP_HWSCALE_DOWN;
1049 return 0;
1052 static void uninit(void)
1054 free_video_specific();
1055 if (EnterMoviesDone)
1056 ExitMovies();
1057 EnterMoviesDone = 0;
1059 ShowMenuBar();
1062 static int preinit(const char *arg)
1064 int parse_err = 0;
1066 if(arg)
1068 char *parse_pos = (char *)&arg[0];
1070 while (parse_pos[0] && !parse_err)
1072 if (strncmp(parse_pos, "device_id=", 10) == 0)
1074 parse_pos = &parse_pos[10];
1075 device_id = strtol(parse_pos, &parse_pos, 0);
1077 if (strncmp(parse_pos, "fs_res=", 7) == 0)
1079 parse_pos = &parse_pos[7];
1080 fs_res_x = strtol(parse_pos, &parse_pos, 0);
1081 parse_pos = &parse_pos[1];
1082 fs_res_y = strtol(parse_pos, &parse_pos, 0);
1084 if (parse_pos[0] == ':')
1085 parse_pos = &parse_pos[1];
1086 else if (parse_pos[0])
1087 parse_err = 1;
1091 osx_foreground_hack();
1093 return 0;
1096 static uint32_t draw_yuv_image(mp_image_t * mpi)
1098 // ATM we're only called for planar IMGFMT
1099 // drawing is done directly in P
1100 // and displaying is in flip_page.
1101 return get_image_done ? VO_TRUE : VO_FALSE;
1104 static uint32_t get_yuv_image(mp_image_t * mpi)
1106 if (mpi->type != MP_IMGTYPE_EXPORT) return VO_FALSE;
1108 if (mpi->imgfmt != image_format) return VO_FALSE;
1110 if (mpi->flags & MP_IMGFLAG_PLANAR)
1112 if (mpi->num_planes != 3)
1114 mp_msg(MSGT_VO, MSGL_ERR, "Quartz error: only 3 planes allowed in get_yuv_image for planar (%d) \n", mpi->num_planes);
1115 return VO_FALSE;
1118 mpi->planes[0] = ((char *)P) + be2me_32(P->componentInfoY.offset);
1119 mpi->stride[0] = imgRect.right;
1120 mpi->width = imgRect.right;
1122 if (mpi->flags & MP_IMGFLAG_SWAPPED)
1124 // I420
1125 mpi->planes[1] = ((char *)P) + be2me_32(P->componentInfoCb.offset);
1126 mpi->planes[2] = ((char *)P) + be2me_32(P->componentInfoCr.offset);
1127 mpi->stride[1] = imgRect.right / 2;
1128 mpi->stride[2] = imgRect.right / 2;
1130 else
1132 // YV12
1133 mpi->planes[1] = ((char *)P) + be2me_32(P->componentInfoCr.offset);
1134 mpi->planes[2] = ((char *)P) + be2me_32(P->componentInfoCb.offset);
1135 mpi->stride[1] = imgRect.right / 2;
1136 mpi->stride[2] = imgRect.right / 2;
1139 mpi->flags |= MP_IMGFLAG_DIRECT;
1140 get_image_done = 1;
1141 return VO_TRUE;
1143 else
1145 // doesn't work yet
1146 if (mpi->num_planes != 1)
1148 mp_msg(MSGT_VO, MSGL_ERR, "Quartz error: only 1 plane allowed in get_yuv_image for packed (%d) \n", mpi->num_planes);
1149 return VO_FALSE;
1152 mpi->planes[0] = (char *)P;
1153 mpi->stride[0] = imgRect.right * 2;
1154 mpi->width = imgRect.right;
1155 mpi->flags |= MP_IMGFLAG_DIRECT;
1156 get_image_done = 1;
1157 return VO_TRUE;
1159 return VO_FALSE;
1162 static int control(uint32_t request, void *data)
1164 switch (request)
1166 case VOCTRL_PAUSE: return int_pause = 1;
1167 case VOCTRL_RESUME: return int_pause = 0;
1168 case VOCTRL_FULLSCREEN: vo_fs = !vo_fs; window_fullscreen(); return VO_TRUE;
1169 case VOCTRL_ONTOP: vo_ontop = !vo_ontop; window_ontop(); return VO_TRUE;
1170 case VOCTRL_QUERY_FORMAT: return query_format(*(uint32_t *) data);
1171 case VOCTRL_GET_PANSCAN: return VO_TRUE;
1172 case VOCTRL_SET_PANSCAN: window_panscan(); return VO_TRUE;
1174 case VOCTRL_GET_IMAGE:
1175 switch (image_format)
1177 case IMGFMT_YV12:
1178 case IMGFMT_IYUV:
1179 case IMGFMT_I420:
1180 case IMGFMT_UYVY:
1181 case IMGFMT_YUY2:
1182 return get_yuv_image(data);
1183 break;
1184 default:
1185 break;
1187 case VOCTRL_DRAW_IMAGE:
1188 switch (image_format)
1190 case IMGFMT_YV12:
1191 case IMGFMT_IYUV:
1192 case IMGFMT_I420:
1193 case IMGFMT_UYVY:
1194 case IMGFMT_YUY2:
1195 return draw_yuv_image(data);
1196 break;
1197 default:
1198 break;
1200 case VOCTRL_UPDATE_SCREENINFO:
1201 update_screen_info();
1202 return VO_TRUE;
1204 return VO_NOTIMPL;
1207 void window_resized(void)
1209 uint32_t d_width;
1210 uint32_t d_height;
1212 CGRect tmpBounds;
1214 CGContextRef context;
1216 GetWindowPortBounds(theWindow, &winRect);
1217 d_width = vo_dwidth = winRect.right;
1218 d_height = vo_dheight = winRect.bottom;
1220 if (vo_keepaspect)
1221 aspect(&d_width, &d_height, A_WINZOOM);
1222 SetRect(&dstRect, (vo_dwidth - d_width) / 2, (vo_dheight - d_height) / 2, d_width, d_height);
1224 switch (image_format)
1226 case IMGFMT_RGB32:
1228 bounds = CGRectMake(dstRect.left, dstRect.top, dstRect.right - dstRect.left, dstRect.bottom - dstRect.top);
1229 break;
1231 case IMGFMT_YV12:
1232 case IMGFMT_IYUV:
1233 case IMGFMT_I420:
1234 case IMGFMT_UYVY:
1235 case IMGFMT_YUY2:
1237 long scale_X = FixDiv(Long2Fix(dstRect.right - dstRect.left), Long2Fix(imgRect.right));
1238 long scale_Y = FixDiv(Long2Fix(dstRect.bottom - dstRect.top), Long2Fix(imgRect.bottom));
1240 SetIdentityMatrix(&matrix);
1241 if (dstRect.right - dstRect.left != imgRect.right || dstRect.bottom - dstRect.right != imgRect.bottom)
1243 ScaleMatrix(&matrix, scale_X, scale_Y, 0, 0);
1245 if (vo_dwidth > d_width || vo_dheight > d_height)
1247 TranslateMatrix(&matrix, Long2Fix(dstRect.left), Long2Fix(dstRect.top));
1251 SetDSequenceMatrix(seqId, &matrix);
1252 break;
1254 default:
1255 break;
1258 // Clear Background
1259 tmpBounds = CGRectMake(0, 0, winRect.right, winRect.bottom);
1260 QDBeginCGContext(GetWindowPort(theWindow), &context);
1261 CGContextFillRect(context, tmpBounds);
1262 QDEndCGContext(GetWindowPort(theWindow), &context);
1265 void window_ontop(void)
1267 if (!vo_quartz_fs)
1269 // Cycle between level
1270 winLevel++;
1271 if (winLevel > 2)
1272 winLevel = 1;
1274 SetWindowGroupLevel(winGroup, CGWindowLevelForKey(levelList[winLevel]));
1277 void window_fullscreen(void)
1279 // go fullscreen
1280 if (vo_fs)
1282 if (winLevel != 0)
1284 if (displayId == kCGDirectMainDisplay)
1286 SetSystemUIMode(kUIModeAllHidden, kUIOptionAutoShowMenuBar);
1287 CGDisplayHideCursor(displayId);
1288 mouseHide = TRUE;
1291 if (fs_res_x != 0 || fs_res_y != 0)
1293 CFDictionaryRef mode;
1294 size_t desiredBitDepth = 32;
1295 boolean_t exactMatch;
1297 originalMode = CGDisplayCurrentMode(displayId);
1299 mode = CGDisplayBestModeForParameters(displayId, desiredBitDepth, fs_res_x, fs_res_y, &exactMatch);
1301 if (mode != NULL)
1303 if (!exactMatch)
1305 // Warn if the mode doesn't match exactly
1306 mp_msg(MSGT_VO, MSGL_WARN, "Quartz warning: did not get exact mode match (got %dx%d) \n", (int)CFDictionaryGetValue(mode, kCGDisplayWidth), (int)CFDictionaryGetValue(mode, kCGDisplayHeight));
1309 CGDisplayCapture(displayId);
1310 CGDisplaySwitchToMode(displayId, mode);
1312 else
1314 mp_msg(MSGT_VO, MSGL_ERR, "Quartz error: can't switch to fullscreen \n");
1317 // Get Main device info///////////////////////////////////////////////////
1318 update_screen_info();
1321 // save old window size
1322 if (!vo_quartz_fs)
1324 GetWindowPortBounds(theWindow, &oldWinRect);
1325 GetWindowBounds(theWindow, kWindowContentRgn, &oldWinBounds);
1327 // go fullscreen
1328 ChangeWindowAttributes(theWindow, kWindowNoShadowAttribute, 0);
1330 vo_quartz_fs = 1;
1331 window_panscan();
1333 else //go back to windowed mode
1335 vo_quartz_fs = 0;
1336 if (originalMode != NULL)
1338 CGDisplaySwitchToMode(displayId, originalMode);
1339 CGDisplayRelease(displayId);
1341 // Get Main device info///////////////////////////////////////////////////
1342 update_screen_info();
1344 originalMode = NULL;
1346 SetSystemUIMode(kUIModeNormal, 0);
1348 // show mouse cursor
1349 CGDisplayShowCursor(displayId);
1350 mouseHide = FALSE;
1352 // revert window to previous setting
1353 ChangeWindowAttributes(theWindow, 0, kWindowNoShadowAttribute);
1354 SizeWindow(theWindow, oldWinRect.right, oldWinRect.bottom, 1);
1355 MoveWindow(theWindow, oldWinBounds.left, oldWinBounds.top, 1);
1357 window_resized();
1360 void window_panscan(void)
1362 panscan_calc();
1364 if (vo_panscan > 0)
1365 CheckMenuItem(aspectMenu, 2, 1);
1366 else
1367 CheckMenuItem(aspectMenu, 2, 0);
1369 if (vo_quartz_fs)
1371 MoveWindow(theWindow, xinerama_x - (vo_panscan_x >> 1), xinerama_y - (vo_panscan_y >> 1), 1);
1372 SizeWindow(theWindow, vo_screenwidth + vo_panscan_x, vo_screenheight + vo_panscan_y, 1);