1# ----------------------------------------------------------------------------
2# pyglet
3# Copyright (c) 2006-2008 Alex Holkner
4# Copyright (c) 2008-2021 pyglet contributors
5# All rights reserved.
6#
7# Redistribution and use in source and binary forms, with or without
8# modification, are permitted provided that the following conditions
9# are met:
10#
11#  * Redistributions of source code must retain the above copyright
12#    notice, this list of conditions and the following disclaimer.
13#  * Redistributions in binary form must reproduce the above copyright
14#    notice, this list of conditions and the following disclaimer in
15#    the documentation and/or other materials provided with the
16#    distribution.
17#  * Neither the name of pyglet nor the names of its
18#    contributors may be used to endorse or promote products
19#    derived from this software without specific prior written
20#    permission.
21#
22# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
23# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
24# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
25# FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
26# COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
27# INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
28# BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
29# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
30# CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
31# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
32# ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
33# POSSIBILITY OF SUCH DAMAGE.
34# ----------------------------------------------------------------------------
35
36from pyglet.libs.darwin.cocoapy import ObjCClass, ObjCSubclass, ObjCInstance
37from pyglet.libs.darwin.cocoapy import NSApplicationDidHideNotification
38from pyglet.libs.darwin.cocoapy import NSApplicationDidUnhideNotification
39from pyglet.libs.darwin.cocoapy import send_super, get_selector
40from pyglet.libs.darwin.cocoapy import PyObjectEncoding
41from pyglet.libs.darwin.cocoapy import quartz
42from .systemcursor import SystemCursor
43
44NSNotificationCenter = ObjCClass('NSNotificationCenter')
45NSApplication = ObjCClass('NSApplication')
46
47
48class PygletDelegate_Implementation:
49    PygletDelegate = ObjCSubclass('NSObject', 'PygletDelegate')
50
51    @PygletDelegate.method(b'@'+PyObjectEncoding)
52    def initWithWindow_(self, window):
53        self = ObjCInstance(send_super(self, 'init'))
54
55        if not self:
56            return None
57
58        # CocoaWindow object.
59        self._window = window
60        window._nswindow.setDelegate_(self)
61
62        # Register delegate for hide and unhide notifications so that we
63        # can dispatch the corresponding pyglet events.
64        notificationCenter = NSNotificationCenter.defaultCenter()
65
66        notificationCenter.addObserver_selector_name_object_(
67            self, get_selector('applicationDidHide:'),
68            NSApplicationDidHideNotification, None)
69
70        notificationCenter.addObserver_selector_name_object_(
71            self, get_selector('applicationDidUnhide:'),
72            NSApplicationDidUnhideNotification, None)
73
74        # Flag set when we pause exclusive mouse mode if window loses key status.
75        self.did_pause_exclusive_mouse = False
76        return self
77
78    @PygletDelegate.method('v')
79    def dealloc(self):
80        # Unregister delegate from notification center.
81        notificationCenter = NSNotificationCenter.defaultCenter()
82        notificationCenter.removeObserver_(self)
83        self._window = None
84        send_super(self, 'dealloc')
85
86    @PygletDelegate.method('v@')
87    def applicationDidHide_(self, notification):
88        self._window.dispatch_event("on_hide")
89
90    @PygletDelegate.method('v@')
91    def applicationDidUnhide_(self, notification):
92        if self._window._is_mouse_exclusive and quartz.CGCursorIsVisible():
93            # The cursor should be hidden, but for some reason it's not;
94            # try to force the cursor to hide (without over-hiding).
95            SystemCursor.unhide()
96            SystemCursor.hide()
97            pass
98        self._window.dispatch_event("on_show")
99
100    @PygletDelegate.method('B@')
101    def windowShouldClose_(self, notification):
102        # The method is not called if [NSWindow close] was used.
103        self._window.dispatch_event("on_close")
104        return False
105
106    @PygletDelegate.method('v@')
107    def windowDidMove_(self, notification):
108        x, y = self._window.get_location()
109        self._window.dispatch_event("on_move", x, y)
110
111    @PygletDelegate.method('v@')
112    def windowDidBecomeKey_(self, notification):
113         # Restore exclusive mouse mode if it was active before we lost key status.
114         if self.did_pause_exclusive_mouse:
115             self._window.set_exclusive_mouse(True)
116             self.did_pause_exclusive_mouse = False
117             self._window._nswindow.setMovable_(True)  # Mac OS 10.6
118         # Restore previous mouse visibility settings.
119         self._window.set_mouse_platform_visible()
120         self._window.dispatch_event("on_activate")
121
122    @PygletDelegate.method('v@')
123    def windowDidResignKey_(self, notification):
124        # Pause exclusive mouse mode if it is active.
125        if self._window._is_mouse_exclusive:
126            self._window.set_exclusive_mouse(False)
127            self.did_pause_exclusive_mouse = True
128            # We need to prevent the window from being unintentionally dragged
129            # (by the call to set_mouse_position in set_exclusive_mouse) when
130            # the window is reactivated by clicking on its title bar.
131            self._window._nswindow.setMovable_(False)  # Mac OS X 10.6
132        # Make sure that cursor is visible.
133        self._window.set_mouse_platform_visible(True)
134        self._window.dispatch_event("on_deactivate")
135
136    @PygletDelegate.method('v@')
137    def windowDidMiniaturize_(self, notification):
138        self._window.dispatch_event("on_hide")
139
140    @PygletDelegate.method('v@')
141    def windowDidDeminiaturize_(self, notification):
142        if self._window._is_mouse_exclusive and quartz.CGCursorIsVisible():
143            # The cursor should be hidden, but for some reason it's not;
144            # try to force the cursor to hide (without over-hiding).
145            SystemCursor.unhide()
146            SystemCursor.hide()
147            pass
148        self._window.dispatch_event("on_show")
149
150    @PygletDelegate.method('v@')
151    def windowDidExpose_(self,  notification):
152        self._window.dispatch_event("on_expose")
153
154    @PygletDelegate.method('v@')
155    def terminate_(self, sender):
156        NSApp = NSApplication.sharedApplication()
157        NSApp.terminate_(self)
158
159    @PygletDelegate.method('B@')
160    def validateMenuItem_(self, menuitem):
161        # Disable quitting with command-q when in keyboard exclusive mode.
162        if menuitem.action() == get_selector('terminate:'):
163            return not self._window._is_keyboard_exclusive
164        return True
165
166
167PygletDelegate = ObjCClass('PygletDelegate')
168