撤销了一些pyglet更新

This commit is contained in:
shenjack 2023-03-01 13:35:55 +08:00
parent 1ddd3c6d41
commit 9aaa1a1a23
21 changed files with 158 additions and 661 deletions

View File

@ -9,7 +9,7 @@ import sys
from typing import TYPE_CHECKING
#: The release version
version = '2.0.5'
version = '2.0.4'
__version__ = version
MIN_PYTHON_VERSION = 3, 8
@ -125,7 +125,6 @@ options = {
'xlib_fullscreen_override_redirect': False,
'search_local_libs': True,
'win32_gdi_font': False,
'scale_with_dpi': False,
'headless': False,
'headless_device': 0,
'win32_disable_shaping': False,
@ -156,7 +155,6 @@ _option_types = {
'vsync': bool,
'xsync': bool,
'xlib_fullscreen_override_redirect': bool,
'scale_with_dpi': bool,
'search_local_libs': bool,
'win32_gdi_font': bool,
'headless': bool,

View File

@ -267,20 +267,6 @@ class Screen:
"""
raise NotImplementedError('abstract')
def get_dpi(self):
"""Get the DPI of the screen.
:rtype: int
"""
raise NotImplementedError('abstract')
def get_scale(self):
"""Get the pixel scale ratio of the screen.
:rtype: float
"""
raise NotImplementedError('abstract')
class ScreenMode:
"""Screen resolution and display settings.

View File

@ -4,11 +4,9 @@ from ctypes import *
from .base import Display, Screen, ScreenMode, Canvas
from pyglet.libs.darwin.cocoapy import CGDirectDisplayID, quartz, cf, ObjCClass, get_NSString
from pyglet.libs.darwin.cocoapy import CGDirectDisplayID, quartz, cf
from pyglet.libs.darwin.cocoapy import cfstring_to_string, cfarray_to_list
from ..libs.darwin import NSDeviceResolution
NSScreen = ObjCClass('NSScreen')
class CocoaDisplay(Display):
@ -33,40 +31,28 @@ class CocoaScreen(Screen):
self._cg_display_id = displayID
# Save the default mode so we can restore to it.
self._default_mode = self.get_mode()
self._ns_screen = self.get_nsscreen()
def get_nsscreen(self):
"""Returns the NSScreen instance that matches our CGDirectDisplayID."""
# Get a list of all currently active NSScreens and then search through
# them until we find one that matches our CGDisplayID.
screen_array = NSScreen.screens()
count = screen_array.count()
for i in range(count):
nsscreen = screen_array.objectAtIndex_(i)
screenInfo = nsscreen.deviceDescription()
displayID = screenInfo.objectForKey_(get_NSString('NSScreenNumber'))
displayID = displayID.intValue()
if displayID == self._cg_display_id:
return nsscreen
return None
def get_dpi(self):
desc = self._ns_screen.deviceDescription()
#psize = desc.objectForKey_(NSDeviceSize).sizeValue()
rsize = desc.objectForKey_(NSDeviceResolution).sizeValue()
return int(rsize.width)
def get_scale(self):
ratio = 1.0
if self._ns_screen:
pts = self._ns_screen.frame()
pixels = self._ns_screen.convertRectToBacking_(pts)
ratio = pixels.size.width / pts.size.width
else:
print("Could not initialize NSScreen to retrieve DPI. Using default.")
print("BSF", self._ns_screen.backingScaleFactor(), "ratio:", ratio)
return ratio
# FIX ME:
# This method is needed to get multi-monitor support working properly.
# However the NSScreens.screens() message currently sends out a warning:
# "*** -[NSLock unlock]: lock (<NSLock: 0x...> '(null)') unlocked when not locked"
# on Snow Leopard and apparently causes python to crash on Lion.
#
# def get_nsscreen(self):
# """Returns the NSScreen instance that matches our CGDirectDisplayID."""
# NSScreen = ObjCClass('NSScreen')
# # Get a list of all currently active NSScreens and then search through
# # them until we find one that matches our CGDisplayID.
# screen_array = NSScreen.screens()
# count = screen_array.count()
# for i in range(count):
# nsscreen = screen_array.objectAtIndex_(i)
# screenInfo = nsscreen.deviceDescription()
# displayID = screenInfo.objectForKey_(get_NSString('NSScreenNumber'))
# displayID = displayID.intValue()
# if displayID == self._cg_display_id:
# return nsscreen
# return None
def get_matching_configs(self, template):
canvas = CocoaCanvas(self.display, self, None)

View File

@ -1,22 +1,9 @@
from .base import Display, Screen, ScreenMode, Canvas
from pyglet.libs.win32 import _kernel32, _user32, _shcore, _gdi32, types, constants
from pyglet.libs.win32 import _user32
from pyglet.libs.win32.constants import *
from pyglet.libs.win32.types import *
def set_dpi_awareness():
"""
Setting DPI varies per Windows version.
Note: DPI awareness needs to be set before Window, Display, or Screens are initialized.
"""
if WINDOWS_10_CREATORS_UPDATE_OR_GREATER:
_user32.SetProcessDpiAwarenessContext(DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2)
elif WINDOWS_8_1_OR_GREATER: # 8.1 and above allows per monitor DPI.
_shcore.SetProcessDpiAwareness(PROCESS_PER_MONITOR_DPI_AWARE)
elif WINDOWS_VISTA_OR_GREATER: # Only System wide DPI
_user32.SetProcessDPIAware()
set_dpi_awareness()
class Win32Display(Display):
def get_screens(self):
@ -57,24 +44,6 @@ class Win32Screen(Screen):
info.cbSize = sizeof(MONITORINFOEX)
_user32.GetMonitorInfoW(self._handle, byref(info))
return info.szDevice
def get_dpi(self):
if WINDOWS_8_1_OR_GREATER:
xdpi = UINT()
ydpi = UINT()
_shcore.GetDpiForMonitor(self._handle, 0, byref(xdpi), byref(ydpi))
xdpi, ydpi = xdpi.value, ydpi.value
else:
dc = _user32.GetDC(None)
xdpi = _gdi32.GetDeviceCaps(dc, LOGPIXELSX)
ydpi = _gdi32.GetDeviceCaps(dc, LOGPIXELSY)
_user32.ReleaseDC(0, dc)
return xdpi
def get_scale(self):
xdpi = self.get_dpi()
return xdpi / USER_DEFAULT_SCREEN_DPI
def get_modes(self):
device_name = self.get_device_name()

View File

@ -6,7 +6,6 @@ from pyglet.app.xlib import XlibSelectDevice
from .base import Display, Screen, ScreenMode, Canvas
from . import xlib_vidmoderestore
from ..util import asbytes
# XXX
@ -178,28 +177,6 @@ class XlibScreen(Screen):
super(XlibScreen, self).__init__(display, x, y, width, height)
self._xinerama = xinerama
def get_dpi(self):
resource = xlib.XResourceManagerString(self.display._display)
dpi = 96
if resource:
xlib.XrmInitialize()
db = xlib.XrmGetStringDatabase(resource)
if db:
rs_type = c_char_p()
value = xlib.XrmValue()
if xlib.XrmGetResource(db, asbytes("Xft.dpi"), asbytes("Xft.dpi"),
byref(rs_type), byref(value)):
if value.addr and rs_type.value == b'String':
dpi = int(value.addr)
xlib.XrmDestroyDatabase(db)
return dpi
def get_scale(self):
return self.get_dpi() / 96
def get_matching_configs(self, template):
canvas = XlibCanvas(self.display, None)
configs = template.match(canvas)

View File

@ -1,8 +1,6 @@
import platform
from ctypes import c_uint32, c_int, byref
import pyglet
from pyglet.gl.base import Config, CanvasConfig, Context
from pyglet.gl import ContextException
@ -262,7 +260,7 @@ class CocoaContext(Context):
# The NSView instance should be attached to a nondeferred window before calling
# setView, otherwise you get an "invalid drawable" message.
self._nscontext.setView_(canvas.nsview)
self._nscontext.view().setWantsBestResolutionOpenGLSurface_(1)
self.set_current()
def detach(self):

View File

@ -676,7 +676,7 @@ class ImageData(AbstractImage):
:rtype: cls or cls.region_class
"""
internalformat = self._get_internalformat(self._desired_format)
texture = cls.create(self.width, self.height, GL_TEXTURE_2D, internalformat, False, blank_data=False)
texture = cls.create(self.width, self.height, GL_TEXTURE_2D, internalformat)
if self.anchor_x or self.anchor_y:
texture.anchor_x = self.anchor_x
texture.anchor_y = self.anchor_y
@ -704,7 +704,7 @@ class ImageData(AbstractImage):
if self._current_mipmap_texture:
return self._current_mipmap_texture
texture = Texture.create(self.width, self.height, GL_TEXTURE_2D, None, blank_data=False)
texture = Texture.create(self.width, self.height, GL_TEXTURE_2D, None)
if self.anchor_x or self.anchor_y:
texture.anchor_x = self.anchor_x
texture.anchor_y = self.anchor_y
@ -816,17 +816,12 @@ class ImageData(AbstractImage):
# Unset GL_UNPACK_ROW_LENGTH:
glPixelStorei(GL_UNPACK_ROW_LENGTH, 0)
self._default_region_unpack()
# Flush image upload before data get GC'd:
glFlush()
def _apply_region_unpack(self):
pass
def _default_region_unpack(self):
pass
def _convert(self, fmt, pitch):
"""Return data in the desired format; does not alter this instance's
current format or pitch.
@ -1000,10 +995,6 @@ class ImageDataRegion(ImageData):
glPixelStorei(GL_UNPACK_SKIP_PIXELS, self.x)
glPixelStorei(GL_UNPACK_SKIP_ROWS, self.y)
def _default_region_unpack(self):
glPixelStorei(GL_UNPACK_SKIP_PIXELS, 0)
glPixelStorei(GL_UNPACK_SKIP_ROWS, 0)
def get_region(self, x, y, width, height):
x += self.x
y += self.y
@ -1226,7 +1217,7 @@ class Texture(AbstractImage):
glBindImageTexture(unit, self.id, level, layered, layer, access, fmt)
@classmethod
def create(cls, width, height, target=GL_TEXTURE_2D, internalformat=GL_RGBA8, min_filter=None, mag_filter=None, fmt=GL_RGBA, blank_data=True):
def create(cls, width, height, target=GL_TEXTURE_2D, internalformat=GL_RGBA8, min_filter=None, mag_filter=None, fmt=GL_RGBA):
"""Create a Texture
Create a Texture with the specified dimentions, target and format.
@ -1251,9 +1242,6 @@ class Texture(AbstractImage):
GL constant giving format of texture; for example, ``GL_RGBA``.
The format specifies what format the pixel data we're expecting to write
to the texture and should ideally be the same as for internal format.
`blank_data` : bool
Setting to True will initialize the texture data with all zeros. Setting False, will initialize Texture
with no data.
:rtype: :py:class:`~pyglet.image.Texture`
"""
@ -1267,14 +1255,13 @@ class Texture(AbstractImage):
glTexParameteri(target, GL_TEXTURE_MAG_FILTER, mag_filter)
if internalformat is not None:
blank = (GLubyte * (width * height * 4))() if blank_data else None
glTexImage2D(target, 0,
internalformat,
width, height,
0,
fmt,
GL_UNSIGNED_BYTE,
blank)
None)
glFlush()
texture = cls(width, height, target, tex_id.value)
@ -1313,7 +1300,7 @@ class Texture(AbstractImage):
glPixelStorei(GL_PACK_ALIGNMENT, 1)
glCheckFramebufferStatus(GL_FRAMEBUFFER)
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, self.id, self.level)
glReadPixels(0, 0, self.width, self.height, gl_format, GL_UNSIGNED_BYTE, buf)
glReadPixels(0, 0, self.width, self.height, gl_format, GL_UNSIGNED_BYTE, buf)
glBindFramebuffer(GL_FRAMEBUFFER, 0)
glDeleteFramebuffers(1, fbo)
else:
@ -1479,7 +1466,7 @@ class Texture3D(Texture, UniformTextureSequence):
items = ()
@classmethod
def create_for_images(cls, images, internalformat=GL_RGBA, blank_data=True):
def create_for_images(cls, images, internalformat=GL_RGBA):
item_width = images[0].width
item_height = images[0].height
for image in images:
@ -1495,13 +1482,12 @@ class Texture3D(Texture, UniformTextureSequence):
texture.images = depth
blank = (GLubyte * (texture.width * texture.height * texture.images))() if blank_data else None
glBindTexture(texture.target, texture.id)
glTexImage3D(texture.target, texture.level,
internalformat,
texture.width, texture.height, texture.images, 0,
GL_ALPHA, GL_UNSIGNED_BYTE,
blank)
None)
items = []
for i, image in enumerate(images):
@ -2106,7 +2092,7 @@ class ColorBufferImage(BufferImage):
format = 'RGBA'
def get_texture(self, rectangle=False):
texture = Texture.create(self.width, self.height, GL_TEXTURE_2D, GL_RGBA, blank_data=False)
texture = Texture.create(self.width, self.height, GL_TEXTURE_2D, GL_RGBA)
self.blit_to_texture(texture.target, texture.level, self.anchor_x, self.anchor_y, 0)
return texture

