view libvo/vo_quartz.c @ 12189:bd67f85c6440

-rdynamic is not supported on windows
author faust3
date Tue, 13 Apr 2004 10:02:37 +0000
parents 56e4423f16be
children 1da74cbaf324
line wrap: on
line source

/* 
 * vo_quartz.c
 *
 * Copyright (c) Nicolas Plourde - January 2004
 *
 * MPlayer Mac OSX Quartz video out module.
 *
 * TODO: -Fullscreen
 *       -Better event handling
 *
 * Note on performance:
 *  Right now i can play fullsize dvd video with -framedrop on my 
 *  iBook G4 800mhz. YUV to RGB converstion will speed up thing alot.
 *  Another thing is the slow fps when you maximize the window, I was
 *  not expecting that. I will fix this a.s.a.p. Im new to Mac 
 *  programming so help is welcome.
 */

//SYS
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
#include <errno.h>

//OSX
#include <Carbon/Carbon.h>
#include <QuickTime/QuickTime.h>

//MPLAYER
#include "config.h"
#include "video_out.h"
#include "video_out_internal.h"
#include "aspect.h"

#include "../input/input.h"
#include "../input/mouse.h"

#include "vo_quartz.h"

static vo_info_t info = {
  "MacOS X (Quartz)",
  "quartz",
  "Nicolas Plourde <nicolasplourde@hotmail.com>",
  ""
};

LIBVO_EXTERN (quartz)
static unsigned char *ImageData = NULL;

static uint32_t image_width;
static uint32_t image_height;
static uint32_t image_depth;
static uint32_t image_bytes;
static uint32_t image_format;

static int int_pause = 0;

int screen_width, screen_height;

WindowRef theWindow;
CGContextRef context;
CGRect bounds;
CGRect winBounds;
Rect contentRect;
CGImageRef image;
CGDataProviderRef dataProviderRef;
Ptr oldscreenstate;
RGBColor black = { 0, 0, 0 };
float winAlpha = 1;

#include "../osdep/keycodes.h"
extern void mplayer_put_key (int code);

//PROTOTYPE/////////////////////////////////////////////////////////////////
void resize_window (uint32_t width, uint32_t height);
static OSStatus MainWindowEventHandler (EventHandlerCallRef nextHandler,
					EventRef event, void *userData);
static OSStatus MainKeyEventHandler (EventHandlerCallRef nextHandler,
				     EventRef event, void *userData);


//default window event handler
static OSStatus
MainWindowEventHandler (EventHandlerCallRef nextHandler, EventRef event,
			void *userData)
{
  OSStatus err = noErr;
  WindowRef window;
  Rect rectPort = { 0, 0, 0, 0 };
  OSStatus result = eventNotHandledErr;
  UInt32 class = GetEventClass (event);
  UInt32 kind = GetEventKind (event);

  GetEventParameter (event, kEventParamDirectObject, typeWindowRef, NULL,
		     sizeof (WindowRef), NULL, &window);
  if (window)
    {
      GetWindowPortBounds (window, &rectPort);
    }

  switch (kind)
    {
    case kEventWindowActivated:

    case kEventWindowDrawContent:
      break;

    case kEventWindowClosed:
      HideWindow (window);
      mplayer_put_key (KEY_ESC);
      break;

    case kEventWindowShown:
      InvalWindowRect (window, &rectPort);
      break;

    case kEventWindowBoundsChanged:
      resize_window (rectPort.right, rectPort.bottom);
      break;

    case kEventWindowZoomed:
      resize_window (rectPort.right, rectPort.bottom);
      break;

    default:
      err = eventNotHandledErr;
      break;
    }

  return err;
}

