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 ctypes import *
37
38import pyglet
39from pyglet import gl
40from pyglet.window import BaseWindow, WindowException
41from pyglet.window import MouseCursor, DefaultMouseCursor
42from pyglet.event import EventDispatcher
43
44from pyglet.canvas.cocoa import CocoaCanvas
45
46from pyglet.libs.darwin import cocoapy, CGPoint
47
48from .systemcursor import SystemCursor
49from .pyglet_delegate import PygletDelegate
50from .pyglet_textview import PygletTextView
51from .pyglet_window import PygletWindow, PygletToolWindow
52from .pyglet_view import PygletView
53
54NSApplication = cocoapy.ObjCClass('NSApplication')
55NSCursor = cocoapy.ObjCClass('NSCursor')
56NSAutoreleasePool = cocoapy.ObjCClass('NSAutoreleasePool')
57NSColor = cocoapy.ObjCClass('NSColor')
58NSEvent = cocoapy.ObjCClass('NSEvent')
59NSImage = cocoapy.ObjCClass('NSImage')
60
61quartz = cocoapy.quartz
62cf = cocoapy.cf
63
64
65class CocoaMouseCursor(MouseCursor):
66    gl_drawable = False
67
68    def __init__(self, cursorName):
69        # cursorName is a string identifying one of the named default NSCursors
70        # e.g. 'pointingHandCursor', and can be sent as message to NSCursor class.
71        self.cursorName = cursorName
72
73    def set(self):
74        cursor = getattr(NSCursor, self.cursorName)()
75        cursor.set()
76
77
78class CocoaWindow(BaseWindow):
79
80    # NSWindow instance.
81    _nswindow = None
82
83    # Delegate object.
84    _delegate = None
85
86    # Window properties
87    _minimum_size = None
88    _maximum_size = None
89
90    _is_mouse_exclusive = False
91    _mouse_platform_visible = True
92    _mouse_ignore_motion = False
93
94    _is_keyboard_exclusive = False
95
96    # Flag set during close() method.
97    _was_closed = False
98
99    # NSWindow style masks.
100    _style_masks = {
101        BaseWindow.WINDOW_STYLE_DEFAULT:    cocoapy.NSTitledWindowMask |
102                                            cocoapy.NSClosableWindowMask |
103                                            cocoapy.NSMiniaturizableWindowMask,
104        BaseWindow.WINDOW_STYLE_DIALOG:     cocoapy.NSTitledWindowMask |
105                                            cocoapy.NSClosableWindowMask,
106        BaseWindow.WINDOW_STYLE_TOOL:       cocoapy.NSTitledWindowMask |
107                                            cocoapy.NSClosableWindowMask |
108                                            cocoapy.NSUtilityWindowMask,
109        BaseWindow.WINDOW_STYLE_BORDERLESS: cocoapy.NSBorderlessWindowMask,
110    }
111
112    def _recreate(self, changes):
113        if 'context' in changes:
114            self.context.set_current()
115
116        if 'fullscreen' in changes:
117            if not self._fullscreen:  # leaving fullscreen
118                self.screen.release_display()
119
120        self._create()
121
122    def _create(self):
123        # Create a temporary autorelease pool for this method.
124        pool = NSAutoreleasePool.alloc().init()
125
126        if self._nswindow:
127            # The window is about the be recreated so destroy everything
128            # associated with the old window, then destroy the window itself.
129            nsview = self.canvas.nsview
130            self.canvas = None
131            self._nswindow.orderOut_(None)
132            self._nswindow.close()
133            self.context.detach()
134            self._nswindow.release()
135            self._nswindow = None
136            nsview.release()
137            self._delegate.release()
138            self._delegate = None
139
140        # Determine window parameters.
141        content_rect = cocoapy.NSMakeRect(0, 0, self._width, self._height)
142        WindowClass = PygletWindow
143        if self._fullscreen:
144            style_mask = cocoapy.NSBorderlessWindowMask
145        else:
146            if self._style not in self._style_masks:
147                self._style = self.WINDOW_STYLE_DEFAULT
148            style_mask = self._style_masks[self._style]
149            if self._resizable:
150                style_mask |= cocoapy.NSResizableWindowMask
151            if self._style == BaseWindow.WINDOW_STYLE_TOOL:
152                WindowClass = PygletToolWindow
153
154        # First create an instance of our NSWindow subclass.
155
156        # FIX ME:
157        # Need to use this initializer to have any hope of multi-monitor support.
158        # But currently causes problems on Mac OS X Lion.  So for now, we initialize the
159        # window without including screen information.
160        #
161        # self._nswindow = WindowClass.alloc().initWithContentRect_styleMask_backing_defer_screen_(
162        #     content_rect,           # contentRect
163        #     style_mask,             # styleMask
164        #     NSBackingStoreBuffered, # backing
165        #     False,                  # defer
166        #     self.screen.get_nsscreen())  # screen
167
168        self._nswindow = WindowClass.alloc().initWithContentRect_styleMask_backing_defer_(
169            content_rect,                    # contentRect
170            style_mask,                      # styleMask
171            cocoapy.NSBackingStoreBuffered,  # backing
172            False)                           # defer
173
174        if self._fullscreen:
175            # BUG: I suspect that this doesn't do the right thing when using
176            # multiple monitors (which would be to go fullscreen on the monitor
177            # where the window is located).  However I've no way to test.
178            blackColor = NSColor.blackColor()
179            self._nswindow.setBackgroundColor_(blackColor)
180            self._nswindow.setOpaque_(True)
181            self.screen.capture_display()
182            self._nswindow.setLevel_(quartz.CGShieldingWindowLevel())
183            self.context.set_full_screen()
184            self._center_window()
185            self._mouse_in_window = True
186        else:
187            self._set_nice_window_location()
188            self._mouse_in_window = self._mouse_in_content_rect()
189
190        # Then create a view and set it as our NSWindow's content view.
191        self._nsview = PygletView.alloc().initWithFrame_cocoaWindow_(content_rect, self)
192        self._nswindow.setContentView_(self._nsview)
193        self._nswindow.makeFirstResponder_(self._nsview)
194
195        # Create a canvas with the view as its drawable and attach context to it.
196        self.canvas = CocoaCanvas(self.display, self.screen, self._nsview)
197        self.context.attach(self.canvas)
198
199        # Configure the window.
200        self._nswindow.setAcceptsMouseMovedEvents_(True)
201        self._nswindow.setReleasedWhenClosed_(False)
202        self._nswindow.useOptimizedDrawing_(True)
203        self._nswindow.setPreservesContentDuringLiveResize_(False)
204
205        # Set the delegate.
206        self._delegate = PygletDelegate.alloc().initWithWindow_(self)
207
208        # Configure CocoaWindow.
209        self.set_caption(self._caption)
210        if self._minimum_size is not None:
211            self.set_minimum_size(*self._minimum_size)
212        if self._maximum_size is not None:
213            self.set_maximum_size(*self._maximum_size)
214
215        # TODO: Add support for file drops.
216        if self._file_drops:
217            raise NotImplementedError("File drops are not implemented on MacOS")
218
219        self.context.update_geometry()
220        self.switch_to()
221        self.set_vsync(self._vsync)
222        self.set_visible(self._visible)
223
224        pool.drain()
225
226    def _set_nice_window_location(self):
227        # Construct a list of all visible windows that aren't us.
228        visible_windows = [win for win in pyglet.app.windows if
229                           win is not self and
230                           win._nswindow and
231                           win._nswindow.isVisible()]
232        # If there aren't any visible windows, then center this window.
233        if not visible_windows:
234            self._center_window()
235        # Otherwise, cascade from last window in list.
236        else:
237            point = visible_windows[-1]._nswindow.cascadeTopLeftFromPoint_(cocoapy.NSZeroPoint)
238            self._nswindow.cascadeTopLeftFromPoint_(point)
239
240    def _center_window(self):
241        # [NSWindow center] does not move the window to a true center position
242        # and also always moves the window to the main display.
243        x = self.screen.x + int((self.screen.width - self._width) // 2)
244        y = self.screen.y + int((self.screen.height - self._height) // 2)
245        self._nswindow.setFrameOrigin_(cocoapy.NSPoint(x, y))
246
247    def close(self):
248        # If we've already gone through this once, don't do it again.
249        if self._was_closed:
250            return
251
252        # Create a temporary autorelease pool for this method.
253        pool = NSAutoreleasePool.new()
254
255        # Restore cursor visibility
256        self.set_mouse_platform_visible(True)
257        self.set_exclusive_mouse(False)
258        self.set_exclusive_keyboard(False)
259
260        # Remove the delegate object
261        if self._delegate:
262            self._nswindow.setDelegate_(None)
263            self._delegate.release()
264            self._delegate = None
265
266        # Remove window from display and remove its view.
267        if self._nswindow:
268            self._nswindow.orderOut_(None)
269            self._nswindow.setContentView_(None)
270            self._nswindow.close()
271
272        # Restore screen mode. This also releases the display
273        # if it was captured for fullscreen mode.
274        self.screen.restore_mode()
275
276        # Remove view from canvas and then remove canvas.
277        if self.canvas:
278            self.canvas.nsview.release()
279            self.canvas.nsview = None
280            self.canvas = None
281
282        # Do this last, so that we don't see white flash
283        # when exiting application from fullscreen mode.
284        super(CocoaWindow, self).close()
285
286        self._was_closed = True
287        pool.drain()
288
289    def switch_to(self):
290        if self.context:
291            self.context.set_current()
292
293    def flip(self):
294        self.draw_mouse_cursor()
295        if self.context:
296            self.context.flip()
297
298    def dispatch_events(self):
299        self._allow_dispatch_event = True
300        # Process all pyglet events.
301        self.dispatch_pending_events()
302        event = True
303
304        # Dequeue and process all of the pending Cocoa events.
305        pool = NSAutoreleasePool.new()
306        NSApp = NSApplication.sharedApplication()
307        while event and self._nswindow and self._context:
308            event = NSApp.nextEventMatchingMask_untilDate_inMode_dequeue_(
309                cocoapy.NSAnyEventMask, None, cocoapy.NSEventTrackingRunLoopMode, True)
310
311            if event:
312                event_type = event.type()
313                # Pass on all events.
314                NSApp.sendEvent_(event)
315                # And resend key events to special handlers.
316                if event_type == cocoapy.NSKeyDown and not event.isARepeat():
317                    NSApp.sendAction_to_from_(cocoapy.get_selector('pygletKeyDown:'), None, event)
318                elif event_type == cocoapy.NSKeyUp:
319                    NSApp.sendAction_to_from_(cocoapy.get_selector('pygletKeyUp:'), None, event)
320                elif event_type == cocoapy.NSFlagsChanged:
321                    NSApp.sendAction_to_from_(cocoapy.get_selector('pygletFlagsChanged:'), None, event)
322                NSApp.updateWindows()
323
324        pool.drain()
325
326        self._allow_dispatch_event = False
327
328    def dispatch_pending_events(self):
329        while self._event_queue:
330            event = self._event_queue.pop(0)
331            EventDispatcher.dispatch_event(self, *event)
332
333    def set_caption(self, caption):
334        self._caption = caption
335        if self._nswindow is not None:
336            self._nswindow.setTitle_(cocoapy.get_NSString(caption))
337
338    def set_icon(self, *images):
339        # Only use the biggest image from the list.
340        max_image = images[0]
341        for img in images:
342            if img.width > max_image.width and img.height > max_image.height:
343                max_image = img
344
345        # Grab image data from pyglet image.
346        image = max_image.get_image_data()
347        format = 'ARGB'
348        bytesPerRow = len(format) * image.width
349        data = image.get_data(format, -bytesPerRow)
350
351        # Use image data to create a data provider.
352        # Using CGDataProviderCreateWithData crashes PyObjC 2.2b3, so we create
353        # a CFDataRef object first and use it to create the data provider.
354        cfdata = c_void_p(cf.CFDataCreate(None, data, len(data)))
355        provider = c_void_p(quartz.CGDataProviderCreateWithCFData(cfdata))
356
357        colorSpace = c_void_p(quartz.CGColorSpaceCreateDeviceRGB())
358
359        # Then create a CGImage from the provider.
360        cgimage = c_void_p(quartz.CGImageCreate(
361            image.width, image.height, 8, 32, bytesPerRow,
362            colorSpace,
363            cocoapy.kCGImageAlphaFirst,
364            provider,
365            None,
366            True,
367            cocoapy.kCGRenderingIntentDefault))
368
369        if not cgimage:
370            return
371
372        cf.CFRelease(cfdata)
373        quartz.CGDataProviderRelease(provider)
374        quartz.CGColorSpaceRelease(colorSpace)
375
376        # Turn the CGImage into an NSImage.
377        size = cocoapy.NSMakeSize(image.width, image.height)
378        nsimage = NSImage.alloc().initWithCGImage_size_(cgimage, size)
379        if not nsimage:
380            return
381
382        # And finally set the app icon.
383        NSApp = NSApplication.sharedApplication()
384        NSApp.setApplicationIconImage_(nsimage)
385        nsimage.release()
386
387    def get_location(self):
388        window_frame = self._nswindow.frame()
389        rect = self._nswindow.contentRectForFrameRect_(window_frame)
390        screen_frame = self._nswindow.screen().frame()
391        screen_width = int(screen_frame.size.width)
392        screen_height = int(screen_frame.size.height)
393        return int(rect.origin.x), int(screen_height - rect.origin.y - rect.size.height)
394
395    def set_location(self, x, y):
396        window_frame = self._nswindow.frame()
397        rect = self._nswindow.contentRectForFrameRect_(window_frame)
398        screen_frame = self._nswindow.screen().frame()
399        screen_width = int(screen_frame.size.width)
400        screen_height = int(screen_frame.size.height)
401        origin = cocoapy.NSPoint(x, screen_height - y - rect.size.height)
402        self._nswindow.setFrameOrigin_(origin)
403
404    def get_size(self):
405        window_frame = self._nswindow.frame()
406        rect = self._nswindow.contentRectForFrameRect_(window_frame)
407        return int(rect.size.width), int(rect.size.height)
408
409    def get_framebuffer_size(self):
410        view = self.context._nscontext.view()
411        bounds = view.convertRectToBacking_(view.bounds()).size
412        return int(bounds.width), int(bounds.height)
413
414    # :deprecated: Use Window.get_framebuffer_size
415    get_viewport_size = get_framebuffer_size
416
417    def set_size(self, width, height):
418        if self._fullscreen:
419            raise WindowException('Cannot set size of fullscreen window.')
420        self._width = max(1, int(width))
421        self._height = max(1, int(height))
422        # Move frame origin down so that top-left corner of window doesn't move.
423        window_frame = self._nswindow.frame()
424        rect = self._nswindow.contentRectForFrameRect_(window_frame)
425        rect.origin.y += rect.size.height - self._height
426        rect.size.width = self._width
427        rect.size.height = self._height
428        new_frame = self._nswindow.frameRectForContentRect_(rect)
429        # The window background flashes when the frame size changes unless it's
430        # animated, but we can set the window's animationResizeTime to zero.
431        is_visible = self._nswindow.isVisible()
432        self._nswindow.setFrame_display_animate_(new_frame, True, is_visible)
433
434    def set_minimum_size(self, width, height):
435        self._minimum_size = cocoapy.NSSize(width, height)
436
437        if self._nswindow is not None:
438            self._nswindow.setContentMinSize_(self._minimum_size)
439
440    def set_maximum_size(self, width, height):
441        self._maximum_size = cocoapy.NSSize(width, height)
442
443        if self._nswindow is not None:
444            self._nswindow.setContentMaxSize_(self._maximum_size)
445
446    def activate(self):
447        if self._nswindow is not None:
448            NSApp = NSApplication.sharedApplication()
449            NSApp.activateIgnoringOtherApps_(True)
450            self._nswindow.makeKeyAndOrderFront_(None)
451
452    def set_visible(self, visible=True):
453        self._visible = visible
454        if self._nswindow is not None:
455            if visible:
456                # Not really sure why on_resize needs to be here,
457                # but it's what pyglet wants.
458                self.dispatch_event('on_resize', self._width, self._height)
459                self.dispatch_event('on_show')
460                self.dispatch_event('on_expose')
461                self._nswindow.makeKeyAndOrderFront_(None)
462            else:
463                self._nswindow.orderOut_(None)
464
465    def minimize(self):
466        self._mouse_in_window = False
467        if self._nswindow is not None:
468            self._nswindow.miniaturize_(None)
469
470    def maximize(self):
471        if self._nswindow is not None:
472            self._nswindow.zoom_(None)
473
474    def set_vsync(self, vsync):
475        if pyglet.options['vsync'] is not None:
476            vsync = pyglet.options['vsync']
477        self._vsync = vsync  # _recreate depends on this
478        if self.context:
479            self.context.set_vsync(vsync)
480
481    def _mouse_in_content_rect(self):
482        # Returns true if mouse is inside the window's content rectangle.
483        # Better to use this method to check manually rather than relying
484        # on instance variables that may not be set correctly.
485        point = NSEvent.mouseLocation()
486        window_frame = self._nswindow.frame()
487        rect = self._nswindow.contentRectForFrameRect_(window_frame)
488        return cocoapy.foundation.NSMouseInRect(point, rect, False)
489
490    def set_mouse_platform_visible(self, platform_visible=None):
491        # When the platform_visible argument is supplied with a boolean, then this
492        # method simply sets whether or not the platform mouse cursor is visible.
493        if platform_visible is not None:
494            if platform_visible:
495                SystemCursor.unhide()
496            else:
497                SystemCursor.hide()
498        # But if it has been called without an argument, it turns into
499        # a completely different function.  Now we are trying to figure out
500        # whether or not the mouse *should* be visible, and if so, what it should
501        # look like.
502        else:
503            # If we are in mouse exclusive mode, then hide the mouse cursor.
504            if self._is_mouse_exclusive:
505                SystemCursor.hide()
506            # If we aren't inside the window, then always show the mouse
507            # and make sure that it is the default cursor.
508            elif not self._mouse_in_content_rect():
509                NSCursor.arrowCursor().set()
510                SystemCursor.unhide()
511            # If we are in the window, then what we do depends on both
512            # the current pyglet-set visibility setting for the mouse and
513            # the type of the mouse cursor.  If the cursor has been hidden
514            # in the window with set_mouse_visible() then don't show it.
515            elif not self._mouse_visible:
516                SystemCursor.hide()
517            # If the mouse is set as a system-defined cursor, then we
518            # need to set the cursor and show the mouse.
519            # *** FIX ME ***
520            elif isinstance(self._mouse_cursor, CocoaMouseCursor):
521                self._mouse_cursor.set()
522                SystemCursor.unhide()
523            # If the mouse cursor is OpenGL drawable, then it we need to hide
524            # the system mouse cursor, so that the cursor can draw itself.
525            elif self._mouse_cursor.gl_drawable:
526                SystemCursor.hide()
527            # Otherwise, show the default cursor.
528            else:
529                NSCursor.arrowCursor().set()
530                SystemCursor.unhide()
531
532    def get_system_mouse_cursor(self, name):
533        # It would make a lot more sense for most of this code to be
534        # inside the CocoaMouseCursor class, but all of the CURSOR_xxx
535        # constants are defined as properties of BaseWindow.
536        if name == self.CURSOR_DEFAULT:
537            return DefaultMouseCursor()
538        cursors = {
539            self.CURSOR_CROSSHAIR:       'crosshairCursor',
540            self.CURSOR_HAND:            'pointingHandCursor',
541            self.CURSOR_HELP:            'arrowCursor',
542            self.CURSOR_NO:              'operationNotAllowedCursor',  # Mac OS 10.6
543            self.CURSOR_SIZE:            'arrowCursor',
544            self.CURSOR_SIZE_UP:         'resizeUpCursor',
545            self.CURSOR_SIZE_UP_RIGHT:   'arrowCursor',
546            self.CURSOR_SIZE_RIGHT:      'resizeRightCursor',
547            self.CURSOR_SIZE_DOWN_RIGHT: 'arrowCursor',
548            self.CURSOR_SIZE_DOWN:       'resizeDownCursor',
549            self.CURSOR_SIZE_DOWN_LEFT:  'arrowCursor',
550            self.CURSOR_SIZE_LEFT:       'resizeLeftCursor',
551            self.CURSOR_SIZE_UP_LEFT:    'arrowCursor',
552            self.CURSOR_SIZE_UP_DOWN:    'resizeUpDownCursor',
553            self.CURSOR_SIZE_LEFT_RIGHT: 'resizeLeftRightCursor',
554            self.CURSOR_TEXT:            'IBeamCursor',
555            self.CURSOR_WAIT:            'arrowCursor',  # No wristwatch cursor in Cocoa
556            self.CURSOR_WAIT_ARROW:      'arrowCursor',  # No wristwatch cursor in Cocoa
557            }
558        if name not in cursors:
559            raise RuntimeError('Unknown cursor name "%s"' % name)
560        return CocoaMouseCursor(cursors[name])
561
562    def set_mouse_position(self, x, y, absolute=False):
563        if absolute:
564            # If absolute, then x, y is given in global display coordinates
565            # which sets (0,0) at top left corner of main display.  It is possible
566            # to warp the mouse position to a point inside of another display.
567            quartz.CGWarpMouseCursorPosition(CGPoint(x,y))
568        else:
569            # Window-relative coordinates: (x, y) are given in window coords
570            # with (0,0) at bottom-left corner of window and y up.  We find
571            # which display the window is in and then convert x, y into local
572            # display coords where (0,0) is now top-left of display and y down.
573            screenInfo = self._nswindow.screen().deviceDescription()
574            displayID = screenInfo.objectForKey_(cocoapy.get_NSString('NSScreenNumber'))
575            displayID = displayID.intValue()
576            displayBounds = quartz.CGDisplayBounds(displayID)
577            frame = self._nswindow.frame()
578            windowOrigin = frame.origin
579            x += windowOrigin.x
580            y = displayBounds.size.height - windowOrigin.y - y
581            quartz.CGDisplayMoveCursorToPoint(displayID, cocoapy.NSPoint(x, y))
582
583    def set_exclusive_mouse(self, exclusive=True):
584        self._is_mouse_exclusive = exclusive
585        if exclusive:
586            # Skip the next motion event, which would return a large delta.
587            self._mouse_ignore_motion = True
588            # Move mouse to center of window.
589            frame = self._nswindow.frame()
590            width, height = frame.size.width, frame.size.height
591            self.set_mouse_position(width/2, height/2)
592            quartz.CGAssociateMouseAndMouseCursorPosition(False)
593        else:
594            quartz.CGAssociateMouseAndMouseCursorPosition(True)
595
596        # Update visibility of mouse cursor.
597        self.set_mouse_platform_visible()
598
599    def set_exclusive_keyboard(self, exclusive=True):
600        # http://developer.apple.com/mac/library/technotes/tn2002/tn2062.html
601        # http://developer.apple.com/library/mac/#technotes/KioskMode/
602
603        # BUG: System keys like F9 or command-tab are disabled, however
604        # pyglet also does not receive key press events for them.
605
606        # This flag is queried by window delegate to determine whether
607        # the quit menu item is active.
608        self._is_keyboard_exclusive = exclusive
609
610        if exclusive:
611            # "Be nice! Don't disable force-quit!"
612            #          -- Patrick Swayze, Road House (1989)
613            options = cocoapy.NSApplicationPresentationHideDock | \
614                      cocoapy.NSApplicationPresentationHideMenuBar | \
615                      cocoapy.NSApplicationPresentationDisableProcessSwitching | \
616                      cocoapy.NSApplicationPresentationDisableHideApplication
617        else:
618            options = cocoapy.NSApplicationPresentationDefault
619
620        NSApp = NSApplication.sharedApplication()
621        NSApp.setPresentationOptions_(options)
622
623
624__all__ = ["CocoaWindow"]
625