From f16807715c59f8afa6dba804be2a8b8754626efe Mon Sep 17 00:00:00 2001 From: shenjack <3695888@qq.com> Date: Thu, 23 Feb 2023 22:08:53 +0800 Subject: [PATCH] use new pyglet local MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit [build skip] docs try hard link 非得更新一下是吧 [build skip] --- README.md | 2 - docs/README-en.md | 2 - docs/src/404.md | 2 +- docs/src/README-en.md | 116 +++ docs/src/README.md | 110 +++ docs/src/SUMMARY.md | 3 + docs/src/contributors.md | 7 +- libs/pyglet/libs/darwin/cocoapy/runtime.py | 19 +- libs/pyglet/math.py | 2 +- libs/pyglet/media/codecs/base.py | 13 +- libs/pyglet/media/drivers/base.py | 36 +- .../media/drivers/directsound/adaptation.py | 10 +- .../pyglet/media/drivers/openal/adaptation.py | 7 +- libs/pyglet/media/drivers/pulse/adaptation.py | 17 +- .../media/drivers/xaudio2/adaptation.py | 10 +- libs/pyglet/window/__init__.py | 917 ++++++++++++------ 16 files changed, 903 insertions(+), 370 deletions(-) create mode 100644 docs/src/README-en.md create mode 120000 docs/src/README.md diff --git a/README.md b/README.md index 2ee4ce1..633cfa0 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,5 @@ # Difficult Rocket -[comment]: <> ([中文](./docs/README-cn.md) | English) - 中文 | [English](/docs/README-en.md) - [GitHub](https://github.com/shenjackyuanjie/Difficult-Rocket) diff --git a/docs/README-en.md b/docs/README-en.md index da02c08..3ba88d4 100644 --- a/docs/README-en.md +++ b/docs/README-en.md @@ -1,7 +1,5 @@ # Difficult Rocket -[comment]: <> (中文 | [English](https://github.com/shenjackyuanjie/Difficult-Rocket).) - [中文](../README.md) | English - [GitHub](https://github.com/shenjackyuanjie/Difficult-Rocket) diff --git a/docs/src/404.md b/docs/src/404.md index 66d2ce4..c1d985e 100644 --- a/docs/src/404.md +++ b/docs/src/404.md @@ -1,4 +1,4 @@ -# 嘿 看啥呢 +# 嘿 看啥呢 404! - 很明显,这里没有你想要的东西 diff --git a/docs/src/README-en.md b/docs/src/README-en.md new file mode 100644 index 0000000..3ba88d4 --- /dev/null +++ b/docs/src/README-en.md @@ -0,0 +1,116 @@ +# Difficult Rocket + +[中文](../README.md) | English + +- [GitHub](https://github.com/shenjackyuanjie/Difficult-Rocket) +- [gitee](https://gitee.com/shenjackyuanjie/Difficult-Rocket) +- [discord](https://discord.gg/kWzw2JrG6M) +- [kook](https://kook.top/sRPjFG) + +996.icu +[![Generic badge](https://img.shields.io/badge/SemVer-2.0.0-blue.svg)](https://Semver.org/) +[![Generic badge](https://img.shields.io/badge/Write_with_Python-3.8.10-blue.svg)](https://Python.org) +[![Generic badge](https://img.shields.io/badge/Write_with_Pyglet-2.0.4-blue.svg)](https://pyglet.org) +[![Generic badge](https://img.shields.io/badge/Python-_3.8_|_3.9_|_3.10_|_3.11_-blue.svg)](https://Python.org) + +## Version + +[![Generic badge](https://img.shields.io/badge/Release-0.7.1.1-blue.svg)](https://github.com/shenjackyuanjie/Difficult-Rocket/releases) +[![Generic badge](https://img.shields.io/badge/Pre_Release-0.7.1.1-blue.svg)](https://github.com/shenjackyuanjie/Difficult-Rocket/releases) +[![Generic badge](https://img.shields.io/badge/Devloping-0.7.2-blue.svg)](https://github.com/shenjackyuanjie/Difficult-Rocket/releases) + +## 中文README请移步 [这里](../README.md) + +> Difficult-rocket is a Simple Rocket liked game build with Python (in short: rocket simulator) + +## Advantage + +> Lighter than Vanilla SR + +## [Plan feature list](/docs/src/plan_features.md) + +- [microsoft TODO](https://to-do.microsoft.com/sharing?InvitationToken=Q6SN1kdtitK8cwFktFl71gSnsRMNmrH7CC7kHY_Tq6ReMRwHgInP4_q5ie2IwrHx8) + +## [Update logs](/docs/src/update_logs.md) + +## Environment (been tested / developed on) + +- `Develop platform 1 - Windows 10 x64 22H2` + - `Python 3.8.10` + - `pillow 9.3.0` + - `pyperclip 1.8.2` + - `pyglet 2.0` + - `psutil 5.9.4` + - `objprint 0.2.2` + - `rtoml 0.9.0` + - `xmltodict 0.13.0` + - `tomlkit 0.11.6` + - `AMD R5 5600X` + - `AMD RX 550 4G` + +## Required python modules + +- `rtoml` +- `tomlkit` +- `pyglet` (pre-installed V2.0.4 path:`./libs/pyglet`) +- `xmltodict` (pre-installed V0.12.0 path:`./libs/xmltodict`) +- `pyperclip` (pre-installed V1.8.2 path: `./libs/pyperclip`) +- `pillow` +- `defusedxml` +- `objprint` +- `psutil` + +## thanks to + +- Open Source Projects + - [pyglet](https://github.com/pyglet/pyglet): GUI and graphics + - `tomlkit` / `rtoml` toml parser + - `xmltodict`: translate data between xml and dict + - `pyperclip`: paste board! + - `rapier2d`: Phy simulate engine + +- Main contributors + - [@Rayawa](https://github.com/Rayawa) : check mistake in docs & some translates + - [@rouxiao-you](https://github.com/ruoxiao-you) : translate chinese to English + - [@Billchyi](https://github.com/Billchyi) : check mistake in docs + +## Other links + +## About License + +#### https://creativecommons.org/licenses/by-nc-sa/4.0/ + +#### Attribution-NonCommercial-ShareAlike 4.0 International (CC BY-NC-SA 4.0) + +This is a human-readable summary of (and not a substitute for) the license. Disclaimer. + +You are free to: + +Share — copy and redistribute the material in any medium or format + +Adapt — remix, transform, and build upon the material + +The licensor cannot revoke these freedoms as long as you follow the license terms. + +Under the following terms: + +Attribution — You must give appropriate credit, provide a link to the license, and indicate if changes were made. You +may do so in any reasonable manner, but not in any way + +that suggests the licensor endorses you or your use. + +NonCommercial — You may not use the material for commercial purposes. + +ShareAlike — If you remix, transform, or build upon the material, you must distribute your contributions under the same +license as the original. + +No additional restrictions — You may not apply legal terms or technological measures that legally restrict others from +doing anything the license permits. + +Notices: + +You do not have to comply with the license for elements of the material in the public domain or where your use is +permitted by an applicable exception or limitation. + +No warranties are given. The license may not give you all the permissions necessary for your intended use. For example, +other rights such as publicity, privacy, or moral rights may limit how you use the material. diff --git a/docs/src/README.md b/docs/src/README.md new file mode 120000 index 0000000..633cfa0 --- /dev/null +++ b/docs/src/README.md @@ -0,0 +1,110 @@ +# Difficult Rocket + +中文 | [English](/docs/README-en.md) + +- [GitHub](https://github.com/shenjackyuanjie/Difficult-Rocket) +- [gitee](https://gitee.com/shenjackyuanjie/Difficult-Rocket) +- [discord](https://discord.gg/kWzw2JrG6M) +- [kook](https://kook.top/sRPjFG) + +996.icu +[![Generic badge](https://img.shields.io/badge/SemVer-2.0.0-blue.svg)](https://Semver.org/) +[![Generic badge](https://img.shields.io/badge/编写于_Python_版本-3.8.10-blue.svg)](https://Python.org) +[![Generic badge](https://img.shields.io/badge/编写于_Pyglet_版本-2.0.4-blue.svg)](https://pyglet.org) +[![Generic badge](https://img.shields.io/badge/Python-_3.8_|_3.9_|_3.10_|_3.11_-blue.svg)](https://Python.org) + +## 版本 + +[![Generic badge](https://img.shields.io/badge/Release-0.7.1.1-blue.svg)](https://github.com/shenjackyuanjie/Difficult-Rocket/releases) +[![Generic badge](https://img.shields.io/badge/Pre_Release-0.7.1.1-blue.svg)](https://github.com/shenjackyuanjie/Difficult-Rocket/releases) +[![Generic badge](https://img.shields.io/badge/Devloping-0.7.2-blue.svg)](https://github.com/shenjackyuanjie/Difficult-Rocket/releases) + +## English README please look [here](./docs/README-en.md) + +> 这是一个用Python制作的类Simple Rocket游戏(简称:火箭模拟器) + +## 优势 + +> 相对于原版SR比较“轻量化” + +## [计划特性列表](/docs/plan_features) + +- [microsoft TODO](https://to-do.microsoft.com/sharing?InvitationToken=Q6SN1kdtitK8cwFktFl71gSnsRMNmrH7CC7kHY_Tq6ReMRwHgInP4_q5ie2IwrHx8) + +## [更新日志](/docs/update_logs.md) + +## 环境需求 (测试过的 / 开发平台) + +- `开发平台 1 - Windows 10 x64 22H2` + - `Python 3.8.10` + - `pillow 9.3.0` + - `pyperclip 1.8.2` + - `pyglet 2.0` + - `psutil 5.9.4` + - `objprint 0.2.2` + - `rtoml 0.9.0` + - `xmltodict 0.13.0` + - `tomlkit 0.11.6` + - `AMD R5 5600X` + - `AMD RX 550 4G` + +## 需要的Python模块 + +- `rtoml` +- `tomlkit` +- `pyglet` (已经内置 V2.0.4 路径:`./libs/pyglet`) +- `xmltodict` (已经内置 V0.12.0 路径:`./libs/xmltodict`) +- `pyperclip` (已经内置 V1.8.2 路径: `./libs/pyperclip`) +- `pillow` +- `defusedxml` +- `objprint` +- `psutil` + +## 感谢 + +- 开源项目 + - [pyglet](https://github.com/pyglet/pyglet) : GUI 和画面渲染 + - `tomlkit` / `rtoml` : toml 解析器 + - `xmltodict`: xml 与 dict 转换器 + - `pyperclip`: 剪贴板! + - `rapier2d`: 物理模拟引擎 + +- 主要贡献者 + - [@Rayawa](https://github.com/Rayawa) : 文档矫正 & 翻译部分 lang + - [@rouxiao-you](https://github.com/ruoxiao-you) : 翻译 lang + - [@Billchyi](https://github.com/Billchyi) : 文档矫正 + - [@MSDNicrosoft]() + +## 相关链接 + +## 关于分享协议 + +#### https://creativecommons.org/licenses/by-nc-sa/4.0/ + +#### 署名-非商业性使用-相同方式共享 4.0 国际 (CC BY-NC-SA 4.0) + +这是一份普通人可以理解的许可协议概要 (但不是替代) 。 免责声明. + +您可以自由地: + +共享 — 在任何媒介以任何形式复制、发行本作品 + +演绎 — 修改、转换或以本作品为基础进行创作 + +只要你遵守许可协议条款,许可人就无法收回你的这些权利。 + +惟须遵守下列条件: + +署名 — 您必须给出地当的署名,提供指向本许可协议的链接,同时标明是否(对原始作品)作了修改。您可以用任何合理的方式来署名,但是不得以任何方式暗示许可人为您或您的使用背书。 + +非商业性使用 — 您不得将本作品用于商业目的。 + +相同方式共享 — 如果您再混合、转换或者基于本作品进行创作,您必须基于与原先许可协议地同的许可协议 分发您贡献的作品。 + +没有附加限制 — 您不得适用法律术语或者 技术措施 从而限制其他人做许可协议允许的事情。 + +声明: + +您不必因为公共领域的作品要素而遵守许可协议,或者您的使用被可适用的 例外或限制所允许。 + +不提供担保。许可协议可能不会给与您意图使用的所必须的所有许可。例如,其他权利比如形象权、隐私权或人格权可能限制您如何使用作品。 diff --git a/docs/src/SUMMARY.md b/docs/src/SUMMARY.md index fc1bf29..b4871a3 100644 --- a/docs/src/SUMMARY.md +++ b/docs/src/SUMMARY.md @@ -1,5 +1,8 @@ # Summary +- [README](./README.md) +- [README-en](./README-en.md) + - [update logs](./update_logs.md) - [contributors](./contributors.md) diff --git a/docs/src/contributors.md b/docs/src/contributors.md index 26d9672..9e3e009 100644 --- a/docs/src/contributors.md +++ b/docs/src/contributors.md @@ -1,3 +1,8 @@ # 如何帮助 DR 开发 -aaaa \ No newline at end of file +## 需要准备的工具 + +1. Powershell 7+ +2. Python3.8 + +3. gcc / clang 编译器 +4. git diff --git a/libs/pyglet/libs/darwin/cocoapy/runtime.py b/libs/pyglet/libs/darwin/cocoapy/runtime.py index 99ba096..ade2f72 100644 --- a/libs/pyglet/libs/darwin/cocoapy/runtime.py +++ b/libs/pyglet/libs/darwin/cocoapy/runtime.py @@ -415,6 +415,7 @@ objc.sel_isEqual.argtypes = [c_void_p, c_void_p] objc.sel_registerName.restype = c_void_p objc.sel_registerName.argtypes = [c_char_p] + ###################################################################### # void *objc_autoreleasePoolPush(void) objc.objc_autoreleasePoolPush.restype = c_void_p @@ -644,8 +645,8 @@ def parse_type_encoding(encoding): def cfunctype_for_encoding(encoding): # Check if we've already created a CFUNCTYPE for this encoding. # If so, then return the cached CFUNCTYPE. - #if encoding in cfunctype_table: - # return cfunctype_table[encoding] + if encoding in cfunctype_table: + return cfunctype_table[encoding] # Otherwise, create a new CFUNCTYPE for the encoding. typecodes = {b'c': c_char, b'i': c_int, b's': c_short, b'l': c_long, b'q': c_longlong, @@ -668,7 +669,7 @@ def cfunctype_for_encoding(encoding): # Cache the new CFUNCTYPE in the cfunctype_table. # We do this mainly because it prevents the CFUNCTYPE # from being garbage-collected while we need it. - #cfunctype_table[encoding] = cfunctype + cfunctype_table[encoding] = cfunctype return cfunctype @@ -728,12 +729,12 @@ class ObjCMethod: # Note, need to map 'c' to c_byte rather than c_char, because otherwise # ctypes converts the value into a one-character string which is generally # not what we want at all, especially when the 'c' represents a bool var. - typecodes = {b'c': c_byte, b'i': c_int, b's': c_short, b'l': c_long, b'q': c_longlong, - b'C': c_ubyte, b'I': c_uint, b'S': c_ushort, b'L': c_ulong, b'Q': c_ulonglong, - b'f': c_float, b'd': c_double, b'B': c_bool, b'v': None, b'Vv': None, b'*': c_char_p, - b'@': c_void_p, b'#': c_void_p, b':': c_void_p, b'^v': c_void_p, b'?': c_void_p, - NSPointEncoding: NSPoint, NSSizeEncoding: NSSize, NSRectEncoding: NSRect, - NSRangeEncoding: NSRange, + typecodes = {b'c': c_byte, b'i': c_int, b's': c_short, b'l': c_long, b'q': c_longlong, + b'C': c_ubyte, b'I': c_uint, b'S': c_ushort, b'L': c_ulong, b'Q': c_ulonglong, + b'f': c_float, b'd': c_double, b'B': c_bool, b'v': None, b'Vv': None, b'*': c_char_p, + b'@': c_void_p, b'#': c_void_p, b':': c_void_p, b'^v': c_void_p, b'?': c_void_p, + NSPointEncoding: NSPoint, NSSizeEncoding: NSSize, NSRectEncoding: NSRect, + NSRangeEncoding: NSRange, PyObjectEncoding: py_object} cfunctype_table = {} diff --git a/libs/pyglet/math.py b/libs/pyglet/math.py index ead4414..6647808 100644 --- a/libs/pyglet/math.py +++ b/libs/pyglet/math.py @@ -861,7 +861,7 @@ class Mat4(tuple): def look_at(cls: type[Mat4T], position: Vec3, target: Vec3, up: Vec3): f = (target - position).normalize() u = up.normalize() - s = f.cross(u) + s = f.cross(u).normalize() u = s.cross(f) return cls([s.x, u.x, -f.x, 0.0, diff --git a/libs/pyglet/media/codecs/base.py b/libs/pyglet/media/codecs/base.py index d65b23a..652a8a8 100644 --- a/libs/pyglet/media/codecs/base.py +++ b/libs/pyglet/media/codecs/base.py @@ -135,17 +135,6 @@ class AudioData: self.duration -= num_bytes / audio_format.bytes_per_second self.timestamp += num_bytes / audio_format.bytes_per_second - def get_string_data(self): - """Return data as a bytestring. - - Returns: - bytes: Data as a (byte)string. - """ - if self.data is None: - return b'' - - return memoryview(self.data).tobytes()[:self.length] - class SourceInfo: """Source metadata information. @@ -409,7 +398,7 @@ class StaticSource(Source): audio_data = source.get_audio_data(buffer_size) if not audio_data: break - data.write(audio_data.get_string_data()) + data.write(audio_data.data) self._data = data.getvalue() self._duration = len(self._data) / self.audio_format.bytes_per_second diff --git a/libs/pyglet/media/drivers/base.py b/libs/pyglet/media/drivers/base.py index 3c99783..6ada69d 100644 --- a/libs/pyglet/media/drivers/base.py +++ b/libs/pyglet/media/drivers/base.py @@ -1,9 +1,8 @@ import math -import time import weakref + from abc import ABCMeta, abstractmethod -import pyglet from pyglet.util import with_metaclass @@ -185,36 +184,3 @@ class AbstractAudioDriver(with_metaclass(ABCMeta)): @abstractmethod def delete(self): pass - - -class MediaEvent: - """Representation of a media event. - - These events are used internally by some audio driver implementation to - communicate events to the :class:`~pyglet.media.player.Player`. - One example is the ``on_eos`` event. - - Args: - event (str): Event description. - timestamp (float): The time when this event happens. - *args: Any required positional argument to go along with this event. - """ - - __slots__ = 'event', 'timestamp', 'args' - - def __init__(self, event, timestamp=0, *args): - # Meaning of timestamp is dependent on context; and not seen by application. - self.event = event - self.timestamp = timestamp - self.args = args - - def sync_dispatch_to_player(self, player): - pyglet.app.platform_event_loop.post_event(player, self.event, *self.args) - time.sleep(0) - # TODO sync with media.dispatch_events - - def __repr__(self): - return f"MediaEvent({self.event}, {self.timestamp}, {self.args})" - - def __lt__(self, other): - return hash(self) < hash(other) diff --git a/libs/pyglet/media/drivers/directsound/adaptation.py b/libs/pyglet/media/drivers/directsound/adaptation.py index af53dab..61969e6 100644 --- a/libs/pyglet/media/drivers/directsound/adaptation.py +++ b/libs/pyglet/media/drivers/directsound/adaptation.py @@ -1,10 +1,11 @@ import math import ctypes +import pyglet.app from . import interface from pyglet.util import debug_print from pyglet.media.mediathreads import PlayerWorkerThread -from pyglet.media.drivers.base import AbstractAudioDriver, AbstractAudioPlayer, MediaEvent +from pyglet.media.drivers.base import AbstractAudioDriver, AbstractAudioPlayer from pyglet.media.drivers.listener import AbstractListener _debug = debug_print('debug_media') @@ -75,7 +76,7 @@ class DirectSoundAudioPlayer(AbstractAudioPlayer): self._play_cursor_ring = 0 self._write_cursor_ring = 0 - # List of (play_cursor, MediaEvent), in sort order + # List of play_cursor, in sort order self._events = [] # List of (cursor, timestamp), in sort order (cursor gives expiry @@ -157,9 +158,6 @@ class DirectSoundAudioPlayer(AbstractAudioPlayer): return (self._eos_cursor is not None and self._play_cursor > self._eos_cursor) - def _dispatch_new_event(self, event_name): - MediaEvent(event_name).sync_dispatch_to_player(self.player) - def _get_audiodata(self): if self._audiodata_buffer is None or self._audiodata_buffer.length == 0: self._get_new_audiodata() @@ -246,7 +244,7 @@ class DirectSoundAudioPlayer(AbstractAudioPlayer): if self._playing and self._has_underrun(): assert _debug('underrun, stopping') self.stop() - self._dispatch_new_event('on_eos') + pyglet.app.platform_event_loop.post_event(self.player, 'on_eos') def _get_write_size(self): self.update_play_cursor() diff --git a/libs/pyglet/media/drivers/openal/adaptation.py b/libs/pyglet/media/drivers/openal/adaptation.py index 0c70ce1..b093f67 100644 --- a/libs/pyglet/media/drivers/openal/adaptation.py +++ b/libs/pyglet/media/drivers/openal/adaptation.py @@ -1,8 +1,9 @@ import weakref +import pyglet.app from . import interface from pyglet.util import debug_print -from pyglet.media.drivers.base import AbstractAudioDriver, AbstractAudioPlayer, MediaEvent +from pyglet.media.drivers.base import AbstractAudioDriver, AbstractAudioPlayer from pyglet.media.mediathreads import PlayerWorkerThread from pyglet.media.drivers.listener import AbstractListener @@ -111,7 +112,7 @@ class OpenALAudioPlayer(AbstractAudioPlayer): # of underrun) self._underrun_timestamp = None - # List of (cursor, MediaEvent) + # List of cursor self._events = [] # Desired play state (True even if stopped due to underrun) @@ -280,7 +281,7 @@ class OpenALAudioPlayer(AbstractAudioPlayer): assert _debug('No audio data left') if self._has_underrun(): assert _debug('Underrun') - MediaEvent('on_eos').sync_dispatch_to_player(self.player) + pyglet.app.platform_event_loop.post_event(self.player, 'on_eos') def _queue_audio_data(self, audio_data, length): buf = self.alsource.get_buffer() diff --git a/libs/pyglet/media/drivers/pulse/adaptation.py b/libs/pyglet/media/drivers/pulse/adaptation.py index 5b85114..c438fa7 100644 --- a/libs/pyglet/media/drivers/pulse/adaptation.py +++ b/libs/pyglet/media/drivers/pulse/adaptation.py @@ -1,6 +1,7 @@ import weakref -from pyglet.media.drivers.base import AbstractAudioDriver, AbstractAudioPlayer, MediaEvent +import pyglet.app +from pyglet.media.drivers.base import AbstractAudioDriver, AbstractAudioPlayer from pyglet.media.drivers.listener import AbstractListener from pyglet.util import debug_print @@ -217,7 +218,7 @@ class PulseAudioPlayer(AbstractAudioPlayer): if self._has_audio_data(): self._write_to_stream() else: - self._add_event_at_write_index('on_eos') + self._events.append('on_eos') def _process_events(self): assert _debug('PulseAudioPlayer: Process events') @@ -235,13 +236,9 @@ class PulseAudioPlayer(AbstractAudioPlayer): assert _debug('PulseAudioPlayer: Dispatch events at index {}'.format(read_index)) while self._events and self._events[0][0] <= read_index: - _, event = self._events.pop(0) - assert _debug('PulseAudioPlayer: Dispatch event', event) - event._sync_dispatch_to_player(self.player) - - def _add_event_at_write_index(self, event_name): - assert _debug('PulseAudioPlayer: Add event at index {}'.format(self._write_index)) - self._events.append((self._write_index, MediaEvent(event_name))) + event_name = self._events.pop(0) + assert _debug('PulseAudioPlayer: Dispatch event', event_name) + pyglet.app.platform_event_loop.post_event(self.player, event_name) def delete(self): assert _debug('Delete PulseAudioPlayer') @@ -255,7 +252,7 @@ class PulseAudioPlayer(AbstractAudioPlayer): if driver.mainloop is None: assert _debug('PulseAudioDriver already deleted. ' - 'PulseAudioPlayer could not clean up properly.') + 'PulseAudioPlayer could not clean up properly.') return if self._time_sync_operation is not None: diff --git a/libs/pyglet/media/drivers/xaudio2/adaptation.py b/libs/pyglet/media/drivers/xaudio2/adaptation.py index 0287edf..53911a3 100644 --- a/libs/pyglet/media/drivers/xaudio2/adaptation.py +++ b/libs/pyglet/media/drivers/xaudio2/adaptation.py @@ -1,7 +1,7 @@ import math import pyglet -from pyglet.media.drivers.base import AbstractAudioDriver, AbstractAudioPlayer, MediaEvent +from pyglet.media.drivers.base import AbstractAudioDriver, AbstractAudioPlayer from pyglet.media.drivers.listener import AbstractListener from pyglet.util import debug_print from . import interface @@ -48,7 +48,7 @@ class XAudio2AudioPlayer(AbstractAudioPlayer): self._write_cursor = 0 self._play_cursor = 0 - # List of (play_cursor, MediaEvent), in sort order + # List of play_cursor, in sort order self._events = [] # List of (cursor, timestamp), in sort order (cursor gives expiry @@ -89,7 +89,6 @@ class XAudio2AudioPlayer(AbstractAudioPlayer): if not self._buffers: self._xa2_driver.return_voice(self._xa2_source_voice) - def play(self): assert _debug('XAudio2 play') @@ -168,7 +167,7 @@ class XAudio2AudioPlayer(AbstractAudioPlayer): if self.buffer_end_submitted: if buffers_queued == 0: self._xa2_source_voice.stop() - MediaEvent("on_eos").sync_dispatch_to_player(self.player) + pyglet.app.platform_event_loop.post_event(self.player, 'on_eos') else: current_buffers = [] while buffers_queued < self.max_buffer_count: @@ -206,9 +205,6 @@ class XAudio2AudioPlayer(AbstractAudioPlayer): self._dispatch_pending_events() - def _dispatch_new_event(self, event_name): - MediaEvent(event_name).sync_dispatch_to_player(self.player) - def _add_audiodata_events(self, audio_data): for event in audio_data.events: event_cursor = self._write_cursor + event.timestamp * self.source.audio_format.bytes_per_second diff --git a/libs/pyglet/window/__init__.py b/libs/pyglet/window/__init__.py index e7f8fa7..5bc5277 100644 --- a/libs/pyglet/window/__init__.py +++ b/libs/pyglet/window/__init__.py @@ -623,6 +623,80 @@ class BaseWindow(with_metaclass(_WindowMetaclass, EventDispatcher)): """ raise NotImplementedError('abstract') + # Public methods (sort alphabetically): + def activate(self): + """Attempt to restore keyboard focus to the window. + + Depending on the window manager or operating system, this may not + be successful. For example, on Windows XP an application is not + allowed to "steal" focus from another application. Instead, the + window's taskbar icon will flash, indicating it requires attention. + """ + raise NotImplementedError('abstract') + + @staticmethod + def clear(): + """Clear the window. + + This is a convenience method for clearing the color and depth + buffer. The window must be the active context (see `switch_to`). + """ + gl.glClear(gl.GL_COLOR_BUFFER_BIT | gl.GL_DEPTH_BUFFER_BIT) + + def close(self): + """Close the window. + + After closing the window, the GL context will be invalid. The + window instance cannot be reused once closed (see also `set_visible`). + + The `pyglet.app.EventLoop.on_window_close` event is dispatched on + `pyglet.app.event_loop` when this method is called. + """ + from pyglet import app + if not self._context: + return + app.windows.remove(self) + self._context.destroy() + self._config = None + self._context = None + if app.event_loop: + app.event_loop.dispatch_event('on_window_close', self) + self._event_queue = [] + + def dispatch_event(self, *args): + if not self._enable_event_queue or self._allow_dispatch_event: + super().dispatch_event(*args) + else: + self._event_queue.append(args) + + def dispatch_events(self): + """Poll the operating system event queue for new events and call + attached event handlers. + + This method is provided for legacy applications targeting pyglet 1.0, + and advanced applications that must integrate their event loop + into another framework. + + Typical applications should use `pyglet.app.run`. + """ + raise NotImplementedError('abstract') + + def draw_mouse_cursor(self): + """Draw the custom mouse cursor. + + If the current mouse cursor has ``drawable`` set, this method + is called before the buffers are flipped to render it. + + There is little need to override this method; instead, subclass + :py:class:`MouseCursor` and provide your own + :py:meth:`~MouseCursor.draw` method. + """ + # Draw mouse cursor if set and visible. + + if self._mouse_cursor.gl_drawable and self._mouse_visible and self._mouse_in_window: + # TODO: consider projection differences + self._mouse_cursor.draw(self._mouse_x, self._mouse_y) + def flip(self): """Swap the OpenGL front and back buffers. @@ -635,13 +709,124 @@ class BaseWindow(with_metaclass(_WindowMetaclass, EventDispatcher)): """ raise NotImplementedError('abstract') - def switch_to(self): - """Make this window the current OpenGL rendering context. + def get_framebuffer_size(self): + """Return the size in actual pixels of the Window framebuffer. + + When using HiDPI screens, the size of the Window's framebuffer + can be higher than that of the Window size requested. If you + are performing operations that require knowing the actual number + of pixels in the window, this method should be used instead of + :py:func:`Window.get_size()`. For example, setting the Window + projection or setting the glViewport size. + + :rtype: (int, int) + :return: The width and height of the Window's framebuffer, in pixels. + """ + return self.get_size() + + def get_location(self): + """Return the current position of the window. + + :rtype: (int, int) + :return: The distances of the left and top edges from their respective + edges on the virtual desktop, in pixels. + """ + raise NotImplementedError('abstract') + + def get_pixel_ratio(self): + """Return the framebuffer/window size ratio. + + Some platforms and/or window systems support subpixel scaling, + making the framebuffer size larger than the window size. + Retina screens on OS X and Gnome on Linux are some examples. + + On a Retina systems the returned ratio would usually be 2.0 as a + window of size 500 x 500 would have a framebuffer of 1000 x 1000. + Fractional values between 1.0 and 2.0, as well as values above + 2.0 may also be encountered. + + :rtype: float + :return: The framebuffer/window size ratio + """ + return self.get_framebuffer_size()[0] / self.width + + def get_size(self) -> Tuple[int, int]: + """Return the current size of the window. + + This does not include the windows' border or title bar. + + :rtype: (int, int) + :return: The width and height of the window, in pixels. + """ + return self._width, self._height + + def get_system_mouse_cursor(self, name): + """Obtain a system mouse cursor. + + Use `set_mouse_cursor` to make the cursor returned by this method + active. The names accepted by this method are the ``CURSOR_*`` + constants defined on this class. + + :Parameters: + `name` : str + Name describing the mouse cursor to return. For example, + ``CURSOR_WAIT``, ``CURSOR_HELP``, etc. + + :rtype: `MouseCursor` + :return: A mouse cursor which can be used with `set_mouse_cursor`. + """ + raise NotImplementedError() + + def minimize(self): + """Minimize the window. + """ + raise NotImplementedError('abstract') + + def maximize(self): + """Maximize the window. + + The behaviour of this method is somewhat dependent on the user's + display setup. On a multi-monitor system, the window may maximize + to either a single screen or the entire virtual desktop. + """ + raise NotImplementedError('abstract') + + def on_close(self): + """Default on_close handler.""" + self.has_exit = True + from pyglet import app + if app.event_loop.is_running: + self.close() + + def on_key_press(self, symbol, modifiers): + """Default on_key_press handler.""" + if symbol == key.ESCAPE and not (modifiers & ~(key.MOD_NUMLOCK | + key.MOD_CAPSLOCK | + key.MOD_SCROLLLOCK)): + self.dispatch_event('on_close') + + def on_resize(self, width, height): + """A default resize event handler. + + This default handler updates the GL viewport to cover the entire + window. The bottom-left corner is (0, 0) and the top-right + corner is the width and height of the window's framebuffer. + In addition, the projection matrix is set to an orghogonal + projection based on the same dimensions. + """ + gl.glViewport(0, 0, *self.get_framebuffer_size()) + self.projection = Mat4.orthogonal_projection(0, width, 0, height, -255, 255) + + def set_caption(self, caption): + """Set the window's caption. + + The caption appears in the titlebar of the window, if it has one, + and in the taskbar on Windows and many X11 window managers. + + :Parameters: + `caption` : str or unicode + The caption to set. - Only one OpenGL context can be active at a time. This method sets - the current window's context to be current. You should use this - method in preference to `pyglet.gl.Context.set_current`, as it may - perform additional initialisation functions. """ raise NotImplementedError('abstract') @@ -740,69 +925,233 @@ class BaseWindow(with_metaclass(_WindowMetaclass, EventDispatcher)): height = self.screen.height return width, height - def on_resize(self, width, height): - """A default resize event handler. + def set_minimum_size(self, width: int, height: int) -> None: + """Set the minimum size of the window. + + Once set, the user will not be able to resize the window smaller + than the given dimensions. There is no way to remove the + minimum size constraint on a window (but you could set it to 0,0). + + The behaviour is undefined if the minimum size is set larger than + the current size of the window. + + The window size does not include the border or title bar. + + :Parameters: + `width` : int + Minimum width of the window, in pixels. + `height` : int + Minimum height of the window, in pixels. - This default handler updates the GL viewport to cover the entire - window. The bottom-left corner is (0, 0) and the top-right - corner is the width and height of the window's framebuffer. - In addition, the projection matrix is set to an orghogonal - projection based on the same dimensions. """ - gl.glViewport(0, 0, *self.get_framebuffer_size()) - self.projection = Mat4.orthogonal_projection(0, width, 0, height, -255, 255) + if width < 1 or height < 1: + raise ValueError('width and height must be positive integers') - def on_close(self): - """Default on_close handler.""" - self.has_exit = True - from pyglet import app - if app.event_loop.is_running: - self.close() + self._minimum_size = width, height - def on_key_press(self, symbol, modifiers): - """Default on_key_press handler.""" - if symbol == key.ESCAPE and not (modifiers & ~(key.MOD_NUMLOCK | - key.MOD_CAPSLOCK | - key.MOD_SCROLLLOCK)): - self.dispatch_event('on_close') + def set_maximum_size(self, width: int, height: int) -> None: + """Set the maximum size of the window. - def close(self): - """Close the window. + Once set, the user will not be able to resize the window larger + than the given dimensions. There is no way to remove the + maximum size constraint on a window (but you could set it to a large + value). - After closing the window, the GL context will be invalid. The - window instance cannot be reused once closed (see also `set_visible`). + The behaviour is undefined if the maximum size is set smaller than + the current size of the window. + + The window size does not include the border or title bar. + + :Parameters: + `width` : int + Maximum width of the window, in pixels. + `height` : int + Maximum height of the window, in pixels. - The `pyglet.app.EventLoop.on_window_close` event is dispatched on - `pyglet.app.event_loop` when this method is called. """ - from pyglet import app - if not self._context: - return - app.windows.remove(self) - self._context.destroy() - self._config = None - self._context = None - if app.event_loop: - app.event_loop.dispatch_event('on_window_close', self) - self._event_queue = [] + if width < 1 or height < 1: + raise ValueError('width and height must be positive integers') - def draw_mouse_cursor(self): - """Draw the custom mouse cursor. + self._maximum_size = width, height - If the current mouse cursor has ``drawable`` set, this method - is called before the buffers are flipped to render it. + def set_size(self, width: int, height: int) -> None: + """Resize the window. + + The behaviour is undefined if the window is not resizable, or if + it is currently fullscreen. + + The window size does not include the border or title bar. + + :Parameters: + `width` : int + New width of the window, in pixels. + `height` : int + New height of the window, in pixels. - There is little need to override this method; instead, subclass - :py:class:`MouseCursor` and provide your own - :py:meth:`~MouseCursor.draw` method. """ - # Draw mouse cursor if set and visible. + if self._fullscreen: + raise WindowException('Cannot set size of fullscreen window.') + if width < 1 or height < 1: + raise ValueError('width and height must be positive integers') - if self._mouse_cursor.gl_drawable and self._mouse_visible and self._mouse_in_window: - # TODO: consider projection differences - self._mouse_cursor.draw(self._mouse_x, self._mouse_y) + self._width, self._height = width, height - # These properties provide read-only access to instance variables. + def set_location(self, x, y): + """Set the position of the window. + + :Parameters: + `x` : int + Distance of the left edge of the window from the left edge + of the virtual desktop, in pixels. + `y` : int + Distance of the top edge of the window from the top edge of + the virtual desktop, in pixels. + + """ + raise NotImplementedError('abstract') + + def set_visible(self, visible: bool = True) -> None: + """Show or hide the window. + + :Parameters: + `visible` : bool + If True, the window will be shown; otherwise it will be + hidden. + """ + self._visible = visible + + def set_vsync(self, vsync: bool) -> None: + """Enable or disable vertical sync control. + + When enabled, this option ensures flips from the back to the front + buffer are performed only during the vertical retrace period of the + primary display. This can prevent "tearing" or flickering when + the buffer is updated in the middle of a video scan. + + Note that LCD monitors have an analogous time in which they are not + reading from the video buffer; while it does not correspond to + a vertical retrace it has the same effect. + + Also note that with multi-monitor systems the secondary monitor + cannot be synchronised to, so tearing and flicker cannot be avoided + when the window is positioned outside of the primary display. + + :Parameters: + `vsync` : bool + If True, vsync is enabled, otherwise it is disabled. + + """ + self._vsync = vsync + + def set_mouse_visible(self, visible=True): + """Show or hide the mouse cursor. + + The mouse cursor will only be hidden while it is positioned within + this window. Mouse events will still be processed as usual. + + :Parameters: + `visible` : bool + If True, the mouse cursor will be visible, otherwise it + will be hidden. + + """ + self._mouse_visible = visible + self.set_mouse_platform_visible() + + def set_mouse_platform_visible(self, platform_visible=None): + """Set the platform-drawn mouse cursor visibility. This is called + automatically after changing the mouse cursor or exclusive mode. + + Applications should not normally need to call this method, see + `set_mouse_visible` instead. + + :Parameters: + `platform_visible` : bool or None + If None, sets platform visibility to the required visibility + for the current exclusive mode and cursor type. Otherwise, + a bool value will override and force a visibility. + + """ + raise NotImplementedError() + + def set_mouse_cursor(self, cursor=None): + """Change the appearance of the mouse cursor. + + The appearance of the mouse cursor is only changed while it is + within this window. + + :Parameters: + `cursor` : `MouseCursor` + The cursor to set, or None to restore the default cursor. + + """ + if cursor is None: + cursor = DefaultMouseCursor() + self._mouse_cursor = cursor + self.set_mouse_platform_visible() + + def set_exclusive_mouse(self, exclusive=True): + """Hide the mouse cursor and direct all mouse events to this + window. + + When enabled, this feature prevents the mouse leaving the window. It + is useful for certain styles of games that require complete control of + the mouse. The position of the mouse as reported in subsequent events + is meaningless when exclusive mouse is enabled; you should only use + the relative motion parameters ``dx`` and ``dy``. + + :Parameters: + `exclusive` : bool + If True, exclusive mouse is enabled, otherwise it is disabled. + + """ + self._mouse_exclusive = exclusive + + def set_exclusive_keyboard(self, exclusive=True): + """Prevent the user from switching away from this window using + keyboard accelerators. + + When enabled, this feature disables certain operating-system specific + key combinations such as Alt+Tab (Command+Tab on OS X). This can be + useful in certain kiosk applications, it should be avoided in general + applications or games. + + :Parameters: + `exclusive` : bool + If True, exclusive keyboard is enabled, otherwise it is + disabled. + + """ + self._keyboard_exclusive = exclusive + + def set_icon(self, *images): + """Set the window icon. + + If multiple images are provided, one with an appropriate size + will be selected (if the correct size is not provided, the image + will be scaled). + + Useful sizes to provide are 16x16, 32x32, 64x64 (Mac only) and + 128x128 (Mac only). + + :Parameters: + `images` : sequence of `pyglet.image.AbstractImage` + List of images to use for the window icon. + + """ + pass + + def switch_to(self): + """Make this window the current OpenGL rendering context. + + Only one OpenGL context can be active at a time. This method sets + the current window's context to be current. You should use this + method in preference to `pyglet.gl.Context.set_current`, as it may + perform additional initialisation functions. + """ + raise NotImplementedError('abstract') + + # Attributes (sort alphabetically): @property def caption(self): """The window caption (title). Read-only. @@ -935,6 +1284,9 @@ class BaseWindow(with_metaclass(_WindowMetaclass, EventDispatcher)): def projection(self): """The OpenGL window projection matrix. Read-write. + This matrix is used to transform vertices when using any of the built-in + drawable classes. `view` is done first, then `projection`. + The default projection matrix is orthographic (2D), but a custom :py:class:`pyglet.math.Mat4` instance can be set. Alternatively, you can supply a flat @@ -959,6 +1311,9 @@ class BaseWindow(with_metaclass(_WindowMetaclass, EventDispatcher)): def view(self): """The OpenGL window view matrix. Read-write. + This matrix is used to transform vertices when using any of the built-in + drawable classes. `view` is done first, then `projection`. + The default view is an identity matrix, but a custom :py:class:`pyglet.math.Mat4` instance can be set. Alternatively, you can supply a flat tuple of 16 values. @@ -1344,6 +1699,118 @@ class BaseWindow(with_metaclass(_WindowMetaclass, EventDispatcher)): # If documenting, show the event methods. Otherwise, leave them out # as they are not really methods. if _is_pyglet_doc_run: + def on_activate(self): + """The window was activated. + + This event can be triggered by clicking on the title bar, bringing + it to the foreground; or by some platform-specific method. + + When a window is "active" it has the keyboard focus. + + :event: + """ + + def on_close(self): + """The user attempted to close the window. + + This event can be triggered by clicking on the "X" control box in + the window title bar, or by some other platform-dependent manner. + + The default handler sets `has_exit` to ``True``. In pyglet 1.1, if + `pyglet.app.event_loop` is being used, `close` is also called, + closing the window immediately. + + :event: + """ + + def on_context_lost(self): + """The window's GL context was lost. + + When the context is lost no more GL methods can be called until it + is recreated. This is a rare event, triggered perhaps by the user + switching to an incompatible video mode. When it occurs, an + application will need to reload all objects (display lists, texture + objects, shaders) as well as restore the GL state. + + :event: + """ + + def on_context_state_lost(self): + """The state of the window's GL context was lost. + + pyglet may sometimes need to recreate the window's GL context if + the window is moved to another video device, or between fullscreen + or windowed mode. In this case it will try to share the objects + (display lists, texture objects, shaders) between the old and new + contexts. If this is possible, only the current state of the GL + context is lost, and the application should simply restore state. + + :event: + """ + + def on_deactivate(self): + """The window was deactivated. + + This event can be triggered by clicking on another application + window. When a window is deactivated it no longer has the + keyboard focus. + + :event: + """ + + def on_draw(self, dt): + """The window contents must be redrawn. + + The `EventLoop` will dispatch this event when the window + should be redrawn. This will happen during idle time after + any window events and after any scheduled functions were called. + + The window will already have the GL context, so there is no + need to call `switch_to`. The window's `flip` method will + be called after this event, so your event handler should not. + + You should make no assumptions about the window contents when + this event is triggered; a resize or expose event may have + invalidated the framebuffer since the last time it was drawn. + + .. versionadded:: 1.1 + + :event: + """ + + def on_expose(self): + """A portion of the window needs to be redrawn. + + This event is triggered when the window first appears, and any time + the contents of the window is invalidated due to another window + obscuring it. + + There is no way to determine which portion of the window needs + redrawing. Note that the use of this method is becoming + increasingly uncommon, as newer window managers composite windows + automatically and keep a backing store of the window contents. + + :event: + """ + + def on_file_drop(self, x, y, paths): + """File(s) were dropped into the window, will return the position of the cursor and + a list of paths to the files that were dropped. + + .. versionadded:: 1.5.1 + + :event: + """ + + def on_hide(self): + """The window was hidden. + + This event is triggered when a window is minimised + or hidden by the user. + + :event: + """ + def on_key_press(self, symbol, modifiers): """A key on the keyboard was pressed (and held down). @@ -1371,96 +1838,6 @@ class BaseWindow(with_metaclass(_WindowMetaclass, EventDispatcher)): :event: """ - def on_text(self, text): - """The user input some text. - - Typically this is called after :py:meth:`~pyglet.window.Window.on_key_press` and before - :py:meth:`~pyglet.window.Window.on_key_release`, but may also be called multiple times if the key - is held down (key repeating); or called without key presses if - another input method was used (e.g., a pen input). - - You should always use this method for interpreting text, as the - key symbols often have complex mappings to their unicode - representation which this event takes care of. - - :Parameters: - `text` : unicode - The text entered by the user. - - :event: - """ - - def on_text_motion(self, motion): - """The user moved the text input cursor. - - Typically this is called after :py:meth:`~pyglet.window.Window.on_key_press` and before - :py:meth:`~pyglet.window.Window.on_key_release`, but may also be called multiple times if the key - is help down (key repeating). - - You should always use this method for moving the text input cursor - (caret), as different platforms have different default keyboard - mappings, and key repeats are handled correctly. - - The values that `motion` can take are defined in - :py:mod:`pyglet.window.key`: - - * MOTION_UP - * MOTION_RIGHT - * MOTION_DOWN - * MOTION_LEFT - * MOTION_NEXT_WORD - * MOTION_PREVIOUS_WORD - * MOTION_BEGINNING_OF_LINE - * MOTION_END_OF_LINE - * MOTION_NEXT_PAGE - * MOTION_PREVIOUS_PAGE - * MOTION_BEGINNING_OF_FILE - * MOTION_END_OF_FILE - * MOTION_BACKSPACE - * MOTION_DELETE - - :Parameters: - `motion` : int - The direction of motion; see remarks. - - :event: - """ - - def on_text_motion_select(self, motion): - """The user moved the text input cursor while extending the - selection. - - Typically this is called after :py:meth:`~pyglet.window.Window.on_key_press` and before - :py:meth:`~pyglet.window.Window.on_key_release`, but may also be called multiple times if the key - is help down (key repeating). - - You should always use this method for responding to text selection - events rather than the raw :py:meth:`~pyglet.window.Window.on_key_press`, as different platforms - have different default keyboard mappings, and key repeats are - handled correctly. - - The values that `motion` can take are defined in :py:mod:`pyglet.window.key`: - - * MOTION_UP - * MOTION_RIGHT - * MOTION_DOWN - * MOTION_LEFT - * MOTION_NEXT_WORD - * MOTION_PREVIOUS_WORD - * MOTION_BEGINNING_OF_LINE - * MOTION_END_OF_LINE - * MOTION_NEXT_PAGE - * MOTION_PREVIOUS_PAGE - * MOTION_BEGINNING_OF_FILE - * MOTION_END_OF_FILE - - :Parameters: - `motion` : int - The direction of selection motion; see remarks. - - :event: - """ - def on_mouse_motion(self, x, y, dx, dy): """The mouse was moved with no buttons held down. @@ -1556,19 +1933,6 @@ class BaseWindow(with_metaclass(_WindowMetaclass, EventDispatcher)): :event: """ - def on_close(self): - """The user attempted to close the window. - - This event can be triggered by clicking on the "X" control box in - the window title bar, or by some other platform-dependent manner. - - The default handler sets `has_exit` to ``True``. In pyglet 1.1, if - `pyglet.app.event_loop` is being used, `close` is also called, - closing the window immediately. - - :event: - """ - def on_mouse_enter(self, x, y): """The mouse was moved into the window. @@ -1600,36 +1964,6 @@ class BaseWindow(with_metaclass(_WindowMetaclass, EventDispatcher)): :event: """ - def on_expose(self): - """A portion of the window needs to be redrawn. - - This event is triggered when the window first appears, and any time - the contents of the window is invalidated due to another window - obscuring it. - - There is no way to determine which portion of the window needs - redrawing. Note that the use of this method is becoming - increasingly uncommon, as newer window managers composite windows - automatically and keep a backing store of the window contents. - - :event: - """ - - def on_resize(self, width, height): - """The window was resized. - - The window will have the GL context when this event is dispatched; - there is no need to call `switch_to` in this handler. - - :Parameters: - `width` : int - The new width of the window, in pixels. - `height` : int - The new height of the window, in pixels. - - :event: - """ - def on_move(self, x, y): """The window was moved. @@ -1645,99 +1979,6 @@ class BaseWindow(with_metaclass(_WindowMetaclass, EventDispatcher)): :event: """ - def on_activate(self): - """The window was activated. - - This event can be triggered by clicking on the title bar, bringing - it to the foreground; or by some platform-specific method. - - When a window is "active" it has the keyboard focus. - - :event: - """ - - def on_deactivate(self): - """The window was deactivated. - - This event can be triggered by clicking on another application - window. When a window is deactivated it no longer has the - keyboard focus. - - :event: - """ - - def on_show(self): - """The window was shown. - - This event is triggered when a window is restored after being - minimised, hidden, or after being displayed for the first time. - - :event: - """ - - def on_hide(self): - """The window was hidden. - - This event is triggered when a window is minimised - or hidden by the user. - - :event: - """ - - def on_context_lost(self): - """The window's GL context was lost. - - When the context is lost no more GL methods can be called until it - is recreated. This is a rare event, triggered perhaps by the user - switching to an incompatible video mode. When it occurs, an - application will need to reload all objects (display lists, texture - objects, shaders) as well as restore the GL state. - - :event: - """ - - def on_context_state_lost(self): - """The state of the window's GL context was lost. - - pyglet may sometimes need to recreate the window's GL context if - the window is moved to another video device, or between fullscreen - or windowed mode. In this case it will try to share the objects - (display lists, texture objects, shaders) between the old and new - contexts. If this is possible, only the current state of the GL - context is lost, and the application should simply restore state. - - :event: - """ - - def on_file_drop(self, x, y, paths): - """File(s) were dropped into the window, will return the position of the cursor and - a list of paths to the files that were dropped. - - .. versionadded:: 1.5.1 - - :event: - """ - - def on_draw(self, dt): - """The window contents must be redrawn. - - The `EventLoop` will dispatch this event when the window - should be redrawn. This will happen during idle time after - any window events and after any scheduled functions were called. - - The window will already have the GL context, so there is no - need to call `switch_to`. The window's `flip` method will - be called after this event, so your event handler should not. - - You should make no assumptions about the window contents when - this event is triggered; a resize or expose event may have - invalidated the framebuffer since the last time it was drawn. - - .. versionadded:: 1.1 - - :event: - """ - def on_refresh(self, dt): """The window contents must be redrawn. @@ -1757,6 +1998,120 @@ class BaseWindow(with_metaclass(_WindowMetaclass, EventDispatcher)): :event: """ + def on_resize(self, width, height): + """The window was resized. + + The window will have the GL context when this event is dispatched; + there is no need to call `switch_to` in this handler. + + :Parameters: + `width` : int + The new width of the window, in pixels. + `height` : int + The new height of the window, in pixels. + + :event: + """ + + def on_show(self): + """The window was shown. + + This event is triggered when a window is restored after being + minimised, hidden, or after being displayed for the first time. + + :event: + """ + + def on_text(self, text): + """The user input some text. + + Typically this is called after :py:meth:`~pyglet.window.Window.on_key_press` and before + :py:meth:`~pyglet.window.Window.on_key_release`, but may also be called multiple times if the key + is held down (key repeating); or called without key presses if + another input method was used (e.g., a pen input). + + You should always use this method for interpreting text, as the + key symbols often have complex mappings to their unicode + representation which this event takes care of. + + :Parameters: + `text` : unicode + The text entered by the user. + + :event: + """ + + def on_text_motion(self, motion): + """The user moved the text input cursor. + + Typically this is called after :py:meth:`~pyglet.window.Window.on_key_press` and before + :py:meth:`~pyglet.window.Window.on_key_release`, but may also be called multiple times if the key + is help down (key repeating). + + You should always use this method for moving the text input cursor + (caret), as different platforms have different default keyboard + mappings, and key repeats are handled correctly. + + The values that `motion` can take are defined in + :py:mod:`pyglet.window.key`: + + * MOTION_UP + * MOTION_RIGHT + * MOTION_DOWN + * MOTION_LEFT + * MOTION_NEXT_WORD + * MOTION_PREVIOUS_WORD + * MOTION_BEGINNING_OF_LINE + * MOTION_END_OF_LINE + * MOTION_NEXT_PAGE + * MOTION_PREVIOUS_PAGE + * MOTION_BEGINNING_OF_FILE + * MOTION_END_OF_FILE + * MOTION_BACKSPACE + * MOTION_DELETE + + :Parameters: + `motion` : int + The direction of motion; see remarks. + + :event: + """ + + def on_text_motion_select(self, motion): + """The user moved the text input cursor while extending the + selection. + + Typically this is called after :py:meth:`~pyglet.window.Window.on_key_press` and before + :py:meth:`~pyglet.window.Window.on_key_release`, but may also be called multiple times if the key + is help down (key repeating). + + You should always use this method for responding to text selection + events rather than the raw :py:meth:`~pyglet.window.Window.on_key_press`, as different platforms + have different default keyboard mappings, and key repeats are + handled correctly. + + The values that `motion` can take are defined in :py:mod:`pyglet.window.key`: + + * MOTION_UP + * MOTION_RIGHT + * MOTION_DOWN + * MOTION_LEFT + * MOTION_NEXT_WORD + * MOTION_PREVIOUS_WORD + * MOTION_BEGINNING_OF_LINE + * MOTION_END_OF_LINE + * MOTION_NEXT_PAGE + * MOTION_PREVIOUS_PAGE + * MOTION_BEGINNING_OF_FILE + * MOTION_END_OF_FILE + + :Parameters: + `motion` : int + The direction of selection motion; see remarks. + + :event: + """ + BaseWindow.register_event_type('on_key_press') BaseWindow.register_event_type('on_key_release')