//keyboard event handler
static OSStatus
MainKeyEventHandler (EventHandlerCallRef nextHandler, EventRef event,
		     void *userData)
{
  OSStatus err = noErr;
  UInt32 macKeyCode;

  GetEventParameter (event, kEventParamKeyCode, typeUInt32, NULL,
		     sizeof (macKeyCode), NULL, &macKeyCode);

  switch (GetEventKind (event))
    {
    case kEventRawKeyDown:
      {
	switch (macKeyCode)
	  {
	  case QZ_RETURN:
	    mplayer_put_key (KEY_ENTER);
	    break;
	  case QZ_ESCAPE:
	    EndFullScreen (oldscreenstate, 0);
	    QuitApplicationEventLoop ();
	    mplayer_put_key (KEY_ESC);
	    break;
	  case QZ_q:
	    mplayer_put_key ('q');
	    break;
	  case QZ_F1:
	    mplayer_put_key (KEY_F + 1);
	    break;
	  case QZ_F2:
	    mplayer_put_key (KEY_F + 2);
	    break;
	  case QZ_F3:
	    mplayer_put_key (KEY_F + 3);
	    break;
	  case QZ_F4:
	    mplayer_put_key (KEY_F + 4);
	    break;
	  case QZ_F5:
	    mplayer_put_key (KEY_F + 5);
	    break;
	  case QZ_F6:
	    mplayer_put_key (KEY_F + 6);
	    break;
	  case QZ_F7:
	    mplayer_put_key (KEY_F + 7);
	    break;
	  case QZ_F8:
	    mplayer_put_key (KEY_F + 8);
	    break;
	  case QZ_F9:
	    mplayer_put_key (KEY_F + 9);
	    break;
	  case QZ_F10:
	    mplayer_put_key (KEY_F + 10);
	    break;
	  case QZ_F11:
	    mplayer_put_key (KEY_F + 11);
	    break;
	  case QZ_F12:
	    mplayer_put_key (KEY_F + 12);
	    break;
	  case QZ_o:
	    mplayer_put_key ('o');
	    break;
	  case QZ_SPACE:
	    mplayer_put_key (' ');
	    break;
	  case QZ_p:
	    mplayer_put_key ('p');
	    break;
	    //case QZ_7: mplayer_put_key(shift_key?'/':'7');
	    //case QZ_PLUS: mplayer_put_key(shift_key?'*':'+');
	  case QZ_KP_PLUS:
	    mplayer_put_key ('+');
	    break;
	  case QZ_MINUS:
	  case QZ_KP_MINUS:
	    mplayer_put_key ('-');
	    break;
	  case QZ_TAB:
	    mplayer_put_key ('\t');
	    break;
	  case QZ_PAGEUP:
	    mplayer_put_key (KEY_PAGE_UP);
	    break;
	  case QZ_PAGEDOWN:
	    mplayer_put_key (KEY_PAGE_DOWN);
	    break;
	  case QZ_UP:
	    mplayer_put_key (KEY_UP);
	    break;
	  case QZ_DOWN:
	    mplayer_put_key (KEY_DOWN);
	    break;
	  case QZ_LEFT:
	    mplayer_put_key (KEY_LEFT);
	    break;
	  case QZ_RIGHT:
	    mplayer_put_key (KEY_RIGHT);
	    break;
	    //case QZ_LESS: mplayer_put_key(shift_key?'>':'<'); break;
	    //case QZ_GREATER: mplayer_put_key('>'); break;
	    //case QZ_ASTERISK:
	  case QZ_KP_MULTIPLY:
	    mplayer_put_key ('*');
	    break;
	  case QZ_SLASH:
	  case QZ_KP_DIVIDE:
	    mplayer_put_key ('/');
	    break;
	  case QZ_KP0:
	    mplayer_put_key (KEY_KP0);
	    break;
	  case QZ_KP1:
	    mplayer_put_key (KEY_KP1);
	    break;
	  case QZ_KP2:
	    mplayer_put_key (KEY_KP2);
	    break;
	  case QZ_KP3:
	    mplayer_put_key (KEY_KP3);
	    break;
	  case QZ_KP4:
	    mplayer_put_key (KEY_KP4);
	    break;
	  case QZ_KP5:
	    mplayer_put_key (KEY_KP5);
	    break;
	  case QZ_KP6:
	    mplayer_put_key (KEY_KP6);
	    break;
	  case QZ_KP7:
	    mplayer_put_key (KEY_KP7);
	    break;
	  case QZ_KP8:
	    mplayer_put_key (KEY_KP8);
	    break;
	  case QZ_KP9:
	    mplayer_put_key (KEY_KP9);
	    break;
	  case QZ_KP_PERIOD:
	    mplayer_put_key (KEY_KPDEC);
	    break;
	  case QZ_KP_ENTER:
	    mplayer_put_key (KEY_KPENTER);
	    break;
	  case QZ_LEFTBRACKET:
	    SetWindowAlpha (theWindow, winAlpha -= 0.05);
	    break;
	  case QZ_RIGHTBRACKET:
	    SetWindowAlpha (theWindow, winAlpha += 0.05);
	    break;
	  case QZ_f:
	    break;
	  default:
	    break;
	  }
      }
      break;
    default:
      err = eventNotHandledErr;
      break;
    }

  return err;
}

