Kivy-1.9.0/0000775000175000017500000000000012507221737012433 5ustar titotito00000000000000Kivy-1.9.0/setup.cfg0000664000175000017500000000010512263260206014241 0ustar titotito00000000000000[nosetests] with-coverage=1 cover-package=kivy with-id=1 verbosity=2 Kivy-1.9.0/LICENSE0000664000175000017500000000207112462275426013444 0ustar titotito00000000000000Copyright (c) 2010-2015 Kivy Team and other contributors Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. Kivy-1.9.0/PKG-INFO0000664000175000017500000000345112507221737013533 0ustar titotito00000000000000Metadata-Version: 1.1 Name: Kivy Version: 1.9.0 Summary: A software library for rapid development of hardware-accelerated multitouch applications. Home-page: http://kivy.org/ Author: Kivy Crew Author-email: kivy-dev@googlegroups.com License: MIT Description: UNKNOWN Platform: UNKNOWN Classifier: Development Status :: 5 - Production/Stable Classifier: Environment :: MacOS X Classifier: Environment :: Win32 (MS Windows) Classifier: Environment :: X11 Applications Classifier: Intended Audience :: Developers Classifier: Intended Audience :: End Users/Desktop Classifier: Intended Audience :: Information Technology Classifier: Intended Audience :: Science/Research Classifier: License :: OSI Approved :: MIT License Classifier: Natural Language :: English Classifier: Operating System :: MacOS :: MacOS X Classifier: Operating System :: Microsoft :: Windows Classifier: Operating System :: POSIX :: BSD :: FreeBSD Classifier: Operating System :: POSIX :: Linux Classifier: Programming Language :: Python :: 2.7 Classifier: Programming Language :: Python :: 3.3 Classifier: Programming Language :: Python :: 3.4 Classifier: Topic :: Artistic Software Classifier: Topic :: Games/Entertainment Classifier: Topic :: Multimedia :: Graphics :: 3D Rendering Classifier: Topic :: Multimedia :: Graphics :: Capture :: Digital Camera Classifier: Topic :: Multimedia :: Graphics :: Presentation Classifier: Topic :: Multimedia :: Graphics :: Viewers Classifier: Topic :: Multimedia :: Sound/Audio :: Players :: MP3 Classifier: Topic :: Multimedia :: Video :: Display Classifier: Topic :: Scientific/Engineering :: Human Machine Interfaces Classifier: Topic :: Scientific/Engineering :: Visualization Classifier: Topic :: Software Development :: Libraries :: Application Frameworks Classifier: Topic :: Software Development :: User Interfaces Kivy-1.9.0/kivy/0000775000175000017500000000000012507221737013415 5ustar titotito00000000000000Kivy-1.9.0/kivy/network/0000775000175000017500000000000012507221737015106 5ustar titotito00000000000000Kivy-1.9.0/kivy/network/urlrequest.py0000664000175000017500000004233312462275426017704 0ustar titotito00000000000000''' Url Request =========== .. versionadded:: 1.0.8 You can use the :class:`UrlRequest` to make asynchronous requests on the web and get the result when the request is completed. The spirit is the same as the XHR object in Javascript. The content is also decoded if the Content-Type is application/json and the result automatically passed through json.loads. The syntax to create a request:: from kivy.network.urlrequest import UrlRequest req = UrlRequest(url, on_success, on_redirect, on_failure, on_error, on_progress, req_body, req_headers, chunk_size, timeout, method, decode, debug, file_path) Only the first argument is mandatory: the rest are optional. By default, a "GET" request will be sent. If the :attr:`UrlRequest.req_body` is not None, a "POST" request will be sent. It's up to you to adjust :attr:`UrlRequest.req_headers` to suit your requirements and the response to the request will be accessible as the parameter called "result" on the callback function of the on_success event. Example of fetching weather in Paris:: def got_weather(req, results): for key, value in results['weather'][0].items(): print(key, ': ', value) req = UrlRequest( 'http://api.openweathermap.org/data/2.5/weather?q=Paris,fr', got_weather) Example of Posting data (adapted from httplib example):: import urllib def bug_posted(req, result): print('Our bug is posted !') print(result) params = urllib.urlencode({'@number': 12524, '@type': 'issue', '@action': 'show'}) headers = {'Content-type': 'application/x-www-form-urlencoded', 'Accept': 'text/plain'} req = UrlRequest('bugs.python.org', on_success=bug_posted, req_body=params, req_headers=headers) If you want a synchronous request, you can call the wait() method. ''' from collections import deque from threading import Thread from json import loads from time import sleep from kivy.compat import PY2 if PY2: from httplib import HTTPConnection from urlparse import urlparse else: from http.client import HTTPConnection from urllib.parse import urlparse try: HTTPSConnection = None if PY2: from httplib import HTTPSConnection else: from http.client import HTTPSConnection except ImportError: # depending the platform, if openssl support wasn't compiled before python, # this class is not available. pass from kivy.clock import Clock from kivy.weakmethod import WeakMethod from kivy.logger import Logger # list to save UrlRequest and prevent GC on un-referenced objects g_requests = [] class UrlRequest(Thread): '''A UrlRequest. See module documentation for usage. .. versionchanged:: 1.5.1 Add `debug` parameter .. versionchanged:: 1.0.10 Add `method` parameter :Parameters: `url`: str Complete url string to call. `on_success`: callback(request, result) Callback function to call when the result has been fetched. `on_redirect`: callback(request, result) Callback function to call if the server returns a Redirect. `on_failure`: callback(request, result) Callback function to call if the server returns a Client or Server Error. `on_error`: callback(request, error) Callback function to call if an error occurs. `on_progress`: callback(request, current_size, total_size) Callback function that will be called to report progression of the download. `total_size` might be -1 if no Content-Length has been reported in the http response. This callback will be called after each `chunk_size` is read. `req_body`: str, defaults to None Data to sent in the request. If it's not None, a POST will be done instead of a GET. `req_headers`: dict, defaults to None Custom headers to add to the request. `chunk_size`: int, defaults to 8192 Size of each chunk to read, used only when `on_progress` callback has been set. If you decrease it too much, a lot of on_progress callbacks will be fired and will slow down your download. If you want to have the maximum download speed, increase the chunk_size or don't use ``on_progress``. `timeout`: int, defaults to None If set, blocking operations will timeout after this many seconds. `method`: str, defaults to 'GET' (or 'POST' if ``body`` is specified) The HTTP method to use. `decode`: bool, defaults to True If False, skip decoding of the response. `debug`: bool, defaults to False If True, it will use the Logger.debug to print information about url access/progression/errors. `file_path`: str, defaults to None If set, the result of the UrlRequest will be written to this path instead of in memory. .. versionchanged:: 1.8.0 Parameter `decode` added. Parameter `file_path` added. Parameter `on_redirect` added. Parameter `on_failure` added. ''' def __init__(self, url, on_success=None, on_redirect=None, on_failure=None, on_error=None, on_progress=None, req_body=None, req_headers=None, chunk_size=8192, timeout=None, method=None, decode=True, debug=False, file_path=None): super(UrlRequest, self).__init__() self._queue = deque() self._trigger_result = Clock.create_trigger(self._dispatch_result, 0) self.daemon = True self.on_success = WeakMethod(on_success) if on_success else None self.on_redirect = WeakMethod(on_redirect) if on_redirect else None self.on_failure = WeakMethod(on_failure) if on_failure else None self.on_error = WeakMethod(on_error) if on_error else None self.on_progress = WeakMethod(on_progress) if on_progress else None self.decode = decode self.file_path = file_path self._debug = debug self._result = None self._error = None self._is_finished = False self._resp_status = None self._resp_headers = None self._resp_length = -1 self._chunk_size = chunk_size self._timeout = timeout self._method = method #: Url of the request self.url = url #: Request body passed in __init__ self.req_body = req_body #: Request headers passed in __init__ self.req_headers = req_headers # save our request to prevent GC g_requests.append(self) self.start() def run(self): q = self._queue.appendleft url = self.url req_body = self.req_body req_headers = self.req_headers try: result, resp = self._fetch_url(url, req_body, req_headers, q) if self.decode: result = self.decode_result(result, resp) except Exception as e: q(('error', None, e)) else: q(('success', resp, result)) # using trigger can result in a missed on_success event self._trigger_result() # clean ourself when the queue is empty while len(self._queue): sleep(.1) self._trigger_result() # ok, authorize the GC to clean us. if self in g_requests: g_requests.remove(self) def _fetch_url(self, url, body, headers, q): # Parse and fetch the current url trigger = self._trigger_result chunk_size = self._chunk_size report_progress = self.on_progress is not None timeout = self._timeout file_path = self.file_path if self._debug: Logger.debug('UrlRequest: {0} Fetch url <{1}>'.format( id(self), url)) Logger.debug('UrlRequest: {0} - body: {1}'.format( id(self), body)) Logger.debug('UrlRequest: {0} - headers: {1}'.format( id(self), headers)) # parse url parse = urlparse(url) # translate scheme to connection class cls = self.get_connection_for_scheme(parse.scheme) # correctly determine host/port port = None host = parse.netloc.split(':') if len(host) > 1: port = int(host[1]) host = host[0] # create connection instance args = {} if timeout is not None: args['timeout'] = timeout req = cls(host, port, **args) # reconstruct path to pass on the request path = parse.path if parse.params: path += ';' + parse.params if parse.query: path += '?' + parse.query if parse.fragment: path += '#' + parse.fragment # send request method = self._method if method is None: method = 'GET' if body is None else 'POST' req.request(method, path, body, headers or {}) # read header resp = req.getresponse() # read content if report_progress or file_path is not None: try: total_size = int(resp.getheader('content-length')) except: total_size = -1 # before starting the download, send a fake progress to permit the # user to initialize his ui if report_progress: q(('progress', resp, (0, total_size))) def get_chunks(fd=None): bytes_so_far = 0 result = b'' while 1: chunk = resp.read(chunk_size) if not chunk: break if fd: fd.write(chunk) else: result += chunk bytes_so_far += len(chunk) # report progress to user if report_progress: q(('progress', resp, (bytes_so_far, total_size))) trigger() return bytes_so_far, result if file_path is not None: with open(file_path, 'wb') as fd: bytes_so_far, result = get_chunks(fd) else: bytes_so_far, result = get_chunks() # ensure that restults are dispatched for the last chunk, # avoid trigger if report_progress: q(('progress', resp, (bytes_so_far, total_size))) trigger() else: result = resp.read() try: if isinstance(result, bytes): result = result.decode('utf-8') except UnicodeDecodeError: # if it's an image? decoding would not work pass req.close() # return everything return result, resp def get_connection_for_scheme(self, scheme): '''Return the Connection class for a particular scheme. This is an internal function that can be expanded to support custom schemes. Actual supported schemes: http, https. ''' if scheme == 'http': return HTTPConnection elif scheme == 'https' and HTTPSConnection is not None: return HTTPSConnection else: raise Exception('No class for scheme %s' % scheme) def decode_result(self, result, resp): '''Decode the result fetched from url according to his Content-Type. Currently supports only application/json. ''' # Entry to decode url from the content type. # For example, if the content type is a json, it will be automatically # decoded. content_type = resp.getheader('Content-Type', None) if content_type is not None: ct = content_type.split(';')[0] if ct == 'application/json': try: return loads(result) except: return result return result def _dispatch_result(self, dt): while True: # Read the result pushed on the queue, and dispatch to the client try: result, resp, data = self._queue.pop() except IndexError: return if resp: # XXX usage of dict can be dangerous if multiple headers # are set even if it's invalid. But it look like it's ok # ? http://stackoverflow.com/questions/2454494/.. # ..urllib2-multiple-set-cookie-headers-in-response self._resp_headers = dict(resp.getheaders()) self._resp_status = resp.status if result == 'success': status_class = resp.status // 100 if status_class in (1, 2): if self._debug: Logger.debug('UrlRequest: {0} Download finished with' ' {1} datalen'.format(id(self), len(data))) self._is_finished = True self._result = data if self.on_success: func = self.on_success() if func: func(self, data) elif status_class == 3: if self._debug: Logger.debug('UrlRequest: {} Download ' 'redirected'.format(id(self))) self._is_finished = True self._result = data if self.on_redirect: func = self.on_redirect() if func: func(self, data) elif status_class in (4, 5): if self._debug: Logger.debug('UrlRequest: {} Download failed with ' 'http error {}'.format(id(self), resp.status)) self._is_finished = True self._result = data if self.on_failure: func = self.on_failure() if func: func(self, data) elif result == 'error': if self._debug: Logger.debug('UrlRequest: {0} Download error ' '<{1}>'.format(id(self), data)) self._is_finished = True self._error = data if self.on_error: func = self.on_error() if func: func(self, data) elif result == 'progress': if self._debug: Logger.debug('UrlRequest: {0} Download progress ' '{1}'.format(id(self), data)) if self.on_progress: func = self.on_progress() if func: func(self, data[0], data[1]) else: assert(0) @property def is_finished(self): '''Return True if the request has finished, whether it's a success or a failure. ''' return self._is_finished @property def result(self): '''Return the result of the request. This value is not determined until the request is finished. ''' return self._result @property def resp_headers(self): '''If the request has been completed, return a dictionary containing the headers of the response. Otherwise, it will return None. ''' return self._resp_headers @property def resp_status(self): '''Return the status code of the response if the request is complete, otherwise return None. ''' return self._resp_status @property def error(self): '''Return the error of the request. This value is not determined until the request is completed. ''' return self._error @property def chunk_size(self): '''Return the size of a chunk, used only in "progress" mode (when on_progress callback is set.) ''' return self._chunk_size def wait(self, delay=0.5): '''Wait for the request to finish (until :attr:`resp_status` is not None) .. note:: This method is intended to be used in the main thread, and the callback will be dispatched from the same thread from which you're calling. .. versionadded:: 1.1.0 ''' while self.resp_status is None: self._dispatch_result(delay) sleep(delay) if __name__ == '__main__': from pprint import pprint def on_success(req, result): pprint('Got the result:') pprint(result) def on_error(req, error): pprint('Got an error:') pprint(error) req = UrlRequest('http://en.wikipedia.org/w/api.php?format' '=json&action=query&titles=Kivy&prop=revisions&rvprop=content', on_success, on_error) while not req.is_finished: sleep(1) Clock.tick() print('result =', req.result) print('error =', req.error) Kivy-1.9.0/kivy/network/__init__.py0000664000175000017500000000024412276430013017207 0ustar titotito00000000000000''' Network support =============== Kivy currently supports basic, asynchronous network requests. Please refer to :class:`kivy.network.urlrequest.UrlRequest`. ''' Kivy-1.9.0/kivy/factory_registers.py0000664000175000017500000002045312462275426017535 0ustar titotito00000000000000# Auto-generated file by setup.py build_factory from kivy.factory import Factory r = Factory.register r('Adapter', module='kivy.adapters.adapter') r('ListAdapter', module='kivy.adapters.listadapter') r('SimpleListAdapter', module='kivy.adapters.simplelistadapter') r('DictAdapter', module='kivy.adapters.dictadapter') r('SelectableDataItem', module='kivy.adapters.models') r('Animation', module='kivy.animation') r('AnimationTransition', module='kivy.animation') r('ExceptionHandler', module='kivy.base') r('Cache', module='kivy.cache') r('ClockBase', module='kivy.clock') r('ColorPicker', module='kivy.uix.colorpicker') r('ColorWheel', module='kivy.uix.colorpicker') r('ConfigParser', module='kivy.config') r('EventDispatcher', module='kivy.event') r('Observable', module='kivy.event') r('FactoryException', module='kivy.factory') r('Gesture', module='kivy.gesture') r('GestureDatabase', module='kivy.gesture') r('GesturePoint', module='kivy.gesture') r('GestureStroke', module='kivy.gesture') r('Parser', module='kivy.lang') r('LoaderBase', module='kivy.loader') r('ProxyImage', module='kivy.loader') r('LoggerHistory', module='kivy.logger') r('NumericProperty', module='kivy.properties') r('StringProperty', module='kivy.properties') r('ListProperty', module='kivy.properties') r('ObjectProperty', module='kivy.properties') r('BooleanProperty', module='kivy.properties') r('BoundedNumericProperty', module='kivy.properties') r('OptionProperty', module='kivy.properties') r('ReferenceListProperty', module='kivy.properties') r('AliasProperty', module='kivy.properties') r('NumericProperty', module='kivy.properties') r('Property', module='kivy.properties') r('SafeList', module='kivy.utils') r('Vector', module='kivy.vector') r('Color', module='kivy.graphics.context_instructions') r('BindTexture', module='kivy.graphics.context_instructions') r('PushMatrix', module='kivy.graphics.context_instructions') r('PopMatrix', module='kivy.graphics.context_instructions') r('Rotate', module='kivy.graphics.context_instructions') r('Scale', module='kivy.graphics.context_instructions') r('Translate', module='kivy.graphics.context_instructions') r('MatrixInstruction', module='kivy.graphics.context_instructions') r('Fbo', module='kivy.graphics.fbo') r('Instruction', module='kivy.graphics.instructions') r('InstructionGroup', module='kivy.graphics.instructions') r('ContextInstruction', module='kivy.graphics.instructions') r('VertexInstruction', module='kivy.graphics.instructions') r('Canvas', module='kivy.graphics.instructions') r('CanvasBase', module='kivy.graphics.instructions') r('RenderContext', module='kivy.graphics.instructions') r('Shader', module='kivy.graphics.shader') r('Texture', module='kivy.graphics.texture') r('TextureRegion', module='kivy.graphics.texture') r('Matrix', module='kivy.graphics.transformation') r('VBO', module='kivy.graphics.vbo') r('VertexBatch', module='kivy.graphics.vbo') r('StencilPush', module='kivy.graphics.stencil_instructions') r('StencilPop', module='kivy.graphics.stencil_instructions') r('StencilUse', module='kivy.graphics.stencil_instructions') r('StencilUnUse', module='kivy.graphics.stencil_instructions') r('Triangle', module='kivy.graphics.vertex_instructions') r('Quad', module='kivy.graphics.vertex_instructions') r('Rectangle', module='kivy.graphics.vertex_instructions') r('BorderImage', module='kivy.graphics.vertex_instructions') r('Ellipse', module='kivy.graphics.vertex_instructions') r('Line', module='kivy.graphics.vertex_instructions') r('SmoothLine', module='kivy.graphics.vertex_instructions') r('Point', module='kivy.graphics.vertex_instructions') r('Bezier', module='kivy.graphics.vertex_instructions') r('Mesh', module='kivy.graphics.vertex_instructions') r('Svg', module='kivy.graphics.svg') r('MotionEventFactory', module='kivy.input.factory') r('MotionEventProvider', module='kivy.input.provider') r('Shape', module='kivy.input.shape') r('ShapeRect', module='kivy.input.shape') r('ActionBar', module='kivy.uix.actionbar') r('ActionItem', module='kivy.uix.actionbar') r('ActionButton', module='kivy.uix.actionbar') r('ActionToggleButton', module='kivy.uix.actionbar') r('ActionCheck', module='kivy.uix.actionbar') r('ActionSeparator', module='kivy.uix.actionbar') r('ActionDropDown', module='kivy.uix.actionbar') r('ActionGroup', module='kivy.uix.actionbar') r('ActionOverflow', module='kivy.uix.actionbar') r('ActionView', module='kivy.uix.actionbar') r('ContextualActionView', module='kivy.uix.actionbar') r('AnchorLayout', module='kivy.uix.anchorlayout') r('BoxLayout', module='kivy.uix.boxlayout') r('GridLayout', module='kivy.uix.gridlayout') r('PageLayout', module='kivy.uix.pagelayout') r('Accordion', module='kivy.uix.accordion') r('AccordionItem', module='kivy.uix.accordion') r('Button', module='kivy.uix.button') r('ButtonBehavior', module='kivy.uix.behaviors') r('ToggleButtonBehavior', module='kivy.uix.behaviors') r('DragBehavior', module='kivy.uix.behaviors') r('FocusBehavior', module='kivy.uix.behaviors') r('CompoundSelectionBehavior', module='kivy.uix.behaviors') r('Bubble', module='kivy.uix.bubble') r('BubbleButton', module='kivy.uix.bubble') r('Camera', module='kivy.uix.camera') r('Carousel', module='kivy.uix.carousel') r('CodeInput', module='kivy.uix.codeinput') r('CheckBox', module='kivy.uix.checkbox') r('DropDown', module='kivy.uix.dropdown') r('EffectWidget', module='kivy.uix.effectwidget') r('FloatLayout', module='kivy.uix.floatlayout') r('RelativeLayout', module='kivy.uix.relativelayout') r('ScatterLayout', module='kivy.uix.scatterlayout') r('ScatterPlaneLayout', module='kivy.uix.scatterlayout') r('FileChooserListView', module='kivy.uix.filechooser') r('FileChooserIconView', module='kivy.uix.filechooser') r('FileChooser', module='kivy.uix.filechooser') r('Image', module='kivy.uix.image') r('AsyncImage', module='kivy.uix.image') r('Label', module='kivy.uix.label') r('Layout', module='kivy.uix.layout') r('AbstractView', module='kivy.uix.abstractview') r('CompositeListItem', module='kivy.uix.listview') r('ListItemButton', module='kivy.uix.listview') r('ListItemLabel', module='kivy.uix.listview') r('ListView', module='kivy.uix.listview') r('SelectableView', module='kivy.uix.listview') r('ModalView', module='kivy.uix.modalview') r('ProgressBar', module='kivy.uix.progressbar') r('Popup', module='kivy.uix.popup') r('Scatter', module='kivy.uix.scatter') r('ScatterPlane', module='kivy.uix.scatter') r('ScrollView', module='kivy.uix.scrollview') r('Settings', module='kivy.uix.settings') r('Slider', module='kivy.uix.slider') r('Screen', module='kivy.uix.screenmanager') r('ScreenManager', module='kivy.uix.screenmanager') r('Spinner', module='kivy.uix.spinner') r('Splitter', module='kivy.uix.splitter') r('StackLayout', module='kivy.uix.stacklayout') r('StencilView', module='kivy.uix.stencilview') r('Switch', module='kivy.uix.switch') r('TabbedPanel', module='kivy.uix.tabbedpanel') r('TabbedPanelHeader', module='kivy.uix.tabbedpanel') r('TextInput', module='kivy.uix.textinput') r('ToggleButton', module='kivy.uix.togglebutton') r('TreeView', module='kivy.uix.treeview') r('TreeViewLabel', module='kivy.uix.treeview') r('TreeViewNode', module='kivy.uix.treeview') r('ShaderTransition', module='kivy.uix.screenmanager') r('SlideTransition', module='kivy.uix.screenmanager') r('SwapTransition', module='kivy.uix.screenmanager') r('WipeTransition', module='kivy.uix.screenmanager') r('FadeTransition', module='kivy.uix.screenmanager') r('Sandbox', module='kivy.uix.sandbox') r('Video', module='kivy.uix.video') r('VideoPlayer', module='kivy.uix.videoplayer') r('VideoPlayerVolume', module='kivy.uix.videoplayer') r('VideoPlayerStop', module='kivy.uix.videoplayer') r('VideoPlayerPlayPause', module='kivy.uix.videoplayer') r('VideoPlayerProgressBar', module='kivy.uix.videoplayer') r('VKeyboard', module='kivy.uix.vkeyboard') r('Widget', module='kivy.uix.widget') r('WidgetException', module='kivy.uix.widget') r('RstDocument', module='kivy.uix.rst') r('KineticEffect', module='kivy.effects.kinetic') r('ScrollEffect', module='kivy.effects.scroll') r('DampedScrollEffect', module='kivy.effects.dampedscroll') r('OpacityScrollEffect', module='kivy.effects.opacityscroll') r('Recognizer', module='kivy.multistroke') r('MultistrokeGesture', module='kivy.multistroke') r('UnistrokeTemplate', module='kivy.multistroke') r('ProgressTracker', module='kivy.multistroke') r('GestureSurface', module='kivy.uix.gesturesurface') r('GestureContainer', module='kivy.uix.gesturesurface') Kivy-1.9.0/kivy/core/0000775000175000017500000000000012507221737014345 5ustar titotito00000000000000Kivy-1.9.0/kivy/core/text/0000775000175000017500000000000012507221737015331 5ustar titotito00000000000000Kivy-1.9.0/kivy/core/text/text_pil.py0000664000175000017500000000322612462275426017542 0ustar titotito00000000000000''' Text PIL: Draw text with PIL ''' __all__ = ('LabelPIL', ) try: from PIL import Image, ImageFont, ImageDraw except: raise from kivy.compat import text_type from kivy.core.text import LabelBase from kivy.core.image import ImageData # used for fetching extends before creature image surface default_font = ImageFont.load_default() class LabelPIL(LabelBase): _cache = {} def _select_font(self): fontsize = int(self.options['font_size']) fontname = self.options['font_name_r'] try: id = '%s.%s' % (text_type(fontname), text_type(fontsize)) except UnicodeDecodeError: id = '%s.%s' % (fontname, fontsize) if not id in self._cache: font = ImageFont.truetype(fontname, fontsize) self._cache[id] = font return self._cache[id] def get_extents(self, text): font = self._select_font() w, h = font.getsize(text) return w, h def get_cached_extents(self): return self._select_font().getsize def _render_begin(self): # create a surface, context, font... self._pil_im = Image.new('RGBA', self._size) self._pil_draw = ImageDraw.Draw(self._pil_im) def _render_text(self, text, x, y): color = tuple([int(c * 255) for c in self.options['color']]) self._pil_draw.text((int(x), int(y)), text, font=self._select_font(), fill=color) def _render_end(self): data = ImageData(self._size[0], self._size[1], self._pil_im.mode.lower(), self._pil_im.tostring()) del self._pil_im del self._pil_draw return data Kivy-1.9.0/kivy/core/text/text_layout.pxd0000664000175000017500000000047312462275426020437 0ustar titotito00000000000000 cdef class LayoutWord: cdef public object text cdef public int lw, lh cdef public dict options cdef class LayoutLine: cdef public int x, y, w, h cdef public int line_wrap # whether this line wraps from last line cdef public int is_last_line # in a paragraph cdef public list words Kivy-1.9.0/kivy/core/text/_text_sdl2.pyx0000664000175000017500000000763312471133021020137 0ustar titotito00000000000000#cython: c_string_type=unicode, c_string_encoding=utf8 ''' TODO: - ensure that we correctly check allocation - remove compat sdl usage (like SDL_SetAlpha must be replaced with sdl 1.3 call, not 1.2) ''' include '../../lib/sdl2.pxi' from libc.string cimport memset from kivy.core.image import ImageData from kivy.compat import PY2 cdef dict sdl2_cache = {} cdef list sdl2_cache_order = [] cdef class _TTFContainer: cdef TTF_Font* font def __cinit__(self): self.font = NULL def __dealloc__(self): if self.font != NULL: TTF_CloseFont(self.font) self.font = NULL cdef class _SurfaceContainer: cdef SDL_Surface* surface cdef int w, h def __cinit__(self, w, h): self.surface = NULL self.w = w self.h = h def __init__(self, w, h): # XXX check on OSX to see if little endian/big endian make a difference # here. self.surface = SDL_CreateRGBSurface(0, w, h, 32, 0x000000ff, 0x0000ff00, 0x00ff0000, 0xff000000) memset(self.surface.pixels, 0, w * h * 4) def __dealloc__(self): if self.surface != NULL: SDL_FreeSurface(self.surface) self.surface = NULL def render(self, container, text, x, y): cdef TTF_Font *font = _get_font(container) cdef SDL_Color c cdef SDL_Surface *st cdef SDL_Rect r cdef list color = list(container.options['color']) if font == NULL: return c.r = (color[0] * 255) c.g = (color[1] * 255) c.b = (color[2] * 255) bytes_text = text.encode('utf-8') st = TTF_RenderUTF8_Blended(font, bytes_text, c) if st == NULL: return r.x = x r.y = y r.w = st.w r.h = st.h SDL_SetSurfaceAlphaMod(st, 0xff); SDL_SetSurfaceBlendMode(st, SDL_BLENDMODE_NONE); SDL_BlitSurface(st, NULL, self.surface, &r) SDL_FreeSurface(st) def get_data(self): cdef int datalen = self.surface.w * self.surface.h * 4 cdef bytes pixels = (self.surface.pixels)[:datalen] data = ImageData(self.w, self.h, 'rgba', pixels) return data cdef TTF_Font *_get_font(self): cdef TTF_Font *fontobject = NULL cdef _TTFContainer ttfc cdef char *error cdef str s_error # fast path fontid = self._get_font_id() if fontid in sdl2_cache: ttfc = sdl2_cache[fontid] return ttfc.font # ensure ttf is init. if not TTF_WasInit(): TTF_Init() # try first the file if it's a filename fontname = self.options['font_name_r'] bytes_fontname = fontname.encode('utf-8') ext = fontname.rsplit('.', 1) if len(ext) == 2: # try to open the fount if it has an extension fontobject = TTF_OpenFont(bytes_fontname, int(self.options['font_size'])) # fallback to search a system font if fontobject == NULL: s_error = (SDL_GetError()).encode('utf-8') print(s_error) assert(0) sdl2_cache[fontid] = ttfc = _TTFContainer() ttfc.font = fontobject sdl2_cache_order.append(fontid) # to prevent too much file open, limit the number of opened fonts to 64 while len(sdl2_cache_order) > 64: popid = sdl2_cache_order.pop(0) ttfc = sdl2_cache[popid] del sdl2_cache[popid] ttfc = sdl2_cache[fontid] return ttfc.font def _get_extents(container, text): cdef TTF_Font *font = _get_font(container) cdef int w, h if font == NULL: return 0, 0 if not PY2: text = text.encode('utf-8') bytes_text = text TTF_SizeUTF8(font, bytes_text, &w, &h) return w, h def _get_fontdescent(container): return TTF_FontDescent(_get_font(container)) def _get_fontascent(container): return TTF_FontAscent(_get_font(container)) Kivy-1.9.0/kivy/core/text/markup.py0000664000175000017500000007510412465721151017207 0ustar titotito00000000000000''' Text Markup =========== .. versionadded:: 1.1.0 We provide a simple text-markup for inline text styling. The syntax look the same as the `BBCode `_. A tag is defined as ``[tag]``, and might have a closed tag associated: ``[/tag]``. Example of a markup text:: [b]Hello [color=ff0000]world[/b][/color] The following tags are availables: ``[b][/b]`` Activate bold text ``[i][/i]`` Activate italic text ``[font=][/font]`` Change the font ``[size=][/size]`` Change the font size ``[color=#][/color]`` Change the text color ``[ref=][/ref]`` Add an interactive zone. The reference + all the word box inside the reference will be available in :attr:`MarkupLabel.refs` ``[anchor=]`` Put an anchor in the text. You can get the position of your anchor within the text with :attr:`MarkupLabel.anchors` ``[sub][/sub]`` Display the text at a subscript position relative to the text before it. ``[sup][/sup]`` Display the text at a superscript position relative to the text before it. If you need to escape the markup from the current text, use :func:`kivy.utils.escape_markup`. ''' __all__ = ('MarkupLabel', ) import re from kivy.properties import dpi2px from kivy.parser import parse_color from kivy.logger import Logger from kivy.core.text import Label, LabelBase from kivy.core.text.text_layout import layout_text, LayoutWord, LayoutLine from copy import copy from math import ceil from functools import partial # We need to do this trick when documentation is generated MarkupLabelBase = Label if Label is None: MarkupLabelBase = LabelBase class MarkupLabel(MarkupLabelBase): '''Markup text label. See module documentation for more informations. ''' def __init__(self, *largs, **kwargs): self._style_stack = {} self._refs = {} self._anchors = {} super(MarkupLabel, self).__init__(*largs, **kwargs) self._internal_size = 0, 0 self._cached_lines = [] @property def refs(self): '''Get the bounding box of all the ``[ref=...]``:: { 'refA': ((x1, y1, x2, y2), (x1, y1, x2, y2)), ... } ''' return self._refs @property def anchors(self): '''Get the position of all the ``[anchor=...]``:: { 'anchorA': (x, y), 'anchorB': (x, y), ... } ''' return self._anchors @property def markup(self): '''Return the text with all the markup splitted:: >>> MarkupLabel('[b]Hello world[/b]').markup >>> ('[b]', 'Hello world', '[/b]') ''' s = re.split('(\[.*?\])', self.label) s = [x for x in s if x != ''] return s def _push_style(self, k): if not k in self._style_stack: self._style_stack[k] = [] self._style_stack[k].append(self.options[k]) def _pop_style(self, k): if k not in self._style_stack or len(self._style_stack[k]) == 0: Logger.warning('Label: pop style stack without push') return v = self._style_stack[k].pop() self.options[k] = v def render(self, real=False): options = copy(self.options) if not real: ret = self._pre_render() else: ret = self._real_render() self.options = options return ret def _pre_render(self): # split markup, words, and lines # result: list of word with position and width/height # during the first pass, we don't care about h/valign self._cached_lines = lines = [] self._refs = {} self._anchors = {} clipped = False w = h = 0 uw, uh = self.text_size spush = self._push_style spop = self._pop_style opts = options = self.options options['_ref'] = None options['_anchor'] = None options['script'] = 'normal' shorten = options['shorten'] # if shorten, then don't split lines to fit uw, because it will be # flattened later when shortening and broken up lines if broken # mid-word will have space mid-word when lines are joined uw_temp = None if shorten else uw xpad = options['padding_x'] uhh = (None if uh is not None and options['valign'][-1] != 'p' or options['shorten'] else uh) options['strip'] = options['strip'] or options['halign'][-1] == 'y' for item in self.markup: if item == '[b]': spush('bold') options['bold'] = True self.resolve_font_name() elif item == '[/b]': spop('bold') self.resolve_font_name() elif item == '[i]': spush('italic') options['italic'] = True self.resolve_font_name() elif item == '[/i]': spop('italic') self.resolve_font_name() elif item[:6] == '[size=': item = item[6:-1] try: if item[-2:] in ('px', 'pt', 'in', 'cm', 'mm', 'dp', 'sp'): size = dpi2px(item[:-2], item[-2:]) else: size = int(item) except ValueError: raise size = options['font_size'] spush('font_size') options['font_size'] = size elif item == '[/size]': spop('font_size') elif item[:7] == '[color=': color = parse_color(item[7:-1]) spush('color') options['color'] = color elif item == '[/color]': spop('color') elif item[:6] == '[font=': fontname = item[6:-1] spush('font_name') options['font_name'] = fontname self.resolve_font_name() elif item == '[/font]': spop('font_name') self.resolve_font_name() elif item[:5] == '[sub]': spush('font_size') spush('script') options['font_size'] = options['font_size'] * .5 options['script'] = 'subscript' elif item == '[/sub]': spop('font_size') spop('script') elif item[:5] == '[sup]': spush('font_size') spush('script') options['font_size'] = options['font_size'] * .5 options['script'] = 'superscript' elif item == '[/sup]': spop('font_size') spop('script') elif item[:5] == '[ref=': ref = item[5:-1] spush('_ref') options['_ref'] = ref elif item == '[/ref]': spop('_ref') elif not clipped and item[:8] == '[anchor=': options['_anchor'] = item[8:-1] elif not clipped: item = item.replace('&bl;', '[').replace( '&br;', ']').replace('&', '&') opts = copy(options) extents = self.get_cached_extents() opts['space_width'] = extents(' ')[0] w, h, clipped = layout_text(item, lines, (w, h), (uw_temp, uhh), opts, extents, True, False) if len(lines): # remove any trailing spaces from the last line old_opts = self.options self.options = copy(opts) w, h, clipped = layout_text('', lines, (w, h), (uw_temp, uhh), self.options, self.get_cached_extents(), True, True) self.options = old_opts if shorten: options['_ref'] = None # no refs for you! options['_anchor'] = None w, h, lines = self.shorten_post(lines, w, h) self._cached_lines = lines # when valign is not top, for markup we layout everything (text_size[1] # is temporarily set to None) and after layout cut to size if too tall elif uh != uhh and h > uh and len(lines) > 1: if options['valign'][-1] == 'm': # bottom i = 0 while i < len(lines) - 1 and h > uh: h -= lines[i].h i += 1 del lines[:i] else: # middle i = 0 top = int(h / 2. + uh / 2.) # remove extra top portion while i < len(lines) - 1 and h > top: h -= lines[i].h i += 1 del lines[:i] i = len(lines) - 1 # remove remaining bottom portion while i and h > uh: h -= lines[i].h i -= 1 del lines[i + 1:] # now justify the text if options['halign'][-1] == 'y' and uw is not None: # XXX: update refs to justified pos # when justify, each line shouldv'e been stripped already split = partial(re.split, re.compile('( +)')) uww = uw - 2 * xpad chr = type(self.text) space = chr(' ') empty = chr('') for i in range(len(lines)): line = lines[i] words = line.words # if there's nothing to justify, we're done if (not line.w or int(uww - line.w) <= 0 or not len(words) or line.is_last_line): continue done = False parts = [None, ] * len(words) # contains words split by space idxs = [None, ] * len(words) # indices of the space in parts # break each word into spaces and add spaces until it's full # do first round of split in case we don't need to split all for w in range(len(words)): word = words[w] sw = word.options['space_width'] p = parts[w] = split(word.text) idxs[w] = [v for v in range(len(p)) if p[v].startswith(' ')] # now we have the indices of the spaces in split list for k in idxs[w]: # try to add single space at each space if line.w + sw > uww: done = True break line.w += sw word.lw += sw p[k] += space if done: break # there's not a single space in the line? if not any(idxs): continue # now keep adding spaces to already split words until done while not done: for w in range(len(words)): if not idxs[w]: continue word = words[w] sw = word.options['space_width'] p = parts[w] for k in idxs[w]: # try to add single space at each space if line.w + sw > uww: done = True break line.w += sw word.lw += sw p[k] += space if done: break # if not completely full, push last words to right edge diff = int(uww - line.w) if diff > 0: # find the last word that had a space for w in range(len(words) - 1, -1, -1): if not idxs[w]: continue break old_opts = self.options self.options = word.options word = words[w] # split that word into left/right and push right till uww l_text = empty.join(parts[w][:idxs[w][-1]]) r_text = empty.join(parts[w][idxs[w][-1]:]) left = LayoutWord(word.options, self.get_extents(l_text)[0], word.lh, l_text) right = LayoutWord(word.options, self.get_extents(r_text)[0], word.lh, r_text) left.lw = max(left.lw, word.lw + diff - right.lw) self.options = old_opts # now put words back together with right/left inserted for k in range(len(words)): if idxs[k]: words[k].text = empty.join(parts[k]) words[w] = right words.insert(w, left) else: for k in range(len(words)): if idxs[k]: words[k].text = empty.join(parts[k]) line.w = uww w = max(w, uww) self._internal_size = w, h if uw: w = uw if uh: h = uh if h > 1 and w < 2: w = 2 if w < 1: w = 1 if h < 1: h = 1 return int(w), int(h) def _real_render(self): lines = self._cached_lines options = None for line in lines: if len(line.words): # get opts from first line, first word options = line.words[0].options break if not options: # there was no text to render self._render_begin() data = self._render_end() assert(data) if data is not None and data.width > 1: self.texture.blit_data(data) return old_opts = self.options render_text = self._render_text xpad, ypad = options['padding_x'], options['padding_y'] x, y = xpad, ypad # pos in the texture iw, ih = self._internal_size # the real size of text, not texture w, h = self.size halign = options['halign'] valign = options['valign'] refs = self._refs anchors = self._anchors self._render_begin() if valign == 'bottom': y = h - ih + ypad elif valign == 'middle': y = int((h - ih) / 2 + ypad) for layout_line in lines: # for plain label each line has only one str lw, lh = layout_line.w, layout_line.h x = xpad if halign[0] == 'c': # center x = int((w - lw) / 2.) elif halign[0] == 'r': # right x = max(0, int(w - lw - xpad)) layout_line.x = x layout_line.y = y psp = pph = 0 for word in layout_line.words: options = self.options = word.options # the word height is not scaled by line_height, only lh was wh = options['line_height'] * word.lh # calculate sub/super script pos if options['script'] == 'superscript': script_pos = max(0, psp if psp else self.get_descent()) psp = script_pos pph = wh elif options['script'] == 'subscript': script_pos = min(lh - wh, ((psp + pph) - wh) if pph else (lh - wh)) pph = wh psp = script_pos else: script_pos = (lh - wh) / 1.25 psp = pph = 0 if len(word.text): render_text(word.text, x, y + script_pos) # should we record refs ? ref = options['_ref'] if ref is not None: if not ref in refs: refs[ref] = [] refs[ref].append((x, y, x + word.lw, y + wh)) # Should we record anchors? anchor = options['_anchor'] if anchor is not None: if not anchor in anchors: anchors[anchor] = (x, y) x += word.lw y += lh self.options = old_opts # get data from provider data = self._render_end() assert(data) # If the text is 1px width, usually, the data is black. # Don't blit that kind of data, otherwise, you have a little black bar. if data is not None and data.width > 1: self.texture.blit_data(data) def shorten_post(self, lines, w, h, margin=2): ''' Shortens the text to a single line according to the label options. This function operates on a text that has already been laid out because for markup, parts of text can have different size and options. If :attr:`text_size` [0] is None, the lines are returned unchanged. Otherwise, the lines are converted to a single line fitting within the constrained width, :attr:`text_size` [0]. :params: `lines`: list of `LayoutLine` instances describing the text. `w`: int, the width of the text in lines, including padding. `h`: int, the height of the text in lines, including padding. `margin` int, the additional space left on the sides. This is in addition to :attr:`padding_x`. :returns: 3-tuple of (xw, h, lines), where w, and h is similar to the input and contains the resulting width / height of the text, including padding. lines, is a list containing a single `LayoutLine`, which contains the words for the line. ''' def n(line, c): ''' A function similar to text.find, except it's an iterator that returns successive occurrences of string c in list line. line is not a string, but a list of LayoutWord instances that we walk from left to right returning the indices of c in the words as we encounter them. Note that the options can be different among the words. :returns: 3-tuple: the index of the word in line, the index of the occurrence in word, and the extents (width) of the combined words until this occurrence, not including the occurrence char. If no more are found it returns (-1, -1, total_w) where total_w is the full width of all the words. ''' total_w = 0 for w in range(len(line)): word = line[w] if not word.lw: continue f = partial(word.text.find, c) i = f() while i != -1: self.options = word.options yield w, i, total_w + self.get_extents(word.text[:i])[0] i = f(i + 1) self.options = word.options total_w += self.get_extents(word.text)[0] yield -1, -1, total_w # this should never be reached, really def p(line, c): ''' Similar to the `n` function, except it returns occurrences of c from right to left in the list, line, similar to rfind. ''' total_w = 0 offset = 0 if len(c) else 1 for w in range(len(line) - 1, -1, -1): word = line[w] if not word.lw: continue f = partial(word.text.rfind, c) i = f() while i != -1: self.options = word.options yield (w, i, total_w + self.get_extents(word.text[i + 1:])[0]) if i: i = f(0, i - offset) else: if not c: self.options = word.options yield (w, -1, total_w + self.get_extents(word.text)[0]) break self.options = word.options total_w += self.get_extents(word.text)[0] yield -1, -1, total_w # this should never be reached, really def n_restricted(line, uw, c): ''' Similar to the function `n`, except it only returns the first occurrence and it's not an iterator. Furthermore, if the first occurrence doesn't fit within width uw, it returns the index of whatever amount of text will still fit in uw. :returns: similar to the function `n`, except it's a 4-tuple, with the last element a boolean, indicating if we had to clip the text to fit in uw (True) or if the whole text until the first occurrence fitted in uw (False). ''' total_w = 0 if not len(line): return 0, 0, 0 for w in range(len(line)): word = line[w] f = partial(word.text.find, c) self.options = word.options extents = self.get_cached_extents() i = f() if i != -1: ww = extents(word.text[:i])[0] if i != -1 and total_w + ww <= uw: # found and it fits return w, i, total_w + ww, False elif i == -1: ww = extents(word.text)[0] if total_w + ww <= uw: # wasn't found and all fits total_w += ww continue i = len(word.text) # now just find whatever amount of the word does fit e = 0 while e != i and total_w + extents(word.text[:e])[0] <= uw: e += 1 e = max(0, e - 1) return w, e, total_w + extents(word.text[:e])[0], True return -1, -1, total_w, False def p_restricted(line, uw, c): ''' Similar to `n_restricted`, except it returns the first occurrence starting from the right, like `p`. ''' total_w = 0 if not len(line): return 0, 0, 0 for w in range(len(line) - 1, -1, -1): word = line[w] f = partial(word.text.rfind, c) self.options = word.options extents = self.get_cached_extents() i = f() if i != -1: ww = extents(word.text[i + 1:])[0] if i != -1 and total_w + ww <= uw: # found and it fits return w, i, total_w + ww, False elif i == -1: ww = extents(word.text)[0] if total_w + ww <= uw: # wasn't found and all fits total_w += ww continue # now just find whatever amount of the word does fit s = len(word.text) - 1 while s >= 0 and total_w + extents(word.text[s:])[0] <= uw: s -= 1 return w, s, total_w + extents(word.text[s + 1:])[0], True return -1, -1, total_w, False textwidth = self.get_cached_extents() uw = self.text_size[0] if uw is None: return w, h, lines old_opts = copy(self.options) uw = max(0, int(uw - old_opts['padding_x'] * 2 - margin)) chr = type(self.text) ssize = textwidth(' ') c = old_opts['split_str'] line_height = old_opts['line_height'] xpad, ypad = old_opts['padding_x'], old_opts['padding_y'] dir = old_opts['shorten_from'][0] # flatten lines into single line line = [] last_w = 0 for l in range(len(lines)): # concatenate (non-empty) inside lines with a space this_line = lines[l] if last_w and this_line.w and not this_line.line_wrap: line.append(LayoutWord(old_opts, ssize[0], ssize[1], chr(' '))) last_w = this_line.w or last_w for word in this_line.words: if word.lw: line.append(word) # if that fits, just return the flattened line lw = sum([word.lw for word in line]) if lw <= uw: lh = max([word.lh for word in line] + [0]) * line_height return lw + 2 * xpad, lh + 2 * ypad, [LayoutLine(0, 0, lw, lh, 1, 0, line)] # find the size of ellipsis that'll fit elps_s = textwidth('...') if elps_s[0] > uw: # even ellipsis didn't fit... s = textwidth('..') if s[0] <= uw: return (s[0] + 2 * xpad, s[1] * line_height + 2 * ypad, [LayoutLine(0, 0, s[0], s[1], 1, 0, [LayoutWord(old_opts, s[0], s[1], '..')])]) else: s = textwidth('.') return (s[0] + 2 * xpad, s[1] * line_height + 2 * ypad, [LayoutLine(0, 0, s[0], s[1], 1, 0, [LayoutWord(old_opts, s[0], s[1], '.')])]) elps = LayoutWord(old_opts, elps_s[0], elps_s[1], '...') uw -= elps_s[0] # now find the first left and right words that fit w1, e1, l1, clipped1 = n_restricted(line, uw, c) w2, s2, l2, clipped2 = p_restricted(line, uw, c) if dir != 'l': # center or right line1 = None if clipped1 or clipped2 or l1 + l2 > uw: # if either was clipped or both don't fit, just take first if len(c): self.options = old_opts old_opts['split_str'] = '' res = self.shorten_post(lines, w, h, margin) self.options['split_str'] = c return res line1 = line[:w1] last_word = line[w1] last_text = last_word.text[:e1] self.options = last_word.options s = self.get_extents(last_text) line1.append(LayoutWord(last_word.options, s[0], s[1], last_text)) elif (w1, e1) == (-1, -1): # this shouldn't occur line1 = line if line1: line1.append(elps) lw = sum([word.lw for word in line1]) lh = max([word.lh for word in line1]) * line_height self.options = old_opts return lw + 2 * xpad, lh + 2 * ypad, [LayoutLine(0, 0, lw, lh, 1, 0, line1)] # now we know that both the first and last word fit, and that # there's at least one instances of the split_str in the line if (w1, e1) != (w2, s2): # more than one split_str if dir == 'r': f = n(line, c) # iterator assert next(f)[:-1] == (w1, e1) # first word should match ww1, ee1, l1 = next(f) while l2 + l1 <= uw: w1, e1 = ww1, ee1 ww1, ee1, l1 = next(f) if (w1, e1) == (w2, s2): break else: # center f = n(line, c) # iterator f_inv = p(line, c) # iterator assert next(f)[:-1] == (w1, e1) assert next(f_inv)[:-1] == (w2, s2) while True: if l1 <= l2: ww1, ee1, l1 = next(f) # hypothesize that next fit if l2 + l1 > uw: break w1, e1 = ww1, ee1 if (w1, e1) == (w2, s2): break else: ww2, ss2, l2 = next(f_inv) if l2 + l1 > uw: break w2, s2 = ww2, ss2 if (w1, e1) == (w2, s2): break else: # left line1 = [elps] if clipped1 or clipped2 or l1 + l2 > uw: # if either was clipped or both don't fit, just take last if len(c): self.options = old_opts old_opts['split_str'] = '' res = self.shorten_post(lines, w, h, margin) self.options['split_str'] = c return res first_word = line[w2] first_text = first_word.text[s2 + 1:] self.options = first_word.options s = self.get_extents(first_text) line1.append(LayoutWord(first_word.options, s[0], s[1], first_text)) line1.extend(line[w2 + 1:]) elif (w1, e1) == (-1, -1): # this shouldn't occur line1 = line if len(line1) != 1: lw = sum([word.lw for word in line1]) lh = max([word.lh for word in line1]) * line_height self.options = old_opts return lw + 2 * xpad, lh + 2 * ypad, [LayoutLine(0, 0, lw, lh, 1, 0, line1)] # now we know that both the first and last word fit, and that # there's at least one instances of the split_str in the line if (w1, e1) != (w2, s2): # more than one split_str f_inv = p(line, c) # iterator assert next(f_inv)[:-1] == (w2, s2) # last word should match ww2, ss2, l2 = next(f_inv) while l2 + l1 <= uw: w2, s2 = ww2, ss2 ww2, ss2, l2 = next(f_inv) if (w1, e1) == (w2, s2): break # now add back the left half line1 = line[:w1] last_word = line[w1] last_text = last_word.text[:e1] self.options = last_word.options s = self.get_extents(last_text) if len(last_text): line1.append(LayoutWord(last_word.options, s[0], s[1], last_text)) line1.append(elps) # now add back the right half first_word = line[w2] first_text = first_word.text[s2 + 1:] self.options = first_word.options s = self.get_extents(first_text) if len(first_text): line1.append(LayoutWord(first_word.options, s[0], s[1], first_text)) line1.extend(line[w2 + 1:]) lw = sum([word.lw for word in line1]) lh = max([word.lh for word in line1]) * line_height self.options = old_opts return lw + 2 * xpad, lh + 2 * ypad, [LayoutLine(0, 0, lw, lh, 1, 0, line1)] Kivy-1.9.0/kivy/core/text/text_pygame.py0000664000175000017500000000664412471133021020227 0ustar titotito00000000000000''' Text Pygame: Draw text with pygame ''' __all__ = ('LabelPygame', ) from kivy.compat import PY2 from kivy.core.text import LabelBase from kivy.core.image import ImageData try: import pygame except: raise pygame_cache = {} pygame_font_handles = {} pygame_cache_order = [] # init pygame font try: pygame.ftfont.init() except: pygame.font.init() class LabelPygame(LabelBase): def _get_font_id(self): if PY2: try: return '|'.join([unicode(self.options[x]) for x in ('font_size', 'font_name_r', 'bold', 'italic')]) except UnicodeDecodeError: pass return '|'.join([str(self.options[x]) for x in ('font_size', 'font_name_r', 'bold', 'italic')]) def _get_font(self): fontid = self._get_font_id() if fontid not in pygame_cache: # try first the file if it's a filename font_handle = fontobject = None fontname = self.options['font_name_r'] ext = fontname.rsplit('.', 1) if len(ext) == 2: # try to open the font if it has an extension font_handle = open(fontname, 'rb') fontobject = pygame.font.Font(font_handle, int(self.options['font_size'])) # fallback to search a system font if fontobject is None: # try to search the font font = pygame.font.match_font( self.options['font_name_r'].replace(' ', ''), bold=self.options['bold'], italic=self.options['italic']) # fontobject fontobject = pygame.font.Font(font, int(self.options['font_size'])) pygame_cache[fontid] = fontobject pygame_font_handles[fontid] = font_handle pygame_cache_order.append(fontid) # to prevent too much file open, limit the number of opened fonts to 64 while len(pygame_cache_order) > 64: popid = pygame_cache_order.pop(0) del pygame_cache[popid] font_handle = pygame_font_handles.pop(popid) if font_handle is not None: font_handle.close() return pygame_cache[fontid] def get_ascent(self): return self._get_font().get_ascent() def get_descent(self): return self._get_font().get_descent() def get_extents(self, text): return self._get_font().size(text) def get_cached_extents(self): return self._get_font().size def _render_begin(self): self._pygame_surface = pygame.Surface(self._size, pygame.SRCALPHA, 32) self._pygame_surface.fill((0, 0, 0, 0)) def _render_text(self, text, x, y): font = self._get_font() color = [c * 255 for c in self.options['color']] color[0], color[2] = color[2], color[0] try: text = font.render(text, True, color) self._pygame_surface.blit(text, (x, y), None, pygame.BLEND_RGBA_ADD) except pygame.error: pass def _render_end(self): w, h = self._size data = ImageData(w, h, 'rgba', self._pygame_surface.get_buffer().raw) del self._pygame_surface return data Kivy-1.9.0/kivy/core/text/text_layout.pyx0000664000175000017500000005746312462275426020477 0ustar titotito00000000000000''' Text layout ============ An internal module for laying out text according to options and constraints. This is not part of the API and may change at any time. ''' __all__ = ('layout_text', 'LayoutWord', 'LayoutLine') cdef inline int max(int a, int b): return b if a <= b else a cdef inline int min(int a, int b): return a if a <= b else b cdef class LayoutWord: '''Formally describes a word contained in a line. The name word simply means a chunk of text and can be used to describe any text. A word has some width, height and is rendered according to options saved in :attr:`options`. See :class:`LayoutLine` for its usage. :Parameters: `options`: dict the label options dictionary for this word. `lw`: int the width of the text in pixels. `lh`: int the height of the text in pixels. `text`: string the text of the word. ''' def __cinit__(self, dict options, int lw, int lh, object text): self.text = text self.lw = lw self.lh = lh self.options = options cdef class LayoutLine: ''' Formally describes a line of text. A line of text is composed of many :class:`LayoutWord` instances, each with it's own text, size and options. A :class:`LayoutLine` instance does not always imply that the words contained in the line ended with a newline. That is only the case if :attr:`is_last_line` is True. For example a single real line of text can be split across multiple :class:`LayoutLine` instances if the whole line doesn't fit in the constrained width. :Parameters: `x`: int the location in a texture from where the left side of this line is began drawn. `y`: int the location in a texture from where the bottom of this line is drawn. `w`: int the width of the line. This is the sum of the individual widths of its :class:`LayoutWord` instances. Does not include any padding. `h`: int the height of the line. This is the maximum of the individual heights of its :class:`LayoutWord` instances multiplied by the `line_height` of these instance. So this is larger then the word height. `is_last_line`: bool whether this line was the last line in a paragraph. When True, it implies that the line was followed by a newline. Newlines should not be included in the text of words, but is implicit by setting this to True. `line_wrap`: bool whether this line is continued from a previous line which didn't fit into a constrained width and was therefore split across multiple :class:`LayoutLine` instances. `line_wrap` can be True or False independently of `is_last_line`. `words`: python list a list that contains only :class:`LayoutWord` instances describing the text of the line. ''' def __cinit__(self, int x=0, int y=0, int w=0, int h=0, int is_last_line=0, int line_wrap=0, list words=None): self.x = x self.y = y self.w = w self.h = h self.is_last_line = is_last_line self.line_wrap = line_wrap if words is None: words = [] self.words = words cdef inline LayoutLine add_line(object text, int lw, int lh, LayoutLine line, list lines, dict options, float line_height, int xpad, int *w, int *h, int pos, int strip): ''' Adds to the current line the text if lw is not zero. Increases that line's w/h by required amount, increases global h/w by required amount and returns new empty line. pos being -1 indicates we just append line, else, we insert the line at index pos in lines. E.g. if we add lines from bottom up. This assumes that global h is accurate and includes the text previously added to the line. ''' cdef int old_lh = line.h, count = len(lines), add_h if lw: line.words.append(LayoutWord(options, lw, lh, text)) line.w += lw line.h = max(int(lh * line_height), line.h) if count: add_h = line.h else: add_h = max(lh, line.h) # if we're appending to existing line don't add height twice h[0] = h[0] + add_h - old_lh w[0] = max(w[0], line.w + 2 * xpad) if strip: final_strip(line) if pos == -1: lines.append(line) else: lines.insert(pos, line) return LayoutLine() cdef inline void final_strip(LayoutLine line): ''' Ensures that the line does not end with trailing spaces. Given the line, it'll start from the last word and strip from the right. If the word becomes empty, it'll remove it and trip the word previous to that and so on. ''' cdef int diff cdef LayoutWord last_word cdef object stripped # XXX: here we strip any trailing spaces reducing the width of the line # however, the height is not reduced, even if the part that might be larger # is removed, potentially reducing the height of the line. It is not likely # a issue, but can be 'fixed' at the cost of re-computing line height while (len(line.words) and (line.words[-1].text.endswith(' ') or line.words[-1].text == '')): last_word = line.words.pop() if last_word.text == '': # empty str, pop it line.w -= last_word.lw # likely 0 continue stripped = last_word.text.rstrip() # ends with space # subtract ending space length diff = ((len(last_word.text) - len(stripped)) * last_word.options['space_width']) line.w = max(0, line.w - diff) # line w line.words.append(LayoutWord( # re-add last word last_word.options, max(0, last_word.lw - diff), last_word.lh, stripped)) cdef inline layout_text_unrestricted(object text, list lines, int w, int h, int uh, dict options, object get_extents, int dwn, int complete, int xpad, int max_lines, float line_height, int strip): ''' Layout when the width is unrestricted; text_size[0] is None. It's a bit faster. ''' cdef list new_lines cdef int s, lw, lh, old_lh, i = -1, n cdef int lhh, k, pos, add_h cdef object line, val = False, indices cdef LayoutLine _line new_lines = text.split('\n') n = len(new_lines) s = 0 k = n pos = len(lines) # always include first line, start w/ no lines added # there's a last line to which first (last) new line must be appended if pos: if dwn: # append to last line _line = lines[-1] line = new_lines[0] s = 1 else: # append to first line _line = lines[0] line = new_lines[-1] k = n - 1 if strip: if not _line.w: # no proceeding text: strip leading line = line.lstrip() # ends this line so right strip if complete or (dwn and n > 1 or not dwn and pos > 1): line = line.rstrip() lw, lh = get_extents(line) old_lh = _line.h if lw: # when adding to existing line, don't check uh _line.words.append(LayoutWord(options, lw, lh, line)) _line.w += lw _line.h = max(int(lh * line_height), _line.h) if pos == 1: # still first line add_h = max(lh, _line.h) else: add_h = _line.h elif strip and (complete or (dwn and n > 1 or not dwn and pos > 1)): # if we finish this line, make sure it doesn't end in spaces final_strip(_line) add_h = _line.h else: add_h = _line.h w = max(w, _line.w + 2 * xpad) h += add_h - old_lh # now do the remaining lines indices = range(s, k) if dwn else reversed(range(s, k)) for i in indices: # always compute first line, even if it won't be displayed if (max_lines > 0 and pos + 1 > max_lines or pos and uh != -1 and h > uh): i += -1 if dwn else 1 break line = new_lines[i] # the last line is only stripped from left if strip: if complete or (dwn and i < n - 1 or not dwn and i > s): line = line.strip() else: line = line.lstrip() lw, lh = get_extents(line) lhh = int(lh * line_height) if pos: add_h = lhh else: # for the first line, always use full height add_h = lh if uh != -1 and h + add_h > uh and pos: # too high i += -1 if dwn else 1 break pos += 1 w = max(w, int(lw + 2 * xpad)) h += add_h if lw: _line = LayoutLine(0, 0, lw, lhh, 1, 0, [LayoutWord(options, lw, lh, line)]) else: _line = LayoutLine(0, 0, 0, lhh, 1, 0, []) new_lines[i] = _line if s != k: if dwn: lines.extend(new_lines[s:i + 1]) val = i != k - 1 else: if k != i: lines.extend([None, ] * (k - i)) lines[(k - i):] = lines[:len(lines) - (k - i)] lines[:(k - i)] = new_lines[i:k] val = i != 0 return w, h, val def layout_text(object text, list lines, tuple size, tuple text_size, dict options, object get_extents, int append_down, int complete): ''' Lays out text into a series of :class:`LayoutWord` and :class:`LayoutLine` instances according to the options specified. The function is designed to be called many times, each time new text is appended to the last line (or first line if appending upwards), unless a newline is present in the text. Each text appended is described by it's own options which can change between successive calls. If the text is constrained, we stop as soon as the constraint is reached. :Parameters: `text`: string or bytes the text to be broken down into lines. If lines is not empty, the text is added to the last line (or first line if `append_down` is False) until a newline is reached which creates a new line in `lines`. See :class:`LayoutLine`. `lines`: list a list of :class:`LayoutLine` instances, each describing a line of the text. Calls to :func:`layout_text` append or create new :class:`LayoutLine` instances in `lines`. `size`: 2-tuple of ints the size of the laid out text so far. Upon first call it should probably be (0, 0), afterwards it should be the (w, h) returned by this function in a previous call. When size reaches the constraining size, `text_size`, we stop adding lines and return True for the clipped parameter. size includes the x and y padding. `text_size`: 2-tuple of ints or None. the size constraint on the laid out text. If either element is None, the text is not constrained in that dimension. For example, (None, 200) will constrain the height, including padding to 200, while the width is unconstrained. The first line, and the first character of a line is always returned, even if it exceeds the constraint. The value be changed between different calls. `options`: dict the label options of this `text`. The options are saved with each word allowing different words to have different options from successive calls. Note, `options` must include a `space_width` key with a value indicating the width of a space for that set of options. `get_extents`: callable a function called with a string, which returns a tuple containing the width, height of the string. `append_down`: bool Whether successive calls to the function appends lines before or after the existing lines. If True, they are appended to the last line and below it. If False, it's appended at the first line and above. For example, if False, everything after the last newline in `text` is appended to the first line in lines. Everything before the last newline is inserted at the start of lines in same order as text; that is we do not invert the line order. This allows laying out from top to bottom until the constrained is reached, or from bottom to top until the constrained is reached. `complete`: bool whether this text complete lines. It use is that normally is strip in `options` is True, all leading and trailing spaces are removed from each line except from the last line (or first line if `append_down` is False) which only removes leading spaces. That's because further text can still be appended to the last line so we cannot strip them. If `complete` is True, it indicates no further text is coming and all lines will be stripped. The function can also be called with `text` set to the empty string and `complete` set to True in order for the last (first) line to be stripped. :returns: 3-tuple, (w, h, clipped). w and h is the width and height of the text in lines so far and includes padding. This can be larger than `text_size`, e.g. if not even a single fitted, the first line would still be returned. `clipped` is True if not all the text has been added to lines because w, h reached the constrained size. Following is a simple example with no padding and no stripping:: >>> from kivy.core.text import Label >>> from kivy.core.text.text_layout import layout_text >>> l = Label() >>> lines = [] >>> # layout text with width constraint by 50, but no height constraint >>> w, h, clipped = layout_text('heres some text\\nah, another line', ... lines, (0, 0), (50, None), l.options, l.get_cached_extents(), True, ... False) >>> w, h, clipped (46, 90, False) # now add text from bottom up, and constrain witdh only be 100 >>> w, h, clipped = layout_text('\\nyay, more text\\n', lines, (w, h), ... (100, None), l.options, l.get_cached_extents(), False, True) >>> w, h, clipped (77, 120, 0) >>> for line in lines: ... print('line w: {}, line h: {}'.format(line.w, line.h)) ... for word in line.words: ... print('w: {}, h: {}, text: {}'.format(word.lw, word.lh, ... [word.text])) line w: 0, line h: 15 line w: 77, line h: 15 w: 77, h: 15, text: ['yay, more text'] line w: 31, line h: 15 w: 31, h: 15, text: ['heres'] line w: 34, line h: 15 w: 34, h: 15, text: [' some'] line w: 24, line h: 15 w: 24, h: 15, text: [' text'] line w: 17, line h: 15 w: 17, h: 15, text: ['ah,'] line w: 46, line h: 15 w: 46, h: 15, text: [' another'] line w: 23, line h: 15 w: 23, h: 15, text: [' line'] ''' cdef int uw, uh, _do_last_line, lwe, lhe, ends_line, is_last_line cdef int xpad = options['padding_x'], ypad = options['padding_y'] cdef int max_lines = int(options.get('max_lines', 0)) cdef float line_height = options['line_height'] cdef int strip = options['strip'] or options['halign'][-1] == 'y' cdef int ref_strip = options['strip_reflow'] cdef int w = size[0], h = size[1] # width and height of the texture so far cdef list new_lines cdef int s, lw = -1, lh = -1, old_lh, i = -1, n, m, e cdef int lhh, lww, k, bare_h, dwn = append_down, pos = 0 cdef object line, ln, val, indices cdef LayoutLine _line cdef int is_space = 0 uw = text_size[0] if text_size[0] is not None else -1 uh = text_size[1] if text_size[1] is not None else -1 if not h: h = ypad * 2 if uw == -1: # no width specified return layout_text_unrestricted(text, lines, w, h, uh, options, get_extents, dwn, complete, xpad, max_lines, line_height, strip) new_lines = text.split('\n') n = len(new_lines) uw = max(0, uw - xpad * 2) # actual w, h allowed for rendering _, bare_h = get_extents('') if dwn: pos = -1 # don't use pos when going down b/c we append at end of lines # split into lines and find how many line wraps each line requires indices = range(n) if dwn else reversed(range(n)) for i in indices: k = len(lines) if (max_lines > 0 and k > max_lines or uh != -1 and h > uh and k > 1): break is_last_line = not (dwn and i < n - 1 or not dwn and i) # whether this line is ended, or if we may append to it later ends_line = complete or not is_last_line if not dwn: # new line will be appended at top, unless changed below pos = 0 # for the first (last if not down) new line, append it to previous line if (i and dwn or not dwn and i != n - 1) or not k: # interior line _line = LayoutLine() else: if dwn: # take last line _line = lines.pop() else: # need to append right before 1st line ends in case of wrap while pos + 1 < k and not lines[pos + 1].line_wrap: pos += 1 _line = lines.pop(pos) line = new_lines[i] # there's no proceeding text, so strip leading if not _line.w and (strip or ref_strip and _line.line_wrap): line = line.lstrip() if strip and ends_line: line = line.rstrip() k = len(line) if not k: # just add empty line if empty _line.is_last_line = ends_line # nothing will be appended # ensure we don't leave trailing from before _line = add_line('', 0, bare_h, _line, lines, options, line_height, xpad, &w, &h, pos, _line.w and ends_line) continue '''----------------- we now a non-empty line ------------------------ what we do is given the current text in this real line if we can fit another word, add it. Otherwise add it to a new line. But if a single word doen't fit on a single line, just split the word itself into multiple lines''' # s is idx in line of start of this actual line, e is idx of # next space, m is idx after s that still fits on this line s = m = e = 0 while s != k: # find next space or end, if end don't keep checking if e != k: # leading spaces if s == m and not _line.w and line[s] == ' ' and (strip or ref_strip and _line.line_wrap): s = m = s + 1 # trailing spaces were stripped, so end is always not space continue # when not stripping, if we found a space last, don't jump to # the next space, but instead move pos to after this space, to # allow fitting this space on the current line if strip or not is_space: e = line.find(' ', m + 1) is_space = 1 else: e = m + 1 is_space = 0 if e is -1: e = k lwe, lhe = get_extents(line[s:e]) # does next word fit? if lwe + _line.w > uw: # too wide ln = '' lww, lhh = 0, bare_h _do_last_line = 0 # if there's already some text, commit and go next line if s != m: _do_last_line = 1 if (strip or ref_strip) and line[m - 1] == ' ': ln = line[s:m].rstrip() lww, lhh = get_extents(ln) else: ln = line[s:m] lww, lhh = lw, lh s = m elif _line.w: _do_last_line = 1 if _do_last_line: # if there's proceeding text and ln is '': strip trailing _line = add_line(ln, lww, lhh, _line, lines, options, line_height, xpad, &w, &h, pos, _line.w and not lww) _line.line_wrap = 1 if not dwn: pos += 1 # try to fit word on new line, if it doesn't fit we'll # have to break the word into as many lines needed if strip or ref_strip and _line.line_wrap: s = e - len(line[s:e].lstrip()) if s == e: # if it was only a stripped space, move on m = s continue # now break single word into as many lines needed m = s while s != e: # does remainder fit in single line? lwe, lhe = get_extents(line[s:e]) if lwe + _line.w <= uw: m = e break # if not, fit as much as possible into this line while (m != e and get_extents(line[s:m + 1])[0] + _line.w <= uw): m += 1 # not enough room for even single char, skip it if m == s: m += 1 _line.is_last_line = m == k # is last line? lww, lhh = get_extents(line[s:m]) _line = add_line(line[s:m], lww, lhh, _line, lines, options, line_height, xpad, &w, &h, pos, 0) _line.line_wrap = 1 if not dwn: pos += 1 s = m m = s # done with long word, go back to normal else: # the word fits # don't allow leading spaces on empty lines #if strip and m == s and line[s:e] == ' ' and not _line.w: if (strip or ref_strip and _line.line_wrap) and line[s:e] == ' ' and not _line.w: s = m = e continue m = e if m == k: # we're done if s != k or _line.w: _line.is_last_line = ends_line # line end _line = add_line(line[s:], lwe, lhe, _line, lines, options, line_height, xpad, &w, &h, pos, 0) break lw, lh = lwe, lhe # save current lw/lh, then fit more in line val = dwn and i != n - 1 or not dwn and i # ensure the number of lines is not more than the user asked # above, we might have gone a few lines over if max_lines > 0 and len(lines) > max_lines: val = True if dwn: del lines[max_lines:] else: del lines[:max(0, len(lines) - max_lines)] # now make sure we don't have lines outside specified height k = len(lines) if k > 1 and uh != -1 and h > uh: val = True if dwn: # remove from last line going up i = k -1 # will removing the ith line make it fit? while i > 0 and h > uh: h -= lines[i].h i -= 1 del lines[i + 1:] # we stopped when keeping the ith line still fits else: # remove from first line going down i = 0 # will removing the ith line make it fit? while i < k - 1 and h > uh: h -= lines[i].h i += 1 del lines[:i] return w, h, val Kivy-1.9.0/kivy/core/text/text_sdl2.py0000664000175000017500000000241012462275426017614 0ustar titotito00000000000000''' SDL2 text provider ================== Based on SDL2 + SDL2_ttf ''' __all__ = ('LabelSDL2', ) from kivy.compat import PY2 from kivy.core.text import LabelBase from kivy.core.text._text_sdl2 import (_SurfaceContainer, _get_extents, _get_fontdescent, _get_fontascent) class LabelSDL2(LabelBase): def _get_font_id(self): if PY2: try: return '|'.join([unicode(self.options[x]) for x in ('font_size', 'font_name_r', 'bold', 'italic')]) except UnicodeDecodeError: pass return '|'.join([str(self.options[x]) for x in ('font_size', 'font_name_r', 'bold', 'italic')]) def get_extents(self, text): try: if PY2: text = text.encode('UTF-8') except: pass return _get_extents(self, text) def get_descent(self): return _get_fontdescent(self) def get_ascent(self): return _get_fontascent(self) def _render_begin(self): self._surface = _SurfaceContainer(self._size[0], self._size[1]) def _render_text(self, text, x, y): self._surface.render(self, text, x, y) def _render_end(self): return self._surface.get_data() Kivy-1.9.0/kivy/core/text/__init__.py0000664000175000017500000006513112502235633017443 0ustar titotito00000000000000''' Text ==== An abstraction of text creation. Depending of the selected backend, the accuracy of text rendering may vary. .. versionchanged:: 1.5.0 :attr:`LabelBase.line_height` added. .. versionchanged:: 1.0.7 The :class:`LabelBase` does not generate any texture if the text has a width <= 1. This is the backend layer for getting text out of different text providers, you should only be using this directly if your needs aren't fulfilled by the :class:`~kivy.uix.label.Label`. Usage example:: from kivy.core.label import Label as CoreLabel ... ... my_label = CoreLabel() my_label.text = 'hello' # the label is usually not drawn until needed, so force it to draw. my_label.refresh() # Now access the texture of the label and use it wherever and # however you may please. hello_texture = my_label.texture ''' __all__ = ('LabelBase', 'Label') import re import os from functools import partial from copy import copy from kivy import kivy_data_dir from kivy.utils import platform from kivy.graphics.texture import Texture from kivy.core import core_select_lib from kivy.core.text.text_layout import layout_text, LayoutWord from kivy.resources import resource_find, resource_add_path from kivy.compat import PY2 from kivy.setupconfig import USE_SDL2 DEFAULT_FONT = 'DroidSans' FONT_REGULAR = 0 FONT_ITALIC = 1 FONT_BOLD = 2 FONT_BOLDITALIC = 3 class LabelBase(object): '''Core text label. This is the abstract class used by different backends to render text. .. warning:: The core text label can't be changed at runtime. You must recreate one. :Parameters: `font_size`: int, defaults to 12 Font size of the text `font_name`: str, defaults to DEFAULT_FONT Font name of the text `bold`: bool, defaults to False Activate "bold" text style `italic`: bool, defaults to False Activate "italic" text style `text_size`: tuple, defaults to (None, None) Add constraint to render the text (inside a bounding box). If no size is given, the label size will be set to the text size. `padding`: float, defaults to None If it's a float, it will set padding_x and padding_y `padding_x`: float, defaults to 0.0 Left/right padding `padding_y`: float, defaults to 0.0 Top/bottom padding `halign`: str, defaults to "left" Horizontal text alignment inside the bounding box `valign`: str, defaults to "bottom" Vertical text alignment inside the bounding box `shorten`: bool, defaults to False Indicate whether the label should attempt to shorten its textual contents as much as possible if a `size` is given. Setting this to True without an appropriately set size will lead to unexpected results. `shorten_from`: str, defaults to `center` The side from which we should shorten the text from, can be left, right, or center. E.g. if left, the ellipsis will appear towards the left side and it will display as much text starting from the right as possible. `split_str`: string, defaults to `' '` (space) The string to use to split the words by when shortening. If empty, we can split after every character filling up the line as much as possible. `max_lines`: int, defaults to 0 (unlimited) If set, this indicate how maximum line are allowed to render the text. Works only if a limitation on text_size is set. `mipmap` : bool, defaults to False Create a mipmap for the texture `strip` : bool, defaults to False Whether each row of text has its leading and trailing spaces stripped. If `halign` is `justify` it is implicitly True. `strip_reflow` : bool, defaults to True Whether text that has been reflowed into a second line should be striped, even if `strip` is False. This is only in effect when `size_hint_x` is not None, because otherwise lines are never split. `unicode_errors` : str, defaults to `'replace'` How to handle unicode decode errors. Can be `'strict'`, `'replace'` or `'ignore'`. .. versionchanged:: 1.9.0 `strip`, `strip_reflow`, `shorten_from`, `split_str`, and `unicode_errors` were added. .. versionchanged:: 1.9.0 `padding_x` and `padding_y` has been fixed to work as expected. In the past, the text was padded by the negative of their values. .. versionchanged:: 1.8.0 `max_lines` parameters has been added. .. versionchanged:: 1.0.8 `size` have been deprecated and replaced with `text_size`. .. versionchanged:: 1.0.7 The `valign` is now respected. This wasn't the case previously so you might have an issue in your application if you have not considered this. ''' __slots__ = ('options', 'texture', '_label', '_text_size') _cached_lines = [] _fonts = {} _fonts_cache = {} _fonts_dirs = [] _texture_1px = None def __init__( self, text='', font_size=12, font_name=DEFAULT_FONT, bold=False, italic=False, halign='left', valign='bottom', shorten=False, text_size=None, mipmap=False, color=None, line_height=1.0, strip=False, strip_reflow=True, shorten_from='center', split_str=' ', unicode_errors='replace', **kwargs): # Include system fonts_dir in resource paths. # This allows us to specify a font from those dirs. LabelBase.get_system_fonts_dir() options = {'text': text, 'font_size': font_size, 'font_name': font_name, 'bold': bold, 'italic': italic, 'halign': halign, 'valign': valign, 'shorten': shorten, 'mipmap': mipmap, 'line_height': line_height, 'strip': strip, 'strip_reflow': strip_reflow, 'shorten_from': shorten_from, 'split_str': split_str, 'unicode_errors': unicode_errors} options['color'] = color or (1, 1, 1, 1) options['padding'] = kwargs.get('padding', (0, 0)) if not isinstance(options['padding'], (list, tuple)): options['padding'] = (options['padding'], options['padding']) options['padding_x'] = kwargs.get('padding_x', options['padding'][0]) options['padding_y'] = kwargs.get('padding_y', options['padding'][1]) if 'size' in kwargs: options['text_size'] = kwargs['size'] else: if text_size is None: options['text_size'] = (None, None) else: options['text_size'] = text_size self._text_size = options['text_size'] self._text = options['text'] self._internal_size = 0, 0 # the real computed text size (inclds pad) self._cached_lines = [] self.options = options self.texture = None self.resolve_font_name() @staticmethod def register(name, fn_regular, fn_italic=None, fn_bold=None, fn_bolditalic=None): '''Register an alias for a Font. .. versionadded:: 1.1.0 If you're using a ttf directly, you might not be able to use the bold/italic properties of the ttf version. If the font is delivered in multiple files (one regular, one italic and one bold), then you need to register these files and use the alias instead. All the fn_regular/fn_italic/fn_bold parameters are resolved with :func:`kivy.resources.resource_find`. If fn_italic/fn_bold are None, fn_regular will be used instead. ''' fonts = [] for font_type in fn_regular, fn_italic, fn_bold, fn_bolditalic: if font_type is not None: font = resource_find(font_type) if font is None: raise IOError('File {0}s not found'.format(font_type)) else: fonts.append(font) else: fonts.append(fonts[-1]) # add regular font to list again LabelBase._fonts[name] = tuple(fonts) def resolve_font_name(self): options = self.options fontname = options['font_name'] fonts = self._fonts fontscache = self._fonts_cache # is the font is registered ? if fontname in fonts: # return the prefered font for the current bold/italic combinaison italic = int(options['italic']) if options['bold']: bold = FONT_BOLD else: bold = FONT_REGULAR options['font_name_r'] = fonts[fontname][italic | bold] elif fontname in fontscache: options['font_name_r'] = fontscache[fontname] else: filename = resource_find(fontname) if not filename: fontname = fontname + \ ('' if fontname.endswith('.ttf') else '.ttf') filename = resource_find(fontname) if filename is None: # XXX for compatibility, check directly in the data dir filename = os.path.join(kivy_data_dir, fontname) if not os.path.exists(filename): raise IOError('Label: File %r not found' % fontname) fontscache[fontname] = filename options['font_name_r'] = filename @staticmethod def get_system_fonts_dir(): '''Return the Directory used by the system for fonts. ''' if LabelBase._fonts_dirs: return LabelBase._fonts_dirs fdirs = [] if platform == 'linux': fdirs = [ '/usr/share/fonts/truetype', '/usr/local/share/fonts', os.path.expanduser('~/.fonts'), os.path.expanduser('~/.local/share/fonts')] elif platform == 'macosx': fdirs = ['/Library/Fonts', '/System/Library/Fonts', os.path.expanduser('~/Library/Fonts')] elif platform == 'win': fdirs = [os.environ['SYSTEMROOT'] + os.sep + 'Fonts'] elif platform == 'ios': fdirs = ['/System/Library/Fonts'] elif platform == 'android': fdirs = ['/system/fonts'] if fdirs: fdirs.append(kivy_data_dir + os.sep + 'fonts') # let's register the font dirs rdirs = [] for _dir in fdirs: if os.path.exists(_dir): resource_add_path(_dir) rdirs.append(_dir) LabelBase._fonts_dirs = rdirs return rdirs raise Exception("Unknown Platform {}".format(platform)) def get_extents(self, text): '''Return a tuple (width, height) indicating the size of the specified text''' return (0, 0) def get_cached_extents(self): '''Returns a cached version of the :meth:`get_extents` function. :: >>> func = self._get_cached_extents() >>> func >>> func('a line') (36, 18) .. warning:: This method returns a size measuring function that is valid for the font settings used at the time :meth:`get_cached_extents` was called. Any change in the font settings will render the returned function incorrect. You should only use this if you know what you're doing. .. versionadded:: 1.9.0 ''' return self.get_extents def _render_begin(self): pass def _render_text(self, text, x, y): pass def _render_end(self): pass def shorten(self, text, margin=2): ''' Shortens the text to fit into a single line by the width specified by :attr:`text_size` [0]. If :attr:`text_size` [0] is None, it returns text text unchanged. :attr:`split_str` and :attr:`shorten_from` determines how the text is shortened. :params: `text` str, the text to be shortened. `margin` int, the amount of space to leave between the margins and the text. This is in addition to :attr:`padding_x`. :retruns: the text shortened to fit into a single line. ''' textwidth = self.get_cached_extents() uw = self.text_size[0] if uw is None or not text: return text opts = self.options uw = max(0, int(uw - opts['padding_x'] * 2 - margin)) # if larger, it won't fit so don't even try extents chr = type(text) text = text.replace(chr('\n'), chr(' ')) if len(text) <= uw and textwidth(text)[0] <= uw: return text c = opts['split_str'] offset = 0 if len(c) else 1 dir = opts['shorten_from'][0] elps = textwidth('...')[0] if elps > uw: if textwidth('..')[0] <= uw: return '..' else: return '.' uw -= elps f = partial(text.find, c) f_rev = partial(text.rfind, c) # now find the first and last word e1, s2 = f(), f_rev() if dir != 'l': # center or right # no split, or the first word doesn't even fit if e1 != -1: l1 = textwidth(text[:e1])[0] l2 = textwidth(text[s2 + 1:])[0] if e1 == -1 or l1 + l2 > uw: if len(c): opts['split_str'] = '' res = self.shorten(text, margin) opts['split_str'] = c return res # at this point we do char by char so e1 must be zero if l1 <= uw: return chr('{0}...').format(text[:e1]) return chr('...') # both word fits, and there's at least on split_str if s2 == e1: # there's only on split_str return chr('{0}...{1}').format(text[:e1], text[s2 + 1:]) # both the first and last word fits, and they start/end at diff pos if dir == 'r': ee1 = f(e1 + 1) while l2 + textwidth(text[:ee1])[0] <= uw: e1 = ee1 if e1 == s2: break ee1 = f(e1 + 1) else: while True: if l1 <= l2: ee1 = f(e1 + 1) l1 = textwidth(text[:ee1])[0] if l2 + l1 > uw: break e1 = ee1 if e1 == s2: break else: ss2 = f_rev(0, s2 - offset) l2 = textwidth(text[ss2 + 1:])[0] if l2 + l1 > uw: break s2 = ss2 if e1 == s2: break else: # left # no split, or the last word doesn't even fit if s2 != -1: l2 = textwidth(text[s2 + (1 if len(c) else -1):])[0] l1 = textwidth(text[:max(0, e1)])[0] # if split_str if s2 == -1 or l2 + l1 > uw: if len(c): opts['split_str'] = '' res = self.shorten(text, margin) opts['split_str'] = c return res return chr('...') # both word fits, and there's at least on split_str if s2 == e1: # there's only on split_str return chr('{0}...{1}').format(text[:e1], text[s2 + 1:]) # both the first and last word fits, and they start/end at diff pos ss2 = f_rev(0, s2 - offset) while l1 + textwidth(text[ss2 + 1:])[0] <= uw: s2 = ss2 if s2 == e1: break ss2 = f_rev(0, s2 - offset) return chr('{0}...{1}').format(text[:e1], text[s2 + 1:]) def _render_real(self): lines = self._cached_lines options = None for line in lines: if len(line.words): # get opts from first line, first word options = line.words[0].options break if not options: # there was no text to render self._render_begin() data = self._render_end() assert(data) if data is not None and data.width > 1: self.texture.blit_data(data) return render_text = self._render_text get_extents = self.get_cached_extents() uw, uh = options['text_size'] xpad, ypad = options['padding_x'], options['padding_y'] x, y = xpad, ypad # pos in the texture iw, ih = self._internal_size # the real size of text, not texture if uw is not None: uww = uw - 2 * xpad # real width of just text w, h = self.size sw = options['space_width'] halign = options['halign'] valign = options['valign'] split = re.split pat = re.compile('( +)') self._render_begin() if valign == 'bottom': y = h - ih + ypad elif valign == 'middle': y = int((h - ih) / 2 + ypad) for layout_line in lines: # for plain label each line has only one str lw, lh = layout_line.w, layout_line.h line = '' assert len(layout_line.words) < 2 if len(layout_line.words): last_word = layout_line.words[0] line = last_word.text x = xpad if halign[0] == 'c': # center x = int((w - lw) / 2.) elif halign[0] == 'r': # right x = max(0, int(w - lw - xpad)) # right left justify # divide left over space between `spaces` # TODO implement a better method of stretching glyphs? if (uw is not None and halign[-1] == 'y' and line and not layout_line.is_last_line): # number spaces needed to fill, and remainder n, rem = divmod(max(uww - lw, 0), sw) n = int(n) words = None if n or rem: # there's no trailing space when justify is selected words = split(pat, line) if words is not None and len(words) > 1: space = type(line)(' ') # words: every even index is spaces, just add ltr n spaces for i in range(n): idx = (2 * i + 1) % (len(words) - 1) words[idx] = words[idx] + space if rem: # render the last word at the edge, also add it to line ext = get_extents(words[-1]) word = LayoutWord(last_word.options, ext[0], ext[1], words[-1]) layout_line.words.append(word) last_word.lw = uww - ext[0] # word was stretched render_text(words[-1], x + last_word.lw, y) last_word.text = line = ''.join(words[:-2]) else: last_word.lw = uww # word was stretched last_word.text = line = ''.join(words) layout_line.w = uww # the line occupies full width if len(line): layout_line.x = x layout_line.y = y render_text(line, x, y) y += lh # get data from provider data = self._render_end() assert(data) # If the text is 1px width, usually, the data is black. # Don't blit that kind of data, otherwise, you have a little black bar. if data is not None and data.width > 1: self.texture.blit_data(data) def render(self, real=False): '''Return a tuple (width, height) to create the image with the user constraints. (width, height) includes the padding. ''' if real: return self._render_real() options = copy(self.options) options['space_width'] = self.get_extents(' ')[0] options['strip'] = strip = (options['strip'] or options['halign'][-1] == 'y') uw, uh = options['text_size'] = self._text_size text = self.text if strip: text = text.strip() if uw is not None and options['shorten']: text = self.shorten(text) self._cached_lines = lines = [] if not text: return 0, 0 if uh is not None and options['valign'][-1] == 'e': # middle center = -1 # pos of newline if len(text) > 1: middle = int(len(text) // 2) l, r = text.rfind('\n', 0, middle), text.find('\n', middle) if l != -1 and r != -1: center = l if center - l <= r - center else r elif l != -1: center = l elif r != -1: center = r # if a newline split text, render from center down and up til uh if center != -1: # layout from center down until half uh w, h, clipped = layout_text(text[center + 1:], lines, (0, 0), (uw, uh / 2), options, self.get_cached_extents(), True, True) # now layout from center upwards until uh is reached w, h, clipped = layout_text(text[:center + 1], lines, (w, h), (uw, uh), options, self.get_cached_extents(), False, True) else: # if there's no new line, layout everything w, h, clipped = layout_text(text, lines, (0, 0), (uw, None), options, self.get_cached_extents(), True, True) else: # top or bottom w, h, clipped = layout_text(text, lines, (0, 0), (uw, uh), options, self.get_cached_extents(), options['valign'][-1] == 'p', True) self._internal_size = w, h if uw: w = uw if uh: h = uh if h > 1 and w < 2: w = 2 return int(w), int(h) def _texture_refresh(self, *l): self.refresh() def _texture_fill(self, texture): # second pass, render for real self.render(real=True) def refresh(self): '''Force re-rendering of the text ''' self.resolve_font_name() # first pass, calculating width/height sz = self.render() self._size_texture = sz self._size = (sz[0], sz[1]) # if no text are rendered, return nothing. width, height = self._size if width <= 1 or height <= 1: self.texture = self.texture_1px return # create a delayed texture texture = self.texture if texture is None or \ width != texture.width or \ height != texture.height: texture = Texture.create(size=(width, height), mipmap=self.options['mipmap'], callback=self._texture_fill) texture.flip_vertical() texture.add_reload_observer(self._texture_refresh) self.texture = texture else: texture.ask_update(self._texture_fill) def _get_text(self): if PY2: try: if isinstance(self._text, unicode): return self._text return self._text.decode('utf8') except AttributeError: # python 3 support return str(self._text) except UnicodeDecodeError: return self._text else: return self._text def _set_text(self, text): if text != self._text: self._text = text text = property(_get_text, _set_text, doc='Get/Set the text') label = property(_get_text, _set_text, doc='Get/Set the text') @property def texture_1px(self): if LabelBase._texture_1px is None: tex = Texture.create(size=(1, 1), colorfmt='rgba') tex.blit_buffer(b'\x00\x00\x00\x00', colorfmt='rgba') LabelBase._texture_1px = tex return LabelBase._texture_1px @property def size(self): return self._size @property def width(self): return self._size[0] @property def height(self): return self._size[1] @property def content_width(self): '''Return the content width; i.e. the width of the text without any padding.''' if self.texture is None: return 0 return self.texture.width - 2 * self.options['padding_x'] @property def content_height(self): '''Return the content height; i.e. the height of the text without any padding.''' if self.texture is None: return 0 return self.texture.height - 2 * self.options['padding_y'] @property def content_size(self): '''Return the content size (width, height)''' if self.texture is None: return (0, 0) return (self.content_width, self.content_height) @property def fontid(self): '''Return a unique id for all font parameters''' return str([self.options[x] for x in ( 'font_size', 'font_name_r', 'bold', 'italic')]) def _get_text_size(self): return self._text_size def _set_text_size(self, x): self._text_size = x text_size = property(_get_text_size, _set_text_size, doc='''Get/set the (width, height) of the ' 'contrained rendering box''') usersize = property(_get_text_size, _set_text_size, doc='''(deprecated) Use text_size instead.''') # Load the appropriate provider label_libs = [] if USE_SDL2: label_libs += [('sdl2', 'text_sdl2', 'LabelSDL2')] else: label_libs += [('pygame', 'text_pygame', 'LabelPygame')] label_libs += [ ('pil', 'text_pil', 'LabelPIL')] Label = core_select_lib('text', label_libs) if 'KIVY_DOC' not in os.environ: if not Label: from kivy.logger import Logger import sys Logger.critical('App: Unable to get a Text provider, abort.') sys.exit(1) # For the first initalization, register the default font Label.register('DroidSans', 'data/fonts/DroidSans.ttf', 'data/fonts/DroidSans-Italic.ttf', 'data/fonts/DroidSans-Bold.ttf', 'data/fonts/DroidSans-BoldItalic.ttf') Kivy-1.9.0/kivy/core/spelling/0000775000175000017500000000000012507221737016162 5ustar titotito00000000000000Kivy-1.9.0/kivy/core/spelling/spelling_osxappkit.py0000664000175000017500000000461112276430013022445 0ustar titotito00000000000000''' AppKit Spelling: Implements spelling backend based on OSX's spellchecking features provided by the ApplicationKit. NOTE: Requires pyobjc and setuptools to be installed! `sudo easy_install pyobjc setuptools` Developers should read: http://developer.apple.com/mac/library/documentation/ Cocoa/Conceptual/SpellCheck/SpellCheck.html http://developer.apple.com/cocoa/pyobjc.html ''' from AppKit import NSSpellChecker, NSMakeRange from kivy.core.spelling import SpellingBase, NoSuchLangError class SpellingOSXAppKit(SpellingBase): ''' Spelling backend based on OSX's spelling features provided by AppKit. ''' def __init__(self, language=None): self._language = NSSpellChecker.alloc().init() super(SpellingOSXAppKit, self).__init__(language) def select_language(self, language): success = self._language.setLanguage_(language) if not success: err = 'AppKit Backend: No language "%s" ' % (language, ) raise NoSuchLangError(err) def list_languages(self): return list(self._language.availableLanguages()) def check(self, word): # TODO Implement this! # NSSpellChecker provides several functions that look like what we # need, but they're a) slooow and b) return a strange result. # Might be a snow leopard bug. Have to test further. # See: http://paste.pocoo.org/show/217968/ if not word: return None err = 'check() not currently supported by the OSX AppKit backend' raise NotImplementedError(err) def suggest(self, fragment): l = self._language # XXX Both ways below work on OSX 10.6. It has not been tested on any # other version, but it should work. try: # This is deprecated as of OSX 10.6, hence the try-except return list(l.guessesForWord_(fragment)) except AttributeError: # From 10.6 onwards you're supposed to do it like this: checkrange = NSMakeRange(0, len(fragment)) g = l.guessesForWordRange_inString_language_inSpellDocumentWithTag_( checkrange, fragment, l.language(), 0) # Right, this was much easier, Apple! :-) return list(g) Kivy-1.9.0/kivy/core/spelling/spelling_enchant.py0000664000175000017500000000246112276430013022044 0ustar titotito00000000000000''' Enchant Spelling: Implements spelling backend based on enchant. ''' import enchant from kivy.core.spelling import SpellingBase, NoSuchLangError from kivy.compat import PY2 class SpellingEnchant(SpellingBase): ''' Spelling backend based on the enchant library. ''' def __init__(self, language=None): self._language = None super(SpellingEnchant, self).__init__(language) def select_language(self, language): try: self._language = enchant.Dict(language) except enchant.DictNotFoundError: err = 'Enchant Backend: No language for "%s"' % (language, ) raise NoSuchLangError(err) def list_languages(self): # Note: We do NOT return enchant.list_dicts because that also returns # the enchant dict objects and not only the language identifiers. return enchant.list_languages() def check(self, word): if not word: return None return self._language.check(word) def suggest(self, fragment): suggestions = self._language.suggest(fragment) # Don't show suggestions that are invalid suggestions = [s for s in suggestions if self.check(s)] if PY2: suggestions = [s.decode('utf-8') for s in suggestions] return suggestions Kivy-1.9.0/kivy/core/spelling/__init__.py0000664000175000017500000001103512276430013020263 0ustar titotito00000000000000''' Spelling ======== Provides abstracted access to a range of spellchecking backends as well as word suggestions. The API is inspired by enchant but other backends can be added that implement the same API. Spelling currently requires `python-enchant` for all platforms except OSX, where a native implementation exists. :: >>> from kivy.core.spelling import Spelling >>> s = Spelling() >>> s.list_languages() ['en', 'en_CA', 'en_GB', 'en_US'] >>> s.select_language('en_US') >>> s.suggest('helo') [u'hole', u'help', u'helot', u'hello', u'halo', u'hero', u'hell', u'held', u'helm', u'he-lo'] ''' __all__ = ('Spelling', 'SpellingBase', 'NoSuchLangError', 'NoLanguageSelectedError') import sys from kivy.core import core_select_lib class NoSuchLangError(Exception): ''' Exception to be raised when a specific language could not be found. ''' pass class NoLanguageSelectedError(Exception): ''' Exception to be raised when a language-using method is called but no language was selected prior to the call. ''' pass class SpellingBase(object): ''' Base class for all spelling providers. Supports some abstract methods for checking words and getting suggestions. ''' def __init__(self, language=None): ''' If a `language` identifier (such as 'en_US') is provided and a matching language exists, it is selected. If an identifier is provided and no matching language exists, a NoSuchLangError exception is raised by self.select_language(). If no `language` identifier is provided, we just fall back to the first one that is available. :Parameters: `language` : str, defaults to None If provided, indicates the language to be used. This needs to be a language identifier understood by select_language(), i.e. one of the options returned by list_languages(). If nothing is provided, the first available language is used. If no language is available, NoLanguageSelectedError is raised. ''' langs = self.list_languages() try: # If no language was specified, we just use the first one # that is available. fallback_lang = langs[0] except IndexError: raise NoLanguageSelectedError("No languages available!") self.select_language(language or fallback_lang) def select_language(self, language): ''' From the set of registered languages, select the first language for `language`. :Parameters: `language` : str Language identifier. Needs to be one of the options returned by list_languages(). Sets the language used for spell checking and word suggestions. ''' raise NotImplementedError('select_language() method not implemented ' 'by abstract spelling base class!') def list_languages(self): ''' Return a list of all supported languages. E.g. ['en', 'en_GB', 'en_US', 'de', ...] ''' raise NotImplementedError('list_languages() is not implemented ' 'by abstract spelling base class!') def check(self, word): ''' If `word` is a valid word in `self._language` (the currently active language), returns True. If the word shouldn't be checked, returns None (e.g. for ''). If it is not a valid word in `self._language`, return False. :Parameters: `word` : str The word to check. ''' raise NotImplementedError('check() not implemented by abstract ' + 'spelling base class!') def suggest(self, fragment): ''' For a given `fragment` (i.e. part of a word or a word by itself), provide corrections (`fragment` may be misspelled) or completions as a list of strings. :Parameters: `fragment` : str The word fragment to get suggestions/corrections for. E.g. 'foo' might become 'of', 'food' or 'foot'. ''' raise NotImplementedError('suggest() not implemented by abstract ' + 'spelling base class!') _libs = (('enchant', 'spelling_enchant', 'SpellingEnchant'), ) if sys.platform == 'darwin': _libs += (('osxappkit', 'spelling_osxappkit', 'SpellingOSXAppKit'), ) Spelling = core_select_lib('spelling', _libs) Kivy-1.9.0/kivy/core/video/0000775000175000017500000000000012507221737015453 5ustar titotito00000000000000Kivy-1.9.0/kivy/core/video/video_pygst.py0000664000175000017500000001360312462275426020370 0ustar titotito00000000000000''' Video PyGst =========== Implementation of a VideoBase using PyGST. This module is compatible only with Python 2. ''' # # Important notes: you must take care of glib event + python. If you connect() # directly an event to a python object method, the object will be ref, and will # be never unref. # To prevent memory leak, you must connect() to a func, and you might want to # pass the referenced object with weakref() # import pygst if not hasattr(pygst, '_gst_already_checked'): found = False for version in ('1.0', '0.10'): try: pygst.require(version) found = True break except: continue if found: pygst._gst_already_checked = True else: raise Exception('Unable to find a valid Gstreamer version to use') import gst from functools import partial from os import path from threading import Lock from urllib import pathname2url from weakref import ref from kivy.core.video import VideoBase from kivy.graphics.texture import Texture from kivy.logger import Logger from kivy.support import install_gobject_iteration install_gobject_iteration() def _gst_new_buffer(obj, appsink): obj = obj() if not obj: return with obj._buffer_lock: obj._buffer = obj._appsink.emit('pull-buffer') def _on_gst_message(bus, message): Logger.trace('VideoPyGst: (bus) %s' % str(message)) # log all error messages if message.type == gst.MESSAGE_ERROR: error, debug = list(map(str, message.parse_error())) Logger.error('VideoPyGst: %s' % error) Logger.debug('VideoPyGst: %s' % debug) def _on_gst_eos(obj, *largs): obj = obj() if not obj: return obj._do_eos() class VideoPyGst(VideoBase): def __init__(self, **kwargs): self._buffer_lock = Lock() self._buffer = None self._texture = None self._gst_init() super(VideoPyGst, self).__init__(**kwargs) def _gst_init(self): # self._appsink will receive the buffers so we can upload them to GPU self._appsink = gst.element_factory_make('appsink', '') self._appsink.set_property('caps', gst.Caps( 'video/x-raw-rgb,red_mask=(int)0xff0000,' 'green_mask=(int)0x00ff00,blue_mask=(int)0x0000ff')) self._appsink.set_property('async', True) self._appsink.set_property('drop', True) self._appsink.set_property('qos', True) self._appsink.set_property('emit-signals', True) self._appsink.connect('new-buffer', partial( _gst_new_buffer, ref(self))) # playbin, takes care of all, loading, playing, etc. # XXX playbin2 have some issue when playing some video or streaming :/ self._playbin = gst.element_factory_make('playbin', 'playbin') self._playbin.set_property('video-sink', self._appsink) # gstreamer bus, to attach and listen to gst messages self._bus = self._playbin.get_bus() self._bus.add_signal_watch() self._bus.connect('message', _on_gst_message) self._bus.connect('message::eos', partial( _on_gst_eos, ref(self))) def _update_texture(self, buf): # texture will be updated with newest buffer/frame size = None caps = buf.get_caps() _s = caps.get_structure(0) size = _s['width'], _s['height'] if not self._texture: # texture is not allocated yet, so create it first self._texture = Texture.create(size=size, colorfmt='rgb') self._texture.flip_vertical() self.dispatch('on_load') # upload texture data to GPU if self._texture: self._texture.blit_buffer(buf.data, size=size, colorfmt='rgb') def _update(self, dt): buf = None with self._buffer_lock: buf = self._buffer self._buffer = None if buf is not None: self._update_texture(buf) self.dispatch('on_frame') def unload(self): self._playbin.set_state(gst.STATE_NULL) self._buffer = None self._texture = None def load(self): Logger.debug('VideoPyGst: Load <%s>' % self._filename) self._playbin.set_state(gst.STATE_NULL) self._playbin.set_property('uri', self._get_uri()) self._playbin.set_state(gst.STATE_READY) def stop(self): '''.. versionchanged:: 1.4.0''' self._state = '' self._playbin.set_state(gst.STATE_PAUSED) def pause(self): '''.. versionadded:: 1.4.0''' self._state = 'paused' self._playbin.set_state(gst.STATE_PAUSED) def play(self): self._state = 'playing' self._playbin.set_state(gst.STATE_PLAYING) def seek(self, percent): seek_t = percent * self._get_duration() * 10e8 seek_format = gst.FORMAT_TIME seek_flags = gst.SEEK_FLAG_FLUSH | gst.SEEK_FLAG_KEY_UNIT self._playbin.seek_simple(seek_format, seek_flags, seek_t) #if pipeline is not playing, we need to pull pre-roll to update frame if not self._state == 'playing': with self._buffer_lock: self._buffer = self._appsink.emit('pull-preroll') def _get_uri(self): uri = self.filename if not uri: return if not '://' in uri: uri = 'file:' + pathname2url(path.realpath(uri)) return uri def _get_position(self): try: value, fmt = self._appsink.query_position(gst.FORMAT_TIME) return value / 10e8 except: return -1 def _get_duration(self): try: return self._playbin.query_duration(gst.FORMAT_TIME)[0] / 10e8 except: return -1 def _get_volume(self): self._volume = self._playbin.get_property('volume') return self._volume def _set_volume(self, volume): self._playbin.set_property('volume', volume) self._volume = volume Kivy-1.9.0/kivy/core/video/video_ffmpeg.py0000664000175000017500000000534312462275426020470 0ustar titotito00000000000000''' FFmpeg video abstraction ======================== .. versionadded:: 1.0.8 This abstraction requires ffmpeg python extensions. We have made a special extension that is used for the android platform but can also be used on x86 platforms. The project is available at:: http://github.com/tito/ffmpeg-android The extension is designed for implementing a video player. Refer to the documentation of the ffmpeg-android project for more information about the requirements. ''' try: import ffmpeg except: raise from kivy.core.video import VideoBase from kivy.graphics.texture import Texture class VideoFFMpeg(VideoBase): def __init__(self, **kwargs): self._do_load = False self._player = None super(VideoFFMpeg, self).__init__(**kwargs) def unload(self): if self._player: self._player.stop() self._player = None self._state = '' self._do_load = False def load(self): self.unload() def play(self): if self._player: self.unload() self._player = ffmpeg.FFVideo(self._filename) self._do_load = True def stop(self): self.unload() def seek(self, percent): if self._player is None: return self._player.seek(percent) def _do_eos(self): self.unload() self.dispatch('on_eos') super(VideoFFMpeg, self)._do_eos() def _update(self, dt): if self._do_load: self._player.open() self._do_load = False return player = self._player if player is None: return if player.is_open is False: self._do_eos() return frame = player.get_next_frame() if frame is None: return # first time we got a frame, we know that video is readed now. if self._texture is None: self._texture = Texture.create(size=( player.get_width(), player.get_height()), colorfmt='rgb') self._texture.flip_vertical() self.dispatch('on_load') if self._texture: self._texture.blit_buffer(frame) self.dispatch('on_frame') def _get_duration(self): if self._player is None: return 0 return self._player.get_duration() def _get_position(self): if self._player is None: return 0 return self._player.get_position() def _get_volume(self): if self._player is None: return 0 self._volume = self._player.get_volume() return self._volume def _set_volume(self, volume): if self._player is None: return self._player.set_volume(volume) Kivy-1.9.0/kivy/core/video/video_pyglet.py0000664000175000017500000000566212276430013020520 0ustar titotito00000000000000 ''' VideoPyglet: implementation of VideoBase with Pyglet ''' import pyglet from kivy.core.video import VideoBase #have to set these before importing pyglet.gl #otherwise pyglet creates a seperate gl context and fails # on error checks becasue we use pygame window pyglet.options['shadow_window'] = False pyglet.options['debug_gl'] = False import pyglet.gl class FakePygletContext: # another pyglet fix, because pyglet has a bugfix which is a bad hacked, # it checks for context._workaround_unpack_row_length..but we're using # the implicit context form pyglet or glut window # this means we cant have a pyglet window provider though! if we do, # this will break pyglet window context _workaround_unpack_row_length = False pyglet.gl.current_context = FakePygletContext() class VideoPyglet(VideoBase): '''VideoBase implementation using Pyglet ''' def unload(self): self.player = None self._source = None self._fbo = None def load(self): self.unload() # make sure we unload an resources #load media file and set size of video self._source = source = pyglet.media.load(self._filename) self._format = source.video_format self.size = (self._format.width, self._format.height) #load pyglet player and have it play teh video we loaded self._player = None self._player = pyglet.media.Player() self._player.queue(source) self.play() self.stop() # we have to keep track of tie ourselves.. # at least its the only way i can get pyglet player to restart, # _player.time does not get reset when you do seek(0) for soe reason, # and is read only self.time = self._player.time def _update(self, dt): if self._source.duration - self.time < 0.1: # we are at the end self.seek(0) if self.state == 'playing': # keep track of time into video self.time += dt # required by pyglet video if not in pyglet window self._player.dispatch_events(dt) if self._player.get_texture(): # TODO: blit the pyglet texture to our own texture. assert('TODO') def stop(self): self._player.pause() super(VideoPyglet, self).stop() def play(self): self._player.play() super(VideoPyglet, self).play() def seek(self, percent): t = self._source.duration * percent self.time = t self._player.seek(t) self.stop() def _get_position(self): if self._player: return self.time def _get_duration(self): if self._source: return self._source.duration def _get_volume(self): if self._player: return self._player.volume return 0 def _set_volume(self, volume): if self._player: self._player.volume = volume self.dispatch('on_frame') Kivy-1.9.0/kivy/core/video/video_null.py0000664000175000017500000000034012276430013020152 0ustar titotito00000000000000 ''' VideoNull: empty implementation of VideoBase for the no provider case ''' from kivy.core.video import VideoBase class VideoNull(VideoBase): '''VideoBase implementation when there is no provider. ''' pass Kivy-1.9.0/kivy/core/video/video_gi.py0000664000175000017500000001652712462275426017631 0ustar titotito00000000000000''' Video GI ======== Implementation of VideoBase with using pygi / gstreamer. Pygi is both compatible with Python 2 and 3. ''' # # Important notes: you must take care of glib event + python. If you connect() # directly an event to a python object method, the object will be ref, and will # be never unref. # To prevent memory leak, you must connect() to a func, and you might want to # pass the referenced object with weakref() # from gi.repository import Gst from functools import partial from os.path import realpath from threading import Lock from weakref import ref from kivy.compat import PY2 from kivy.core.video import VideoBase from kivy.graphics.texture import Texture from kivy.logger import Logger from kivy.support import install_gobject_iteration from ctypes import Structure, c_void_p, c_int, string_at import atexit if PY2: from urllib import pathname2url else: from urllib.request import pathname2url # initialize the video/gi. if the older version is used, don't use video_gi. Gst.init(None) version = Gst.version() if version < (1, 0, 0, 0): raise Exception('Cannot use video_gi, Gstreamer < 1.0 is not supported.') Logger.info('VideoGi: Using Gstreamer {}'.format( '.'.join(['{}'.format(x) for x in Gst.version()]))) install_gobject_iteration() class _MapInfo(Structure): _fields_ = [ ('memory', c_void_p), ('flags', c_int), ('data', c_void_p)] # we don't care about the rest def _gst_new_buffer(obj, appsink): obj = obj() if not obj: return with obj._buffer_lock: obj._buffer = obj._appsink.emit('pull-sample') return False def _on_gst_message(bus, message): Logger.trace('VideoGi: (bus) {}'.format(message)) # log all error messages if message.type == Gst.MessageType.ERROR: error, debug = list(map(str, message.parse_error())) Logger.error('VideoGi: {}'.format(error)) Logger.debug('VideoGi: {}'.format(debug)) def _on_gst_eos(obj, *largs): obj = obj() if not obj: return obj._do_eos() def _on_videogi_unref(obj): if obj in VideoGi._instances: VideoGi._instances.remove(obj) class VideoGi(VideoBase): _instances = [] def __init__(self, **kwargs): self._buffer_lock = Lock() self._buffer = None self._texture = None self._gst_init() wk = ref(self, _on_videogi_unref) VideoGi._instances.append(wk) super(VideoGi, self).__init__(**kwargs) def _gst_init(self): # self._appsink will receive the buffers so we can upload them to GPU self._appsink = Gst.ElementFactory.make('appsink', '') self._appsink.props.caps = Gst.caps_from_string( 'video/x-raw,format=RGB') self._appsink.props.async = True self._appsink.props.drop = True self._appsink.props.qos = True self._appsink.props.emit_signals = True self._appsink.connect('new-sample', partial( _gst_new_buffer, ref(self))) # playbin, takes care of all, loading, playing, etc. self._playbin = Gst.ElementFactory.make('playbin', 'playbin') self._playbin.props.video_sink = self._appsink # gstreamer bus, to attach and listen to gst messages self._bus = self._playbin.get_bus() self._bus.add_signal_watch() self._bus.connect('message', _on_gst_message) self._bus.connect('message::eos', partial( _on_gst_eos, ref(self))) def _update_texture(self, sample): # texture will be updated with newest buffer/frame # read the data from the buffer memory mapinfo = data = None try: buf = sample.get_buffer() result, mapinfo = buf.map(Gst.MapFlags.READ) # We cannot get the data out of mapinfo, using Gst 1.0.6 + Gi 3.8.0 # related bug report: # https://bugzilla.gnome.org/show_bug.cgi?id=678663 # ie: mapinfo.data is normally a char*, but here, we have an int # So right now, we use ctypes instead to read the mapinfo ourself. addr = mapinfo.__hash__() c_mapinfo = _MapInfo.from_address(addr) # now get the memory data = string_at(c_mapinfo.data, mapinfo.size) finally: if mapinfo is not None: buf.unmap(mapinfo) # upload the data to the GPU info = sample.get_caps().get_structure(0) size = info.get_value('width'), info.get_value('height') # texture is not allocated yet, create it first if not self._texture: self._texture = Texture.create(size=size, colorfmt='rgb') self._texture.flip_vertical() self.dispatch('on_load') if self._texture: self._texture.blit_buffer(data, size=size, colorfmt='rgb') def _update(self, dt): buf = None with self._buffer_lock: buf = self._buffer self._buffer = None if buf is not None: self._update_texture(buf) self.dispatch('on_frame') def unload(self): self._playbin.set_state(Gst.State.NULL) self._buffer = None self._texture = None def load(self): Logger.debug('VideoGi: Load <{}>'.format(self._filename)) self._playbin.set_state(Gst.State.NULL) self._playbin.props.uri = self._get_uri() self._playbin.set_state(Gst.State.READY) def stop(self): self._state = '' self._playbin.set_state(Gst.State.PAUSED) def pause(self): self._state = 'paused' self._playbin.set_state(Gst.State.PAUSED) def play(self): self._state = 'playing' self._playbin.set_state(Gst.State.PLAYING) def seek(self, percent): seek_t = percent * self._get_duration() * 10e8 seek_format = Gst.Format.TIME seek_flags = Gst.SeekFlags.FLUSH | Gst.SeekFlags.KEY_UNIT self._playbin.seek_simple(seek_format, seek_flags, seek_t) #if pipeline is not playing, we need to pull pre-roll to update frame if not self._state == 'playing': with self._buffer_lock: self._buffer = self._appsink.emit('pull-preroll') def _get_uri(self): uri = self.filename if not uri: return if not '://' in uri: uri = 'file:' + pathname2url(realpath(uri)) return uri def _get_position(self): try: ret, value = self._appsink.query_position(Gst.Format.TIME) if ret: return value / float(Gst.SECOND) except: pass return -1 def _get_duration(self): try: ret, value = self._playbin.query_duration(Gst.Format.TIME) if ret: return value / float(Gst.SECOND) except: pass return -1 def _get_volume(self): self._volume = self._playbin.props.volume return self._volume def _set_volume(self, volume): self._playbin.props.volume = volume self._volume = volume @atexit.register def video_gi_clean(): # if we leave the python process with some video running, we can hit a # segfault. This is forcing the stop/unload of all remaining videos before # exiting the python process. for weakvideo in VideoGi._instances: video = weakvideo() if video: video.stop() video.unload() Kivy-1.9.0/kivy/core/video/video_gstplayer.py0000664000175000017500000000741712462275426021242 0ustar titotito00000000000000''' Video Gstplayer =============== .. versionadded:: 1.8.0 Implementation of a VideoBase with Kivy :class:`~kivy.lib.gstplayer.GstPlayer` This player is the prefered player, using Gstreamer 1.0, working on both Python 2 and 3. ''' from kivy.lib.gstplayer import GstPlayer, get_gst_version from kivy.graphics.texture import Texture from kivy.core.video import VideoBase from kivy.logger import Logger from kivy.clock import Clock from kivy.compat import PY2 from threading import Lock from functools import partial from os.path import realpath from weakref import ref if PY2: from urllib import pathname2url else: from urllib.request import pathname2url Logger.info('VideoGstplayer: Using Gstreamer {}'.format( '.'.join(map(str, get_gst_version())))) def _on_gstplayer_buffer(video, width, height, data): video = video() # if we still receive the video but no more player, remove it. if not video: return with video._buffer_lock: video._buffer = (width, height, data) def _on_gstplayer_message(mtype, message): if mtype == 'error': Logger.error('VideoGstplayer: {}'.format(message)) elif mtype == 'warning': Logger.warning('VideoGstplayer: {}'.format(message)) elif mtype == 'info': Logger.info('VideoGstplayer: {}'.format(message)) class VideoGstplayer(VideoBase): def __init__(self, **kwargs): self.player = None self._buffer = None self._buffer_lock = Lock() super(VideoGstplayer, self).__init__(**kwargs) def _on_gst_eos_sync(self): Clock.schedule_once(self._do_eos, 0) def load(self): Logger.debug('VideoGstplayer: Load <{}>'.format(self._filename)) uri = self._get_uri() wk_self = ref(self) self.player_callback = partial(_on_gstplayer_buffer, wk_self) self.player = GstPlayer(uri, self.player_callback, self._on_gst_eos_sync, _on_gstplayer_message) self.player.load() def unload(self): if self.player: self.player.unload() self.player = None with self._buffer_lock: self._buffer = None self._texture = None def stop(self): super(VideoGstplayer, self).stop() self.player.stop() def pause(self): super(VideoGstplayer, self).pause() self.player.pause() def play(self): super(VideoGstplayer, self).play() self.player.set_volume(self.volume) self.player.play() def seek(self, percent): self.player.seek(percent) def _get_position(self): return self.player.get_position() def _get_duration(self): return self.player.get_duration() def _get_volume(self): return self._volume def _set_volume(self, value): self._volume = value if self.player: self.player.set_volume(self._volume) def _update(self, dt): buf = None with self._buffer_lock: buf = self._buffer self._buffer = None if buf is not None: self._update_texture(buf) self.dispatch('on_frame') def _update_texture(self, buf): width, height, data = buf # texture is not allocated yet, create it first if not self._texture: self._texture = Texture.create(size=(width, height), colorfmt='rgb') self._texture.flip_vertical() self.dispatch('on_load') if self._texture: self._texture.blit_buffer( data, size=(width, height), colorfmt='rgb') def _get_uri(self): uri = self.filename if not uri: return if not '://' in uri: uri = 'file:' + pathname2url(realpath(uri)) return uri Kivy-1.9.0/kivy/core/video/video_ffpyplayer.py0000664000175000017500000002477012465721151021404 0ustar titotito00000000000000''' FFmpeg based video abstraction ============================== To use, you need to install ffpyplyaer and have a compiled ffmpeg shared library. https://github.com/matham/ffpyplayer The docs there describe how to set this up. But briefly, first you need to compile ffmpeg using the shared flags while disabling the static flags (you'll probably have to set the fPIC flag, e.g. CFLAGS=-fPIC). Here's some instructions: https://trac.ffmpeg.org/wiki/CompilationGuide. For Windows, you can download compiled GPL binaries from http://ffmpeg.zeranoe.com/builds/. Similarly, you should download SDL. Now, you should a ffmpeg and sdl directory. In each, you should have a include, bin, and lib directory, where e.g. for Windows, lib contains the .dll.a files, while bin contains the actual dlls. The include directory holds the headers. The bin directory is only needed if the shared libraries are not already on the path. In the environment define FFMPEG_ROOT and SDL_ROOT, each pointing to the ffmpeg, and SDL directories, respectively. (If you're using SDL2, the include directory will contain a directory called SDL2, which then holds the headers). Once defined, download the ffpyplayer git and run python setup.py build_ext --inplace Finally, before running you need to ensure that ffpyplayer is in python's path. ..Note:: When kivy exits by closing the window while the video is playing, it appears that the __del__method of VideoFFPy is not called. Because of this the VideoFFPy object is not properly deleted when kivy exits. The consequence is that because MediaPlayer creates internal threads which do not have their daemon flag set, when the main threads exists it'll hang and wait for the other MediaPlayer threads to exit. But since __del__ is not called to delete the MediaPlayer object, those threads will remain alive hanging kivy. What this means is that you have to be sure to delete the MediaPlayer object before kivy exits by setting it to None. ''' __all__ = ('VideoFFPy', ) try: import ffpyplayer from ffpyplayer.player import MediaPlayer from ffpyplayer.tools import set_log_callback, loglevels, get_log_callback except: raise from threading import Thread from kivy.clock import Clock, mainthread from kivy.logger import Logger from kivy.core.video import VideoBase from kivy.graphics import Rectangle, BindTexture from kivy.graphics.texture import Texture from kivy.graphics.fbo import Fbo from kivy.weakmethod import WeakMethod import time Logger.info('VideoFFPy: Using ffpyplayer {}'.format(ffpyplayer.version)) logger_func = {'quiet': Logger.critical, 'panic': Logger.critical, 'fatal': Logger.critical, 'error': Logger.error, 'warning': Logger.warning, 'info': Logger.info, 'verbose': Logger.debug, 'debug': Logger.debug} def _log_callback(message, level): message = message.strip() if message: logger_func[level]('ffpyplayer: {}'.format(message)) if not get_log_callback(): set_log_callback(_log_callback) class VideoFFPy(VideoBase): YUV_RGB_FS = """ $HEADER$ uniform sampler2D tex_y; uniform sampler2D tex_u; uniform sampler2D tex_v; void main(void) { float y = texture2D(tex_y, tex_coord0).r; float u = texture2D(tex_u, tex_coord0).r - 0.5; float v = texture2D(tex_v, tex_coord0).r - 0.5; float r = y + 1.402 * v; float g = y - 0.344 * u - 0.714 * v; float b = y + 1.772 * u; gl_FragColor = vec4(r, g, b, 1.0); } """ def __init__(self, **kwargs): self._ffplayer = None self._thread = None self._next_frame = None self._ffplayer_need_quit = False self._callback_ref = WeakMethod(self._player_callback) self._trigger = Clock.create_trigger(self._redraw) super(VideoFFPy, self).__init__(**kwargs) def __del__(self): self.unload() if self._log_callback_set: set_log_callback(None) def _player_callback(self, selector, value): if self._ffplayer is None: return if selector == 'quit': def close(*args): self.unload() Clock.schedule_once(close, 0) def _get_position(self): if self._ffplayer is not None: return self._ffplayer.get_pts() return 0 def _set_position(self, pos): self.seek(pos) def _get_volume(self): if self._ffplayer is not None: self._volume = self._ffplayer.get_volume() return self._volume def _set_volume(self, volume): self._volume = volume if self._ffplayer is not None: self._ffplayer.set_volume(volume) def _get_duration(self): if self._ffplayer is None: return 0 return self._ffplayer.get_metadata()['duration'] @mainthread def _do_eos(self): if self.eos == 'pause': self.pause() elif self.eos == 'stop': self.stop() elif self.eos == 'loop': self.position = 0 self.dispatch('on_eos') @mainthread def _change_state(self, state): self._state = state def _redraw(self, *args): if not self._ffplayer: return next_frame = self._next_frame if not next_frame: return img, pts = next_frame if img.get_size() != self._size or self._texture is None: self._size = w, h = img.get_size() if self._out_fmt == 'yuv420p': w2 = int(w / 2) h2 = int(h / 2) self._tex_y = Texture.create( size=(w, h), colorfmt='luminance') self._tex_u = Texture.create( size=(w2, h2), colorfmt='luminance') self._tex_v = Texture.create( size=(w2, h2), colorfmt='luminance') self._fbo = fbo = Fbo(size=self._size) with fbo: BindTexture(texture=self._tex_u, index=1) BindTexture(texture=self._tex_v, index=2) Rectangle(size=fbo.size, texture=self._tex_y) fbo.shader.fs = VideoFFPy.YUV_RGB_FS fbo['tex_y'] = 0 fbo['tex_u'] = 1 fbo['tex_v'] = 2 self._texture = fbo.texture else: self._texture = Texture.create(size=self._size, colorfmt='rgba') # XXX FIXME #self.texture.add_reload_observer(self.reload_buffer) self._texture.flip_vertical() self.dispatch('on_load') if self._texture: if self._out_fmt == 'yuv420p': dy, du, dv, _ = img.to_memoryview() self._tex_y.blit_buffer(dy, colorfmt='luminance') self._tex_u.blit_buffer(du, colorfmt='luminance') self._tex_v.blit_buffer(dv, colorfmt='luminance') else: self._texture.blit_buffer( img.to_memoryview()[0], colorfmt='rgba') self._fbo.ask_update() self._fbo.draw() self.dispatch('on_frame') def _next_frame_run(self): ffplayer = self._ffplayer sleep = time.sleep trigger = self._trigger did_dispatch_eof = False # fast path, if the source video is yuv420p, we'll use a glsl shader for # buffer conversion to rgba while not self._ffplayer_need_quit: src_pix_fmt = ffplayer.get_metadata().get('src_pix_fmt') if not src_pix_fmt: sleep(0.005) continue if src_pix_fmt == 'yuv420p': self._out_fmt = 'yuv420p' ffplayer.set_output_pix_fmt(self._out_fmt) self._ffplayer.toggle_pause() break if self._ffplayer_need_quit: return # wait until loaded or failed, shouldn't take long, but just to make # sure metadata is available. s = time.clock() while not self._ffplayer_need_quit: if ffplayer.get_metadata()['src_vid_size'] != (0, 0): break # XXX if will fail later then? if time.clock() - s > 10.: break sleep(0.005) if self._ffplayer_need_quit: return # we got all the informations, now, get the frames :) self._change_state('playing') while not self._ffplayer_need_quit: t1 = time.time() frame, val = ffplayer.get_frame() t2 = time.time() if val == 'eof': sleep(0.2) if not did_dispatch_eof: self._do_eos() did_dispatch_eof = True elif val == 'paused': did_dispatch_eof = False sleep(0.2) else: did_dispatch_eof = False if frame: self._next_frame = frame trigger() else: val = val if val else (1 / 30.) sleep(val) def seek(self, percent): if self._ffplayer is None: return self._ffplayer.seek(percent * self._ffplayer.get_metadata() ['duration'], relative=False) self._next_frame = None def stop(self): self.unload() def pause(self): if self._ffplayer and self._state != 'paused': self._ffplayer.toggle_pause() self._state = 'paused' def play(self): if self._ffplayer and self._state == 'paused': self._ffplayer.toggle_pause() self._state = 'playing' return self.load() self._out_fmt = 'rgba' ff_opts = { 'paused': True, 'out_fmt': self._out_fmt } self._ffplayer = MediaPlayer( self._filename, callback=self._callback_ref, thread_lib='SDL', loglevel='info', ff_opts=ff_opts) self._thread = Thread(target=self._next_frame_run, name='Next frame') self._thread.daemon = True self._thread.start() def load(self): self.unload() def unload(self): Clock.unschedule(self._redraw) self._ffplayer_need_quit = True if self._thread: self._thread.join() self._thread = None if self._ffplayer: self._ffplayer = None self._next_frame = None self._size = (0, 0) self._state = '' self._ffplayer_need_quit = False Kivy-1.9.0/kivy/core/video/__init__.py0000664000175000017500000001364112462275426017575 0ustar titotito00000000000000''' Video ===== Core class for reading video files and managing the :class:`kivy.graphics.texture.Texture` video. .. versionchanged:: 1.8.0 There is now 2 distinct Gstreamer implementation: one using Gi/Gst working for both Python 2+3 with Gstreamer 1.0, and one using PyGST working only for Python 2 + Gstreamer 0.10. If you have issue with GStreamer, have a look at :ref:`gstreamer-compatibility` .. note:: Recording is not supported. ''' __all__ = ('VideoBase', 'Video') from kivy.clock import Clock from kivy.core import core_select_lib from kivy.event import EventDispatcher from kivy.logger import Logger from kivy.compat import PY2 class VideoBase(EventDispatcher): '''VideoBase, a class used to implement a video reader. :Parameters: `filename` : str Filename of the video. Can be a file or an URI. `eos` : str, defaults to 'pause' Action to take when EOS is hit. Can be one of 'pause', 'stop' or 'loop'. .. versionchanged:: unknown added 'pause' `async` : bool, defaults to True Load the video asynchronously (may be not supported by all providers). `autoplay` : bool, defaults to False Auto play the video on init. :Events: `on_eos` Fired when EOS is hit. `on_load` Fired when the video is loaded and the texture is available. `on_frame` Fired when a new frame is written to the texture. ''' __slots__ = ('_wantplay', '_buffer', '_filename', '_texture', '_volume', 'eos', '_state', '_async', '_autoplay') __events__ = ('on_eos', 'on_load', 'on_frame') def __init__(self, **kwargs): kwargs.setdefault('filename', None) kwargs.setdefault('eos', 'stop') kwargs.setdefault('async', True) kwargs.setdefault('autoplay', False) super(VideoBase, self).__init__() self._wantplay = False self._buffer = None self._filename = None self._texture = None self._volume = 1. self._state = '' self._autoplay = kwargs.get('autoplay') self._async = kwargs.get('async') self.eos = kwargs.get('eos') if self.eos == 'pause': Logger.warning("'pause' is deprecated. Use 'stop' instead.") self.eos = 'stop' self.filename = kwargs.get('filename') Clock.schedule_interval(self._update, 1 / 30.) if self._autoplay: self.play() def __del__(self): self.unload() def on_eos(self): pass def on_load(self): pass def on_frame(self): pass def _get_filename(self): return self._filename def _set_filename(self, filename): if filename == self._filename: return self.unload() self._filename = filename if self._filename is None: return self.load() filename = property(lambda self: self._get_filename(), lambda self, x: self._set_filename(x), doc='Get/set the filename/uri of the current video') def _get_position(self): return 0 def _set_position(self, pos): self.seek(pos) position = property(lambda self: self._get_position(), lambda self, x: self._set_position(x), doc='Get/set the position in the video (in seconds)') def _get_volume(self): return self._volume def _set_volume(self, volume): self._volume = volume volume = property(lambda self: self._get_volume(), lambda self, x: self._set_volume(x), doc='Get/set the volume in the video (1.0 = 100%)') def _get_duration(self): return 0 duration = property(lambda self: self._get_duration(), doc='Get the video duration (in seconds)') def _get_texture(self): return self._texture texture = property(lambda self: self._get_texture(), doc='Get the video texture') def _get_state(self): return self._state state = property(lambda self: self._get_state(), doc='Get the video playing status') def _do_eos(self, *args): ''' .. versionchanged:: 1.4.0 Now dispatches the `on_eos` event. ''' if self.eos == 'pause': self.pause() elif self.eos == 'stop': self.stop() elif self.eos == 'loop': self.position = 0 self.play() self.dispatch('on_eos') def _update(self, dt): '''Update the video content to texture. ''' pass def seek(self, percent): '''Move on percent position''' pass def stop(self): '''Stop the video playing''' self._state = '' def pause(self): '''Pause the video .. versionadded:: 1.4.0 ''' self._state = 'paused' def play(self): '''Play the video''' self._state = 'playing' def load(self): '''Load the video from the current filename''' pass def unload(self): '''Unload the actual video''' self._state = '' # Load the appropriate provider video_providers = [] try: from kivy.lib.gstplayer import GstPlayer # NOQA video_providers += [('gstplayer', 'video_gstplayer', 'VideoGstplayer')] except ImportError: #video_providers += [('gi', 'video_gi', 'VideoGi')] if PY2: # if peoples do not have gi, fallback on pygst, only for python2 video_providers += [ ('pygst', 'video_pygst', 'VideoPyGst')] video_providers += [ ('ffmpeg', 'video_ffmpeg', 'VideoFFMpeg'), ('ffpyplayer', 'video_ffpyplayer', 'VideoFFPy'), ('pyglet', 'video_pyglet', 'VideoPyglet'), ('null', 'video_null', 'VideoNull')] Video = core_select_lib('video', video_providers) Kivy-1.9.0/kivy/core/gl/0000775000175000017500000000000012507221737014747 5ustar titotito00000000000000Kivy-1.9.0/kivy/core/gl/__init__.py0000664000175000017500000000541012276430013017050 0ustar titotito00000000000000# pylint: disable=W0611 ''' OpenGL ====== Select and use the best OpenGL library available. Depending on your system, the core provider can select an OpenGL ES or a 'classic' desktop OpenGL library. ''' from os import environ from sys import platform as sysplatform, exit MIN_REQUIRED_GL_VERSION = (2, 0) def msgbox(message): if sysplatform == 'win32': import ctypes ctypes.windll.user32.MessageBoxW( None, message, u"Kivy Fatal Error", 0) exit(1) if 'KIVY_DOC' not in environ: from kivy.logger import Logger from kivy.graphics import gl_init_resources from kivy.graphics.opengl_utils import gl_get_version from kivy.graphics.opengl import GL_VERSION, GL_VENDOR, GL_RENDERER, \ GL_MAX_TEXTURE_IMAGE_UNITS, GL_MAX_TEXTURE_SIZE, \ GL_SHADING_LANGUAGE_VERSION,\ glGetString, glGetIntegerv, gl_init_symbols from kivy.utils import platform def init_gl(): gl_init_symbols() print_gl_version() gl_init_resources() def print_gl_version(): version = glGetString(GL_VERSION) vendor = glGetString(GL_VENDOR) renderer = glGetString(GL_RENDERER) Logger.info('GL: OpenGL version <{0}>'.format(version)) Logger.info('GL: OpenGL vendor <{0}>'.format(vendor)) Logger.info('GL: OpenGL renderer <{0}>'.format(renderer)) # Let the user know if his graphics hardware/drivers are too old major, minor = gl_get_version() Logger.info('GL: OpenGL parsed version: %d, %d' % (major, minor)) if (major, minor) < MIN_REQUIRED_GL_VERSION: msg = ( 'GL: Minimum required OpenGL version (2.0) NOT found!\n\n' 'OpenGL version detected: {0}.{1}\n\n' 'Version: {2}\nVendor: {3}\nRenderer: {4}\n\n' 'Try upgrading your graphics drivers and/or your ' 'graphics hardware in case of problems.\n\n' 'The application will leave now.').format( major, minor, version, vendor, renderer) Logger.critical(msg) msgbox(msg) if platform != 'android': # XXX in the android emulator (latest version at 22 march 2013), # this call was segfaulting the gl stack. Logger.info('GL: Shading version <{0}>'.format( glGetString(GL_SHADING_LANGUAGE_VERSION))) Logger.info('GL: Texture max size <{0}>'.format( glGetIntegerv(GL_MAX_TEXTURE_SIZE)[0])) Logger.info('GL: Texture max units <{0}>'.format( glGetIntegerv(GL_MAX_TEXTURE_IMAGE_UNITS)[0])) # To be able to use our GL provider, we must have a window # Automaticly import window auto to ensure the default window creation import kivy.core.window # NOQA Kivy-1.9.0/kivy/core/window/0000775000175000017500000000000012507221737015654 5ustar titotito00000000000000Kivy-1.9.0/kivy/core/window/window_x11_keytab.c0000664000175000017500000017734712263260206021373 0ustar titotito00000000000000struct codepair { unsigned short keysym; unsigned short ucs; } keysymtab[] = { { 0x01a1, 0x0104 }, /* Aogonek Ą LATIN CAPITAL LETTER A WITH OGONEK */ { 0x01a2, 0x02d8 }, /* breve ˘ BREVE */ { 0x01a3, 0x0141 }, /* Lstroke Ł LATIN CAPITAL LETTER L WITH STROKE */ { 0x01a5, 0x013d }, /* Lcaron Ľ LATIN CAPITAL LETTER L WITH CARON */ { 0x01a6, 0x015a }, /* Sacute Ś LATIN CAPITAL LETTER S WITH ACUTE */ { 0x01a9, 0x0160 }, /* Scaron Š LATIN CAPITAL LETTER S WITH CARON */ { 0x01aa, 0x015e }, /* Scedilla Ş LATIN CAPITAL LETTER S WITH CEDILLA */ { 0x01ab, 0x0164 }, /* Tcaron Ť LATIN CAPITAL LETTER T WITH CARON */ { 0x01ac, 0x0179 }, /* Zacute Ź LATIN CAPITAL LETTER Z WITH ACUTE */ { 0x01ae, 0x017d }, /* Zcaron Ž LATIN CAPITAL LETTER Z WITH CARON */ { 0x01af, 0x017b }, /* Zabovedot Ż LATIN CAPITAL LETTER Z WITH DOT ABOVE */ { 0x01b1, 0x0105 }, /* aogonek ą LATIN SMALL LETTER A WITH OGONEK */ { 0x01b2, 0x02db }, /* ogonek ˛ OGONEK */ { 0x01b3, 0x0142 }, /* lstroke ł LATIN SMALL LETTER L WITH STROKE */ { 0x01b5, 0x013e }, /* lcaron ľ LATIN SMALL LETTER L WITH CARON */ { 0x01b6, 0x015b }, /* sacute ś LATIN SMALL LETTER S WITH ACUTE */ { 0x01b7, 0x02c7 }, /* caron ˇ CARON */ { 0x01b9, 0x0161 }, /* scaron š LATIN SMALL LETTER S WITH CARON */ { 0x01ba, 0x015f }, /* scedilla ş LATIN SMALL LETTER S WITH CEDILLA */ { 0x01bb, 0x0165 }, /* tcaron ť LATIN SMALL LETTER T WITH CARON */ { 0x01bc, 0x017a }, /* zacute ź LATIN SMALL LETTER Z WITH ACUTE */ { 0x01bd, 0x02dd }, /* doubleacute ˝ DOUBLE ACUTE ACCENT */ { 0x01be, 0x017e }, /* zcaron ž LATIN SMALL LETTER Z WITH CARON */ { 0x01bf, 0x017c }, /* zabovedot ż LATIN SMALL LETTER Z WITH DOT ABOVE */ { 0x01c0, 0x0154 }, /* Racute Ŕ LATIN CAPITAL LETTER R WITH ACUTE */ { 0x01c3, 0x0102 }, /* Abreve Ă LATIN CAPITAL LETTER A WITH BREVE */ { 0x01c5, 0x0139 }, /* Lacute Ĺ LATIN CAPITAL LETTER L WITH ACUTE */ { 0x01c6, 0x0106 }, /* Cacute Ć LATIN CAPITAL LETTER C WITH ACUTE */ { 0x01c8, 0x010c }, /* Ccaron Č LATIN CAPITAL LETTER C WITH CARON */ { 0x01ca, 0x0118 }, /* Eogonek Ę LATIN CAPITAL LETTER E WITH OGONEK */ { 0x01cc, 0x011a }, /* Ecaron Ě LATIN CAPITAL LETTER E WITH CARON */ { 0x01cf, 0x010e }, /* Dcaron Ď LATIN CAPITAL LETTER D WITH CARON */ { 0x01d0, 0x0110 }, /* Dstroke Đ LATIN CAPITAL LETTER D WITH STROKE */ { 0x01d1, 0x0143 }, /* Nacute Ń LATIN CAPITAL LETTER N WITH ACUTE */ { 0x01d2, 0x0147 }, /* Ncaron Ň LATIN CAPITAL LETTER N WITH CARON */ { 0x01d5, 0x0150 }, /* Odoubleacute Ő LATIN CAPITAL LETTER O WITH DOUBLE ACUTE */ { 0x01d8, 0x0158 }, /* Rcaron Ř LATIN CAPITAL LETTER R WITH CARON */ { 0x01d9, 0x016e }, /* Uring Ů LATIN CAPITAL LETTER U WITH RING ABOVE */ { 0x01db, 0x0170 }, /* Udoubleacute Ű LATIN CAPITAL LETTER U WITH DOUBLE ACUTE */ { 0x01de, 0x0162 }, /* Tcedilla Ţ LATIN CAPITAL LETTER T WITH CEDILLA */ { 0x01e0, 0x0155 }, /* racute ŕ LATIN SMALL LETTER R WITH ACUTE */ { 0x01e3, 0x0103 }, /* abreve ă LATIN SMALL LETTER A WITH BREVE */ { 0x01e5, 0x013a }, /* lacute ĺ LATIN SMALL LETTER L WITH ACUTE */ { 0x01e6, 0x0107 }, /* cacute ć LATIN SMALL LETTER C WITH ACUTE */ { 0x01e8, 0x010d }, /* ccaron č LATIN SMALL LETTER C WITH CARON */ { 0x01ea, 0x0119 }, /* eogonek ę LATIN SMALL LETTER E WITH OGONEK */ { 0x01ec, 0x011b }, /* ecaron ě LATIN SMALL LETTER E WITH CARON */ { 0x01ef, 0x010f }, /* dcaron ď LATIN SMALL LETTER D WITH CARON */ { 0x01f0, 0x0111 }, /* dstroke đ LATIN SMALL LETTER D WITH STROKE */ { 0x01f1, 0x0144 }, /* nacute ń LATIN SMALL LETTER N WITH ACUTE */ { 0x01f2, 0x0148 }, /* ncaron ň LATIN SMALL LETTER N WITH CARON */ { 0x01f5, 0x0151 }, /* odoubleacute ő LATIN SMALL LETTER O WITH DOUBLE ACUTE */ { 0x01f8, 0x0159 }, /* rcaron ř LATIN SMALL LETTER R WITH CARON */ { 0x01f9, 0x016f }, /* uring ů LATIN SMALL LETTER U WITH RING ABOVE */ { 0x01fb, 0x0171 }, /* udoubleacute ű LATIN SMALL LETTER U WITH DOUBLE ACUTE */ { 0x01fe, 0x0163 }, /* tcedilla ţ LATIN SMALL LETTER T WITH CEDILLA */ { 0x01ff, 0x02d9 }, /* abovedot ˙ DOT ABOVE */ { 0x02a1, 0x0126 }, /* Hstroke Ħ LATIN CAPITAL LETTER H WITH STROKE */ { 0x02a6, 0x0124 }, /* Hcircumflex Ĥ LATIN CAPITAL LETTER H WITH CIRCUMFLEX */ { 0x02a9, 0x0130 }, /* Iabovedot İ LATIN CAPITAL LETTER I WITH DOT ABOVE */ { 0x02ab, 0x011e }, /* Gbreve Ğ LATIN CAPITAL LETTER G WITH BREVE */ { 0x02ac, 0x0134 }, /* Jcircumflex Ĵ LATIN CAPITAL LETTER J WITH CIRCUMFLEX */ { 0x02b1, 0x0127 }, /* hstroke ħ LATIN SMALL LETTER H WITH STROKE */ { 0x02b6, 0x0125 }, /* hcircumflex ĥ LATIN SMALL LETTER H WITH CIRCUMFLEX */ { 0x02b9, 0x0131 }, /* idotless ı LATIN SMALL LETTER DOTLESS I */ { 0x02bb, 0x011f }, /* gbreve ğ LATIN SMALL LETTER G WITH BREVE */ { 0x02bc, 0x0135 }, /* jcircumflex ĵ LATIN SMALL LETTER J WITH CIRCUMFLEX */ { 0x02c5, 0x010a }, /* Cabovedot Ċ LATIN CAPITAL LETTER C WITH DOT ABOVE */ { 0x02c6, 0x0108 }, /* Ccircumflex Ĉ LATIN CAPITAL LETTER C WITH CIRCUMFLEX */ { 0x02d5, 0x0120 }, /* Gabovedot Ġ LATIN CAPITAL LETTER G WITH DOT ABOVE */ { 0x02d8, 0x011c }, /* Gcircumflex Ĝ LATIN CAPITAL LETTER G WITH CIRCUMFLEX */ { 0x02dd, 0x016c }, /* Ubreve Ŭ LATIN CAPITAL LETTER U WITH BREVE */ { 0x02de, 0x015c }, /* Scircumflex Ŝ LATIN CAPITAL LETTER S WITH CIRCUMFLEX */ { 0x02e5, 0x010b }, /* cabovedot ċ LATIN SMALL LETTER C WITH DOT ABOVE */ { 0x02e6, 0x0109 }, /* ccircumflex ĉ LATIN SMALL LETTER C WITH CIRCUMFLEX */ { 0x02f5, 0x0121 }, /* gabovedot ġ LATIN SMALL LETTER G WITH DOT ABOVE */ { 0x02f8, 0x011d }, /* gcircumflex ĝ LATIN SMALL LETTER G WITH CIRCUMFLEX */ { 0x02fd, 0x016d }, /* ubreve ŭ LATIN SMALL LETTER U WITH BREVE */ { 0x02fe, 0x015d }, /* scircumflex ŝ LATIN SMALL LETTER S WITH CIRCUMFLEX */ { 0x03a2, 0x0138 }, /* kra ĸ LATIN SMALL LETTER KRA */ { 0x03a3, 0x0156 }, /* Rcedilla Ŗ LATIN CAPITAL LETTER R WITH CEDILLA */ { 0x03a5, 0x0128 }, /* Itilde Ĩ LATIN CAPITAL LETTER I WITH TILDE */ { 0x03a6, 0x013b }, /* Lcedilla Ļ LATIN CAPITAL LETTER L WITH CEDILLA */ { 0x03aa, 0x0112 }, /* Emacron Ē LATIN CAPITAL LETTER E WITH MACRON */ { 0x03ab, 0x0122 }, /* Gcedilla Ģ LATIN CAPITAL LETTER G WITH CEDILLA */ { 0x03ac, 0x0166 }, /* Tslash Ŧ LATIN CAPITAL LETTER T WITH STROKE */ { 0x03b3, 0x0157 }, /* rcedilla ŗ LATIN SMALL LETTER R WITH CEDILLA */ { 0x03b5, 0x0129 }, /* itilde ĩ LATIN SMALL LETTER I WITH TILDE */ { 0x03b6, 0x013c }, /* lcedilla ļ LATIN SMALL LETTER L WITH CEDILLA */ { 0x03ba, 0x0113 }, /* emacron ē LATIN SMALL LETTER E WITH MACRON */ { 0x03bb, 0x0123 }, /* gcedilla ģ LATIN SMALL LETTER G WITH CEDILLA */ { 0x03bc, 0x0167 }, /* tslash ŧ LATIN SMALL LETTER T WITH STROKE */ { 0x03bd, 0x014a }, /* ENG Ŋ LATIN CAPITAL LETTER ENG */ { 0x03bf, 0x014b }, /* eng ŋ LATIN SMALL LETTER ENG */ { 0x03c0, 0x0100 }, /* Amacron Ā LATIN CAPITAL LETTER A WITH MACRON */ { 0x03c7, 0x012e }, /* Iogonek Į LATIN CAPITAL LETTER I WITH OGONEK */ { 0x03cc, 0x0116 }, /* Eabovedot Ė LATIN CAPITAL LETTER E WITH DOT ABOVE */ { 0x03cf, 0x012a }, /* Imacron Ī LATIN CAPITAL LETTER I WITH MACRON */ { 0x03d1, 0x0145 }, /* Ncedilla Ņ LATIN CAPITAL LETTER N WITH CEDILLA */ { 0x03d2, 0x014c }, /* Omacron Ō LATIN CAPITAL LETTER O WITH MACRON */ { 0x03d3, 0x0136 }, /* Kcedilla Ķ LATIN CAPITAL LETTER K WITH CEDILLA */ { 0x03d9, 0x0172 }, /* Uogonek Ų LATIN CAPITAL LETTER U WITH OGONEK */ { 0x03dd, 0x0168 }, /* Utilde Ũ LATIN CAPITAL LETTER U WITH TILDE */ { 0x03de, 0x016a }, /* Umacron Ū LATIN CAPITAL LETTER U WITH MACRON */ { 0x03e0, 0x0101 }, /* amacron ā LATIN SMALL LETTER A WITH MACRON */ { 0x03e7, 0x012f }, /* iogonek į LATIN SMALL LETTER I WITH OGONEK */ { 0x03ec, 0x0117 }, /* eabovedot ė LATIN SMALL LETTER E WITH DOT ABOVE */ { 0x03ef, 0x012b }, /* imacron ī LATIN SMALL LETTER I WITH MACRON */ { 0x03f1, 0x0146 }, /* ncedilla ņ LATIN SMALL LETTER N WITH CEDILLA */ { 0x03f2, 0x014d }, /* omacron ō LATIN SMALL LETTER O WITH MACRON */ { 0x03f3, 0x0137 }, /* kcedilla ķ LATIN SMALL LETTER K WITH CEDILLA */ { 0x03f9, 0x0173 }, /* uogonek ų LATIN SMALL LETTER U WITH OGONEK */ { 0x03fd, 0x0169 }, /* utilde ũ LATIN SMALL LETTER U WITH TILDE */ { 0x03fe, 0x016b }, /* umacron ū LATIN SMALL LETTER U WITH MACRON */ { 0x047e, 0x203e }, /* overline ‾ OVERLINE */ { 0x04a1, 0x3002 }, /* kana_fullstop 。 IDEOGRAPHIC FULL STOP */ { 0x04a2, 0x300c }, /* kana_openingbracket 「 LEFT CORNER BRACKET */ { 0x04a3, 0x300d }, /* kana_closingbracket 」 RIGHT CORNER BRACKET */ { 0x04a4, 0x3001 }, /* kana_comma 、 IDEOGRAPHIC COMMA */ { 0x04a5, 0x30fb }, /* kana_conjunctive ・ KATAKANA MIDDLE DOT */ { 0x04a6, 0x30f2 }, /* kana_WO ヲ KATAKANA LETTER WO */ { 0x04a7, 0x30a1 }, /* kana_a ァ KATAKANA LETTER SMALL A */ { 0x04a8, 0x30a3 }, /* kana_i ィ KATAKANA LETTER SMALL I */ { 0x04a9, 0x30a5 }, /* kana_u ゥ KATAKANA LETTER SMALL U */ { 0x04aa, 0x30a7 }, /* kana_e ェ KATAKANA LETTER SMALL E */ { 0x04ab, 0x30a9 }, /* kana_o ォ KATAKANA LETTER SMALL O */ { 0x04ac, 0x30e3 }, /* kana_ya ャ KATAKANA LETTER SMALL YA */ { 0x04ad, 0x30e5 }, /* kana_yu ュ KATAKANA LETTER SMALL YU */ { 0x04ae, 0x30e7 }, /* kana_yo ョ KATAKANA LETTER SMALL YO */ { 0x04af, 0x30c3 }, /* kana_tsu ッ KATAKANA LETTER SMALL TU */ { 0x04b0, 0x30fc }, /* prolongedsound ー KATAKANA-HIRAGANA PROLONGED SOUND MARK */ { 0x04b1, 0x30a2 }, /* kana_A ア KATAKANA LETTER A */ { 0x04b2, 0x30a4 }, /* kana_I イ KATAKANA LETTER I */ { 0x04b3, 0x30a6 }, /* kana_U ウ KATAKANA LETTER U */ { 0x04b4, 0x30a8 }, /* kana_E エ KATAKANA LETTER E */ { 0x04b5, 0x30aa }, /* kana_O オ KATAKANA LETTER O */ { 0x04b6, 0x30ab }, /* kana_KA カ KATAKANA LETTER KA */ { 0x04b7, 0x30ad }, /* kana_KI キ KATAKANA LETTER KI */ { 0x04b8, 0x30af }, /* kana_KU ク KATAKANA LETTER KU */ { 0x04b9, 0x30b1 }, /* kana_KE ケ KATAKANA LETTER KE */ { 0x04ba, 0x30b3 }, /* kana_KO コ KATAKANA LETTER KO */ { 0x04bb, 0x30b5 }, /* kana_SA サ KATAKANA LETTER SA */ { 0x04bc, 0x30b7 }, /* kana_SHI シ KATAKANA LETTER SI */ { 0x04bd, 0x30b9 }, /* kana_SU ス KATAKANA LETTER SU */ { 0x04be, 0x30bb }, /* kana_SE セ KATAKANA LETTER SE */ { 0x04bf, 0x30bd }, /* kana_SO ソ KATAKANA LETTER SO */ { 0x04c0, 0x30bf }, /* kana_TA タ KATAKANA LETTER TA */ { 0x04c1, 0x30c1 }, /* kana_CHI チ KATAKANA LETTER TI */ { 0x04c2, 0x30c4 }, /* kana_TSU ツ KATAKANA LETTER TU */ { 0x04c3, 0x30c6 }, /* kana_TE テ KATAKANA LETTER TE */ { 0x04c4, 0x30c8 }, /* kana_TO ト KATAKANA LETTER TO */ { 0x04c5, 0x30ca }, /* kana_NA ナ KATAKANA LETTER NA */ { 0x04c6, 0x30cb }, /* kana_NI ニ KATAKANA LETTER NI */ { 0x04c7, 0x30cc }, /* kana_NU ヌ KATAKANA LETTER NU */ { 0x04c8, 0x30cd }, /* kana_NE ネ KATAKANA LETTER NE */ { 0x04c9, 0x30ce }, /* kana_NO ノ KATAKANA LETTER NO */ { 0x04ca, 0x30cf }, /* kana_HA ハ KATAKANA LETTER HA */ { 0x04cb, 0x30d2 }, /* kana_HI ヒ KATAKANA LETTER HI */ { 0x04cc, 0x30d5 }, /* kana_FU フ KATAKANA LETTER HU */ { 0x04cd, 0x30d8 }, /* kana_HE ヘ KATAKANA LETTER HE */ { 0x04ce, 0x30db }, /* kana_HO ホ KATAKANA LETTER HO */ { 0x04cf, 0x30de }, /* kana_MA マ KATAKANA LETTER MA */ { 0x04d0, 0x30df }, /* kana_MI ミ KATAKANA LETTER MI */ { 0x04d1, 0x30e0 }, /* kana_MU ム KATAKANA LETTER MU */ { 0x04d2, 0x30e1 }, /* kana_ME メ KATAKANA LETTER ME */ { 0x04d3, 0x30e2 }, /* kana_MO モ KATAKANA LETTER MO */ { 0x04d4, 0x30e4 }, /* kana_YA ヤ KATAKANA LETTER YA */ { 0x04d5, 0x30e6 }, /* kana_YU ユ KATAKANA LETTER YU */ { 0x04d6, 0x30e8 }, /* kana_YO ヨ KATAKANA LETTER YO */ { 0x04d7, 0x30e9 }, /* kana_RA ラ KATAKANA LETTER RA */ { 0x04d8, 0x30ea }, /* kana_RI リ KATAKANA LETTER RI */ { 0x04d9, 0x30eb }, /* kana_RU ル KATAKANA LETTER RU */ { 0x04da, 0x30ec }, /* kana_RE レ KATAKANA LETTER RE */ { 0x04db, 0x30ed }, /* kana_RO ロ KATAKANA LETTER RO */ { 0x04dc, 0x30ef }, /* kana_WA ワ KATAKANA LETTER WA */ { 0x04dd, 0x30f3 }, /* kana_N ン KATAKANA LETTER N */ { 0x04de, 0x309b }, /* voicedsound ゛ KATAKANA-HIRAGANA VOICED SOUND MARK */ { 0x04df, 0x309c }, /* semivoicedsound ゜ KATAKANA-HIRAGANA SEMI-VOICED SOUND MARK */ { 0x05ac, 0x060c }, /* Arabic_comma ، ARABIC COMMA */ { 0x05bb, 0x061b }, /* Arabic_semicolon ؛ ARABIC SEMICOLON */ { 0x05bf, 0x061f }, /* Arabic_question_mark ؟ ARABIC QUESTION MARK */ { 0x05c1, 0x0621 }, /* Arabic_hamza ء ARABIC LETTER HAMZA */ { 0x05c2, 0x0622 }, /* Arabic_maddaonalef آ ARABIC LETTER ALEF WITH MADDA ABOVE */ { 0x05c3, 0x0623 }, /* Arabic_hamzaonalef أ ARABIC LETTER ALEF WITH HAMZA ABOVE */ { 0x05c4, 0x0624 }, /* Arabic_hamzaonwaw ؤ ARABIC LETTER WAW WITH HAMZA ABOVE */ { 0x05c5, 0x0625 }, /* Arabic_hamzaunderalef إ ARABIC LETTER ALEF WITH HAMZA BELOW */ { 0x05c6, 0x0626 }, /* Arabic_hamzaonyeh ئ ARABIC LETTER YEH WITH HAMZA ABOVE */ { 0x05c7, 0x0627 }, /* Arabic_alef ا ARABIC LETTER ALEF */ { 0x05c8, 0x0628 }, /* Arabic_beh ب ARABIC LETTER BEH */ { 0x05c9, 0x0629 }, /* Arabic_tehmarbuta ة ARABIC LETTER TEH MARBUTA */ { 0x05ca, 0x062a }, /* Arabic_teh ت ARABIC LETTER TEH */ { 0x05cb, 0x062b }, /* Arabic_theh ث ARABIC LETTER THEH */ { 0x05cc, 0x062c }, /* Arabic_jeem ج ARABIC LETTER JEEM */ { 0x05cd, 0x062d }, /* Arabic_hah ح ARABIC LETTER HAH */ { 0x05ce, 0x062e }, /* Arabic_khah خ ARABIC LETTER KHAH */ { 0x05cf, 0x062f }, /* Arabic_dal د ARABIC LETTER DAL */ { 0x05d0, 0x0630 }, /* Arabic_thal ذ ARABIC LETTER THAL */ { 0x05d1, 0x0631 }, /* Arabic_ra ر ARABIC LETTER REH */ { 0x05d2, 0x0632 }, /* Arabic_zain ز ARABIC LETTER ZAIN */ { 0x05d3, 0x0633 }, /* Arabic_seen س ARABIC LETTER SEEN */ { 0x05d4, 0x0634 }, /* Arabic_sheen ش ARABIC LETTER SHEEN */ { 0x05d5, 0x0635 }, /* Arabic_sad ص ARABIC LETTER SAD */ { 0x05d6, 0x0636 }, /* Arabic_dad ض ARABIC LETTER DAD */ { 0x05d7, 0x0637 }, /* Arabic_tah ط ARABIC LETTER TAH */ { 0x05d8, 0x0638 }, /* Arabic_zah ظ ARABIC LETTER ZAH */ { 0x05d9, 0x0639 }, /* Arabic_ain ع ARABIC LETTER AIN */ { 0x05da, 0x063a }, /* Arabic_ghain غ ARABIC LETTER GHAIN */ { 0x05e0, 0x0640 }, /* Arabic_tatweel ـ ARABIC TATWEEL */ { 0x05e1, 0x0641 }, /* Arabic_feh ف ARABIC LETTER FEH */ { 0x05e2, 0x0642 }, /* Arabic_qaf ق ARABIC LETTER QAF */ { 0x05e3, 0x0643 }, /* Arabic_kaf ك ARABIC LETTER KAF */ { 0x05e4, 0x0644 }, /* Arabic_lam ل ARABIC LETTER LAM */ { 0x05e5, 0x0645 }, /* Arabic_meem م ARABIC LETTER MEEM */ { 0x05e6, 0x0646 }, /* Arabic_noon ن ARABIC LETTER NOON */ { 0x05e7, 0x0647 }, /* Arabic_ha ه ARABIC LETTER HEH */ { 0x05e8, 0x0648 }, /* Arabic_waw و ARABIC LETTER WAW */ { 0x05e9, 0x0649 }, /* Arabic_alefmaksura ى ARABIC LETTER ALEF MAKSURA */ { 0x05ea, 0x064a }, /* Arabic_yeh ي ARABIC LETTER YEH */ { 0x05eb, 0x064b }, /* Arabic_fathatan ً ARABIC FATHATAN */ { 0x05ec, 0x064c }, /* Arabic_dammatan ٌ ARABIC DAMMATAN */ { 0x05ed, 0x064d }, /* Arabic_kasratan ٍ ARABIC KASRATAN */ { 0x05ee, 0x064e }, /* Arabic_fatha َ ARABIC FATHA */ { 0x05ef, 0x064f }, /* Arabic_damma ُ ARABIC DAMMA */ { 0x05f0, 0x0650 }, /* Arabic_kasra ِ ARABIC KASRA */ { 0x05f1, 0x0651 }, /* Arabic_shadda ّ ARABIC SHADDA */ { 0x05f2, 0x0652 }, /* Arabic_sukun ْ ARABIC SUKUN */ { 0x06a1, 0x0452 }, /* Serbian_dje ђ CYRILLIC SMALL LETTER DJE */ { 0x06a2, 0x0453 }, /* Macedonia_gje ѓ CYRILLIC SMALL LETTER GJE */ { 0x06a3, 0x0451 }, /* Cyrillic_io ё CYRILLIC SMALL LETTER IO */ { 0x06a4, 0x0454 }, /* Ukrainian_ie є CYRILLIC SMALL LETTER UKRAINIAN IE */ { 0x06a5, 0x0455 }, /* Macedonia_dse ѕ CYRILLIC SMALL LETTER DZE */ { 0x06a6, 0x0456 }, /* Ukrainian_i і CYRILLIC SMALL LETTER BYELORUSSIAN-UKRAINIAN I */ { 0x06a7, 0x0457 }, /* Ukrainian_yi ї CYRILLIC SMALL LETTER YI */ { 0x06a8, 0x0458 }, /* Cyrillic_je ј CYRILLIC SMALL LETTER JE */ { 0x06a9, 0x0459 }, /* Cyrillic_lje љ CYRILLIC SMALL LETTER LJE */ { 0x06aa, 0x045a }, /* Cyrillic_nje њ CYRILLIC SMALL LETTER NJE */ { 0x06ab, 0x045b }, /* Serbian_tshe ћ CYRILLIC SMALL LETTER TSHE */ { 0x06ac, 0x045c }, /* Macedonia_kje ќ CYRILLIC SMALL LETTER KJE */ { 0x06ae, 0x045e }, /* Byelorussian_shortu ў CYRILLIC SMALL LETTER SHORT U */ { 0x06af, 0x045f }, /* Cyrillic_dzhe џ CYRILLIC SMALL LETTER DZHE */ { 0x06b0, 0x2116 }, /* numerosign № NUMERO SIGN */ { 0x06b1, 0x0402 }, /* Serbian_DJE Ђ CYRILLIC CAPITAL LETTER DJE */ { 0x06b2, 0x0403 }, /* Macedonia_GJE Ѓ CYRILLIC CAPITAL LETTER GJE */ { 0x06b3, 0x0401 }, /* Cyrillic_IO Ё CYRILLIC CAPITAL LETTER IO */ { 0x06b4, 0x0404 }, /* Ukrainian_IE Є CYRILLIC CAPITAL LETTER UKRAINIAN IE */ { 0x06b5, 0x0405 }, /* Macedonia_DSE Ѕ CYRILLIC CAPITAL LETTER DZE */ { 0x06b6, 0x0406 }, /* Ukrainian_I І CYRILLIC CAPITAL LETTER BYELORUSSIAN-UKRAINIAN I */ { 0x06b7, 0x0407 }, /* Ukrainian_YI Ї CYRILLIC CAPITAL LETTER YI */ { 0x06b8, 0x0408 }, /* Cyrillic_JE Ј CYRILLIC CAPITAL LETTER JE */ { 0x06b9, 0x0409 }, /* Cyrillic_LJE Љ CYRILLIC CAPITAL LETTER LJE */ { 0x06ba, 0x040a }, /* Cyrillic_NJE Њ CYRILLIC CAPITAL LETTER NJE */ { 0x06bb, 0x040b }, /* Serbian_TSHE Ћ CYRILLIC CAPITAL LETTER TSHE */ { 0x06bc, 0x040c }, /* Macedonia_KJE Ќ CYRILLIC CAPITAL LETTER KJE */ { 0x06be, 0x040e }, /* Byelorussian_SHORTU Ў CYRILLIC CAPITAL LETTER SHORT U */ { 0x06bf, 0x040f }, /* Cyrillic_DZHE Џ CYRILLIC CAPITAL LETTER DZHE */ { 0x06c0, 0x044e }, /* Cyrillic_yu ю CYRILLIC SMALL LETTER YU */ { 0x06c1, 0x0430 }, /* Cyrillic_a а CYRILLIC SMALL LETTER A */ { 0x06c2, 0x0431 }, /* Cyrillic_be б CYRILLIC SMALL LETTER BE */ { 0x06c3, 0x0446 }, /* Cyrillic_tse ц CYRILLIC SMALL LETTER TSE */ { 0x06c4, 0x0434 }, /* Cyrillic_de д CYRILLIC SMALL LETTER DE */ { 0x06c5, 0x0435 }, /* Cyrillic_ie е CYRILLIC SMALL LETTER IE */ { 0x06c6, 0x0444 }, /* Cyrillic_ef ф CYRILLIC SMALL LETTER EF */ { 0x06c7, 0x0433 }, /* Cyrillic_ghe г CYRILLIC SMALL LETTER GHE */ { 0x06c8, 0x0445 }, /* Cyrillic_ha х CYRILLIC SMALL LETTER HA */ { 0x06c9, 0x0438 }, /* Cyrillic_i и CYRILLIC SMALL LETTER I */ { 0x06ca, 0x0439 }, /* Cyrillic_shorti й CYRILLIC SMALL LETTER SHORT I */ { 0x06cb, 0x043a }, /* Cyrillic_ka к CYRILLIC SMALL LETTER KA */ { 0x06cc, 0x043b }, /* Cyrillic_el л CYRILLIC SMALL LETTER EL */ { 0x06cd, 0x043c }, /* Cyrillic_em м CYRILLIC SMALL LETTER EM */ { 0x06ce, 0x043d }, /* Cyrillic_en н CYRILLIC SMALL LETTER EN */ { 0x06cf, 0x043e }, /* Cyrillic_o о CYRILLIC SMALL LETTER O */ { 0x06d0, 0x043f }, /* Cyrillic_pe п CYRILLIC SMALL LETTER PE */ { 0x06d1, 0x044f }, /* Cyrillic_ya я CYRILLIC SMALL LETTER YA */ { 0x06d2, 0x0440 }, /* Cyrillic_er р CYRILLIC SMALL LETTER ER */ { 0x06d3, 0x0441 }, /* Cyrillic_es с CYRILLIC SMALL LETTER ES */ { 0x06d4, 0x0442 }, /* Cyrillic_te т CYRILLIC SMALL LETTER TE */ { 0x06d5, 0x0443 }, /* Cyrillic_u у CYRILLIC SMALL LETTER U */ { 0x06d6, 0x0436 }, /* Cyrillic_zhe ж CYRILLIC SMALL LETTER ZHE */ { 0x06d7, 0x0432 }, /* Cyrillic_ve в CYRILLIC SMALL LETTER VE */ { 0x06d8, 0x044c }, /* Cyrillic_softsign ь CYRILLIC SMALL LETTER SOFT SIGN */ { 0x06d9, 0x044b }, /* Cyrillic_yeru ы CYRILLIC SMALL LETTER YERU */ { 0x06da, 0x0437 }, /* Cyrillic_ze з CYRILLIC SMALL LETTER ZE */ { 0x06db, 0x0448 }, /* Cyrillic_sha ш CYRILLIC SMALL LETTER SHA */ { 0x06dc, 0x044d }, /* Cyrillic_e э CYRILLIC SMALL LETTER E */ { 0x06dd, 0x0449 }, /* Cyrillic_shcha щ CYRILLIC SMALL LETTER SHCHA */ { 0x06de, 0x0447 }, /* Cyrillic_che ч CYRILLIC SMALL LETTER CHE */ { 0x06df, 0x044a }, /* Cyrillic_hardsign ъ CYRILLIC SMALL LETTER HARD SIGN */ { 0x06e0, 0x042e }, /* Cyrillic_YU Ю CYRILLIC CAPITAL LETTER YU */ { 0x06e1, 0x0410 }, /* Cyrillic_A А CYRILLIC CAPITAL LETTER A */ { 0x06e2, 0x0411 }, /* Cyrillic_BE Б CYRILLIC CAPITAL LETTER BE */ { 0x06e3, 0x0426 }, /* Cyrillic_TSE Ц CYRILLIC CAPITAL LETTER TSE */ { 0x06e4, 0x0414 }, /* Cyrillic_DE Д CYRILLIC CAPITAL LETTER DE */ { 0x06e5, 0x0415 }, /* Cyrillic_IE Е CYRILLIC CAPITAL LETTER IE */ { 0x06e6, 0x0424 }, /* Cyrillic_EF Ф CYRILLIC CAPITAL LETTER EF */ { 0x06e7, 0x0413 }, /* Cyrillic_GHE Г CYRILLIC CAPITAL LETTER GHE */ { 0x06e8, 0x0425 }, /* Cyrillic_HA Х CYRILLIC CAPITAL LETTER HA */ { 0x06e9, 0x0418 }, /* Cyrillic_I И CYRILLIC CAPITAL LETTER I */ { 0x06ea, 0x0419 }, /* Cyrillic_SHORTI Й CYRILLIC CAPITAL LETTER SHORT I */ { 0x06eb, 0x041a }, /* Cyrillic_KA К CYRILLIC CAPITAL LETTER KA */ { 0x06ec, 0x041b }, /* Cyrillic_EL Л CYRILLIC CAPITAL LETTER EL */ { 0x06ed, 0x041c }, /* Cyrillic_EM М CYRILLIC CAPITAL LETTER EM */ { 0x06ee, 0x041d }, /* Cyrillic_EN Н CYRILLIC CAPITAL LETTER EN */ { 0x06ef, 0x041e }, /* Cyrillic_O О CYRILLIC CAPITAL LETTER O */ { 0x06f0, 0x041f }, /* Cyrillic_PE П CYRILLIC CAPITAL LETTER PE */ { 0x06f1, 0x042f }, /* Cyrillic_YA Я CYRILLIC CAPITAL LETTER YA */ { 0x06f2, 0x0420 }, /* Cyrillic_ER Р CYRILLIC CAPITAL LETTER ER */ { 0x06f3, 0x0421 }, /* Cyrillic_ES С CYRILLIC CAPITAL LETTER ES */ { 0x06f4, 0x0422 }, /* Cyrillic_TE Т CYRILLIC CAPITAL LETTER TE */ { 0x06f5, 0x0423 }, /* Cyrillic_U У CYRILLIC CAPITAL LETTER U */ { 0x06f6, 0x0416 }, /* Cyrillic_ZHE Ж CYRILLIC CAPITAL LETTER ZHE */ { 0x06f7, 0x0412 }, /* Cyrillic_VE В CYRILLIC CAPITAL LETTER VE */ { 0x06f8, 0x042c }, /* Cyrillic_SOFTSIGN Ь CYRILLIC CAPITAL LETTER SOFT SIGN */ { 0x06f9, 0x042b }, /* Cyrillic_YERU Ы CYRILLIC CAPITAL LETTER YERU */ { 0x06fa, 0x0417 }, /* Cyrillic_ZE З CYRILLIC CAPITAL LETTER ZE */ { 0x06fb, 0x0428 }, /* Cyrillic_SHA Ш CYRILLIC CAPITAL LETTER SHA */ { 0x06fc, 0x042d }, /* Cyrillic_E Э CYRILLIC CAPITAL LETTER E */ { 0x06fd, 0x0429 }, /* Cyrillic_SHCHA Щ CYRILLIC CAPITAL LETTER SHCHA */ { 0x06fe, 0x0427 }, /* Cyrillic_CHE Ч CYRILLIC CAPITAL LETTER CHE */ { 0x06ff, 0x042a }, /* Cyrillic_HARDSIGN Ъ CYRILLIC CAPITAL LETTER HARD SIGN */ { 0x07a1, 0x0386 }, /* Greek_ALPHAaccent Ά GREEK CAPITAL LETTER ALPHA WITH TONOS */ { 0x07a2, 0x0388 }, /* Greek_EPSILONaccent Έ GREEK CAPITAL LETTER EPSILON WITH TONOS */ { 0x07a3, 0x0389 }, /* Greek_ETAaccent Ή GREEK CAPITAL LETTER ETA WITH TONOS */ { 0x07a4, 0x038a }, /* Greek_IOTAaccent Ί GREEK CAPITAL LETTER IOTA WITH TONOS */ { 0x07a5, 0x03aa }, /* Greek_IOTAdiaeresis Ϊ GREEK CAPITAL LETTER IOTA WITH DIALYTIKA */ { 0x07a7, 0x038c }, /* Greek_OMICRONaccent Ό GREEK CAPITAL LETTER OMICRON WITH TONOS */ { 0x07a8, 0x038e }, /* Greek_UPSILONaccent Ύ GREEK CAPITAL LETTER UPSILON WITH TONOS */ { 0x07a9, 0x03ab }, /* Greek_UPSILONdieresis Ϋ GREEK CAPITAL LETTER UPSILON WITH DIALYTIKA */ { 0x07ab, 0x038f }, /* Greek_OMEGAaccent Ώ GREEK CAPITAL LETTER OMEGA WITH TONOS */ { 0x07ae, 0x0385 }, /* Greek_accentdieresis ΅ GREEK DIALYTIKA TONOS */ { 0x07af, 0x2015 }, /* Greek_horizbar ― HORIZONTAL BAR */ { 0x07b1, 0x03ac }, /* Greek_alphaaccent ά GREEK SMALL LETTER ALPHA WITH TONOS */ { 0x07b2, 0x03ad }, /* Greek_epsilonaccent έ GREEK SMALL LETTER EPSILON WITH TONOS */ { 0x07b3, 0x03ae }, /* Greek_etaaccent ή GREEK SMALL LETTER ETA WITH TONOS */ { 0x07b4, 0x03af }, /* Greek_iotaaccent ί GREEK SMALL LETTER IOTA WITH TONOS */ { 0x07b5, 0x03ca }, /* Greek_iotadieresis ϊ GREEK SMALL LETTER IOTA WITH DIALYTIKA */ { 0x07b6, 0x0390 }, /* Greek_iotaaccentdieresis ΐ GREEK SMALL LETTER IOTA WITH DIALYTIKA AND TONOS */ { 0x07b7, 0x03cc }, /* Greek_omicronaccent ό GREEK SMALL LETTER OMICRON WITH TONOS */ { 0x07b8, 0x03cd }, /* Greek_upsilonaccent ύ GREEK SMALL LETTER UPSILON WITH TONOS */ { 0x07b9, 0x03cb }, /* Greek_upsilondieresis ϋ GREEK SMALL LETTER UPSILON WITH DIALYTIKA */ { 0x07ba, 0x03b0 }, /* Greek_upsilonaccentdieresis ΰ GREEK SMALL LETTER UPSILON WITH DIALYTIKA AND TONOS */ { 0x07bb, 0x03ce }, /* Greek_omegaaccent ώ GREEK SMALL LETTER OMEGA WITH TONOS */ { 0x07c1, 0x0391 }, /* Greek_ALPHA Α GREEK CAPITAL LETTER ALPHA */ { 0x07c2, 0x0392 }, /* Greek_BETA Β GREEK CAPITAL LETTER BETA */ { 0x07c3, 0x0393 }, /* Greek_GAMMA Γ GREEK CAPITAL LETTER GAMMA */ { 0x07c4, 0x0394 }, /* Greek_DELTA Δ GREEK CAPITAL LETTER DELTA */ { 0x07c5, 0x0395 }, /* Greek_EPSILON Ε GREEK CAPITAL LETTER EPSILON */ { 0x07c6, 0x0396 }, /* Greek_ZETA Ζ GREEK CAPITAL LETTER ZETA */ { 0x07c7, 0x0397 }, /* Greek_ETA Η GREEK CAPITAL LETTER ETA */ { 0x07c8, 0x0398 }, /* Greek_THETA Θ GREEK CAPITAL LETTER THETA */ { 0x07c9, 0x0399 }, /* Greek_IOTA Ι GREEK CAPITAL LETTER IOTA */ { 0x07ca, 0x039a }, /* Greek_KAPPA Κ GREEK CAPITAL LETTER KAPPA */ { 0x07cb, 0x039b }, /* Greek_LAMBDA Λ GREEK CAPITAL LETTER LAMDA */ { 0x07cc, 0x039c }, /* Greek_MU Μ GREEK CAPITAL LETTER MU */ { 0x07cd, 0x039d }, /* Greek_NU Ν GREEK CAPITAL LETTER NU */ { 0x07ce, 0x039e }, /* Greek_XI Ξ GREEK CAPITAL LETTER XI */ { 0x07cf, 0x039f }, /* Greek_OMICRON Ο GREEK CAPITAL LETTER OMICRON */ { 0x07d0, 0x03a0 }, /* Greek_PI Π GREEK CAPITAL LETTER PI */ { 0x07d1, 0x03a1 }, /* Greek_RHO Ρ GREEK CAPITAL LETTER RHO */ { 0x07d2, 0x03a3 }, /* Greek_SIGMA Σ GREEK CAPITAL LETTER SIGMA */ { 0x07d4, 0x03a4 }, /* Greek_TAU Τ GREEK CAPITAL LETTER TAU */ { 0x07d5, 0x03a5 }, /* Greek_UPSILON Υ GREEK CAPITAL LETTER UPSILON */ { 0x07d6, 0x03a6 }, /* Greek_PHI Φ GREEK CAPITAL LETTER PHI */ { 0x07d7, 0x03a7 }, /* Greek_CHI Χ GREEK CAPITAL LETTER CHI */ { 0x07d8, 0x03a8 }, /* Greek_PSI Ψ GREEK CAPITAL LETTER PSI */ { 0x07d9, 0x03a9 }, /* Greek_OMEGA Ω GREEK CAPITAL LETTER OMEGA */ { 0x07e1, 0x03b1 }, /* Greek_alpha α GREEK SMALL LETTER ALPHA */ { 0x07e2, 0x03b2 }, /* Greek_beta β GREEK SMALL LETTER BETA */ { 0x07e3, 0x03b3 }, /* Greek_gamma γ GREEK SMALL LETTER GAMMA */ { 0x07e4, 0x03b4 }, /* Greek_delta δ GREEK SMALL LETTER DELTA */ { 0x07e5, 0x03b5 }, /* Greek_epsilon ε GREEK SMALL LETTER EPSILON */ { 0x07e6, 0x03b6 }, /* Greek_zeta ζ GREEK SMALL LETTER ZETA */ { 0x07e7, 0x03b7 }, /* Greek_eta η GREEK SMALL LETTER ETA */ { 0x07e8, 0x03b8 }, /* Greek_theta θ GREEK SMALL LETTER THETA */ { 0x07e9, 0x03b9 }, /* Greek_iota ι GREEK SMALL LETTER IOTA */ { 0x07ea, 0x03ba }, /* Greek_kappa κ GREEK SMALL LETTER KAPPA */ { 0x07eb, 0x03bb }, /* Greek_lambda λ GREEK SMALL LETTER LAMDA */ { 0x07ec, 0x03bc }, /* Greek_mu μ GREEK SMALL LETTER MU */ { 0x07ed, 0x03bd }, /* Greek_nu ν GREEK SMALL LETTER NU */ { 0x07ee, 0x03be }, /* Greek_xi ξ GREEK SMALL LETTER XI */ { 0x07ef, 0x03bf }, /* Greek_omicron ο GREEK SMALL LETTER OMICRON */ { 0x07f0, 0x03c0 }, /* Greek_pi π GREEK SMALL LETTER PI */ { 0x07f1, 0x03c1 }, /* Greek_rho ρ GREEK SMALL LETTER RHO */ { 0x07f2, 0x03c3 }, /* Greek_sigma σ GREEK SMALL LETTER SIGMA */ { 0x07f3, 0x03c2 }, /* Greek_finalsmallsigma ς GREEK SMALL LETTER FINAL SIGMA */ { 0x07f4, 0x03c4 }, /* Greek_tau τ GREEK SMALL LETTER TAU */ { 0x07f5, 0x03c5 }, /* Greek_upsilon υ GREEK SMALL LETTER UPSILON */ { 0x07f6, 0x03c6 }, /* Greek_phi φ GREEK SMALL LETTER PHI */ { 0x07f7, 0x03c7 }, /* Greek_chi χ GREEK SMALL LETTER CHI */ { 0x07f8, 0x03c8 }, /* Greek_psi ψ GREEK SMALL LETTER PSI */ { 0x07f9, 0x03c9 }, /* Greek_omega ω GREEK SMALL LETTER OMEGA */ { 0x08a1, 0x23b7 }, /* leftradical ⎷ ??? */ { 0x08a2, 0x250c }, /* topleftradical ┌ BOX DRAWINGS LIGHT DOWN AND RIGHT */ { 0x08a3, 0x2500 }, /* horizconnector ─ BOX DRAWINGS LIGHT HORIZONTAL */ { 0x08a4, 0x2320 }, /* topintegral ⌠ TOP HALF INTEGRAL */ { 0x08a5, 0x2321 }, /* botintegral ⌡ BOTTOM HALF INTEGRAL */ { 0x08a6, 0x2502 }, /* vertconnector │ BOX DRAWINGS LIGHT VERTICAL */ { 0x08a7, 0x23a1 }, /* topleftsqbracket ⎡ ??? */ { 0x08a8, 0x23a3 }, /* botleftsqbracket ⎣ ??? */ { 0x08a9, 0x23a4 }, /* toprightsqbracket ⎤ ??? */ { 0x08aa, 0x23a6 }, /* botrightsqbracket ⎦ ??? */ { 0x08ab, 0x239b }, /* topleftparens ⎛ ??? */ { 0x08ac, 0x239d }, /* botleftparens ⎝ ??? */ { 0x08ad, 0x239e }, /* toprightparens ⎞ ??? */ { 0x08ae, 0x23a0 }, /* botrightparens ⎠ ??? */ { 0x08af, 0x23a8 }, /* leftmiddlecurlybrace ⎨ ??? */ { 0x08b0, 0x23ac }, /* rightmiddlecurlybrace ⎬ ??? */ /* 0x08b1 topleftsummation ? ??? */ /* 0x08b2 botleftsummation ? ??? */ /* 0x08b3 topvertsummationconnector ? ??? */ /* 0x08b4 botvertsummationconnector ? ??? */ /* 0x08b5 toprightsummation ? ??? */ /* 0x08b6 botrightsummation ? ??? */ /* 0x08b7 rightmiddlesummation ? ??? */ { 0x08bc, 0x2264 }, /* lessthanequal ≤ LESS-THAN OR EQUAL TO */ { 0x08bd, 0x2260 }, /* notequal ≠ NOT EQUAL TO */ { 0x08be, 0x2265 }, /* greaterthanequal ≥ GREATER-THAN OR EQUAL TO */ { 0x08bf, 0x222b }, /* integral ∫ INTEGRAL */ { 0x08c0, 0x2234 }, /* therefore ∴ THEREFORE */ { 0x08c1, 0x221d }, /* variation ∝ PROPORTIONAL TO */ { 0x08c2, 0x221e }, /* infinity ∞ INFINITY */ { 0x08c5, 0x2207 }, /* nabla ∇ NABLA */ { 0x08c8, 0x223c }, /* approximate ∼ TILDE OPERATOR */ { 0x08c9, 0x2243 }, /* similarequal ≃ ASYMPTOTICALLY EQUAL TO */ { 0x08cd, 0x21d4 }, /* ifonlyif ⇔ LEFT RIGHT DOUBLE ARROW */ { 0x08ce, 0x21d2 }, /* implies ⇒ RIGHTWARDS DOUBLE ARROW */ { 0x08cf, 0x2261 }, /* identical ≡ IDENTICAL TO */ { 0x08d6, 0x221a }, /* radical √ SQUARE ROOT */ { 0x08da, 0x2282 }, /* includedin ⊂ SUBSET OF */ { 0x08db, 0x2283 }, /* includes ⊃ SUPERSET OF */ { 0x08dc, 0x2229 }, /* intersection ∩ INTERSECTION */ { 0x08dd, 0x222a }, /* union ∪ UNION */ { 0x08de, 0x2227 }, /* logicaland ∧ LOGICAL AND */ { 0x08df, 0x2228 }, /* logicalor ∨ LOGICAL OR */ { 0x08ef, 0x2202 }, /* partialderivative ∂ PARTIAL DIFFERENTIAL */ { 0x08f6, 0x0192 }, /* function ƒ LATIN SMALL LETTER F WITH HOOK */ { 0x08fb, 0x2190 }, /* leftarrow ← LEFTWARDS ARROW */ { 0x08fc, 0x2191 }, /* uparrow ↑ UPWARDS ARROW */ { 0x08fd, 0x2192 }, /* rightarrow → RIGHTWARDS ARROW */ { 0x08fe, 0x2193 }, /* downarrow ↓ DOWNWARDS ARROW */ /* 0x09df blank ? ??? */ { 0x09e0, 0x25c6 }, /* soliddiamond ◆ BLACK DIAMOND */ { 0x09e1, 0x2592 }, /* checkerboard ▒ MEDIUM SHADE */ { 0x09e2, 0x2409 }, /* ht ␉ SYMBOL FOR HORIZONTAL TABULATION */ { 0x09e3, 0x240c }, /* ff ␌ SYMBOL FOR FORM FEED */ { 0x09e4, 0x240d }, /* cr ␍ SYMBOL FOR CARRIAGE RETURN */ { 0x09e5, 0x240a }, /* lf ␊ SYMBOL FOR LINE FEED */ { 0x09e8, 0x2424 }, /* nl ␤ SYMBOL FOR NEWLINE */ { 0x09e9, 0x240b }, /* vt ␋ SYMBOL FOR VERTICAL TABULATION */ { 0x09ea, 0x2518 }, /* lowrightcorner ┘ BOX DRAWINGS LIGHT UP AND LEFT */ { 0x09eb, 0x2510 }, /* uprightcorner ┐ BOX DRAWINGS LIGHT DOWN AND LEFT */ { 0x09ec, 0x250c }, /* upleftcorner ┌ BOX DRAWINGS LIGHT DOWN AND RIGHT */ { 0x09ed, 0x2514 }, /* lowleftcorner └ BOX DRAWINGS LIGHT UP AND RIGHT */ { 0x09ee, 0x253c }, /* crossinglines ┼ BOX DRAWINGS LIGHT VERTICAL AND HORIZONTAL */ { 0x09ef, 0x23ba }, /* horizlinescan1 ⎺ HORIZONTAL SCAN LINE-1 (Unicode 3.2 draft) */ { 0x09f0, 0x23bb }, /* horizlinescan3 ⎻ HORIZONTAL SCAN LINE-3 (Unicode 3.2 draft) */ { 0x09f1, 0x2500 }, /* horizlinescan5 ─ BOX DRAWINGS LIGHT HORIZONTAL */ { 0x09f2, 0x23bc }, /* horizlinescan7 ⎼ HORIZONTAL SCAN LINE-7 (Unicode 3.2 draft) */ { 0x09f3, 0x23bd }, /* horizlinescan9 ⎽ HORIZONTAL SCAN LINE-9 (Unicode 3.2 draft) */ { 0x09f4, 0x251c }, /* leftt ├ BOX DRAWINGS LIGHT VERTICAL AND RIGHT */ { 0x09f5, 0x2524 }, /* rightt ┤ BOX DRAWINGS LIGHT VERTICAL AND LEFT */ { 0x09f6, 0x2534 }, /* bott ┴ BOX DRAWINGS LIGHT UP AND HORIZONTAL */ { 0x09f7, 0x252c }, /* topt ┬ BOX DRAWINGS LIGHT DOWN AND HORIZONTAL */ { 0x09f8, 0x2502 }, /* vertbar │ BOX DRAWINGS LIGHT VERTICAL */ { 0x0aa1, 0x2003 }, /* emspace   EM SPACE */ { 0x0aa2, 0x2002 }, /* enspace   EN SPACE */ { 0x0aa3, 0x2004 }, /* em3space   THREE-PER-EM SPACE */ { 0x0aa4, 0x2005 }, /* em4space   FOUR-PER-EM SPACE */ { 0x0aa5, 0x2007 }, /* digitspace   FIGURE SPACE */ { 0x0aa6, 0x2008 }, /* punctspace   PUNCTUATION SPACE */ { 0x0aa7, 0x2009 }, /* thinspace   THIN SPACE */ { 0x0aa8, 0x200a }, /* hairspace   HAIR SPACE */ { 0x0aa9, 0x2014 }, /* emdash — EM DASH */ { 0x0aaa, 0x2013 }, /* endash – EN DASH */ /* 0x0aac signifblank ? ??? */ { 0x0aae, 0x2026 }, /* ellipsis … HORIZONTAL ELLIPSIS */ { 0x0aaf, 0x2025 }, /* doubbaselinedot ‥ TWO DOT LEADER */ { 0x0ab0, 0x2153 }, /* onethird ⅓ VULGAR FRACTION ONE THIRD */ { 0x0ab1, 0x2154 }, /* twothirds ⅔ VULGAR FRACTION TWO THIRDS */ { 0x0ab2, 0x2155 }, /* onefifth ⅕ VULGAR FRACTION ONE FIFTH */ { 0x0ab3, 0x2156 }, /* twofifths ⅖ VULGAR FRACTION TWO FIFTHS */ { 0x0ab4, 0x2157 }, /* threefifths ⅗ VULGAR FRACTION THREE FIFTHS */ { 0x0ab5, 0x2158 }, /* fourfifths ⅘ VULGAR FRACTION FOUR FIFTHS */ { 0x0ab6, 0x2159 }, /* onesixth ⅙ VULGAR FRACTION ONE SIXTH */ { 0x0ab7, 0x215a }, /* fivesixths ⅚ VULGAR FRACTION FIVE SIXTHS */ { 0x0ab8, 0x2105 }, /* careof ℅ CARE OF */ { 0x0abb, 0x2012 }, /* figdash ‒ FIGURE DASH */ { 0x0abc, 0x2329 }, /* leftanglebracket 〈 LEFT-POINTING ANGLE BRACKET */ /* 0x0abd decimalpoint ? ??? */ { 0x0abe, 0x232a }, /* rightanglebracket 〉 RIGHT-POINTING ANGLE BRACKET */ /* 0x0abf marker ? ??? */ { 0x0ac3, 0x215b }, /* oneeighth ⅛ VULGAR FRACTION ONE EIGHTH */ { 0x0ac4, 0x215c }, /* threeeighths ⅜ VULGAR FRACTION THREE EIGHTHS */ { 0x0ac5, 0x215d }, /* fiveeighths ⅝ VULGAR FRACTION FIVE EIGHTHS */ { 0x0ac6, 0x215e }, /* seveneighths ⅞ VULGAR FRACTION SEVEN EIGHTHS */ { 0x0ac9, 0x2122 }, /* trademark ™ TRADE MARK SIGN */ { 0x0aca, 0x2613 }, /* signaturemark ☓ SALTIRE */ /* 0x0acb trademarkincircle ? ??? */ { 0x0acc, 0x25c1 }, /* leftopentriangle ◁ WHITE LEFT-POINTING TRIANGLE */ { 0x0acd, 0x25b7 }, /* rightopentriangle ▷ WHITE RIGHT-POINTING TRIANGLE */ { 0x0ace, 0x25cb }, /* emopencircle ○ WHITE CIRCLE */ { 0x0acf, 0x25af }, /* emopenrectangle ▯ WHITE VERTICAL RECTANGLE */ { 0x0ad0, 0x2018 }, /* leftsinglequotemark ‘ LEFT SINGLE QUOTATION MARK */ { 0x0ad1, 0x2019 }, /* rightsinglequotemark ’ RIGHT SINGLE QUOTATION MARK */ { 0x0ad2, 0x201c }, /* leftdoublequotemark “ LEFT DOUBLE QUOTATION MARK */ { 0x0ad3, 0x201d }, /* rightdoublequotemark ” RIGHT DOUBLE QUOTATION MARK */ { 0x0ad4, 0x211e }, /* prescription ℞ PRESCRIPTION TAKE */ { 0x0ad6, 0x2032 }, /* minutes ′ PRIME */ { 0x0ad7, 0x2033 }, /* seconds ″ DOUBLE PRIME */ { 0x0ad9, 0x271d }, /* latincross ✝ LATIN CROSS */ /* 0x0ada hexagram ? ??? */ { 0x0adb, 0x25ac }, /* filledrectbullet ▬ BLACK RECTANGLE */ { 0x0adc, 0x25c0 }, /* filledlefttribullet ◀ BLACK LEFT-POINTING TRIANGLE */ { 0x0add, 0x25b6 }, /* filledrighttribullet ▶ BLACK RIGHT-POINTING TRIANGLE */ { 0x0ade, 0x25cf }, /* emfilledcircle ● BLACK CIRCLE */ { 0x0adf, 0x25ae }, /* emfilledrect ▮ BLACK VERTICAL RECTANGLE */ { 0x0ae0, 0x25e6 }, /* enopencircbullet ◦ WHITE BULLET */ { 0x0ae1, 0x25ab }, /* enopensquarebullet ▫ WHITE SMALL SQUARE */ { 0x0ae2, 0x25ad }, /* openrectbullet ▭ WHITE RECTANGLE */ { 0x0ae3, 0x25b3 }, /* opentribulletup △ WHITE UP-POINTING TRIANGLE */ { 0x0ae4, 0x25bd }, /* opentribulletdown ▽ WHITE DOWN-POINTING TRIANGLE */ { 0x0ae5, 0x2606 }, /* openstar ☆ WHITE STAR */ { 0x0ae6, 0x2022 }, /* enfilledcircbullet • BULLET */ { 0x0ae7, 0x25aa }, /* enfilledsqbullet ▪ BLACK SMALL SQUARE */ { 0x0ae8, 0x25b2 }, /* filledtribulletup ▲ BLACK UP-POINTING TRIANGLE */ { 0x0ae9, 0x25bc }, /* filledtribulletdown ▼ BLACK DOWN-POINTING TRIANGLE */ { 0x0aea, 0x261c }, /* leftpointer ☜ WHITE LEFT POINTING INDEX */ { 0x0aeb, 0x261e }, /* rightpointer ☞ WHITE RIGHT POINTING INDEX */ { 0x0aec, 0x2663 }, /* club ♣ BLACK CLUB SUIT */ { 0x0aed, 0x2666 }, /* diamond ♦ BLACK DIAMOND SUIT */ { 0x0aee, 0x2665 }, /* heart ♥ BLACK HEART SUIT */ { 0x0af0, 0x2720 }, /* maltesecross ✠ MALTESE CROSS */ { 0x0af1, 0x2020 }, /* dagger † DAGGER */ { 0x0af2, 0x2021 }, /* doubledagger ‡ DOUBLE DAGGER */ { 0x0af3, 0x2713 }, /* checkmark ✓ CHECK MARK */ { 0x0af4, 0x2717 }, /* ballotcross ✗ BALLOT X */ { 0x0af5, 0x266f }, /* musicalsharp ♯ MUSIC SHARP SIGN */ { 0x0af6, 0x266d }, /* musicalflat ♭ MUSIC FLAT SIGN */ { 0x0af7, 0x2642 }, /* malesymbol ♂ MALE SIGN */ { 0x0af8, 0x2640 }, /* femalesymbol ♀ FEMALE SIGN */ { 0x0af9, 0x260e }, /* telephone ☎ BLACK TELEPHONE */ { 0x0afa, 0x2315 }, /* telephonerecorder ⌕ TELEPHONE RECORDER */ { 0x0afb, 0x2117 }, /* phonographcopyright ℗ SOUND RECORDING COPYRIGHT */ { 0x0afc, 0x2038 }, /* caret ‸ CARET */ { 0x0afd, 0x201a }, /* singlelowquotemark ‚ SINGLE LOW-9 QUOTATION MARK */ { 0x0afe, 0x201e }, /* doublelowquotemark „ DOUBLE LOW-9 QUOTATION MARK */ /* 0x0aff cursor ? ??? */ { 0x0ba3, 0x003c }, /* leftcaret < LESS-THAN SIGN */ { 0x0ba6, 0x003e }, /* rightcaret > GREATER-THAN SIGN */ { 0x0ba8, 0x2228 }, /* downcaret ∨ LOGICAL OR */ { 0x0ba9, 0x2227 }, /* upcaret ∧ LOGICAL AND */ { 0x0bc0, 0x00af }, /* overbar ¯ MACRON */ { 0x0bc2, 0x22a5 }, /* downtack ⊥ UP TACK */ { 0x0bc3, 0x2229 }, /* upshoe ∩ INTERSECTION */ { 0x0bc4, 0x230a }, /* downstile ⌊ LEFT FLOOR */ { 0x0bc6, 0x005f }, /* underbar _ LOW LINE */ { 0x0bca, 0x2218 }, /* jot ∘ RING OPERATOR */ { 0x0bcc, 0x2395 }, /* quad ⎕ APL FUNCTIONAL SYMBOL QUAD */ { 0x0bce, 0x22a4 }, /* uptack ⊤ DOWN TACK */ { 0x0bcf, 0x25cb }, /* circle ○ WHITE CIRCLE */ { 0x0bd3, 0x2308 }, /* upstile ⌈ LEFT CEILING */ { 0x0bd6, 0x222a }, /* downshoe ∪ UNION */ { 0x0bd8, 0x2283 }, /* rightshoe ⊃ SUPERSET OF */ { 0x0bda, 0x2282 }, /* leftshoe ⊂ SUBSET OF */ { 0x0bdc, 0x22a2 }, /* lefttack ⊢ RIGHT TACK */ { 0x0bfc, 0x22a3 }, /* righttack ⊣ LEFT TACK */ { 0x0cdf, 0x2017 }, /* hebrew_doublelowline ‗ DOUBLE LOW LINE */ { 0x0ce0, 0x05d0 }, /* hebrew_aleph א HEBREW LETTER ALEF */ { 0x0ce1, 0x05d1 }, /* hebrew_bet ב HEBREW LETTER BET */ { 0x0ce2, 0x05d2 }, /* hebrew_gimel ג HEBREW LETTER GIMEL */ { 0x0ce3, 0x05d3 }, /* hebrew_dalet ד HEBREW LETTER DALET */ { 0x0ce4, 0x05d4 }, /* hebrew_he ה HEBREW LETTER HE */ { 0x0ce5, 0x05d5 }, /* hebrew_waw ו HEBREW LETTER VAV */ { 0x0ce6, 0x05d6 }, /* hebrew_zain ז HEBREW LETTER ZAYIN */ { 0x0ce7, 0x05d7 }, /* hebrew_chet ח HEBREW LETTER HET */ { 0x0ce8, 0x05d8 }, /* hebrew_tet ט HEBREW LETTER TET */ { 0x0ce9, 0x05d9 }, /* hebrew_yod י HEBREW LETTER YOD */ { 0x0cea, 0x05da }, /* hebrew_finalkaph ך HEBREW LETTER FINAL KAF */ { 0x0ceb, 0x05db }, /* hebrew_kaph כ HEBREW LETTER KAF */ { 0x0cec, 0x05dc }, /* hebrew_lamed ל HEBREW LETTER LAMED */ { 0x0ced, 0x05dd }, /* hebrew_finalmem ם HEBREW LETTER FINAL MEM */ { 0x0cee, 0x05de }, /* hebrew_mem מ HEBREW LETTER MEM */ { 0x0cef, 0x05df }, /* hebrew_finalnun ן HEBREW LETTER FINAL NUN */ { 0x0cf0, 0x05e0 }, /* hebrew_nun נ HEBREW LETTER NUN */ { 0x0cf1, 0x05e1 }, /* hebrew_samech ס HEBREW LETTER SAMEKH */ { 0x0cf2, 0x05e2 }, /* hebrew_ayin ע HEBREW LETTER AYIN */ { 0x0cf3, 0x05e3 }, /* hebrew_finalpe ף HEBREW LETTER FINAL PE */ { 0x0cf4, 0x05e4 }, /* hebrew_pe פ HEBREW LETTER PE */ { 0x0cf5, 0x05e5 }, /* hebrew_finalzade ץ HEBREW LETTER FINAL TSADI */ { 0x0cf6, 0x05e6 }, /* hebrew_zade צ HEBREW LETTER TSADI */ { 0x0cf7, 0x05e7 }, /* hebrew_qoph ק HEBREW LETTER QOF */ { 0x0cf8, 0x05e8 }, /* hebrew_resh ר HEBREW LETTER RESH */ { 0x0cf9, 0x05e9 }, /* hebrew_shin ש HEBREW LETTER SHIN */ { 0x0cfa, 0x05ea }, /* hebrew_taw ת HEBREW LETTER TAV */ { 0x0da1, 0x0e01 }, /* Thai_kokai ก THAI CHARACTER KO KAI */ { 0x0da2, 0x0e02 }, /* Thai_khokhai ข THAI CHARACTER KHO KHAI */ { 0x0da3, 0x0e03 }, /* Thai_khokhuat ฃ THAI CHARACTER KHO KHUAT */ { 0x0da4, 0x0e04 }, /* Thai_khokhwai ค THAI CHARACTER KHO KHWAI */ { 0x0da5, 0x0e05 }, /* Thai_khokhon ฅ THAI CHARACTER KHO KHON */ { 0x0da6, 0x0e06 }, /* Thai_khorakhang ฆ THAI CHARACTER KHO RAKHANG */ { 0x0da7, 0x0e07 }, /* Thai_ngongu ง THAI CHARACTER NGO NGU */ { 0x0da8, 0x0e08 }, /* Thai_chochan จ THAI CHARACTER CHO CHAN */ { 0x0da9, 0x0e09 }, /* Thai_choching ฉ THAI CHARACTER CHO CHING */ { 0x0daa, 0x0e0a }, /* Thai_chochang ช THAI CHARACTER CHO CHANG */ { 0x0dab, 0x0e0b }, /* Thai_soso ซ THAI CHARACTER SO SO */ { 0x0dac, 0x0e0c }, /* Thai_chochoe ฌ THAI CHARACTER CHO CHOE */ { 0x0dad, 0x0e0d }, /* Thai_yoying ญ THAI CHARACTER YO YING */ { 0x0dae, 0x0e0e }, /* Thai_dochada ฎ THAI CHARACTER DO CHADA */ { 0x0daf, 0x0e0f }, /* Thai_topatak ฏ THAI CHARACTER TO PATAK */ { 0x0db0, 0x0e10 }, /* Thai_thothan ฐ THAI CHARACTER THO THAN */ { 0x0db1, 0x0e11 }, /* Thai_thonangmontho ฑ THAI CHARACTER THO NANGMONTHO */ { 0x0db2, 0x0e12 }, /* Thai_thophuthao ฒ THAI CHARACTER THO PHUTHAO */ { 0x0db3, 0x0e13 }, /* Thai_nonen ณ THAI CHARACTER NO NEN */ { 0x0db4, 0x0e14 }, /* Thai_dodek ด THAI CHARACTER DO DEK */ { 0x0db5, 0x0e15 }, /* Thai_totao ต THAI CHARACTER TO TAO */ { 0x0db6, 0x0e16 }, /* Thai_thothung ถ THAI CHARACTER THO THUNG */ { 0x0db7, 0x0e17 }, /* Thai_thothahan ท THAI CHARACTER THO THAHAN */ { 0x0db8, 0x0e18 }, /* Thai_thothong ธ THAI CHARACTER THO THONG */ { 0x0db9, 0x0e19 }, /* Thai_nonu น THAI CHARACTER NO NU */ { 0x0dba, 0x0e1a }, /* Thai_bobaimai บ THAI CHARACTER BO BAIMAI */ { 0x0dbb, 0x0e1b }, /* Thai_popla ป THAI CHARACTER PO PLA */ { 0x0dbc, 0x0e1c }, /* Thai_phophung ผ THAI CHARACTER PHO PHUNG */ { 0x0dbd, 0x0e1d }, /* Thai_fofa ฝ THAI CHARACTER FO FA */ { 0x0dbe, 0x0e1e }, /* Thai_phophan พ THAI CHARACTER PHO PHAN */ { 0x0dbf, 0x0e1f }, /* Thai_fofan ฟ THAI CHARACTER FO FAN */ { 0x0dc0, 0x0e20 }, /* Thai_phosamphao ภ THAI CHARACTER PHO SAMPHAO */ { 0x0dc1, 0x0e21 }, /* Thai_moma ม THAI CHARACTER MO MA */ { 0x0dc2, 0x0e22 }, /* Thai_yoyak ย THAI CHARACTER YO YAK */ { 0x0dc3, 0x0e23 }, /* Thai_rorua ร THAI CHARACTER RO RUA */ { 0x0dc4, 0x0e24 }, /* Thai_ru ฤ THAI CHARACTER RU */ { 0x0dc5, 0x0e25 }, /* Thai_loling ล THAI CHARACTER LO LING */ { 0x0dc6, 0x0e26 }, /* Thai_lu ฦ THAI CHARACTER LU */ { 0x0dc7, 0x0e27 }, /* Thai_wowaen ว THAI CHARACTER WO WAEN */ { 0x0dc8, 0x0e28 }, /* Thai_sosala ศ THAI CHARACTER SO SALA */ { 0x0dc9, 0x0e29 }, /* Thai_sorusi ษ THAI CHARACTER SO RUSI */ { 0x0dca, 0x0e2a }, /* Thai_sosua ส THAI CHARACTER SO SUA */ { 0x0dcb, 0x0e2b }, /* Thai_hohip ห THAI CHARACTER HO HIP */ { 0x0dcc, 0x0e2c }, /* Thai_lochula ฬ THAI CHARACTER LO CHULA */ { 0x0dcd, 0x0e2d }, /* Thai_oang อ THAI CHARACTER O ANG */ { 0x0dce, 0x0e2e }, /* Thai_honokhuk ฮ THAI CHARACTER HO NOKHUK */ { 0x0dcf, 0x0e2f }, /* Thai_paiyannoi ฯ THAI CHARACTER PAIYANNOI */ { 0x0dd0, 0x0e30 }, /* Thai_saraa ะ THAI CHARACTER SARA A */ { 0x0dd1, 0x0e31 }, /* Thai_maihanakat ั THAI CHARACTER MAI HAN-AKAT */ { 0x0dd2, 0x0e32 }, /* Thai_saraaa า THAI CHARACTER SARA AA */ { 0x0dd3, 0x0e33 }, /* Thai_saraam ำ THAI CHARACTER SARA AM */ { 0x0dd4, 0x0e34 }, /* Thai_sarai ิ THAI CHARACTER SARA I */ { 0x0dd5, 0x0e35 }, /* Thai_saraii ี THAI CHARACTER SARA II */ { 0x0dd6, 0x0e36 }, /* Thai_saraue ึ THAI CHARACTER SARA UE */ { 0x0dd7, 0x0e37 }, /* Thai_sarauee ื THAI CHARACTER SARA UEE */ { 0x0dd8, 0x0e38 }, /* Thai_sarau ุ THAI CHARACTER SARA U */ { 0x0dd9, 0x0e39 }, /* Thai_sarauu ู THAI CHARACTER SARA UU */ { 0x0dda, 0x0e3a }, /* Thai_phinthu ฺ THAI CHARACTER PHINTHU */ /* 0x0dde Thai_maihanakat_maitho ? ??? */ { 0x0ddf, 0x0e3f }, /* Thai_baht ฿ THAI CURRENCY SYMBOL BAHT */ { 0x0de0, 0x0e40 }, /* Thai_sarae เ THAI CHARACTER SARA E */ { 0x0de1, 0x0e41 }, /* Thai_saraae แ THAI CHARACTER SARA AE */ { 0x0de2, 0x0e42 }, /* Thai_sarao โ THAI CHARACTER SARA O */ { 0x0de3, 0x0e43 }, /* Thai_saraaimaimuan ใ THAI CHARACTER SARA AI MAIMUAN */ { 0x0de4, 0x0e44 }, /* Thai_saraaimaimalai ไ THAI CHARACTER SARA AI MAIMALAI */ { 0x0de5, 0x0e45 }, /* Thai_lakkhangyao ๅ THAI CHARACTER LAKKHANGYAO */ { 0x0de6, 0x0e46 }, /* Thai_maiyamok ๆ THAI CHARACTER MAIYAMOK */ { 0x0de7, 0x0e47 }, /* Thai_maitaikhu ็ THAI CHARACTER MAITAIKHU */ { 0x0de8, 0x0e48 }, /* Thai_maiek ่ THAI CHARACTER MAI EK */ { 0x0de9, 0x0e49 }, /* Thai_maitho ้ THAI CHARACTER MAI THO */ { 0x0dea, 0x0e4a }, /* Thai_maitri ๊ THAI CHARACTER MAI TRI */ { 0x0deb, 0x0e4b }, /* Thai_maichattawa ๋ THAI CHARACTER MAI CHATTAWA */ { 0x0dec, 0x0e4c }, /* Thai_thanthakhat ์ THAI CHARACTER THANTHAKHAT */ { 0x0ded, 0x0e4d }, /* Thai_nikhahit ํ THAI CHARACTER NIKHAHIT */ { 0x0df0, 0x0e50 }, /* Thai_leksun ๐ THAI DIGIT ZERO */ { 0x0df1, 0x0e51 }, /* Thai_leknung ๑ THAI DIGIT ONE */ { 0x0df2, 0x0e52 }, /* Thai_leksong ๒ THAI DIGIT TWO */ { 0x0df3, 0x0e53 }, /* Thai_leksam ๓ THAI DIGIT THREE */ { 0x0df4, 0x0e54 }, /* Thai_leksi ๔ THAI DIGIT FOUR */ { 0x0df5, 0x0e55 }, /* Thai_lekha ๕ THAI DIGIT FIVE */ { 0x0df6, 0x0e56 }, /* Thai_lekhok ๖ THAI DIGIT SIX */ { 0x0df7, 0x0e57 }, /* Thai_lekchet ๗ THAI DIGIT SEVEN */ { 0x0df8, 0x0e58 }, /* Thai_lekpaet ๘ THAI DIGIT EIGHT */ { 0x0df9, 0x0e59 }, /* Thai_lekkao ๙ THAI DIGIT NINE */ { 0x0ea1, 0x3131 }, /* Hangul_Kiyeog ㄱ HANGUL LETTER KIYEOK */ { 0x0ea2, 0x3132 }, /* Hangul_SsangKiyeog ㄲ HANGUL LETTER SSANGKIYEOK */ { 0x0ea3, 0x3133 }, /* Hangul_KiyeogSios ㄳ HANGUL LETTER KIYEOK-SIOS */ { 0x0ea4, 0x3134 }, /* Hangul_Nieun ㄴ HANGUL LETTER NIEUN */ { 0x0ea5, 0x3135 }, /* Hangul_NieunJieuj ㄵ HANGUL LETTER NIEUN-CIEUC */ { 0x0ea6, 0x3136 }, /* Hangul_NieunHieuh ㄶ HANGUL LETTER NIEUN-HIEUH */ { 0x0ea7, 0x3137 }, /* Hangul_Dikeud ㄷ HANGUL LETTER TIKEUT */ { 0x0ea8, 0x3138 }, /* Hangul_SsangDikeud ㄸ HANGUL LETTER SSANGTIKEUT */ { 0x0ea9, 0x3139 }, /* Hangul_Rieul ㄹ HANGUL LETTER RIEUL */ { 0x0eaa, 0x313a }, /* Hangul_RieulKiyeog ㄺ HANGUL LETTER RIEUL-KIYEOK */ { 0x0eab, 0x313b }, /* Hangul_RieulMieum ㄻ HANGUL LETTER RIEUL-MIEUM */ { 0x0eac, 0x313c }, /* Hangul_RieulPieub ㄼ HANGUL LETTER RIEUL-PIEUP */ { 0x0ead, 0x313d }, /* Hangul_RieulSios ㄽ HANGUL LETTER RIEUL-SIOS */ { 0x0eae, 0x313e }, /* Hangul_RieulTieut ㄾ HANGUL LETTER RIEUL-THIEUTH */ { 0x0eaf, 0x313f }, /* Hangul_RieulPhieuf ㄿ HANGUL LETTER RIEUL-PHIEUPH */ { 0x0eb0, 0x3140 }, /* Hangul_RieulHieuh ㅀ HANGUL LETTER RIEUL-HIEUH */ { 0x0eb1, 0x3141 }, /* Hangul_Mieum ㅁ HANGUL LETTER MIEUM */ { 0x0eb2, 0x3142 }, /* Hangul_Pieub ㅂ HANGUL LETTER PIEUP */ { 0x0eb3, 0x3143 }, /* Hangul_SsangPieub ㅃ HANGUL LETTER SSANGPIEUP */ { 0x0eb4, 0x3144 }, /* Hangul_PieubSios ㅄ HANGUL LETTER PIEUP-SIOS */ { 0x0eb5, 0x3145 }, /* Hangul_Sios ㅅ HANGUL LETTER SIOS */ { 0x0eb6, 0x3146 }, /* Hangul_SsangSios ㅆ HANGUL LETTER SSANGSIOS */ { 0x0eb7, 0x3147 }, /* Hangul_Ieung ㅇ HANGUL LETTER IEUNG */ { 0x0eb8, 0x3148 }, /* Hangul_Jieuj ㅈ HANGUL LETTER CIEUC */ { 0x0eb9, 0x3149 }, /* Hangul_SsangJieuj ㅉ HANGUL LETTER SSANGCIEUC */ { 0x0eba, 0x314a }, /* Hangul_Cieuc ㅊ HANGUL LETTER CHIEUCH */ { 0x0ebb, 0x314b }, /* Hangul_Khieuq ㅋ HANGUL LETTER KHIEUKH */ { 0x0ebc, 0x314c }, /* Hangul_Tieut ㅌ HANGUL LETTER THIEUTH */ { 0x0ebd, 0x314d }, /* Hangul_Phieuf ㅍ HANGUL LETTER PHIEUPH */ { 0x0ebe, 0x314e }, /* Hangul_Hieuh ㅎ HANGUL LETTER HIEUH */ { 0x0ebf, 0x314f }, /* Hangul_A ㅏ HANGUL LETTER A */ { 0x0ec0, 0x3150 }, /* Hangul_AE ㅐ HANGUL LETTER AE */ { 0x0ec1, 0x3151 }, /* Hangul_YA ㅑ HANGUL LETTER YA */ { 0x0ec2, 0x3152 }, /* Hangul_YAE ㅒ HANGUL LETTER YAE */ { 0x0ec3, 0x3153 }, /* Hangul_EO ㅓ HANGUL LETTER EO */ { 0x0ec4, 0x3154 }, /* Hangul_E ㅔ HANGUL LETTER E */ { 0x0ec5, 0x3155 }, /* Hangul_YEO ㅕ HANGUL LETTER YEO */ { 0x0ec6, 0x3156 }, /* Hangul_YE ㅖ HANGUL LETTER YE */ { 0x0ec7, 0x3157 }, /* Hangul_O ㅗ HANGUL LETTER O */ { 0x0ec8, 0x3158 }, /* Hangul_WA ㅘ HANGUL LETTER WA */ { 0x0ec9, 0x3159 }, /* Hangul_WAE ㅙ HANGUL LETTER WAE */ { 0x0eca, 0x315a }, /* Hangul_OE ㅚ HANGUL LETTER OE */ { 0x0ecb, 0x315b }, /* Hangul_YO ㅛ HANGUL LETTER YO */ { 0x0ecc, 0x315c }, /* Hangul_U ㅜ HANGUL LETTER U */ { 0x0ecd, 0x315d }, /* Hangul_WEO ㅝ HANGUL LETTER WEO */ { 0x0ece, 0x315e }, /* Hangul_WE ㅞ HANGUL LETTER WE */ { 0x0ecf, 0x315f }, /* Hangul_WI ㅟ HANGUL LETTER WI */ { 0x0ed0, 0x3160 }, /* Hangul_YU ㅠ HANGUL LETTER YU */ { 0x0ed1, 0x3161 }, /* Hangul_EU ㅡ HANGUL LETTER EU */ { 0x0ed2, 0x3162 }, /* Hangul_YI ㅢ HANGUL LETTER YI */ { 0x0ed3, 0x3163 }, /* Hangul_I ㅣ HANGUL LETTER I */ { 0x0ed4, 0x11a8 }, /* Hangul_J_Kiyeog ᆨ HANGUL JONGSEONG KIYEOK */ { 0x0ed5, 0x11a9 }, /* Hangul_J_SsangKiyeog ᆩ HANGUL JONGSEONG SSANGKIYEOK */ { 0x0ed6, 0x11aa }, /* Hangul_J_KiyeogSios ᆪ HANGUL JONGSEONG KIYEOK-SIOS */ { 0x0ed7, 0x11ab }, /* Hangul_J_Nieun ᆫ HANGUL JONGSEONG NIEUN */ { 0x0ed8, 0x11ac }, /* Hangul_J_NieunJieuj ᆬ HANGUL JONGSEONG NIEUN-CIEUC */ { 0x0ed9, 0x11ad }, /* Hangul_J_NieunHieuh ᆭ HANGUL JONGSEONG NIEUN-HIEUH */ { 0x0eda, 0x11ae }, /* Hangul_J_Dikeud ᆮ HANGUL JONGSEONG TIKEUT */ { 0x0edb, 0x11af }, /* Hangul_J_Rieul ᆯ HANGUL JONGSEONG RIEUL */ { 0x0edc, 0x11b0 }, /* Hangul_J_RieulKiyeog ᆰ HANGUL JONGSEONG RIEUL-KIYEOK */ { 0x0edd, 0x11b1 }, /* Hangul_J_RieulMieum ᆱ HANGUL JONGSEONG RIEUL-MIEUM */ { 0x0ede, 0x11b2 }, /* Hangul_J_RieulPieub ᆲ HANGUL JONGSEONG RIEUL-PIEUP */ { 0x0edf, 0x11b3 }, /* Hangul_J_RieulSios ᆳ HANGUL JONGSEONG RIEUL-SIOS */ { 0x0ee0, 0x11b4 }, /* Hangul_J_RieulTieut ᆴ HANGUL JONGSEONG RIEUL-THIEUTH */ { 0x0ee1, 0x11b5 }, /* Hangul_J_RieulPhieuf ᆵ HANGUL JONGSEONG RIEUL-PHIEUPH */ { 0x0ee2, 0x11b6 }, /* Hangul_J_RieulHieuh ᆶ HANGUL JONGSEONG RIEUL-HIEUH */ { 0x0ee3, 0x11b7 }, /* Hangul_J_Mieum ᆷ HANGUL JONGSEONG MIEUM */ { 0x0ee4, 0x11b8 }, /* Hangul_J_Pieub ᆸ HANGUL JONGSEONG PIEUP */ { 0x0ee5, 0x11b9 }, /* Hangul_J_PieubSios ᆹ HANGUL JONGSEONG PIEUP-SIOS */ { 0x0ee6, 0x11ba }, /* Hangul_J_Sios ᆺ HANGUL JONGSEONG SIOS */ { 0x0ee7, 0x11bb }, /* Hangul_J_SsangSios ᆻ HANGUL JONGSEONG SSANGSIOS */ { 0x0ee8, 0x11bc }, /* Hangul_J_Ieung ᆼ HANGUL JONGSEONG IEUNG */ { 0x0ee9, 0x11bd }, /* Hangul_J_Jieuj ᆽ HANGUL JONGSEONG CIEUC */ { 0x0eea, 0x11be }, /* Hangul_J_Cieuc ᆾ HANGUL JONGSEONG CHIEUCH */ { 0x0eeb, 0x11bf }, /* Hangul_J_Khieuq ᆿ HANGUL JONGSEONG KHIEUKH */ { 0x0eec, 0x11c0 }, /* Hangul_J_Tieut ᇀ HANGUL JONGSEONG THIEUTH */ { 0x0eed, 0x11c1 }, /* Hangul_J_Phieuf ᇁ HANGUL JONGSEONG PHIEUPH */ { 0x0eee, 0x11c2 }, /* Hangul_J_Hieuh ᇂ HANGUL JONGSEONG HIEUH */ { 0x0eef, 0x316d }, /* Hangul_RieulYeorinHieuh ㅭ HANGUL LETTER RIEUL-YEORINHIEUH */ { 0x0ef0, 0x3171 }, /* Hangul_SunkyeongeumMieum ㅱ HANGUL LETTER KAPYEOUNMIEUM */ { 0x0ef1, 0x3178 }, /* Hangul_SunkyeongeumPieub ㅸ HANGUL LETTER KAPYEOUNPIEUP */ { 0x0ef2, 0x317f }, /* Hangul_PanSios ㅿ HANGUL LETTER PANSIOS */ { 0x0ef3, 0x3181 }, /* Hangul_KkogjiDalrinIeung ㆁ HANGUL LETTER YESIEUNG */ { 0x0ef4, 0x3184 }, /* Hangul_SunkyeongeumPhieuf ㆄ HANGUL LETTER KAPYEOUNPHIEUPH */ { 0x0ef5, 0x3186 }, /* Hangul_YeorinHieuh ㆆ HANGUL LETTER YEORINHIEUH */ { 0x0ef6, 0x318d }, /* Hangul_AraeA ㆍ HANGUL LETTER ARAEA */ { 0x0ef7, 0x318e }, /* Hangul_AraeAE ㆎ HANGUL LETTER ARAEAE */ { 0x0ef8, 0x11eb }, /* Hangul_J_PanSios ᇫ HANGUL JONGSEONG PANSIOS */ { 0x0ef9, 0x11f0 }, /* Hangul_J_KkogjiDalrinIeung ᇰ HANGUL JONGSEONG YESIEUNG */ { 0x0efa, 0x11f9 }, /* Hangul_J_YeorinHieuh ᇹ HANGUL JONGSEONG YEORINHIEUH */ { 0x0eff, 0x20a9 }, /* Korean_Won ₩ WON SIGN */ { 0x13a4, 0x20ac }, /* Euro € EURO SIGN */ { 0x13bc, 0x0152 }, /* OE Œ LATIN CAPITAL LIGATURE OE */ { 0x13bd, 0x0153 }, /* oe œ LATIN SMALL LIGATURE OE */ { 0x13be, 0x0178 }, /* Ydiaeresis Ÿ LATIN CAPITAL LETTER Y WITH DIAERESIS */ { 0x20ac, 0x20ac }, /* EuroSign € EURO SIGN */ }; long keysym2ucs(KeySym keysym) { int min = 0; int max = sizeof(keysymtab) / sizeof(struct codepair) - 1; int mid; /* first check for Latin-1 characters (1:1 mapping) */ if ((keysym >= 0x0020 && keysym <= 0x007e) || (keysym >= 0x00a0 && keysym <= 0x00ff)) return keysym; /* also check for directly encoded 24-bit UCS characters */ if ((keysym & 0xff000000) == 0x01000000) return keysym & 0x00ffffff; /* binary search in table */ while (max >= min) { mid = (min + max) / 2; if (keysymtab[mid].keysym < keysym) min = mid + 1; else if (keysymtab[mid].keysym > keysym) max = mid - 1; else { /* found it */ return keysymtab[mid].ucs; } } /* no matching Unicode value found */ return -1; } Kivy-1.9.0/kivy/core/window/window_x11.pyx0000664000175000017500000001566312502235633020424 0ustar titotito00000000000000''' Window X11 ========== Window implementation in top of X11 ''' __all__ = ('WindowX11', ) from kivy.core.window import WindowBase from kivy.logger import Logger from kivy.config import Config from kivy.base import stopTouchApp, EventLoop, ExceptionManager from kivy.utils import platform from os import environ cdef extern from "window_x11_core.c": pass cdef extern from "X11/Xutil.h": int KeyPress int KeyRelease int ButtonPress int ButtonRelease int MotionNotify int ControlMask int ShiftMask int Mod3Mask int Mod4Mask ctypedef struct XKeyEvent: unsigned int keycode unsigned int state ctypedef struct XMotionEvent: int x, y unsigned int state ctypedef struct XButtonEvent: int x, y unsigned int state unsigned int button ctypedef union XEvent: int type XKeyEvent xkey XMotionEvent xmotion XButtonEvent xbutton cdef extern int x11_create_window(int width, int height, int x, int y, \ int resizable, int fullscreen, int border, int above, int CWOR, char *title) cdef extern void x11_gl_swap() cdef extern int x11_idle() cdef extern int x11_get_width() cdef extern int x11_get_height() ctypedef int (*event_cb_t)(XEvent *event) cdef extern void x11_set_event_callback(event_cb_t callback) cdef extern long x11_keycode_to_keysym(unsigned int keycode, int shiftDown) _window_object = None cdef list get_modifiers_from_state(unsigned int state): ret = [] if state & ShiftMask: ret.append('shift') elif state & ControlMask: ret.append('ctrl') elif state & Mod3Mask: ret.append('alt') elif state & Mod4Mask: ret.append('meta') return ret cdef int event_callback(XEvent *event): if event.type == KeyPress or event.type == KeyRelease: modifiers = get_modifiers_from_state(event.xkey.state) scancode = event.xkey.keycode key = x11_keycode_to_keysym(event.xkey.keycode, 'shift' in modifiers) if key == -1: return 0 try: codepoint = chr(key) except: codepoint = None if event.type == KeyRelease: _window_object.dispatch('on_key_up', key, scancode) return 0 if _window_object.dispatch('on_key_down', key, scancode, codepoint, modifiers): return 0 _window_object.dispatch('on_keyboard', key, scancode, codepoint, modifiers) elif event.type == MotionNotify: modifiers = get_modifiers_from_state(event.xmotion.state) _window_object.dispatch('on_mouse_move', event.xmotion.x, event.xmotion.y, modifiers) # mouse motion elif event.type == ButtonPress or event.type == ButtonRelease: btn = 'left' if event.xbutton.button == 3: btn = 'right' elif event.xbutton.button == 2: btn = 'middle' elif event.xbutton.button == 4: btn = 'scrolldown' elif event.xbutton.button == 5: btn = 'scrollup' modifiers = get_modifiers_from_state(event.xbutton.state) eventname = 'on_mouse_down' if event.type == ButtonRelease: eventname = 'on_mouse_up' _window_object.dispatch(eventname, event.xbutton.x, event.xbutton.y, btn, modifiers) else: pass return 0 x11_set_event_callback(event_callback) class WindowX11(WindowBase): def create_window(self, *args): global _window_object _window_object = self # ensure the mouse is still not up after window creation, otherwise, we # have some weird bugs self.dispatch('on_mouse_up', 0, 0, 'all', []) resizable = Config.getint('graphics', 'resizable') multisamples = Config.getint('graphics', 'multisamples') pos = (0, 0) if self.position == 'auto': pos = (0, 0) elif self.position == 'custom': pos = self.left, self.top else: raise ValueError('position token in configuration accept only ' '"auto" or "custom"') fullscreen = False border = True above = False CWOR = False size = list(self.system_size) if self.fullscreen == 'fake': fullscreen = True Logger.debug('WinX11: Set window to fake fullscreen mode') border = False pos = (0, 0) elif self.fullscreen == 'auto': size = [-1, -1] fullscreen = True elif self.fullscreen is True: Logger.debug('WinX11: Set window to fullscreen mode') fullscreen = True if 'KIVY_WINDOW_NO_BORDER' in environ: border = False if 'KIVY_WINDOW_ABOVE' in environ: above = True # Sets CWOverrideRedirect in x11. # This can lead to unknown effects depending on your # system-configuration as the WindowManager will loos the control # about this window. (In most cases the window then just gets placed # above all other windows without any decoration) if 'KIVY_WINDOW_X11_CWOR' in environ: CWOR = True if x11_create_window(size[0], size[1], pos[0], pos[1], resizable, fullscreen, border, above, CWOR, self.title) < 0: Logger.critical('WinX11: Unable to create the window') return size[0] = x11_get_width() size[1] = x11_get_height() self._pos = (0, 0) self.system_size = size super(WindowX11, self).create_window() def mainloop(self): while not EventLoop.quit and EventLoop.status == 'started': try: self._mainloop() except BaseException, inst: # use exception manager first r = ExceptionManager.handle_exception(inst) if r == ExceptionManager.RAISE: stopTouchApp() raise else: pass def _mainloop(self): EventLoop.idle() if x11_idle() == 0 and not self.dispatch('on_request_close'): EventLoop.quit = True def flip(self): x11_gl_swap() super(WindowX11, self).flip() def on_keyboard(self, key, scancode=None, codepoint=None, modifier=None, **kwargs): codepoint = codepoint or kwargs.get('unicode') # Quit if user presses ESC or the typical OSX shortcuts CMD+q or CMD+w # TODO If just CMD+w is pressed, only the window should be closed. is_osx = platform == 'darwin' if key == 27 or (is_osx and key in (113, 119) and modifier == 1024): if not self.dispatch('on_request_close', source='keyboard'): stopTouchApp() self.close() # not sure what to do here return True super(WindowX11, self).on_keyboard(key, scancode, codepoint=codepoint, modifier=modifier) Kivy-1.9.0/kivy/core/window/window_pygame.py0000664000175000017500000004143212475364136021110 0ustar titotito00000000000000''' Window Pygame: windowing provider based on Pygame ''' __all__ = ('WindowPygame', ) # fail early if possible import pygame from kivy.compat import PY2 from kivy.core.window import WindowBase from kivy.core import CoreCriticalException from os import environ from os.path import exists, join from kivy.config import Config from kivy import kivy_data_dir from kivy.base import ExceptionManager from kivy.logger import Logger from kivy.base import stopTouchApp, EventLoop from kivy.utils import platform, deprecated from kivy.resources import resource_find from kivy.clock import Clock try: android = None if platform == 'android': import android except ImportError: pass # late binding glReadPixels = GL_RGBA = GL_UNSIGNED_BYTE = None class WindowPygame(WindowBase): 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', []) # force display to show (available only for fullscreen) displayidx = Config.getint('graphics', 'display') if not 'SDL_VIDEO_FULLSCREEN_HEAD' in environ and displayidx != -1: environ['SDL_VIDEO_FULLSCREEN_HEAD'] = '%d' % displayidx # init some opengl, same as before. self.flags = pygame.HWSURFACE | pygame.OPENGL | pygame.DOUBLEBUF # 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 in ('linux', 'macosx', 'win') and \ Config.getboolean('graphics', 'resizable'): self.flags |= pygame.RESIZABLE try: pygame.display.init() except pygame.error as e: raise CoreCriticalException(e.message) multisamples = Config.getint('graphics', 'multisamples') if multisamples > 0: pygame.display.gl_set_attribute(pygame.GL_MULTISAMPLEBUFFERS, 1) pygame.display.gl_set_attribute(pygame.GL_MULTISAMPLESAMPLES, multisamples) pygame.display.gl_set_attribute(pygame.GL_DEPTH_SIZE, 16) pygame.display.gl_set_attribute(pygame.GL_STENCIL_SIZE, 1) pygame.display.set_caption(self.title) if self.position == 'auto': self._pos = None elif self.position == 'custom': self._pos = self.left, self.top else: raise ValueError('position token in configuration accept only ' '"auto" or "custom"') if self._fake_fullscreen: if not self.borderless: self.fullscreen = self._fake_fullscreen = False elif not self.fullscreen or self.fullscreen == 'auto': self.borderless = self._fake_fullscreen = False if self.fullscreen == 'fake': self.borderless = self._fake_fullscreen = True Logger.warning("The 'fake' fullscreen option has been " "deprecated, use Window.borderless or the " "borderless Config option instead.") if self.fullscreen == 'fake' or self.borderless: Logger.debug('WinPygame: Set window to borderless mode.') self.flags |= pygame.NOFRAME # If no position set in borderless mode, we always need # to set the position. So use 0, 0. if self._pos is None: self._pos = (0, 0) environ['SDL_VIDEO_WINDOW_POS'] = '%d,%d' % self._pos elif self.fullscreen in ('auto', True): Logger.debug('WinPygame: Set window to fullscreen mode') self.flags |= pygame.FULLSCREEN elif self._pos is not None: environ['SDL_VIDEO_WINDOW_POS'] = '%d,%d' % self._pos # never stay with a None pos, application using w.center will be fired. self._pos = (0, 0) # prepare keyboard repeat_delay = int(Config.get('kivy', 'keyboard_repeat_delay')) repeat_rate = float(Config.get('kivy', 'keyboard_repeat_rate')) pygame.key.set_repeat(repeat_delay, int(1000. / repeat_rate)) # set window icon before calling set_mode try: filename_icon = self.icon or Config.get('kivy', 'window_icon') if filename_icon == '': logo_size = 32 if platform == 'macosx': logo_size = 512 elif platform == 'win': logo_size = 64 filename_icon = 'kivy-icon-{}.png'.format(logo_size) filename_icon = resource_find( join(kivy_data_dir, 'logo', filename_icon)) self.set_icon(filename_icon) except: Logger.exception('Window: cannot set icon') # try to use mode with multisamples try: self._pygame_set_mode() except pygame.error as e: if multisamples: Logger.warning('WinPygame: Video: failed (multisamples=%d)' % multisamples) Logger.warning('WinPygame: trying without antialiasing') pygame.display.gl_set_attribute( pygame.GL_MULTISAMPLEBUFFERS, 0) pygame.display.gl_set_attribute( pygame.GL_MULTISAMPLESAMPLES, 0) multisamples = 0 try: self._pygame_set_mode() except pygame.error as e: raise CoreCriticalException(e.message) else: raise CoreCriticalException(e.message) info = pygame.display.Info() self._size = (info.current_w, info.current_h) #self.dispatch('on_resize', *self._size) # in order to debug futur issue with pygame/display, let's show # more debug output. Logger.debug('Window: Display driver ' + pygame.display.get_driver()) Logger.debug('Window: Actual window size: %dx%d', info.current_w, info.current_h) if platform != 'android': # unsupported platform, such as android that doesn't support # gl_get_attribute. Logger.debug( 'Window: Actual color bits r%d g%d b%d a%d', pygame.display.gl_get_attribute(pygame.GL_RED_SIZE), pygame.display.gl_get_attribute(pygame.GL_GREEN_SIZE), pygame.display.gl_get_attribute(pygame.GL_BLUE_SIZE), pygame.display.gl_get_attribute(pygame.GL_ALPHA_SIZE)) Logger.debug( 'Window: Actual depth bits: %d', pygame.display.gl_get_attribute(pygame.GL_DEPTH_SIZE)) Logger.debug( 'Window: Actual stencil bits: %d', pygame.display.gl_get_attribute(pygame.GL_STENCIL_SIZE)) Logger.debug( 'Window: Actual multisampling samples: %d', pygame.display.gl_get_attribute(pygame.GL_MULTISAMPLESAMPLES)) super(WindowPygame, self).create_window() # set mouse visibility pygame.mouse.set_visible( Config.getboolean('graphics', 'show_cursor')) # if we are on android platform, automaticly create hooks if android: from kivy.support import install_android install_android() def close(self): pygame.display.quit() self.dispatch('on_close') def on_title(self, instance, value): if self.initialized: pygame.display.set_caption(self.title) def set_icon(self, filename): if not exists(filename): return False try: if platform == 'win': try: if self._set_icon_win(filename): return True except: # fallback on standard loading then. pass # for all others platform, or if the ico is not available, use the # default way to set it. self._set_icon_standard(filename) super(WindowPygame, self).set_icon(filename) except: Logger.exception('WinPygame: unable to set icon') def _set_icon_standard(self, filename): if PY2: try: im = pygame.image.load(filename) except UnicodeEncodeError: im = pygame.image.load(filename.encode('utf8')) else: im = pygame.image.load(filename) if im is None: raise Exception('Unable to load window icon (not found)') pygame.display.set_icon(im) def _set_icon_win(self, filename): # ensure the window ico is ended by ico if not filename.endswith('.ico'): filename = '{}.ico'.format(filename.rsplit('.', 1)[0]) if not exists(filename): return False import win32api import win32gui import win32con hwnd = pygame.display.get_wm_info()['window'] icon_big = win32gui.LoadImage( None, filename, win32con.IMAGE_ICON, 48, 48, win32con.LR_LOADFROMFILE) icon_small = win32gui.LoadImage( None, filename, win32con.IMAGE_ICON, 16, 16, win32con.LR_LOADFROMFILE) win32api.SendMessage( hwnd, win32con.WM_SETICON, win32con.ICON_SMALL, icon_small) win32api.SendMessage( hwnd, win32con.WM_SETICON, win32con.ICON_BIG, icon_big) return True def screenshot(self, *largs, **kwargs): global glReadPixels, GL_RGBA, GL_UNSIGNED_BYTE filename = super(WindowPygame, self).screenshot(*largs, **kwargs) if filename is None: return None if glReadPixels is None: from kivy.graphics.opengl import (glReadPixels, GL_RGBA, GL_UNSIGNED_BYTE) width, height = self.system_size data = glReadPixels(0, 0, width, height, GL_RGBA, GL_UNSIGNED_BYTE) if PY2: data = str(buffer(data)) else: data = bytes(bytearray(data)) surface = pygame.image.fromstring(data, (width, height), 'RGBA', True) pygame.image.save(surface, filename) Logger.debug('Window: Screenshot saved at <%s>' % filename) return filename def flip(self): pygame.display.flip() super(WindowPygame, self).flip() @deprecated def toggle_fullscreen(self): if self.flags & pygame.FULLSCREEN: self.flags &= ~pygame.FULLSCREEN else: self.flags |= pygame.FULLSCREEN self._pygame_set_mode() def _mainloop(self): EventLoop.idle() for event in pygame.event.get(): # kill application (SIG_TERM) if event.type == pygame.QUIT: if self.dispatch('on_request_close'): continue EventLoop.quit = True self.close() # mouse move elif event.type == pygame.MOUSEMOTION: x, y = event.pos self.mouse_pos = x, self.system_size[1] - y # don't dispatch motion if no button are pressed if event.buttons == (0, 0, 0): continue self._mouse_x = x self._mouse_y = y self._mouse_meta = self.modifiers self.dispatch('on_mouse_move', x, y, self.modifiers) # mouse action elif event.type in (pygame.MOUSEBUTTONDOWN, pygame.MOUSEBUTTONUP): self._pygame_update_modifiers() x, y = event.pos btn = 'left' if event.button == 3: btn = 'right' elif event.button == 2: btn = 'middle' elif event.button == 4: btn = 'scrolldown' elif event.button == 5: btn = 'scrollup' elif event.button == 6: btn = 'scrollright' elif event.button == 7: btn = 'scrollleft' 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) # joystick action elif event.type == pygame.JOYAXISMOTION: self.dispatch('on_joy_axis', event.joy, event.axis, event.value) elif event.type == pygame.JOYHATMOTION: self.dispatch('on_joy_hat', event.joy, event.hat, event.value) elif event.type == pygame.JOYBALLMOTION: self.dispatch('on_joy_ball', event.joy, event.ballid, event.rel[0], event.rel[1]) elif event.type == pygame.JOYBUTTONDOWN: self.dispatch('on_joy_button_down', event.joy, event.button) elif event.type == pygame.JOYBUTTONUP: self.dispatch('on_joy_button_up', event.joy, event.button) # keyboard action elif event.type in (pygame.KEYDOWN, pygame.KEYUP): self._pygame_update_modifiers(event.mod) # atm, don't handle keyup if event.type == pygame.KEYUP: self.dispatch('on_key_up', event.key, event.scancode) continue # don't dispatch more key if down event is accepted if self.dispatch('on_key_down', event.key, event.scancode, event.unicode, self.modifiers): continue self.dispatch('on_keyboard', event.key, event.scancode, event.unicode, self.modifiers) # video resize elif event.type == pygame.VIDEORESIZE: self._size = event.size self.update_viewport() elif event.type == pygame.VIDEOEXPOSE: self.canvas.ask_update() # ignored event elif event.type == pygame.ACTIVEEVENT: pass # drop file (pygame patch needed) elif event.type == pygame.USEREVENT and \ hasattr(pygame, 'USEREVENT_DROPFILE') and \ event.code == pygame.USEREVENT_DROPFILE: self.dispatch('on_dropfile', event.filename) ''' # unhandled event ! else: Logger.debug('WinPygame: Unhandled event %s' % str(event)) ''' def mainloop(self): while not EventLoop.quit and EventLoop.status == 'started': try: self._mainloop() if not pygame.display.get_active(): pygame.time.wait(100) except BaseException as inst: # use exception manager first r = ExceptionManager.handle_exception(inst) if r == ExceptionManager.RAISE: stopTouchApp() raise else: pass # # Pygame wrapper # def _pygame_set_mode(self, size=None): if size is None: size = self.size if self.fullscreen == 'auto': pygame.display.set_mode((0, 0), self.flags) else: pygame.display.set_mode(size, self.flags) def _pygame_update_modifiers(self, mods=None): # Available mod, from dir(pygame) # 'KMOD_ALT', 'KMOD_CAPS', 'KMOD_CTRL', 'KMOD_LALT', # 'KMOD_LCTRL', 'KMOD_LMETA', 'KMOD_LSHIFT', 'KMOD_META', # 'KMOD_MODE', 'KMOD_NONE' if mods is None: mods = pygame.key.get_mods() self._modifiers = [] if mods & (pygame.KMOD_SHIFT | pygame.KMOD_LSHIFT): self._modifiers.append('shift') if mods & (pygame.KMOD_ALT | pygame.KMOD_LALT): self._modifiers.append('alt') if mods & (pygame.KMOD_CTRL | pygame.KMOD_LCTRL): self._modifiers.append('ctrl') if mods & (pygame.KMOD_META | pygame.KMOD_LMETA): self._modifiers.append('meta') def request_keyboard(self, callback, target, input_type='text'): keyboard = super(WindowPygame, self).request_keyboard( callback, target, input_type) if android and not self.allow_vkeyboard: android.show_keyboard(target, input_type) return keyboard def release_keyboard(self, *largs): super(WindowPygame, self).release_keyboard(*largs) if android: android.hide_keyboard() return True Kivy-1.9.0/kivy/core/window/_window_sdl2.pyx0000664000175000017500000002703712506214117021012 0ustar titotito00000000000000include "../../../kivy/lib/sdl2.pxi" include "../../../kivy/graphics/config.pxi" from libc.string cimport memcpy from os import environ cdef class _WindowSDL2Storage: cdef SDL_Window *win cdef SDL_GLContext ctx cdef SDL_Surface *surface cdef SDL_Surface *icon cdef int win_flags def __cinit__(self): self.win = NULL self.ctx = NULL self.surface = NULL self.win_flags = 0 def die(self): raise RuntimeError( SDL_GetError()) def setup_window(self, x, y, width, height, borderless, fullscreen, resizable, state): self.win_flags = SDL_WINDOW_OPENGL | SDL_WINDOW_SHOWN | SDL_WINDOW_ALLOW_HIGHDPI IF USE_IOS: self.win_flags |= SDL_WINDOW_BORDERLESS | SDL_WINDOW_RESIZABLE | SDL_WINDOW_FULLSCREEN_DESKTOP ELSE: if resizable: self.win_flags |= SDL_WINDOW_RESIZABLE if borderless: self.win_flags |= SDL_WINDOW_BORDERLESS if fullscreen == 'auto': self.win_flags |= SDL_WINDOW_FULLSCREEN_DESKTOP elif fullscreen is True: self.win_flags |= SDL_WINDOW_FULLSCREEN if state == 'maximized': self.win_flags |= SDL_WINDOW_MAXIMIZED elif state == 'minimized': self.win_flags |= SDL_WINDOW_MINIMIZED elif state == 'hidden': self.win_flags |= SDL_WINDOW_HIDDEN if SDL_Init(SDL_INIT_VIDEO | SDL_INIT_JOYSTICK) < 0: self.die() # Set default orientation (force landscape for now) cdef bytes orientations if PY3: orientations = bytes(environ.get('KIVY_ORIENTATION', 'LandscapeLeft LandscapeRight'), encoding='utf8') elif USE_IOS: # ios should use all if available orientations = environ.get('KIVY_ORIENTATION', 'LandscapeLeft LandscapeRight Portrait PortraitUpsideDown') else: orientations = environ.get('KIVY_ORIENTATION', 'LandscapeLeft LandscapeRight') SDL_SetHint(SDL_HINT_ORIENTATIONS, orientations) SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1) SDL_GL_SetAttribute(SDL_GL_DEPTH_SIZE, 16) SDL_GL_SetAttribute(SDL_GL_STENCIL_SIZE, 1) SDL_GL_SetAttribute(SDL_GL_RED_SIZE, 8) SDL_GL_SetAttribute(SDL_GL_BLUE_SIZE, 8) SDL_GL_SetAttribute(SDL_GL_GREEN_SIZE, 8) SDL_GL_SetAttribute(SDL_GL_ALPHA_SIZE, 8) SDL_GL_SetAttribute(SDL_GL_RETAINED_BACKING, 0) SDL_GL_SetAttribute(SDL_GL_ACCELERATED_VISUAL, 1) if x is None: x = SDL_WINDOWPOS_UNDEFINED if y is None: y = SDL_WINDOWPOS_UNDEFINED self.win = SDL_CreateWindow(NULL, x, y, width, height, self.win_flags) if not self.win: self.die() SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 2) SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 0) self.ctx = SDL_GL_CreateContext(self.win) if not self.ctx: self.die() SDL_JoystickOpen(0) SDL_EventState(SDL_DROPFILE, SDL_ENABLE) cdef int w, h SDL_GetWindowSize(self.win, &w, &h) return w, h def _get_gl_size(self): cdef int w, h SDL_GL_GetDrawableSize(self.win, &w, &h) return w, h def resize_display_mode(self, w, h): cdef SDL_DisplayMode mode cdef int draw_w, draw_h SDL_GetWindowDisplayMode(self.win, &mode) if USE_IOS: SDL_GL_GetDrawableSize(self.win, &draw_w, &draw_h) mode.w = draw_w mode.h = draw_h SDL_SetWindowDisplayMode(self.win, &mode) else: mode.w = w mode.h = h SDL_SetWindowDisplayMode(self.win, &mode) SDL_GetWindowDisplayMode(self.win, &mode) return mode.w, mode.h def resize_window(self, w, h): if self.window_size != [w, h]: SDL_SetWindowSize(self.win, w, h) def maximize_window(self): SDL_MaximizeWindow(self.win) def minimize_window(self): SDL_MinimizeWindow(self.win) def restore_window(self): SDL_RestoreWindow(self.win) def hide_window(self): SDL_HideWindow(self.win) def show_window(self): SDL_ShowWindow(self.win) def set_border_state(self, state): SDL_SetWindowBordered(self.win, SDL_FALSE if state else SDL_TRUE) def set_fullscreen_mode(self, mode): if mode == 'auto': mode = SDL_WINDOW_FULLSCREEN_DESKTOP elif mode is True: mode = SDL_WINDOW_FULLSCREEN else: mode = False IF not USE_IOS: SDL_SetWindowFullscreen(self.win, mode) def set_window_title(self, str title): SDL_SetWindowTitle(self.win, title.encode('utf-8')) def set_window_icon(self, str filename): icon = IMG_Load(filename.encode('utf-8')) SDL_SetWindowIcon(self.win, icon) def teardown_window(self): SDL_GL_DeleteContext(self.ctx) SDL_DestroyWindow(self.win) SDL_Quit() def show_keyboard(self): if not SDL_IsTextInputActive(): SDL_StartTextInput() def hide_keyboard(self): if SDL_IsTextInputActive(): SDL_StopTextInput() def is_keyboard_shown(self): return SDL_IsTextInputActive() def poll(self): cdef SDL_Event event if SDL_PollEvent(&event) == 0: return False action = None if event.type == SDL_QUIT: return ('quit', ) elif event.type == SDL_DROPFILE: return ('dropfile', event.drop.file) elif event.type == SDL_MOUSEMOTION: x = event.motion.x y = event.motion.y return ('mousemotion', x, y) elif event.type == SDL_MOUSEBUTTONDOWN or event.type == SDL_MOUSEBUTTONUP: x = event.button.x y = event.button.y button = event.button.button action = 'mousebuttondown' if event.type == SDL_MOUSEBUTTONDOWN else 'mousebuttonup' return (action, x, y, button) elif event.type == SDL_MOUSEWHEEL: x = event.button.x y = event.button.y button = event.button.button action = 'mousewheel' + ('down' if x > 0 else 'up') if x != 0 else ('left' if y < 0 else 'right') return (action, x, y, button) elif event.type == SDL_FINGERMOTION: fid = event.tfinger.fingerId x = event.tfinger.x y = event.tfinger.y return ('fingermotion', fid, x, y) elif event.type == SDL_FINGERDOWN or event.type == SDL_FINGERUP: fid = event.tfinger.fingerId x = event.tfinger.x y = event.tfinger.y action = 'fingerdown' if event.type == SDL_FINGERDOWN else 'fingerup' return (action, fid, x, y) elif event.type == SDL_JOYAXISMOTION: return ('joyaxismotion', event.jaxis.which, event.jaxis.axis, event.jaxis.value) elif event.type == SDL_JOYHATMOTION: vx = 0 vy = 0 if (event.jhat.value != SDL_HAT_CENTERED): if (event.jhat.value & SDL_HAT_UP): vy=1 elif (event.jhat.value & SDL_HAT_DOWN): vy=-1 if (event.jhat.value & SDL_HAT_RIGHT): vx=1 elif (event.jhat.value & SDL_HAT_LEFT): vx=-1 return ('joyhatmotion', event.jhat.which, event.jhat.hat, (vx,vy)) elif event.type == SDL_JOYBALLMOTION: return ('joyballmotion', event.jball.which, event.jball.ball, event.jball.xrel, event.jball.yrel) elif event.type == SDL_JOYBUTTONDOWN: return ('joybuttondown', event.jbutton.which, event.jbutton.button) elif event.type == SDL_JOYBUTTONUP: return ('joybuttonup', event.jbutton.which, event.jbutton.button) elif event.type == SDL_WINDOWEVENT: if event.window.event == SDL_WINDOWEVENT_EXPOSED: action = ('windowexposed', ) elif event.window.event == SDL_WINDOWEVENT_RESIZED: action = ('windowresized', event.window.data1, event.window.data2) elif event.window.event == SDL_WINDOWEVENT_MINIMIZED: action = ('windowminimized', ) elif event.window.event == SDL_WINDOWEVENT_RESTORED: action = ('windowrestored', ) elif event.window.event == SDL_WINDOWEVENT_SHOWN: action = ('windowshown', ) elif event.window.event == SDL_WINDOWEVENT_HIDDEN: action = ('windowhidden', ) elif event.window.event == SDL_WINDOWEVENT_ENTER: action = ('windowenter', ) elif event.window.event == SDL_WINDOWEVENT_LEAVE: action = ('windowleave', ) elif event.window.event == SDL_WINDOWEVENT_FOCUS_GAINED: action = ('windowfocusgained', ) elif event.window.event == SDL_WINDOWEVENT_FOCUS_LOST: action = ('windowfocuslost', ) elif event.window.event == SDL_WINDOWEVENT_CLOSE: action = ('windowclose', ) elif event.window.event == SDL_WINDOWEVENT_MOVED: action = ('windowmoved', event.window.data1, event.window.data2) else: # print('receive unknown sdl window event', event.type) pass return action elif event.type == SDL_KEYDOWN or event.type == SDL_KEYUP: action = 'keydown' if event.type == SDL_KEYDOWN else 'keyup' mod = event.key.keysym.mod scancode = event.key.keysym.scancode key = event.key.keysym.sym return (action, mod, key, scancode, None) elif event.type == SDL_TEXTINPUT: s = event.text.text.decode('utf-8') return ('textinput', s) else: # print('receive unknown sdl event', event.type) pass def flip(self): SDL_GL_SwapWindow(self.win) def save_bytes_in_png(self, filename, data, int width, int height): cdef SDL_Surface *surface = SDL_CreateRGBSurfaceFrom( data, width, height, 24, width*3, 0x0000ff, 0x00ff00, 0xff0000, 0) cdef bytes bytes_filename = filename.encode('utf-8') cdef char *real_filename = bytes_filename cdef SDL_Surface *flipped_surface = flipVert(surface) IMG_SavePNG(flipped_surface, real_filename) property window_size: def __get__(self): cdef int w, h SDL_GetWindowSize(self.win, &w, &h) return [w, h] # Based on the example at # http://content.gpwiki.org/index.php/OpenGL:Tutorials:Taking_a_Screenshot cdef SDL_Surface* flipVert(SDL_Surface* sfc): cdef SDL_Surface* result = SDL_CreateRGBSurface( sfc.flags, sfc.w, sfc.h, sfc.format.BytesPerPixel * 8, sfc.format.Rmask, sfc.format.Gmask, sfc.format.Bmask, sfc.format.Amask) cdef Uint8* pixels = sfc.pixels cdef Uint8* rpixels = result.pixels cdef tuple output = (sfc.w, sfc.h, sfc.format.BytesPerPixel, sfc.pitch) print(output) cdef Uint32 pitch = sfc.pitch cdef Uint32 pxlength = pitch*sfc.h cdef Uint32 pos cdef int line for line in range(sfc.h): pos = line * pitch; memcpy(&rpixels[pos], &pixels[(pxlength-pos)-pitch], pitch) return result Kivy-1.9.0/kivy/core/window/window_egl_rpi.py0000664000175000017500000000531212462275426021243 0ustar titotito00000000000000''' EGL Rpi Window: EGL Window provider, specialized for the Pi Inspired by: rpi_vid_core + JF002 rpi kivy repo ''' __all__ = ('WindowEglRpi', ) from kivy.logger import Logger from kivy.core.window import WindowBase from kivy.base import EventLoop from kivy.lib.vidcore_lite import bcm, egl class WindowEglRpi(WindowBase): def create_window(self): bcm.host_init() w, h = bcm.graphics_get_display_size(0) Logger.debug('Window: Actual display size: {}x{}'.format( w, h)) self._size = w, h self._create_window(w, h) self._create_egl_context(self.win, 0) super(WindowEglRpi, self).create_window() def _create_window(self, w, h): dst = bcm.Rect(0, 0, w, h) src = bcm.Rect(0, 0, w << 16, h << 16) display = egl.bcm_display_open(0) update = egl.bcm_update_start(0) element = egl.bcm_element_add(update, display, 0, dst, src) self.win = egl.NativeWindow(element, w, h) egl.bcm_update_submit_sync(update) def _create_egl_context(self, win, flags): api = egl._constants.EGL_OPENGL_ES_API c = egl._constants attribs = [ c.EGL_RED_SIZE, 8, c.EGL_GREEN_SIZE, 8, c.EGL_BLUE_SIZE, 8, c.EGL_ALPHA_SIZE, 8, c.EGL_DEPTH_SIZE, 16, c.EGL_STENCIL_SIZE, 8, c.EGL_SURFACE_TYPE, c.EGL_WINDOW_BIT, c.EGL_NONE] attribs_context = [c.EGL_CONTEXT_CLIENT_VERSION, 2, c.EGL_NONE] display = egl.GetDisplay(c.EGL_DEFAULT_DISPLAY) egl.Initialise(display) egl.BindAPI(c.EGL_OPENGL_ES_API) egl.GetConfigs(display) config = egl.ChooseConfig(display, attribs, 1)[0] surface = egl.CreateWindowSurface(display, config, win) context = egl.CreateContext(display, config, None, attribs_context) egl.MakeCurrent(display, surface, surface, context) self.egl_info = (display, surface, context) egl.MakeCurrent(display, surface, surface, context) def close(self): egl.Terminate(self.egl_info[0]) def flip(self): egl.SwapBuffers(self.egl_info[0], self.egl_info[1]) def _mainloop(self): EventLoop.idle() def mainloop(self): while not EventLoop.quit and EventLoop.status == 'started': try: self._mainloop() except BaseException as inst: raise ''' # use exception manager first r = ExceptionManager.handle_exception(inst) if r == ExceptionManager.RAISE: #stopTouchApp() raise else: pass ''' Kivy-1.9.0/kivy/core/window/window_sdl2.py0000664000175000017500000005003612507217730020463 0ustar titotito00000000000000# found a way to include it more easily. ''' SDL2 Window =========== Windowing provider directly based on our own wrapped version of SDL. TODO: - fix keys - support scrolling - clean code - manage correctly all sdl events ''' __all__ = ('WindowSDL2', ) from os.path import join from kivy import kivy_data_dir from kivy.logger import Logger from kivy import metrics from kivy.base import EventLoop, ExceptionManager, stopTouchApp from kivy.clock import Clock from kivy.config import Config from kivy.core.window import WindowBase from kivy.core.window._window_sdl2 import _WindowSDL2Storage from kivy.input.provider import MotionEventProvider from kivy.input.motionevent import MotionEvent from kivy.resources import resource_find from kivy.utils import platform, deprecated from kivy.compat import unichr from collections import deque KMOD_LCTRL = 64 KMOD_RCTRL = 128 KMOD_RSHIFT = 2 KMOD_LSHIFT = 1 KMOD_RALT = 512 KMOD_LALT = 256 KMOD_LMETA = 1024 KMOD_RMETA = 2048 SDLK_SHIFTL = 1073742049 SDLK_SHIFTR = 1073742053 SDLK_LCTRL = 1073742048 SDLK_RCTRL = 1073742052 SDLK_LALT = 1073742050 SDLK_RALT = 1073742054 SDLK_LEFT = 1073741904 SDLK_RIGHT = 1073741903 SDLK_UP = 1073741906 SDLK_DOWN = 1073741905 SDLK_HOME = 1073741898 SDLK_END = 1073741901 SDLK_PAGEUP = 1073741899 SDLK_PAGEDOWN = 1073741902 SDLK_SUPER = 1073742051 SDLK_CAPS = 1073741881 SDLK_INSERT = 1073741897 SDLK_KEYPADNUM = 1073741907 SDLK_F1 = 1073741882 SDLK_F2 = 1073741883 SDLK_F3 = 1073741884 SDLK_F4 = 1073741885 SDLK_F5 = 1073741886 SDLK_F6 = 1073741887 SDLK_F7 = 1073741888 SDLK_F8 = 1073741889 SDLK_F9 = 1073741890 SDLK_F10 = 1073741891 SDLK_F11 = 1073741892 SDLK_F12 = 1073741893 SDLK_F13 = 1073741894 SDLK_F14 = 1073741895 SDLK_F15 = 1073741896 class SDL2MotionEvent(MotionEvent): def depack(self, args): self.is_touch = True self.profile = ('pos', ) self.sx, self.sy = args win = EventLoop.window super(SDL2MotionEvent, self).depack(args) class SDL2MotionEventProvider(MotionEventProvider): win = None q = deque() touchmap = {} def update(self, dispatch_fn): touchmap = self.touchmap while True: try: value = self.q.pop() except IndexError: return action, fid, x, y = value y = 1 - y if fid not in touchmap: touchmap[fid] = me = SDL2MotionEvent('sdl', fid, (x, y)) else: me = touchmap[fid] me.move((x, y)) if action == 'fingerdown': dispatch_fn('begin', me) elif action == 'fingerup': me.update_time_end() dispatch_fn('end', me) del touchmap[fid] else: dispatch_fn('update', me) class WindowSDL(WindowBase): def __init__(self, **kwargs): self._win = _WindowSDL2Storage() super(WindowSDL, self).__init__() self._mouse_x = self._mouse_y = -1 self._meta_keys = (KMOD_LCTRL, KMOD_RCTRL, KMOD_RSHIFT, KMOD_LSHIFT, KMOD_RALT, KMOD_LALT, KMOD_LMETA, KMOD_RMETA) self.command_keys = { 27: 'escape', 9: 'tab', 8: 'backspace', 13: 'enter', 127: 'del', 271: 'enter', 273: 'up', 274: 'down', 275: 'right', 276: 'left', 278: 'home', 279: 'end', 280: 'pgup', 281: 'pgdown'} self._mouse_buttons_down = set() def create_window(self, *largs): if self._fake_fullscreen: if not self.borderless: self.fullscreen = self._fake_fullscreen = False elif not self.fullscreen or self.fullscreen == 'auto': self.borderless = self._fake_fullscreen = False if self.fullscreen == 'fake': self.borderless = self._fake_fullscreen = True Logger.warning("The 'fake' fullscreen option has been " "deprecated, use Window.borderless or the " "borderless Config option instead.") if not self.initialized: if self.position == 'auto': pos = None, None elif self.position == 'custom': pos = self.left, self.top # setup ! w, h = self.system_size resizable = Config.getboolean('graphics', 'resizable') state = (Config.get('graphics', 'window_state') if self._is_desktop else None) self.system_size = _size = self._win.setup_window( pos[0], pos[1], w, h, self.borderless, self.fullscreen, resizable, state) # calculate density sz = self._win._get_gl_size()[0] self._density = density = sz / _size[0] if self._is_desktop and self.size[0] != _size[0]: self.dpi = density * 96. # never stay with a None pos, application using w.center # will be fired. self._pos = (0, 0) else: w, h = self.system_size self._win.resize_window(w, h) self._win.set_border_state(self.borderless) self._win.set_fullscreen_mode(self.fullscreen) super(WindowSDL, self).create_window() if self.initialized: return # auto add input provider Logger.info('Window: auto add sdl2 input provider') from kivy.base import EventLoop SDL2MotionEventProvider.win = self EventLoop.add_input_provider(SDL2MotionEventProvider('sdl', '')) # set window icon before calling set_mode try: filename_icon = self.icon or Config.get('kivy', 'window_icon') if filename_icon == '': logo_size = 32 if platform == 'macosx': logo_size = 512 elif platform == 'win': logo_size = 64 filename_icon = 'kivy-icon-{}.png'.format(logo_size) filename_icon = resource_find( join(kivy_data_dir, 'logo', filename_icon)) self.set_icon(filename_icon) except: Logger.exception('Window: cannot set icon') def close(self): self._win.teardown_window() self.dispatch('on_close') def maximize(self): if self._is_desktop: self._win.maximize_window() else: Logger.warning('Window: maximize() is used only on desktop OSes.') def minimize(self): if self._is_desktop: self._win.minimize_window() else: Logger.warning('Window: minimize() is used only on desktop OSes.') def restore(self): if self._is_desktop: self._win.restore_window() else: Logger.warning('Window: restore() is used only on desktop OSes.') def hide(self): if self._is_desktop: self._win.hide_window() else: Logger.warning('Window: hide() is used only on desktop OSes.') def show(self): if self._is_desktop: self._win.show_window() else: Logger.warning('Window: show() is used only on desktop OSes.') @deprecated def toggle_fullscreen(self): if self.fullscreen in (True, 'auto'): self.fullscreen = False else: self.fullscreen = 'auto' def set_title(self, title): self._win.set_window_title(title) def set_icon(self, filename): self._win.set_window_icon(str(filename)) def screenshot(self, *largs, **kwargs): filename = super(WindowSDL, self).screenshot(*largs, **kwargs) if filename is None: return from kivy.graphics.opengl import glReadPixels, GL_RGB, GL_UNSIGNED_BYTE width, height = self.size data = glReadPixels(0, 0, width, height, GL_RGB, GL_UNSIGNED_BYTE) self._win.save_bytes_in_png(filename, data, width, height) Logger.debug('Window: Screenshot saved at <%s>' % filename) return filename def flip(self): self._win.flip() super(WindowSDL, self).flip() def _fix_mouse_pos(self, x, y): density = self._density y -= 1 if self._is_desktop and self.size[0] != self._size[0]: x, y = x * density, (y * density) - self.system_size[1] #y = self.system_size[1] - y self.mouse_pos = x, y else: self.mouse_pos = x, self.system_size[1] - y return x, y def _mainloop(self): EventLoop.idle() while True: event = self._win.poll() if event is False: break if event is None: continue action, args = event[0], event[1:] if action == 'quit': EventLoop.quit = True self.close() break elif action in ('fingermotion', 'fingerdown', 'fingerup'): # for finger, pass the raw event to SDL motion event provider # XXX this is problematic. On OSX, it generates touches with 0, # 0 coordinates, at the same times as mouse. But it works. # We have a conflict of using either the mouse or the finger. # Right now, we have no mechanism that we could use to know # which is the preferred one for the application. if platform == "ios": SDL2MotionEventProvider.q.appendleft(event) pass elif action == 'mousemotion': x, y = args x, y = self._fix_mouse_pos(x, y) self._mouse_x = x self._mouse_y = y # don't dispatch motion if no button are pressed if len(self._mouse_buttons_down) == 0: continue self._mouse_meta = self.modifiers self.dispatch('on_mouse_move', x, y, self.modifiers) elif action in ('mousebuttondown', 'mousebuttonup'): x, y, button = args x, y = self._fix_mouse_pos(x, y) btn = 'left' if button == 3: btn = 'right' elif button == 2: btn = 'middle' eventname = 'on_mouse_down' self._mouse_buttons_down.add(button) if action == 'mousebuttonup': eventname = 'on_mouse_up' self._mouse_buttons_down.remove(button) self._mouse_x = x self._mouse_y = y self.dispatch(eventname, x, y, btn, self.modifiers) elif action.startswith('mousewheel'): self._update_modifiers() x, y, button = args btn = 'scrolldown' if action.endswith('up'): btn = 'scrollup' elif action.endswith('right'): btn = 'scrollright' elif action.endswith('left'): btn = 'scrollleft' self._mouse_meta = self.modifiers self._mouse_btn = btn #times = x if y == 0 else y #times = min(abs(times), 100) #for k in range(times): self._mouse_down = True self.dispatch('on_mouse_down', self._mouse_x, self._mouse_y, btn, self.modifiers) self._mouse_down = False self.dispatch('on_mouse_up', self._mouse_x, self._mouse_y, btn, self.modifiers) elif action == 'dropfile': dropfile = args self.dispatch('on_dropfile', dropfile[0]) # video resize elif action == 'windowresized': self._size = self._win.window_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) elif action == 'windowresized': self.canvas.ask_update() elif action == 'windowrestored': self.canvas.ask_update() elif action == 'windowexposed': self.canvas.ask_update() elif action == 'windowminimized': if Config.getboolean('kivy', 'pause_on_minimize'): self.do_pause() elif action == 'joyaxismotion': stickid, axisid, value = args self.dispatch('on_joy_axis', stickid, axisid, value) elif action == 'joyhatmotion': stickid, hatid, value = args self.dispatch('on_joy_hat', stickid, hatid, value) elif action == 'joyballmotion': stickid, ballid, xrel, yrel = args self.dispatch('on_joy_ball', stickid, ballid, xrel, yrel) elif action == 'joybuttondown': stickid, buttonid = args self.dispatch('on_joy_button_down', stickid, buttonid) elif action == 'joybuttonup': stickid, buttonid = args self.dispatch('on_joy_button_up', stickid, buttonid) elif action in ('keydown', 'keyup'): mod, key, scancode, kstr = args key_swap = { SDLK_LEFT: 276, SDLK_RIGHT: 275, SDLK_UP: 273, SDLK_DOWN: 274, SDLK_HOME: 278, SDLK_END: 279, SDLK_PAGEDOWN: 281, SDLK_PAGEUP: 280, SDLK_SHIFTR: 303, SDLK_SHIFTL: 304, SDLK_SUPER: 309, SDLK_LCTRL: 305, SDLK_RCTRL: 306, SDLK_LALT: 308, SDLK_RALT: 307, SDLK_CAPS: 301, SDLK_INSERT: 277, SDLK_F1: 282, SDLK_F2: 283, SDLK_F3: 284, SDLK_F4: 285, SDLK_F5: 286, SDLK_F6: 287, SDLK_F7: 288, SDLK_F8: 289, SDLK_F9: 290, SDLK_F10: 291, SDLK_F11: 292, SDLK_F12: 293, SDLK_F13: 294, SDLK_F14: 295, SDLK_F15: 296, SDLK_KEYPADNUM: 300} if platform == 'ios': # XXX ios keyboard suck, when backspace is hit, the delete # keycode is sent. fix it. key_swap[127] = 8 # back try: key = key_swap[key] except KeyError: pass if action == 'keydown': self._update_modifiers(mod, key) else: self._update_modifiers(mod) # ignore the key, it # has been released # if mod in self._meta_keys: if (key not in self._modifiers and key not in self.command_keys.keys()): try: kstr = unichr(key) except ValueError: pass #if 'shift' in self._modifiers and key\ # not in self.command_keys.keys(): # return if action == 'keyup': self.dispatch('on_key_up', key, scancode) continue # don't dispatch more key if down event is accepted if self.dispatch('on_key_down', key, scancode, kstr, self.modifiers): continue self.dispatch('on_keyboard', key, scancode, kstr, self.modifiers) elif action == 'textinput': text = args[0] self.dispatch('on_textinput', text) # XXX on IOS, keydown/up don't send unicode anymore. # With latest sdl, the text is sent over textinput # Right now, redo keydown/up, but we need to seperate both call # too. (and adapt on_key_* API.) #self.dispatch() #self.dispatch('on_key_down', key, None, args[0], # self.modifiers) #self.dispatch('on_keyboard', None, None, args[0], # self.modifiers) #self.dispatch('on_key_up', key, None, args[0], # self.modifiers) # unhandled event ! else: Logger.trace('WindowSDL: Unhandled event %s' % str(event)) def _do_resize(self, dt): Logger.debug('Window: Resize window to %s' % str(self.size)) self._win.resize_window(*self._size) self.dispatch('on_resize', *self.size) def do_pause(self): # should go to app pause mode. from kivy.app import App from kivy.base import stopTouchApp app = App.get_running_app() if not app: Logger.info('WindowSDL: No running App found, exit.') stopTouchApp() return if not app.dispatch('on_pause'): Logger.info('WindowSDL: App doesn\'t support pause mode, stop.') stopTouchApp() return # XXX FIXME wait for sdl resume while True: event = self._win.poll() if event is False: continue if event is None: continue action, args = event[0], event[1:] if action == 'quit': EventLoop.quit = True self.close() break elif action == 'windowrestored': break app.dispatch('on_resume') 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() except BaseException as inst: # use exception manager first r = ExceptionManager.handle_exception(inst) if r == ExceptionManager.RAISE: stopTouchApp() raise else: pass # # Pygame wrapper # def _update_modifiers(self, mods=None, key=None): # Available mod, from dir(pygame) # 'KMOD_ALT', 'KMOD_CAPS', 'KMOD_CTRL', 'KMOD_LALT', # 'KMOD_LCTRL', 'KMOD_LMETA', 'KMOD_LSHIFT', 'KMOD_META', # 'KMOD_MODE', 'KMOD_NONE' if mods is None and key is None: return modifiers = set() if mods is not None: if mods & (KMOD_RSHIFT | KMOD_LSHIFT): modifiers.add('shift') if mods & (KMOD_RALT | KMOD_LALT): modifiers.add('alt') if mods & (KMOD_RCTRL | KMOD_LCTRL): modifiers.add('ctrl') if mods & (KMOD_RMETA | KMOD_LMETA): modifiers.add('meta') if key is not None: if key in (KMOD_RSHIFT, KMOD_LSHIFT): modifiers.add('shift') if key in (KMOD_RALT, KMOD_LALT): modifiers.add('alt') if key in (KMOD_RCTRL, KMOD_LCTRL): modifiers.add('ctrl') if key in (KMOD_RMETA, KMOD_LMETA): modifiers.add('meta') self._modifiers = list(modifiers) return def request_keyboard(self, callback, target, input_type='text'): self._sdl_keyboard = super(WindowSDL, self).\ request_keyboard(callback, target, input_type) self._win.show_keyboard() Clock.schedule_interval(self._check_keyboard_shown, 1 / 5.) return self._sdl_keyboard def release_keyboard(self, *largs): super(WindowSDL, self).release_keyboard(*largs) self._win.hide_keyboard() self._sdl_keyboard = None return True def _check_keyboard_shown(self, dt): if self._sdl_keyboard is None: return False if not self._win.is_keyboard_shown(): self._sdl_keyboard.release() Kivy-1.9.0/kivy/core/window/window_x11_core.c0000664000175000017500000002443712502235633021035 0ustar titotito00000000000000 #include #include #include #include #include #include #include #include #include #include #include typedef int (*event_cb_t)(XEvent *event); event_cb_t g_event_callback = NULL; static int Xscreen; static Atom del_atom; static Colormap cmap; static Display *Xdisplay; static XVisualInfo *visual; static XRenderPictFormat *pict_format; static GLXFBConfig *fbconfigs, fbconfig; static int numfbconfigs; static GLXContext render_context; static Window Xroot, window_handle; static GLXWindow glX_window_handle; static int g_width, g_height; static int VisData[] = { GLX_RENDER_TYPE, GLX_RGBA_BIT, GLX_DRAWABLE_TYPE, GLX_WINDOW_BIT, GLX_DOUBLEBUFFER, True, GLX_RED_SIZE, 8, GLX_GREEN_SIZE, 8, GLX_BLUE_SIZE, 8, GLX_ALPHA_SIZE, 8, GLX_DEPTH_SIZE, 16, None }; typedef struct WMHints { unsigned long flags; unsigned long functions; unsigned long decorations; long inputMode; unsigned long status; } WMHints; static void fatalError(const char *why) { fprintf(stderr, "%s", why); exit(0x666); } static Bool WaitForMapNotify(Display *d, XEvent *e, char *arg) { return d && e && arg && (e->type == MapNotify) && (e->xmap.window == *(Window*)arg); } static void describe_fbconfig(GLXFBConfig fbconfig) { int doublebuffer; int red_bits, green_bits, blue_bits, alpha_bits, depth_bits; glXGetFBConfigAttrib(Xdisplay, fbconfig, GLX_DOUBLEBUFFER, &doublebuffer); glXGetFBConfigAttrib(Xdisplay, fbconfig, GLX_RED_SIZE, &red_bits); glXGetFBConfigAttrib(Xdisplay, fbconfig, GLX_GREEN_SIZE, &green_bits); glXGetFBConfigAttrib(Xdisplay, fbconfig, GLX_BLUE_SIZE, &blue_bits); glXGetFBConfigAttrib(Xdisplay, fbconfig, GLX_ALPHA_SIZE, &alpha_bits); glXGetFBConfigAttrib(Xdisplay, fbconfig, GLX_DEPTH_SIZE, &depth_bits); fprintf(stderr, "FBConfig selected:\n" "Doublebuffer: %s\n" "Red Bits: %d, Green Bits: %d, Blue Bits: %d, Alpha Bits: %d, Depth Bits: %d\n", doublebuffer == True ? "Yes" : "No", red_bits, green_bits, blue_bits, alpha_bits, depth_bits); } static void createTheWindow(int width, int height, int x, int y, int resizable, int fullscreen, int border, int above, int CWOR, char *title) { XEvent event; int attr_mask; XSizeHints hints; XWMHints *startup_state; XTextProperty textprop; XSetWindowAttributes attr = {0,}; Xdisplay = XOpenDisplay(NULL); if (!Xdisplay) { fatalError("Couldn't connect to X server\n"); } Xscreen = DefaultScreen(Xdisplay); Xroot = RootWindow(Xdisplay, Xscreen); fbconfigs = glXChooseFBConfig(Xdisplay, Xscreen, VisData, &numfbconfigs); fbconfig = 0; int i; for(i = 0; ivisual); if(!pict_format) continue; fbconfig = fbconfigs[i]; if(pict_format->direct.alphaMask > 0) { break; } } if(!fbconfig) { fatalError("No matching FB config found"); } describe_fbconfig(fbconfig); /* Create a colormap - only needed on some X clients, eg. IRIX */ cmap = XCreateColormap(Xdisplay, Xroot, visual->visual, AllocNone); attr.colormap = cmap; attr.background_pixmap = None; attr.border_pixmap = None; attr.border_pixel = 0; attr.override_redirect = True; attr.event_mask = StructureNotifyMask | EnterWindowMask | LeaveWindowMask | ExposureMask | ButtonPressMask | ButtonReleaseMask | OwnerGrabButtonMask | KeyPressMask | PointerMotionMask | KeyReleaseMask; attr_mask = CWBackPixmap| CWBorderPixel| CWColormap| CWEventMask; // Get the available display size int disp_width = DisplayWidth(Xdisplay, DefaultScreen(Xdisplay)); int disp_height = DisplayHeight(Xdisplay, DefaultScreen(Xdisplay)); if ( fullscreen ) { // If the fullscreen is set, we take the size of the screen if ( width == -1 ) { width = disp_width; height = disp_height; } border = 0; }else{ // Check if the user did go fullscreen (set width & height to the size of the screen) // even he didn't set the fullscreen arg. if ( (width == disp_width) & (height == disp_height) ){ fullscreen = True; } } if ( CWOR ){ // As soon attr_mask is set to CWOverrideRedirect, the WM (windowmanager) won't be able to controll // the window properly. To (as an example) make the window stay above you need the cooperation of // the WM and mustn't set CWOR. attr_mask |= CWOverrideRedirect; } window_handle = XCreateWindow( Xdisplay, Xroot, x, y, width, height, 0, visual->depth, InputOutput, visual->visual, attr_mask, &attr); g_width = width; g_height = height; if( !window_handle ) { fatalError("Couldn't create the window\n"); } #if USE_GLX_CREATE_WINDOW int glXattr[] = { None }; glX_window_handle = glXCreateWindow(Xdisplay, fbconfig, window_handle, glXattr); if( !glX_window_handle ) { fatalError("Couldn't create the GLX window\n"); } #else glX_window_handle = window_handle; #endif textprop.value = (unsigned char*)title; textprop.encoding = XA_STRING; textprop.format = 8; textprop.nitems = strlen(title); hints.x = x; hints.y = y; hints.width = width; hints.height = height; hints.flags = USPosition|USSize; startup_state = XAllocWMHints(); startup_state->initial_state = NormalState; startup_state->flags = StateHint; XSetWMProperties(Xdisplay, window_handle,&textprop, &textprop, NULL, 0, &hints, startup_state, NULL); XEvent xev; if ( above ){ Atom type = XInternAtom(Xdisplay,"_NET_WM_STATE", False); Atom value = XInternAtom(Xdisplay,"_NET_WM_STATE_ABOVE", False); XChangeProperty(Xdisplay, window_handle, type, XA_ATOM, 32, PropModeReplace, (const unsigned char *)&value, 1); if ( fullscreen ) { // The fullscreen atom has only be set if the window should be above (and we need the help of the WM) Atom wm_state = XInternAtom(Xdisplay, "_NET_WM_STATE", False); Atom wm_fullscreen = XInternAtom(Xdisplay, "_NET_WM_STATE_FULLSCREEN", False); memset(&xev, 0, sizeof(xev)); xev.type = ClientMessage; xev.xclient.window = window_handle; xev.xclient.message_type = wm_state; xev.xclient.format = 32; xev.xclient.data.l[0] = 1; xev.xclient.data.l[1] = wm_fullscreen; xev.xclient.data.l[2] = 0; } } // Remove window decoration (= no border) by setting WM-hints. This only works // if the WindowManager respects those arguments. if ( !border ){ WMHints wmhints; Atom prop = XInternAtom(Xdisplay,"_MOTIF_WM_HINTS", False); wmhints.flags = 2; wmhints.decorations = 0; XChangeProperty(Xdisplay, window_handle, prop, prop, 32,PropModeReplace, (unsigned char *)&wmhints, 5); } XFree(startup_state); XMapWindow(Xdisplay, window_handle); if ( fullscreen & above ){ // Send the Fullscreen event after the window got mapped XSendEvent (Xdisplay, DefaultRootWindow(Xdisplay), False, SubstructureRedirectMask | SubstructureNotifyMask, &xev); } XIfEvent(Xdisplay, &event, WaitForMapNotify, (char*)&window_handle); if ((del_atom = XInternAtom(Xdisplay, "WM_DELETE_WINDOW", 0)) != None) { XSetWMProtocols(Xdisplay, window_handle, &del_atom, 1); } XFlush(Xdisplay); // Set the PID atom pid_t pid = getpid(); Atom am_wm_pid; am_wm_pid = XInternAtom(Xdisplay, "_NET_WM_PID", False); XChangeProperty(Xdisplay, window_handle, am_wm_pid, XA_CARDINAL, 32, PropModeReplace, (unsigned char *)&pid, 1); } static void createTheRenderContext(void) { int dummy; if (!glXQueryExtension(Xdisplay, &dummy, &dummy)) { fatalError("OpenGL not supported by X server\n"); } { render_context = glXCreateNewContext(Xdisplay, fbconfig, GLX_RGBA_TYPE, 0, True); if (!render_context) { fatalError("Failed to create a GL context\n"); } } if (!glXMakeContextCurrent(Xdisplay, glX_window_handle, glX_window_handle, render_context)) { fatalError("glXMakeCurrent failed for window\n"); } } static int updateTheMessageQueue(void) { XEvent event; //XConfigureEvent *xc; while (XPending(Xdisplay)) { XNextEvent(Xdisplay, &event); switch (event.type) { case ClientMessage: if (event.xclient.data.l[0] == del_atom) { return 0; } break; default: if ( g_event_callback ) { if ( g_event_callback(&event) < 0 ) return 0; } break; /** case ConfigureNotify: xc = &(event.xconfigure); g_width = xc->width; g_height = xc->height; break; **/ } } return 1; } //----------------------------------------------------------------------------- // // Minimal API to be used from cython // void x11_set_event_callback(event_cb_t callback) { g_event_callback = callback; } int x11_create_window(int width, int height, int x, int y, int resizable, int fullscreen, int border, int above, int CWOR, char *title) { createTheWindow(width, height, x, y, resizable, fullscreen, border, above, CWOR, title); createTheRenderContext(); return 1; } void x11_gl_swap(void) { glXSwapBuffers(Xdisplay, glX_window_handle); } int x11_get_width(void) { return g_width; } int x11_get_height(void) { return g_height; } int x11_idle(void) { return updateTheMessageQueue(); } #include "window_x11_keytab.c" long x11_keycode_to_keysym(unsigned int keycode, int shiftDown) { KeySym keysym; long ucs; keysym = XkbKeycodeToKeysym(Xdisplay, keycode, 0, shiftDown); if ( keysym == NoSymbol ) return 0; if ( keysym == XK_Escape ) return 27; else if ( keysym == XK_Return ) return 13; else if ( keysym == XK_BackSpace ) return 8; else if ( keysym == XK_Delete ) return 127; else if ( keysym == XK_Up ) return 273; else if ( keysym == XK_Down ) return 274; else if ( keysym == XK_Left ) return 276; else if ( keysym == XK_Right ) return 275; else if ( keysym == XK_space ) return 32; else if ( keysym == XK_Home ) return 278; else if ( keysym == XK_End ) return 279; else if ( keysym == XK_Page_Up ) return 280; else if ( keysym == XK_Page_Down ) return 281; ucs = keysym2ucs(keysym); //printf("%d -> %d (ucs %ld)\n", keycode, keysym, ucs); return ucs; } Kivy-1.9.0/kivy/core/window/__init__.py0000775000175000017500000013773412507217730020005 0ustar titotito00000000000000# pylint: disable=W0611 # coding: utf-8 ''' Window ====== Core class for creating the default Kivy window. Kivy supports only one window per application: please don't try to create more than one. ''' __all__ = ('Keyboard', 'WindowBase', 'Window') 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, stopTouchApp from kivy.modules import Modules from kivy.event import EventDispatcher from kivy.properties import ListProperty, ObjectProperty, AliasProperty, \ NumericProperty, OptionProperty, StringProperty, BooleanProperty from kivy.utils import platform, reify from kivy.context import get_current_context from kivy.uix.behaviors import FocusBehavior from kivy.setupconfig import USE_SDL2 from kivy.graphics.transformation import Matrix # late import VKeyboard = None android = None class Keyboard(EventDispatcher): '''Keyboard interface that is returned by :meth:`WindowBase.request_keyboard`. When you request a keyboard, you'll get an instance of this class. Whatever the keyboard input is (system or virtual keyboard), you'll receive events through this instance. :Events: `on_key_down`: keycode, text, modifiers Fired when a new key is pressed down `on_key_up`: keycode Fired when a key is released (up) Here is an example of how to request a Keyboard in accordance with the current configuration: .. include:: ../../examples/widgets/keyboardlistener.py :literal: ''' # Keycodes mapping, between str <-> int. Theses keycode are # currently taken from pygame.key. But when a new provider will be # used, it must do the translation to these keycodes too. keycodes = { # specials keys 'backspace': 8, 'tab': 9, 'enter': 13, 'rshift': 303, 'shift': 304, 'alt': 308, 'rctrl': 306, 'lctrl': 305, 'super': 309, 'alt-gr': 307, 'compose': 311, 'pipe': 310, 'capslock': 301, 'escape': 27, 'spacebar': 32, 'pageup': 280, 'pagedown': 281, 'end': 279, 'home': 278, 'left': 276, 'up': 273, 'right': 275, 'down': 274, 'insert': 277, 'delete': 127, 'numlock': 300, 'print': 144, 'screenlock': 145, 'pause': 19, # a-z keys 'a': 97, 'b': 98, 'c': 99, 'd': 100, 'e': 101, 'f': 102, 'g': 103, 'h': 104, 'i': 105, 'j': 106, 'k': 107, 'l': 108, 'm': 109, 'n': 110, 'o': 111, 'p': 112, 'q': 113, 'r': 114, 's': 115, 't': 116, 'u': 117, 'v': 118, 'w': 119, 'x': 120, 'y': 121, 'z': 122, # 0-9 keys '0': 48, '1': 49, '2': 50, '3': 51, '4': 52, '5': 53, '6': 54, '7': 55, '8': 56, '9': 57, # numpad 'numpad0': 256, 'numpad1': 257, 'numpad2': 258, 'numpad3': 259, 'numpad4': 260, 'numpad5': 261, 'numpad6': 262, 'numpad7': 263, 'numpad8': 264, 'numpad9': 265, 'numpaddecimal': 266, 'numpaddivide': 267, 'numpadmul': 268, 'numpadsubstract': 269, 'numpadadd': 270, 'numpadenter': 271, # F1-15 'f1': 282, 'f2': 283, 'f3': 284, 'f4': 285, 'f5': 286, 'f6': 287, 'f7': 288, 'f8': 289, 'f9': 290, 'f10': 291, 'f11': 292, 'f12': 293, 'f13': 294, 'f14': 295, 'f15': 296, # other keys '(': 40, ')': 41, '[': 91, ']': 93, '{': 123, '}': 125, ':': 59, ';': 59, '=': 61, '+': 43, '-': 45, '_': 95, '/': 47, '*': 42, '?': 47, '`': 96, '~': 126, '´': 180, '¦': 166, '\\': 92, '|': 124, '"': 34, "'": 39, ',': 44, '.': 46, '<': 60, '>': 62, '@': 64, '!': 33, '#': 35, '$': 36, '%': 37, '^': 94, '&': 38, '¬': 172, '¨': 168, '…': 8230, 'ù': 249, 'à': 224, 'é': 233, 'è': 232, } __events__ = ('on_key_down', 'on_key_up', 'on_textinput') def __init__(self, **kwargs): super(Keyboard, self).__init__() #: Window which the keyboard is attached too self.window = kwargs.get('window', None) #: Callback that will be called when the keyboard is released self.callback = kwargs.get('callback', None) #: Target that have requested the keyboard self.target = kwargs.get('target', None) #: VKeyboard widget, if allowed by the configuration self.widget = kwargs.get('widget', None) def on_key_down(self, keycode, text, modifiers): pass def on_key_up(self, keycode): pass def on_textinput(self, text): pass def release(self): '''Call this method to release the current keyboard. This will ensure that the keyboard is no longer attached to your callback.''' if self.window: self.window.release_keyboard(self.target) def _on_window_textinput(self, instance, text): return self.dispatch('on_textinput', text) def _on_window_key_down(self, instance, keycode, scancode, text, modifiers): keycode = (keycode, self.keycode_to_string(keycode)) if text == '\x04': Window.trigger_keyboard_height() return return self.dispatch('on_key_down', keycode, text, modifiers) def _on_window_key_up(self, instance, keycode, *largs): keycode = (keycode, self.keycode_to_string(keycode)) return self.dispatch('on_key_up', keycode) def _on_vkeyboard_key_down(self, instance, keycode, text, modifiers): if keycode is None: keycode = text.lower() keycode = (self.string_to_keycode(keycode), keycode) return self.dispatch('on_key_down', keycode, text, modifiers) def _on_vkeyboard_key_up(self, instance, keycode, text, modifiers): if keycode is None: keycode = text keycode = (self.string_to_keycode(keycode), keycode) return self.dispatch('on_key_up', keycode) def _on_vkeyboard_textinput(self, instance, text): return self.dispatch('on_textinput', text) def string_to_keycode(self, value): '''Convert a string to a keycode number according to the :attr:`Keyboard.keycodes`. If the value is not found in the keycodes, it will return -1. ''' return Keyboard.keycodes.get(value, -1) def keycode_to_string(self, value): '''Convert a keycode number to a string according to the :attr:`Keyboard.keycodes`. If the value is not found in the keycodes, it will return ''. ''' keycodes = list(Keyboard.keycodes.values()) if value in keycodes: return list(Keyboard.keycodes.keys())[keycodes.index(value)] return '' class WindowBase(EventDispatcher): '''WindowBase is an abstract window widget for any window implementation. :Parameters: `borderless`: str, one of ('0', '1') Set the window border state. Check the :mod:`~kivy.config` documentation for a more detailed explanation on the values. `fullscreen`: str, one of ('0', '1', 'auto', 'fake') Make the window fullscreen. Check the :mod:`~kivy.config` documentation for a more detailed explanation on the values. `width`: int Width of the window. `height`: int Height of the window. :Events: `on_motion`: etype, motionevent Fired when a new :class:`~kivy.input.motionevent.MotionEvent` is dispatched `on_touch_down`: Fired when a new touch event is initiated. `on_touch_move`: Fired when an existing touch event changes location. `on_touch_up`: Fired when an existing touch event is terminated. `on_draw`: Fired when the :class:`Window` is being drawn. `on_flip`: Fired when the :class:`Window` GL surface is being flipped. `on_rotate`: rotation Fired when the :class:`Window` is being rotated. `on_close`: Fired when the :class:`Window` is closed. `on_request_close`: Fired when the event loop wants to close the window, or if the escape key is pressed and `exit_on_escape` is `True`. If a function bound to this event returns `True`, the window will not be closed. If the the event is triggered because of the keyboard escape key, the keyword argument `source` is dispatched along with a value of `keyboard` to the bound functions. .. versionadded:: 1.9.0 `on_keyboard`: key, scancode, codepoint, modifier Fired when the keyboard is used for input. .. versionchanged:: 1.3.0 The *unicode* parameter has been deprecated in favor of codepoint, and will be removed completely in future versions. `on_key_down`: key, scancode, codepoint Fired when a key pressed. .. versionchanged:: 1.3.0 The *unicode* parameter has been deprecated in favor of codepoint, and will be removed completely in future versions. `on_key_up`: key, scancode, codepoint Fired when a key is released. .. versionchanged:: 1.3.0 The *unicode* parameter has be deprecated in favor of codepoint, and will be removed completely in future versions. `on_dropfile`: str Fired when a file is dropped on the application. ''' __instance = None __initialized = False _fake_fullscreen = False _density = 1 # private properties _size = ListProperty([0, 0]) _modifiers = ListProperty([]) _rotation = NumericProperty(0) _clearcolor = ObjectProperty([0, 0, 0, 1]) children = ListProperty([]) '''List of the children of this window. :attr:`children` is a :class:`~kivy.properties.ListProperty` instance and defaults to an empty list. Use :meth:`add_widget` and :meth:`remove_widget` to manipulate the list of children. Don't manipulate the list directly unless you know what you are doing. ''' parent = ObjectProperty(None, allownone=True) '''Parent of this window. :attr:`parent` is a :class:`~kivy.properties.ObjectProperty` instance and defaults to None. When created, the parent is set to the window itself. You must take care of it if you are doing a recursive check. ''' icon = StringProperty() def _get_modifiers(self): return self._modifiers modifiers = AliasProperty(_get_modifiers, None) '''List of keyboard modifiers currently active. ''' def _get_size(self): r = self._rotation w, h = self._size if self._density != 1: w, h = self._win._get_gl_size() if self.softinput_mode == 'resize': h -= self.keyboard_height if r in (0, 180): return w, h return h, w def _set_size(self, size): if self._size != size: r = self._rotation if r in (0, 180): self._size = size else: self._size = size[1], size[0] self.dispatch('on_resize', *size) return True else: return False size = AliasProperty(_get_size, _set_size, bind=('_size', )) '''Get the rotated size of the window. If :attr:`rotation` is set, then the size will change to reflect the rotation. ''' def _get_clearcolor(self): return self._clearcolor def _set_clearcolor(self, value): if value is not None: if type(value) not in (list, tuple): raise Exception('Clearcolor must be a list or tuple') if len(value) != 4: raise Exception('Clearcolor must contain 4 values') self._clearcolor = value clearcolor = AliasProperty(_get_clearcolor, _set_clearcolor, bind=('_clearcolor', )) '''Color used to clear the window. :: from kivy.core.window import Window # red background color Window.clearcolor = (1, 0, 0, 1) # don't clear background at all Window.clearcolor = None .. versionchanged:: 1.7.2 The clearcolor default value is now: (0, 0, 0, 1). ''' # make some property read-only def _get_width(self): _size = self._size if self._density != 1: _size = self._win._get_gl_size() r = self._rotation if r == 0 or r == 180: return _size[0] return _size[1] width = AliasProperty(_get_width, None, bind=('_rotation', '_size')) '''Rotated window width. :attr:`width` is a read-only :class:`~kivy.properties.AliasProperty`. ''' def _get_height(self): '''Rotated window height''' r = self._rotation _size = self._size if self._density != 1: _size = self._win._get_gl_size() kb = self.keyboard_height if self.softinput_mode == 'resize' else 0 if r == 0 or r == 180: return _size[1] - kb return _size[0] - kb height = AliasProperty(_get_height, None, bind=('_rotation', '_size')) '''Rotated window height. :attr:`height` is a read-only :class:`~kivy.properties.AliasProperty`. ''' def _get_center(self): return self.width / 2., self.height / 2. center = AliasProperty(_get_center, None, bind=('width', 'height')) '''Center of the rotated window. :attr:`center` is a :class:`~kivy.properties.AliasProperty`. ''' def _get_rotation(self): return self._rotation def _set_rotation(self, x): x = int(x % 360) if x == self._rotation: return if x not in (0, 90, 180, 270): raise ValueError('can rotate only 0, 90, 180, 270 degrees') self._rotation = x if self.initialized is False: return self.dispatch('on_resize', *self.size) self.dispatch('on_rotate', x) rotation = AliasProperty(_get_rotation, _set_rotation, bind=('_rotation', )) '''Get/set the window content rotation. Can be one of 0, 90, 180, 270 degrees. ''' softinput_mode = OptionProperty('', options=('', 'pan', 'scale', 'resize')) '''This specifies the behavior of window contents on display of soft keyboard on mobile platform. Can be one of '', 'pan', 'scale', 'resize'. When '' The main window is left as it is allowing the user to use :attr:`keyboard_height` to manage the window contents the way they want. when 'pan' The main window pans moving the bottom part of the window to be always on top of the keyboard. when 'resize' The window is resized and the contents scaled to fit the remaining space. ..versionadded::1.9.0 :attr:`softinput_mode` is a :class:`OptionProperty` defaults to None. ''' _keyboard_changed = BooleanProperty(False) def _upd_kbd_height(self, *kargs): self._keyboard_changed = not self._keyboard_changed def _get_ios_kheight(self): return 0 def _get_android_kheight(self): global android if not android: import android return android.get_keyboard_height() def _get_kheight(self): if platform == 'android': return self._get_android_kheight() if platform == 'ios': return self._get_ios_kheight() return 0 keyboard_height = AliasProperty(_get_kheight, None, bind=('_keyboard_changed',)) '''Rerturns the height of the softkeyboard/IME on mobile platforms. Will return 0 if not on mobile platform or if IME is not active. ..versionadded:: 1.9.0 :attr:`keyboard_height` is a read-only :class:`AliasProperty` defaults to 0. ''' def _set_system_size(self, size): self._size = size def _get_system_size(self): if self.softinput_mode == 'resize': return self._size[0], self._size[1] - self.keyboard_height return self._size system_size = AliasProperty( _get_system_size, _set_system_size, bind=('_size', )) '''Real size of the window ignoring rotation. ''' borderless = BooleanProperty(False) '''When set to True, this property removes the window border/decoration. .. versionadded:: 1.9.0 :attr:`borderless` is a :class:`BooleanProperty`, defaults to False. ''' fullscreen = OptionProperty(False, options=(True, False, 'auto', 'fake')) '''This property sets the fullscreen mode of the window. Available options are: True, False, 'auto', 'fake'. Check the :mod:`~kivy.config` documentation for a more detailed explanation on the values. .. versionadded:: 1.2.0 .. note:: The 'fake' option has been deprecated, use the :attr:`borderless` property instead. ''' mouse_pos = ObjectProperty([0, 0]) '''2d position of the mouse within the window. .. versionadded:: 1.2.0 ''' @property def __self__(self): return self 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') __events__ = ( 'on_draw', 'on_flip', 'on_rotate', 'on_resize', 'on_close', 'on_motion', 'on_touch_down', 'on_touch_move', 'on_touch_up', 'on_mouse_down', 'on_mouse_move', 'on_mouse_up', 'on_keyboard', 'on_key_down', 'on_key_up', 'on_textinput', 'on_dropfile', 'on_request_close', 'on_joy_axis', 'on_joy_hat', 'on_joy_ball', 'on_joy_button_down', "on_joy_button_up") def __new__(cls, **kwargs): if cls.__instance is None: cls.__instance = EventDispatcher.__new__(cls) return cls.__instance def __init__(self, **kwargs): force = kwargs.pop('force', False) # don't init window 2 times, # except if force is specified if WindowBase.__instance is not None and not force: return self.initialized = False self._is_desktop = Config.getboolean('kivy', 'desktop') # 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) # Create a trigger for updating the keyboard height self.trigger_keyboard_height = Clock.create_trigger( self._upd_kbd_height, .5) # set the default window parameter according to the configuration if 'borderless' not in kwargs: kwargs['borderless'] = Config.getboolean('graphics', 'borderless') 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.getdefault('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 self._bind_create_window() self.bind(size=self.trigger_keyboard_height, rotation=self.trigger_keyboard_height) self.bind(softinput_mode=lambda *dt: self.update_viewport(), keyboard_height=lambda *dt: self.update_viewport()) # init privates self._system_keyboard = Keyboard(window=self) self._keyboards = {'system': self._system_keyboard} self._vkeyboard_cls = None self.children = [] self.parent = self # before creating the window import kivy.core.gl # NOQA # configure the window self.create_window() # attach modules + listener event EventLoop.set_window(self) Modules.register_window(self) EventLoop.add_event_listener(self) # manage keyboard(s) self.configure_keyboards() # assign the default context of the widget creation if not hasattr(self, '_context'): self._context = get_current_context() # mark as initialized self.initialized = True def _bind_create_window(self): for prop in ( 'fullscreen', 'borderless', 'position', 'top', 'left', '_size', 'system_size'): self.bind(**{prop: self.trigger_create_window}) def _unbind_create_window(self): for prop in ( 'fullscreen', 'borderless', 'position', 'top', 'left', '_size', 'system_size'): self.unbind(**{prop: self.trigger_create_window}) def toggle_fullscreen(self): '''Toggle between fullscreen and windowed mode. .. deprecated:: 1.9.0 Use :attr:`fullscreen` instead. ''' pass def maximize(self): '''Maximizes the window. This method should be used on desktop platforms only. .. versionadded:: 1.9.0 .. note:: This feature works with the SDL2 window provider only. .. warning:: This code is still experimental, and its API may be subject to change in a future version. ''' Logger.warning('Window: maximize() is not implemented in the current ' 'window provider.') def minimize(self): '''Minimizes the window. This method should be used on desktop platforms only. .. versionadded:: 1.9.0 .. note:: This feature works with the SDL2 window provider only. .. warning:: This code is still experimental, and its API may be subject to change in a future version. ''' Logger.warning('Window: minimize() is not implemented in the current ' 'window provider.') def restore(self): '''Restores the size and position of a maximized or minimized window. This method should be used on desktop platforms only. .. versionadded:: 1.9.0 .. note:: This feature works with the SDL2 window provider only. .. warning:: This code is still experimental, and its API may be subject to change in a future version. ''' Logger.warning('Window: restore() is not implemented in the current ' 'window provider.') def hide(self): '''Hides the window. This method should be used on desktop platforms only. .. versionadded:: 1.9.0 .. note:: This feature works with the SDL2 window provider only. .. warning:: This code is still experimental, and its API may be subject to change in a future version. ''' Logger.warning('Window: hide() is not implemented in the current ' 'window provider.') def show(self): '''Shows the window. This method should be used on desktop platforms only. .. versionadded:: 1.9.0 .. note:: This feature works with the SDL2 window provider only. .. warning:: This code is still experimental, and its API may be subject to change in a future version. ''' Logger.warning('Window: show() is not implemented in the current ' 'window provider.') def close(self): '''Close the window''' pass def create_window(self, *largs): '''Will create the main window and configure it. .. warning:: This method is called automatically at runtime. If you call it, it will recreate a RenderContext and Canvas. This means you'll have a new graphics tree, and the old one will be unusable. This method exist to permit the creation of a new OpenGL context AFTER closing the first one. (Like using runTouchApp() and stopTouchApp()). This method has only been tested in a unittest environment and is not suitable for Applications. Again, don't use this method unless you know exactly what you are doing! ''' # just to be sure, if the trigger is set, and if this method is # manually called, unset the trigger Clock.unschedule(self.create_window) # ensure the window creation will not be called twice if platform in ('android', 'ios'): self._unbind_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. # XXX check how it's working on embed platform. if platform == 'linux' or Window.__class__.__name__ == 'WindowSDL': # on linux, it's safe for just sending a resize. self.dispatch('on_resize', *self.system_size) else: # on other platform, window are recreated, we need to reload. from kivy.graphics.context import get_context get_context().reload() Clock.schedule_once(lambda x: self.canvas.ask_update(), 0) self.dispatch('on_resize', *self.system_size) # ensure the gl viewport is correct self.update_viewport() def on_flip(self): '''Flip between buffers (event)''' self.flip() def flip(self): '''Flip between buffers''' pass def _update_childsize(self, instance, value): self.update_childsize([instance]) def add_widget(self, widget, canvas=None): '''Add a widget to a window''' widget.parent = self self.children.insert(0, widget) canvas = self.canvas.before if canvas == 'before' else \ self.canvas.after if canvas == 'after' else self.canvas canvas.add(widget.canvas) self.update_childsize([widget]) widget.bind( pos_hint=self._update_childsize, size_hint=self._update_childsize, size=self._update_childsize, pos=self._update_childsize) def remove_widget(self, widget): '''Remove a widget from a window ''' if not widget in self.children: return self.children.remove(widget) if widget.canvas in self.canvas.children: self.canvas.remove(widget.canvas) elif widget.canvas in self.canvas.after.children: self.canvas.after.remove(widget.canvas) elif widget.canvas in self.canvas.before.children: self.canvas.before.remove(widget.canvas) widget.parent = None widget.unbind( pos_hint=self._update_childsize, size_hint=self._update_childsize, size=self._update_childsize, pos=self._update_childsize) def clear(self): '''Clear the window with the background color''' # XXX FIXME use late binding from kivy.graphics.opengl import glClearColor, glClear, \ GL_COLOR_BUFFER_BIT, GL_DEPTH_BUFFER_BIT, GL_STENCIL_BUFFER_BIT cc = self._clearcolor if cc is not None: glClearColor(*cc) glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT | GL_STENCIL_BUFFER_BIT) def set_title(self, title): '''Set the window title. .. versionadded:: 1.0.5 ''' self.title = title def set_icon(self, filename): '''Set the icon of the window. .. versionadded:: 1.0.5 ''' self.icon = filename def to_widget(self, x, y, initial=True, relative=False): return (x, y) def to_window(self, x, y, initial=True, relative=False): return (x, y) def _apply_transform(self, m): return m def get_window_matrix(self, x=0, y=0): m = Matrix() m.translate(x, y, 0) return m def get_root_window(self): return self def get_parent_window(self): return self def get_parent_layout(self): return None def on_draw(self): self.clear() self.render_context.draw() def on_motion(self, etype, me): '''Event called when a Motion Event is received. :Parameters: `etype`: str One of 'begin', 'update', 'end' `me`: :class:`~kivy.input.motionevent.MotionEvent` The Motion Event currently dispatched. ''' if me.is_touch: w, h = self.system_size if platform == 'ios': w, h = self.size me.scale_for_screen(w, h, rotation=self._rotation, smode=self.softinput_mode, kheight=self.keyboard_height) if etype == 'begin': self.dispatch('on_touch_down', me) elif etype == 'update': self.dispatch('on_touch_move', me) elif etype == 'end': self.dispatch('on_touch_up', me) FocusBehavior._handle_post_on_touch_up(me) def on_touch_down(self, touch): '''Event called when a touch down event is initiated. .. versionchanged:: 1.9.0 The touch `pos` is now transformed to window coordinates before this method is called. Before, the touch `pos` coordinate would be `(0, 0)` when this method was called. ''' for w in self.children[:]: if w.dispatch('on_touch_down', touch): return True def on_touch_move(self, touch): '''Event called when a touch event moves (changes location). .. versionchanged:: 1.9.0 The touch `pos` is now transformed to window coordinates before this method is called. Before, the touch `pos` coordinate would be `(0, 0)` when this method was called. ''' for w in self.children[:]: if w.dispatch('on_touch_move', touch): return True def on_touch_up(self, touch): '''Event called when a touch event is released (terminated). .. versionchanged:: 1.9.0 The touch `pos` is now transformed to window coordinates before this method is called. Before, the touch `pos` coordinate would be `(0, 0)` when this method was called. ''' for w in self.children[:]: if w.dispatch('on_touch_up', touch): return True def on_resize(self, width, height): '''Event called when the window is resized.''' self.update_viewport() def update_viewport(self): from kivy.graphics.opengl import glViewport from kivy.graphics.transformation import Matrix from math import radians w, h = self.system_size if self._density != 1: w, h = self.size smode = self.softinput_mode kheight = self.keyboard_height w2, h2 = w / 2., h / 2. r = radians(self.rotation) x, y = 0, 0 _h = h if smode: y = kheight if smode == 'scale': _h -= kheight # prepare the viewport glViewport(x, y, w, _h) # do projection matrix projection_mat = Matrix() projection_mat.view_clip(0.0, w, 0.0, h, -1.0, 1.0, 0) self.render_context['projection_mat'] = projection_mat # do modelview matrix modelview_mat = Matrix().translate(w2, h2, 0) modelview_mat = modelview_mat.multiply(Matrix().rotate(r, 0, 0, 1)) w, h = self.size w2, h2 = w / 2., h / 2. modelview_mat = modelview_mat.multiply(Matrix().translate(-w2, -h2, 0)) self.render_context['modelview_mat'] = modelview_mat # redraw canvas self.canvas.ask_update() # and update childs self.update_childsize() def update_childsize(self, childs=None): width, height = self.size if childs is None: childs = self.children for w in childs: shw, shh = w.size_hint if shw and shh: w.size = shw * width, shh * height elif shw: w.width = shw * width elif shh: w.height = shh * height for key, value in w.pos_hint.items(): if key == 'x': w.x = value * width elif key == 'right': w.right = value * width elif key == 'y': w.y = value * height elif key == 'top': w.top = value * height elif key == 'center_x': w.center_x = value * width elif key == 'center_y': w.center_y = value * height def screenshot(self, name='screenshot{:04d}.png'): '''Save the actual displayed image in a file ''' i = 0 path = None if name != 'screenshot{:04d}.png': _ext = name.split('.')[-1] name = ''.join((name[:-(len(_ext) + 1)], '{:04d}.', _ext)) while True: i += 1 path = join(getcwd(), name.format(i)) if not exists(path): break return path def on_rotate(self, rotation): '''Event called when the screen has been rotated. ''' pass def on_close(self, *largs): '''Event called when the window is closed''' Modules.unregister_window(self) EventLoop.remove_event_listener(self) def on_request_close(self, *largs, **kwargs): '''Event called before we close the window. If a bound function returns `True`, the window will not be closed. If the the event is triggered because of the keyboard escape key, the keyword argument `source` is dispatched along with a value of `keyboard` to the bound functions. .. warning:: When the bound function returns True the window will not be closed, so use with care because the user would not be able to close the program, even if the red X is clicked. ''' pass def on_mouse_down(self, x, y, button, modifiers): '''Event called when the mouse is used (pressed/released)''' pass def on_mouse_move(self, x, y, modifiers): '''Event called when the mouse is moved with buttons pressed''' pass def on_mouse_up(self, x, y, button, modifiers): '''Event called when the mouse is moved with buttons pressed''' pass def on_joy_axis(self, stickid, axisid, value): '''Event called when a joystick has a stick or other axis moved .. versionadded:: 1.9.0''' pass def on_joy_hat(self, stickid, hatid, value): '''Event called when a joystick has a hat/dpad moved .. versionadded:: 1.9.0''' pass def on_joy_ball(self, stickid, ballid, value): '''Event called when a joystick has a ball moved .. versionadded:: 1.9.0''' pass def on_joy_button_down(self, stickid, buttonid): '''Event called when a joystick has a button pressed .. versionadded:: 1.9.0''' pass def on_joy_button_up(self, stickid, buttonid): '''Event called when a joystick has a button released .. versionadded:: 1.9.0''' pass def on_keyboard(self, key, scancode=None, codepoint=None, modifier=None, **kwargs): '''Event called when keyboard is used. .. warning:: Some providers may omit `scancode`, `codepoint` and/or `modifier`! ''' if 'unicode' in kwargs: Logger.warning("The use of the unicode parameter is deprecated, " "and will be removed in future versions. Use " "codepoint instead, which has identical " "semantics.") # Quit if user presses ESC or the typical OSX shortcuts CMD+q or CMD+w # TODO If just CMD+w is pressed, only the window should be closed. is_osx = platform == 'darwin' if WindowBase.on_keyboard.exit_on_escape: if key == 27 or all([is_osx, key in [113, 119], modifier == 1024]): if not self.dispatch('on_request_close', source='keyboard'): stopTouchApp() self.close() return True if Config: on_keyboard.exit_on_escape = Config.getboolean('kivy', 'exit_on_escape') def __exit(section, name, value): WindowBase.__dict__['on_keyboard'].exit_on_escape = \ Config.getboolean('kivy', 'exit_on_escape') Config.add_callback(__exit, 'kivy', 'exit_on_escape') def on_key_down(self, key, scancode=None, codepoint=None, modifier=None, **kwargs): '''Event called when a key is down (same arguments as on_keyboard)''' if 'unicode' in kwargs: Logger.warning("The use of the unicode parameter is deprecated, " "and will be removed in future versions. Use " "codepoint instead, which has identical " "semantics.") def on_key_up(self, key, scancode=None, codepoint=None, modifier=None, **kwargs): '''Event called when a key is released (same arguments as on_keyboard) ''' if 'unicode' in kwargs: Logger.warning("The use of the unicode parameter is deprecated, " "and will be removed in future versions. Use " "codepoint instead, which has identical " "semantics.") def on_textinput(self, text): '''Event called whem text: i.e. alpha numeric non control keys or set of keys is entered. As it is not gaurenteed whether we get one character or multiple ones, this event supports handling multiple characters. ..versionadded:: 1.9.0 ''' pass def on_dropfile(self, filename): '''Event called when a file is dropped on the application. .. warning:: This event currently works with sdl2 window provider, on pygame window provider and MacOSX with a patched version of pygame. This event is left in place for further evolution (ios, android etc.) .. versionadded:: 1.2.0 ''' pass @reify def dpi(self): '''Return the DPI of the screen. If the implementation doesn't support any DPI lookup, it will just return 96. .. warning:: This value is not cross-platform. Use :attr:`kivy.base.EventLoop.dpi` instead. ''' return 96. def configure_keyboards(self): # Configure how to provide keyboards (virtual or not) # register system keyboard to listening keys from window sk = self._system_keyboard self.bind( on_key_down=sk._on_window_key_down, on_key_up=sk._on_window_key_up, on_textinput=sk._on_window_textinput) # use the device's real keyboard self.use_syskeyboard = True # use the device's real keyboard self.allow_vkeyboard = False # one single vkeyboard shared between all widgets self.single_vkeyboard = True # the single vkeyboard is always sitting at the same position self.docked_vkeyboard = False # now read the configuration mode = Config.get('kivy', 'keyboard_mode') if mode not in ('', 'system', 'dock', 'multi', 'systemanddock', 'systemandmulti'): Logger.critical('Window: unknown keyboard mode %r' % mode) # adapt mode according to the configuration if mode == 'system': self.use_syskeyboard = True self.allow_vkeyboard = False self.single_vkeyboard = True self.docked_vkeyboard = False elif mode == 'dock': self.use_syskeyboard = False self.allow_vkeyboard = True self.single_vkeyboard = True self.docked_vkeyboard = True elif mode == 'multi': self.use_syskeyboard = False self.allow_vkeyboard = True self.single_vkeyboard = False self.docked_vkeyboard = False elif mode == 'systemanddock': self.use_syskeyboard = True self.allow_vkeyboard = True self.single_vkeyboard = True self.docked_vkeyboard = True elif mode == 'systemandmulti': self.use_syskeyboard = True self.allow_vkeyboard = True self.single_vkeyboard = False self.docked_vkeyboard = False Logger.info( 'Window: virtual keyboard %sallowed, %s, %s' % ( '' if self.allow_vkeyboard else 'not ', 'single mode' if self.single_vkeyboard else 'multiuser mode', 'docked' if self.docked_vkeyboard else 'not docked')) def set_vkeyboard_class(self, cls): '''.. versionadded:: 1.0.8 Set the VKeyboard class to use. If set to None, it will use the :class:`kivy.uix.vkeyboard.VKeyboard`. ''' self._vkeyboard_cls = cls def release_all_keyboards(self): '''.. versionadded:: 1.0.8 This will ensure that no virtual keyboard / system keyboard is requested. All instances will be closed. ''' for key in list(self._keyboards.keys())[:]: keyboard = self._keyboards[key] if keyboard: keyboard.release() def request_keyboard(self, callback, target, input_type='text'): '''.. versionadded:: 1.0.4 Internal widget method to request the keyboard. This method is rarely required by the end-user as it is handled automatically by the :class:`~kivy.uix.textinput.TextInput`. We expose it in case you want to handle the keyboard manually for unique input scenarios. A widget can request the keyboard, indicating a callback to call when the keyboard is released (or taken by another widget). :Parameters: `callback`: func Callback that will be called when the keyboard is closed. This can be because somebody else requested the keyboard or the user closed it. `target`: Widget Attach the keyboard to the specified `target`. This should be the widget that requested the keyboard. Ensure you have a different target attached to each keyboard if you're working in a multi user mode. .. versionadded:: 1.0.8 `input_type`: string Choose the type of soft keyboard to request. Can be one of 'text', 'number', 'url', 'mail', 'datetime', 'tel', 'address'. .. note:: `input_type` is currently only honored on mobile devices. .. versionadded:: 1.8.0 :Return: An instance of :class:`Keyboard` containing the callback, target, and if the configuration allows it, a :class:`~kivy.uix.vkeyboard.VKeyboard` instance attached as a *.widget* property. .. note:: The behavior of this function is heavily influenced by the current `keyboard_mode`. Please see the Config's :ref:`configuration tokens ` section for more information. ''' # release any previous keyboard attached. self.release_keyboard(target) # if we can use virtual vkeyboard, activate it. if self.allow_vkeyboard: keyboard = None # late import global VKeyboard if VKeyboard is None and self._vkeyboard_cls is None: from kivy.uix.vkeyboard import VKeyboard self._vkeyboard_cls = VKeyboard # if the keyboard doesn't exist, create it. key = 'single' if self.single_vkeyboard else target if key not in self._keyboards: vkeyboard = self._vkeyboard_cls() keyboard = Keyboard(widget=vkeyboard, window=self) vkeyboard.bind( on_key_down=keyboard._on_vkeyboard_key_down, on_key_up=keyboard._on_vkeyboard_key_up, on_textinput=keyboard._on_vkeyboard_textinput) self._keyboards[key] = keyboard else: keyboard = self._keyboards[key] # configure vkeyboard keyboard.target = keyboard.widget.target = target keyboard.callback = keyboard.widget.callback = callback # add to the window self.add_widget(keyboard.widget) # only after add, do dock mode keyboard.widget.docked = self.docked_vkeyboard keyboard.widget.setup_mode() else: # system keyboard, just register the callback. keyboard = self._system_keyboard keyboard.callback = callback keyboard.target = target # use system (hardware) keyboard according to flag if self.allow_vkeyboard and self.use_syskeyboard: self.unbind( on_key_down=keyboard._on_window_key_down, on_key_up=keyboard._on_window_key_up, on_textinput=keyboard._on_window_textinput) self.bind( on_key_down=keyboard._on_window_key_down, on_key_up=keyboard._on_window_key_up, on_textinput=keyboard._on_window_textinput) return keyboard def release_keyboard(self, target=None): '''.. versionadded:: 1.0.4 Internal method for the widget to release the real-keyboard. Check :meth:`request_keyboard` to understand how it works. ''' if self.allow_vkeyboard: key = 'single' if self.single_vkeyboard else target if key not in self._keyboards: return keyboard = self._keyboards[key] callback = keyboard.callback if callback: keyboard.callback = None callback() keyboard.target = None self.remove_widget(keyboard.widget) if key != 'single' and key in self._keyboards: del self._keyboards[key] elif self._system_keyboard.callback: # this way will prevent possible recursion. callback = self._system_keyboard.callback self._system_keyboard.callback = None callback() return True #: Instance of a :class:`WindowBase` implementation window_impl = [] if platform == 'linux': window_impl += [('egl_rpi', 'window_egl_rpi', 'WindowEglRpi')] if USE_SDL2: window_impl += [('sdl2', 'window_sdl2', 'WindowSDL')] else: window_impl += [ ('pygame', 'window_pygame', 'WindowPygame')] if platform == 'linux': window_impl += [('x11', 'window_x11', 'WindowX11')] Window = core_select_lib('window', window_impl, True) Kivy-1.9.0/kivy/core/audio/0000775000175000017500000000000012507221737015446 5ustar titotito00000000000000Kivy-1.9.0/kivy/core/audio/audio_pygst.py0000664000175000017500000001007112276430013020336 0ustar titotito00000000000000''' Audio Gstreamer =============== Implementation of Sound with GStreamer ''' try: import gi # NOQA except ImportError: gi_found = False else: raise Exception('Avoiding PyGST, Gi is better.') try: import pygst if not hasattr(pygst, '_gst_already_checked'): pygst.require('0.10') pygst._gst_already_checked = True import gst except: raise from kivy.core.audio import Sound, SoundLoader import os import sys from kivy.logger import Logger # install the gobject iteration from kivy.support import install_gobject_iteration install_gobject_iteration() class SoundPyGst(Sound): @staticmethod def extensions(): return ('wav', 'ogg', 'mp3', ) def __init__(self, **kwargs): self._data = None super(SoundPyGst, self).__init__(**kwargs) def __del__(self): if self._data is not None: self._data.set_state(gst.STATE_NULL) def _on_gst_message(self, bus, message): t = message.type if t == gst.MESSAGE_EOS: self._data.set_state(gst.STATE_NULL) if self.loop: self.play() else: self.stop() elif t == gst.MESSAGE_ERROR: self._data.set_state(gst.STATE_NULL) err, debug = message.parse_error() Logger.error('AudioPyGst: %s' % err) Logger.debug(str(debug)) self.stop() def play(self): if not self._data: return self._data.set_property('volume', self.volume) self._data.set_state(gst.STATE_PLAYING) super(SoundPyGst, self).play() def stop(self): if not self._data: return self._data.set_state(gst.STATE_NULL) super(SoundPyGst, self).stop() def load(self): self.unload() fn = self.filename if fn is None: return slash = '' if sys.platform in ('win32', 'cygwin'): slash = '/' if fn[0] == '/': filepath = 'file://' + slash + fn else: filepath = 'file://' + slash + os.path.join(os.getcwd(), fn) self._data = gst.element_factory_make('playbin2', 'player') fakesink = gst.element_factory_make('fakesink', 'fakesink') self._data.set_property('video-sink', fakesink) bus = self._data.get_bus() bus.add_signal_watch() bus.connect('message', self._on_gst_message) self._data.set_property('uri', filepath) self._data.set_state(gst.STATE_READY) def unload(self): self.stop() self._data = None def seek(self, position): if self._data is None: return self._data.seek_simple(gst.FORMAT_TIME, gst.SEEK_FLAG_SKIP, position * 1000000000.) def get_pos(self): if self._data is not None: if self._data.get_state()[1] == gst.STATE_PLAYING: try: return self._data.query_position( gst.Format(gst.FORMAT_TIME))[0] / 1000000000. except: pass return 0 def on_volume(self, instance, volume): if self._data is not None: self._data.set_property('volume', volume) def _get_length(self): if self._data is not None: if self._data.get_state()[1] != gst.STATE_PLAYING: volume_before = self._data.get_property('volume') self._data.set_property('volume', 0) self._data.set_state(gst.STATE_PLAYING) try: self._data.get_state() return self._data.query_duration(gst.Format( gst.FORMAT_TIME))[0] / 1000000000. finally: self._data.set_state(gst.STATE_NULL) self._data.set_property('volume', volume_before) else: return self._data.query_duration( gst.Format(gst.FORMAT_TIME))[0] / 1000000000. return super(SoundPyGst, self)._get_length() SoundLoader.register(SoundPyGst) Kivy-1.9.0/kivy/core/audio/audio_gi.py0000664000175000017500000001016112276430013017567 0ustar titotito00000000000000''' Audio Gi ======== Implementation of Sound with Gi. Gi is both compatible with Python 2 and 3. ''' from gi.repository import Gst from kivy.core.audio import Sound, SoundLoader from kivy.logger import Logger from kivy.support import install_gobject_iteration import os import sys # initialize the audio/gi. if the older version is used, don't use audio_gi. Gst.init(None) version = Gst.version() if version < (1, 0, 0, 0): raise Exception('Cannot use audio_gi, Gstreamer < 1.0 is not supported.') Logger.info('AudioGi: Using Gstreamer {}'.format( '.'.join(['{}'.format(x) for x in Gst.version()]))) install_gobject_iteration() class SoundGi(Sound): @staticmethod def extensions(): return ('wav', 'ogg', 'mp3', ) def __init__(self, **kwargs): self._data = None super(SoundGi, self).__init__(**kwargs) def __del__(self): if self._data is not None: self._data.set_state(Gst.State.NULL) def _on_gst_message(self, bus, message): t = message.type if t == Gst.MessageType.EOS: self._data.set_state(Gst.State.NULL) if self.loop: self.play() else: self.stop() elif t == Gst.MessageType.ERROR: self._data.set_state(Gst.State.NULL) err, debug = message.parse_error() Logger.error('AudioGi: %s' % err) Logger.debug(str(debug)) self.stop() def play(self): if not self._data: return self._data.props.volume = self.volume self._data.set_state(Gst.State.PLAYING) super(SoundGi, self).play() def stop(self): if not self._data: return self._data.set_state(Gst.State.NULL) super(SoundGi, self).stop() def load(self): self.unload() fn = self.filename if fn is None: return slash = '' if sys.platform in ('win32', 'cygwin'): slash = '/' if fn[0] == '/': uri = 'file://' + slash + fn else: uri = 'file://' + slash + os.path.join(os.getcwd(), fn) self._data = Gst.ElementFactory.make('playbin', '') fakesink = Gst.ElementFactory.make('fakesink', '') self._data.props.video_sink = fakesink bus = self._data.get_bus() bus.add_signal_watch() bus.connect('message', self._on_gst_message) self._data.props.uri = uri self._data.set_state(Gst.State.READY) def unload(self): self.stop() self._data = None def seek(self, position): if self._data is None: return self._data.seek_simple( Gst.Format.TIME, Gst.SeekFlags.SKIP, position * Gst.SECOND) def get_pos(self): if self._data is not None: if self._data.get_state()[1] == Gst.State.PLAYING: try: ret, value = self._data.query_position(Gst.Format.TIME) if ret: return value / float(Gst.SECOND) except: pass return 0 def on_volume(self, instance, volume): if self._data is not None: self._data.set_property('volume', volume) def _get_length(self): if self._data is not None: if self._data.get_state()[1] != Gst.State.PLAYING: volume_before = self._data.get_property('volume') self._data.set_property('volume', 0) self._data.set_state(Gst.State.PLAYING) try: self._data.get_state() ret, value = self._data.query_duration(Gst.Format.TIME) if ret: return value / float(Gst.SECOND) finally: self._data.set_state(Gst.State.NULL) self._data.set_property('volume', volume_before) else: ret, value = self._data.query_duration(Gst.Format.TIME) if ret: return value / float(Gst.SECOND) return super(SoundGi, self)._get_length() SoundLoader.register(SoundGi) Kivy-1.9.0/kivy/core/audio/audio_pygame.py0000664000175000017500000000620012465721151020457 0ustar titotito00000000000000''' AudioPygame: implementation of Sound with Pygame ''' __all__ = ('SoundPygame', ) from kivy.clock import Clock from kivy.utils import platform from kivy.core.audio import Sound, SoundLoader _platform = platform try: if _platform == 'android': try: import android.mixer as mixer except ImportError: # old python-for-android version import android_mixer as mixer else: from pygame import mixer except: raise # init pygame sound mixer.pre_init(44100, -16, 2, 1024) mixer.init() mixer.set_num_channels(32) class SoundPygame(Sound): # XXX we don't set __slots__ here, to automaticly add # a dictionary. We need that to be able to use weakref for # SoundPygame object. Otherwise, it failed with: # TypeError: cannot create weak reference to 'SoundPygame' object # We use our clock in play() method. # __slots__ = ('_data', '_channel') @staticmethod def extensions(): if _platform == 'android': return ('wav', 'ogg', 'mp3', 'm4a') return ('wav', 'ogg') def __init__(self, **kwargs): self._data = None self._channel = None super(SoundPygame, self).__init__(**kwargs) def _check_play(self, dt): if self._channel is None: return False if self._channel.get_busy(): return if self.loop: def do_loop(dt): self.play() Clock.schedule_once(do_loop) else: self.stop() return False def play(self): if not self._data: return self._data.set_volume(self.volume) self._channel = self._data.play() self.start_time = Clock.time() # schedule event to check if the sound is still playing or not Clock.schedule_interval(self._check_play, 0.1) super(SoundPygame, self).play() def stop(self): if not self._data: return self._data.stop() # ensure we don't have anymore the callback Clock.unschedule(self._check_play) self._channel = None super(SoundPygame, self).stop() def load(self): self.unload() if self.filename is None: return self._data = mixer.Sound(self.filename) def unload(self): self.stop() self._data = None def seek(self, position): if not self._data: return if _platform == 'android' and self._channel: self._channel.seek(position) def get_pos(self): if self._data is not None and self._channel: if _platform == 'android': return self._channel.get_pos() return Clock.time() - self.start_time return 0 def on_volume(self, instance, volume): if self._data is not None: self._data.set_volume(volume) def _get_length(self): if _platform == 'android' and self._channel: return self._channel.get_length() if self._data is not None: return self._data.get_length() return super(SoundPygame, self)._get_length() SoundLoader.register(SoundPygame) Kivy-1.9.0/kivy/core/audio/audio_gstplayer.py0000664000175000017500000000511412462275426021220 0ustar titotito00000000000000''' Audio Gstplayer =============== .. versionadded:: 1.8.0 Implementation of a VideoBase with Kivy :class:`~kivy.lib.gstplayer.GstPlayer` This player is the prefered player, using Gstreamer 1.0, working on both Python 2 and 3. ''' from kivy.lib.gstplayer import GstPlayer, get_gst_version from kivy.core.audio import Sound, SoundLoader from kivy.logger import Logger from kivy.compat import PY2 from kivy.clock import Clock from os.path import realpath if PY2: from urllib import pathname2url else: from urllib.request import pathname2url Logger.info('AudioGstplayer: Using Gstreamer {}'.format( '.'.join(map(str, get_gst_version())))) def _on_gstplayer_message(mtype, message): if mtype == 'error': Logger.error('AudioGstplayer: {}'.format(message)) elif mtype == 'warning': Logger.warning('AudioGstplayer: {}'.format(message)) elif mtype == 'info': Logger.info('AudioGstplayer: {}'.format(message)) class SoundGstplayer(Sound): @staticmethod def extensions(): return ('wav', 'ogg', 'mp3', 'm4a') def __init__(self, **kwargs): self.player = None super(SoundGstplayer, self).__init__(**kwargs) def _on_gst_eos_sync(self): Clock.schedule_once(self._on_gst_eos, 0) def _on_gst_eos(self, *dt): if self.loop: self.player.stop() self.player.play() else: self.stop() def load(self): self.unload() uri = self._get_uri() self.player = GstPlayer(uri, None, self._on_gst_eos_sync, _on_gstplayer_message) self.player.load() def play(self): # we need to set the volume everytime, it seems that stopping + playing # the sound reset the volume. self.player.set_volume(self.volume) self.player.play() super(SoundGstplayer, self).play() def stop(self): self.player.stop() super(SoundGstplayer, self).stop() def unload(self): if self.player: self.player.unload() self.player = None def seek(self, position): self.player.seek(position / self.length) def get_pos(self): return self.player.get_position() def _get_length(self): return self.player.get_duration() def on_volume(self, instance, volume): self.player.set_volume(volume) def _get_uri(self): uri = self.filename if not uri: return if not '://' in uri: uri = 'file:' + pathname2url(realpath(uri)) return uri SoundLoader.register(SoundGstplayer) Kivy-1.9.0/kivy/core/audio/audio_ffpyplayer.py0000664000175000017500000001354012462275426021371 0ustar titotito00000000000000''' FFmpeg based audio player ========================= To use, you need to install ffpyplyaer and have a compiled ffmpeg shared library. https://github.com/matham/ffpyplayer The docs there describe how to set this up. But briefly, first you need to compile ffmpeg using the shared flags while disabling the static flags (you'll probably have to set the fPIC flag, e.g. CFLAGS=-fPIC). Here's some instructions: https://trac.ffmpeg.org/wiki/CompilationGuide. For Windows, you can download compiled GPL binaries from http://ffmpeg.zeranoe.com/builds/. Similarly, you should download SDL. Now, you should a ffmpeg and sdl directory. In each, you should have a include, bin, and lib directory, where e.g. for Windows, lib contains the .dll.a files, while bin contains the actual dlls. The include directory holds the headers. The bin directory is only needed if the shared libraries are not already on the path. In the environment define FFMPEG_ROOT and SDL_ROOT, each pointing to the ffmpeg, and SDL directories, respectively. (If you're using SDL2, the include directory will contain a directory called SDL2, which then holds the headers). Once defined, download the ffpyplayer git and run python setup.py build_ext --inplace Finally, before running you need to ensure that ffpyplayer is in python's path. ..Note:: When kivy exits by closing the window while the audio is playing, it appears that the __del__method of SoundFFPy is not called. Because of this the SoundFFPy object is not properly deleted when kivy exits. The consequence is that because MediaPlayer creates internal threads which do not have their daemon flag set, when the main threads exists it'll hang and wait for the other MediaPlayer threads to exit. But since __del__ is not called to delete the MediaPlayer object, those threads will remain alive hanging kivy. What this means is that you have to be sure to delete the MediaPlayer object before kivy exits by setting it to None. ''' __all__ = ('SoundFFPy', ) try: import ffpyplayer from ffpyplayer.player import MediaPlayer from ffpyplayer.tools import set_log_callback, loglevels,\ get_log_callback, formats_in except: raise from kivy.clock import Clock from kivy.logger import Logger from kivy.core.audio import Sound, SoundLoader from kivy.weakmethod import WeakMethod import time Logger.info('SoundFFPy: Using ffpyplayer {}'.format(ffpyplayer.version)) logger_func = {'quiet': Logger.critical, 'panic': Logger.critical, 'fatal': Logger.critical, 'error': Logger.error, 'warning': Logger.warning, 'info': Logger.info, 'verbose': Logger.debug, 'debug': Logger.debug} def _log_callback(message, level): message = message.strip() if message: logger_func[level]('ffpyplayer: {}'.format(message)) class SoundFFPy(Sound): @staticmethod def extensions(): return formats_in def __init__(self, **kwargs): self._ffplayer = None self.quitted = False self._log_callback_set = False self._state = '' self.state = 'stop' self._callback_ref = WeakMethod(self._player_callback) if not get_log_callback(): set_log_callback(_log_callback) self._log_callback_set = True super(SoundFFPy, self).__init__(**kwargs) def __del__(self): self.unload() if self._log_callback_set: set_log_callback(None) def _player_callback(self, selector, value): if self._ffplayer is None: return if selector == 'quit': def close(*args): self.quitted = True self.unload() Clock.schedule_once(close, 0) elif selector == 'eof': Clock.schedule_once(self._do_eos, 0) def load(self): self.unload() ff_opts = {'vn': True, 'sn': True} # only audio self._ffplayer = MediaPlayer(self.source, callback=self._callback_ref, loglevel='info', ff_opts=ff_opts) player = self._ffplayer player.set_volume(self.volume) player.toggle_pause() self._state = 'paused' # wait until loaded or failed, shouldn't take long, but just to make # sure metadata is available. s = time.clock() while ((not player.get_metadata()['duration']) and not self.quitted and time.clock() - s < 10.): time.sleep(0.005) def unload(self): if self._ffplayer: self._ffplayer = None self._state = '' self.state = 'stop' self.quitted = False def play(self): if self._state == 'playing': super(SoundFFPy, self).play() return if not self._ffplayer: self.load() self._ffplayer.toggle_pause() self._state = 'playing' self.state = 'play' super(SoundFFPy, self).play() def stop(self): if self._ffplayer and self._state == 'playing': self._ffplayer.toggle_pause() self._state = 'paused' self.state = 'stop' super(SoundFFPy, self).stop() def seek(self, position): if self._ffplayer is None: return self._ffplayer.seek(position, relative=False) def get_pos(self): if self._ffplayer is not None: return self._ffplayer.get_pts() return 0 def on_volume(self, instance, volume): if self._ffplayer is not None: self._ffplayer.set_volume(volume) def _get_length(self): if self._ffplayer is None: return super(SoundFFPy, self)._get_length() return self._ffplayer.get_metadata()['duration'] def _do_eos(self, *args): if not self.loop: self.stop() else: self.seek(0.) SoundLoader.register(SoundFFPy) Kivy-1.9.0/kivy/core/audio/audio_sdl2.pyx0000664000175000017500000001126512502235633020235 0ustar titotito00000000000000''' SDL2 audio provider =================== This core audio implementation require SDL_mixer library. It might conflict with any other library that are using SDL_mixer, such as ffmpeg-android. Depending the compilation of SDL2 mixer, it can support wav, ogg, mp3, flac. ''' __all__ = ('SoundSDL2', ) include "../../../kivy/lib/sdl2.pxi" from kivy.core.audio import Sound, SoundLoader from kivy.logger import Logger from kivy.clock import Clock cdef int mix_is_init = 0 cdef int mix_flags = 0 # old code from audio_sdl, never used it = unfinished? #cdef void channel_finished_cb(int channel) nogil: # with gil: # print('Channel finished playing.', channel) cdef mix_init(): cdef int audio_rate = 44100 cdef unsigned short audio_format = AUDIO_S16SYS cdef int audio_channels = 2 cdef int audio_buffers = 4096 global mix_is_init # avoid next call if mix_is_init != 0: return if SDL_Init(SDL_INIT_AUDIO) < 0: Logger.critical('AudioSDL2: Unable to initialize SDL') mix_is_init = -1 return 0 mix_flags = Mix_Init(0) if Mix_OpenAudio(audio_rate, audio_format, audio_channels, audio_buffers): Logger.critical('AudioSDL2: Unable to open mixer') mix_is_init = -1 return 0 #Mix_ChannelFinished(channel_finished_cb) mix_is_init = 1 return 1 cdef class MixContainer: cdef Mix_Chunk *chunk cdef int channel def __init__(self): self.chunk = NULL self.channel = -1 def __dealloc__(self): if self.chunk != NULL: if Mix_GetChunk(self.channel) == self.chunk: Mix_HaltChannel(self.channel) Mix_FreeChunk(self.chunk) self.chunk = NULL class SoundSDL2(Sound): @staticmethod def extensions(): mix_init() extensions = ["wav"] if mix_flags & MIX_INIT_FLAC: extensions.append("flac") if mix_flags & MIX_INIT_MOD: extensions.append("mod") if mix_flags & MIX_INIT_MP3: extensions.append("mp3") if mix_flags & MIX_INIT_OGG: extensions.append("ogg") return extensions def __init__(self, **kwargs): self.mc = MixContainer() mix_init() super(SoundSDL2, self).__init__(**kwargs) def _check_play(self, dt): cdef MixContainer mc = self.mc if mc.channel == -1 or mc.chunk == NULL: return False if Mix_Playing(mc.channel): return if self.loop: def do_loop(dt): self.play() Clock.schedule_once(do_loop) else: self.stop() return False def _get_length(self): cdef MixContainer mc = self.mc cdef int freq, channels, mixerbytes, numsamples cdef unsigned short fmt if mc.chunk == NULL: return 0 Mix_QuerySpec(&freq, &fmt, &channels) if fmt == AUDIO_S8 or fmt == AUDIO_U8: mixerbytes = 1 else: mixerbytes = 2 numsamples = mc.chunk.alen / mixerbytes / channels return numsamples / channels def play(self): cdef MixContainer mc = self.mc self.stop() if mc.chunk == NULL: return mc.chunk.volume = int(self.volume * 128) mc.channel = Mix_PlayChannel(-1, mc.chunk, 0) if mc.channel == -1: Logger.warning( 'AudioSDL2: Unable to play %r, no more free channel' % self.filename) return # schedule event to check if the sound is still playing or not Clock.schedule_interval(self._check_play, 0.1) super(SoundSDL2, self).play() def stop(self): cdef MixContainer mc = self.mc if mc.chunk == NULL or mc.channel == -1: return if Mix_GetChunk(mc.channel) == mc.chunk: Mix_HaltChannel(mc.channel) mc.channel = -1 Clock.unschedule(self._check_play) super(SoundSDL2, self).stop() def load(self): cdef MixContainer mc = self.mc self.unload() if self.filename is None: return mc.chunk = Mix_LoadWAV(self.filename) if mc.chunk == NULL: Logger.warning('AudioSDL2: Unable to load %r' % self.filename) else: mc.chunk.volume = int(self.volume * 128) def unload(self): cdef MixContainer mc = self.mc self.stop() if mc.chunk != NULL: Mix_FreeChunk(mc.chunk) mc.chunk = NULL def on_volume(self, instance, volume): cdef MixContainer mc = self.mc if mc.chunk != NULL: mc.chunk.volume = int(volume * 128) SoundLoader.register(SoundSDL2) Kivy-1.9.0/kivy/core/audio/__init__.py0000664000175000017500000001305712502235633017560 0ustar titotito00000000000000''' Audio ===== Load an audio sound and play it with:: from kivy.core.audio import SoundLoader sound = SoundLoader.load('mytest.wav') if sound: print("Sound found at %s" % sound.source) print("Sound is %.3f seconds" % sound.length) sound.play() You should not use the Sound class directly. The class returned by **SoundLoader.load** will be the best sound provider for that particular file type, so it might return different Sound classes depending the file type. .. versionchanged:: 1.8.0 There are now 2 distinct Gstreamer implementations: one using Gi/Gst working for both Python 2+3 with Gstreamer 1.0, and one using PyGST working only for Python 2 + Gstreamer 0.10. If you have issue with GStreamer, have a look at :ref:`gstreamer-compatibility` .. note:: The core audio library does not support recording audio. If you require this functionality, please refer to the `audiostream `_ extension. ''' __all__ = ('Sound', 'SoundLoader') from kivy.logger import Logger from kivy.event import EventDispatcher from kivy.core import core_register_libs from kivy.compat import PY2 from kivy.resources import resource_find from kivy.properties import StringProperty, NumericProperty, OptionProperty, \ AliasProperty, BooleanProperty from kivy.setupconfig import USE_SDL2 class SoundLoader: '''Load a sound, using the best loader for the given file type. ''' _classes = [] @staticmethod def register(classobj): '''Register a new class to load the sound.''' Logger.debug('Audio: register %s' % classobj.__name__) SoundLoader._classes.append(classobj) @staticmethod def load(filename): '''Load a sound, and return a Sound() instance.''' rfn = resource_find(filename) if rfn is not None: filename = rfn ext = filename.split('.')[-1].lower() if '?' in ext: ext = ext.split('?')[0] for classobj in SoundLoader._classes: if ext in classobj.extensions(): return classobj(source=filename) Logger.warning('Audio: Unable to find a loader for <%s>' % filename) return None class Sound(EventDispatcher): '''Represents a sound to play. This class is abstract, and cannot be used directly. Use SoundLoader to load a sound. :Events: `on_play` : None Fired when the sound is played. `on_stop` : None Fired when the sound is stopped. ''' source = StringProperty(None) '''Filename / source of your audio file. .. versionadded:: 1.3.0 :attr:`source` is a :class:`~kivy.properties.StringProperty` that defaults to None and is read-only. Use the :meth:`SoundLoader.load` for loading audio. ''' volume = NumericProperty(1.) '''Volume, in the range 0-1. 1 means full volume, 0 means mute. .. versionadded:: 1.3.0 :attr:`volume` is a :class:`~kivy.properties.NumericProperty` and defaults to 1. ''' state = OptionProperty('stop', options=('stop', 'play')) '''State of the sound, one of 'stop' or 'play'. .. versionadded:: 1.3.0 :attr:`state` is a read-only :class:`~kivy.properties.OptionProperty`.''' loop = BooleanProperty(False) '''Set to True if the sound should automatically loop when it finishes. .. versionadded:: 1.8.0 :attr:`loop` is a :class:`~kivy.properties.BooleanProperty` and defaults to False.''' # # deprecated # def _get_status(self): return self.state status = AliasProperty(_get_status, None, bind=('state', )) ''' .. deprecated:: 1.3.0 Use :attr:`state` instead. ''' def _get_filename(self): return self.source filename = AliasProperty(_get_filename, None, bind=('source', )) ''' .. deprecated:: 1.3.0 Use :attr:`source` instead. ''' __events__ = ('on_play', 'on_stop') def on_source(self, instance, filename): self.unload() if filename is None: return self.load() def get_pos(self): ''' Returns the current position of the audio file. Returns 0 if not playing. .. versionadded:: 1.4.1 ''' return 0 def _get_length(self): return 0 length = property(lambda self: self._get_length(), doc='Get length of the sound (in seconds).') def load(self): '''Load the file into memory.''' pass def unload(self): '''Unload the file from memory.''' pass def play(self): '''Play the file.''' self.state = 'play' self.dispatch('on_play') def stop(self): '''Stop playback.''' self.state = 'stop' self.dispatch('on_stop') def seek(self, position): '''Go to the (in seconds).''' pass def on_play(self): pass def on_stop(self): pass # Little trick here, don't activate gstreamer on window # seem to have lot of crackle or something... audio_libs = [] # from now on, prefer our gstplayer instead of gi/pygst. try: from kivy.lib.gstplayer import GstPlayer # NOQA audio_libs += [('gstplayer', 'audio_gstplayer')] except ImportError: #audio_libs += [('gi', 'audio_gi')] if PY2: audio_libs += [('pygst', 'audio_pygst')] audio_libs += [('ffpyplayer', 'audio_ffpyplayer')] if USE_SDL2: audio_libs += [('sdl2', 'audio_sdl2')] else: audio_libs += [('pygame', 'audio_pygame')] core_register_libs('audio', audio_libs) Kivy-1.9.0/kivy/core/image/0000775000175000017500000000000012507221737015427 5ustar titotito00000000000000Kivy-1.9.0/kivy/core/image/img_gif.py0000664000175000017500000004643512465721151017414 0ustar titotito00000000000000#-*- coding: utf-8 -*- # # this program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by # the Free Software Foundation; either version 2 of the License, or # (at your option) any later version. # # this program is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # if not, write to the Free Software # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA # # The Graphics Interchange Format(c) is the Copyright property of # CompuServe Incorporated. GIF(sm) is a Service Mark property of # CompuServe Incorporated. # # The unisys/lzw patent has expired, yes. If anyone puts another patent # over this code, you must *burn* this file. '''pygif: gif implementation in python http://www.java2s.com/Open-Source/Python/Network/\ emesene/emesene-1.6.2/pygif/pygif.py.htm''' #TODO issues to fix #optimize for speed #partially done# a lot of room for improvement import struct from array import array KNOWN_FORMATS = ('GIF87a', 'GIF89a') from kivy.compat import PY2 from kivy.logger import Logger from kivy.core.image import ImageLoaderBase, ImageData, ImageLoader Debug = False class ImageLoaderGIF(ImageLoaderBase): '''Image loader for gif''' @staticmethod def extensions(): '''Return accepted extension for this loader''' return ('gif', ) def load(self, filename): try: try: im = GifDecoder(open(filename, 'rb').read()) except UnicodeEncodeError: if PY2: im = GifDecoder(open(filename.encode('utf8'), 'rb').read()) except: Logger.warning('Image: Unable to load Image <%s>' % filename) raise if Debug: print(im.print_info()) img_data = [] ls_width = im.ls_width ls_height = im.ls_height im_images = im.images im_palette = im.palette pixel_map = array('B', [0] * (ls_width * ls_height * 4)) for img in im_images: palette = img.palette if img.local_color_table_flag\ else im_palette have_transparent_color = img.has_transparent_color transparent_color = img.transparent_color #draw_method_restore_previous = 1 \ # if img.draw_method == 'restore previous' else 0 draw_method_replace = 1 \ if ((img.draw_method == 'replace') or (img.draw_method == 'restore background')) else 0 pixels = img.pixels img_height = img.height img_width = img.width left = img.left top = img.top if img_height > ls_height or img_width > ls_width or\ top > ls_height or left > ls_width: Logger.warning('Image_GIF: decoding error on frame <%s>' % len(img_data)) img_height = ls_height img_width = ls_width left = top = 0 #reverse top to bottom and left to right tmp_top = (ls_height - (img_height + top)) img_width_plus_left = (img_width + left) ls_width_multiply_4 = ls_width * 4 left_multiply_4 = left * 4 img_data_append = img_data.append while img_height > 0: i = left img_height -= 1 x = (img_height * img_width) - left rgba_pos = (tmp_top * ls_width_multiply_4) + (left_multiply_4) tmp_top += 1 while i < img_width_plus_left: #this should now display corrupted gif's #instead of crashing on gif's not decoded properly try: (r, g, b) = palette[pixels[x + i]] except: rgba_pos += 4 i += 1 continue # when not magic pink if (r, g, b) != (255, 0, 255): if have_transparent_color: if transparent_color == pixels[x + i]: if draw_method_replace: #transparent pixel draw method replace pixel_map[rgba_pos + 3] = 0 rgba_pos += 4 i += 1 continue #transparent pixel draw method combine rgba_pos += 4 i += 1 continue # this pixel isn't transparent #doesn't have transparent color (pixel_map[rgba_pos], pixel_map[rgba_pos + 1], pixel_map[rgba_pos + 2]) = (r, g, b) pixel_map[rgba_pos + 3] = 255 # if magic pink move to next pixel rgba_pos += 4 i += 1 img_data_append(ImageData(ls_width, ls_height, 'rgba', pixel_map.tostring(), flip_vertical=False)) if draw_method_replace: pixel_map = array('B', [0] * (ls_width * ls_height * 4)) self.filename = filename return img_data class Gif(object): '''Base class to decoder''' # struct format strings #17,18: FMT_HEADER = '<6sHHBBB' #20: FMT_IMGDESC = ' aode size <%d>' % (code, codesize)) string_table[code] = string_table[0] output_append(ord(string_table[code])) old = string_table[code] bitlen = len(bits) while self.bitpointer < bitlen: # read next code code = self_bits_to_int(pop(codesize, bits)) # special code? if code == clearcode: index = clear() codesize = initial_codesize + 1 code = self_bits_to_int(pop(codesize, bits)) if code in string_table: output_append(ord(string_table[code])) else: Logger.warning('Image_GIF: decoding error on code ' '<%d> aode size <%d>' % (code, codesize)) string_table[code] = string_table[0] output_append(ord(string_table[code])) old = string_table[code] continue elif code == end_of_info: break # code in stringtable? if code in string_table: c = string_table[code] string_table[index] = ''.join((old, c[0])) else: c = ''.join((old, old[0])) string_table[code] = c index += 1 old = c output_extend(list(map(ord, c))) if index == 2 ** codesize: codesize += 1 if codesize == 13: codesize = 12 if self.debug_enabled: print('Output stream len: %d' % len(output)) return output def get_bits(flags, reverse=False, bits=8): '''return a list with $bits items, one for each enabled bit''' mybits = (1, 2, 4, 8, 16, 32, 64, 128, 256, 512, 1024, 2048)[:bits] rev_num = 1 if reverse: rev_num = -1 ret = array('B') ret_append = ret.append for bit in mybits[::rev_num]: ret_append(flags & bit != 0) return ret def pack_bits(bits): '''convert a bit (bool or int) tuple into a int''' packed = 0 level = 0 for bit in bits: if bit: packed += 2 ** level level += 1 return packed # register ImageLoader.register(ImageLoaderGIF) Kivy-1.9.0/kivy/core/image/img_pil.py0000664000175000017500000000671712462275426017440 0ustar titotito00000000000000''' PIL: PIL image loader ''' __all__ = ('ImageLoaderPIL', ) try: from PIL import Image as PILImage except: import Image as PILImage from kivy.logger import Logger from kivy.core.image import ImageLoaderBase, ImageData, ImageLoader class ImageLoaderPIL(ImageLoaderBase): '''Image loader based on the PIL library. .. versionadded:: 1.0.8 Support for GIF animation added. Gif animation has a lot of issues(transparency/color depths... etc). In order to keep it simple, what is implimented here is what is natively supported by the PIL library. As a general rule, try to use gifs that have no transparency. Gif's with transparency will work but be prepared for some artifacts until transparency support is improved. ''' @staticmethod def can_save(): return True @staticmethod def can_load_memory(): return True @staticmethod def extensions(): '''Return accepted extensions for this loader''' # See http://www.pythonware.com/library/pil/handbook/index.htm return ('bmp', 'bufr', 'cur', 'dcx', 'fits', 'fl', 'fpx', 'gbr', 'gd', 'gif', 'grib', 'hdf5', 'ico', 'im', 'imt', 'iptc', 'jpeg', 'jpg', 'jpe', 'mcidas', 'mic', 'mpeg', 'msp', 'pcd', 'pcx', 'pixar', 'png', 'ppm', 'psd', 'sgi', 'spider', 'tga', 'tiff', 'wal', 'wmf', 'xbm', 'xpm', 'xv') def _img_correct(self, _img_tmp): '''Convert image to the correct format and orientation. ''' # image loader work only with rgb/rgba image if _img_tmp.mode.lower() not in ('rgb', 'rgba'): try: imc = _img_tmp.convert('RGBA') except: Logger.warning( 'Image: Unable to convert image to rgba (was %s)' % (_img_tmp.mode.lower())) raise _img_tmp = imc return _img_tmp def _img_read(self, im): '''Read images from an animated file. ''' im.seek(0) # Read all images inside try: img_ol = None while True: img_tmp = im img_tmp = self._img_correct(img_tmp) if img_ol and (hasattr(im, 'dispose') and not im.dispose): # paste new frame over old so as to handle # transparency properly img_ol.paste(img_tmp, (0, 0), img_tmp) img_tmp = img_ol img_ol = img_tmp yield ImageData(img_tmp.size[0], img_tmp.size[1], img_tmp.mode.lower(), img_tmp.tostring()) im.seek(im.tell() + 1) except EOFError: pass def load(self, filename): try: im = PILImage.open(filename) except: Logger.warning('Image: Unable to load image <%s>' % filename) raise # update internals if not self._inline: self.filename = filename # returns an array of type ImageData len 1 if not a sequence image return list(self._img_read(im)) @staticmethod def save(filename, width, height, fmt, pixels, flipped=False): image = PILImage.fromstring(fmt.upper(), (width, height), pixels) if flipped: image = image.transpose(PILImage.FLIP_TOP_BOTTOM) image.save(filename) return True # register ImageLoader.register(ImageLoaderPIL) Kivy-1.9.0/kivy/core/image/_img_sdl2.pyx0000664000175000017500000000675112475370650020044 0ustar titotito00000000000000include '../../lib/sdl2.pxi' from kivy.logger import Logger from libc.string cimport memset from libc.stdlib cimport malloc cdef int _is_init = 0 def init(): global _is_init if _is_init: return cdef int ret for flags in (IMG_INIT_JPG, IMG_INIT_PNG, IMG_INIT_TIF, IMG_INIT_WEBP): ret = IMG_Init(flags) if ret & flags != flags: # FIXME replace flags by a good string Logger.error( 'ImageSDL2: Failed to init required {} support'.format(flags)) Logger.error('ImageSDL2: {}'.format(IMG_GetError())) _is_init = 1 def save(filename, w, h, fmt, pixels, flipped): # this only saves in png for now. cdef bytes c_filename = filename.encode('utf-8') cdef int pitch pitch = w * 4 cdef char *c_pixels = pixels if flipped: Logger.warn( 'ImageSDL2: saving flipped textures not supported; image will be flipped') cdef SDL_Surface *image = SDL_CreateRGBSurfaceFrom(c_pixels, w, h, 32, pitch, 0x00000000ff, 0x0000ff00, 0x00ff0000, 0xff000000) IMG_SavePNG(image, c_filename) SDL_FreeSurface(image) cdef load_from_surface(SDL_Surface *image): cdef SDL_Surface *image2 = NULL cdef SDL_Surface *fimage = NULL cdef SDL_PixelFormat pf cdef bytes pixels try: if image == NULL: return None fmt = '' if image.format.BytesPerPixel == 3: fmt = 'rgb' elif image.format.BytesPerPixel == 4: fmt = 'rgba' # FIXME the format might be 3 or 4, but it doesn't mean it's rgb/rgba. # It could be argb, bgra etc. it needs to be detected correctly. I guess # we could even let the original pass, bgra / argb support exists in # some opengl card. if fmt not in ('rgb', 'rgba'): if fmt == 'rgb': pf.format = SDL_PIXELFORMAT_BGR888 fmt = 'rgb' else: pf.format = SDL_PIXELFORMAT_ABGR8888 fmt = 'rgba' image2 = SDL_ConvertSurfaceFormat(image, pf.format, 0) if image2 == NULL: return fimage = image2 else: if (image.format.Rshift > image.format.Bshift): memset(&pf, 0, sizeof(pf)) pf.BitsPerPixel = 32 pf.Rmask = 0x000000FF pf.Gmask = 0x0000FF00 pf.Bmask = 0x00FF0000 pf.Amask = 0xFF000000 image2 = SDL_ConvertSurface(image, &pf, 0) fimage = image2 else: fimage = image pixels = (fimage.pixels)[:fimage.pitch * fimage.h] return (fimage.w, fimage.h, fmt, pixels, fimage.pitch) finally: if image2: SDL_FreeSurface(image2) def load_from_filename(filename): cdef bytes c_filename = filename.encode('utf-8') cdef SDL_Surface *image = IMG_Load(c_filename) if image == NULL: return try: return load_from_surface(image) finally: if image: SDL_FreeSurface(image) def load_from_memory(bytes data): cdef SDL_RWops *rw = NULL cdef SDL_Surface *image = NULL cdef char *c_data = data rw = SDL_RWFromMem(c_data, len(data)) if rw == NULL: return image = IMG_Load_RW(rw, 0) if image == NULL: return try: return load_from_surface(image) finally: if image: SDL_FreeSurface(image) if rw: SDL_FreeRW(rw) Kivy-1.9.0/kivy/core/image/img_imageio.pyx0000664000175000017500000003022512462275426020445 0ustar titotito00000000000000''' ImageIO OSX framework ===================== Image loader implementation based on CoreGraphics OSX framework. .. todo:: clean all unused definitions handle all errors cases add documentation ''' __all__ = ('ImageLoaderImageIO', ) from kivy.logger import Logger from kivy.core.image import ImageLoaderBase, ImageData, ImageLoader from array import array from libcpp cimport bool from libc.stdlib cimport malloc, free from libc.string cimport memcpy ctypedef unsigned long size_t ctypedef signed long CFIndex cdef unsigned int kCFStringEncodingUTF8 = 0x08000100 cdef extern from "stdlib.h" nogil: void* calloc(size_t, size_t) cdef extern from "CoreGraphics/CGDataProvider.h" nogil: ctypedef void *CFDataRef unsigned char *CFDataGetBytePtr(CFDataRef) ctypedef struct CGPoint: float x float y ctypedef struct CGSize: float width float height ctypedef struct CGRect: CGPoint origin CGSize size CGRect CGRectMake(float, float, float, float) cdef extern from "CoreFoundation/CFBase.h" nogil: ctypedef void *CFAllocatorRef ctypedef void *CFStringRef ctypedef void *CFURLRef ctypedef void *CFTypeRef CFStringRef CFStringCreateWithCString(CFAllocatorRef alloc, char *cStr, int encoding) void CFRelease(CFTypeRef cf) cdef extern from "CoreFoundation/CFURL.h" nogil: ctypedef void *CFURLRef ctypedef int CFURLPathStyle int kCFURLPOSIXPathStyle CFURLRef CFURLCreateFromFileSystemRepresentation( CFAllocatorRef, unsigned char *, CFIndex, bool) CFURLRef CFURLCreateWithFileSystemPath(CFAllocatorRef allocator, CFStringRef filePath, CFURLPathStyle pathStyle, int isDirectory) cdef extern from "CoreFoundation/CFDictionary.h": ctypedef void *CFDictionaryRef cdef extern from "CoreFoundation/CoreFoundation.h" nogil: CFDataRef CFDataCreateWithBytesNoCopy( CFAllocatorRef, char *, int length, CFAllocatorRef) cdef extern from "CoreGraphics/CGImage.h" nogil: ctypedef void *CGImageRef void CGImageRelease(CGImageRef image) size_t CGImageGetWidth(CGImageRef) size_t CGImageGetHeight(CGImageRef) int kCGImageAlphaNoneSkipLast int kCGImageAlphaNoneSkipFirst int kCGImageAlphaFirst int kCGImageAlphaLast int kCGImageAlphaPremultipliedLast int kCGImageAlphaPremultipliedFirst int kCGBitmapByteOrder32Host cdef extern from "CoreGraphics/CGColorSpace.h" nogil: ctypedef void *CGColorSpaceRef CGColorSpaceRef CGColorSpaceCreateDeviceRGB() void CGColorSpaceRelease(CGColorSpaceRef cs) cdef extern from "CoreGraphics/CGAffineTransform.h" nogil: ctypedef void *CGAffineTransform CGAffineTransform CGAffineTransformMake(float a, float b, float c, float d, float tx, float ty) cdef extern from "CoreGraphics/CGContext.h" nogil: ctypedef void *CGContextRef void CGContextRelease(CGContextRef c) void CGContextDrawImage(CGContextRef, CGRect, CGImageRef) int kCGBlendModeCopy int kCGBlendModeNormal void CGContextSetBlendMode(CGContextRef, int) void CGContextConcatCTM(CGContextRef fc, CGAffineTransform matrix) cdef extern from "CoreGraphics/CGBitmapContext.h" nogil: CGImageRef CGBitmapContextCreateImage(CGColorSpaceRef) CGContextRef CGBitmapContextCreate( void *data, size_t width, size_t height, size_t bitsPerComponent, size_t bytesPerRow, CGColorSpaceRef colorspace, unsigned int bitmapInfo) cdef extern from "ImageIO/CGImageSource.h" nogil: ctypedef void *CGImageSourceRef CGImageSourceRef CGImageSourceCreateWithURL( CFURLRef, CFDictionaryRef) CGImageRef CGImageSourceCreateImageAtIndex( CGImageSourceRef, size_t, CFDictionaryRef) CGImageRef CGImageSourceCreateWithData( CFDataRef data, CFDictionaryRef options) cdef extern from "ImageIO/CGImageDestination.h" nogil: ctypedef void *CGImageDestinationRef CGImageDestinationRef CGImageDestinationCreateWithURL( CFURLRef, CFStringRef, size_t, CFDictionaryRef) void CGImageDestinationAddImage(CGImageDestinationRef idst, CGImageRef image, CFDictionaryRef properties) int CGImageDestinationFinalize(CGImageDestinationRef idst) cdef extern from "Accelerate/Accelerate.h" nogil: ctypedef struct vImage_Buffer: void *data int width int height size_t rowBytes int vImagePermuteChannels_ARGB8888( vImage_Buffer *src, vImage_Buffer *dst, unsigned char *permuteMap, int flags) def load_image_data(bytes _url, bytes _data=None): cdef size_t width, height cdef char *r_data = NULL cdef size_t datalen = 0 cdef char *c_url = NULL cdef char *c_data = NULL if _url: c_url = _url datalen = len(_url) if _data: c_data = _data datalen = len(_data) c_load_image_data(c_url, c_data, datalen, &width, &height, &r_data) if r_data == NULL: raise ValueError("No image to load at {}".format(_url)) py_data = r_data[:width * height * 4] free(r_data) return (width, height, 'rgba', py_data) cdef void c_load_image_data(char *_url, char *_data, size_t datalen, size_t *width, size_t *height, char **r_data) nogil: # load an image from the _url with CoreGraphics, and output an RGBA string. cdef CFURLRef url = NULL cdef CGImageSourceRef myImageSourceRef cdef CFDataRef dataref = NULL width[0] = height[0] = 0 r_data[0] = NULL if _data != NULL: dataref = CFDataCreateWithBytesNoCopy(NULL, _data, datalen, NULL) myImageSourceRef = CGImageSourceCreateWithData(dataref, NULL) if not myImageSourceRef: CFRelease(dataref) return else: url = CFURLCreateFromFileSystemRepresentation(NULL, _url, datalen, 0) myImageSourceRef = CGImageSourceCreateWithURL(url, NULL) if not myImageSourceRef: CFRelease(url) return cdef CGImageRef myImageRef = CGImageSourceCreateImageAtIndex(myImageSourceRef, 0, NULL) width[0] = CGImageGetWidth(myImageRef) height[0] = CGImageGetHeight(myImageRef) if myImageRef == NULL: CFRelease(myImageSourceRef) return cdef CGRect rect = CGRectMake(0, 0, width[0], height[0]) cdef CGColorSpaceRef space = CGColorSpaceCreateDeviceRGB() cdef vImage_Buffer src cdef vImage_Buffer dest dest.height = src.height = height[0] dest.width = src.width = width[0] dest.rowBytes = src.rowBytes = width[0] * 4 src.data = calloc(width[0] * 4, height[0]) dest.data = r_data[0] = calloc(width[0] * 4, height[0]) # endianness: kCGBitmapByteOrder32Little = (2 << 12) # (2 << 12) | kCGImageAlphaPremultipliedLast) cdef CGContextRef myBitmapContext = CGBitmapContextCreate( src.data, width[0], height[0], 8, width[0] * 4, space, kCGBitmapByteOrder32Host | kCGImageAlphaNoneSkipFirst) CGContextSetBlendMode(myBitmapContext, kCGBlendModeCopy) CGContextDrawImage(myBitmapContext, rect, myImageRef) # convert to RGBA using Accelerate framework cdef unsigned char *pmap = [2, 1, 0, 3] vImagePermuteChannels_ARGB8888(&src, &dest, pmap, 0) # release everything CGImageRelease(myImageSourceRef) CFRelease(myImageRef) CGContextRelease(myBitmapContext) CGColorSpaceRelease(space) free(src.data) #free(dest.data) # this part is freed by the caller. def save_image_rgba(filename, width, height, data, flipped): # compatibility, could be removed i guess save_image(filename, width, height, 'rgba', data, flipped) def save_image(filenm, width, height, fmt, data, flipped): # save a RGBA string into filename using CoreGraphics # FIXME only png output are accepted. # the day we want to support another output format, we need to adapt the # ctype variable: "public.png" is not a name, but a domain that represent # the type of the output file. So we need to map the extension of the # filename into a CoreGraphics image domain type. fileformat = 'public.png' cdef bytes filename = filenm.encode('utf-8') if filename.endswith('.png'): fileformat = 'public.png' if filename.endswith('.jpg') or filename.endswith('.jpeg'): fileformat = 'public.jpeg' cdef char *source = NULL if type(data) is array: data = data.tostring() bsource = data[:len(data)] source = bsource cdef int fmt_length = 3 if fmt == 'rgba': fmt_length = 4 cdef char *pixels = malloc(int(width * height * fmt_length)) memcpy(pixels, source, int(width * height * fmt_length)) cdef CGColorSpaceRef colorSpace = CGColorSpaceCreateDeviceRGB() cdef CGContextRef bitmapContext = CGBitmapContextCreate( pixels, width, height, 8, # bitsPerComponent fmt_length * width, # bytesPerRow colorSpace, kCGImageAlphaNoneSkipLast) fileformat = fileformat.encode('utf-8') cdef CGImageRef cgImage = CGBitmapContextCreateImage(bitmapContext) cdef char *cfilename = filename cdef CFStringRef sfilename = CFStringCreateWithCString(NULL, cfilename, kCFStringEncodingUTF8) cdef CFURLRef url = CFURLCreateWithFileSystemPath(NULL, sfilename, kCFURLPOSIXPathStyle, 0) cdef CFStringRef ctype = CFStringCreateWithCString(NULL, fileformat, kCFStringEncodingUTF8) cdef CGImageDestinationRef dest = CGImageDestinationCreateWithURL(url, ctype, 1, NULL) # copy the image into a transformed context cdef CGContextRef flippedContext cdef CGImageRef newImageRef if flipped: flippedContext = CGBitmapContextCreate( NULL, width, height, 8, # bitsPerComponent fmt_length * width, # bytesPerRow colorSpace, kCGImageAlphaNoneSkipLast) CGContextConcatCTM(flippedContext, CGAffineTransformMake(1.0, 0.0, 0.0, -1.0, 0.0, height)) CGContextDrawImage(flippedContext, CGRectMake(0, 0, width, height), cgImage) newImageRef = CGBitmapContextCreateImage(flippedContext) CGImageDestinationAddImage(dest, newImageRef, NULL) CGImageDestinationFinalize(dest) CFRelease(newImageRef) CFRelease(flippedContext) else: CGImageDestinationAddImage(dest, cgImage, NULL) CGImageDestinationFinalize(dest) #Release everything CFRelease(cgImage) CFRelease(bitmapContext) CFRelease(colorSpace) free(pixels) class ImageLoaderImageIO(ImageLoaderBase): '''Image loader based on ImageIO MacOSX Framework ''' @staticmethod def extensions(): # FIXME check which one are available on osx return ('bmp', 'bufr', 'cur', 'dcx', 'fits', 'fl', 'fpx', 'gbr', 'gd', 'grib', 'hdf5', 'ico', 'im', 'imt', 'iptc', 'jpeg', 'jpg', 'jpe', 'mcidas', 'mic', 'mpeg', 'msp', 'pcd', 'pcx', 'pixar', 'png', 'ppm', 'psd', 'sgi', 'spider', 'tga', 'tiff', 'wal', 'wmf', 'xbm', 'xpm', 'xv', 'icns') def load(self, filename): # FIXME: if the filename is unicode, the loader is failing. if self._inline: data = filename.read() ret = load_image_data(None, data) else: ret = load_image_data(filename.encode('utf-8')) if ret is None: Logger.warning('Image: Unable to load image <%s>' % filename) raise Exception('Unable to load image') w, h, imgtype, data = ret return [ImageData(w, h, imgtype, data, source=filename)] @staticmethod def can_save(): return True @staticmethod def can_load_memory(): return True @staticmethod def save(filename, width, height, fmt, pixels, flipped=False): save_image(filename, width, height, fmt, pixels, flipped) return True # register ImageLoader.register(ImageLoaderImageIO) Kivy-1.9.0/kivy/core/image/img_sdl2.py0000664000175000017500000000304312475365302017502 0ustar titotito00000000000000''' SDL2 image loader ================= ''' __all__ = ('ImageLoaderSDL2', ) from kivy.compat import PY2 from kivy.logger import Logger from kivy.core.image import ImageLoaderBase, ImageData, ImageLoader from kivy.core.image import _img_sdl2 class ImageLoaderSDL2(ImageLoaderBase): '''Image loader based on the PIL library''' def _ensure_ext(self): _img_sdl2.init() @staticmethod def extensions(): '''Return accepted extensions for this loader''' return ('bmp', 'jpg', 'jpeg', 'lbm', 'pcx', 'png', 'pnm', 'tga', 'tiff', 'webp', 'xcf', 'xpm', 'xv') @staticmethod def can_save(): return True @staticmethod def can_load_memory(): return True def load(self, filename): if self._inline: data = filename.read() info = _img_sdl2.load_from_memory(data) else: info = _img_sdl2.load_from_filename(filename) if not info: Logger.warning('Image: Unable to load image <%s>' % filename) raise Exception('SDL2: Unable to load image') w, h, fmt, pixels, rowlength = info # update internals if not self._inline: self.filename = filename return [ImageData( w, h, fmt, pixels, source=filename, rowlength=rowlength)] @staticmethod def save(filename, width, height, fmt, pixels, flipped): _img_sdl2.save(filename, width, height, fmt, pixels, flipped) return True # register ImageLoader.register(ImageLoaderSDL2) Kivy-1.9.0/kivy/core/image/img_ffpyplayer.py0000664000175000017500000000504712465721151021022 0ustar titotito00000000000000''' FFPyPlayer: FFmpeg based image loader ''' __all__ = ('ImageLoaderFFPy', ) import ffpyplayer from ffpyplayer.pic import ImageLoader as ffImageLoader, SWScale from ffpyplayer.tools import set_log_callback, loglevels, get_log_callback from kivy.logger import Logger from kivy.core.image import ImageLoaderBase, ImageData, ImageLoader Logger.info('ImageLoaderFFPy: Using ffpyplayer {}'.format(ffpyplayer.version)) logger_func = {'quiet': Logger.critical, 'panic': Logger.critical, 'fatal': Logger.critical, 'error': Logger.error, 'warning': Logger.warning, 'info': Logger.info, 'verbose': Logger.debug, 'debug': Logger.debug} def _log_callback(message, level): message = message.strip() if message: logger_func[level]('ffpyplayer: {}'.format(message)) if not get_log_callback(): set_log_callback(_log_callback) class ImageLoaderFFPy(ImageLoaderBase): '''Image loader based on the ffpyplayer library. .. versionadded:: 1.9.0 .. note: This provider may support more formats than what is listed in :meth:`extensions`. ''' @staticmethod def extensions(): '''Return accepted extensions for this loader''' # See https://www.ffmpeg.org/general.html#Image-Formats return ('bmp', 'dpx', 'exr', 'gif', 'ico', 'jpeg', 'jpg2000', 'jpg', 'jls', 'pam', 'pbm', 'pcx', 'pgm', 'pgmyuv', 'pic', 'png', 'ppm', 'ptx', 'sgi', 'ras', 'tga', 'tiff', 'webp', 'xbm', 'xface', 'xwd') def load(self, filename): try: loader = ffImageLoader(filename) except: Logger.warning('Image: Unable to load image <%s>' % filename) raise # update internals self.filename = filename images = [] while True: frame, t = loader.next_frame() if frame is None: break images.append(frame) if not len(images): raise Exception('No image found in {}'.format(filename)) w, h = images[0].get_size() ifmt = images[0].get_pixel_format() if ifmt != 'rgba' and ifmt != 'rgb24': fmt = 'rgba' sws = SWScale(w, h, ifmt, ofmt=fmt) for i, image in enumerate(images): images[i] = sws.scale(image) else: fmt = ifmt if ifmt == 'rgba' else 'rgb' return [ImageData(w, h, fmt, img.to_memoryview()[0], source_image=img) for img in images] # register ImageLoader.register(ImageLoaderFFPy) Kivy-1.9.0/kivy/core/image/img_dds.py0000664000175000017500000000203012276430013017372 0ustar titotito00000000000000''' DDS: DDS image loader ''' __all__ = ('ImageLoaderDDS', ) from kivy.lib.ddsfile import DDSFile from kivy.logger import Logger from kivy.core.image import ImageLoaderBase, ImageData, ImageLoader class ImageLoaderDDS(ImageLoaderBase): @staticmethod def extensions(): return ('dds', ) def load(self, filename): try: dds = DDSFile(filename=filename) except: Logger.warning('Image: Unable to load image <%s>' % filename) raise self.filename = filename width, height = dds.size im = ImageData(width, height, dds.dxt, dds.images[0], source=filename, flip_vertical=False) if len(dds.images) > 1: images = dds.images images_size = dds.images_size for index in range(1, len(dds.images)): w, h = images_size[index] data = images[index] im.add_mipmap(index, w, h, data) return [im] # register ImageLoader.register(ImageLoaderDDS) Kivy-1.9.0/kivy/core/image/img_pygame.py0000664000175000017500000000627512462275426020135 0ustar titotito00000000000000''' Pygame: Pygame image loader ''' __all__ = ('ImageLoaderPygame', ) from kivy.compat import PY2 from kivy.logger import Logger from kivy.core.image import ImageLoaderBase, ImageData, ImageLoader from os.path import isfile try: import pygame except: raise class ImageLoaderPygame(ImageLoaderBase): '''Image loader based on the PIL library''' @staticmethod def extensions(): '''Return accepted extensions for this loader''' # under macosx, i got with "pygame.error: File is not a Windows BMP # file". documentation said: The image module is a required dependency # of Pygame, but it only optionally supports any extended file formats. # By default it can only load uncompressed BMP image if pygame.image.get_extended() == 0: return ('bmp', ) return ('jpg', 'jpeg', 'jpe', 'png', 'bmp', 'pcx', 'tga', 'tiff', 'tif', 'lbm', 'pbm', 'ppm', 'xpm') @staticmethod def can_save(): return True @staticmethod def can_load_memory(): return True def load(self, filename): if not filename: import traceback traceback.print_stack() return try: im = None if self._inline: im = pygame.image.load(filename, 'x.{}'.format(self._ext)) elif isfile(filename): with open(filename, 'rb') as fd: im = pygame.image.load(fd) elif isinstance(filename, bytes): try: fname = filename.decode() if isfile(fname): with open(fname, 'rb') as fd: im = pygame.image.load(fd) except UnicodeDecodeError: pass if im is None: im = pygame.image.load(filename) except: #Logger.warning(type(filename)('Image: Unable to load image <%s>') # % filename) raise fmt = '' if im.get_bytesize() == 3: fmt = 'rgb' elif im.get_bytesize() == 4: fmt = 'rgba' # image loader work only with rgb/rgba image if fmt not in ('rgb', 'rgba'): try: imc = im.convert(32) fmt = 'rgba' except: try: imc = im.convert_alpha() fmt = 'rgba' except: Logger.warning( 'Image: Unable to convert image %r to rgba (was %r)' % (filename, im.fmt)) raise im = imc # update internals if not self._inline: self.filename = filename data = pygame.image.tostring(im, fmt.upper()) return [ImageData(im.get_width(), im.get_height(), fmt, data, source=filename)] @staticmethod def save(filename, width, height, fmt, pixels, flipped): surface = pygame.image.fromstring( pixels, (width, height), fmt.upper(), flipped) pygame.image.save(surface, filename) return True # register ImageLoader.register(ImageLoaderPygame) Kivy-1.9.0/kivy/core/image/img_tex.py0000664000175000017500000000301412276430013017423 0ustar titotito00000000000000''' Tex: Compressed texture ''' __all__ = ('ImageLoaderTex', ) import json from struct import unpack from kivy.logger import Logger from kivy.core.image import ImageLoaderBase, ImageData, ImageLoader class ImageLoaderTex(ImageLoaderBase): @staticmethod def extensions(): return ('tex', ) def load(self, filename): try: fd = open(filename, 'rb') if fd.read(4) != 'KTEX': raise Exception('Invalid tex identifier') headersize = unpack('I', fd.read(4))[0] header = fd.read(headersize) if len(header) != headersize: raise Exception('Truncated tex header') info = json.loads(header) data = fd.read() if len(data) != info['datalen']: raise Exception('Truncated tex data') except: Logger.warning('Image: Image <%s> is corrupted' % filename) raise width, height = info['image_size'] tw, th = info['texture_size'] images = [data] im = ImageData(width, height, str(info['format']), images[0], source=filename) ''' if len(dds.images) > 1: images = dds.images images_size = dds.images_size for index in range(1, len(dds.images)): w, h = images_size[index] data = images[index] im.add_mipmap(index, w, h, data) ''' return [im] # register ImageLoader.register(ImageLoaderTex) Kivy-1.9.0/kivy/core/image/__init__.py0000664000175000017500000007042412465721151017545 0ustar titotito00000000000000''' Image ===== Core classes for loading images and converting them to a :class:`~kivy.graphics.texture.Texture`. The raw image data can be keep in memory for further access. In-memory image loading ----------------------- .. versionadded:: 1.9.0 Official support for in-memory loading. Not all the providers supports it, but at the moment, pygame, pil and imageio works. To load an image with a filename, you usually do:: from kivy.core.image import Image as CoreImage im = CoreImage("image.png") Now you can load from memory block. Instead of passing the filename, you'll need to pass the data as a BytesIO object + an "ext" parameters. Both are mandatory:: import io from kivy.core.image import Image as CoreImage data = io.BytesIO(open("image.png", "rb").read()) im = CoreImage(data, ext="png") By default, the image will not be cached, as our internal cache require a filename. If you want caching, add a filename that represent your file (it will be used only for caching):: import io from kivy.core.image import Image as CoreImage data = io.BytesIO(open("image.png", "rb").read()) im = CoreImage(data, ext="png", filename="image.png") ''' __all__ = ('Image', 'ImageLoader', 'ImageData') from kivy.event import EventDispatcher from kivy.core import core_register_libs from kivy.logger import Logger from kivy.cache import Cache from kivy.clock import Clock from kivy.atlas import Atlas from kivy.resources import resource_find from kivy.utils import platform from kivy.compat import string_types from kivy.setupconfig import USE_SDL2 import zipfile from io import BytesIO # late binding Texture = TextureRegion = None # register image caching only for keep_data=True Cache.register('kv.image', timeout=60) Cache.register('kv.atlas') class ImageData(object): '''Container for images and mipmap images. The container will always have at least the mipmap level 0. ''' __slots__ = ('fmt', 'mipmaps', 'source', 'flip_vertical', 'source_image') _supported_fmts = ('rgb', 'rgba', 'bgr', 'bgra', 's3tc_dxt1', 's3tc_dxt3', 's3tc_dxt5', 'pvrtc_rgb2', 'pvrtc_rgb4', 'pvrtc_rgba2', 'pvrtc_rgba4', 'etc1_rgb8') def __init__(self, width, height, fmt, data, source=None, flip_vertical=True, source_image=None, rowlength=0): assert fmt in ImageData._supported_fmts #: Decoded image format, one of a available texture format self.fmt = fmt #: Data for each mipmap. self.mipmaps = {} self.add_mipmap(0, width, height, data, rowlength) #: Image source, if available self.source = source #: Indicate if the texture will need to be vertically flipped self.flip_vertical = flip_vertical # the original image, which we might need to save if it is a memoryview self.source_image = source_image def release_data(self): mm = self.mipmaps for item in mm.values(): item[2] = None self.source_image = None @property def width(self): '''Image width in pixels. (If the image is mipmapped, it will use the level 0) ''' return self.mipmaps[0][0] @property def height(self): '''Image height in pixels. (If the image is mipmapped, it will use the level 0) ''' return self.mipmaps[0][1] @property def data(self): '''Image data. (If the image is mipmapped, it will use the level 0) ''' return self.mipmaps[0][2] @property def rowlength(self): '''Image rowlength. (If the image is mipmapped, it will use the level 0) .. versionadded:: 1.9.0 ''' return self.mipmaps[0][3] @property def size(self): '''Image (width, height) in pixels. (If the image is mipmapped, it will use the level 0) ''' mm = self.mipmaps[0] return mm[0], mm[1] @property def have_mipmap(self): return len(self.mipmaps) > 1 def __repr__(self): return ('' % ( self.width, self.height, self.fmt, self.source, len(self.mipmaps))) def add_mipmap(self, level, width, height, data, rowlength): '''Add a image for a specific mipmap level. .. versionadded:: 1.0.7 ''' self.mipmaps[level] = [int(width), int(height), data, rowlength] def get_mipmap(self, level): '''Get the mipmap image at a specific level if it exists .. versionadded:: 1.0.7 ''' if level == 0: return (self.width, self.height, self.data, self.rowlength) assert(level < len(self.mipmaps)) return self.mipmaps[level] def iterate_mipmaps(self): '''Iterate over all mipmap images available. .. versionadded:: 1.0.7 ''' mm = self.mipmaps for x in range(len(mm)): item = mm.get(x, None) if item is None: raise Exception('Invalid mipmap level, found empty one') yield x, item[0], item[1], item[2], item[3] class ImageLoaderBase(object): '''Base to implement an image loader.''' __slots__ = ('_texture', '_data', 'filename', 'keep_data', '_mipmap', '_nocache', '_ext', '_inline') def __init__(self, filename, **kwargs): self._mipmap = kwargs.get('mipmap', False) self.keep_data = kwargs.get('keep_data', False) self._nocache = kwargs.get('nocache', False) self._ext = kwargs.get('ext') self._inline = kwargs.get('inline') self.filename = filename if self._inline: self._data = self.load(kwargs.get('rawdata')) else: self._data = self.load(filename) self._textures = None def load(self, filename): '''Load an image''' return None @staticmethod def can_save(): '''Indicate if the loader can save the Image object ''' return False @staticmethod def can_load_memory(): '''Indicate if the loader can load an image by passing data ''' return False @staticmethod def save(): raise NotImplementedError() def populate(self): self._textures = [] fname = self.filename if __debug__: Logger.trace('Image: %r, populate to textures (%d)' % (fname, len(self._data))) for count in range(len(self._data)): # first, check if a texture with the same name already exist in the # cache chr = type(fname) uid = chr(u'%s|%d|%d') % (fname, self._mipmap, count) texture = Cache.get('kv.texture', uid) # if not create it and append to the cache if texture is None: imagedata = self._data[count] source = '{}{}|'.format( 'zip|' if fname.endswith('.zip') else '', self._nocache) imagedata.source = chr(source) + uid texture = Texture.create_from_data( imagedata, mipmap=self._mipmap) if not self._nocache: Cache.append('kv.texture', uid, texture) if imagedata.flip_vertical: texture.flip_vertical() # set as our current texture self._textures.append(texture) # release data if ask if not self.keep_data: self._data[count].release_data() @property def width(self): '''Image width ''' return self._data[0].width @property def height(self): '''Image height ''' return self._data[0].height @property def size(self): '''Image size (width, height) ''' return (self._data[0].width, self._data[0].height) @property def texture(self): '''Get the image texture (created on the first call) ''' if self._textures is None: self.populate() if self._textures is None: return None return self._textures[0] @property def textures(self): '''Get the textures list (for mipmapped image or animated image) .. versionadded:: 1.0.8 ''' if self._textures is None: self.populate() return self._textures @property def nocache(self): '''Indicate if the texture will not be stored in the cache .. versionadded:: 1.6.0 ''' return self._nocache class ImageLoader(object): loaders = [] @staticmethod def zip_loader(filename, **kwargs): '''Read images from an zip file. .. versionadded:: 1.0.8 Returns an Image with a list of type ImageData stored in Image._data ''' # read zip in menory for faster access _file = BytesIO(open(filename, 'rb').read()) # read all images inside the zip z = zipfile.ZipFile(_file) image_data = [] # sort filename list znamelist = z.namelist() znamelist.sort() image = None for zfilename in znamelist: try: #read file and store it in mem with fileIO struct around it tmpfile = BytesIO(z.read(zfilename)) ext = zfilename.split('.')[-1].lower() im = None for loader in ImageLoader.loaders: if (ext not in loader.extensions() or not loader.can_load_memory()): continue Logger.debug('Image%s: Load <%s> from <%s>' % (loader.__name__[11:], zfilename, filename)) try: im = loader(zfilename, ext=ext, rawdata=tmpfile, inline=True, **kwargs) except: # Loader failed, continue trying. continue break if im is not None: # append ImageData to local variable before it's # overwritten image_data.append(im._data[0]) image = im #else: if not image file skip to next except: Logger.warning('Image: Unable to load image' '<%s> in zip <%s> trying to continue...' % (zfilename, filename)) z.close() if len(image_data) == 0: raise Exception('no images in zip <%s>' % filename) # replace Image.Data with the array of all the images in the zip image._data = image_data image.filename = filename return image @staticmethod def register(defcls): ImageLoader.loaders.append(defcls) @staticmethod def load(filename, **kwargs): # atlas ? if filename[:8] == 'atlas://': # remove the url rfn = filename[8:] # last field is the ID try: rfn, uid = rfn.rsplit('/', 1) except ValueError: raise ValueError( 'Image: Invalid %s name for atlas' % filename) # search if we already got the atlas loaded atlas = Cache.get('kv.atlas', rfn) # atlas already loaded, so reupload the missing texture in cache, # because when it's not in use, the texture can be removed from the # kv.texture cache. if atlas: texture = atlas[uid] fn = 'atlas://%s/%s' % (rfn, uid) cid = '{}|{:d}|{:d}'.format(fn, False, 0) Cache.append('kv.texture', cid, texture) return Image(texture) # search with resource afn = rfn if not afn.endswith('.atlas'): afn += '.atlas' afn = resource_find(afn) if not afn: raise Exception('Unable to found %r atlas' % afn) atlas = Atlas(afn) Cache.append('kv.atlas', rfn, atlas) # first time, fill our texture cache. for nid, texture in atlas.textures.items(): fn = 'atlas://%s/%s' % (rfn, nid) cid = '{}|{:d}|{:d}'.format(fn, False, 0) Cache.append('kv.texture', cid, texture) return Image(atlas[uid]) # extract extensions ext = filename.split('.')[-1].lower() # prevent url querystrings if filename.startswith((('http://', 'https://'))): ext = ext.split('?')[0] filename = resource_find(filename) # special case. When we are trying to load a "zip" file with image, we # will use the special zip_loader in ImageLoader. This might return a # sequence of images contained in the zip. if ext == 'zip': return ImageLoader.zip_loader(filename) else: im = None for loader in ImageLoader.loaders: if ext not in loader.extensions(): continue Logger.debug('Image%s: Load <%s>' % (loader.__name__[11:], filename)) im = loader(filename, **kwargs) break if im is None: raise Exception('Unknown <%s> type, no loader found.' % ext) return im class Image(EventDispatcher): '''Load an image and store the size and texture. .. versionchanged:: 1.0.7 `mipmap` attribute has been added. The `texture_mipmap` and `texture_rectangle` have been deleted. .. versionchanged:: 1.0.8 An Image widget can change its texture. A new event 'on_texture' has been introduced. New methods for handling sequenced animation have been added. :Parameters: `arg` : can be a string (str), Texture or Image object. A string is interpreted as a path to the image to be loaded. You can also provide a texture object or an already existing image object. In the latter case, a real copy of the given image object will be returned. `keep_data` : bool, defaults to False. Keep the image data when the texture is created. `scale` : float, defaults to 1.0 Scale of the image. `mipmap` : bool, defaults to False Create mipmap for the texture. `anim_delay`: float, defaults to .25 Delay in seconds between each animation frame. Lower values means faster animation. ''' copy_attributes = ('_size', '_filename', '_texture', '_image', '_mipmap', '_nocache') def __init__(self, arg, **kwargs): # this event should be fired on animation of sequenced img's self.register_event_type('on_texture') super(Image, self).__init__() self._mipmap = kwargs.get('mipmap', False) self._keep_data = kwargs.get('keep_data', False) self._nocache = kwargs.get('nocache', False) self._size = [0, 0] self._image = None self._filename = None self._texture = None self._anim_available = False self._anim_index = 0 self._anim_delay = 0 self.anim_delay = kwargs.get('anim_delay', .25) # indicator of images having been loded in cache self._iteration_done = False if isinstance(arg, Image): for attr in Image.copy_attributes: self.__setattr__(attr, arg.__getattribute__(attr)) elif type(arg) in (Texture, TextureRegion): if not hasattr(self, 'textures'): self.textures = [] self.textures.append(arg) self._texture = arg self._size = self.texture.size elif isinstance(arg, ImageLoaderBase): self.image = arg elif isinstance(arg, BytesIO): ext = kwargs.get('ext', None) if not ext: raise Exception('Inline loading require "ext" parameter') filename = kwargs.get('filename') if not filename: self._nocache = True filename = '__inline__' self.load_memory(arg, ext, filename) elif isinstance(arg, string_types): self.filename = arg else: raise Exception('Unable to load image type {0!r}'.format(arg)) def remove_from_cache(self): '''Remove the Image from cache. This facilitates re-loading of images from disk in case the image content has changed. .. versionadded:: 1.3.0 Usage:: im = CoreImage('1.jpg') # -- do something -- im.remove_from_cache() im = CoreImage('1.jpg') # this time image will be re-loaded from disk ''' count = 0 f = self.filename pat = type(f)(u'%s|%d|%d') uid = pat % (f, self._mipmap, count) Cache.remove("kv.image", uid) while Cache.get("kv.texture", uid): Cache.remove("kv.texture", uid) count += 1 uid = pat % (f, self._mipmap, count) def _anim(self, *largs): if not self._image: return textures = self.image.textures if self._anim_index >= len(textures): self._anim_index = 0 self._texture = self.image.textures[self._anim_index] self.dispatch('on_texture') self._anim_index += 1 self._anim_index %= len(self._image.textures) def anim_reset(self, allow_anim): '''Reset an animation if available. .. versionadded:: 1.0.8 :Parameters: `allow_anim`: bool Indicate whether the animation should restart playing or not. Usage:: # start/reset animation image.anim_reset(True) # or stop the animation image.anim_reset(False) You can change the animation speed whilst it is playing:: # Set to 20 FPS image.anim_delay = 1 / 20. ''' # stop animation Clock.unschedule(self._anim) if allow_anim and self._anim_available: Clock.schedule_interval(self._anim, self.anim_delay) self._anim() def _get_anim_delay(self): return self._anim_delay def _set_anim_delay(self, x): if self._anim_delay == x: return self._anim_delay = x if self._anim_available: Clock.unschedule(self._anim) if self._anim_delay >= 0: Clock.schedule_interval(self._anim, self._anim_delay) anim_delay = property(_get_anim_delay, _set_anim_delay) '''Delay between each animation frame. A lower value means faster animation. .. versionadded:: 1.0.8 ''' @property def anim_available(self): '''Return True if this Image instance has animation available. .. versionadded:: 1.0.8 ''' return self._anim_available @property def anim_index(self): '''Return the index number of the image currently in the texture. .. versionadded:: 1.0.8 ''' return self._anim_index def _img_iterate(self, *largs): if not self.image or self._iteration_done: return self._iteration_done = True imgcount = len(self.image.textures) if imgcount > 1: self._anim_available = True self.anim_reset(True) self._texture = self.image.textures[0] def on_texture(self, *largs): '''This event is fired when the texture reference or content has changed. It is normally used for sequenced images. .. versionadded:: 1.0.8 ''' pass @staticmethod def load(filename, **kwargs): '''Load an image :Parameters: `filename` : str Filename of the image. `keep_data` : bool, defaults to False Keep the image data when the texture is created. ''' kwargs.setdefault('keep_data', False) return Image(filename, **kwargs) def _get_image(self): return self._image def _set_image(self, image): self._image = image if hasattr(image, 'filename'): self._filename = image.filename if image: self._size = (self.image.width, self.image.height) image = property(_get_image, _set_image, doc='Get/set the data image object') def _get_filename(self): return self._filename def _set_filename(self, value): if value is None or value == self._filename: return self._filename = value # construct uid as a key for Cache f = self.filename uid = type(f)(u'%s|%d|%d') % (f, self._mipmap, 0) # in case of Image have been asked with keep_data # check the kv.image cache instead of texture. image = Cache.get('kv.image', uid) if image: # we found an image, yeah ! but reset the texture now. self.image = image # if image.__class__ is core image then it's a texture # from atlas or other sources and has no data so skip if (image.__class__ != self.__class__ and not image.keep_data and self._keep_data): self.remove_from_cache() self._filename = '' self._set_filename(value) else: self._texture = None self._img_iterate() return else: # if we already got a texture, it will be automatically reloaded. _texture = Cache.get('kv.texture', uid) if _texture: self._texture = _texture return # if image not already in cache then load tmpfilename = self._filename image = ImageLoader.load( self._filename, keep_data=self._keep_data, mipmap=self._mipmap, nocache=self._nocache) self._filename = tmpfilename # put the image into the cache if needed if isinstance(image, Texture): self._texture = image self._size = image.size else: self.image = image if not self._nocache: Cache.append('kv.image', uid, self.image) filename = property(_get_filename, _set_filename, doc='Get/set the filename of image') def load_memory(self, data, ext, filename='__inline__'): '''(internal) Method to load an image from raw data. ''' self._filename = filename # see if there is a available loader for it loaders = [loader for loader in ImageLoader.loaders if loader.can_load_memory() and ext in loader.extensions()] if not loaders: raise Exception('No inline loader found to load {}'.format(ext)) image = loaders[0](filename, ext=ext, rawdata=data, inline=True, nocache=self._nocache, mipmap=self._mipmap, keep_data=self._keep_data) if isinstance(image, Texture): self._texture = image self._size = image.size else: self.image = image @property def size(self): '''Image size (width, height) ''' return self._size @property def width(self): '''Image width ''' return self._size[0] @property def height(self): '''Image height ''' return self._size[1] @property def texture(self): '''Texture of the image''' if self.image: if not self._iteration_done: self._img_iterate() return self._texture @property def nocache(self): '''Indicate whether the texture will not be stored in the cache or not. .. versionadded:: 1.6.0 ''' return self._nocache def save(self, filename, flipped=False): '''Save image texture to file. The filename should have the '.png' extension because the texture data read from the GPU is in the RGBA format. '.jpg' might work but has not been heavilly tested so some providers might break when using it. Any other extensions are not officially supported. The flipped parameter flips the saved image vertically, and defaults to True. Example:: # Save an core image object from kivy.core.image import Image img = Image('hello.png') img.save('hello2.png') # Save a texture texture = Texture.create(...) img = Image(texture) img.save('hello3.png') .. versionadded:: 1.7.0 .. versionchanged:: 1.8.0 Parameter `flipped` added to flip the image before saving, default to False. ''' pixels = None size = None loaders = [x for x in ImageLoader.loaders if x.can_save()] if not loaders: return False loader = loaders[0] if self.image: # we might have a ImageData object to use data = self.image._data[0] if data.data is not None: if data.fmt not in ('rgba', 'rgb'): # fast path, use the "raw" data when keep_data is used size = data.width, data.height pixels = data.data else: # the format is not rgba, we need to convert it. # use texture for that. self.populate() if pixels is None and self._texture: # use the texture pixels size = self._texture.size pixels = self._texture.pixels if pixels is None: return False l_pixels = len(pixels) if l_pixels == size[0] * size[1] * 3: fmt = 'rgb' elif l_pixels == size[0] * size[1] * 4: fmt = 'rgba' else: raise Exception('Unable to determine the format of the pixels') return loader.save(filename, size[0], size[1], fmt, pixels, flipped) def read_pixel(self, x, y): '''For a given local x/y position, return the pixel color at that position. .. warning:: This function can only be used with images loaded with the keep_data=True keyword. For example:: m = Image.load('image.png', keep_data=True) color = m.read_pixel(150, 150) :Parameters: `x` : int Local x coordinate of the pixel in question. `y` : int Local y coordinate of the pixel in question. ''' data = self.image._data[0] # can't use this fonction without ImageData if data.data is None: raise EOFError('Image data is missing, make sure that image is' 'loaded with keep_data=True keyword.') # check bounds x, y = int(x), int(y) if not (0 <= x < data.width and 0 <= y < data.height): raise IndexError('Position (%d, %d) is out of range.' % (x, y)) assert data.fmt in ImageData._supported_fmts size = 3 if data.fmt in ('rgb', 'bgr') else 4 index = y * data.width * size + x * size raw = bytearray(data.data[index:index + size]) color = [c / 255.0 for c in raw] # conversion for BGR->RGB, BGR->RGBA format if data.fmt in ('bgr', 'bgra'): color[0], color[2] = color[2], color[0] return color def load(filename): '''Load an image''' return Image.load(filename) # load image loaders image_libs = [] if platform in ('macosx', 'ios'): image_libs += [('imageio', 'img_imageio')] image_libs += [ ('tex', 'img_tex'), ('dds', 'img_dds')] if USE_SDL2: image_libs += [('sdl2', 'img_sdl2')] else: image_libs += [('pygame', 'img_pygame')] image_libs += [ ('ffpy', 'img_ffpyplayer'), ('pil', 'img_pil'), ('gif', 'img_gif')] libs_loaded = core_register_libs('image', image_libs) from os import environ if not 'KIVY_DOC' in environ and not libs_loaded: import sys Logger.critical('App: Unable to get any Image provider, abort.') sys.exit(1) # resolve binding. from kivy.graphics.texture import Texture, TextureRegion Kivy-1.9.0/kivy/core/clipboard/0000775000175000017500000000000012507221737016304 5ustar titotito00000000000000Kivy-1.9.0/kivy/core/clipboard/clipboard_android.py0000664000175000017500000000516312502235633022315 0ustar titotito00000000000000''' Clipboard Android ================= Android implementation of Clipboard provider, using Pyjnius. ''' __all__ = ('ClipboardAndroid', ) from kivy.core.clipboard import ClipboardBase from jnius import autoclass from android.runnable import run_on_ui_thread AndroidString = autoclass('java.lang.String') PythonActivity = autoclass('org.renpy.android.PythonActivity') Context = autoclass('android.content.Context') VER = autoclass('android.os.Build$VERSION') sdk = VER.SDK_INT class ClipboardAndroid(ClipboardBase): def __init__(self): super(ClipboardAndroid, self).__init__() self._clipboard = None self._data = dict() self._data['text/plain'] = None self._data['application/data'] = None PythonActivity._clipboard = None def get(self, mimetype='text/plain'): return self._get(mimetype) def put(self, data, mimetype='text/plain'): self._set(data, mimetype) def get_types(self): return list(self._data.keys()) @run_on_ui_thread def _initialize_clipboard(self): PythonActivity._clipboard = PythonActivity.getSystemService( Context.CLIPBOARD_SERVICE) def _get_clipboard(f): def called(*args, **kargs): self = args[0] if not PythonActivity._clipboard: self._initialize_clipboard() import time while not PythonActivity._clipboard: time.sleep(.01) return f(*args, **kargs) return called @_get_clipboard def _get(self, mimetype='text/plain'): clippy = PythonActivity._clipboard if sdk < 11: data = clippy.getText() else: ClipDescription = autoclass('android.content.ClipDescription') primary_clip = clippy.getPrimaryClip() if primary_clip and clippy.getPrimaryClipDescription().hasMimeType( ClipDescription.MIMETYPE_TEXT_PLAIN): data = primary_clip.getItemAt(0).getText().toString() else: # TODO: non text data types Not yet implemented data = '' return data @_get_clipboard def _set(self, data, mimetype): clippy = PythonActivity._clipboard if sdk < 11: #versions previous to honeycomb clippy.setText(AndroidString(data)) else: ClipData = autoclass('android.content.ClipData') new_clip = ClipData.newPlainText(AndroidString(""), AndroidString(data)) # put text data onto clipboard clippy.setPrimaryClip(new_clip) Kivy-1.9.0/kivy/core/clipboard/clipboard_dummy.py0000664000175000017500000000117312276430013022022 0ustar titotito00000000000000''' Clipboard Dummy: an internal implementation that does not use the system clipboard. ''' __all__ = ('ClipboardDummy', ) from kivy.core.clipboard import ClipboardBase class ClipboardDummy(ClipboardBase): def __init__(self): super(ClipboardDummy, self).__init__() self._data = dict() self._data['text/plain'] = None self._data['application/data'] = None def get(self, mimetype='text/plain'): return self._data.get(mimetype, None) def put(self, data, mimetype='text/plain'): self._data[mimetype] = data def get_types(self): return list(self._data.keys()) Kivy-1.9.0/kivy/core/clipboard/clipboard_gtk3.py0000664000175000017500000000221712506213301021532 0ustar titotito00000000000000''' Clipboard Gtk3: an implementation of the Clipboard using Gtk3. ''' __all__ = ('ClipboardGtk3',) from kivy.utils import platform from kivy.support import install_gobject_iteration from kivy.core.clipboard import ClipboardBase if platform != 'linux': raise SystemError('unsupported platform for gtk3 clipboard') from gi.repository import Gtk, Gdk clipboard = Gtk.Clipboard.get(Gdk.SELECTION_CLIPBOARD) class ClipboardGtk3(ClipboardBase): _is_init = False def init(self): if self._is_init: return install_gobject_iteration() self._is_init = True def get(self, mimetype='text/plain;charset=utf-8'): self.init() if mimetype == 'text/plain;charset=utf-8': contents = clipboard.wait_for_text() if contents: return contents return '' def put(self, data, mimetype='text/plain;charset=utf-8'): self.init() if mimetype == 'text/plain;charset=utf-8': text = data.decode(self._encoding) clipboard.set_text(text, -1) def get_types(self): self.init() return ['text/plain;charset=utf-8'] Kivy-1.9.0/kivy/core/clipboard/clipboard_sdl2.py0000664000175000017500000000152612504401637021541 0ustar titotito00000000000000''' Clipboard SDL2: an implementation of the Clipboard using sdl2. ''' __all__ = ('ClipboardSDL2', ) from kivy.utils import platform from kivy.core.clipboard import ClipboardBase if platform not in ('win', 'linux', 'macosx', 'android', 'ios'): raise SystemError('unsupported platform for sdl2 clipboard') try: from kivy.core.clipboard._clipboard_sdl2 import ( _get_text, _has_text, _set_text) except ImportError: raise SystemError('extension not compiled?') class ClipboardSDL2(ClipboardBase): def get(self, mimetype): return _get_text() if _has_text() else '' def _ensure_clipboard(self): super(ClipboardSDL2, self)._ensure_clipboard() self._encoding = 'utf8' def put(self, data=b'', mimetype='text/plain'): _set_text(data) def get_types(self): return ['text/plain'] Kivy-1.9.0/kivy/core/clipboard/clipboard_dbusklipper.py0000664000175000017500000000176512504401637023226 0ustar titotito00000000000000''' Clipboard Dbus: an implementation of the Clipboard using dbus and klipper. ''' __all__ = ('ClipboardDbusKlipper', ) from kivy.utils import platform from kivy.core.clipboard import ClipboardBase if platform != 'linux': raise SystemError('unsupported platform for dbus kde clipboard') try: import dbus bus = dbus.SessionBus() proxy = bus.get_object("org.kde.klipper", "/klipper") except: raise class ClipboardDbusKlipper(ClipboardBase): _is_init = False def init(self): if ClipboardDbusKlipper._is_init: return self.iface = dbus.Interface(proxy, "org.kde.klipper.klipper") ClipboardDbusKlipper._is_init = True def get(self, mimetype='text/plain'): self.init() return str(self.iface.getClipboardContents()) def put(self, data, mimetype='text/plain'): self.init() self.iface.setClipboardContents(data.replace('\x00', '')) def get_types(self): self.init() return [u'text/plain'] Kivy-1.9.0/kivy/core/clipboard/clipboard_nspaste.py0000664000175000017500000000230212504401637022343 0ustar titotito00000000000000''' Clipboard OsX: implementation of clipboard using Appkit ''' __all__ = ('ClipboardNSPaste', ) from kivy.core.clipboard import ClipboardBase from kivy.utils import platform if platform != 'macosx': raise SystemError('Unsupported platform for appkit clipboard.') try: from pyobjus import autoclass from pyobjus.dylib_manager import load_framework, INCLUDE load_framework(INCLUDE.AppKit) except ImportError: raise SystemError('Pyobjus not installed. Please run the following' ' command to install it. `pip install --user pyobjus`') NSPasteboard = autoclass('NSPasteboard') NSString = autoclass('NSString') class ClipboardNSPaste(ClipboardBase): def __init__(self): super(ClipboardNSPaste, self).__init__() self._clipboard = NSPasteboard.generalPasteboard() def get(self, mimetype='text/plain'): pb = self._clipboard data = pb.stringForType_('public.utf8-plain-text') if not data: return "" return data.UTF8String() def put(self, data, mimetype='text/plain'): pb = self._clipboard pb.clearContents() pb.writeObjects_([data]) def get_types(self): return list('text/plain',) Kivy-1.9.0/kivy/core/clipboard/_clipboard_sdl2.pyx0000664000175000017500000000046312462275426022077 0ustar titotito00000000000000#cython: c_string_type=unicode, c_string_encoding=utf8 ''' TODO: - everything ''' include '../../lib/sdl2.pxi' def _has_text(): return True if SDL_HasClipboardText() == SDL_TRUE else False def _get_text(): return SDL_GetClipboardText() def _set_text(text): SDL_SetClipboardText(text) Kivy-1.9.0/kivy/core/clipboard/clipboard_pygame.py0000664000175000017500000000264612506213301022152 0ustar titotito00000000000000''' Clipboard Pygame: an implementation of the Clipboard using pygame.scrap. ''' __all__ = ('ClipboardPygame', ) from kivy.utils import platform from kivy.compat import PY2 from kivy.core.clipboard import ClipboardBase if platform not in ('win', 'linux', 'macosx'): raise SystemError('unsupported platform for pygame clipboard') try: import pygame import pygame.scrap except: raise class ClipboardPygame(ClipboardBase): _is_init = False _types = None _aliases = { 'text/plain;charset=utf-8': 'UTF8_STRING' } def init(self): if ClipboardPygame._is_init: return pygame.scrap.init() ClipboardPygame._is_init = True def get(self, mimetype='text/plain'): self.init() mimetype = self._aliases.get(mimetype, mimetype) text = pygame.scrap.get(mimetype) return text def put(self, data, mimetype='text/plain'): self.init() mimetype = self._aliases.get(mimetype, mimetype) pygame.scrap.put(mimetype, data) def get_types(self): if not self._types: self.init() types = pygame.scrap.get_types() for mime, pygtype in self._aliases.items()[:]: if mime in types: del self._aliases[mime] if pygtype in types: types.append(mime) self._types = types return self._types Kivy-1.9.0/kivy/core/clipboard/clipboard_xsel.py0000664000175000017500000000146612504401637021653 0ustar titotito00000000000000''' Clipboard xsel: an implementation of the Clipboard using xsel command line tool. ''' __all__ = ('ClipboardXsel', ) from kivy.utils import platform from kivy.core.clipboard import ClipboardBase if platform != 'linux': raise SystemError('unsupported platform for xsel clipboard') try: import subprocess p = subprocess.Popen(['xsel'], stdout=subprocess.PIPE) p.communicate() except: raise class ClipboardXsel(ClipboardBase): def get(self, mimetype='text/plain'): p = subprocess.Popen(['xsel', '-bo'], stdout=subprocess.PIPE) data, _ = p.communicate() return data def put(self, data, mimetype='text/plain'): p = subprocess.Popen(['xsel', '-bi'], stdin=subprocess.PIPE) p.communicate(data) def get_types(self): return [u'text/plain'] Kivy-1.9.0/kivy/core/clipboard/clipboard_winctypes.py0000664000175000017500000000252312504401637022720 0ustar titotito00000000000000''' Clipboard windows: an implementation of the Clipboard using ctypes. ''' __all__ = ('ClipboardWindows', ) from kivy.utils import platform from kivy.core.clipboard import ClipboardBase if platform != 'win': raise SystemError('unsupported platform for Windows clipboard') import ctypes user32 = ctypes.windll.user32 kernel32 = ctypes.windll.kernel32 msvcrt = ctypes.cdll.msvcrt c_char_p = ctypes.c_char_p c_wchar_p = ctypes.c_wchar_p class ClipboardWindows(ClipboardBase): def get(self, mimetype='text/plain'): user32.OpenClipboard(0) # 1 is CF_TEXT pcontents = user32.GetClipboardData(13) if not pcontents: return '' data = c_wchar_p(pcontents).value.encode(self._encoding) #ctypes.windll.kernel32.GlobalUnlock(pcontents) user32.CloseClipboard() return data def put(self, text, mimetype='text/plain'): GMEM_DDESHARE = 0x2000 CF_UNICODETEXT = 13 user32.OpenClipboard(None) user32.EmptyClipboard() hCd = kernel32.GlobalAlloc(GMEM_DDESHARE, len(text) + 2) pchData = kernel32.GlobalLock(hCd) msvcrt.wcscpy(c_wchar_p(pchData), text) kernel32.GlobalUnlock(hCd) user32.SetClipboardData(CF_UNICODETEXT, hCd) user32.CloseClipboard() def get_types(self): return list('text/plain',) Kivy-1.9.0/kivy/core/clipboard/__init__.py0000664000175000017500000001100312506213301020373 0ustar titotito00000000000000''' Clipboard ========= Core class for accessing the Clipboard. If we are not able to access the system clipboard, a fake one will be used. Usage example:: >>> from kivy.core.clipboard import Clipboard >>> Clipboard.get_types() ['TIMESTAMP', 'TARGETS', 'MULTIPLE', 'SAVE_TARGETS', 'UTF8_STRING', 'COMPOUND_TEXT', 'TEXT', 'STRING', 'text/plain;charset=utf-8', 'text/plain'] >>> Clipboard.get('TEXT') 'Hello World' >>> Clipboard.put('Great', 'UTF8_STRING') >>> Clipboard.get_types() ['UTF8_STRING'] >>> Clipboard.get('UTF8_STRING') 'Great' .. note:: The main implementation relies on Pygame and works well with text/strings. Anything else might not work the same on all platforms. ''' __all__ = ('ClipboardBase', 'Clipboard') from kivy.core import core_select_lib from kivy.utils import platform from kivy.setupconfig import USE_SDL2 class ClipboardBase(object): def get(self, mimetype): '''Get the current data in clipboard, using the mimetype if possible. You not use this method directly. Use :meth:`paste` instead. ''' return None def put(self, data, mimetype): '''Put data on the clipboard, and attach a mimetype. You should not use this method directly. Use :meth:`copy` instead. ''' pass def get_types(self): '''Return a list of supported mimetypes ''' return [] def _ensure_clipboard(self): ''' Ensure that the clipboard has been properly initialised. ''' if hasattr(self, '_clip_mime_type'): return if platform == 'win': self._clip_mime_type = 'text/plain;charset=utf-8' # windows clipboard uses a utf-16 little endian encoding self._encoding = 'utf-16-le' elif platform == 'linux': self._clip_mime_type = 'text/plain;charset=utf-8' self._encoding = 'utf-8' else: self._clip_mime_type = 'text/plain' self._encoding = 'utf-8' def copy(self, data=''): ''' Copy the value provided in argument `data` into current clipboard. If data is not of type string it will be converted to string. .. versionadded:: 1.9.0 ''' if data: self._copy(data) def paste(self): ''' Get text from the system clipboard and return it a usable string. .. versionadded:: 1.9.0 ''' return self._paste() def _copy(self, data): # explicitly terminate strings with a null character # so as to avoid putting spurious data after the end. # MS windows issue. self._ensure_clipboard() if not isinstance(data, bytes): data = data.encode(self._encoding) if platform == 'win': data += b'\x00' self.put(data, self._clip_mime_type) def _paste(self): self._ensure_clipboard() _clip_types = Clipboard.get_types() mime_type = self._clip_mime_type if mime_type not in _clip_types: mime_type = 'text/plain' data = self.get(mime_type) if data is not None: # decode only if we don't have unicode # we would still need to decode from utf-16 (windows) # data is of type bytes in PY3 if isinstance(data, bytes): data = data.decode(self._encoding, 'ignore') # remove null strings mostly a windows issue data = data.replace(u'\x00', u'') return data return u'' # load clipboard implementation _clipboards = [] if platform == 'android': _clipboards.append( ('android', 'clipboard_android', 'ClipboardAndroid')) elif platform == 'macosx': _clipboards.append( ('nspaste', 'clipboard_nspaste', 'ClipboardNSPaste')) elif platform == 'win': _clipboards.append( ('winctypes', 'clipboard_winctypes', 'ClipboardWindows')) elif platform == 'linux': _clipboards.append( ('dbusklipper', 'clipboard_dbusklipper', 'ClipboardDbusKlipper')) _clipboards.append( ('gtk3', 'clipboard_gtk3', 'ClipboardGtk3')) _clipboards.append( ('xsel', 'clipboard_xsel', 'ClipboardXsel')) if USE_SDL2: if platform != 'linux': _clipboards.append( ('sdl2', 'clipboard_sdl2', 'ClipboardSDL2')) else: _clipboards.append( ('pygame', 'clipboard_pygame', 'ClipboardPygame')) _clipboards.append( ('dummy', 'clipboard_dummy', 'ClipboardDummy')) Clipboard = core_select_lib('clipboard', _clipboards, True) Kivy-1.9.0/kivy/core/camera/0000775000175000017500000000000012507221737015575 5ustar titotito00000000000000Kivy-1.9.0/kivy/core/camera/camera_avfoundation_implem.h0000664000175000017500000000052612276430013023311 0ustar titotito00000000000000typedef void *camera_t; camera_t avf_camera_init(int index, int width, int height); void avf_camera_deinit(camera_t camera); void avf_camera_update(camera_t camera); void avf_camera_start(camera_t camera); void avf_camera_stop(camera_t camera); void avf_camera_get_image(camera_t camera, int *width, int *height, int *rowsize, char **data); Kivy-1.9.0/kivy/core/camera/camera_avfoundation.pyx0000664000175000017500000000463612276430013022345 0ustar titotito00000000000000''' AVFoundation Camera =================== Camera implementation using AVFoundation framework for OSX / iOS ''' __all__ = ['CameraAVFoundation'] cdef extern from "camera_avfoundation_implem.h": ctypedef void *camera_t camera_t avf_camera_init(int index, int width, int height) void avf_camera_deinit(camera_t camera) void avf_camera_update(camera_t camera) void avf_camera_start(camera_t camera) void avf_camera_stop(camera_t camera) void avf_camera_get_image(camera_t camera, int *width, int *height, int *rowsize, char **data) from kivy.logger import Logger from kivy.clock import Clock from kivy.graphics.texture import Texture from kivy.core.camera import CameraBase cdef class _AVStorage: cdef camera_t camera def __cinit__(self): self.camera = NULL class CameraAVFoundation(CameraBase): '''Implementation of CameraBase using AVFoundation ''' def __init__(self, **kwargs): self._storage = _AVStorage() super(CameraAVFoundation, self).__init__(**kwargs) def init_camera(self): cdef _AVStorage storage = <_AVStorage>self._storage storage.camera = avf_camera_init( self._index, self.resolution[0], self.resolution[1]) def _update(self, dt): cdef _AVStorage storage = <_AVStorage>self._storage cdef int width, height, rowsize cdef char *data if self.stopped: return avf_camera_update(storage.camera) avf_camera_get_image(storage.camera, &width, &height, &rowsize, &data) if data == NULL: return self._resolution = (width, height) if self._texture is None or self._texture.size != self._resolution: self._texture = Texture.create(self._resolution) self._texture.flip_vertical() self.dispatch('on_load') self._buffer = data[:rowsize * height] self._format = 'bgra' self._copy_to_gpu() def start(self): cdef _AVStorage storage = <_AVStorage>self._storage super(CameraAVFoundation, self).start() Clock.unschedule(self._update) Clock.schedule_interval(self._update, 1 / 30.) avf_camera_start(storage.camera) def stop(self): cdef _AVStorage storage = <_AVStorage>self._storage super(CameraAVFoundation, self).stop() Clock.unschedule(self._update) avf_camera_stop(storage.camera) Kivy-1.9.0/kivy/core/camera/camera_videocapture.py0000664000175000017500000000321412276430013022141 0ustar titotito00000000000000''' VideoCapture Camera: Implement CameraBase with VideoCapture ''' # # TODO: make usage of thread or multiprocess # __all__ = ('CameraVideoCapture', ) from kivy.core.camera import CameraBase from kivy.clock import Clock try: from VideoCapture import Device except: raise class CameraVideoCapture(CameraBase): '''Implementation of CameraBase using VideoCapture ''' def __init__(self, **kwargs): self._device = None super(CameraVideoCapture, self).__init__(**kwargs) self._format = 'bgr' def init_camera(self): # create the device self._device = Device(devnum=self._index, showVideoWindow=0) # set resolution try: self._device.setResolution(self.resolution[0], self.resolution[1]) except: raise Exception('VideoCapture: Resolution not supported') self.fps = 1 / 30. def _update(self, dt): data, camera_width, camera_height = self._device.getBuffer() if self._texture is None: # first update, resize if necessary self.size = camera_width, camera_height # and create texture from kivy.graphics.texture import Texture self._texture = Texture.create(size=self.size, colorfmt='rgb') self.dispatch('on_load') # update buffer self._buffer = data self._copy_to_gpu() def start(self): super(CameraVideoCapture, self).start() Clock.unschedule(self._update) Clock.schedule_interval(self._update, self.fps) def stop(self): super(CameraVideoCapture, self).stop() Clock.unschedule(self._update) Kivy-1.9.0/kivy/core/camera/camera_opencv.py0000664000175000017500000000603312276430013020743 0ustar titotito00000000000000''' OpenCV Camera: Implement CameraBase with OpenCV ''' # # TODO: make usage of thread or multiprocess # __all__ = ('CameraOpenCV') from kivy.logger import Logger from kivy.clock import Clock from kivy.graphics.texture import Texture from kivy.core.camera import CameraBase try: import opencv as cv import opencv.highgui as hg except ImportError: import cv class Hg(object): ''' On OSX, not only are the import names different, but the API also differs. There is no module called 'highgui' but the names are directly available in the 'cv' module. Some of them even have a different names. Therefore we use this proxy object. ''' def __getattr__(self, attr): if attr.startswith('cv'): attr = attr[2:] got = getattr(cv, attr) return got hg = Hg() class CameraOpenCV(CameraBase): '''Implementation of CameraBase using OpenCV ''' def __init__(self, **kwargs): self._device = None super(CameraOpenCV, self).__init__(**kwargs) def init_camera(self): # create the device self._device = hg.cvCreateCameraCapture(self._index) # Set preferred resolution cv.SetCaptureProperty(self._device, cv.CV_CAP_PROP_FRAME_WIDTH, self.resolution[0]) cv.SetCaptureProperty(self._device, cv.CV_CAP_PROP_FRAME_HEIGHT, self.resolution[1]) # and get frame to check if it's ok frame = hg.cvQueryFrame(self._device) # Just set the resolution to the frame we just got, but don't use # self.resolution for that as that would cause an infinite recursion # with self.init_camera (but slowly as we'd have to always get a # frame). self._resolution = (int(frame.width), int(frame.height)) #get fps self.fps = cv.GetCaptureProperty(self._device, cv.CV_CAP_PROP_FPS) if self.fps <= 0: self.fps = 1 / 30. if not self.stopped: self.start() def _update(self, dt): if self.stopped: return if self._texture is None: # Create the texture self._texture = Texture.create(self._resolution) self._texture.flip_vertical() self.dispatch('on_load') try: frame = hg.cvQueryFrame(self._device) self._format = 'bgr' try: self._buffer = frame.imageData except AttributeError: # On OSX there is no imageData attribute but a tostring() # method. self._buffer = frame.tostring() self._copy_to_gpu() except: Logger.exception('OpenCV: Couldn\'t get image from Camera') def start(self): super(CameraOpenCV, self).start() Clock.unschedule(self._update) Clock.schedule_interval(self._update, self.fps) def stop(self): super(CameraOpenCV, self).stop() Clock.unschedule(self._update) Kivy-1.9.0/kivy/core/camera/camera_gi.py0000664000175000017500000001317512276430013020055 0ustar titotito00000000000000''' Gi Camera ========= Implement CameraBase with Gi / Gstreamer, working on both Python 2 and 3 ''' __all__ = ('CameraGi', ) from gi.repository import Gst from kivy.clock import Clock from kivy.graphics.texture import Texture from kivy.core.camera import CameraBase from kivy.support import install_gobject_iteration from kivy.logger import Logger from ctypes import Structure, c_void_p, c_int, string_at from weakref import ref import atexit # initialize the camera/gi. if the older version is used, don't use camera_gi. Gst.init(None) version = Gst.version() if version < (1, 0, 0, 0): raise Exception('Cannot use camera_gi, Gstreamer < 1.0 is not supported.') Logger.info('CameraGi: Using Gstreamer {}'.format( '.'.join(['{}'.format(x) for x in Gst.version()]))) install_gobject_iteration() class _MapInfo(Structure): _fields_ = [ ('memory', c_void_p), ('flags', c_int), ('data', c_void_p)] # we don't care about the rest def _on_cameragi_unref(obj): if obj in CameraGi._instances: CameraGi._instances.remove(obj) class CameraGi(CameraBase): '''Implementation of CameraBase using GStreamer :Parameters: `video_src` : str, default is 'v4l2src' Other tested options are: 'dc1394src' for firewire dc camera (e.g. firefly MV). Any gstreamer video source should potentially work. Theoretically a longer string using "!" can be used describing the first part of a gstreamer pipeline. ''' _instances = [] def __init__(self, **kwargs): self._pipeline = None self._camerasink = None self._decodebin = None self._texturesize = None self._video_src = kwargs.get('video_src', 'v4l2src') wk = ref(self, _on_cameragi_unref) CameraGi._instances.append(wk) super(CameraGi, self).__init__(**kwargs) def init_camera(self): # TODO: This doesn't work when camera resolution is resized at runtime. # There must be some other way to release the camera? if self._pipeline: self._pipeline = None video_src = self._video_src if video_src == 'v4l2src': video_src += ' device=/dev/video%d' % self._index elif video_src == 'dc1394src': video_src += ' camera-number=%d' % self._index if Gst.version() < (1, 0, 0, 0): caps = ('video/x-raw-rgb,red_mask=(int)0xff0000,' 'green_mask=(int)0x00ff00,blue_mask=(int)0x0000ff') pl = ('{} ! decodebin name=decoder ! ffmpegcolorspace ! ' 'appsink name=camerasink emit-signals=True caps={}') else: caps = 'video/x-raw,format=RGB' pl = '{} ! decodebin name=decoder ! videoconvert ! appsink ' + \ 'name=camerasink emit-signals=True caps={}' self._pipeline = Gst.parse_launch(pl.format(video_src, caps)) self._camerasink = self._pipeline.get_by_name('camerasink') self._camerasink.connect('new-sample', self._gst_new_sample) self._decodebin = self._pipeline.get_by_name('decoder') if self._camerasink and not self.stopped: self.start() def _gst_new_sample(self, *largs): sample = self._camerasink.emit('pull-sample') if sample is None: return False self._sample = sample if self._texturesize is None: # try to get the camera image size for pad in self._decodebin.srcpads: s = pad.get_current_caps().get_structure(0) self._texturesize = ( s.get_value('width'), s.get_value('height')) Clock.schedule_once(self._update) return False Clock.schedule_once(self._update) return False def start(self): super(CameraGi, self).start() self._pipeline.set_state(Gst.State.PLAYING) def stop(self): super(CameraGi, self).stop() self._pipeline.set_state(Gst.State.PAUSED) def unload(self): self._pipeline.set_state(Gst.State.NULL) def _update(self, dt): sample, self._sample = self._sample, None if sample is None: return if self._texture is None and self._texturesize is not None: self._texture = Texture.create( size=self._texturesize, colorfmt='rgb') self._texture.flip_vertical() self.dispatch('on_load') # decode sample # read the data from the buffer memory try: buf = sample.get_buffer() result, mapinfo = buf.map(Gst.MapFlags.READ) # We cannot get the data out of mapinfo, using Gst 1.0.6 + Gi 3.8.0 # related bug report: # https://bugzilla.gnome.org/show_bug.cgi?id=6t8663 # ie: mapinfo.data is normally a char*, but here, we have an int # So right now, we use ctypes instead to read the mapinfo ourself. addr = mapinfo.__hash__() c_mapinfo = _MapInfo.from_address(addr) # now get the memory self._buffer = string_at(c_mapinfo.data, mapinfo.size) self._copy_to_gpu() finally: if mapinfo is not None: buf.unmap(mapinfo) @atexit.register def camera_gi_clean(): # if we leave the python process with some video running, we can hit a # segfault. This is forcing the stop/unload of all remaining videos before # exiting the python process. for weakcamera in CameraGi._instances: camera = weakcamera() if isinstance(camera, CameraGi): camera.stop() camera.unload() Kivy-1.9.0/kivy/core/camera/__init__.py0000664000175000017500000001051212462275426017711 0ustar titotito00000000000000''' Camera ====== Core class for acquiring the camera and converting its input into a :class:`~kivy.graphics.texture.Texture`. .. versionchanged:: 1.8.0 There is now 2 distinct Gstreamer implementation: one using Gi/Gst working for both Python 2+3 with Gstreamer 1.0, and one using PyGST working only for Python 2 + Gstreamer 0.10. If you have issue with GStreamer, have a look at :ref:`gstreamer-compatibility` ''' __all__ = ('CameraBase', 'Camera') import sys from kivy.event import EventDispatcher from kivy.logger import Logger from kivy.core import core_select_lib class CameraBase(EventDispatcher): '''Abstract Camera Widget class. Concrete camera classes must implement initialization and frame capturing to a buffer that can be uploaded to the gpu. :Parameters: `index`: int Source index of the camera. `size` : tuple (int, int) Size at which the image is drawn. If no size is specified, it defaults to the resolution of the camera image. `resolution` : tuple (int, int) Resolution to try to request from the camera. Used in the gstreamer pipeline by forcing the appsink caps to this resolution. If the camera doesnt support the resolution, a negotiation error might be thrown. :Events: `on_load` Fired when the camera is loaded and the texture has become available. `on_frame` Fired each time the camera texture is updated. ''' __events__ = ('on_load', 'on_texture') def __init__(self, **kwargs): kwargs.setdefault('stopped', False) kwargs.setdefault('resolution', (640, 480)) kwargs.setdefault('index', 0) self.stopped = kwargs.get('stopped') self._resolution = kwargs.get('resolution') self._index = kwargs.get('index') self._buffer = None self._format = 'rgb' self._texture = None self.capture_device = None kwargs.setdefault('size', self._resolution) super(CameraBase, self).__init__() self.init_camera() if not self.stopped: self.start() def _set_resolution(self, res): self._resolution = res self.init_camera() def _get_resolution(self): return self._resolution resolution = property(lambda self: self._get_resolution(), lambda self, x: self._set_resolution(x), doc='Resolution of camera capture (width, height)') def _set_index(self, x): if x == self._index: return self._index = x self.init_camera() def _get_index(self): return self._x index = property(lambda self: self._get_index(), lambda self, x: self._set_index(x), doc='Source index of the camera') def _get_texture(self): return self._texture texture = property(lambda self: self._get_texture(), doc='Return the camera texture with the latest capture') def init_camera(self): '''Initialise the camera (internal)''' pass def start(self): '''Start the camera acquire''' self.stopped = False def stop(self): '''Release the camera''' self.stopped = True def _update(self, dt): '''Update the camera (internal)''' pass def _copy_to_gpu(self): '''Copy the the buffer into the texture''' if self._texture is None: Logger.debug('Camera: copy_to_gpu() failed, _texture is None !') return self._texture.blit_buffer(self._buffer, colorfmt=self._format) self._buffer = None self.dispatch('on_texture') def on_texture(self): pass def on_load(self): pass # Load the appropriate providers providers = () if sys.platform == 'win32': providers += (('videocapture', 'camera_videocapture', 'CameraVideoCapture'), ) elif sys.platform == 'darwin': providers += (('avfoundation', 'camera_avfoundation', 'CameraAVFoundation'), ) else: #providers += (('gi', 'camera_gi', 'CameraGi'), ) providers += (('pygst', 'camera_pygst', 'CameraPyGst'), ) providers += (('opencv', 'camera_opencv', 'CameraOpenCV'), ) Camera = core_select_lib('camera', (providers)) Kivy-1.9.0/kivy/core/camera/camera_pygst.py0000664000175000017500000000663312276430013020625 0ustar titotito00000000000000''' GStreamer Camera ================ Implement CameraBase with GStreamer, based on PyGST ''' __all__ = ('CameraPyGst', ) from kivy.clock import Clock from kivy.graphics.texture import Texture from kivy.core.camera import CameraBase try: import pygst if not hasattr(pygst, '_gst_already_checked'): pygst.require('0.10') pygst._gst_already_checked = True import gst except: raise # install the gobject iteration from kivy.support import install_gobject_iteration install_gobject_iteration() class CameraPyGst(CameraBase): '''Implementation of CameraBase using GStreamer :Parameters: `video_src` : str, default is 'v4l2src' Other tested options are: 'dc1394src' for firewire dc camera (e.g. firefly MV). Any gstreamer video source should potentially work. Theoretically a longer string using "!" can be used describing the first part of a gstreamer pipeline. ''' def __init__(self, **kwargs): self._pipeline = None self._camerasink = None self._decodebin = None self._texturesize = None self._video_src = kwargs.get('video_src', 'v4l2src') super(CameraPyGst, self).__init__(**kwargs) def init_camera(self): # TODO: This doesn't work when camera resolution is resized at runtime. # There must be some other way to release the camera? if self._pipeline: self._pipeline = None video_src = self._video_src if video_src == 'v4l2src': video_src += ' device=/dev/video%d' % self._index elif video_src == 'dc1394src': video_src += ' camera-number=%d' % self._index GL_CAPS = 'video/x-raw-rgb,red_mask=(int)0xff0000,' + \ 'green_mask=(int)0x00ff00,blue_mask=(int)0x0000ff' pl = '%s ! decodebin name=decoder ! ffmpegcolorspace ! appsink ' + \ 'name=camerasink emit-signals=True caps=%s' self._pipeline = gst.parse_launch(pl % (video_src, GL_CAPS)) self._camerasink = self._pipeline.get_by_name('camerasink') self._camerasink.connect('new-buffer', self._gst_new_buffer) self._decodebin = self._pipeline.get_by_name('decoder') if self._camerasink and not self.stopped: self.start() def _gst_new_buffer(self, *largs): self._format = 'rgb' frame = self._camerasink.emit('pull-buffer') if frame is None: return self._buffer = frame.data if self._texturesize is None: # try to get the camera image size for x in self._decodebin.src_pads(): for cap in x.get_caps(): self._texturesize = (cap['width'], cap['height']) Clock.schedule_once(self._update) return Clock.schedule_once(self._update) def start(self): super(CameraPyGst, self).start() self._pipeline.set_state(gst.STATE_PLAYING) def stop(self): super(CameraPyGst, self).stop() self._pipeline.set_state(gst.STATE_PAUSED) def _update(self, dt): if self._buffer is None: return if self._texture is None and self._texturesize is not None: self._texture = Texture.create( size=self._texturesize, colorfmt='rgb') self._texture.flip_vertical() self.dispatch('on_load') self._copy_to_gpu() Kivy-1.9.0/kivy/core/__init__.py0000664000175000017500000001153112462275426016463 0ustar titotito00000000000000''' Core Abstraction ================ This module defines the abstraction layers for our core providers and their implementations. For further information, please refer to :ref:`architecture` and the :ref:`providers` section of the documentation. In most cases, you shouldn't directly use a library that's already covered by the core abstraction. Always try to use our providers first. In case we are missing a feature or method, please let us know by opening a new Bug report instead of relying on your library. .. warning:: These are **not** widgets! These are just abstractions of the respective functionality. For example, you cannot add a core image to your window. You have to use the image **widget** class instead. If you're really looking for widgets, please refer to :mod:`kivy.uix` instead. ''' import os import sys import traceback import kivy from kivy.logger import Logger class CoreCriticalException(Exception): pass def core_select_lib(category, llist, create_instance=False, base='kivy.core'): if 'KIVY_DOC' in os.environ: return category = category.lower() libs_ignored = [] errs = [] for option, modulename, classname in llist: try: # module activated in config ? try: if option not in kivy.kivy_options[category]: libs_ignored.append(modulename) Logger.debug( '{0}: Provider <{1}> ignored by config'.format( category.capitalize(), option)) continue except KeyError: pass # import module mod = __import__(name='{2}.{0}.{1}'.format( category, modulename, base), globals=globals(), locals=locals(), fromlist=[modulename], level=0) cls = mod.__getattribute__(classname) # ok ! Logger.info('{0}: Provider: {1}{2}'.format( category.capitalize(), option, '({0} ignored)'.format(libs_ignored) if libs_ignored else '')) if create_instance: cls = cls() return cls except ImportError as e: errs.append((option, e, sys.exc_info()[2])) libs_ignored.append(modulename) Logger.debug('{0}: Ignored <{1}> (import error)'.format( category.capitalize(), option)) Logger.trace('', exc_info=e) except CoreCriticalException as e: errs.append((option, e, sys.exc_info()[2])) Logger.error('{0}: Unable to use {1}'.format( category.capitalize(), option)) Logger.error( '{0}: The module raised an important error: {1!r}'.format( category.capitalize(), e.message)) raise except Exception as e: errs.append((option, e, sys.exc_info()[2])) libs_ignored.append(modulename) Logger.trace('{0}: Unable to use {1}'.format( category.capitalize(), option, category)) Logger.trace('', exc_info=e) err = '\n'.join(['{} - {}: {}\n{}'.format(opt, e.__class__.__name__, e, ''.join(traceback.format_tb(tb))) for opt, e, tb in errs]) Logger.critical( '{0}: Unable to find any valuable {0} provider at all!\n{1}'.format( category.capitalize(), err)) def core_register_libs(category, libs, base='kivy.core'): if 'KIVY_DOC' in os.environ: return category = category.lower() kivy_options = kivy.kivy_options[category] libs_loadable = {} libs_ignored = [] for option, lib in libs: # module activated in config ? if option not in kivy_options: Logger.debug('{0}: option <{1}> ignored by config'.format( category.capitalize(), option)) libs_ignored.append(lib) continue libs_loadable[option] = lib libs_loaded = [] for item in kivy_options: try: # import module try: lib = libs_loadable[item] except KeyError: continue __import__(name='{2}.{0}.{1}'.format(category, lib, base), globals=globals(), locals=locals(), fromlist=[lib], level=0) libs_loaded.append(lib) except Exception as e: Logger.trace('{0}: Unable to use <{1}> as loader!'.format( category.capitalize(), option)) Logger.trace('', exc_info=e) libs_ignored.append(lib) Logger.info('{0}: Providers: {1} {2}'.format( category.capitalize(), ', '.join(libs_loaded), '({0} ignored)'.format( ', '.join(libs_ignored)) if libs_ignored else '')) return libs_loaded Kivy-1.9.0/kivy/cache.py0000664000175000017500000001744712504401637015043 0ustar titotito00000000000000''' Cache manager ============= The cache manager can be used to store python objects attached to a unique key. The cache can be controlled in two ways: with a object limit or a timeout. For example, we can create a new cache with a limit of 10 objects and a timeout of 5 seconds:: # register a new Cache Cache.register('mycache', limit=10, timeout=5) # create an object + id key = 'objectid' instance = Label(text=text) Cache.append('mycache', key, instance) # retrieve the cached object instance = Cache.get('mycache', key) If the instance is NULL, the cache may have trashed it because you've not used the label for 5 seconds and you've reach the limit. ''' __all__ = ('Cache', ) from os import environ from kivy.logger import Logger from kivy.clock import Clock class Cache(object): '''See module documentation for more information. ''' _categories = {} _objects = {} @staticmethod def register(category, limit=None, timeout=None): '''Register a new category in the cache with the specified limit. :Parameters: `category` : str Identifier of the category. `limit` : int (optional) Maximum number of objects allowed in the cache. If None, no limit is applied. `timeout` : double (optional) Time after which to delete the object if it has not been used. If None, no timeout is applied. ''' Cache._categories[category] = { 'limit': limit, 'timeout': timeout} Cache._objects[category] = {} Logger.debug( 'Cache: register <%s> with limit=%s, timeout=%s' % (category, str(limit), str(timeout))) @staticmethod def append(category, key, obj, timeout=None): '''Add a new object to the cache. :Parameters: `category` : str Identifier of the category. `key` : str Unique identifier of the object to store. `obj` : object Object to store in cache. `timeout` : double (optional) Time after which to delete the object if it has not been used. If None, no timeout is applied. ''' #check whether obj should not be cached first if getattr(obj, '_no_cache', False): return try: cat = Cache._categories[category] except KeyError: Logger.warning('Cache: category <%s> not exist' % category) return timeout = timeout or cat['timeout'] # FIXME: activate purge when limit is hit #limit = cat['limit'] #if limit is not None and len(Cache._objects[category]) >= limit: # Cache._purge_oldest(category) Cache._objects[category][key] = { 'object': obj, 'timeout': timeout, 'lastaccess': Clock.get_time(), 'timestamp': Clock.get_time()} @staticmethod def get(category, key, default=None): '''Get a object from the cache. :Parameters: `category` : str Identifier of the category. `key` : str Unique identifier of the object in the store. `default` : anything, defaults to None Default value to be returned if the key is not found. ''' try: Cache._objects[category][key]['lastaccess'] = Clock.get_time() return Cache._objects[category][key]['object'] except Exception: return default @staticmethod def get_timestamp(category, key, default=None): '''Get the object timestamp in the cache. :Parameters: `category` : str Identifier of the category. `key` : str Unique identifier of the object in the store. `default` : anything, defaults to None Default value to be returned if the key is not found. ''' try: return Cache._objects[category][key]['timestamp'] except Exception: return default @staticmethod def get_lastaccess(category, key, default=None): '''Get the objects last access time in the cache. :Parameters: `category` : str Identifier of the category. `key` : str Unique identifier of the object in the store. `default` : anything, defaults to None Default value to be returned if the key is not found. ''' try: return Cache._objects[category][key]['lastaccess'] except Exception: return default @staticmethod def remove(category, key=None): '''Purge the cache. :Parameters: `category` : str Identifier of the category. `key` : str (optional) Unique identifier of the object in the store. If this arguement is not supplied, the entire category will be purged. ''' try: if key is not None: del Cache._objects[category][key] else: Cache._objects[category] = {} except Exception: pass @staticmethod def _purge_oldest(category, maxpurge=1): print('PURGE', category) import heapq heap_list = [] for key in Cache._objects[category]: obj = Cache._objects[category][key] if obj['lastaccess'] == obj['timestamp']: continue heapq.heappush(heap_list, (obj['lastaccess'], key)) print('<<<', obj['lastaccess']) n = 0 while n < maxpurge: try: lastaccess, key = heapq.heappop(heap_list) print('=>', key, lastaccess, Clock.get_time()) except Exception: return del Cache._objects[category][key] @staticmethod def _purge_by_timeout(dt): curtime = Clock.get_time() for category in Cache._objects: if category not in Cache._categories: continue timeout = Cache._categories[category]['timeout'] if timeout is not None and dt > timeout: # XXX got a lag ! that may be because the frame take lot of # time to draw. and the timeout is not adapted to the current # framerate. So, increase the timeout by two. # ie: if the timeout is 1 sec, and framerate go to 0.7, newly # object added will be automaticly trashed. timeout *= 2 Cache._categories[category]['timeout'] = timeout continue for key in list(Cache._objects[category].keys())[:]: lastaccess = Cache._objects[category][key]['lastaccess'] objtimeout = Cache._objects[category][key]['timeout'] # take the object timeout if available if objtimeout is not None: timeout = objtimeout # no timeout, cancel if timeout is None: continue if curtime - lastaccess > timeout: del Cache._objects[category][key] @staticmethod def print_usage(): '''Print the cache usage to the console.''' print('Cache usage :') for category in Cache._categories: print(' * %s : %d / %s, timeout=%s' % ( category.capitalize(), len(Cache._objects[category]), str(Cache._categories[category]['limit']), str(Cache._categories[category]['timeout']))) if 'KIVY_DOC_INCLUDE' not in environ: # install the schedule clock for purging Clock.schedule_interval(Cache._purge_by_timeout, 1) Kivy-1.9.0/kivy/vector.py0000664000175000017500000002376412462275426015311 0ustar titotito00000000000000'''Vector ====== The :class:`Vector` represents a 2D vector (x, y). Our implementation is built on top of a Python list. An example of constructing a Vector:: >>> # Construct a point at 82,34 >>> v = Vector(82, 34) >>> v[0] 82 >>> v.x 82 >>> v[1] 34 >>> v.y 34 >>> # Construct by giving a list of 2 values >>> pos = (93, 45) >>> v = Vector(pos) >>> v[0] 93 >>> v.x 93 >>> v[1] 45 >>> v.y 45 Optimized usage --------------- Most of the time, you can use a list for arguments instead of using a Vector. For example, if you want to calculate the distance between 2 points:: a = (10, 10) b = (87, 34) # optimized method print('distance between a and b:', Vector(a).distance(b)) # non-optimized method va = Vector(a) vb = Vector(b) print('distance between a and b:', va.distance(vb)) Vector operators ---------------- The :class:`Vector` supports some numeric operators such as +, -, /:: >>> Vector(1, 1) + Vector(9, 5) [10, 6] >>> Vector(9, 5) - Vector(5, 5) [4, 0] >>> Vector(10, 10) / Vector(2., 4.) [5.0, 2.5] >>> Vector(10, 10) / 5. [2.0, 2.0] You can also use in-place operators:: >>> v = Vector(1, 1) >>> v += 2 >>> v [3, 3] >>> v *= 5 [15, 15] >>> v /= 2. [7.5, 7.5] ''' __all__ = ('Vector', ) import math class Vector(list): '''Vector class. See module documentation for more information. ''' def __init__(self, *largs): if len(largs) == 1: super(Vector, self).__init__(largs[0]) elif len(largs) == 2: super(Vector, self).__init__(largs) else: raise Exception('Invalid vector') def _get_x(self): return self[0] def _set_x(self, x): self[0] = x x = property(_get_x, _set_x) ''':attr:`x` represents the first element in the list. >>> v = Vector(12, 23) >>> v[0] 12 >>> v.x 12 ''' def _get_y(self): return self[1] def _set_y(self, y): self[1] = y y = property(_get_y, _set_y) ''':attr:`y` represents the second element in the list. >>> v = Vector(12, 23) >>> v[1] 23 >>> v.y 23 ''' def __getslice__(self, i, j): try: # use the list __getslice__ method and convert # result to vector return Vector(super(Vector, self).__getslice__(i, j)) except Exception: raise TypeError('vector::FAILURE in __getslice__') def __add__(self, val): return Vector(list(map(lambda x, y: x + y, self, val))) def __iadd__(self, val): if type(val) in (int, float): self.x += val self.y += val else: self.x += val.x self.y += val.y return self def __neg__(self): return Vector([-x for x in self]) def __sub__(self, val): return Vector(list(map(lambda x, y: x - y, self, val))) def __isub__(self, val): if type(val) in (int, float): self.x -= val self.y -= val else: self.x -= val.x self.y -= val.y return self def __mul__(self, val): try: return Vector(list(map(lambda x, y: x * y, self, val))) except Exception: return Vector([x * val for x in self]) def __imul__(self, val): if type(val) in (int, float): self.x *= val self.y *= val else: self.x *= val.x self.y *= val.y return self def __rmul__(self, val): return (self * val) def __truediv__(self, val): try: return Vector(list(map(lambda x, y: x / y, self, val))) except Exception: return Vector([x / val for x in self]) def __div__(self, val): try: return Vector(list(map(lambda x, y: x / y, self, val))) except Exception: return Vector([x / val for x in self]) def __rtruediv__(self, val): try: return Vector(*val) / self except Exception: return Vector(val, val) / self def __rdiv__(self, val): try: return Vector(*val) / self except Exception: return Vector(val, val) / self def __idiv__(self, val): if type(val) in (int, float): self.x /= val self.y /= val else: self.x /= val.x self.y /= val.y return self def length(self): '''Returns the length of a vector. >>> Vector(10, 10).length() 14.142135623730951 >>> pos = (10, 10) >>> Vector(pos).length() 14.142135623730951 ''' return math.sqrt(self[0] ** 2 + self[1] ** 2) def length2(self): '''Returns the length of a vector squared. >>> Vector(10, 10).length2() 200 >>> pos = (10, 10) >>> Vector(pos).length2() 200 ''' return self[0] ** 2 + self[1] ** 2 def distance(self, to): '''Returns the distance between two points. >>> Vector(10, 10).distance((5, 10)) 5. >>> a = (90, 33) >>> b = (76, 34) >>> Vector(a).distance(b) 14.035668847618199 ''' return math.sqrt((self[0] - to[0]) ** 2 + (self[1] - to[1]) ** 2) def distance2(self, to): '''Returns the distance between two points squared. >>> Vector(10, 10).distance2((5, 10)) 25 ''' return (self[0] - to[0]) ** 2 + (self[1] - to[1]) ** 2 def normalize(self): '''Returns a new vector that has the same direction as vec, but has a length of one. >>> v = Vector(88, 33).normalize() >>> v [0.93632917756904444, 0.3511234415883917] >>> v.length() 1.0 ''' if self[0] == 0. and self[1] == 0.: return Vector(0., 0.) return self / self.length() def dot(self, a): '''Computes the dot product of a and b. >>> Vector(2, 4).dot((2, 2)) 12 ''' return self[0] * a[0] + self[1] * a[1] def angle(self, a): '''Computes the angle between a and b, and returns the angle in degrees. >>> Vector(100, 0).angle((0, 100)) -90.0 >>> Vector(87, 23).angle((-77, 10)) -157.7920283010705 ''' angle = -(180 / math.pi) * math.atan2( self[0] * a[1] - self[1] * a[0], self[0] * a[0] + self[1] * a[1]) return angle def rotate(self, angle): '''Rotate the vector with an angle in degrees. >>> v = Vector(100, 0) >>> v.rotate(45) >>> v [70.710678118654755, 70.710678118654741] ''' angle = math.radians(angle) return Vector( (self[0] * math.cos(angle)) - (self[1] * math.sin(angle)), (self[1] * math.cos(angle)) + (self[0] * math.sin(angle))) @staticmethod def line_intersection(v1, v2, v3, v4): ''' Finds the intersection point between the lines (1)v1->v2 and (2)v3->v4 and returns it as a vector object. >>> a = (98, 28) >>> b = (72, 33) >>> c = (10, -5) >>> d = (20, 88) >>> Vector.line_intersection(a, b, c, d) [15.25931928687196, 43.911669367909241] .. warning:: This is a line intersection method, not a segment intersection. For math see: http://en.wikipedia.org/wiki/Line-line_intersection ''' #linear algebar sucks...seriously!! x1, x2, x3, x4 = float(v1[0]), float(v2[0]), float(v3[0]), float(v4[0]) y1, y2, y3, y4 = float(v1[1]), float(v2[1]), float(v3[1]), float(v4[1]) u = (x1 * y2 - y1 * x2) v = (x3 * y4 - y3 * x4) denom = (x1 - x2) * (y3 - y4) - (y1 - y2) * (x3 - x4) if denom == 0: return None px = (u * (x3 - x4) - (x1 - x2) * v) / denom py = (u * (y3 - y4) - (y1 - y2) * v) / denom return Vector(px, py) @staticmethod def segment_intersection(v1, v2, v3, v4): ''' Finds the intersection point between segments (1)v1->v2 and (2)v3->v4 and returns it as a vector object. >>> a = (98, 28) >>> b = (72, 33) >>> c = (10, -5) >>> d = (20, 88) >>> Vector.segment_intersection(a, b, c, d) None >>> a = (0, 0) >>> b = (10, 10) >>> c = (0, 10) >>> d = (10, 0) >>> Vector.segment_intersection(a, b, c, d) [5, 5] ''' #Yaaay! I love linear algebra applied within the realms of geometry. x1, x2, x3, x4 = float(v1[0]), float(v2[0]), float(v3[0]), float(v4[0]) y1, y2, y3, y4 = float(v1[1]), float(v2[1]), float(v3[1]), float(v4[1]) #This is mostly the same as the line_intersection u = (x1 * y2 - y1 * x2) v = (x3 * y4 - y3 * x4) denom = (x1 - x2) * (y3 - y4) - (y1 - y2) * (x3 - x4) if denom == 0: return None px = (u * (x3 - x4) - (x1 - x2) * v) / denom py = (u * (y3 - y4) - (y1 - y2) * v) / denom #Here are the new bits c1 = (x1 <= px <= x2) or (x2 <= px <= x1) c2 = (y1 <= py <= y2) or (y2 <= py <= y1) c3 = (x3 <= px <= x4) or (x4 <= px <= x3) c4 = (y3 <= py <= y4) or (y4 <= py <= y3) if (c1 and c2) and (c3 and c4): return Vector(px, py) else: return None @staticmethod def in_bbox(point, a, b): '''Return True if `point` is in the bounding box defined by `a` and `b`. >>> bmin = (0, 0) >>> bmax = (100, 100) >>> Vector.in_bbox((50, 50), bmin, bmax) True >>> Vector.in_bbox((647, -10), bmin, bmax) False ''' return ((point[0] <= a[0] and point[0] >= b[0] or point[0] <= b[0] and point[0] >= a[0]) and (point[1] <= a[1] and point[1] >= b[1] or point[1] <= b[1] and point[1] >= a[1])) Kivy-1.9.0/kivy/data/0000775000175000017500000000000012507221737014326 5ustar titotito00000000000000Kivy-1.9.0/kivy/data/style.kv0000664000175000017500000011532112502235633016026 0ustar titotito00000000000000#:kivy 1.0