View File

@ -57,7 +57,8 @@ class Win32ControllerManager(base.ControllerManager):
def on_disconnect(xdevice):
self.dispatch_event('on_disconnect', self._xinput_controllers[xdevice])
self._set_initial_didevices()
for device in _di_device_manager.devices:
self._add_di_controller(device)
@_di_device_manager.event
def on_connect(di_device):
@ -72,14 +73,6 @@ class Win32ControllerManager(base.ControllerManager):
del self._di_controllers[di_device]
pyglet.app.platform_event_loop.post_event(self, 'on_disconnect', _controller)
def _set_initial_didevices(self):
if not _di_device_manager.registered:
_di_device_manager.register_device_events()
_di_device_manager.set_current_devices()
for device in _di_device_manager.devices:
self._add_di_controller(device)
def _add_di_controller(self, device: DirectInputDevice) -> Optional[base.Controller]:
controller = _create_controller(device)
if controller:

View File

@ -15,6 +15,7 @@ from pyglet.libs.win32 import dinput, _user32, DEV_BROADCAST_DEVICEINTERFACE, co
from pyglet.libs.win32 import _kernel32
from pyglet.input.controller import get_mapping
from pyglet.input.base import ControllerManager
from pyglet.libs.win32.dinput import DIPROPHEADER
# These instance names are not defined anywhere, obtained by experiment. The
# GUID names (which seem to be ideally what are needed) are wrong/missing for
@ -209,78 +210,31 @@ GUID_DEVINTERFACE_HID = com.GUID(0x4D1E55B2, 0xF16F, 0x11CF, 0x88, 0xCB, 0x00, 0
class DIDeviceManager(EventDispatcher):
def __init__(self):
self.registered = False
self.window = None
self._devnotify = None
# Pick any open window, or the shadow window if no windows have been created yet.
window = pyglet.gl._shadow_window
for window in pyglet.app.windows:
break
self.window = window
dbi = DEV_BROADCAST_DEVICEINTERFACE()
dbi.dbcc_size = ctypes.sizeof(dbi)
dbi.dbcc_devicetype = DBT_DEVTYP_DEVICEINTERFACE
dbi.dbcc_classguid = GUID_DEVINTERFACE_HID
# Register we look for HID device unplug/plug.
_user32.RegisterDeviceNotificationW(window._hwnd, ctypes.byref(dbi), DEVICE_NOTIFY_WINDOW_HANDLE)
window._event_handlers[WM_DEVICECHANGE] = self._event_devicechange
# All devices.
self.devices: List[DirectInputDevice] = []
if self.register_device_events(skip_warning=True):
self.set_current_devices()
def set_current_devices(self):
"""Sets all currently discovered devices in the devices list.
Be careful when using this, as this creates new device objects. Should only be called on initialization of
the manager and if for some reason the window connection event isn't registered.
"""
new_devices, _ = self._get_devices()
self.devices = new_devices
def register_device_events(self, skip_warning=False, window=None):
"""Register the first OS Window to receive events of disconnect and connection of devices.
Returns True if events were successfully registered.
"""
if not self.registered:
# If a specific window is not specified, find one.
if not window:
# Pick any open window, or the shadow window if no windows have been created yet.
window = pyglet.gl._shadow_window
if not window:
for window in pyglet.app.windows:
break
self.window = window
if self.window is not None:
dbi = DEV_BROADCAST_DEVICEINTERFACE()
dbi.dbcc_size = ctypes.sizeof(dbi)
dbi.dbcc_devicetype = DBT_DEVTYP_DEVICEINTERFACE
dbi.dbcc_classguid = GUID_DEVINTERFACE_HID
# Register we look for HID device unplug/plug.
self._devnotify = _user32.RegisterDeviceNotificationW(self.window._hwnd, ctypes.byref(dbi), DEVICE_NOTIFY_WINDOW_HANDLE)
self.window._event_handlers[WM_DEVICECHANGE] = self._event_devicechange
self.registered = True
self.window.push_handlers(self)
return True
else:
if not skip_warning:
warnings.warn("DirectInput Device Manager requires a window to receive device connection events.")
return False
def _unregister_device_events(self):
del self.window._event_handlers[WM_DEVICECHANGE]
_user32.UnregisterDeviceNotification(self._devnotify)
self.registered = False
self._devnotify = None
def on_close(self):
if self.registered:
self._unregister_device_events()
import pyglet.app
if len(pyglet.app.windows) != 0:
# At this point the closed windows aren't removed from the app.windows list. Check for non-current window.
for existing_window in pyglet.app.windows:
if existing_window != self.window:
self.register_device_events(skip_warning=True, window=existing_window)
return
self.window = None
self.devices.extend(new_devices)
def __del__(self):
if self.registered:
self._unregister_device_events()
del self.window._event_handlers[WM_DEVICECHANGE] # Remove handler.
def _get_devices(self, display=None):
"""Enumerate all the devices on the system.
@ -340,13 +294,11 @@ class DIDeviceManager(EventDispatcher):
if wParam == DBT_DEVICEARRIVAL or wParam == DBT_DEVICEREMOVECOMPLETE:
hdr_ptr = ctypes.cast(lParam, ctypes.POINTER(DEV_BROADCAST_HDR))
if hdr_ptr.contents.dbch_devicetype == DBT_DEVTYP_DEVICEINTERFACE:
# Need to call this outside the generate OS event to prevent COM deadlock.
pyglet.app.platform_event_loop.post_event(self, '_recheck_devices')
self._recheck_devices()
DIDeviceManager.register_event_type('on_connect')
DIDeviceManager.register_event_type('on_disconnect')
DIDeviceManager.register_event_type('_recheck_devices') # Not to be used by subclasses!
_di_manager = DIDeviceManager()
@ -382,10 +334,6 @@ class DIControllerManager(ControllerManager):
return None
def get_controllers(self):
if not _di_manager.registered:
_di_manager.register_device_events()
_di_manager.set_current_devices()
return list(self._controllers.values())
@ -439,20 +387,12 @@ def _create_joystick(device):
def get_joysticks(display=None):
if not _di_manager.registered:
_di_manager.register_device_events()
_di_manager.set_current_devices()
return [joystick for joystick in
[_create_joystick(device) for device in _di_manager.devices]
if joystick is not None]
def get_controllers(display=None):
if not _di_manager.registered:
_di_manager.register_device_events()
_di_manager.set_current_devices()
return [controller for controller in
[_create_controller(device) for device in _di_manager.devices]
if controller is not None]

View File

@ -400,9 +400,6 @@ quartz.CGDisplayCopyDisplayMode.argtypes = [CGDirectDisplayID]
quartz.CGDisplayModeGetRefreshRate.restype = c_double
quartz.CGDisplayModeGetRefreshRate.argtypes = [c_void_p]
quartz.CGDisplayScreenSize.restype = CGSize
quartz.CGDisplayScreenSize.argtypes = [CGDirectDisplayID]
quartz.CGDisplayModeRetain.restype = c_void_p
quartz.CGDisplayModeRetain.argtypes = [c_void_p]

View File

@ -13,48 +13,13 @@ _debug_win32 = pyglet.options['debug_win32']
DebugLibrary = lambda lib: ctypes.WinDLL(lib, use_last_error=True if _debug_win32 else False)
_log_win32 = open('debug_win32.log', 'w')
def format_error(err):
msg = create_string_buffer(256)
_FormatMessageA(constants.FORMAT_MESSAGE_FROM_SYSTEM,
c_void_p(),
err,
0,
msg,
len(msg),
c_void_p())
return msg.value
class DebugLibrary:
def __init__(self, lib):
self.lib = lib
def __getattr__(self, name):
fn = getattr(self.lib, name)
def f(*args):
_SetLastError(0)
result = fn(*args)
err = _GetLastError()
if err != 0:
for entry in traceback.format_list(traceback.extract_stack()[:-1]):
_log_win32.write(entry)
print(format_error(err), file=_log_win32)
return result
return f
else:
DebugLibrary = lambda lib: lib
_gdi32 = DebugLibrary(windll.gdi32)
_kernel32 = DebugLibrary(windll.kernel32)
_user32 = DebugLibrary(windll.user32)
_dwmapi = DebugLibrary(windll.dwmapi)
_shell32 = DebugLibrary(windll.shell32)
_ole32 = DebugLibrary(windll.ole32)
_shcore = DebugLibrary(windll.shcore)
_gdi32 = DebugLibrary('gdi32')
_kernel32 = DebugLibrary('kernel32')
_user32 = DebugLibrary('user32')
_dwmapi = DebugLibrary('dwmapi')
_shell32 = DebugLibrary('shell32')
_ole32 = DebugLibrary('ole32')
_oleaut32 = DebugLibrary('oleaut32')
# _gdi32
_gdi32.AddFontMemResourceEx.restype = HANDLE
@ -216,9 +181,7 @@ _user32.SetFocus.argtypes = [HWND]
_user32.SetForegroundWindow.restype = BOOL
_user32.SetForegroundWindow.argtypes = [HWND]
_user32.SetTimer.restype = UINT_PTR
_user32.SetTimer.argtypes = [HWND, UINT_PTR, UINT, POINTER(TIMERPROC)]
_user32.KillTimer.restype = UINT_PTR
_user32.KillTimer.argtypes = [HWND, UINT_PTR]
_user32.SetTimer.argtypes = [HWND, UINT_PTR, UINT, TIMERPROC]
_user32.SetWindowLongW.restype = LONG
_user32.SetWindowLongW.argtypes = [HWND, c_int, LONG]
_user32.SetWindowPos.restype = BOOL
@ -244,20 +207,8 @@ _user32.GetRawInputData.restype = UINT
_user32.GetRawInputData.argtypes = [HRAWINPUT, UINT, LPVOID, PUINT, UINT]
_user32.ChangeWindowMessageFilterEx.restype = BOOL
_user32.ChangeWindowMessageFilterEx.argtypes = [HWND, UINT, DWORD, c_void_p]
_user32.SetProcessDPIAware.restype = BOOL
_user32.SetProcessDPIAware.argtypes = []
_user32.MonitorFromWindow.restype = HMONITOR
_user32.MonitorFromWindow.argtypes = [HWND, DWORD]
if constants.WINDOWS_10_CREATORS_UPDATE_OR_GREATER:
_user32.SetProcessDpiAwarenessContext.restype = BOOL
_user32.SetProcessDpiAwarenessContext.argtypes = [DPI_AWARENESS_CONTEXT]
if constants.WINDOWS_10_ANNIVERSARY_UPDATE_OR_GREATER:
_user32.EnableNonClientDpiScaling.restype = BOOL
_user32.EnableNonClientDpiScaling.argtypes = [HWND]
_user32.GetDpiForWindow.restype = UINT
_user32.GetDpiForWindow.argtypes = [HWND]
_user32.RegisterDeviceNotificationW.restype = LPVOID
_user32.RegisterDeviceNotificationW.argtypes = [HANDLE, LPVOID, DWORD]
# dwmapi
_dwmapi.DwmIsCompositionEnabled.restype = c_int

View File

@ -1492,8 +1492,6 @@ WM_IME_KEYDOWN = 656
WM_IME_KEYUP = 657
WM_MOUSEHOVER = 673
WM_MOUSELEAVE = 675
WM_DPICHANGED = 736
WM_GETDPISCALEDSIZE = 740
WM_CUT = 768
WM_COPY = 769
WM_PASTE = 770
@ -5080,4 +5078,3 @@ DBT_DEVTYP_DEVICEINTERFACE = 5
DEVICE_NOTIFY_WINDOW_HANDLE = 0
DEVICE_NOTIFY_SERVICE_HANDLE = 1
USER_DEFAULT_SCREEN_DPI = 96

View File

@ -71,28 +71,6 @@ TIMERPROC = WINFUNCTYPE(None, HWND, UINT, POINTER(UINT), DWORD)
TIMERAPCPROC = WINFUNCTYPE(None, PVOID, DWORD, DWORD)
MONITORENUMPROC = WINFUNCTYPE(BOOL, HMONITOR, HDC, LPRECT, LPARAM)
PROCESS_DPI_AWARENESS = UINT
PROCESS_DPI_UNAWARE = 0
PROCESS_SYSTEM_DPI_AWARE = 1
PROCESS_PER_MONITOR_DPI_AWARE = 2
MONITOR_DPI_TYPE = UINT
MDT_EFFECTIVE_DPI = 0
MDT_ANGULAR_DPI = 1
MDT_RAW_DPI = 2
MDT_DEFAULT = 3
PROCESS_DPI_AWARENESS = UINT
PROCESS_DPI_UNAWARE = 0
PROCESS_SYSTEM_DPI_AWARE = 1
PROCESS_PER_MONITOR_DPI_AWARE = 2
DPI_AWARENESS_CONTEXT = HANDLE
DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2 = DPI_AWARENESS_CONTEXT(-4)
MONITOR_DEFAULTTONEAREST = 0
MONITOR_DEFAULTTONULL = 1
MONITOR_DEFAULTTOPRIMARY = 2
def MAKEINTRESOURCE(i):
return cast(ctypes.c_void_p(i & 0xFFFF), c_wchar_p)

View File

@ -2613,13 +2613,6 @@ struct_anon_94._fields_ = [
('supported_values', POINTER(c_char_p)),
]
class XrmValue(Structure):
_fields_ = (
('size', c_uint),
('addr', c_char_p)
)
XIMValuesList = struct_anon_94 # /usr/include/X11/Xlib.h:1395
# /usr/include/X11/Xlib.h:1405
XLoadQueryFont = _lib.XLoadQueryFont
@ -2686,18 +2679,6 @@ XrmInitialize = _lib.XrmInitialize
XrmInitialize.restype = None
XrmInitialize.argtypes = []
XrmGetStringDatabase = _lib.XrmGetStringDatabase
XrmGetStringDatabase.restype = c_void_p
XrmGetStringDatabase.argtypes = [c_char_p]
XrmDestroyDatabase = _lib.XrmDestroyDatabase
XrmDestroyDatabase.restype = None
XrmDestroyDatabase.argtypes = [c_void_p]
XrmGetResource = _lib.XrmGetResource
XrmGetResource.restype = c_bool
XrmGetResource.argtypes = [c_void_p, c_char_p, c_char_p, POINTER(c_char_p), POINTER(XrmValue)]
# /usr/include/X11/Xlib.h:1502
XFetchBytes = _lib.XFetchBytes
XFetchBytes.restype = c_char_p

View File

@ -7,58 +7,18 @@ import pyglet
_debug = pyglet.options['debug_media']
for driver_name in pyglet.options['audio']:
try:
if driver_name == 'pulse':
from . import pulse
_audio_driver = pulse.create_audio_driver()
break
elif driver_name == 'xaudio2':
from pyglet.libs.win32.constants import WINDOWS_8_OR_GREATER
if WINDOWS_8_OR_GREATER:
from . import xaudio2
_audio_driver = xaudio2.create_audio_driver()
break
elif driver_name == 'directsound':
from . import directsound
_audio_driver = directsound.create_audio_driver()
break
elif driver_name == 'openal':
from . import openal
_audio_driver = openal.create_audio_driver()
break
elif driver_name == 'silent':
from . import silent
_audio_driver = silent.create_audio_driver()
break
except Exception:
if _debug:
print(f'Error importing driver {driver_name}:')
import traceback
traceback.print_exc()
else:
from . import silent
_audio_driver = silent.create_audio_driver()
def get_audio_driver():
"""Get the preferred audio driver for the current platform.
See :data:`pyglet.options` ``audio``, and the Programming guide,
section :doc:`/programming_guide/media` for more information on
setting the preferred driver.
Currently pyglet supports DirectSound, PulseAudio and OpenAL drivers. If
the platform supports more than one of those audio drivers, the
application can give its preference with :data:`pyglet.options` ``audio``
keyword. See the Programming guide, section
:doc:`/programming_guide/media`.
Returns:
AbstractAudioDriver : The concrete implementation of the preferred
audio driver for this platform.
AbstractAudioDriver : The concrete implementation of the preferred
audio driver for this platform.
"""
global _audio_driver
@ -118,4 +78,5 @@ def _delete_audio_driver():
_audio_driver = None
_audio_driver = None
atexit.register(_delete_audio_driver)

View File

@ -989,7 +989,6 @@ class TextLayout:
self._document = document
self._init_document()
@property
def batch(self):
"""The Batch that this Layout is assigned to.
@ -1326,21 +1325,6 @@ class TextLayout:
self._update_enabled = True
self._update()
@property
def dpi(self):
"""Get DPI used by this layout.
Read-only.
:type: float
"""
return self._dpi
@dpi.setter
def dpi(self, value):
self._dpi = value
self._update()
def delete(self):
"""Remove this layout from its batch.
"""

View File

@ -367,7 +367,6 @@ class BaseWindow(with_metaclass(_WindowMetaclass, EventDispatcher)):
# Instance variables accessible only via properties
_width = None
_height = None
_dpi = 96
_caption = None
_resizable = False
_style = WINDOW_STYLE_DEFAULT
@ -385,9 +384,6 @@ class BaseWindow(with_metaclass(_WindowMetaclass, EventDispatcher)):
# Used to restore window size and position after fullscreen
_windowed_size = None
_windowed_location = None
# Used to tell if window is currently being resized.
_window_resizing = False
_minimum_size = None
_maximum_size = None
@ -951,20 +947,7 @@ class BaseWindow(with_metaclass(_WindowMetaclass, EventDispatcher)):
if width < 1 or height < 1:
raise ValueError('width and height must be positive integers')
def on_scale(self, scale, dpi):
"""A default scale event handler.
This default handler is called if the screen or system's DPI changes
during runtime.
"""
pass
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 set_maximum_size(self, width: int, height: int) -> None:
"""Set the maximum size of the window.
@ -1277,24 +1260,8 @@ class BaseWindow(with_metaclass(_WindowMetaclass, EventDispatcher)):
self.set_size(self.width, new_height)
@property
def scale(self):
"""The scale of the window factoring in DPI. Read only.
:type: float
"""
return self._dpi / 96
@property
def dpi(self):
"""DPI values of the Window. Read only.
:type: list
"""
return self._dpi
@property
def projection(self):
"""The OpenGL window projection. Read-write.
def size(self):
"""The size of the window. Read-Write.
:type: tuple
"""
@ -1465,7 +1432,24 @@ class BaseWindow(with_metaclass(_WindowMetaclass, EventDispatcher)):
self._width, self._height = width, height
def get_size(self):
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.
@ -1475,6 +1459,21 @@ class BaseWindow(with_metaclass(_WindowMetaclass, EventDispatcher)):
"""
return self._width, self._height
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 set_location(self, x, y):
"""Set the position of the window.
@ -1489,19 +1488,6 @@ class BaseWindow(with_metaclass(_WindowMetaclass, EventDispatcher)):
"""
raise NotImplementedError('abstract')
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.
@ -2026,20 +2012,6 @@ class BaseWindow(with_metaclass(_WindowMetaclass, EventDispatcher)):
:event:
"""
def on_resize_stop(self, width, height):
"""The window is done being resized.
Called when the window is done resizing.
: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.
@ -2156,8 +2128,6 @@ BaseWindow.register_event_type('on_mouse_leave')
BaseWindow.register_event_type('on_close')
BaseWindow.register_event_type('on_expose')
BaseWindow.register_event_type('on_resize')
BaseWindow.register_event_type('on_resize_stop')
BaseWindow.register_event_type('on_scale')
BaseWindow.register_event_type('on_move')
BaseWindow.register_event_type('on_activate')
BaseWindow.register_event_type('on_deactivate')

View File

@ -8,7 +8,7 @@ from pyglet.event import EventDispatcher
from pyglet.canvas.cocoa import CocoaCanvas
from pyglet.libs.darwin import cocoapy, CGPoint, NSDeviceResolution
from pyglet.libs.darwin import cocoapy, CGPoint, AutoReleasePool
from .systemcursor import SystemCursor
from .pyglet_delegate import PygletDelegate
@ -155,11 +155,9 @@ class CocoaWindow(BaseWindow):
self._nswindow.setContentView_(self._nsview)
self._nswindow.makeFirstResponder_(self._nsview)
# Then create a view and set it as our NSWindow's content view.
self._nsview = PygletView.alloc().initWithFrame_cocoaWindow_(content_rect, self)
self._nsview.setWantsBestResolutionOpenGLSurface_(1 if pyglet.options["scale_with_dpi"] else 0)
self._nswindow.setContentView_(self._nsview)
self._nswindow.makeFirstResponder_(self._nsview)
# Create a canvas with the view as its drawable and attach context to it.
self.canvas = CocoaCanvas(self.display, self.screen, self._nsview)
self.context.attach(self.canvas)
# Configure the window.
self._nswindow.setAcceptsMouseMovedEvents_(True)
@ -181,38 +179,10 @@ class CocoaWindow(BaseWindow):
array = NSArray.arrayWithObject_(cocoapy.NSPasteboardTypeURL)
self._nsview.registerForDraggedTypes_(array)
self._dpi = self._get_dpi_desc()
# TODO: Add support for file drops.
if self._file_drops:
raise NotImplementedError("File drops are not implemented on MacOS")
self.context.update_geometry()
self.switch_to()
self.set_vsync(self._vsync)
self.set_visible(self._visible)
pool.drain()
def _get_dpi_desc(self):
if pyglet.options["scale_with_dpi"] and self._nswindow:
desc = self._nswindow.deviceDescription()
rsize = desc.objectForKey_(NSDeviceResolution).sizeValue()
dpi = int(rsize.width)
return dpi
return 72
@property
def scale(self):
"""The scale of the window factoring in DPI. Read only.
:type: float
"""
if pyglet.options["scale_with_dpi"] and self._nswindow:
return self._nswindow.backingScaleFactor()
return 1.0
self.context.update_geometry()
self.switch_to()
self.set_vsync(self._vsync)
self.set_visible(self._visible)
def _set_nice_window_location(self):
# Construct a list of all visible windows that aren't us.
@ -389,10 +359,8 @@ class CocoaWindow(BaseWindow):
def get_framebuffer_size(self):
view = self.context._nscontext.view()
bounds = view.bounds()
if pyglet.options["scale_with_dpi"]:
bounds = view.convertRectToBacking_(bounds)
return int(bounds.size.width), int(bounds.size.height)
bounds = view.convertRectToBacking_(view.bounds()).size
return int(bounds.width), int(bounds.height)
def set_size(self, width: int, height: int) -> None:
super().set_size(width, height)

View File

@ -3,13 +3,12 @@ from pyglet.libs.darwin.cocoapy import NSApplicationDidHideNotification
from pyglet.libs.darwin.cocoapy import NSApplicationDidUnhideNotification
from pyglet.libs.darwin.cocoapy import send_super, get_selector
from pyglet.libs.darwin.cocoapy import PyObjectEncoding
from pyglet.libs.darwin.cocoapy import quartz, appkit
from pyglet.libs.darwin.cocoapy import quartz
from .systemcursor import SystemCursor
NSNotificationCenter = ObjCClass('NSNotificationCenter')
NSApplication = ObjCClass('NSApplication')
NSBackingPropertyOldScaleFactorKey = c_void_p.in_dll(appkit, 'NSBackingPropertyOldScaleFactorKey')
class PygletDelegate_Implementation:
PygletDelegate = ObjCSubclass('NSObject', 'PygletDelegate')
@ -129,14 +128,5 @@ class PygletDelegate_Implementation:
return not self._window._keyboard_exclusive
return True
@PygletDelegate.method('v@')
def windowDidChangeBackingProperties_(self, notification):
user_info = notification.userInfo()
old_scale = user_info.objectForKey_(NSBackingPropertyOldScaleFactorKey)
new_scale = self._window._nswindow.backingScaleFactor()
if old_scale.doubleValue() != new_scale:
self._window.dispatch_event("on_scale", new_scale, self._window._get_dpi_desc())
PygletDelegate = ObjCClass('PygletDelegate')

View File

@ -100,7 +100,6 @@ class Win32Window(BaseWindow):
self._always_dwm = sys.getwindowsversion() >= (6, 2)
self._interval = 0
self._timer = None
super(Win32Window, self).__init__(*args, **kwargs)
@ -135,19 +134,12 @@ class Win32Window(BaseWindow):
else:
self._ws_style &= ~(WS_THICKFRAME | WS_MAXIMIZEBOX)
self._dpi = self._screen.get_dpi()
if self._fullscreen:
width = self.screen.width
height = self.screen.height
else:
if pyglet.options["scale_with_dpi"]:
if self.scale != 1.0:
self._width = int(self._width * self.scale)
self._height = int(self._height * self.scale)
width, height = \
self._client_to_window_size(self._width, self._height, self._dpi)
self._client_to_window_size(self._width, self._height)
if not self._window_class:
module = _kernel32.GetModuleHandleW(None)
@ -375,9 +367,8 @@ class Win32Window(BaseWindow):
return point.x, point.y
def set_size(self, width, height):
if self._fullscreen:
raise WindowException('Cannot set size of fullscreen window.')
width, height = self._client_to_window_size_dpi(width, height)
super().set_size(width, height)
width, height = self._client_to_window_size(width, height)
_user32.SetWindowPos(self._hwnd, 0, 0, 0, width, height,
(SWP_NOZORDER | SWP_NOMOVE | SWP_NOOWNERZORDER))
self.dispatch_event('on_resize', width, height)
@ -417,13 +408,6 @@ class Win32Window(BaseWindow):
def maximize(self):
_user32.ShowWindow(self._hwnd, SW_MAXIMIZE)
def get_window_screen(self):
""" Gets the current screen the window is on.
If between monitors will retrieve the screen with the most screen space.
"""
handle = _user32.MonitorFromWindow(self._hwnd, MONITOR_DEFAULTTONEAREST)
return [screen for screen in self.display.get_screens() if screen._handle == handle][0]
def set_caption(self, caption):
self._caption = caption
_user32.SetWindowTextW(self._hwnd, c_wchar_p(caption))
@ -670,41 +654,15 @@ class Win32Window(BaseWindow):
return icon
# Private util
def _client_to_window_size(self, width, height, dpi):
"""This returns the true window size factoring in styles, borders, title bars"""
def _client_to_window_size(self, width, height):
rect = RECT()
rect.left = 0
rect.top = 0
rect.right = width
rect.bottom = height
if WINDOWS_10_ANNIVERSARY_UPDATE_OR_GREATER:
_user32.AdjustWindowRectExForDpi(byref(rect),
self._ws_style, False, self._ex_ws_style, dpi)
else:
_user32.AdjustWindowRectEx(byref(rect),
self._ws_style, False, self._ex_ws_style)
return rect.right - rect.left, rect.bottom - rect.top
def _client_to_window_size_dpi(self, width, height):
""" This returns the true window size factoring in styles, borders, title bars.
Retrieves DPI directly from the Window hwnd, used after window creation.
"""
rect = RECT()
rect.left = 0
rect.top = 0
rect.right = width
rect.bottom = height
if WINDOWS_10_ANNIVERSARY_UPDATE_OR_GREATER:
_user32.AdjustWindowRectExForDpi(byref(rect),
self._ws_style, False, self._ex_ws_style, _user32.GetDpiForWindow(self._hwnd))
else:
_user32.AdjustWindowRectEx(byref(rect),
self._ws_style, False, self._ex_ws_style)
_user32.AdjustWindowRectEx(byref(rect),
self._ws_style, False, self._ex_ws_style)
return rect.right - rect.left, rect.bottom - rect.top
def _client_to_window_pos(self, x, y):
@ -712,14 +670,7 @@ class Win32Window(BaseWindow):
rect.left = x
rect.top = y
_user32.AdjustWindowRectEx(byref(rect),
self._ws_style, False, self._ex_ws_style)
if WINDOWS_10_ANNIVERSARY_UPDATE_OR_GREATER:
_user32.AdjustWindowRectExForDpi(byref(rect),
self._ws_style, False, self._ex_ws_style, _user32.GetDpiForWindow(self._hwnd))
else:
_user32.AdjustWindowRectEx(byref(rect),
self._ws_style, False, self._ex_ws_style)
self._ws_style, False, self._ex_ws_style)
return rect.left, rect.top
# Event dispatching
@ -1100,7 +1051,6 @@ class Win32Window(BaseWindow):
def _event_sizing(self, msg, wParam, lParam):
# rect = cast(lParam, POINTER(RECT)).contents
# width, height = self.get_size()
self._window_resizing = True
from pyglet import app
if app.event_loop is not None:
@ -1149,54 +1099,39 @@ class Win32Window(BaseWindow):
if app.event_loop is not None:
app.event_loop.enter_blocking()
@Win32EventHandler(WM_TIMER)
def _event_timer(self, msg, wParam, lParam):
"""This allows events and rendering to occur even when the window is moving."""
if not self._window_resizing:
from pyglet import app
if app.event_loop is not None:
app.event_loop.idle()
@Win32EventHandler(WM_MOVE)
def _event_move(self, msg, wParam, lParam):
x, y = self._get_location(lParam)
self.dispatch_event('on_move', x, y)
return 0
@Win32EventHandler(WM_SETCURSOR)
def _event_setcursor(self, msg, wParam, lParam):
if self._exclusive_mouse and not self._mouse_platform_visible:
lo, hi = self._get_location(lParam)
if lo == HTCLIENT: # In frame
self._set_cursor_visibility(False)
return 1
elif lo in (HTCAPTION, HTCLOSE, HTMAXBUTTON, HTMINBUTTON): # Allow in
self._set_cursor_visibility(True)
return 1
@Win32EventHandler(WM_ENTERSIZEMOVE)
def _event_entersizemove(self, msg, wParam, lParam):
self._timer = _user32.SetTimer(self._hwnd, 100, 25, None)
@Win32EventHandler(WM_EXITSIZEMOVE)
def _event_exitsizemove(self, msg, wParam, lParam):
self._moving = True
from pyglet import app
if self._timer:
_user32.KillTimer(self._hwnd, 100)
self._timer = None
if app.event_loop is not None:
app.event_loop.exit_blocking()
# _window_resizing is needed in combination with WM_EXITSIZEMOVE as
# it is also called when the Window is done being moved, not just resized.
if self._window_resizing is True:
self.dispatch_event('on_resize_stop', self._width, self._height)
self._window_resizing = False
@Win32EventHandler(WM_EXITSIZEMOVE)
def _event_exitsizemove(self, msg, wParam, lParam):
self._moving = False
from pyglet import app
if app.event_loop is not None:
app.event_loop.exit_blocking()
# Alternative to using WM_SETFOCUS and WM_KILLFOCUS. Which
# is better?
"""
@Win32EventHandler(WM_ACTIVATE)
def _event_activate(self, msg, wParam, lParam):
if wParam & 0xffff == WA_INACTIVE:
self.dispatch_event('on_deactivate')
else:
self.dispatch_event('on_activate')
_user32.SetFocus(self._hwnd)
return 0
"""
if self._exclusive_mouse:
self._update_clipped_cursor()
@Win32EventHandler(WM_SETFOCUS)
def _event_setfocus(self, msg, wParam, lParam):
@ -1237,15 +1172,15 @@ class Win32Window(BaseWindow):
@Win32EventHandler(WM_GETMINMAXINFO)
def _event_getminmaxinfo(self, msg, wParam, lParam):
"""Used to determine the minimum or maximum sized window if configured."""
info = MINMAXINFO.from_address(lParam)
if self._minimum_size:
info.ptMinTrackSize.x, info.ptMinTrackSize.y = \
self._client_to_window_size_dpi(*self._minimum_size)
self._client_to_window_size(*self._minimum_size)
if self._maximum_size:
info.ptMaxTrackSize.x, info.ptMaxTrackSize.y = \
self._client_to_window_size_dpi(*self._maximum_size)
self._client_to_window_size(*self._maximum_size)
return 0
@Win32EventHandler(WM_ERASEBKGND)
@ -1289,51 +1224,5 @@ class Win32Window(BaseWindow):
self.dispatch_event('on_file_drop', point.x, self._height - point.y, paths)
return 0
@Win32EventHandler(WM_GETDPISCALEDSIZE)
def _event_dpi_scaled_size(self, msg, wParam, lParam):
if pyglet.options["scale_with_dpi"]:
return None
size = cast(lParam, POINTER(SIZE)).contents
dpi = wParam
if WINDOWS_10_CREATORS_UPDATE_OR_GREATER:
current = RECT()
result = RECT()
# Size between current size and future.
_user32.AdjustWindowRectExForDpi(byref(current),
self._ws_style, False, self._ex_ws_style,
_user32.GetDpiForWindow(self._hwnd))
_user32.AdjustWindowRectExForDpi(byref(result),
self._ws_style, False, self._ex_ws_style, dpi)
size.cx += (result.right - result.left) - (current.right - current.left)
size.cy += (result.bottom - result.top) - (current.bottom - current.top)
return 1
@Win32EventHandler(WM_DPICHANGED)
def _event_dpi_change(self, msg, wParam, lParam):
y_dpi, x_dpi = self._get_location(wParam)
scale = x_dpi / USER_DEFAULT_SCREEN_DPI
if not self._fullscreen and\
(pyglet.options["scale_with_dpi"] or WINDOWS_10_CREATORS_UPDATE_OR_GREATER):
suggested_rect = cast(lParam, POINTER(RECT)).contents
x = suggested_rect.left
y = suggested_rect.top
width = suggested_rect.right - suggested_rect.left
height = suggested_rect.bottom - suggested_rect.top
_user32.SetWindowPos(self._hwnd, 0,
x, y, width, height, SWP_NOZORDER | SWP_NOOWNERZORDER | SWP_NOACTIVATE)
self._dpi = x_dpi
self.switch_to()
self.dispatch_event('on_scale', scale, x_dpi)
return 1
__all__ = ["Win32EventHandler", "Win32Window"]

View File

@ -214,7 +214,10 @@ class XlibWindow(BaseWindow):
# unconditionally.
mask = xlib.CWColormap | xlib.CWBitGravity | xlib.CWBackPixel
self._dpi = self._screen.get_dpi()
if self.style in ('transparent', 'overlay'):
mask |= xlib.CWBorderPixel
window_attributes.border_pixel = 0
window_attributes.background_pixel = 0
if self._fullscreen:
width, height = self.screen.width, self.screen.height
@ -222,11 +225,6 @@ class XlibWindow(BaseWindow):
self._view_y = (height - self._height) // 2
else:
width, height = self._width, self._height
if pyglet.options["scale_with_dpi"]:
if self.scale != 1.0:
self._width = width = int(self._width * self.scale)
self._height = height = int(self._height * self.scale)
self._view_x = self._view_y = 0
self._window = xlib.XCreateWindow(self._x_display, root,