static uint32_t
config (uint32_t width, uint32_t height, uint32_t d_width, uint32_t d_height,
	uint32_t flags, char *title, uint32_t format)
{
  WindowAttributes windowAttrs;
  CFStringRef titleKey;
  CFStringRef windowTitle;
  OSStatus result;
  GDHandle deviceHdl;
  Rect deviceRect;

  //Get Main device info///////////////////////////////////////////////////
  deviceHdl = GetMainDevice ();
  deviceRect = (*deviceHdl)->gdRect;

  screen_width = deviceRect.right;
  screen_height = deviceRect.bottom;

  //misc mplayer setup/////////////////////////////////////////////////////
  image_width = width;
  image_height = height;
  image_depth = IMGFMT_RGB_DEPTH (format);
  image_bytes = (IMGFMT_RGB_DEPTH (format) + 7) / 8;

  aspect_save_orig (width, height);
  aspect_save_prescale (d_width, d_height);
  aspect_save_screenres (screen_width, screen_height);

  aspect (&d_width, &d_height, A_NOZOOM);

  if (ImageData)
    free (ImageData);
  ImageData = malloc (image_width * image_height * image_bytes);

  //Create player window//////////////////////////////////////////////////
  windowAttrs = kWindowStandardDocumentAttributes
    | kWindowMetalAttribute
    | kWindowStandardHandlerAttribute
    | kWindowInWindowMenuAttribute
    | kWindowLiveResizeAttribute | kWindowCompositingAttribute;

  SetRect (&contentRect, 0, 0, d_width, d_height);
  CreateNewWindow (kDocumentWindowClass, windowAttrs, &contentRect,
		   &theWindow);

  titleKey = CFSTR ("MPlayer");
  windowTitle = CFCopyLocalizedString (titleKey, NULL);
  result = SetWindowTitleWithCFString (theWindow, windowTitle);
  CFRelease (titleKey);
  CFRelease (windowTitle);

  const EventTypeSpec winEvents[] = {
    {kEventClassWindow, kEventWindowActivated},
    {kEventClassWindow, kEventWindowDrawContent},
    {kEventClassWindow, kEventWindowClosed},
    {kEventClassWindow, kEventWindowShown},
    {kEventClassWindow, kEventWindowBoundsChanged},
    {kEventClassWindow, kEventWindowZoomed}
  };

  const EventTypeSpec keyEvents[] =
    { {kEventClassKeyboard, kEventRawKeyDown} };
  InstallWindowEventHandler (theWindow,
			     NewEventHandlerUPP (MainWindowEventHandler),
			     GetEventTypeCount (winEvents), winEvents,
			     theWindow, NULL);
  InstallWindowEventHandler (theWindow,
			     NewEventHandlerUPP (MainKeyEventHandler),
			     GetEventTypeCount (keyEvents), keyEvents,
			     theWindow, NULL);

  RepositionWindow (theWindow, NULL, kWindowCascadeOnMainScreen);
  ShowWindow (theWindow);

  //Setup Quartz context
  CreateCGContextForPort (GetWindowPort (theWindow), &context);

  //set size and aspect for current window
  resize_window (d_width, d_height);

  return 0;
}

