1# ---------------------------------------------------------------------------- 2# pyglet 3# Copyright (c) 2006-2008 Alex Holkner 4# Copyright (c) 2008-2020 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 36"""Windowing and user-interface events. 37 38This module allows applications to create and display windows with an 39OpenGL context. Windows can be created with a variety of border styles 40or set fullscreen. 41 42You can register event handlers for keyboard, mouse and window events. 43For games and kiosks you can also restrict the input to your windows, 44for example disabling users from switching away from the application 45with certain key combinations or capturing and hiding the mouse. 46 47Getting started 48--------------- 49 50Call the Window constructor to create a new window:: 51 52 from pyglet.window import Window 53 win = Window(width=640, height=480) 54 55Attach your own event handlers:: 56 57 @win.event 58 def on_key_press(symbol, modifiers): 59 # ... handle this event ... 60 61Place drawing code for the window within the `Window.on_draw` event handler:: 62 63 @win.event 64 def on_draw(): 65 # ... drawing code ... 66 67Call `pyglet.app.run` to enter the main event loop (by default, this 68returns when all open windows are closed):: 69 70 from pyglet import app 71 app.run() 72 73Creating a game window 74---------------------- 75 76Use :py:meth:`~pyglet.window.Window.set_exclusive_mouse` to hide the mouse 77cursor and receive relative mouse movement events. Specify ``fullscreen=True`` 78as a keyword argument to the :py:class:`~pyglet.window.Window` constructor to 79render to the entire screen rather than opening a window:: 80 81 win = Window(fullscreen=True) 82 win.set_exclusive_mouse() 83 84Working with multiple screens 85----------------------------- 86 87By default, fullscreen windows are opened on the primary display (typically 88set by the user in their operating system settings). You can retrieve a list 89of attached screens and select one manually if you prefer. This is useful for 90opening a fullscreen window on each screen:: 91 92 display = pyglet.canvas.get_display() 93 screens = display.get_screens() 94 windows = [] 95 for screen in screens: 96 windows.append(window.Window(fullscreen=True, screen=screen)) 97 98Specifying a screen has no effect if the window is not fullscreen. 99 100Specifying the OpenGL context properties 101---------------------------------------- 102 103Each window has its own context which is created when the window is created. 104You can specify the properties of the context before it is created 105by creating a "template" configuration:: 106 107 from pyglet import gl 108 # Create template config 109 config = gl.Config() 110 config.stencil_size = 8 111 config.aux_buffers = 4 112 # Create a window using this config 113 win = window.Window(config=config) 114 115To determine if a given configuration is supported, query the screen (see 116above, "Working with multiple screens"):: 117 118 configs = screen.get_matching_configs(config) 119 if not configs: 120 # ... config is not supported 121 else: 122 win = window.Window(config=configs[0]) 123 124""" 125 126import sys 127import math 128 129import pyglet 130import pyglet.window.key 131import pyglet.window.event 132 133from pyglet import gl 134from pyglet.event import EventDispatcher 135from pyglet.window import key 136from pyglet.util import with_metaclass 137 138 139_is_pyglet_doc_run = hasattr(sys, "is_pyglet_doc_run") and sys.is_pyglet_doc_run 140 141 142class WindowException(Exception): 143 """The root exception for all window-related errors.""" 144 pass 145 146 147class NoSuchDisplayException(WindowException): 148 """An exception indicating the requested display is not available.""" 149 pass 150 151 152class NoSuchConfigException(WindowException): 153 """An exception indicating the requested configuration is not 154 available.""" 155 pass 156 157 158class NoSuchScreenModeException(WindowException): 159 """An exception indicating the requested screen resolution could not be 160 met.""" 161 pass 162 163 164class MouseCursorException(WindowException): 165 """The root exception for all mouse cursor-related errors.""" 166 pass 167 168 169class MouseCursor: 170 """An abstract mouse cursor.""" 171 172 #: Indicates if the cursor is drawn using OpenGL. This is True 173 #: for all mouse cursors except system cursors. 174 drawable = True 175 176 def draw(self, x, y): 177 """Abstract render method. 178 179 The cursor should be drawn with the "hot" spot at the given 180 coordinates. The projection is set to the pyglet default (i.e., 181 orthographic in window-space), however no other aspects of the 182 state can be assumed. 183 184 :Parameters: 185 `x` : int 186 X coordinate of the mouse pointer's hot spot. 187 `y` : int 188 Y coordinate of the mouse pointer's hot spot. 189 190 """ 191 raise NotImplementedError('abstract') 192 193 194class DefaultMouseCursor(MouseCursor): 195 """The default mouse cursor #sed by the operating system.""" 196 drawable = False 197 198 199class ImageMouseCursor(MouseCursor): 200 """A user-defined mouse cursor created from an image. 201 202 Use this class to create your own mouse cursors and assign them 203 to windows. There are no constraints on the image size or format. 204 """ 205 drawable = True 206 207 def __init__(self, image, hot_x=0, hot_y=0): 208 """Create a mouse cursor from an image. 209 210 :Parameters: 211 `image` : `pyglet.image.AbstractImage` 212 Image to use for the mouse cursor. It must have a 213 valid ``texture`` attribute. 214 `hot_x` : int 215 X coordinate of the "hot" spot in the image relative to the 216 image's anchor. 217 `hot_y` : int 218 Y coordinate of the "hot" spot in the image, relative to the 219 image's anchor. 220 """ 221 self.texture = image.get_texture() 222 self.hot_x = hot_x 223 self.hot_y = hot_y 224 225 def draw(self, x, y): 226 gl.glPushAttrib(gl.GL_ENABLE_BIT | gl.GL_CURRENT_BIT) 227 gl.glColor4f(1, 1, 1, 1) 228 gl.glEnable(gl.GL_BLEND) 229 gl.glBlendFunc(gl.GL_SRC_ALPHA, gl.GL_ONE_MINUS_SRC_ALPHA) 230 self.texture.blit(x - self.hot_x, y - self.hot_y, 0) 231 gl.glPopAttrib() 232 233 234class Projection: 235 """Abstract OpenGL projection.""" 236 237 def set(self, window_width, window_height, viewport_width, viewport_height): 238 """Set the OpenGL projection 239 240 Using the passed in Window and viewport sizes, 241 set a desired orthographic or perspective projection. 242 243 :Parameters: 244 `window_width` : int 245 The Window width 246 `window_height` : int 247 The Window height 248 `viewport_width` : int 249 The Window internal viewport width. 250 `viewport_height` : int 251 The Window internal viewport height. 252 """ 253 raise NotImplementedError('abstract') 254 255 256class Projection2D(Projection): 257 """A 2D orthographic projection""" 258 259 def set(self, window_width, window_height, viewport_width, viewport_height): 260 gl.glViewport(0, 0, max(1, viewport_width), max(1, viewport_height)) 261 gl.glMatrixMode(gl.GL_PROJECTION) 262 gl.glLoadIdentity() 263 gl.glOrtho(0, max(1, window_width), 0, max(1, window_height), -1, 1) 264 gl.glMatrixMode(gl.GL_MODELVIEW) 265 266 267class Projection3D(Projection): 268 """A 3D perspective projection""" 269 270 def __init__(self, fov=60, znear=0.1, zfar=255): 271 """Create a 3D projection 272 273 :Parameters: 274 `fov` : float 275 The field of vision. Defaults to 60. 276 `znear` : float 277 The near clipping plane. Defaults to 0.1. 278 `zfar` : float 279 The far clipping plane. Defaults to 255. 280 """ 281 self.fov = fov 282 self.znear = znear 283 self.zfar = zfar 284 285 def set(self, window_width, window_height, viewport_width, viewport_height): 286 gl.glViewport(0, 0, max(1, viewport_width), max(1, viewport_height)) 287 gl.glMatrixMode(gl.GL_PROJECTION) 288 gl.glLoadIdentity() 289 290 # Pure GL implementation of gluPerspective: 291 aspect_ratio = float(window_width) / float(window_height) 292 f_width = math.tan(self.fov / 360.0 * math.pi ) * self.znear 293 f_height = f_width * aspect_ratio 294 gl.glFrustum(-f_height, f_height, -f_width, f_width, self.znear, self.zfar) 295 296 gl.glMatrixMode(gl.GL_MODELVIEW) 297 298 299def _PlatformEventHandler(data): 300 """Decorator for platform event handlers. 301 302 Apply giving the platform-specific data needed by the window to associate 303 the method with an event. See platform-specific subclasses of this 304 decorator for examples. 305 306 The following attributes are set on the function, which is returned 307 otherwise unchanged: 308 309 _platform_event 310 True 311 _platform_event_data 312 List of data applied to the function (permitting multiple decorators 313 on the same method). 314 """ 315 316 def _event_wrapper(f): 317 f._platform_event = True 318 if not hasattr(f, '_platform_event_data'): 319 f._platform_event_data = [] 320 f._platform_event_data.append(data) 321 return f 322 323 return _event_wrapper 324 325 326def _ViewEventHandler(f): 327 f._view = True 328 return f 329 330 331class _WindowMetaclass(type): 332 """Sets the _platform_event_names class variable on the window 333 subclass. 334 """ 335 336 def __init__(cls, name, bases, dict): 337 cls._platform_event_names = set() 338 for base in bases: 339 if hasattr(base, '_platform_event_names'): 340 cls._platform_event_names.update(base._platform_event_names) 341 for name, func in dict.items(): 342 if hasattr(func, '_platform_event'): 343 cls._platform_event_names.add(name) 344 super(_WindowMetaclass, cls).__init__(name, bases, dict) 345 346 347class BaseWindow(with_metaclass(_WindowMetaclass, EventDispatcher)): 348 """Platform-independent application window. 349 350 A window is a "heavyweight" object occupying operating system resources. 351 The "client" or "content" area of a window is filled entirely with 352 an OpenGL viewport. Applications have no access to operating system 353 widgets or controls; all rendering must be done via OpenGL. 354 355 Windows may appear as floating regions or can be set to fill an entire 356 screen (fullscreen). When floating, windows may appear borderless or 357 decorated with a platform-specific frame (including, for example, the 358 title bar, minimize and close buttons, resize handles, and so on). 359 360 While it is possible to set the location of a window, it is recommended 361 that applications allow the platform to place it according to local 362 conventions. This will ensure it is not obscured by other windows, 363 and appears on an appropriate screen for the user. 364 365 To render into a window, you must first call `switch_to`, to make 366 it the current OpenGL context. If you use only one window in the 367 application, there is no need to do this. 368 """ 369 370 # Filled in by metaclass with the names of all methods on this (sub)class 371 # that are platform event handlers. 372 _platform_event_names = set() 373 374 #: The default window style. 375 WINDOW_STYLE_DEFAULT = None 376 #: The window style for pop-up dialogs. 377 WINDOW_STYLE_DIALOG = 'dialog' 378 #: The window style for tool windows. 379 WINDOW_STYLE_TOOL = 'tool' 380 #: A window style without any decoration. 381 WINDOW_STYLE_BORDERLESS = 'borderless' 382 383 #: The default mouse cursor. 384 CURSOR_DEFAULT = None 385 #: A crosshair mouse cursor. 386 CURSOR_CROSSHAIR = 'crosshair' 387 #: A pointing hand mouse cursor. 388 CURSOR_HAND = 'hand' 389 #: A "help" mouse cursor; typically a question mark and an arrow. 390 CURSOR_HELP = 'help' 391 #: A mouse cursor indicating that the selected operation is not permitted. 392 CURSOR_NO = 'no' 393 #: A mouse cursor indicating the element can be resized. 394 CURSOR_SIZE = 'size' 395 #: A mouse cursor indicating the element can be resized from the top 396 #: border. 397 CURSOR_SIZE_UP = 'size_up' 398 #: A mouse cursor indicating the element can be resized from the 399 #: upper-right corner. 400 CURSOR_SIZE_UP_RIGHT = 'size_up_right' 401 #: A mouse cursor indicating the element can be resized from the right 402 #: border. 403 CURSOR_SIZE_RIGHT = 'size_right' 404 #: A mouse cursor indicating the element can be resized from the lower-right 405 #: corner. 406 CURSOR_SIZE_DOWN_RIGHT = 'size_down_right' 407 #: A mouse cursor indicating the element can be resized from the bottom 408 #: border. 409 CURSOR_SIZE_DOWN = 'size_down' 410 #: A mouse cursor indicating the element can be resized from the lower-left 411 #: corner. 412 CURSOR_SIZE_DOWN_LEFT = 'size_down_left' 413 #: A mouse cursor indicating the element can be resized from the left 414 #: border. 415 CURSOR_SIZE_LEFT = 'size_left' 416 #: A mouse cursor indicating the element can be resized from the upper-left 417 #: corner. 418 CURSOR_SIZE_UP_LEFT = 'size_up_left' 419 #: A mouse cursor indicating the element can be resized vertically. 420 CURSOR_SIZE_UP_DOWN = 'size_up_down' 421 #: A mouse cursor indicating the element can be resized horizontally. 422 CURSOR_SIZE_LEFT_RIGHT = 'size_left_right' 423 #: A text input mouse cursor (I-beam). 424 CURSOR_TEXT = 'text' 425 #: A "wait" mouse cursor; typically an hourglass or watch. 426 CURSOR_WAIT = 'wait' 427 #: The "wait" mouse cursor combined with an arrow. 428 CURSOR_WAIT_ARROW = 'wait_arrow' 429 430 #: True if the user has attempted to close the window. 431 #: 432 #: :deprecated: Windows are closed immediately by the default 433 #: :py:meth:`~pyglet.window.Window.on_close` handler when `pyglet.app.event_loop` is being 434 #: used. 435 has_exit = False 436 437 #: Window display contents validity. The :py:mod:`pyglet.app` event loop 438 #: examines every window each iteration and only dispatches the :py:meth:`~pyglet.window.Window.on_draw` 439 #: event to windows that have `invalid` set. By default, windows always 440 #: have `invalid` set to ``True``. 441 #: 442 #: You can prevent redundant redraws by setting this variable to ``False`` 443 #: in the window's :py:meth:`~pyglet.window.Window.on_draw` handler, and setting it to True again in 444 #: response to any events that actually do require a window contents 445 #: update. 446 #: 447 #: :type: bool 448 #: .. versionadded:: 1.1 449 invalid = True 450 451 #: Legacy invalidation flag introduced in pyglet 1.2: set by all event 452 #: dispatches that go to non-empty handlers. The default 1.2 event loop 453 #: will therefore redraw after any handled event or scheduled function. 454 _legacy_invalid = True 455 456 # Instance variables accessible only via properties 457 458 _width = None 459 _height = None 460 _caption = None 461 _resizable = False 462 _style = WINDOW_STYLE_DEFAULT 463 _fullscreen = False 464 _visible = False 465 _vsync = False 466 _screen = None 467 _config = None 468 _context = None 469 _projection = Projection2D() 470 471 # Used to restore window size and position after fullscreen 472 _windowed_size = None 473 _windowed_location = None 474 475 # Subclasses should update these after relevant events 476 _mouse_cursor = DefaultMouseCursor() 477 _mouse_x = 0 478 _mouse_y = 0 479 _mouse_visible = True 480 _mouse_exclusive = False 481 _mouse_in_window = False 482 483 _event_queue = None 484 _enable_event_queue = True # overridden by EventLoop. 485 _allow_dispatch_event = False # controlled by dispatch_events stack frame 486 487 # Class attributes 488 489 _default_width = 640 490 _default_height = 480 491 492 def __init__(self, 493 width=None, 494 height=None, 495 caption=None, 496 resizable=False, 497 style=WINDOW_STYLE_DEFAULT, 498 fullscreen=False, 499 visible=True, 500 vsync=True, 501 display=None, 502 screen=None, 503 config=None, 504 context=None, 505 mode=None): 506 """Create a window. 507 508 All parameters are optional, and reasonable defaults are assumed 509 where they are not specified. 510 511 The `display`, `screen`, `config` and `context` parameters form 512 a hierarchy of control: there is no need to specify more than 513 one of these. For example, if you specify `screen` the `display` 514 will be inferred, and a default `config` and `context` will be 515 created. 516 517 `config` is a special case; it can be a template created by the 518 user specifying the attributes desired, or it can be a complete 519 `config` as returned from `Screen.get_matching_configs` or similar. 520 521 The context will be active as soon as the window is created, as if 522 `switch_to` was just called. 523 524 :Parameters: 525 `width` : int 526 Width of the window, in pixels. Defaults to 640, or the 527 screen width if `fullscreen` is True. 528 `height` : int 529 Height of the window, in pixels. Defaults to 480, or the 530 screen height if `fullscreen` is True. 531 `caption` : str or unicode 532 Initial caption (title) of the window. Defaults to 533 ``sys.argv[0]``. 534 `resizable` : bool 535 If True, the window will be resizable. Defaults to False. 536 `style` : int 537 One of the ``WINDOW_STYLE_*`` constants specifying the 538 border style of the window. 539 `fullscreen` : bool 540 If True, the window will cover the entire screen rather 541 than floating. Defaults to False. 542 `visible` : bool 543 Determines if the window is visible immediately after 544 creation. Defaults to True. Set this to False if you 545 would like to change attributes of the window before 546 having it appear to the user. 547 `vsync` : bool 548 If True, buffer flips are synchronised to the primary screen's 549 vertical retrace, eliminating flicker. 550 `display` : `Display` 551 The display device to use. Useful only under X11. 552 `screen` : `Screen` 553 The screen to use, if in fullscreen. 554 `config` : `pyglet.gl.Config` 555 Either a template from which to create a complete config, 556 or a complete config. 557 `context` : `pyglet.gl.Context` 558 The context to attach to this window. The context must 559 not already be attached to another window. 560 `mode` : `ScreenMode` 561 The screen will be switched to this mode if `fullscreen` is 562 True. If None, an appropriate mode is selected to accomodate 563 `width` and `height.` 564 565 """ 566 EventDispatcher.__init__(self) 567 self._event_queue = [] 568 569 if not display: 570 display = pyglet.canvas.get_display() 571 572 if not screen: 573 screen = display.get_default_screen() 574 575 if not config: 576 for template_config in [gl.Config(double_buffer=True, depth_size=24), 577 gl.Config(double_buffer=True, depth_size=16), 578 None]: 579 try: 580 config = screen.get_best_config(template_config) 581 break 582 except NoSuchConfigException: 583 pass 584 if not config: 585 raise NoSuchConfigException('No standard config is available.') 586 587 if not config.is_complete(): 588 config = screen.get_best_config(config) 589 590 if not context: 591 context = config.create_context(gl.current_context) 592 593 # Set these in reverse order to above, to ensure we get user preference 594 self._context = context 595 self._config = self._context.config 596 # XXX deprecate config's being screen-specific 597 if hasattr(self._config, 'screen'): 598 self._screen = self._config.screen 599 else: 600 self._screen = screen 601 self._display = self._screen.display 602 603 if fullscreen: 604 if width is None and height is None: 605 self._windowed_size = self._default_width, self._default_height 606 width, height = self._set_fullscreen_mode(mode, width, height) 607 if not self._windowed_size: 608 self._windowed_size = width, height 609 else: 610 if width is None: 611 width = self._default_width 612 if height is None: 613 height = self._default_height 614 615 self._width = width 616 self._height = height 617 self._resizable = resizable 618 self._fullscreen = fullscreen 619 self._style = style 620 if pyglet.options['vsync'] is not None: 621 self._vsync = pyglet.options['vsync'] 622 else: 623 self._vsync = vsync 624 625 if caption is None: 626 caption = sys.argv[0] 627 628 self._caption = caption 629 630 from pyglet import app 631 app.windows.add(self) 632 self._create() 633 634 self.switch_to() 635 if visible: 636 self.set_visible(True) 637 self.activate() 638 639 def __del__(self): 640 # Always try to clean up the window when it is dereferenced. 641 # Makes sure there are no dangling pointers or memory leaks. 642 # If the window is already closed, pass silently. 643 try: 644 self.close() 645 except: # XXX Avoid a NoneType error if already closed. 646 pass 647 648 def __repr__(self): 649 return '%s(width=%d, height=%d)' % (self.__class__.__name__, self.width, self.height) 650 651 def _create(self): 652 raise NotImplementedError('abstract') 653 654 def _recreate(self, changes): 655 """Recreate the window with current attributes. 656 657 :Parameters: 658 `changes` : list of str 659 List of attribute names that were changed since the last 660 `_create` or `_recreate`. For example, ``['fullscreen']`` 661 is given if the window is to be toggled to or from fullscreen. 662 """ 663 raise NotImplementedError('abstract') 664 665 def flip(self): 666 """Swap the OpenGL front and back buffers. 667 668 Call this method on a double-buffered window to update the 669 visible display with the back buffer. The contents of the back buffer 670 is undefined after this operation. 671 672 Windows are double-buffered by default. This method is called 673 automatically by `EventLoop` after the :py:meth:`~pyglet.window.Window.on_draw` event. 674 """ 675 raise NotImplementedError('abstract') 676 677 def switch_to(self): 678 """Make this window the current OpenGL rendering context. 679 680 Only one OpenGL context can be active at a time. This method sets 681 the current window's context to be current. You should use this 682 method in preference to `pyglet.gl.Context.set_current`, as it may 683 perform additional initialisation functions. 684 """ 685 raise NotImplementedError('abstract') 686 687 def set_fullscreen(self, fullscreen=True, screen=None, mode=None, 688 width=None, height=None): 689 """Toggle to or from fullscreen. 690 691 After toggling fullscreen, the GL context should have retained its 692 state and objects, however the buffers will need to be cleared and 693 redrawn. 694 695 If `width` and `height` are specified and `fullscreen` is True, the 696 screen may be switched to a different resolution that most closely 697 matches the given size. If the resolution doesn't match exactly, 698 a higher resolution is selected and the window will be centered 699 within a black border covering the rest of the screen. 700 701 :Parameters: 702 `fullscreen` : bool 703 True if the window should be made fullscreen, False if it 704 should be windowed. 705 `screen` : Screen 706 If not None and fullscreen is True, the window is moved to the 707 given screen. The screen must belong to the same display as 708 the window. 709 `mode` : `ScreenMode` 710 The screen will be switched to the given mode. The mode must 711 have been obtained by enumerating `Screen.get_modes`. If 712 None, an appropriate mode will be selected from the given 713 `width` and `height`. 714 `width` : int 715 Optional width of the window. If unspecified, defaults to the 716 previous window size when windowed, or the screen size if 717 fullscreen. 718 719 .. versionadded:: 1.2 720 `height` : int 721 Optional height of the window. If unspecified, defaults to 722 the previous window size when windowed, or the screen size if 723 fullscreen. 724 725 .. versionadded:: 1.2 726 """ 727 if (fullscreen == self._fullscreen and 728 (screen is None or screen is self._screen) and 729 (width is None or width == self._width) and 730 (height is None or height == self._height)): 731 return 732 733 if not self._fullscreen: 734 # Save windowed size 735 self._windowed_size = self.get_size() 736 self._windowed_location = self.get_location() 737 738 if fullscreen and screen is not None: 739 assert screen.display is self.display 740 self._screen = screen 741 742 self._fullscreen = fullscreen 743 if self._fullscreen: 744 self._width, self._height = self._set_fullscreen_mode(mode, width, height) 745 else: 746 self.screen.restore_mode() 747 748 self._width, self._height = self._windowed_size 749 if width is not None: 750 self._width = width 751 if height is not None: 752 self._height = height 753 754 self._recreate(['fullscreen']) 755 756 if not self._fullscreen and self._windowed_location: 757 # Restore windowed location. 758 self.set_location(*self._windowed_location) 759 760 def _set_fullscreen_mode(self, mode, width, height): 761 if mode is not None: 762 self.screen.set_mode(mode) 763 if width is None: 764 width = self.screen.width 765 if height is None: 766 height = self.screen.height 767 elif width is not None or height is not None: 768 if width is None: 769 width = 0 770 if height is None: 771 height = 0 772 mode = self.screen.get_closest_mode(width, height) 773 if mode is not None: 774 self.screen.set_mode(mode) 775 elif self.screen.get_modes(): 776 # Only raise exception if mode switching is at all possible. 777 raise NoSuchScreenModeException('No mode matching %dx%d' % (width, height)) 778 else: 779 width = self.screen.width 780 height = self.screen.height 781 return width, height 782 783 def on_resize(self, width, height): 784 """A default resize event handler. 785 786 This default handler updates the GL viewport to cover the entire 787 window and sets the ``GL_PROJECTION`` matrix to be orthogonal in 788 window space. The bottom-left corner is (0, 0) and the top-right 789 corner is the width and height of the window in pixels. 790 791 Override this event handler with your own to create another 792 projection, for example in perspective. 793 """ 794 viewport_width, viewport_height = self.get_framebuffer_size() 795 self._projection.set(width, height, viewport_width, viewport_height) 796 797 def on_close(self): 798 """Default on_close handler.""" 799 self.has_exit = True 800 from pyglet import app 801 if app.event_loop.is_running: 802 self.close() 803 804 def on_key_press(self, symbol, modifiers): 805 """Default on_key_press handler.""" 806 if symbol == key.ESCAPE and not (modifiers & ~(key.MOD_NUMLOCK | 807 key.MOD_CAPSLOCK | 808 key.MOD_SCROLLLOCK)): 809 self.dispatch_event('on_close') 810 811 def close(self): 812 """Close the window. 813 814 After closing the window, the GL context will be invalid. The 815 window instance cannot be reused once closed (see also `set_visible`). 816 817 The `pyglet.app.EventLoop.on_window_close` event is dispatched on 818 `pyglet.app.event_loop` when this method is called. 819 """ 820 from pyglet import app 821 if not self._context: 822 return 823 app.windows.remove(self) 824 self._context.destroy() 825 self._config = None 826 self._context = None 827 if app.event_loop: 828 app.event_loop.dispatch_event('on_window_close', self) 829 self._event_queue = [] 830 831 def draw_mouse_cursor(self): 832 """Draw the custom mouse cursor. 833 834 If the current mouse cursor has ``drawable`` set, this method 835 is called before the buffers are flipped to render it. 836 837 This method always leaves the ``GL_MODELVIEW`` matrix as current, 838 regardless of what it was set to previously. No other GL state 839 is affected. 840 841 There is little need to override this method; instead, subclass 842 :py:class:`MouseCursor` and provide your own 843 :py:meth:`~MouseCursor.draw` method. 844 """ 845 # Draw mouse cursor if set and visible. 846 # XXX leaves state in modelview regardless of starting state 847 if (self._mouse_cursor.drawable and 848 self._mouse_visible and 849 self._mouse_in_window): 850 gl.glMatrixMode(gl.GL_PROJECTION) 851 gl.glPushMatrix() 852 gl.glLoadIdentity() 853 gl.glOrtho(0, self.width, 0, self.height, -1, 1) 854 855 gl.glMatrixMode(gl.GL_MODELVIEW) 856 gl.glPushMatrix() 857 gl.glLoadIdentity() 858 859 self._mouse_cursor.draw(self._mouse_x, self._mouse_y) 860 861 gl.glMatrixMode(gl.GL_PROJECTION) 862 gl.glPopMatrix() 863 864 gl.glMatrixMode(gl.GL_MODELVIEW) 865 gl.glPopMatrix() 866 867 # These properties provide read-only access to instance variables. 868 @property 869 def caption(self): 870 """The window caption (title). Read-only. 871 872 :type: str 873 """ 874 return self._caption 875 876 @property 877 def resizeable(self): 878 """True if the window is resizable. Read-only. 879 880 :type: bool 881 """ 882 return self._resizable 883 884 @property 885 def style(self): 886 """The window style; one of the ``WINDOW_STYLE_*`` constants. 887 Read-only. 888 889 :type: int 890 """ 891 return self._style 892 893 @property 894 def fullscreen(self): 895 """True if the window is currently fullscreen. Read-only. 896 897 :type: bool 898 """ 899 return self._fullscreen 900 901 @property 902 def visible(self): 903 """True if the window is currently visible. Read-only. 904 905 :type: bool 906 """ 907 return self._visible 908 909 @property 910 def vsync(self): 911 """True if buffer flips are synchronised to the screen's vertical 912 retrace. Read-only. 913 914 :type: bool 915 """ 916 return self._vsync 917 918 @property 919 def display(self): 920 """The display this window belongs to. Read-only. 921 922 :type: :py:class:`Display` 923 """ 924 return self._display 925 926 @property 927 def screen(self): 928 """The screen this window is fullscreen in. Read-only. 929 930 :type: :py:class:`Screen` 931 """ 932 return self._screen 933 934 @property 935 def config(self): 936 """A GL config describing the context of this window. Read-only. 937 938 :type: :py:class:`pyglet.gl.Config` 939 """ 940 return self._config 941 942 @property 943 def context(self): 944 """The OpenGL context attached to this window. Read-only. 945 946 :type: :py:class:`pyglet.gl.Context` 947 """ 948 return self._context 949 950 # These are the only properties that can be set 951 @property 952 def width(self): 953 """The width of the window, in pixels. Read-write. 954 955 :type: int 956 """ 957 return self.get_size()[0] 958 959 @width.setter 960 def width(self, new_width): 961 self.set_size(new_width, self.height) 962 963 @property 964 def height(self): 965 """The height of the window, in pixels. Read-write. 966 967 :type: int 968 """ 969 return self.get_size()[1] 970 971 @height.setter 972 def height(self, new_height): 973 self.set_size(self.width, new_height) 974 975 @property 976 def projection(self): 977 """The OpenGL window projection. Read-write. 978 979 The default window projection is orthographic (2D), but can 980 be changed to a 3D or custom projection. Custom projections 981 should subclass :py:class:`pyglet.window.Projection`. There 982 are two default projection classes are also provided, which 983 are :py:class:`pyglet.window.Projection3D` and 984 :py:class:`pyglet.window.Projection3D`. 985 986 :type: :py:class:`pyglet.window.Projection` 987 """ 988 return self._projection 989 990 @projection.setter 991 def projection(self, projection): 992 assert isinstance(projection, Projection) 993 projection.set(self._width, self._height, *self.get_framebuffer_size()) 994 self._projection = projection 995 996 def set_caption(self, caption): 997 """Set the window's caption. 998 999 The caption appears in the titlebar of the window, if it has one, 1000 and in the taskbar on Windows and many X11 window managers. 1001 1002 :Parameters: 1003 `caption` : str or unicode 1004 The caption to set. 1005 1006 """ 1007 raise NotImplementedError('abstract') 1008 1009 def set_minimum_size(self, width, height): 1010 """Set the minimum size of the window. 1011 1012 Once set, the user will not be able to resize the window smaller 1013 than the given dimensions. There is no way to remove the 1014 minimum size constraint on a window (but you could set it to 0,0). 1015 1016 The behaviour is undefined if the minimum size is set larger than 1017 the current size of the window. 1018 1019 The window size does not include the border or title bar. 1020 1021 :Parameters: 1022 `width` : int 1023 Minimum width of the window, in pixels. 1024 `height` : int 1025 Minimum height of the window, in pixels. 1026 1027 """ 1028 raise NotImplementedError('abstract') 1029 1030 def set_maximum_size(self, width, height): 1031 """Set the maximum size of the window. 1032 1033 Once set, the user will not be able to resize the window larger 1034 than the given dimensions. There is no way to remove the 1035 maximum size constraint on a window (but you could set it to a large 1036 value). 1037 1038 The behaviour is undefined if the maximum size is set smaller than 1039 the current size of the window. 1040 1041 The window size does not include the border or title bar. 1042 1043 :Parameters: 1044 `width` : int 1045 Maximum width of the window, in pixels. 1046 `height` : int 1047 Maximum height of the window, in pixels. 1048 1049 """ 1050 raise NotImplementedError('abstract') 1051 1052 def set_size(self, width, height): 1053 """Resize the window. 1054 1055 The behaviour is undefined if the window is not resizable, or if 1056 it is currently fullscreen. 1057 1058 The window size does not include the border or title bar. 1059 1060 :Parameters: 1061 `width` : int 1062 New width of the window, in pixels. 1063 `height` : int 1064 New height of the window, in pixels. 1065 1066 """ 1067 raise NotImplementedError('abstract') 1068 1069 def get_pixel_ratio(self): 1070 """Return the framebuffer/window size ratio. 1071 1072 Some platforms and/or window systems support subpixel scaling, 1073 making the framebuffer size larger than the window size. 1074 Retina screens on OS X and Gnome on Linux are some examples. 1075 1076 On a Retina systems the returned ratio would usually be 2.0 as a 1077 window of size 500 x 500 would have a frambuffer of 1000 x 1000. 1078 Fractional values between 1.0 and 2.0, as well as values above 1079 2.0 may also be encountered. 1080 1081 :rtype: float 1082 :return: The framebuffer/window size ratio 1083 """ 1084 return self.get_framebuffer_size()[0] / self.width 1085 1086 def get_size(self): 1087 """Return the current size of the window. 1088 1089 The window size does not include the border or title bar. 1090 1091 :rtype: (int, int) 1092 :return: The width and height of the window, in pixels. 1093 """ 1094 raise NotImplementedError('abstract') 1095 1096 def get_framebuffer_size(self): 1097 """Return the size in actual pixels of the Window framebuffer. 1098 1099 When using HiDPI screens, the size of the Window's framebuffer 1100 can be higher than that of the Window size requested. If you 1101 are performing operations that require knowing the actual number 1102 of pixels in the window, this method should be used instead of 1103 :py:func:`Window.get_size()`. For example, setting the Window 1104 projection or setting the glViewport size. 1105 1106 :rtype: (int, int) 1107 :return: The width and height of the Window viewport, in pixels. 1108 """ 1109 return self.get_size() 1110 1111 # :deprecated: Use Window.get_framebuffer_size 1112 get_viewport_size = get_framebuffer_size 1113 1114 def set_location(self, x, y): 1115 """Set the position of the window. 1116 1117 :Parameters: 1118 `x` : int 1119 Distance of the left edge of the window from the left edge 1120 of the virtual desktop, in pixels. 1121 `y` : int 1122 Distance of the top edge of the window from the top edge of 1123 the virtual desktop, in pixels. 1124 1125 """ 1126 raise NotImplementedError('abstract') 1127 1128 def get_location(self): 1129 """Return the current position of the window. 1130 1131 :rtype: (int, int) 1132 :return: The distances of the left and top edges from their respective 1133 edges on the virtual desktop, in pixels. 1134 """ 1135 raise NotImplementedError('abstract') 1136 1137 def activate(self): 1138 """Attempt to restore keyboard focus to the window. 1139 1140 Depending on the window manager or operating system, this may not 1141 be successful. For example, on Windows XP an application is not 1142 allowed to "steal" focus from another application. Instead, the 1143 window's taskbar icon will flash, indicating it requires attention. 1144 """ 1145 raise NotImplementedError('abstract') 1146 1147 def set_visible(self, visible=True): 1148 """Show or hide the window. 1149 1150 :Parameters: 1151 `visible` : bool 1152 If True, the window will be shown; otherwise it will be 1153 hidden. 1154 1155 """ 1156 raise NotImplementedError('abstract') 1157 1158 def minimize(self): 1159 """Minimize the window. 1160 """ 1161 raise NotImplementedError('abstract') 1162 1163 def maximize(self): 1164 """Maximize the window. 1165 1166 The behaviour of this method is somewhat dependent on the user's 1167 display setup. On a multi-monitor system, the window may maximize 1168 to either a single screen or the entire virtual desktop. 1169 """ 1170 raise NotImplementedError('abstract') 1171 1172 def set_vsync(self, vsync): 1173 """Enable or disable vertical sync control. 1174 1175 When enabled, this option ensures flips from the back to the front 1176 buffer are performed only during the vertical retrace period of the 1177 primary display. This can prevent "tearing" or flickering when 1178 the buffer is updated in the middle of a video scan. 1179 1180 Note that LCD monitors have an analogous time in which they are not 1181 reading from the video buffer; while it does not correspond to 1182 a vertical retrace it has the same effect. 1183 1184 Also note that with multi-monitor systems the secondary monitor 1185 cannot be synchronised to, so tearing and flicker cannot be avoided 1186 when the window is positioned outside of the primary display. 1187 1188 :Parameters: 1189 `vsync` : bool 1190 If True, vsync is enabled, otherwise it is disabled. 1191 1192 """ 1193 raise NotImplementedError('abstract') 1194 1195 def set_mouse_visible(self, visible=True): 1196 """Show or hide the mouse cursor. 1197 1198 The mouse cursor will only be hidden while it is positioned within 1199 this window. Mouse events will still be processed as usual. 1200 1201 :Parameters: 1202 `visible` : bool 1203 If True, the mouse cursor will be visible, otherwise it 1204 will be hidden. 1205 1206 """ 1207 self._mouse_visible = visible 1208 self.set_mouse_platform_visible() 1209 1210 def set_mouse_platform_visible(self, platform_visible=None): 1211 """Set the platform-drawn mouse cursor visibility. This is called 1212 automatically after changing the mouse cursor or exclusive mode. 1213 1214 Applications should not normally need to call this method, see 1215 `set_mouse_visible` instead. 1216 1217 :Parameters: 1218 `platform_visible` : bool or None 1219 If None, sets platform visibility to the required visibility 1220 for the current exclusive mode and cursor type. Otherwise, 1221 a bool value will override and force a visibility. 1222 1223 """ 1224 raise NotImplementedError() 1225 1226 def set_mouse_cursor(self, cursor=None): 1227 """Change the appearance of the mouse cursor. 1228 1229 The appearance of the mouse cursor is only changed while it is 1230 within this window. 1231 1232 :Parameters: 1233 `cursor` : `MouseCursor` 1234 The cursor to set, or None to restore the default cursor. 1235 1236 """ 1237 if cursor is None: 1238 cursor = DefaultMouseCursor() 1239 self._mouse_cursor = cursor 1240 self.set_mouse_platform_visible() 1241 1242 def set_exclusive_mouse(self, exclusive=True): 1243 """Hide the mouse cursor and direct all mouse events to this 1244 window. 1245 1246 When enabled, this feature prevents the mouse leaving the window. It 1247 is useful for certain styles of games that require complete control of 1248 the mouse. The position of the mouse as reported in subsequent events 1249 is meaningless when exclusive mouse is enabled; you should only use 1250 the relative motion parameters ``dx`` and ``dy``. 1251 1252 :Parameters: 1253 `exclusive` : bool 1254 If True, exclusive mouse is enabled, otherwise it is disabled. 1255 1256 """ 1257 raise NotImplementedError('abstract') 1258 1259 def set_exclusive_keyboard(self, exclusive=True): 1260 """Prevent the user from switching away from this window using 1261 keyboard accelerators. 1262 1263 When enabled, this feature disables certain operating-system specific 1264 key combinations such as Alt+Tab (Command+Tab on OS X). This can be 1265 useful in certain kiosk applications, it should be avoided in general 1266 applications or games. 1267 1268 :Parameters: 1269 `exclusive` : bool 1270 If True, exclusive keyboard is enabled, otherwise it is 1271 disabled. 1272 1273 """ 1274 raise NotImplementedError('abstract') 1275 1276 def get_system_mouse_cursor(self, name): 1277 """Obtain a system mouse cursor. 1278 1279 Use `set_mouse_cursor` to make the cursor returned by this method 1280 active. The names accepted by this method are the ``CURSOR_*`` 1281 constants defined on this class. 1282 1283 :Parameters: 1284 `name` : str 1285 Name describing the mouse cursor to return. For example, 1286 ``CURSOR_WAIT``, ``CURSOR_HELP``, etc. 1287 1288 :rtype: `MouseCursor` 1289 :return: A mouse cursor which can be used with `set_mouse_cursor`. 1290 """ 1291 raise NotImplementedError() 1292 1293 def set_icon(self, *images): 1294 """Set the window icon. 1295 1296 If multiple images are provided, one with an appropriate size 1297 will be selected (if the correct size is not provided, the image 1298 will be scaled). 1299 1300 Useful sizes to provide are 16x16, 32x32, 64x64 (Mac only) and 1301 128x128 (Mac only). 1302 1303 :Parameters: 1304 `images` : sequence of `pyglet.image.AbstractImage` 1305 List of images to use for the window icon. 1306 1307 """ 1308 pass 1309 1310 def clear(self): 1311 """Clear the window. 1312 1313 This is a convenience method for clearing the color and depth 1314 buffer. The window must be the active context (see `switch_to`). 1315 """ 1316 gl.glClear(gl.GL_COLOR_BUFFER_BIT | gl.GL_DEPTH_BUFFER_BIT) 1317 1318 def dispatch_event(self, *args): 1319 if not self._enable_event_queue or self._allow_dispatch_event: 1320 if EventDispatcher.dispatch_event(self, *args) != False: 1321 self._legacy_invalid = True 1322 else: 1323 self._event_queue.append(args) 1324 1325 def dispatch_events(self): 1326 """Poll the operating system event queue for new events and call 1327 attached event handlers. 1328 1329 This method is provided for legacy applications targeting pyglet 1.0, 1330 and advanced applications that must integrate their event loop 1331 into another framework. 1332 1333 Typical applications should use `pyglet.app.run`. 1334 """ 1335 raise NotImplementedError('abstract') 1336 1337 # If documenting, show the event methods. Otherwise, leave them out 1338 # as they are not really methods. 1339 if _is_pyglet_doc_run: 1340 def on_key_press(self, symbol, modifiers): 1341 """A key on the keyboard was pressed (and held down). 1342 1343 In pyglet 1.0 the default handler sets `has_exit` to ``True`` if 1344 the ``ESC`` key is pressed. 1345 1346 In pyglet 1.1 the default handler dispatches the :py:meth:`~pyglet.window.Window.on_close` 1347 event if the ``ESC`` key is pressed. 1348 1349 :Parameters: 1350 `symbol` : int 1351 The key symbol pressed. 1352 `modifiers` : int 1353 Bitwise combination of the key modifiers active. 1354 1355 :event: 1356 """ 1357 1358 def on_key_release(self, symbol, modifiers): 1359 """A key on the keyboard was released. 1360 1361 :Parameters: 1362 `symbol` : int 1363 The key symbol pressed. 1364 `modifiers` : int 1365 Bitwise combination of the key modifiers active. 1366 1367 :event: 1368 """ 1369 1370 def on_text(self, text): 1371 """The user input some text. 1372 1373 Typically this is called after :py:meth:`~pyglet.window.Window.on_key_press` and before 1374 :py:meth:`~pyglet.window.Window.on_key_release`, but may also be called multiple times if the key 1375 is held down (key repeating); or called without key presses if 1376 another input method was used (e.g., a pen input). 1377 1378 You should always use this method for interpreting text, as the 1379 key symbols often have complex mappings to their unicode 1380 representation which this event takes care of. 1381 1382 :Parameters: 1383 `text` : unicode 1384 The text entered by the user. 1385 1386 :event: 1387 """ 1388 1389 def on_text_motion(self, motion): 1390 """The user moved the text input cursor. 1391 1392 Typically this is called after :py:meth:`~pyglet.window.Window.on_key_press` and before 1393 :py:meth:`~pyglet.window.Window.on_key_release`, but may also be called multiple times if the key 1394 is help down (key repeating). 1395 1396 You should always use this method for moving the text input cursor 1397 (caret), as different platforms have different default keyboard 1398 mappings, and key repeats are handled correctly. 1399 1400 The values that `motion` can take are defined in 1401 :py:mod:`pyglet.window.key`: 1402 1403 * MOTION_UP 1404 * MOTION_RIGHT 1405 * MOTION_DOWN 1406 * MOTION_LEFT 1407 * MOTION_NEXT_WORD 1408 * MOTION_PREVIOUS_WORD 1409 * MOTION_BEGINNING_OF_LINE 1410 * MOTION_END_OF_LINE 1411 * MOTION_NEXT_PAGE 1412 * MOTION_PREVIOUS_PAGE 1413 * MOTION_BEGINNING_OF_FILE 1414 * MOTION_END_OF_FILE 1415 * MOTION_BACKSPACE 1416 * MOTION_DELETE 1417 1418 :Parameters: 1419 `motion` : int 1420 The direction of motion; see remarks. 1421 1422 :event: 1423 """ 1424 1425 def on_text_motion_select(self, motion): 1426 """The user moved the text input cursor while extending the 1427 selection. 1428 1429 Typically this is called after :py:meth:`~pyglet.window.Window.on_key_press` and before 1430 :py:meth:`~pyglet.window.Window.on_key_release`, but may also be called multiple times if the key 1431 is help down (key repeating). 1432 1433 You should always use this method for responding to text selection 1434 events rather than the raw :py:meth:`~pyglet.window.Window.on_key_press`, as different platforms 1435 have different default keyboard mappings, and key repeats are 1436 handled correctly. 1437 1438 The values that `motion` can take are defined in :py:mod:`pyglet.window.key`: 1439 1440 * MOTION_UP 1441 * MOTION_RIGHT 1442 * MOTION_DOWN 1443 * MOTION_LEFT 1444 * MOTION_NEXT_WORD 1445 * MOTION_PREVIOUS_WORD 1446 * MOTION_BEGINNING_OF_LINE 1447 * MOTION_END_OF_LINE 1448 * MOTION_NEXT_PAGE 1449 * MOTION_PREVIOUS_PAGE 1450 * MOTION_BEGINNING_OF_FILE 1451 * MOTION_END_OF_FILE 1452 1453 :Parameters: 1454 `motion` : int 1455 The direction of selection motion; see remarks. 1456 1457 :event: 1458 """ 1459 1460 def on_mouse_motion(self, x, y, dx, dy): 1461 """The mouse was moved with no buttons held down. 1462 1463 :Parameters: 1464 `x` : int 1465 Distance in pixels from the left edge of the window. 1466 `y` : int 1467 Distance in pixels from the bottom edge of the window. 1468 `dx` : int 1469 Relative X position from the previous mouse position. 1470 `dy` : int 1471 Relative Y position from the previous mouse position. 1472 1473 :event: 1474 """ 1475 1476 def on_mouse_drag(self, x, y, dx, dy, buttons, modifiers): 1477 """The mouse was moved with one or more mouse buttons pressed. 1478 1479 This event will continue to be fired even if the mouse leaves 1480 the window, so long as the drag buttons are continuously held down. 1481 1482 :Parameters: 1483 `x` : int 1484 Distance in pixels from the left edge of the window. 1485 `y` : int 1486 Distance in pixels from the bottom edge of the window. 1487 `dx` : int 1488 Relative X position from the previous mouse position. 1489 `dy` : int 1490 Relative Y position from the previous mouse position. 1491 `buttons` : int 1492 Bitwise combination of the mouse buttons currently pressed. 1493 `modifiers` : int 1494 Bitwise combination of any keyboard modifiers currently 1495 active. 1496 1497 :event: 1498 """ 1499 1500 def on_mouse_press(self, x, y, button, modifiers): 1501 """A mouse button was pressed (and held down). 1502 1503 :Parameters: 1504 `x` : int 1505 Distance in pixels from the left edge of the window. 1506 `y` : int 1507 Distance in pixels from the bottom edge of the window. 1508 `button` : int 1509 The mouse button that was pressed. 1510 `modifiers` : int 1511 Bitwise combination of any keyboard modifiers currently 1512 active. 1513 1514 :event: 1515 """ 1516 1517 def on_mouse_release(self, x, y, button, modifiers): 1518 """A mouse button was released. 1519 1520 :Parameters: 1521 `x` : int 1522 Distance in pixels from the left edge of the window. 1523 `y` : int 1524 Distance in pixels from the bottom edge of the window. 1525 `button` : int 1526 The mouse button that was released. 1527 `modifiers` : int 1528 Bitwise combination of any keyboard modifiers currently 1529 active. 1530 1531 :event: 1532 """ 1533 1534 def on_mouse_scroll(self, x, y, scroll_x, scroll_y): 1535 """The mouse wheel was scrolled. 1536 1537 Note that most mice have only a vertical scroll wheel, so 1538 `scroll_x` is usually 0. An exception to this is the Apple Mighty 1539 Mouse, which has a mouse ball in place of the wheel which allows 1540 both `scroll_x` and `scroll_y` movement. 1541 1542 :Parameters: 1543 `x` : int 1544 Distance in pixels from the left edge of the window. 1545 `y` : int 1546 Distance in pixels from the bottom edge of the window. 1547 `scroll_x` : int 1548 Number of "clicks" towards the right (left if negative). 1549 `scroll_y` : int 1550 Number of "clicks" upwards (downwards if negative). 1551 1552 :event: 1553 """ 1554 1555 def on_close(self): 1556 """The user attempted to close the window. 1557 1558 This event can be triggered by clicking on the "X" control box in 1559 the window title bar, or by some other platform-dependent manner. 1560 1561 The default handler sets `has_exit` to ``True``. In pyglet 1.1, if 1562 `pyglet.app.event_loop` is being used, `close` is also called, 1563 closing the window immediately. 1564 1565 :event: 1566 """ 1567 1568 def on_mouse_enter(self, x, y): 1569 """The mouse was moved into the window. 1570 1571 This event will not be triggered if the mouse is currently being 1572 dragged. 1573 1574 :Parameters: 1575 `x` : int 1576 Distance in pixels from the left edge of the window. 1577 `y` : int 1578 Distance in pixels from the bottom edge of the window. 1579 1580 :event: 1581 """ 1582 1583 def on_mouse_leave(self, x, y): 1584 """The mouse was moved outside of the window. 1585 1586 This event will not be triggered if the mouse is currently being 1587 dragged. Note that the coordinates of the mouse pointer will be 1588 outside of the window rectangle. 1589 1590 :Parameters: 1591 `x` : int 1592 Distance in pixels from the left edge of the window. 1593 `y` : int 1594 Distance in pixels from the bottom edge of the window. 1595 1596 :event: 1597 """ 1598 1599 def on_expose(self): 1600 """A portion of the window needs to be redrawn. 1601 1602 This event is triggered when the window first appears, and any time 1603 the contents of the window is invalidated due to another window 1604 obscuring it. 1605 1606 There is no way to determine which portion of the window needs 1607 redrawing. Note that the use of this method is becoming 1608 increasingly uncommon, as newer window managers composite windows 1609 automatically and keep a backing store of the window contents. 1610 1611 :event: 1612 """ 1613 1614 def on_resize(self, width, height): 1615 """The window was resized. 1616 1617 The window will have the GL context when this event is dispatched; 1618 there is no need to call `switch_to` in this handler. 1619 1620 :Parameters: 1621 `width` : int 1622 The new width of the window, in pixels. 1623 `height` : int 1624 The new height of the window, in pixels. 1625 1626 :event: 1627 """ 1628 1629 def on_move(self, x, y): 1630 """The window was moved. 1631 1632 :Parameters: 1633 `x` : int 1634 Distance from the left edge of the screen to the left edge 1635 of the window. 1636 `y` : int 1637 Distance from the top edge of the screen to the top edge of 1638 the window. Note that this is one of few methods in pyglet 1639 which use a Y-down coordinate system. 1640 1641 :event: 1642 """ 1643 1644 def on_activate(self): 1645 """The window was activated. 1646 1647 This event can be triggered by clicking on the title bar, bringing 1648 it to the foreground; or by some platform-specific method. 1649 1650 When a window is "active" it has the keyboard focus. 1651 1652 :event: 1653 """ 1654 1655 def on_deactivate(self): 1656 """The window was deactivated. 1657 1658 This event can be triggered by clicking on another application 1659 window. When a window is deactivated it no longer has the 1660 keyboard focus. 1661 1662 :event: 1663 """ 1664 1665 def on_show(self): 1666 """The window was shown. 1667 1668 This event is triggered when a window is restored after being 1669 minimised, or after being displayed for the first time. 1670 1671 :event: 1672 """ 1673 1674 def on_hide(self): 1675 """The window was hidden. 1676 1677 This event is triggered when a window is minimised or (on Mac OS X) 1678 hidden by the user. 1679 1680 :event: 1681 """ 1682 1683 def on_context_lost(self): 1684 """The window's GL context was lost. 1685 1686 When the context is lost no more GL methods can be called until it 1687 is recreated. This is a rare event, triggered perhaps by the user 1688 switching to an incompatible video mode. When it occurs, an 1689 application will need to reload all objects (display lists, texture 1690 objects, shaders) as well as restore the GL state. 1691 1692 :event: 1693 """ 1694 1695 def on_context_state_lost(self): 1696 """The state of the window's GL context was lost. 1697 1698 pyglet may sometimes need to recreate the window's GL context if 1699 the window is moved to another video device, or between fullscreen 1700 or windowed mode. In this case it will try to share the objects 1701 (display lists, texture objects, shaders) between the old and new 1702 contexts. If this is possible, only the current state of the GL 1703 context is lost, and the application should simply restore state. 1704 1705 :event: 1706 """ 1707 1708 def on_draw(self): 1709 """The window contents must be redrawn. 1710 1711 The `EventLoop` will dispatch this event when the window 1712 should be redrawn. This will happen during idle time after 1713 any window events and after any scheduled functions were called. 1714 1715 The window will already have the GL context, so there is no 1716 need to call `switch_to`. The window's `flip` method will 1717 be called after this event, so your event handler should not. 1718 1719 You should make no assumptions about the window contents when 1720 this event is triggered; a resize or expose event may have 1721 invalidated the framebuffer since the last time it was drawn. 1722 1723 .. versionadded:: 1.1 1724 1725 :event: 1726 """ 1727 1728 1729BaseWindow.register_event_type('on_key_press') 1730BaseWindow.register_event_type('on_key_release') 1731BaseWindow.register_event_type('on_text') 1732BaseWindow.register_event_type('on_text_motion') 1733BaseWindow.register_event_type('on_text_motion_select') 1734BaseWindow.register_event_type('on_mouse_motion') 1735BaseWindow.register_event_type('on_mouse_drag') 1736BaseWindow.register_event_type('on_mouse_press') 1737BaseWindow.register_event_type('on_mouse_release') 1738BaseWindow.register_event_type('on_mouse_scroll') 1739BaseWindow.register_event_type('on_mouse_enter') 1740BaseWindow.register_event_type('on_mouse_leave') 1741BaseWindow.register_event_type('on_close') 1742BaseWindow.register_event_type('on_expose') 1743BaseWindow.register_event_type('on_resize') 1744BaseWindow.register_event_type('on_move') 1745BaseWindow.register_event_type('on_activate') 1746BaseWindow.register_event_type('on_deactivate') 1747BaseWindow.register_event_type('on_show') 1748BaseWindow.register_event_type('on_hide') 1749BaseWindow.register_event_type('on_context_lost') 1750BaseWindow.register_event_type('on_context_state_lost') 1751BaseWindow.register_event_type('on_draw') 1752 1753 1754class FPSDisplay: 1755 """Display of a window's framerate. 1756 1757 This is a convenience class to aid in profiling and debugging. Typical 1758 usage is to create an `FPSDisplay` for each window, and draw the display 1759 at the end of the windows' :py:meth:`~pyglet.window.Window.on_draw` event handler:: 1760 1761 window = pyglet.window.Window() 1762 fps_display = FPSDisplay(window) 1763 1764 @window.event 1765 def on_draw(): 1766 # ... perform ordinary window drawing operations ... 1767 1768 fps_display.draw() 1769 1770 The style and position of the display can be modified via the :py:func:`~pyglet.text.Label` 1771 attribute. Different text can be substituted by overriding the 1772 `set_fps` method. The display can be set to update more or less often 1773 by setting the `update_period` attribute. Note: setting the `update_period` 1774 to a value smaller than your Window refresh rate will cause inaccurate readings. 1775 1776 :Ivariables: 1777 `label` : Label 1778 The text label displaying the framerate. 1779 1780 """ 1781 1782 #: Time in seconds between updates. 1783 #: 1784 #: :type: float 1785 update_period = 0.25 1786 1787 def __init__(self, window): 1788 from time import time 1789 from pyglet.text import Label 1790 self.label = Label('', x=10, y=10, 1791 font_size=24, bold=True, 1792 color=(127, 127, 127, 127)) 1793 1794 self.window = window 1795 self._window_flip = window.flip 1796 window.flip = self._hook_flip 1797 1798 self.time = 0.0 1799 self.last_time = time() 1800 self.count = 0 1801 1802 def update(self): 1803 """Records a new data point at the current time. This method 1804 is called automatically when the window buffer is flipped. 1805 """ 1806 from time import time 1807 t = time() 1808 self.count += 1 1809 self.time += t - self.last_time 1810 self.last_time = t 1811 1812 if self.time >= self.update_period: 1813 self.set_fps(self.count / self.time) 1814 self.time %= self.update_period 1815 self.count = 0 1816 1817 def set_fps(self, fps): 1818 """Set the label text for the given FPS estimation. 1819 1820 Called by `update` every `update_period` seconds. 1821 1822 :Parameters: 1823 `fps` : float 1824 Estimated framerate of the window. 1825 1826 """ 1827 self.label.text = '%.2f' % fps 1828 1829 def draw(self): 1830 """Draw the label. 1831 1832 The OpenGL state is assumed to be at default values, except 1833 that the MODELVIEW and PROJECTION matrices are ignored. At 1834 the return of this method the matrix mode will be MODELVIEW. 1835 """ 1836 gl.glMatrixMode(gl.GL_MODELVIEW) 1837 gl.glPushMatrix() 1838 gl.glLoadIdentity() 1839 1840 gl.glMatrixMode(gl.GL_PROJECTION) 1841 gl.glPushMatrix() 1842 gl.glLoadIdentity() 1843 gl.glOrtho(0, self.window.width, 0, self.window.height, -1, 1) 1844 1845 self.label.draw() 1846 1847 gl.glPopMatrix() 1848 1849 gl.glMatrixMode(gl.GL_MODELVIEW) 1850 gl.glPopMatrix() 1851 1852 def _hook_flip(self): 1853 self.update() 1854 self._window_flip() 1855 1856 1857if _is_pyglet_doc_run: 1858 # We are building documentation 1859 Window = BaseWindow 1860 Window.__name__ = 'Window' 1861 del BaseWindow 1862else: 1863 # Try to determine which platform to use. 1864 if pyglet.compat_platform == 'darwin': 1865 from pyglet.window.cocoa import CocoaWindow as Window 1866 elif pyglet.compat_platform in ('win32', 'cygwin'): 1867 from pyglet.window.win32 import Win32Window as Window 1868 else: 1869 # XXX HACK around circ problem, should be fixed after removal of 1870 # shadow nonsense 1871 #pyglet.window = sys.modules[__name__] 1872 #import key, mouse 1873 1874 from pyglet.window.xlib import XlibWindow as Window 1875 1876# XXX remove 1877# Create shadow window. (trickery is for circular import) 1878if not _is_pyglet_doc_run: 1879 pyglet.window = sys.modules[__name__] 1880 gl._create_shadow_window() 1881