From d45219d5b868518b9c72bb8d10c12e267fb45d11 Mon Sep 17 00:00:00 2001 From: Mathieu Virbel Date: Mon, 5 Mar 2012 17:03:23 +0100 Subject: [PATCH] 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 --- kivy/core/window/__init__.py | 147 ++++++++++++++++++------------ kivy/core/window/window_pygame.py | 73 +++++++-------- kivy/input/providers/mouse.py | 6 ++ 3 files changed, 124 insertions(+), 102 deletions(-) diff --git a/kivy/core/window/__init__.py b/kivy/core/window/__init__.py index c235deeea..7b4e7ec9f 100755 --- a/kivy/core/window/__init__.py +++ b/kivy/core/window/__init__.py @@ -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 diff --git a/kivy/core/window/window_pygame.py b/kivy/core/window/window_pygame.py index ff3dd2bfa..3218a9119 100644 --- a/kivy/core/window/window_pygame.py +++ b/kivy/core/window/window_pygame.py @@ -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) diff --git a/kivy/input/providers/mouse.py b/kivy/input/providers/mouse.py index f343c1cae..c2b362b00 100644 --- a/kivy/input/providers/mouse.py +++ b/kivy/input/providers/mouse.py @@ -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)