//resize drawing context to fit window
void
resize_window (uint32_t width, uint32_t height)
{
  //this is a "wow it work". Need some improvement.
  uint32_t d_width;
  uint32_t d_height;
  uint32_t size;
  Rect tmpRect;

  float aspectX;
  float aspectY;

  aspect (&d_width, &d_height, A_NOZOOM);

  aspectX = (float) ((float) d_width * (width / (float) d_width));
  aspectY = (float) ((float) d_height * (width / (float) d_width));

  if (aspectY > height)
    {
      aspectX = (float) ((float) d_width * (height / (float) d_height));
      aspectY = (float) ((float) d_height * (height / (float) d_height));

      bounds = CGRectMake ((width - aspectX) / 2, 0, aspectX, aspectY);
    }
  else
    {
      bounds = CGRectMake (0, (height - aspectY) / 2, aspectX, aspectY);
    }

  //create a graphic context for the window
  GetWindowPortBounds (theWindow, &tmpRect);
  SetPortBounds (GetWindowPort (theWindow), &tmpRect);
  CreateCGContextForPort (GetWindowPort (theWindow), &context);

  //fill background with black
  winBounds =
    CGRectMake (tmpRect.top, tmpRect.left, tmpRect.right, tmpRect.bottom);
  CGContextSetRGBFillColor (context, 0.0, 0.0, 0.0, 1.0);
  CGContextFillRect (context, winBounds);
}

static void
check_events (void)
{
  EventRef theEvent;
  EventTargetRef theTarget;

  theTarget = GetEventDispatcherTarget ();

  ReceiveNextEvent (0, NULL, kEventDurationNoWait, true, &theEvent);
  SendEventToEventTarget (theEvent, theTarget);
  ReleaseEvent (theEvent);

  //if(VO_EVENT_RESIZE) resize_window(vo_dwidth,vo_dheight);
  if (VO_EVENT_EXPOSE && int_pause)
    flip_page ();
}

static void
draw_osd (void)
{
}

static void
flip_page (void)
{
  CGContextFlush (context);
}

static uint32_t
draw_slice (uint8_t * src[], int stride[], int w, int h, int x, int y)
{
  return -1;
}

static uint32_t
draw_frame (uint8_t * src[])
{
  //this is very slow. I have to find another way.
  CGImageAlphaInfo alphaInfo;

  dataProviderRef =
    CGDataProviderCreateWithData (0, src[0],
				  image_width * image_height * image_bytes,
				  0);

  if (image_format == IMGFMT_RGB24)
    alphaInfo = kCGImageAlphaNone;
  else if (image_format == IMGFMT_RGB32)
    alphaInfo = kCGImageAlphaNoneSkipFirst;

  image = CGImageCreate (image_width,
			 image_height,
			 8,
			 image_depth,
			 ((image_width * image_depth) + 7) / 8,
			 CGColorSpaceCreateDeviceRGB (),
			 alphaInfo,
			 dataProviderRef, 0, 0, kCGRenderingIntentDefault);

  CGContextDrawImage (context, bounds, image);

  return 0;
}

static uint32_t
query_format (uint32_t format)
{
  image_format = format;

  //Curently supporting only rgb format.
  if ((format == IMGFMT_RGB24) || (format == IMGFMT_RGB32))
    return VFCAP_CSP_SUPPORTED | VFCAP_CSP_SUPPORTED_BY_HW;
  return 0;
}


static void
uninit (void)
{
}

static uint32_t
preinit (const char *arg)
{
  return 0;
}

static uint32_t
control (uint32_t request, void *data, ...)
{
  switch (request)
    {
    case VOCTRL_PAUSE:
      return (int_pause = 1);
    case VOCTRL_RESUME:
      return (int_pause = 0);
    case VOCTRL_QUERY_FORMAT:
      return query_format (*((uint32_t *) data));
    }
  return VO_NOTIMPL;
}