core/window: better handling of system size. window size can now be changed, opengl is automatically reloaded on the first resize (the first initialization doesn't count.)

+ fix issue with mouse_up never fired after a resize
This commit is contained in:
Mathieu Virbel 2012-03-05 17:03:23 +01:00
parent d05777e954
commit d45219d5b8
3 changed files with 124 additions and 102 deletions

View File

@ -12,13 +12,14 @@ from os.path import join, exists
from os import getcwd
from kivy.core import core_select_lib
from kivy.clock import Clock
from kivy.config import Config
from kivy.logger import Logger
from kivy.base import EventLoop
from kivy.modules import Modules
from kivy.event import EventDispatcher
from kivy.properties import ListProperty, ObjectProperty, AliasProperty, \
NumericProperty
NumericProperty, OptionProperty, StringProperty
# late import
VKeyboard = None
@ -331,13 +332,32 @@ class WindowBase(EventDispatcher):
degrees.
'''
def _set_system_size(self, size):
self._size = size
def _get_system_size(self):
return self._size
system_size = AliasProperty(_get_system_size, None, bind=('_size', ))
system_size = AliasProperty(_get_system_size, _set_system_size, bind=('_size', ))
'''Real size of the window, without taking care of the rotation.
'''
fullscreen = OptionProperty(False, options=(True, False, 'auto', 'fake'))
'''If true, the window will be put in fullscreen mode, "auto". That's mean
the screen size will not change, and use the current one to set the app
fullscreen
.. versionadded:: 1.1.2
'''
top = NumericProperty(None, allownone=True)
left = NumericProperty(None, allownone=True)
position = OptionProperty('auto', options=['auto', 'custom'])
render_context = ObjectProperty(None)
canvas = ObjectProperty(None)
title = StringProperty('Kivy')
def __new__(cls, **kwargs):
if cls.__instance is None:
cls.__instance = EventDispatcher.__new__(cls)
@ -346,12 +366,12 @@ class WindowBase(EventDispatcher):
def __init__(self, **kwargs):
kwargs.setdefault('force', False)
kwargs.setdefault('config', None)
# don't init window 2 times,
# except if force is specified
if self.__initialized and not kwargs.get('force'):
if WindowBase.__instance is not None and not kwargs.get('force'):
return
self.initialized = False
# event subsystem
self.register_event_type('on_draw')
@ -371,7 +391,41 @@ class WindowBase(EventDispatcher):
self.register_event_type('on_key_up')
self.register_event_type('on_dropfile')
super(WindowBase, self).__init__()
# create a trigger for update/create the window when one of window
# property changes
self.trigger_create_window = Clock.create_trigger(self.create_window, -1)
# set the default window parameter according to the configuration
if 'fullscreen' not in kwargs:
fullscreen = Config.get('graphics', 'fullscreen')
if fullscreen not in ('auto', 'fake'):
fullscreen = fullscreen.lower() in ('true', '1', 'yes', 'yup')
kwargs['fullscreen'] = fullscreen
if 'width' not in kwargs:
kwargs['width'] = Config.getint('graphics', 'width')
if 'height' not in kwargs:
kwargs['height'] = Config.getint('graphics', 'height')
if 'rotation' not in kwargs:
kwargs['rotation'] = Config.getint('graphics', 'rotation')
if 'position' not in kwargs:
kwargs['position'] = Config.get('graphics', 'position', 'auto')
if 'top' in kwargs:
kwargs['position'] = 'custom'
kwargs['top'] = kwargs['top']
else:
kwargs['top'] = Config.getint('graphics', 'top')
if 'left' in kwargs:
kwargs['position'] = 'custom'
kwargs['left'] = kwargs['left']
else:
kwargs['left'] = Config.getint('graphics', 'left')
kwargs['_size'] = (kwargs.pop('width'), kwargs.pop('height'))
super(WindowBase, self).__init__(**kwargs)
# bind all the properties that need to recreate the window
for prop in ('fullscreen', 'position', 'top', 'left', '_size', 'system_size'):
self.bind(**{prop: self.trigger_create_window})
# init privates
self._system_keyboard = Keyboard(window=self)
@ -381,55 +435,10 @@ class WindowBase(EventDispatcher):
self.children = []
self.parent = self
# add view
if 'view' in kwargs:
self.add_widget(kwargs.get('view'))
# get window params, user options before config option
params = {}
if 'fullscreen' in kwargs:
params['fullscreen'] = kwargs.get('fullscreen')
else:
params['fullscreen'] = Config.get('graphics', 'fullscreen')
if params['fullscreen'] not in ('auto', 'fake'):
params['fullscreen'] = params['fullscreen'].lower() in \
('true', '1', 'yes', 'yup')
if 'width' in kwargs:
params['width'] = kwargs.get('width')
else:
params['width'] = Config.getint('graphics', 'width')
if 'height' in kwargs:
params['height'] = kwargs.get('height')
else:
params['height'] = Config.getint('graphics', 'height')
if 'rotation' in kwargs:
params['rotation'] = kwargs.get('rotation')
else:
params['rotation'] = Config.getint('graphics', 'rotation')
params['position'] = Config.get(
'graphics', 'position', 'auto')
if 'top' in kwargs:
params['position'] = 'custom'
params['top'] = kwargs.get('top')
else:
params['top'] = Config.getint('graphics', 'top')
if 'left' in kwargs:
params['position'] = 'custom'
params['left'] = kwargs.get('left')
else:
params['left'] = Config.getint('graphics', 'left')
# before creating the window
__import__('kivy.core.gl')
# configure the window
self.params = params
self.create_window()
# attach modules + listener event
@ -441,7 +450,7 @@ class WindowBase(EventDispatcher):
self.configure_keyboards()
# mark as initialized
self.__initialized = True
self.initialized = True
def toggle_fullscreen(self):
'''Toggle fullscreen on window'''
@ -451,7 +460,7 @@ class WindowBase(EventDispatcher):
'''Close the window'''
pass
def create_window(self):
def create_window(self, *largs):
'''Will create the main window and configure it.
.. warning::
@ -469,14 +478,32 @@ class WindowBase(EventDispatcher):
Again, don't use this method unless you know exactly what you are
doing !
'''
from kivy.core.gl import init_gl
init_gl()
# just to be sure, if the trigger is set, and if this method is manually
# called, unset the trigger
Clock.unschedule(self.create_window)
# create the render context and canvas
from kivy.graphics import RenderContext, Canvas
self.render_context = RenderContext()
self.canvas = Canvas()
self.render_context.add(self.canvas)
print 'create window !!'
if not self.initialized:
from kivy.core.gl import init_gl
init_gl()
# create the render context and canvas, only the first time.
from kivy.graphics import RenderContext, Canvas
self.render_context = RenderContext()
self.canvas = Canvas()
self.render_context.add(self.canvas)
else:
# if we get initialized more than once, then reload opengl state
# after the second time.
from kivy.graphics.opengl_utils import gl_reload
gl_reload()
def ask_update(dt):
self.canvas.ask_update()
Clock.schedule_once(ask_update, 0)
# ensure the gl viewport is correct
self.update_viewport()
def on_flip(self):
'''Flip between buffers (event)'''
@ -530,7 +557,7 @@ class WindowBase(EventDispatcher):
.. versionadded:: 1.0.5
'''
pass
self.title = title
def set_icon(self, filename):
'''Set the icon of the window

View File

@ -32,9 +32,12 @@ glReadPixels = GL_RGBA = GL_UNSIGNED_BYTE = None
class WindowPygame(WindowBase):
def create_window(self):
params = self.params
def create_window(self, *largs):
# ensure the mouse is still not up after window creation, otherwise, we
# have some weird bugs
self.dispatch('on_mouse_up', 0, 0, 'all', [])
print 'window create'
# force display to show (available only for fullscreen)
displayidx = Config.getint('graphics', 'display')
if not 'SDL_VIDEO_FULLSCREEN_HEAD' in environ and displayidx != -1:
@ -47,11 +50,12 @@ class WindowPygame(WindowBase):
# right now, activate resizable window only on linux.
# on window / macosx, the opengl context is lost, and we need to
# reconstruct everything. Check #168 for a state of the work.
if platform() == 'linux':
if platform() in ('linux', 'macosx', 'win'):
self.flags |= pygame.RESIZABLE
self.flags |= pygame.RESIZABLE
try:
pygame.display.quit()
pygame.display.init()
except pygame.error, e:
raise CoreCriticalException(e.message)
@ -65,18 +69,17 @@ class WindowPygame(WindowBase):
pygame.display.gl_set_attribute(pygame.GL_DEPTH_SIZE, 16)
pygame.display.gl_set_attribute(pygame.GL_STENCIL_SIZE, 1)
pygame.display.gl_set_attribute(pygame.GL_ALPHA_SIZE, 8)
pygame.display.set_caption('kivy')
pygame.display.set_caption(self.title)
if params['position'] == 'auto':
if self.position == 'auto':
self._pos = None
elif params['position'] == 'custom':
self._pos = params['left'], params['top']
elif self.position == 'custom':
self._pos = self.left, self.top
else:
raise ValueError('position token in configuration accept only '
'"auto" or "custom"')
self._fullscreenmode = params['fullscreen']
if self._fullscreenmode == 'fake':
if self.fullscreen == 'fake':
Logger.debug('WinPygame: Set window to fake fullscreen mode')
self.flags |= pygame.NOFRAME
# if no position set, in fake mode, we always need to set the
@ -85,7 +88,7 @@ class WindowPygame(WindowBase):
self._pos = (0, 0)
environ['SDL_VIDEO_WINDOW_POS'] = '%d,%d' % self._pos
elif self._fullscreenmode:
elif self.fullscreen is True:
Logger.debug('WinPygame: Set window to fullscreen mode')
self.flags |= pygame.FULLSCREEN
@ -111,10 +114,6 @@ class WindowPygame(WindowBase):
except:
Logger.exception('Window: cannot set icon')
# init ourself size + setmode
# before calling on_resize
self._size = params['width'], params['height']
# try to use mode with multisamples
try:
self._pygame_set_mode()
@ -133,15 +132,16 @@ class WindowPygame(WindowBase):
else:
raise CoreCriticalException(e.message)
info = pygame.display.Info()
self._size = (info.current_w, info.current_h)
#self.dispatch('on_resize', *self._size)
super(WindowPygame, self).create_window()
# set mouse visibility
pygame.mouse.set_visible(
Config.getboolean('graphics', 'show_cursor'))
# set rotation
self.rotation = params['rotation']
# if we are on android platform, automaticly create hooks
if android:
from kivy.support import install_android
@ -151,8 +151,9 @@ class WindowPygame(WindowBase):
pygame.display.quit()
self.dispatch('on_close')
def set_title(self, title):
pygame.display.set_caption(title)
def on_title(self, instance, value):
if self.initialized:
pygame.display.set_caption(self.title)
def set_icon(self, filename):
try:
@ -220,6 +221,9 @@ class WindowPygame(WindowBase):
if event.buttons == (0, 0, 0):
continue
x, y = event.pos
self._mouse_x = x
self._mouse_y = y
self._mouse_meta = self.modifiers
self.dispatch('on_mouse_move', x, y, self.modifiers)
# mouse action
@ -239,6 +243,11 @@ class WindowPygame(WindowBase):
eventname = 'on_mouse_down'
if event.type == pygame.MOUSEBUTTONUP:
eventname = 'on_mouse_up'
self._mouse_x = x
self._mouse_y = y
self._mouse_meta = self.modifiers
self._mouse_btn = btn
self._mouse_down = eventname == 'on_mouse_down'
self.dispatch(eventname, x, y, btn, self.modifiers)
# keyboard action
@ -261,11 +270,7 @@ class WindowPygame(WindowBase):
# video resize
elif event.type == pygame.VIDEORESIZE:
self._size = event.size
# don't use trigger here, we want to delay the resize event
cb = self._do_resize
Clock.unschedule(cb)
Clock.schedule_once(cb, .1)
self.system_size = event.size
elif event.type == pygame.VIDEOEXPOSE:
self.canvas.ask_update()
@ -285,16 +290,7 @@ class WindowPygame(WindowBase):
Logger.debug('WinPygame: Unhandled event %s' % str(event))
'''
def _do_resize(self, dt):
Logger.debug('Window: Resize window to %s' % str(self._size))
self._pygame_set_mode(self._size)
self.dispatch('on_resize', *self._size)
def mainloop(self):
# don't known why, but pygame required a resize event
# for opengl, before mainloop... window reinit ?
self.dispatch('on_resize', *self.size)
while not EventLoop.quit and EventLoop.status == 'started':
try:
self._mainloop()
@ -312,22 +308,15 @@ class WindowPygame(WindowBase):
# force deletion of window
pygame.display.quit()
def _set_size(self, size):
if super(WindowPygame, self)._set_size(size):
self._pygame_set_mode()
return True
size = property(WindowBase._get_size, _set_size)
#
# Pygame wrapper
#
def _pygame_set_mode(self, size=None):
print '>>> pygame set mode', self._size, self.fullscreen, self.flags
if size is None:
size = self.size
if self._fullscreenmode == 'auto':
if self.fullscreen == 'auto':
pygame.display.set_mode((0, 0), self.flags)
info = pygame.display.Info()
self._size = (info.current_w, info.current_h)
else:
pygame.display.set_mode(size, self.flags)

View File

@ -181,6 +181,12 @@ class MouseMotionEventProvider(MotionEventProvider):
return True
def on_mouse_release(self, win, x, y, button, modifiers):
# special case, if button is all, then remove all the current mouses.
if button == 'all':
for cur in self.touches.values()[:]:
self.remove_touch(cur)
self.current_drag = None
width, height = EventLoop.window.system_size
rx = x / float(width)
ry = 1. - y / float(height)