1 /*****************************************************************************
2 * intf_macosx.c: MacOS X interface plugin
3 *****************************************************************************
4 * Copyright (C) 2001 VideoLAN
8 * This program 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.
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
18 * You should have received a copy of the GNU General Public License
19 * along with this program; if not, write to the Free Software
20 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111, USA.
21 *****************************************************************************/
23 #define MODULE_NAME macosx
24 #include "modules_inner.h"
26 /*****************************************************************************
28 *****************************************************************************/
31 #include <stdlib.h> /* malloc(), free() */
40 #include "interface.h"
44 #include "stream_control.h"
45 #include "input_ext-intf.h"
47 #include "intf_playlist.h"
48 #include "audio_output.h"
52 #include <Carbon/Carbon.h>
54 //how often to have callback to main loop. Target of 30fps then 30hz + maybe some more...
55 //it doesn't really scale if we move to 2x the hz... something else is slowing us down...
57 #define kMainLoopFrequency (kEventDurationSecond / 45) //45 for good measure
70 kAppleQuit = 7, //is this always the same?
74 kFileCloseDivisor = 3,
77 kControlsPlayORPause = 1,
81 kControlsChapterDiv = 5,
82 kControlsChapterNext = 6,
83 kControlsChapterPrevious = 7,
86 kControlsVolumeDiv = 10,
87 kControlsVolumeUp = 11,
88 kControlsVolumeDown = 12,
89 kControlsVolumeMute = 13,
90 kControlsEjectDiv = 14,
99 kAppleQuit = 8, //is this always the same?
107 kControlsPlayORPause = 1,
112 kControlsChapterNext,
113 kControlsChapterPrevious,
125 //virtual key codes ; raw subtract 0x40 from these values
126 //http://devworld.apple.com/techpubs/mac/Text/Text-577.html#HEADING577-0
132 //http://devworld.apple.com/techpubs/mac/Text/Text-571.html#MARKER-9-18
133 kPeriod = 47, //(decimal)
134 kSpace = 49, //(decimal)
135 kEscape = 53 //(decimal)
140 /*****************************************************************************
141 * intf_sys_t: description and status of the interface
142 *****************************************************************************/
143 typedef struct intf_sys_s
145 EventLoopTimerRef manageTimer;
149 /*****************************************************************************
151 *****************************************************************************/
152 static int intf_Probe ( probedata_t *p_data );
153 static int intf_Open ( intf_thread_t *p_intf );
154 static void intf_Close ( intf_thread_t *p_intf );
155 static void intf_Run ( intf_thread_t *p_intf );
159 void CarbonManageCallback ( EventLoopTimerRef inTimer, void *inUserData );
162 void EventLoop( intf_thread_t *p_intf );
163 void DoEvent( intf_thread_t *p_intf , EventRecord *event);
164 void DoMenuCommand( intf_thread_t *p_intf , long menuResult);
165 void DrawWindow(WindowRef window);
168 pascal OSErr QuitEventHandler(const AppleEvent *theEvent, AppleEvent *theReply, SInt32 refCon);
169 static pascal OSStatus MyKeyHandler( EventHandlerCallRef inCallRef, EventRef inEvent, void* userData );
170 static pascal OSStatus MyWindowEventHandler(EventHandlerCallRef myHandler, EventRef event, void* userData);
174 /*****************************************************************************
175 * Functions exported as capabilities. They are declared as static so that
176 * we don't pollute the namespace too much.
177 *****************************************************************************/
178 void _M( intf_getfunctions )( function_list_t * p_function_list )
180 p_function_list->pf_probe = intf_Probe;
181 p_function_list->functions.intf.pf_open = intf_Open;
182 p_function_list->functions.intf.pf_close = intf_Close;
183 p_function_list->functions.intf.pf_run = intf_Run;
186 /*****************************************************************************
187 * intf_Probe: probe the interface and return a score
188 *****************************************************************************
189 * This function checks the interface can be run and returns a score to the
190 * plugin manager so that it can select the best plugin.
191 *****************************************************************************/
192 static int intf_Probe( probedata_t *p_data )
194 if( TestMethod( INTF_METHOD_VAR, "macosx" ) )
199 /* Under MacOS X, this plugin always works */
203 /*****************************************************************************
204 * intf_Open: initialize interface
205 *****************************************************************************/
206 static int intf_Open( intf_thread_t *p_intf )
211 /* Allocate instance and initialize some members */
212 p_intf->p_sys = malloc( sizeof( intf_sys_t ) );
213 if( p_intf->p_sys == NULL )
220 SetQDGlobalsRandomSeed( TickCount() );
222 // neat menu but don't know if we want it.
223 // Install the Windows menu. Free of charge!
224 // CreateStandardWindowMenu( 0, &windMenu );
225 // InsertMenu( windMenu, 0 );
228 menu = NewMenu( kMenuApple, "\p\024" );
229 AppendMenu( menu, "\pAbout VLCÉ/A" );
230 InsertMenu( menu, 0 );
232 menu = NewMenu( kMenuFile, "\pFile" );
233 AppendMenu( menu, "\pNew Viewer Window/N" );
234 AppendMenu( menu, "\pOpenÉ/O" );
235 AppendMenu( menu, "\p(-" );
236 AppendMenu( menu, "\pClose/W" );
237 //standard OS X application menu quit isn't working nicely
238 AppendMenu( menu, "\pQuit/Q" );
239 InsertMenu( menu, 0 );
241 //BIG HONKING MENU - in order Mac OS 9 dvd player
242 //can't get key codes right for menus... argh that's why they use resources!
244 menu = NewMenu( kMenuControls, "\pControls" );
246 AppendMenu( menu, "\pPlay/," );
247 // SetMenuItemCommandKey(menu, 0, false, kSpace);
248 // SetMenuItemModifiers( menu, 0, kMenuNoCommandModifier);
250 AppendMenu( menu, "\pStop/." );
252 AppendMenu( menu, "\pFast Forward/f" );
253 // SetMenuItemCommandKey(menu, 2, false, kRightArrow);
255 AppendMenu( menu, "\pRewind/r" );
256 // SetMenuItemCommandKey(menu, 3, false, kLeftArrow);
258 AppendMenu( menu, "\p(-" ); //4
260 AppendMenu( menu, "\pNext Chapter/c" );
261 // SetMenuItemCommandKey(menu, 5, false, kRightArrow);
262 // SetMenuItemModifiers( menu, 5, kMenuNoCommandModifier);
264 AppendMenu( menu, "\pPrevious Chapter/p" );
265 // SetMenuItemCommandKey(menu, 6, false, kLeftArrow);
266 // SetMenuItemModifiers( menu, 6, kMenuNoCommandModifier);
268 AppendMenu( menu, "\p(-" ); //7
270 AppendMenu( menu, "\pDVD Menu/v" );
271 // SetMenuItemCommandKey(menu, 8, false, kEscape);
272 // SetMenuItemModifiers( menu, 8, kMenuNoCommandModifier);
274 AppendMenu( menu, "\p(-" ); //9
276 AppendMenu( menu, "\pVolume Up/u" );
277 // SetMenuItemCommandKey(menu, 10, false, kUpArrow);
279 AppendMenu( menu, "\pVolume Down/d" );
280 // SetMenuItemCommandKey(menu, 11, false, kDownArrow);
282 AppendMenu( menu, "\pMute/M" ); //12
284 AppendMenu( menu, "\p(-" ); //13
286 AppendMenu( menu, "\pEject/E" ); //14
288 InsertMenu( menu, 0 );
297 /*****************************************************************************
298 * intf_Close: destroy interface
299 *****************************************************************************/
300 static void intf_Close( intf_thread_t *p_intf )
302 /* Destroy structure */
303 free( p_intf->p_sys );
306 /*****************************************************************************
307 * intf_Run: main loop
308 *****************************************************************************/
309 static void intf_Run( intf_thread_t *p_intf )
313 EventLoopTimerUPP manageUPP;
316 Eventually we want to use Carbon events, or maybe even write this app in Cocoa
319 // EventTypeSpec windowEventType = { kEventClassWindow, kEventWindowClose };
320 // EventHandlerUPP windowHandlerUPP;
322 //kinda going out of bounds here... need to bring window creation to this file.
326 EventTypeSpec keyboardEventType = { kEventClassKeyboard, kEventRawKeyDown };
327 EventHandlerUPP keyboardHandlerUPP;
330 manageUPP = NewEventLoopTimerUPP ( CarbonManageCallback );
331 err = InstallEventLoopTimer ( GetCurrentEventLoop(), 0, kMainLoopFrequency, manageUPP, (void *) p_intf, &p_intf->p_sys->manageTimer );
332 assert(err == noErr);
333 DisposeEventLoopTimerUPP(manageUPP);
335 /* windowHandlerUPP = NewEventHandlerUPP ( MyWindowEventHandler );
336 err = InstallWindowEventHandler ( p_main->p_vout->p_sys->p_window , windowHandlerUPP, GetEventTypeCount(windowEventType), &windowEventType, (void *) p_intf, NULL );
337 assert(err == noErr);
338 DisposeEventHandlerUPP(windowHandlerUPP);
346 //Our big event loop !-)
347 RunApplicationEventLoop();
349 err = RemoveEventLoopTimer(p_intf->p_sys->manageTimer);
350 assert(err == noErr);
355 void CarbonManageCallback ( EventLoopTimerRef inTimer, void *inUserData )
357 intf_thread_t * p_intf = (intf_thread_t *) inUserData;
359 /* Manage core vlc functions through the callback */
360 p_intf->pf_manage( p_intf );
364 QuitApplicationEventLoop();
370 void EventLoop( intf_thread_t *p_intf )
377 p_intf->pf_manage( p_intf );
378 gotEvent = WaitNextEvent(everyEvent,&event,32767,nil);
380 DoEvent( p_intf, &event);
381 } while (! p_intf->b_die );
387 void DoEvent( intf_thread_t *p_intf , EventRecord *event)
393 WindowRef whichWindow;
398 part = FindWindow(event->where, &whichWindow);
401 case inMenuBar: /* process a moused menu command */
402 DoMenuCommand( p_intf, MenuSelect(event->where));
409 if (whichWindow != FrontWindow())
410 SelectWindow(whichWindow);
413 case inDrag: /* pass screenBits.bounds */
414 GetRegionBounds(GetGrayRgn(), &tempRect);
415 DragWindow(whichWindow, event->where, &tempRect);
422 p_intf->b_die = true;
424 //DisposeWindow(whichWindow);
430 hit = TrackBox(whichWindow, event->where, part);
433 SetPort(GetWindowPort(whichWindow)); // window must be current port
434 EraseRect(GetWindowPortBounds(whichWindow, &tempRect)); // inval/erase because of ZoomWindow bug
435 ZoomWindow(whichWindow, part, true);
436 InvalWindowRect(whichWindow, GetWindowPortBounds(whichWindow, &tempRect));
444 key = event->message & charCodeMask;
445 if (event->modifiers & cmdKey)
446 if (event->what == keyDown)
447 DoMenuCommand( p_intf, MenuKey(key));
449 case activateEvt: /* if you needed to do something special */
453 DrawWindow((WindowRef) event->message);
456 case kHighLevelEvent:
457 AEProcessAppleEvent( event );
465 void DoMenuCommand( intf_thread_t *p_intf , long menuResult)
467 short menuID; /* the resource ID of the selected menu */
468 short menuItem; /* the item number of the selected menu */
470 static int vol_val; // remember the current volume
471 static int playback_status; // remember playback state
473 menuID = HiWord(menuResult); /* use macros to get item & menu number */
474 menuItem = LoWord(menuResult);
488 p_intf->b_die = true;
512 char device_method_and_name[B_FILE_NAME_LENGTH + 4];
513 if(p_message->FindString("device", device) != B_ERROR)
515 sprintf(device_method_and_name, "dvd:%s", *device);
516 intf_PlaylistAdd( p_main->p_playlist, PLAYLIST_END, device_method_and_name );
525 HideWindow( FrontWindow() );
532 p_intf->b_die = true;
543 case kControlsPlayORPause:
544 // pause the playback
545 if (p_intf->p_input != NULL )
547 // mute the volume if currently playing
548 if (playback_status == PLAYING)
550 if (p_main->p_aout != NULL)
552 p_main->p_aout->vol = 0;
554 playback_status = PAUSED;
557 // restore the volume
559 if (p_main->p_aout != NULL)
561 p_main->p_aout->vol = vol_val;
563 playback_status = PLAYING;
566 input_SetStatus(p_intf->p_input, INPUT_STATUS_PAUSE);
571 // this currently stops playback not nicely
572 if (p_intf->p_input != NULL )
574 // silence the sound, otherwise very horrible
575 if (p_main->p_aout != NULL)
577 p_main->p_aout->vol = 0;
580 input_SetStatus(p_intf->p_input, INPUT_STATUS_END);
584 case kControlsForward:
585 // cycle the fast playback modes
586 if (p_intf->p_input != NULL )
588 if (p_main->p_aout != NULL)
590 p_main->p_aout->vol = 0;
593 input_SetStatus(p_intf->p_input, INPUT_STATUS_FASTER);
597 case kControlsRewind:
598 // cycle the slow playback modes
599 if (p_intf->p_input != NULL )
601 if (p_main->p_aout != NULL)
603 p_main->p_aout->vol = 0;
606 input_SetStatus(p_intf->p_input, INPUT_STATUS_SLOWER);
610 case kControlsChapterNext:
611 if( p_intf->p_input != NULL )
613 /* FIXME: temporary hack */
614 p_intf->p_input->b_eof = 1;
618 case kControlsChapterPrevious:
619 if( p_intf->p_input != NULL )
621 /* FIXME: temporary hack */
622 intf_PlaylistPrev( p_main->p_playlist );
623 intf_PlaylistPrev( p_main->p_playlist );
624 p_intf->p_input->b_eof = 1;
628 case kControlsDVDMenu:
633 case kControlsVolumeUp:
635 if (p_main->p_aout != NULL)
637 p_main->p_aout->vol++;
641 case kControlsVolumeDown:
643 if (p_main->p_aout != NULL)
645 p_main->p_aout->vol--;
649 case kControlsVolumeMute:
651 if (p_main->p_aout != NULL)
653 if (p_main->p_aout->vol == 0)
655 //p_vol->SetEnabled(true);
656 p_main->p_aout->vol = vol_val;
660 //p_vol->SetEnabled(false);
661 vol_val = p_main->p_aout->vol;
662 p_main->p_aout->vol = 0;
680 HiliteMenu(0); /* unhighlight what MenuSelect (or MenuKey) hilited */
683 void DrawWindow(WindowRef window)
689 SetPort(GetWindowPort(window));
691 EraseRect(GetWindowPortBounds(window, &tempRect));
692 DrawControls(window);
693 DrawGrowIcon(window);
701 static pascal OSStatus MyEventHandler(EventHandlerCallRef myHandler, EventRef event, void* userData)
706 HICommand commandStruct;
709 OSStatus result = eventNotHandledErr; // report failure by default
711 GetEventParameter(event, kEventParamDirectObject, typeWindowRef, NULL, sizeof(window), NULL, &window);
713 whatHappened = GetEventKind(event);
715 switch (whatHappened)
717 case kEventWindowActivated:
720 case kEventWindowDeactivated:
723 case kEventWindowDrawContent:
728 case kEventWindowBoundsChanged:
729 InvalWindowRect(window, GetWindowPortBounds(window, &bounds));
734 case kEventWindowClickContentRgn:
735 /*DoContentClick(window);
741 case kEventCommandProcess:
742 GetEventParameter (event, kEventParamDirectObject,
743 typeHICommand, NULL, sizeof(HICommand),
744 NULL, &commandStruct);
745 theMenuRef = commandStruct.menu.menuRef;
747 if (theMenuRef == GetMenuHandle(kMenuApple))
749 // Because the event didn't occur *in* the window, the
750 // window reference isn't valid until we set it here
751 window = FrontWindow();
753 theMenuItem = commandStruct.menu.menuItemIndex;
754 switch ( theMenuItem )
757 SetLight(window, true);
760 SetLight(window, false);
770 case kEventMouseMoved:
772 CursorRgn = NewRgn();
773 GetEventParameter (event, kEventParamMouseLocation, typeQDPoint,
774 NULL, sizeof(Point), NULL, &wheresMyMouse);
775 AdjustCursor(wheresMyMouse, CursorRgn);
776 DisposeRgn(CursorRgn);
782 // If nobody handled the event, it gets propagated to the
783 // application-level handler.