1366 lines
50 KiB
Python
1366 lines
50 KiB
Python
# ----------------------------------------------------------------------------
|
|
# pyglet
|
|
# Copyright (c) 2006-2008 Alex Holkner
|
|
# All rights reserved.
|
|
#
|
|
# Redistribution and use in source and binary forms, with or without
|
|
# modification, are permitted provided that the following conditions
|
|
# are met:
|
|
#
|
|
# * Redistributions of source code must retain the above copyright
|
|
# notice, this list of conditions and the following disclaimer.
|
|
# * Redistributions in binary form must reproduce the above copyright
|
|
# notice, this list of conditions and the following disclaimer in
|
|
# the documentation and/or other materials provided with the
|
|
# distribution.
|
|
# * Neither the name of pyglet nor the names of its
|
|
# contributors may be used to endorse or promote products
|
|
# derived from this software without specific prior written
|
|
# permission.
|
|
#
|
|
# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
|
# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
|
# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
|
|
# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
|
|
# COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
|
|
# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
|
|
# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
|
# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
|
# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
|
# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
|
|
# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
|
# POSSIBILITY OF SUCH DAMAGE.
|
|
# ----------------------------------------------------------------------------
|
|
|
|
'''
|
|
'''
|
|
|
|
__docformat__ = 'restructuredtext'
|
|
__version__ = '$Id: $'
|
|
|
|
from ctypes import *
|
|
import os.path
|
|
import unicodedata
|
|
import warnings
|
|
|
|
import pyglet
|
|
from pyglet.window import WindowException, Platform, Display, Screen, \
|
|
BaseWindow, MouseCursor, DefaultMouseCursor, _PlatformEventHandler
|
|
from pyglet.window import key
|
|
from pyglet.window import mouse
|
|
from pyglet.window import event
|
|
from pyglet.window.carbon.constants import *
|
|
from pyglet.window.carbon.types import *
|
|
from pyglet.window.carbon.quartzkey import keymap
|
|
|
|
import pyglet.lib
|
|
from pyglet import gl
|
|
from pyglet.gl import agl
|
|
from pyglet.gl import gl_info
|
|
from pyglet.gl import glu_info
|
|
from pyglet.event import EventDispatcher
|
|
|
|
class CarbonException(WindowException):
|
|
pass
|
|
|
|
carbon = pyglet.lib.load_library(
|
|
framework='/System/Library/Frameworks/Carbon.framework')
|
|
quicktime = pyglet.lib.load_library(
|
|
framework='/System/Library/Frameworks/QuickTime.framework')
|
|
|
|
carbon.GetEventDispatcherTarget.restype = EventTargetRef
|
|
carbon.ReceiveNextEvent.argtypes = \
|
|
[c_uint32, c_void_p, c_double, c_ubyte, POINTER(EventRef)]
|
|
carbon.GetWindowPort.restype = agl.AGLDrawable
|
|
EventHandlerProcPtr = CFUNCTYPE(c_int, c_int, c_void_p, c_void_p)
|
|
carbon.NewEventHandlerUPP.restype = c_void_p
|
|
carbon.GetCurrentKeyModifiers = c_uint32
|
|
carbon.NewRgn.restype = RgnHandle
|
|
carbon.CGDisplayBounds.argtypes = [c_void_p]
|
|
carbon.CGDisplayBounds.restype = CGRect
|
|
|
|
# Map symbol,modifiers -> motion
|
|
# Determined by experiment with TextEdit.app
|
|
_motion_map = {
|
|
(key.UP, False): key.MOTION_UP,
|
|
(key.RIGHT, False): key.MOTION_RIGHT,
|
|
(key.DOWN, False): key.MOTION_DOWN,
|
|
(key.LEFT, False): key.MOTION_LEFT,
|
|
(key.LEFT, key.MOD_OPTION): key.MOTION_PREVIOUS_WORD,
|
|
(key.RIGHT, key.MOD_OPTION): key.MOTION_NEXT_WORD,
|
|
(key.LEFT, key.MOD_COMMAND): key.MOTION_BEGINNING_OF_LINE,
|
|
(key.RIGHT, key.MOD_COMMAND): key.MOTION_END_OF_LINE,
|
|
(key.PAGEUP, False): key.MOTION_PREVIOUS_PAGE,
|
|
(key.PAGEDOWN, False): key.MOTION_NEXT_PAGE,
|
|
(key.HOME, False): key.MOTION_BEGINNING_OF_FILE,
|
|
(key.END, False): key.MOTION_END_OF_FILE,
|
|
(key.UP, key.MOD_COMMAND): key.MOTION_BEGINNING_OF_FILE,
|
|
(key.DOWN, key.MOD_COMMAND): key.MOTION_END_OF_FILE,
|
|
(key.BACKSPACE, False): key.MOTION_BACKSPACE,
|
|
(key.DELETE, False): key.MOTION_DELETE,
|
|
}
|
|
|
|
class CarbonPlatform(Platform):
|
|
_display = None
|
|
|
|
def get_default_display(self):
|
|
if not self._display:
|
|
self._display = CarbonDisplay()
|
|
return self._display
|
|
|
|
class CarbonDisplay(Display):
|
|
# TODO: CarbonDisplay could be per display device, which would make
|
|
# reporting of screens and available configs more accurate. The number of
|
|
# Macs with more than one video card is probably small, though.
|
|
def __init__(self):
|
|
super(CarbonDisplay, self).__init__()
|
|
|
|
import MacOS
|
|
if not MacOS.WMAvailable():
|
|
raise CarbonException('Window manager is not available. ' \
|
|
'Ensure you run "pythonw", not "python"')
|
|
|
|
self._install_application_event_handlers()
|
|
|
|
def get_screens(self):
|
|
count = CGDisplayCount()
|
|
carbon.CGGetActiveDisplayList(0, None, byref(count))
|
|
displays = (CGDirectDisplayID * count.value)()
|
|
carbon.CGGetActiveDisplayList(count.value, displays, byref(count))
|
|
return [CarbonScreen(self, id) for id in displays]
|
|
|
|
def _install_application_event_handlers(self):
|
|
self._carbon_event_handlers = []
|
|
self._carbon_event_handler_refs = []
|
|
|
|
target = carbon.GetApplicationEventTarget()
|
|
|
|
# TODO something with a metaclass or hacky like CarbonWindow
|
|
# to make this list extensible
|
|
handlers = [
|
|
(self._on_mouse_down, kEventClassMouse, kEventMouseDown),
|
|
(self._on_apple_event, kEventClassAppleEvent, kEventAppleEvent),
|
|
(self._on_command, kEventClassCommand, kEventProcessCommand),
|
|
]
|
|
|
|
ae_handlers = [
|
|
(self._on_ae_quit, kCoreEventClass, kAEQuitApplication),
|
|
]
|
|
|
|
# Install the application-wide handlers
|
|
for method, cls, event in handlers:
|
|
proc = EventHandlerProcPtr(method)
|
|
self._carbon_event_handlers.append(proc)
|
|
upp = carbon.NewEventHandlerUPP(proc)
|
|
types = EventTypeSpec()
|
|
types.eventClass = cls
|
|
types.eventKind = event
|
|
handler_ref = EventHandlerRef()
|
|
carbon.InstallEventHandler(
|
|
target,
|
|
upp,
|
|
1,
|
|
byref(types),
|
|
c_void_p(),
|
|
byref(handler_ref))
|
|
self._carbon_event_handler_refs.append(handler_ref)
|
|
|
|
# Install Apple event handlers
|
|
for method, cls, event in ae_handlers:
|
|
proc = EventHandlerProcPtr(method)
|
|
self._carbon_event_handlers.append(proc)
|
|
upp = carbon.NewAEEventHandlerUPP(proc)
|
|
carbon.AEInstallEventHandler(
|
|
cls,
|
|
event,
|
|
upp,
|
|
0,
|
|
False)
|
|
|
|
def _on_command(self, next_handler, ev, data):
|
|
command = HICommand()
|
|
carbon.GetEventParameter(ev, kEventParamDirectObject,
|
|
typeHICommand, c_void_p(), sizeof(command), c_void_p(),
|
|
byref(command))
|
|
|
|
if command.commandID == kHICommandQuit:
|
|
self._on_quit()
|
|
|
|
return noErr
|
|
|
|
def _on_mouse_down(self, next_handler, ev, data):
|
|
# Check for menubar hit
|
|
position = Point()
|
|
carbon.GetEventParameter(ev, kEventParamMouseLocation,
|
|
typeQDPoint, c_void_p(), sizeof(position), c_void_p(),
|
|
byref(position))
|
|
if carbon.FindWindow(position, None) == inMenuBar:
|
|
# Mouse down in menu bar. MenuSelect() takes care of all
|
|
# menu tracking and blocks until the menu is dismissed.
|
|
# Use command events to handle actual menu item invokations.
|
|
|
|
# This function blocks, so tell the event loop it needs to install
|
|
# a timer.
|
|
from pyglet import app
|
|
if app.event_loop is not None:
|
|
app.event_loop._enter_blocking()
|
|
|
|
carbon.MenuSelect(position)
|
|
|
|
if app.event_loop is not None:
|
|
app.event_loop._exit_blocking()
|
|
|
|
# Menu selection has now returned. Remove highlight from the
|
|
# menubar.
|
|
carbon.HiliteMenu(0)
|
|
|
|
carbon.CallNextEventHandler(next_handler, ev)
|
|
return noErr
|
|
|
|
def _on_apple_event(self, next_handler, ev, data):
|
|
# Somewhat involved way of redispatching Apple event contained
|
|
# within a Carbon event, described in
|
|
# http://developer.apple.com/documentation/AppleScript/
|
|
# Conceptual/AppleEvents/dispatch_aes_aepg/chapter_4_section_3.html
|
|
|
|
release = False
|
|
if carbon.IsEventInQueue(carbon.GetMainEventQueue(), ev):
|
|
carbon.RetainEvent(ev)
|
|
release = True
|
|
carbon.RemoveEventFromQueue(carbon.GetMainEventQueue(), ev)
|
|
|
|
ev_record = EventRecord()
|
|
carbon.ConvertEventRefToEventRecord(ev, byref(ev_record))
|
|
carbon.AEProcessAppleEvent(byref(ev_record))
|
|
|
|
if release:
|
|
carbon.ReleaseEvent(ev)
|
|
|
|
return noErr
|
|
|
|
def _on_ae_quit(self, ae, reply, refcon):
|
|
self._on_quit()
|
|
return noErr
|
|
|
|
def _on_quit(self):
|
|
'''Called when the user tries to quit the application.
|
|
|
|
This is not an actual event handler, it is called in response
|
|
to Command+Q, the Quit menu item, and the Dock context menu's Quit
|
|
item.
|
|
|
|
The default implementation sets `has_exit` to true on all open
|
|
windows. In pyglet 1.1 `has_exit` is set on `EventLoop` if it is
|
|
used instead of the windows.
|
|
'''
|
|
from pyglet import app
|
|
if app.event_loop is not None:
|
|
app.event_loop.exit()
|
|
else:
|
|
for window in self.get_windows():
|
|
window.has_exit = True
|
|
|
|
class CarbonScreen(Screen):
|
|
def __init__(self, display, id):
|
|
self.display = display
|
|
rect = carbon.CGDisplayBounds(id)
|
|
super(CarbonScreen, self).__init__(
|
|
int(rect.origin.x), int(rect.origin.y),
|
|
int(rect.size.width), int(rect.size.height))
|
|
self.id = id
|
|
|
|
mode = carbon.CGDisplayCurrentMode(id)
|
|
kCGDisplayRefreshRate = _create_cfstring('RefreshRate')
|
|
number = carbon.CFDictionaryGetValue(mode, kCGDisplayRefreshRate)
|
|
refresh = c_long()
|
|
kCFNumberLongType = 10
|
|
carbon.CFNumberGetValue(number, kCFNumberLongType, byref(refresh))
|
|
self._refresh_rate = refresh.value
|
|
|
|
def get_gdevice(self):
|
|
gdevice = GDHandle()
|
|
r = carbon.DMGetGDeviceByDisplayID(self.id, byref(gdevice), False)
|
|
_oscheck(r)
|
|
return gdevice
|
|
|
|
def get_matching_configs(self, template):
|
|
# Construct array of attributes for aglChoosePixelFormat
|
|
attrs = []
|
|
for name, value in template.get_gl_attributes():
|
|
attr = CarbonGLConfig._attribute_ids.get(name, None)
|
|
if not attr or not value:
|
|
continue
|
|
attrs.append(attr)
|
|
if attr not in CarbonGLConfig._boolean_attributes:
|
|
attrs.append(int(value))
|
|
|
|
# Support for RAGE-II, which is not compliant
|
|
attrs.append(agl.AGL_ALL_RENDERERS)
|
|
|
|
# Force selection policy and RGBA
|
|
attrs.append(agl.AGL_MAXIMUM_POLICY)
|
|
attrs.append(agl.AGL_RGBA)
|
|
|
|
# In 10.3 and later, AGL_FULLSCREEN is specified so the window can
|
|
# be toggled to/from fullscreen without losing context. pyglet
|
|
# no longer supports earlier versions of OS X, so we always supply it.
|
|
attrs.append(agl.AGL_FULLSCREEN)
|
|
|
|
# Terminate the list.
|
|
attrs.append(agl.AGL_NONE)
|
|
attrib_list = (c_int * len(attrs))(*attrs)
|
|
|
|
device = self.get_gdevice()
|
|
pformat = agl.aglChoosePixelFormat(device, 1, attrib_list)
|
|
_aglcheck()
|
|
|
|
if not pformat:
|
|
return []
|
|
else:
|
|
return [CarbonGLConfig(self, pformat)]
|
|
|
|
class CarbonGLConfig(gl.Config):
|
|
# Valid names for GL attributes, and their corresponding AGL constant.
|
|
_attribute_ids = {
|
|
'double_buffer': agl.AGL_DOUBLEBUFFER,
|
|
'stereo': agl.AGL_STEREO,
|
|
'buffer_size': agl.AGL_BUFFER_SIZE,
|
|
'sample_buffers': agl.AGL_SAMPLE_BUFFERS_ARB,
|
|
'samples': agl.AGL_SAMPLES_ARB,
|
|
'aux_buffers': agl.AGL_AUX_BUFFERS,
|
|
'red_size': agl.AGL_RED_SIZE,
|
|
'green_size': agl.AGL_GREEN_SIZE,
|
|
'blue_size': agl.AGL_BLUE_SIZE,
|
|
'alpha_size': agl.AGL_ALPHA_SIZE,
|
|
'depth_size': agl.AGL_DEPTH_SIZE,
|
|
'stencil_size': agl.AGL_STENCIL_SIZE,
|
|
'accum_red_size': agl.AGL_ACCUM_RED_SIZE,
|
|
'accum_green_size': agl.AGL_ACCUM_GREEN_SIZE,
|
|
'accum_blue_size': agl.AGL_ACCUM_BLUE_SIZE,
|
|
'accum_alpha_size': agl.AGL_ACCUM_ALPHA_SIZE,
|
|
|
|
# Not exposed by pyglet API (set internally)
|
|
'all_renderers': agl.AGL_ALL_RENDERERS,
|
|
'rgba': agl.AGL_RGBA,
|
|
'fullscreen': agl.AGL_FULLSCREEN,
|
|
'minimum_policy': agl.AGL_MINIMUM_POLICY,
|
|
'maximum_policy': agl.AGL_MAXIMUM_POLICY,
|
|
|
|
# Not supported in current pyglet API
|
|
'level': agl.AGL_LEVEL,
|
|
'pixel_size': agl.AGL_PIXEL_SIZE, # == buffer_size
|
|
'aux_depth_stencil': agl.AGL_AUX_DEPTH_STENCIL,
|
|
'color_float': agl.AGL_COLOR_FLOAT,
|
|
'offscreen': agl.AGL_OFFSCREEN,
|
|
'sample_alpha': agl.AGL_SAMPLE_ALPHA,
|
|
'multisample': agl.AGL_MULTISAMPLE,
|
|
'supersample': agl.AGL_SUPERSAMPLE,
|
|
}
|
|
|
|
# AGL constants which do not require a value.
|
|
_boolean_attributes = \
|
|
(agl.AGL_ALL_RENDERERS,
|
|
agl.AGL_RGBA,
|
|
agl.AGL_DOUBLEBUFFER,
|
|
agl.AGL_STEREO,
|
|
agl.AGL_MINIMUM_POLICY,
|
|
agl.AGL_MAXIMUM_POLICY,
|
|
agl.AGL_OFFSCREEN,
|
|
agl.AGL_FULLSCREEN,
|
|
agl.AGL_AUX_DEPTH_STENCIL,
|
|
agl.AGL_COLOR_FLOAT,
|
|
agl.AGL_MULTISAMPLE,
|
|
agl.AGL_SUPERSAMPLE,
|
|
agl.AGL_SAMPLE_ALPHA)
|
|
|
|
def __init__(self, screen, pformat):
|
|
super(CarbonGLConfig, self).__init__()
|
|
self.screen = screen
|
|
self._pformat = pformat
|
|
self._attributes = {}
|
|
|
|
for name, attr in self._attribute_ids.items():
|
|
value = c_int()
|
|
result = agl.aglDescribePixelFormat(pformat, attr, byref(value))
|
|
if result:
|
|
setattr(self, name, value.value)
|
|
|
|
def create_context(self, share):
|
|
if share:
|
|
context = agl.aglCreateContext(self._pformat, share._context)
|
|
else:
|
|
context = agl.aglCreateContext(self._pformat, None)
|
|
_aglcheck()
|
|
return CarbonGLContext(self, context, share, self._pformat)
|
|
|
|
class CarbonGLContext(gl.Context):
|
|
def __init__(self, config, context, share, pixelformat):
|
|
super(CarbonGLContext, self).__init__(share)
|
|
self.config = config
|
|
self._context = context
|
|
self._pixelformat = pixelformat
|
|
|
|
def destroy(self):
|
|
super(CarbonGLContext, self).destroy()
|
|
agl.aglDestroyContext(self._context)
|
|
|
|
class CarbonMouseCursor(MouseCursor):
|
|
drawable = False
|
|
def __init__(self, theme):
|
|
self.theme = theme
|
|
|
|
def CarbonEventHandler(event_class, event_kind):
|
|
return _PlatformEventHandler((event_class, event_kind))
|
|
|
|
class CarbonWindow(BaseWindow):
|
|
_window = None # Carbon WindowRef
|
|
_agl_context = None # AGL context ID
|
|
_recreate_deferred = None
|
|
|
|
# Window properties
|
|
_minimum_size = None
|
|
_maximum_size = None
|
|
_fullscreen_restore = None
|
|
_event_dispatcher = None
|
|
_current_modifiers = 0
|
|
_mapped_modifers = 0
|
|
_carbon_event_handlers = []
|
|
_carbon_event_handler_refs = []
|
|
_track_ref = 0
|
|
_track_region = None
|
|
|
|
_mouse_exclusive = False
|
|
_mouse_platform_visible = True
|
|
_mouse_ignore_motion = False
|
|
|
|
def _recreate(self, changes):
|
|
# We can't destroy the window while event handlers are active,
|
|
# otherwise the (OS X) event dispatcher gets lost and segfaults.
|
|
#
|
|
# Defer actual recreation until dispatch_events next finishes.
|
|
self._recreate_deferred = changes
|
|
|
|
def _recreate_immediate(self):
|
|
# The actual _recreate function.
|
|
changes = self._recreate_deferred
|
|
self._recreate_deferred = None
|
|
|
|
if ('context' in changes):
|
|
agl.aglSetDrawable(self._agl_context, None)
|
|
|
|
if ('fullscreen' in changes and
|
|
not self._fullscreen and
|
|
self._fullscreen_restore):
|
|
|
|
# Leaving fullscreen -- destroy everything before the window.
|
|
self._remove_track_region()
|
|
self._remove_event_handlers()
|
|
agl.aglSetDrawable(self._agl_context, None)
|
|
# EndFullScreen disposes _window.
|
|
quicktime.EndFullScreen(self._fullscreen_restore, 0)
|
|
self._window = None
|
|
|
|
self._create()
|
|
|
|
def _create(self):
|
|
self._agl_context = self.context._context
|
|
|
|
if self._window:
|
|
# The window is about to be recreated; destroy everything
|
|
# associated with the old window, then the window itself.
|
|
self._remove_track_region()
|
|
self._remove_event_handlers()
|
|
agl.aglSetDrawable(self._agl_context, None)
|
|
carbon.DisposeWindow(self._window)
|
|
self._window = None
|
|
|
|
self._window = WindowRef()
|
|
|
|
if self._fullscreen:
|
|
# Switch to fullscreen mode with QuickTime
|
|
fs_width = c_short(0)
|
|
fs_height = c_short(0)
|
|
self._fullscreen_restore = c_void_p()
|
|
quicktime.BeginFullScreen(byref(self._fullscreen_restore),
|
|
self.screen.get_gdevice(),
|
|
byref(fs_width),
|
|
byref(fs_height),
|
|
byref(self._window),
|
|
None,
|
|
0)
|
|
# the following may be used for debugging if you have a second
|
|
# monitor - only the main monitor will go fullscreen
|
|
agl.aglEnable(self._agl_context, agl.AGL_FS_CAPTURE_SINGLE)
|
|
self._width = fs_width.value
|
|
self._height = fs_height.value
|
|
#self._width = self.screen.width
|
|
#self._height = self.screen.height
|
|
agl.aglSetFullScreen(self._agl_context,
|
|
self._width, self._height,
|
|
self.screen._refresh_rate, 0)
|
|
self._mouse_in_window = True
|
|
self.dispatch_event('on_resize', self._width, self._height)
|
|
self.dispatch_event('on_show')
|
|
self.dispatch_event('on_expose')
|
|
else:
|
|
# Create floating window
|
|
rect = Rect()
|
|
location = None # TODO
|
|
if location is not None:
|
|
rect.left = location[0]
|
|
rect.top = location[1]
|
|
else:
|
|
rect.top = rect.left = 0
|
|
rect.right = rect.left + self._width
|
|
rect.bottom = rect.top + self._height
|
|
|
|
styles = {
|
|
self.WINDOW_STYLE_DEFAULT: (kDocumentWindowClass,
|
|
kWindowCloseBoxAttribute |
|
|
kWindowCollapseBoxAttribute),
|
|
self.WINDOW_STYLE_DIALOG: (kDocumentWindowClass,
|
|
kWindowCloseBoxAttribute),
|
|
self.WINDOW_STYLE_TOOL: (kUtilityWindowClass,
|
|
kWindowCloseBoxAttribute),
|
|
self.WINDOW_STYLE_BORDERLESS: (kSimpleWindowClass,
|
|
kWindowNoAttributes)
|
|
}
|
|
window_class, window_attributes = \
|
|
styles.get(self._style, kDocumentWindowClass)
|
|
|
|
if self._resizable:
|
|
window_attributes |= (kWindowFullZoomAttribute |
|
|
kWindowLiveResizeAttribute |
|
|
kWindowResizableAttribute)
|
|
|
|
r = carbon.CreateNewWindow(window_class,
|
|
window_attributes,
|
|
byref(rect),
|
|
byref(self._window))
|
|
_oscheck(r)
|
|
|
|
if location is None:
|
|
carbon.RepositionWindow(self._window, c_void_p(),
|
|
kWindowCascadeOnMainScreen)
|
|
|
|
agl.aglSetDrawable(self._agl_context,
|
|
carbon.GetWindowPort(self._window))
|
|
|
|
_aglcheck()
|
|
|
|
self.set_caption(self._caption)
|
|
|
|
# Get initial state
|
|
self._event_dispatcher = carbon.GetEventDispatcherTarget()
|
|
self._current_modifiers = carbon.GetCurrentKeyModifiers().value
|
|
self._mapped_modifiers = self._map_modifiers(self._current_modifiers)
|
|
|
|
# (re)install Carbon event handlers
|
|
self._install_event_handlers()
|
|
|
|
self._create_track_region()
|
|
|
|
self.switch_to()
|
|
self.set_vsync(self._vsync)
|
|
|
|
if self._visible:
|
|
self.set_visible(True)
|
|
|
|
def _create_track_region(self):
|
|
self._remove_track_region()
|
|
|
|
# Create a tracking region for the content part of the window
|
|
# to receive enter/leave events.
|
|
track_id = MouseTrackingRegionID()
|
|
track_id.signature = DEFAULT_CREATOR_CODE
|
|
track_id.id = 1
|
|
self._track_ref = MouseTrackingRef()
|
|
self._track_region = carbon.NewRgn()
|
|
carbon.GetWindowRegion(self._window,
|
|
kWindowContentRgn, self._track_region)
|
|
carbon.CreateMouseTrackingRegion(self._window,
|
|
self._track_region, None, kMouseTrackingOptionsGlobalClip,
|
|
track_id, None, None,
|
|
byref(self._track_ref))
|
|
|
|
def _remove_track_region(self):
|
|
if self._track_region:
|
|
carbon.ReleaseMouseTrackingRegion(self._track_region)
|
|
self._track_region = None
|
|
|
|
def close(self):
|
|
super(CarbonWindow, self).close()
|
|
if not self._agl_context:
|
|
return
|
|
self._agl_context = None
|
|
self._remove_event_handlers()
|
|
self._remove_track_region()
|
|
|
|
# Restore cursor visibility
|
|
self.set_mouse_platform_visible(True)
|
|
self.set_exclusive_mouse(False)
|
|
|
|
if self._fullscreen:
|
|
quicktime.EndFullScreen(self._fullscreen_restore, 0)
|
|
else:
|
|
carbon.DisposeWindow(self._window)
|
|
self._window = None
|
|
|
|
def switch_to(self):
|
|
agl.aglSetCurrentContext(self._agl_context)
|
|
self._context.set_current()
|
|
_aglcheck()
|
|
gl_info.set_active_context()
|
|
glu_info.set_active_context()
|
|
|
|
def flip(self):
|
|
self.draw_mouse_cursor()
|
|
agl.aglSwapBuffers(self._agl_context)
|
|
_aglcheck()
|
|
|
|
def _get_vsync(self):
|
|
swap = c_long()
|
|
agl.aglGetInteger(self._agl_context, agl.AGL_SWAP_INTERVAL, byref(swap))
|
|
return bool(swap.value)
|
|
vsync = property(_get_vsync) # overrides BaseWindow property
|
|
|
|
def set_vsync(self, vsync):
|
|
if pyglet.options['vsync'] is not None:
|
|
vsync = pyglet.options['vsync']
|
|
self._vsync = vsync # _recreate depends on this
|
|
swap = c_long(int(vsync))
|
|
agl.aglSetInteger(self._agl_context, agl.AGL_SWAP_INTERVAL, byref(swap))
|
|
|
|
def dispatch_events(self):
|
|
self._allow_dispatch_event = True
|
|
while self._event_queue:
|
|
EventDispatcher.dispatch_event(self, *self._event_queue.pop(0))
|
|
|
|
e = EventRef()
|
|
result = carbon.ReceiveNextEvent(0, c_void_p(), 0, True, byref(e))
|
|
while result == noErr:
|
|
carbon.SendEventToEventTarget(e, self._event_dispatcher)
|
|
carbon.ReleaseEvent(e)
|
|
|
|
if self._recreate_deferred:
|
|
self._recreate_immediate()
|
|
result = carbon.ReceiveNextEvent(0, c_void_p(), 0, True, byref(e))
|
|
|
|
self._allow_dispatch_event = False
|
|
|
|
# Return value from ReceiveNextEvent can be ignored if not
|
|
# noErr; we check here only to look for new bugs.
|
|
# eventLoopQuitErr: the inner event loop was quit, see
|
|
# http://lists.apple.com/archives/Carbon-dev/2006/Jun/msg00850.html
|
|
# Can occur when mixing with other toolkits, e.g. Tk.
|
|
# Fixes issue 180.
|
|
if result not in (eventLoopTimedOutErr, eventLoopQuitErr):
|
|
raise 'Error %d' % result
|
|
|
|
def dispatch_pending_events(self):
|
|
while self._event_queue:
|
|
EventDispatcher.dispatch_event(self, *self._event_queue.pop(0))
|
|
|
|
if self._recreate_deferred:
|
|
self._recreate_immediate()
|
|
|
|
def set_caption(self, caption):
|
|
self._caption = caption
|
|
s = _create_cfstring(caption)
|
|
carbon.SetWindowTitleWithCFString(self._window, s)
|
|
carbon.CFRelease(s)
|
|
|
|
def set_location(self, x, y):
|
|
rect = Rect()
|
|
carbon.GetWindowBounds(self._window, kWindowContentRgn, byref(rect))
|
|
rect.right += x - rect.left
|
|
rect.bottom += y - rect.top
|
|
rect.left = x
|
|
rect.top = y
|
|
carbon.SetWindowBounds(self._window, kWindowContentRgn, byref(rect))
|
|
|
|
def get_location(self):
|
|
rect = Rect()
|
|
carbon.GetWindowBounds(self._window, kWindowContentRgn, byref(rect))
|
|
return rect.left, rect.top
|
|
|
|
def set_size(self, width, height):
|
|
if self._fullscreen:
|
|
raise WindowException('Cannot set size of fullscreen window.')
|
|
rect = Rect()
|
|
carbon.GetWindowBounds(self._window, kWindowContentRgn, byref(rect))
|
|
rect.right = rect.left + width
|
|
rect.bottom = rect.top + height
|
|
carbon.SetWindowBounds(self._window, kWindowContentRgn, byref(rect))
|
|
|
|
self._width = width
|
|
self._height = height
|
|
self.dispatch_event('on_resize', width, height)
|
|
self.dispatch_event('on_expose')
|
|
|
|
def get_size(self):
|
|
if self._fullscreen:
|
|
return self._width, self._height
|
|
rect = Rect()
|
|
carbon.GetWindowBounds(self._window, kWindowContentRgn, byref(rect))
|
|
return rect.right - rect.left, rect.bottom - rect.top
|
|
|
|
def set_minimum_size(self, width, height):
|
|
self._minimum_size = (width, height)
|
|
minimum = HISize()
|
|
minimum.width = width
|
|
minimum.height = height
|
|
if self._maximum_size:
|
|
maximum = HISize()
|
|
maximum.width, maximum.height = self._maximum_size
|
|
maximum = byref(maximum)
|
|
else:
|
|
maximum = None
|
|
carbon.SetWindowResizeLimits(self._window,
|
|
byref(minimum), maximum)
|
|
|
|
def set_maximum_size(self, width, height):
|
|
self._maximum_size = (width, height)
|
|
maximum = HISize()
|
|
maximum.width = width
|
|
maximum.height = height
|
|
if self._minimum_size:
|
|
minimum = HISize()
|
|
minimum.width, minimum.height = self._minimum_size
|
|
minimum = byref(minimum)
|
|
else:
|
|
minimum = None
|
|
carbon.SetWindowResizeLimits(self._window,
|
|
minimum, byref(maximum))
|
|
|
|
def activate(self):
|
|
carbon.ActivateWindow(self._window, 1)
|
|
|
|
# Also make the application the "front" application. TODO
|
|
# maybe don't bring forward all of the application's windows?
|
|
psn = ProcessSerialNumber()
|
|
psn.highLongOfPSN = 0
|
|
psn.lowLongOfPSN = kCurrentProcess
|
|
carbon.SetFrontProcess(byref(psn))
|
|
|
|
def set_visible(self, visible=True):
|
|
self._visible = visible
|
|
if visible:
|
|
self.dispatch_event('on_resize', self._width, self._height)
|
|
self.dispatch_event('on_show')
|
|
carbon.ShowWindow(self._window)
|
|
else:
|
|
carbon.HideWindow(self._window)
|
|
|
|
def minimize(self):
|
|
self._mouse_in_window = False
|
|
self.set_mouse_platform_visible()
|
|
carbon.CollapseWindow(self._window, True)
|
|
|
|
def maximize(self):
|
|
# Maximum "safe" value, gets trimmed to screen size automatically.
|
|
p = Point()
|
|
p.v, p.h = 16000,16000
|
|
if not carbon.IsWindowInStandardState(self._window, byref(p), None):
|
|
carbon.ZoomWindowIdeal(self._window, inZoomOut, byref(p))
|
|
|
|
def set_mouse_platform_visible(self, platform_visible=None):
|
|
if platform_visible is None:
|
|
platform_visible = self._mouse_visible and \
|
|
not self._mouse_exclusive and \
|
|
not self._mouse_cursor.drawable
|
|
if not self._mouse_in_window:
|
|
platform_visible = True
|
|
|
|
if self._mouse_in_window and \
|
|
isinstance(self._mouse_cursor, CarbonMouseCursor):
|
|
carbon.SetThemeCursor(self._mouse_cursor.theme)
|
|
else:
|
|
carbon.SetThemeCursor(kThemeArrowCursor)
|
|
|
|
if self._mouse_platform_visible == platform_visible:
|
|
return
|
|
|
|
if platform_visible:
|
|
carbon.ShowCursor()
|
|
else:
|
|
carbon.HideCursor()
|
|
self._mouse_platform_visible = platform_visible
|
|
|
|
def set_exclusive_mouse(self, exclusive=True):
|
|
self._mouse_exclusive = exclusive
|
|
if exclusive:
|
|
# Move mouse to center of window
|
|
rect = Rect()
|
|
carbon.GetWindowBounds(self._window, kWindowContentRgn, byref(rect))
|
|
point = CGPoint()
|
|
point.x = (rect.right + rect.left) / 2
|
|
point.y = (rect.bottom + rect.top) / 2
|
|
# Skip the next motion event, which would return a large delta.
|
|
self._mouse_ignore_motion = True
|
|
carbon.CGWarpMouseCursorPosition(point)
|
|
carbon.CGAssociateMouseAndMouseCursorPosition(False)
|
|
else:
|
|
carbon.CGAssociateMouseAndMouseCursorPosition(True)
|
|
self.set_mouse_platform_visible()
|
|
|
|
def set_exclusive_keyboard(self, exclusive=True):
|
|
if exclusive:
|
|
# Note: power switch can also be disabled, with
|
|
# kUIOptionDisableSessionTerminate. That seems
|
|
# a little extreme though.
|
|
carbon.SetSystemUIMode(kUIModeAllHidden,
|
|
(kUIOptionDisableAppleMenu |
|
|
kUIOptionDisableProcessSwitch |
|
|
kUIOptionDisableForceQuit |
|
|
kUIOptionDisableHide))
|
|
else:
|
|
carbon.SetSystemUIMode(kUIModeNormal, 0)
|
|
|
|
def get_system_mouse_cursor(self, name):
|
|
if name == self.CURSOR_DEFAULT:
|
|
return DefaultMouseCursor()
|
|
|
|
themes = {
|
|
self.CURSOR_CROSSHAIR: kThemeCrossCursor,
|
|
self.CURSOR_HAND: kThemePointingHandCursor,
|
|
self.CURSOR_HELP: kThemeArrowCursor,
|
|
self.CURSOR_NO: kThemeNotAllowedCursor,
|
|
self.CURSOR_SIZE: kThemeArrowCursor,
|
|
self.CURSOR_SIZE_UP: kThemeResizeUpCursor,
|
|
self.CURSOR_SIZE_UP_RIGHT: kThemeArrowCursor,
|
|
self.CURSOR_SIZE_RIGHT: kThemeResizeRightCursor,
|
|
self.CURSOR_SIZE_DOWN_RIGHT: kThemeArrowCursor,
|
|
self.CURSOR_SIZE_DOWN: kThemeResizeDownCursor,
|
|
self.CURSOR_SIZE_DOWN_LEFT: kThemeArrowCursor,
|
|
self.CURSOR_SIZE_LEFT: kThemeResizeLeftCursor,
|
|
self.CURSOR_SIZE_UP_LEFT: kThemeArrowCursor,
|
|
self.CURSOR_SIZE_UP_DOWN: kThemeResizeUpDownCursor,
|
|
self.CURSOR_SIZE_LEFT_RIGHT: kThemeResizeLeftRightCursor,
|
|
self.CURSOR_TEXT: kThemeIBeamCursor,
|
|
self.CURSOR_WAIT: kThemeWatchCursor,
|
|
self.CURSOR_WAIT_ARROW: kThemeWatchCursor,
|
|
}
|
|
if name not in themes:
|
|
raise CarbonException('Unknown cursor name "%s"' % name)
|
|
return CarbonMouseCursor(themes[name])
|
|
|
|
def set_icon(self, *images):
|
|
# Only use the biggest image
|
|
image = images[0]
|
|
size = image.width * image.height
|
|
for img in images:
|
|
if img.width * img.height > size:
|
|
size = img.width * img.height
|
|
image = img
|
|
|
|
image = image.get_image_data()
|
|
format = 'ARGB'
|
|
pitch = -len(format) * image.width
|
|
|
|
data = image.get_data(format, pitch)
|
|
provider = carbon.CGDataProviderCreateWithData(
|
|
None, data, len(data), None)
|
|
|
|
colorspace = carbon.CGColorSpaceCreateDeviceRGB()
|
|
|
|
cgi = carbon.CGImageCreate(
|
|
image.width, image.height, 8, 32, -pitch,
|
|
colorspace,
|
|
kCGImageAlphaFirst,
|
|
provider,
|
|
None,
|
|
True,
|
|
kCGRenderingIntentDefault)
|
|
|
|
carbon.SetApplicationDockTileImage(cgi)
|
|
|
|
carbon.CGDataProviderRelease(provider)
|
|
carbon.CGColorSpaceRelease(colorspace)
|
|
|
|
# Non-public utilities
|
|
|
|
def _update_drawable(self):
|
|
# We can get there after context has been disposed, in which case
|
|
# just do nothing.
|
|
if not self._agl_context:
|
|
return
|
|
|
|
agl.aglUpdateContext(self._agl_context)
|
|
_aglcheck()
|
|
|
|
# Need a redraw
|
|
self.dispatch_event('on_expose')
|
|
|
|
def _update_track_region(self):
|
|
carbon.GetWindowRegion(self._window,
|
|
kWindowContentRgn, self._track_region)
|
|
carbon.ChangeMouseTrackingRegion(self._track_ref,
|
|
self._track_region, None)
|
|
|
|
def _install_event_handlers(self):
|
|
self._remove_event_handlers()
|
|
|
|
if self._fullscreen:
|
|
target = carbon.GetApplicationEventTarget()
|
|
else:
|
|
target = carbon.GetWindowEventTarget(self._window)
|
|
carbon.InstallStandardEventHandler(target)
|
|
|
|
self._carbon_event_handlers = []
|
|
self._carbon_event_handler_refs = []
|
|
|
|
for func_name in self._platform_event_names:
|
|
if not hasattr(self, func_name):
|
|
continue
|
|
|
|
func = getattr(self, func_name)
|
|
for event_class, event_kind in func._platform_event_data:
|
|
# TODO: could just build up array of class/kind
|
|
proc = EventHandlerProcPtr(func)
|
|
self._carbon_event_handlers.append(proc)
|
|
upp = carbon.NewEventHandlerUPP(proc)
|
|
types = EventTypeSpec()
|
|
types.eventClass = event_class
|
|
types.eventKind = event_kind
|
|
handler_ref = EventHandlerRef()
|
|
carbon.InstallEventHandler(
|
|
target,
|
|
upp,
|
|
1,
|
|
byref(types),
|
|
c_void_p(),
|
|
byref(handler_ref))
|
|
self._carbon_event_handler_refs.append(handler_ref)
|
|
|
|
def _remove_event_handlers(self):
|
|
for ref in self._carbon_event_handler_refs:
|
|
carbon.RemoveEventHandler(ref)
|
|
self._carbon_event_handler_refs = []
|
|
self._carbon_event_handlers = []
|
|
|
|
# Carbon event handlers
|
|
|
|
@CarbonEventHandler(kEventClassTextInput, kEventTextInputUnicodeForKeyEvent)
|
|
def _on_text_input(self, next_handler, ev, data):
|
|
size = c_uint32()
|
|
carbon.GetEventParameter(ev, kEventParamTextInputSendText,
|
|
typeUTF8Text, c_void_p(), 0, byref(size), c_void_p())
|
|
text = create_string_buffer(size.value)
|
|
carbon.GetEventParameter(ev, kEventParamTextInputSendText,
|
|
typeUTF8Text, c_void_p(), size.value, c_void_p(), byref(text))
|
|
text = text.value.decode('utf8')
|
|
|
|
raw_event = EventRef()
|
|
carbon.GetEventParameter(ev, kEventParamTextInputSendKeyboardEvent,
|
|
typeEventRef, c_void_p(), sizeof(raw_event), c_void_p(),
|
|
byref(raw_event))
|
|
symbol, modifiers = self._get_symbol_and_modifiers(raw_event)
|
|
|
|
motion_modifiers = modifiers & \
|
|
(key.MOD_COMMAND | key.MOD_CTRL | key.MOD_OPTION)
|
|
if (symbol, motion_modifiers) in _motion_map:
|
|
motion = _motion_map[symbol, motion_modifiers]
|
|
if modifiers & key.MOD_SHIFT:
|
|
self.dispatch_event('on_text_motion_select', motion)
|
|
else:
|
|
self.dispatch_event('on_text_motion', motion)
|
|
elif ((unicodedata.category(text[0]) != 'Cc' or text == u'\r') and
|
|
not (modifiers & key.MOD_COMMAND)):
|
|
self.dispatch_event('on_text', text)
|
|
return noErr
|
|
|
|
@CarbonEventHandler(kEventClassKeyboard, kEventRawKeyUp)
|
|
def _on_key_up(self, next_handler, ev, data):
|
|
symbol, modifiers = self._get_symbol_and_modifiers(ev)
|
|
if symbol:
|
|
self.dispatch_event('on_key_release', symbol, modifiers)
|
|
carbon.CallNextEventHandler(next_handler, ev)
|
|
return noErr
|
|
|
|
@CarbonEventHandler(kEventClassKeyboard, kEventRawKeyDown)
|
|
def _on_key_down(self, next_handler, ev, data):
|
|
symbol, modifiers = self._get_symbol_and_modifiers(ev)
|
|
if symbol:
|
|
self.dispatch_event('on_key_press', symbol, modifiers)
|
|
carbon.CallNextEventHandler(next_handler, ev)
|
|
return noErr
|
|
|
|
@staticmethod
|
|
def _get_symbol_and_modifiers(ev):
|
|
sym = c_uint32()
|
|
carbon.GetEventParameter(ev, kEventParamKeyCode,
|
|
typeUInt32, c_void_p(), sizeof(sym), c_void_p(), byref(sym))
|
|
modifiers = c_uint32()
|
|
carbon.GetEventParameter(ev, kEventParamKeyModifiers,
|
|
typeUInt32, c_void_p(), sizeof(modifiers), c_void_p(),
|
|
byref(modifiers))
|
|
|
|
symbol = keymap.get(sym.value, None)
|
|
if symbol is None:
|
|
symbol = key.user_key(sym.value)
|
|
|
|
return (symbol, CarbonWindow._map_modifiers(modifiers.value))
|
|
|
|
@staticmethod
|
|
def _map_modifiers(modifiers):
|
|
mapped_modifiers = 0
|
|
if modifiers & (shiftKey | rightShiftKey):
|
|
mapped_modifiers |= key.MOD_SHIFT
|
|
if modifiers & (controlKey | rightControlKey):
|
|
mapped_modifiers |= key.MOD_CTRL
|
|
if modifiers & (optionKey | rightOptionKey):
|
|
mapped_modifiers |= key.MOD_OPTION
|
|
if modifiers & alphaLock:
|
|
mapped_modifiers |= key.MOD_CAPSLOCK
|
|
if modifiers & cmdKey:
|
|
mapped_modifiers |= key.MOD_COMMAND
|
|
|
|
return mapped_modifiers
|
|
|
|
@CarbonEventHandler(kEventClassKeyboard, kEventRawKeyModifiersChanged)
|
|
def _on_modifiers_changed(self, next_handler, ev, data):
|
|
modifiers = c_uint32()
|
|
carbon.GetEventParameter(ev, kEventParamKeyModifiers,
|
|
typeUInt32, c_void_p(), sizeof(modifiers), c_void_p(),
|
|
byref(modifiers))
|
|
modifiers = modifiers.value
|
|
deltas = modifiers ^ self._current_modifiers
|
|
for mask, k in [
|
|
(controlKey, key.LCTRL),
|
|
(shiftKey, key.LSHIFT),
|
|
(cmdKey, key.LCOMMAND),
|
|
(optionKey, key.LOPTION),
|
|
(rightShiftKey, key.RSHIFT),
|
|
(rightOptionKey, key.ROPTION),
|
|
(rightControlKey, key.RCTRL),
|
|
(alphaLock, key.CAPSLOCK),
|
|
(numLock, key.NUMLOCK)]:
|
|
if deltas & mask:
|
|
if modifiers & mask:
|
|
self.dispatch_event('on_key_press',
|
|
k, self._mapped_modifiers)
|
|
else:
|
|
self.dispatch_event('on_key_release',
|
|
k, self._mapped_modifiers)
|
|
carbon.CallNextEventHandler(next_handler, ev)
|
|
|
|
self._mapped_modifiers = self._map_modifiers(modifiers)
|
|
self._current_modifiers = modifiers
|
|
return noErr
|
|
|
|
def _get_mouse_position(self, ev):
|
|
position = HIPoint()
|
|
carbon.GetEventParameter(ev, kEventParamMouseLocation,
|
|
typeHIPoint, c_void_p(), sizeof(position), c_void_p(),
|
|
byref(position))
|
|
|
|
bounds = Rect()
|
|
carbon.GetWindowBounds(self._window, kWindowContentRgn, byref(bounds))
|
|
return int(position.x - bounds.left), int(position.y - bounds.top)
|
|
|
|
@staticmethod
|
|
def _get_mouse_button_and_modifiers(ev):
|
|
button = EventMouseButton()
|
|
carbon.GetEventParameter(ev, kEventParamMouseButton,
|
|
typeMouseButton, c_void_p(), sizeof(button), c_void_p(),
|
|
byref(button))
|
|
|
|
if button.value == 1:
|
|
button = mouse.LEFT
|
|
elif button.value == 2:
|
|
button = mouse.RIGHT
|
|
elif button.value == 3:
|
|
button = mouse.MIDDLE
|
|
else:
|
|
button = None
|
|
|
|
modifiers = c_uint32()
|
|
carbon.GetEventParameter(ev, kEventParamKeyModifiers,
|
|
typeUInt32, c_void_p(), sizeof(modifiers), c_void_p(),
|
|
byref(modifiers))
|
|
|
|
return button, CarbonWindow._map_modifiers(modifiers.value)
|
|
|
|
@staticmethod
|
|
def _get_mouse_in_content(ev):
|
|
position = Point()
|
|
carbon.GetEventParameter(ev, kEventParamMouseLocation,
|
|
typeQDPoint, c_void_p(), sizeof(position), c_void_p(),
|
|
byref(position))
|
|
return carbon.FindWindow(position, None) == inContent
|
|
|
|
@CarbonEventHandler(kEventClassMouse, kEventMouseDown)
|
|
def _on_mouse_down(self, next_handler, ev, data):
|
|
if self._fullscreen or self._get_mouse_in_content(ev):
|
|
button, modifiers = self._get_mouse_button_and_modifiers(ev)
|
|
if button is not None:
|
|
x, y = self._get_mouse_position(ev)
|
|
y = self.height - y
|
|
self.dispatch_event('on_mouse_press', x, y, button, modifiers)
|
|
|
|
carbon.CallNextEventHandler(next_handler, ev)
|
|
return noErr
|
|
|
|
@CarbonEventHandler(kEventClassMouse, kEventMouseUp)
|
|
def _on_mouse_up(self, next_handler, ev, data):
|
|
# Always report mouse up, even out of content area, because it's
|
|
# probably after a drag gesture.
|
|
button, modifiers = self._get_mouse_button_and_modifiers(ev)
|
|
if button is not None:
|
|
x, y = self._get_mouse_position(ev)
|
|
y = self.height - y
|
|
self.dispatch_event('on_mouse_release', x, y, button, modifiers)
|
|
|
|
carbon.CallNextEventHandler(next_handler, ev)
|
|
return noErr
|
|
|
|
@CarbonEventHandler(kEventClassMouse, kEventMouseMoved)
|
|
def _on_mouse_moved(self, next_handler, ev, data):
|
|
if ((self._fullscreen or self._get_mouse_in_content(ev))
|
|
and not self._mouse_ignore_motion):
|
|
x, y = self._get_mouse_position(ev)
|
|
y = self.height - y
|
|
|
|
self._mouse_x = x
|
|
self._mouse_y = y
|
|
|
|
delta = HIPoint()
|
|
carbon.GetEventParameter(ev, kEventParamMouseDelta,
|
|
typeHIPoint, c_void_p(), sizeof(delta), c_void_p(),
|
|
byref(delta))
|
|
|
|
# Motion event
|
|
self.dispatch_event('on_mouse_motion',
|
|
x, y, delta.x, -delta.y)
|
|
elif self._mouse_ignore_motion:
|
|
self._mouse_ignore_motion = False
|
|
carbon.CallNextEventHandler(next_handler, ev)
|
|
return noErr
|
|
|
|
@CarbonEventHandler(kEventClassMouse, kEventMouseDragged)
|
|
def _on_mouse_dragged(self, next_handler, ev, data):
|
|
button, modifiers = self._get_mouse_button_and_modifiers(ev)
|
|
if button is not None:
|
|
x, y = self._get_mouse_position(ev)
|
|
y = self.height - y
|
|
|
|
self._mouse_x = x
|
|
self._mouse_y = y
|
|
|
|
delta = HIPoint()
|
|
carbon.GetEventParameter(ev, kEventParamMouseDelta,
|
|
typeHIPoint, c_void_p(), sizeof(delta), c_void_p(),
|
|
byref(delta))
|
|
|
|
# Drag event
|
|
self.dispatch_event('on_mouse_drag',
|
|
x, y, delta.x, -delta.y, button, modifiers)
|
|
|
|
carbon.CallNextEventHandler(next_handler, ev)
|
|
return noErr
|
|
|
|
@CarbonEventHandler(kEventClassMouse, kEventMouseEntered)
|
|
def _on_mouse_entered(self, next_handler, ev, data):
|
|
x, y = self._get_mouse_position(ev)
|
|
y = self.height - y
|
|
|
|
self._mouse_x = x
|
|
self._mouse_y = y
|
|
self._mouse_in_window = True
|
|
self.set_mouse_platform_visible()
|
|
|
|
self.dispatch_event('on_mouse_enter', x, y)
|
|
|
|
carbon.CallNextEventHandler(next_handler, ev)
|
|
return noErr
|
|
|
|
@CarbonEventHandler(kEventClassMouse, kEventMouseExited)
|
|
def _on_mouse_exited(self, next_handler, ev, data):
|
|
if not self._fullscreen:
|
|
x, y = self._get_mouse_position(ev)
|
|
y = self.height - y
|
|
|
|
self._mouse_in_window = False
|
|
self.set_mouse_platform_visible()
|
|
|
|
self.dispatch_event('on_mouse_leave', x, y)
|
|
|
|
carbon.CallNextEventHandler(next_handler, ev)
|
|
return noErr
|
|
|
|
@CarbonEventHandler(kEventClassMouse, kEventMouseWheelMoved)
|
|
def _on_mouse_wheel_moved(self, next_handler, ev, data):
|
|
|
|
x, y = self._get_mouse_position(ev)
|
|
y = self.height - y
|
|
|
|
axis = EventMouseWheelAxis()
|
|
carbon.GetEventParameter(ev, kEventParamMouseWheelAxis,
|
|
typeMouseWheelAxis, c_void_p(), sizeof(axis), c_void_p(),
|
|
byref(axis))
|
|
delta = c_long()
|
|
carbon.GetEventParameter(ev, kEventParamMouseWheelDelta,
|
|
typeSInt32, c_void_p(), sizeof(delta), c_void_p(),
|
|
byref(delta))
|
|
if axis.value == kEventMouseWheelAxisX:
|
|
self.dispatch_event('on_mouse_scroll',
|
|
x, y, delta.value, 0)
|
|
else:
|
|
self.dispatch_event('on_mouse_scroll',
|
|
x, y, 0, delta.value)
|
|
|
|
# _Don't_ call the next handler, which is application, as this then
|
|
# calls our window handler again.
|
|
#carbon.CallNextEventHandler(next_handler, ev)
|
|
return noErr
|
|
|
|
@CarbonEventHandler(kEventClassWindow, kEventWindowClose)
|
|
def _on_window_close(self, next_handler, ev, data):
|
|
self.dispatch_event('on_close')
|
|
|
|
# Presumably the next event handler is the one that closes
|
|
# the window; don't do that here.
|
|
#carbon.CallNextEventHandler(next_handler, ev)
|
|
return noErr
|
|
|
|
_resizing = None
|
|
|
|
@CarbonEventHandler(kEventClassWindow, kEventWindowResizeStarted)
|
|
def _on_window_resize_started(self, next_handler, ev, data):
|
|
self._resizing = (self.width, self.height)
|
|
|
|
from pyglet import app
|
|
if app.event_loop is not None:
|
|
app.event_loop._stop_polling()
|
|
|
|
carbon.CallNextEventHandler(next_handler, ev)
|
|
return noErr
|
|
|
|
@CarbonEventHandler(kEventClassWindow, kEventWindowResizeCompleted)
|
|
def _on_window_resize_completed(self, next_handler, ev, data):
|
|
self._resizing = None
|
|
|
|
rect = Rect()
|
|
carbon.GetWindowBounds(self._window, kWindowContentRgn, byref(rect))
|
|
width = rect.right - rect.left
|
|
height = rect.bottom - rect.top
|
|
|
|
self.switch_to()
|
|
self.dispatch_event('on_resize', width, height)
|
|
self.dispatch_event('on_expose')
|
|
|
|
carbon.CallNextEventHandler(next_handler, ev)
|
|
return noErr
|
|
|
|
_dragging = False
|
|
|
|
@CarbonEventHandler(kEventClassWindow, kEventWindowDragStarted)
|
|
def _on_window_drag_started(self, next_handler, ev, data):
|
|
self._dragging = True
|
|
|
|
from pyglet import app
|
|
if app.event_loop is not None:
|
|
app.event_loop._stop_polling()
|
|
|
|
carbon.CallNextEventHandler(next_handler, ev)
|
|
return noErr
|
|
|
|
@CarbonEventHandler(kEventClassWindow, kEventWindowDragCompleted)
|
|
def _on_window_drag_completed(self, next_handler, ev, data):
|
|
self._dragging = False
|
|
|
|
rect = Rect()
|
|
carbon.GetWindowBounds(self._window, kWindowContentRgn, byref(rect))
|
|
|
|
self.dispatch_event('on_move', rect.left, rect.top)
|
|
|
|
carbon.CallNextEventHandler(next_handler, ev)
|
|
return noErr
|
|
|
|
@CarbonEventHandler(kEventClassWindow, kEventWindowBoundsChanging)
|
|
def _on_window_bounds_changing(self, next_handler, ev, data):
|
|
from pyglet import app
|
|
if app.event_loop is not None:
|
|
carbon.SetEventLoopTimerNextFireTime(app.event_loop._timer,
|
|
c_double(0.0))
|
|
|
|
carbon.CallNextEventHandler(next_handler, ev)
|
|
return noErr
|
|
|
|
@CarbonEventHandler(kEventClassWindow, kEventWindowBoundsChanged)
|
|
def _on_window_bounds_change(self, next_handler, ev, data):
|
|
self._update_track_region()
|
|
self._update_drawable()
|
|
|
|
carbon.CallNextEventHandler(next_handler, ev)
|
|
return noErr
|
|
|
|
@CarbonEventHandler(kEventClassWindow, kEventWindowZoomed)
|
|
def _on_window_zoomed(self, next_handler, ev, data):
|
|
rect = Rect()
|
|
carbon.GetWindowBounds(self._window, kWindowContentRgn, byref(rect))
|
|
width = rect.right - rect.left
|
|
height = rect.bottom - rect.top
|
|
|
|
self.dispatch_event('on_move', rect.left, rect.top)
|
|
self.dispatch_event('on_resize', width, height)
|
|
|
|
carbon.CallNextEventHandler(next_handler, ev)
|
|
return noErr
|
|
|
|
@CarbonEventHandler(kEventClassWindow, kEventWindowActivated)
|
|
def _on_window_activated(self, next_handler, ev, data):
|
|
self.dispatch_event('on_activate')
|
|
|
|
carbon.CallNextEventHandler(next_handler, ev)
|
|
return noErr
|
|
|
|
@CarbonEventHandler(kEventClassWindow, kEventWindowDeactivated)
|
|
def _on_window_deactivated(self, next_handler, ev, data):
|
|
self.dispatch_event('on_deactivate')
|
|
|
|
carbon.CallNextEventHandler(next_handler, ev)
|
|
return noErr
|
|
|
|
@CarbonEventHandler(kEventClassWindow, kEventWindowShown)
|
|
@CarbonEventHandler(kEventClassWindow, kEventWindowExpanded)
|
|
def _on_window_shown(self, next_handler, ev, data):
|
|
self._update_drawable()
|
|
self.dispatch_event('on_show')
|
|
|
|
carbon.CallNextEventHandler(next_handler, ev)
|
|
return noErr
|
|
|
|
@CarbonEventHandler(kEventClassWindow, kEventWindowHidden)
|
|
@CarbonEventHandler(kEventClassWindow, kEventWindowCollapsed)
|
|
def _on_window_hidden(self, next_handler, ev, data):
|
|
self.dispatch_event('on_hide')
|
|
|
|
carbon.CallNextEventHandler(next_handler, ev)
|
|
return noErr
|
|
|
|
@CarbonEventHandler(kEventClassWindow, kEventWindowDrawContent)
|
|
def _on_window_draw_content(self, next_handler, ev, data):
|
|
self.dispatch_event('on_expose')
|
|
|
|
carbon.CallNextEventHandler(next_handler, ev)
|
|
return noErr
|
|
|
|
|
|
|
|
def _create_cfstring(text):
|
|
return carbon.CFStringCreateWithCString(c_void_p(),
|
|
text.encode('utf8'),
|
|
kCFStringEncodingUTF8)
|
|
|
|
def _oscheck(result):
|
|
if result != noErr:
|
|
raise 'Carbon error %d' % result
|
|
return result
|
|
|
|
def _aglcheck():
|
|
err = agl.aglGetError()
|
|
if err != agl.AGL_NO_ERROR:
|
|
raise CarbonException(cast(agl.aglErrorString(err), c_char_p).value)
|
|
|