1
# Miro - an RSS based video player application
2
# Copyright (C) 2005-2010 Participatory Culture Foundation
4
# This program 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
# This program 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
15
# along with this program; if not, write to the Free Software
16
# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
18
# In addition, as a special exception, the copyright holders give
19
# permission to link the code of portions of this program with the OpenSSL
22
# You must obey the GNU General Public License in all respects for all of
23
# the code used other than OpenSSL. If you modify file(s) with this
24
# exception, you may extend this exception to your version of the file(s),
25
# but you are not obligated to do so. If you do not wish to do so, delete
26
# this exception statement from your version. If you delete this exception
27
# statement from all source files in the program, then also delete it here.
31
from objc import YES, NO, nil, pathForFramework, loadBundleFunctions
32
from Foundation import *
37
from miro.plat.frontends.widgets import mediatypes
38
from miro.plat.frontends.widgets import threads
39
from miro.plat.frontends.widgets import overlay
40
from miro.plat.frontends.widgets import quicktime
41
from miro.plat.frontends.widgets import wrappermap
42
from miro.plat.frontends.widgets.base import Widget
44
###############################################################################
45
#### Dynamically link some specific Carbon functions which we need but ####
46
### which are not available in the default MacPython ####
47
###############################################################################
52
carbonPath = pathForFramework('/System/Library/Frameworks/Carbon.framework')
53
carbonBundle = NSBundle.bundleWithPath_(carbonPath)
54
loadBundleFunctions(carbonBundle, globals(), ((u'SetSystemUIMode', 'III'),))
58
coreServicesPath = pathForFramework('/System/Library/Frameworks/CoreServices.framework')
59
coreServicesBundle = NSBundle.bundleWithPath_(coreServicesPath)
60
loadBundleFunctions(coreServicesBundle, globals(), ((u'UpdateSystemActivity', 'IC'),))
62
###############################################################################
64
SUPPORTED_MEDIA_TYPES = mediatypes.VIDEO_MEDIA_TYPES
66
###############################################################################
68
class MiroMovieView (QTMovieView):
70
def movieBounds(self):
72
if movie is not None and app.playback_manager.presentation_mode != 'fit-to-bounds':
73
my_bounds = self.bounds()
74
natural_size = movie.attributeForKey_(QTMovieNaturalSizeAttribute).sizeValue()
76
if app.playback_manager.presentation_mode == 'natural-size':
77
movie_size = natural_size
78
elif app.playback_manager.presentation_mode == 'half-size':
79
movie_size = NSSize(natural_size.width/2, natural_size.height/2)
80
elif app.playback_manager.presentation_mode == 'double-size':
81
movie_size = NSSize(natural_size.width*2, natural_size.height*2)
82
if movie_size.width > my_bounds.size.width or movie_size.height > my_bounds.size.height:
83
return QTMovieView.movieBounds(self)
85
movie_origin = NSPoint(int((my_bounds.size.width - movie_size.width) / 2),
86
int((my_bounds.size.height - movie_size.height) / 2))
87
return NSRect(movie_origin, movie_size)
89
return QTMovieView.movieBounds(self)
91
###############################################################################
93
class VideoPlayer (Widget, quicktime.Player):
96
quicktime.Player.__init__(self, SUPPORTED_MEDIA_TYPES)
99
frame = ((0,0),(200,200))
101
self.view = NSView.alloc().initWithFrame_(frame)
103
self.video_view = MiroMovieView.alloc().initWithFrame_(frame)
104
self.video_view.setFillColor_(NSColor.blackColor())
105
self.video_view.setControllerVisible_(NO)
106
self.video_view.setEditable_(NO)
107
self.video_view.setPreservesAspectRatio_(YES)
110
self.movie_notifications = None
111
self.system_activity_updater_timer = None
112
self.window_moved_handler = None
113
self.item_changed_handler = None
115
def calc_size_request(self):
118
def viewport_created(self):
119
self.video_window = VideoWindow.alloc().initWithContentRect_styleMask_backing_defer_(self.view.frame(), NSBorderlessWindowMask, NSBackingStoreBuffered, NO)
120
self.video_window.setContentView_(self.video_view)
122
self.adjust_video_frame()
123
self.view.window().addChildWindow_ordered_(self.video_window, NSWindowAbove)
124
self.video_window.orderFront_(nil)
125
self.window_moved_handler = wrappermap.wrapper(self.view.window()).connect('did-move', self.on_window_moved)
126
app.info_updater.item_changed_callbacks.add('manual', 'playback-list', self.on_items_changed)
128
def place(self, rect, containing_view):
129
Widget.place(self, rect, containing_view)
130
self.adjust_video_frame()
132
def on_window_moved(self, window):
133
self.adjust_video_frame()
135
def on_items_changed(self, message):
136
if self.video_window is not None:
137
self.video_window.on_items_changed(message.changed)
139
def remove_viewport(self):
140
app.info_updater.item_changed_callbacks.remove('manual', 'playback-list', self.on_items_changed)
141
self.item_changed_handler = None
142
self.prevent_system_sleep(False)
143
self.detach_from_parent_window()
144
self.video_window.close()
145
self.video_window = None
146
Widget.remove_viewport(self)
151
def detach_from_parent_window(self):
152
window = self.view.window()
153
window.removeChildWindow_(self.video_window)
154
wrappermap.wrapper(window).disconnect(self.window_moved_handler)
155
self.window_moved_handler = None
158
self.video_view.setMovie_(nil)
159
quicktime.Player.reset(self)
161
def get_video_frame(self):
162
frame = self.view.frame()
163
frame.origin = self.view.convertPoint_toView_(NSZeroPoint, nil)
165
frame.origin = self.view.window().convertBaseToScreen_(frame.origin)
166
frame.size = NSSize(self.view.window().frame().size.width, frame.size.height)
169
def adjust_video_frame(self):
170
frame = self.get_video_frame()
171
self.video_window.setFrame_display_(frame, YES)
173
def update_for_presentation_mode(self, mode):
174
frame = self.video_view.frame()
175
self.video_view.setFrame_(NSOffsetRect(frame, 1, 1))
176
self.video_view.setFrame_(frame)
178
def set_item(self, item_info, callback, errback, force_subtitles=False):
180
self.video_view.setMovie_(self.movie)
181
self.video_view.setNeedsDisplay_(YES)
182
self.video_window.setup(item_info, self)
184
quicktime.Player.set_item(self, item_info, callback2, errback, force_subtitles)
186
def set_volume(self, volume):
187
quicktime.Player.set_volume(self, volume)
188
if self.video_window:
189
self.video_window.palette.set_volume(volume)
192
threads.warn_if_not_on_main_thread('VideoPlayer.play')
193
self.video_view.play_(nil)
194
self.video_view.setNeedsDisplay_(YES)
195
self.prevent_system_sleep(True)
198
threads.warn_if_not_on_main_thread('VideoPlayer.pause')
199
self.video_view.pause_(nil)
200
self.prevent_system_sleep(True)
202
def stop(self, will_play_another=False):
203
threads.warn_if_not_on_main_thread('VideoPlayer.stop')
204
self.prevent_system_sleep(True)
205
self.video_view.pause_(nil)
206
if self.video_window and not will_play_another:
207
self.video_window.palette.remove()
210
def enter_fullscreen(self):
211
self.video_window.enter_fullscreen()
213
def exit_fullscreen(self):
214
frame = self.get_video_frame()
215
self.video_window.exit_fullscreen(frame)
217
def prepare_switch_to_attached_playback(self):
218
self.video_window.palette.remove()
219
app.widgetapp.window.nswindow.makeKeyAndOrderFront_(nil)
221
def prepare_switch_to_detached_playback(self):
222
self.video_window.palette.remove()
224
def prevent_system_sleep(self, prevent):
225
if prevent and self.system_activity_updater_timer is None:
226
logging.debug("Launching system activity updater timer")
227
self.system_activity_updater_timer = NSTimer.scheduledTimerWithTimeInterval_target_selector_userInfo_repeats_(
230
'updateSystemActivity:',
233
elif not prevent and self.system_activity_updater_timer is not None:
234
logging.debug("Stopping system activity updater timer")
235
self.system_activity_updater_timer.invalidate()
236
self.system_activity_updater_timer = None
238
###############################################################################
240
class VideoWindow (NSWindow):
242
def initWithContentRect_styleMask_backing_defer_(self, rect, style, backing, defer):
243
self = super(VideoWindow, self).initWithContentRect_styleMask_backing_defer_(rect, style, backing, defer)
244
self.setBackgroundColor_(NSColor.blackColor())
245
self.setReleasedWhenClosed_(NO)
246
self.setAcceptsMouseMovedEvents_(YES)
247
self.palette = overlay.OverlayPalette.get_instance()
248
self.is_fullscreen = False
252
self.palette.window().orderOut_(nil)
254
super(VideoWindow, self).close()
256
def on_items_changed(self, changed):
257
if self.palette is not None:
258
self.palette.on_items_changed(changed)
260
def canBecomeMainWindow(self):
263
def canBecomeKeyWindow(self):
266
def setFrame_display_(self, frame, display):
267
super(VideoWindow, self).setFrame_display_(frame, display)
268
if self.palette.window().isVisible():
269
self.palette.adjustPosition(self)
270
if not self.palette.fit_in_video_window(self):
271
self.palette.remove()
273
def setup(self, item_info, renderer):
274
self.palette.setup(item_info, renderer, self)
276
def enter_fullscreen(self):
277
NSCursor.setHiddenUntilMouseMoves_(YES)
278
screens = NSScreen.screens()
280
screenWithMenuBar = screens[0]
281
if self.screen() == screenWithMenuBar:
282
SetSystemUIMode(kUIModeAllHidden, 0)
283
self.setFrame_display_animate_(self.screen().frame(), YES, YES)
284
self.is_fullscreen = True
285
self.palette.enter_fullscreen(self)
287
def exit_fullscreen(self, frame):
288
NSCursor.setHiddenUntilMouseMoves_(NO)
289
self.is_fullscreen = False
290
self.palette.exit_fullscreen(self)
291
self.setFrame_display_animate_(frame, YES, YES)
292
SetSystemUIMode(kUIModeNormal, 0)
294
def updateSystemActivity_(self, timer):
295
UpdateSystemActivity(OverallActivity)
297
def sendEvent_(self, event):
298
if self.parentWindow() is None:
299
# We've been detached since the event was fired. Just ignore it.
302
if event.type() == NSMouseMoved:
303
if NSPointInRect(event.locationInWindow(), self.contentView().bounds()) and self.palette.fit_in_video_window(self):
304
self.palette.reveal(self)
305
elif event.type() == NSLeftMouseDown:
306
if NSApplication.sharedApplication().isActive():
307
if event.clickCount() > 1:
308
app.playback_manager.toggle_fullscreen()
309
elif not self.parentWindow().isMainWindow():
310
self.parentWindow().makeKeyAndOrderFront_(nil)
312
NSApplication.sharedApplication().activateIgnoringOtherApps_(YES)
314
#super(VideoWindow, self).sendEvent_(event)
315
self.parentWindow().sendEvent_(event)
317
###############################################################################