PKk{Hzpystray/__init__.py# coding=utf-8 # pystray # Copyright (C) 2016 Moses Palmér # # This program is free software: you can redistribute it and/or modify it under # the terms of the GNU Lesser General Public License as published by the Free # Software Foundation, either version 3 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 Lesser General Public License for more # details. # # You should have received a copy of the GNU Lesser General Public License # along with this program. If not, see . import os import sys if os.environ.get('__PYSTRAY_GENERATE_DOCUMENTATION') == 'yes': from ._base import Icon else: Icon = None if sys.platform == 'darwin': if not Icon: from ._darwin import Icon elif sys.platform == 'win32': if not Icon: from ._win32 import Icon else: try: if not Icon: from ._xorg import Icon except: pass if not Icon: raise ImportError('this platform is not supported') PK-lxHtHpystray/_darwin.py# coding=utf-8 # pystray # Copyright (C) 2016 Moses Palmér # # This program is free software: you can redistribute it and/or modify it under # the terms of the GNU Lesser General Public License as published by the Free # Software Foundation, either version 3 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 Lesser General Public License for more # details. # # You should have received a copy of the GNU Lesser General Public License # along with this program. If not, see . import io import AppKit import Foundation import objc import PIL from . import _base class Icon(_base.Icon): #: The selector for the button action _ACTION_SELECTOR = 'activate:sender' def __init__(self, *args, **kwargs): super(Icon, self).__init__(*args, **kwargs) #: The NSImage version of the icon self._icon_image = None def _show(self): self._assert_image() self._update_title() self._status_item.button().setHidden_(False) def _hide(self): self._status_item.button().setHidden_(True) def _update_icon(self): self._icon_image = None if self.visible: self._assert_image() def _update_title(self): self._status_item.button().setToolTip_(self.title) def _run(self): # Make sure there is an NSApplication instance self._app = AppKit.NSApplication.sharedApplication() # Make sure we have a delegate to handle the acttion events self._delegate = IconDelegate.alloc().init() self._delegate.icon = self self._status_bar = AppKit.NSStatusBar.systemStatusBar() self._status_item = self._status_bar.statusItemWithLength_( AppKit.NSVariableStatusItemLength) self._status_item.button().setTarget_(self._delegate) self._status_item.button().setAction_(self._ACTION_SELECTOR) self._status_item.button().setHidden_(True) # Notify the setup callback self._mark_ready() try: self._app.run() finally: self._status_bar.removeStatusItem_(self._status_item) def _stop(self): self._app.stop_(self._app) # Post a dummy event; stop_ will only set a flag in NSApp, so it will # not terminate until an event has been processed event = getattr( AppKit.NSEvent, 'otherEventWithType_' 'location_' 'modifierFlags_' 'timestamp_' 'windowNumber_' 'context_' 'subtype_' 'data1_' 'data2_')( AppKit.NSApplicationDefined, AppKit.NSPoint(0, 0), 0, 0.0, 0, None, 0, 0, 0) self._app.postEvent_atStart_(event, False) def _assert_image(self): """Asserts that the cached icon image exists. """ thickness = self._status_bar.thickness() size = (int(thickness), int(thickness)) if self._icon_image and self._icon_image.size() == size: return if self._icon.size == size: source = self._icon else: source = PIL.Image.new( 'RGB', size) source.paste(self._icon.resize( size, PIL.Image.ANTIALIAS)) # Convert the PIL image to an NSImage b = io.BytesIO() source.save(b, 'png') data = Foundation.NSData(b.getvalue()) self._icon_image = AppKit.NSImage.alloc().initWithData_(data) self._status_item.button().setImage_(self._icon_image) class IconDelegate(Foundation.NSObject): @objc.namedSelector(Icon._ACTION_SELECTOR) def activate(self, sender): self.icon.on_activate(self.icon) PKmxHŽ^,^,pystray/_win32.py# coding=utf-8 # pystray # Copyright (C) 2016 Moses Palmér # # This program is free software: you can redistribute it and/or modify it under # the terms of the GNU Lesser General Public License as published by the Free # Software Foundation, either version 3 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 Lesser General Public License for more # details. # # You should have received a copy of the GNU Lesser General Public License # along with this program. If not, see . import ctypes import os import six import sys import threading import tempfile from ctypes import windll, wintypes from six.moves import queue from . import _base class Icon(_base.Icon): _HWND_TO_ICON = {} def __init__(self, *args, **kwargs): super(Icon, self).__init__(*args, **kwargs) self._icon_handle = None self._hwnd = None # This is a mapping from win32 event codes to handlers used by the # mainloop self._message_handlers = { WM_STOP: self._on_stop, WM_NOTIFY: self._on_notify} # Run the mainloop in a separate thread self._queue = queue.Queue() self._thread = threading.Thread(target=self._mainloop) self._thread.daemon = True self._thread.start() # Wait for the mainloop thread to initialise, and reraise any errors result = self._queue.get() if result is not True: six.reraise(*result) def __del__(self): self._stop() self._thread.join() def _show(self): self._assert_icon_handle() self._message( NOTIFYICONDATA.NIM_ADD, NOTIFYICONDATA.NIF_MESSAGE | NOTIFYICONDATA.NIF_ICON | NOTIFYICONDATA.NIF_TIP, uCallbackMessage=WM_NOTIFY, hIcon=self._icon_handle, szTip=self.title) def _hide(self): self._message( NOTIFYICONDATA.NIM_DELETE, 0) def _update_icon(self): self._icon_handle = None self._assert_icon_handle() self._message( NOTIFYICONDATA.NIM_MODIFY, NOTIFYICONDATA.NIF_ICON, hIcon=self._icon_handle) def _update_title(self): self._message( NOTIFYICONDATA.NIM_MODIFY, NOTIFYICONDATA.NIF_TIP, szTip=self.title) def _run(self): self._mark_ready() # Wait for the loop to terminate self._thread.join() def _stop(self): PostMessage(self._hwnd, WM_STOP, 0, 0) def _mainloop(self): """The body of the main loop thread. This method retrieves all events from *Windows* and makes sure to dispatch clicks. """ # Create the message loop msg = wintypes.MSG() lpmsg = ctypes.byref(msg) PeekMessage(lpmsg, None, 0x0400, 0x0400, PM_NOREMOVE) try: atom = self._register_class() self._hwnd = self._create_window(atom) self._HWND_TO_ICON[self._hwnd] = self except: self._queue.put(sys.exc_info()) return # Tell the calling thread that we are ready self._queue.put(True) # Pump messages try: while True: msg = wintypes.MSG() lpmsg = ctypes.byref(msg) while True: r = GetMessage(lpmsg, None, 0, 0) if not r: break elif r == -1: break else: TranslateMessage(lpmsg) DispatchMessage(lpmsg) # Make sure the icon is removed self._hide() except: # TODO: Report errors pass finally: try: self._hide() del self._HWND_TO_ICON[self._hwnd] except: pass DestroyWindow(self._hwnd) self._unregister_class(atom) def _on_stop(self, wparam, lparam): """Handles ``WM_STOP``. This method posts a quit message, causing the mainloop thread to terminate. """ PostQuitMessage(0) def _on_notify(self, wparam, lparam): """Handles ``WM_NOTIFY``. This method calls the activate callback. It will only be called for left button clicks. """ if lparam == WM_LBUTTONDOWN: self.on_activate(self) def _create_window(self, atom): """Creates the system tray icon window. :param atom: The window class atom. :return: a window """ hwnd = CreateWindowEx( 0, atom, None, 0, 0, 0, 0, 0, HWND_MESSAGE, None, GetModuleHandle(None), None) if not hwnd: raise ctypes.WinError(wintypes.get_last_error()) else: return hwnd def _message(self, code, flags, **kwargs): """Sends a message the the systray icon. This method adds ``cbSize``, ``hWnd``, ``hId`` and ``uFlags`` to the message data. :param int message: The message to send. This should be one of the ``NIM_*`` constants. :param int flags: The value of ``NOTIFYICONDATA::uFlags``. :param kwargs: Data for the :class:`NOTIFYICONDATA` object. """ r = Shell_NotifyIcon(code, ctypes.byref(NOTIFYICONDATA( cbSize=ctypes.sizeof(NOTIFYICONDATA), hWnd=self._hwnd, hID=id(self), uFlags=flags, **kwargs))) if not r: raise ctypes.WinError(wintypes.get_last_error()) def _assert_icon_handle(self): """Asserts that the cached icon handle exists. """ if self._icon_handle: return fd, icon_path = tempfile.mkstemp('.ico') try: with os.fdopen(fd, 'wb') as f: self._icon.save(f, format='ICO') hicon = LoadImage( None, wintypes.LPCWSTR(icon_path), IMAGE_ICON, 0, 0, LR_DEFAULTSIZE | LR_LOADFROMFILE) if not hicon: raise ctypes.WinError(wintypes.get_last_error()) else: self._icon_handle = hicon finally: try: os.unlink(icon_path) except: pass def _register_class(self): """Registers the systray window class. :return: the class atom """ window_class = WNDCLASSEX( cbSize=ctypes.sizeof(WNDCLASSEX), style=0, lpfnWndProc=_dispatcher, cbClsExtra=0, cbWndExtra=0, hInstance=GetModuleHandle(None), hIcon=None, hCursor=None, hbrBackground=COLOR_WINDOW + 1, lpszMenuName=None, lpszClassName='%s%dSystemTrayIcon' % (self.name, id(self)), hIconSm=None) atom = RegisterClassEx(ctypes.byref(window_class)) if not atom: raise ctypes.WinError(wintypes.get_last_error()) else: return atom def _unregister_class(self, atom): """Unregisters the systray window class. :param atom: The class atom returned by :meth:`_register_class`. """ r = UnregisterClassEx(atom, GetModuleHandle(None)) if not r: raise ctypes.WinError(wintypes.get_last_error()) WM_CREATE = 0x0001 WM_NCCREATE = 0x0081 WM_LBUTTONDOWN = 0x0201 WM_USER = 0x400 WM_STOP = WM_USER + 10 WM_NOTIFY = WM_USER + 11 HWND_MESSAGE = -3L PM_NOREMOVE = 0 COLOR_WINDOW = 5 IMAGE_ICON = 1 LR_LOADFROMFILE = 0x00000010 LR_DEFAULTSIZE = 0x00000040 NOTIFYICON_VERSION = 3 Shell_NotifyIcon = windll.shell32.Shell_NotifyIconW GetModuleHandle = windll.kernel32.GetModuleHandleW RegisterClassEx = windll.user32.RegisterClassExW CreateWindowEx = windll.user32.CreateWindowExW CreateWindowEx.argtypes = [ wintypes.DWORD, wintypes.LPVOID, wintypes.LPCWSTR, wintypes.DWORD, wintypes.INT, wintypes.INT, wintypes.INT, wintypes.INT, wintypes.HWND, wintypes.HMENU, wintypes.HINSTANCE, wintypes.LPVOID] CreateWindowEx.restype = wintypes.HWND DestroyWindow = windll.user32.DestroyWindow UnregisterClassEx = windll.user32.UnregisterClassW LoadImage = windll.user32.LoadImageW DispatchMessage = windll.user32.DispatchMessageW GetMessage = windll.user32.GetMessageW PeekMessage = windll.user32.PeekMessageW PostMessage = windll.user32.PostMessageW PostQuitMessage = windll.user32.PostQuitMessage TranslateMessage = windll.user32.TranslateMessage WNDPROC = wintypes.WINFUNCTYPE( wintypes.HRESULT, wintypes.HWND, wintypes.UINT, wintypes.WPARAM, wintypes.LPARAM) class WNDCLASSEX(ctypes.Structure): _fields_ = [ ('cbSize', wintypes.UINT), ('style', wintypes.UINT), ('lpfnWndProc', WNDPROC), ('cbClsExtra', wintypes.INT), ('cbWndExtra', wintypes.INT), ('hInstance', wintypes.HANDLE), ('hIcon', wintypes.HICON), ('hCursor', wintypes.HANDLE), ('hbrBackground', wintypes.HBRUSH), ('lpszMenuName', wintypes.LPCWSTR), ('lpszClassName', wintypes.LPCWSTR), ('hIconSm', wintypes.HICON)] @WNDPROC def _dispatcher(hwnd, uMsg, wParam, lParam): try: return int(Icon._HWND_TO_ICON[hwnd]._message_handlers.get( uMsg, lambda w, l: 0)(wParam, lParam)) except KeyError: # Icon._HWND_TO_ICON[hwnd] is not yet set; this message is sent during # window creation, so we assume it is WM_CREATE or WM_NCCREATE and # return TRUE return 1 except: # TODO: Report return 0 class NOTIFYICONDATA(ctypes.Structure): class VERSION_OR_TIMEOUT(ctypes.Union): _fields_ = [ ('uTimeout', wintypes.UINT), ('uVersion', wintypes.UINT)] NIF_MESSAGE = 0x00000001 NIF_ICON = 0x00000002 NIF_TIP = 0x00000004 NIF_STATE = 0x00000008 NIF_INFO = 0x00000010 NIF_GUID = 0x00000020 NIF_REALTIME = 0x00000040 NIF_SHOWTIP = 0x00000080 NIM_ADD = 0x00000000 NIM_MODIFY = 0x00000001 NIM_DELETE = 0x00000002 NIM_SETFOCUS = 0x00000003 NIM_SETVERSION = 0x00000004 _fields_ = [ ('cbSize', wintypes.DWORD), ('hWnd', wintypes.HWND), ('uID', wintypes.UINT), ('uFlags', wintypes.UINT), ('uCallbackMessage', wintypes.UINT), ('hIcon', wintypes.HICON), ('szTip', wintypes.WCHAR * 64), ('dwState', wintypes.DWORD), ('dwStateMask', wintypes.DWORD), ('szInfo', wintypes.WCHAR * 256), ('version_or_timeout', VERSION_OR_TIMEOUT), ('szInfoTitle', wintypes.WCHAR * 64), ('dwInfoFlags', wintypes.DWORD), ('guidItem', wintypes.LPVOID), ('hBalloonIcon', wintypes.HICON)] _anonymous_ = [ 'version_or_timeout'] PKmxH;P[9[9pystray/_xorg.py# coding=utf-8 # pystray # Copyright (C) 2016 Moses Palmér # # This program is free software: you can redistribute it and/or modify it under # the terms of the GNU Lesser General Public License as published by the Free # Software Foundation, either version 3 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 Lesser General Public License for more # details. # # You should have received a copy of the GNU Lesser General Public License # along with this program. If not, see . import contextlib import functools import six import sys import threading import types import PIL import Xlib.display import Xlib.XK from six.moves import queue from . import _base # Create a display to verify that we have an X connection display = Xlib.display.Display() display.close() del display class XError(Exception): """An error that is thrown at the end of a code block managed by a :func:`display_manager` if an *X* error occurred. """ pass @contextlib.contextmanager def display_manager(display): """Traps *X* errors and raises an :class:``XError`` at the end if any error occurred. This handler also ensures that the :class:`Xlib.display.Display` being managed is sync'd. :param Xlib.display.Display display: The *X* display. """ errors = [] def handler(*args): errors.append(args) old_handler = display.set_error_handler(handler) try: yield display.sync() finally: display.set_error_handler(old_handler) if errors: raise XError(errors) class Icon(_base.Icon): _XEMBED_VERSION = 0 _XEMBED_MAPPED = 1 _SYSTEM_TRAY_REQUEST_DOCK = 0 def __init__(self, *args, **kwargs): super(Icon, self).__init__(*args, **kwargs) #: The properly scaled version of the icon image self._icon_data = None #: The window currently embedding this icon self._systray_manager = None # This is a mapping from X event codes to handlers used by the mainloop self._message_handlers = { Xlib.X.ButtonPress: self._on_button_press, Xlib.X.ConfigureNotify: self._on_expose, Xlib.X.DestroyNotify: self._on_destroy_notify, Xlib.X.Expose: self._on_expose} # Run the mainloop in a separate thread self._queue = queue.Queue() self._thread = threading.Thread(target=self._mainloop) self._thread.daemon = True self._thread.start() # Wait for the mainloop thread to initialise, and reraise any errors result = self._queue.get() if result is not True: six.reraise(*result) def __del__(self): try: # Destroying the window will stop the mainloop thread self._stop() self._thread.join() finally: self._display.close() def _show(self): """The implementation of :meth:`_show`, executed in the mainloop thread. """ try: self._assert_docked() except AssertionError: # There is no systray selection owner, so we cannot dock; # ignore and dock later pass def _hide(self): """The implementation of :meth:`_hide`, executed in the mainloop thread. """ if self._systray_manager: self._undock_window() def _update_icon(self): """The implementation of :meth:`_update_icon`, executed in the mainloop thread. """ try: self._assert_docked() except AssertionError: # If we are not docked, we cannot update the icon return # Setting _icon_data to None will force regeneration of the icon # from _icon self._icon_data = None self._draw() def _update_title(self): """The implementation of :meth:`_update_title`, executed in the mainloop thread. """ # The title is the window name self._window.set_wm_name(self.title) def _run(self): self._mark_ready() # Wait for the loop to terminate self._thread.join() def _stop(self): """Stops the mainloop. """ self._window.destroy() self._display.flush() def _mainloop(self): """The body of the main loop thread. This method retrieves all events from *X* and makes sure to dispatch clicks. """ # Connect to X self._display = Xlib.display.Display() try: with display_manager(self._display): # Create the atoms; some of these are required when creating # the window self._create_atoms() # Create the window and get a graphics context self._window = self._create_window() self._gc = self._window.create_gc() # Rewrite the platform implementation methods to ensure they # are executed in this thread self._rewrite_implementation( self._show, self._hide, self._update_icon, self._update_title, self._stop) except: # Pass the error to the calling thread self._queue.put(sys.exc_info()) return # Tell the calling thread that we are ready self._queue.put(True) try: for event in self._events(): # If the systray window is destroyed, the icon has been hidden if (event.type == Xlib.X.DestroyNotify and event.window == self._window): break self._message_handlers.get(event.type, lambda e: None)(event) except: # TODO: Report errors pass def _on_button_press(self, event): """Handles ``Xlib.X.ButtonPress``. This method calls the activate callback. It will only be called for left button clicks. """ if event.detail == 1: self.on_activate(self) def _on_destroy_notify(self, event): """Handles ``Xlib.X.DestroyNotify``. This method clears :attr:`_systray_manager` if it is destroyed. """ # Handle only the systray manager window; the destroy notification # for our own window is handled in the event loop if event.window.id != self._systray_manager.id: return # Try to locate a new systray selection owner self._systray_manager = None try: self._assert_docked() except AssertionError: # There is no new selection owner; we must retry later pass def _on_expose(self, event): """Handles ``Xlib.X.ConfigureNotify`` and ``Xlib.X.Expose``. This method redraws the window. """ # Redraw only our own window if event.window.id != self._window.id: return self._draw() def _create_atoms(self): """Creates the atoms used by the *XEMBED* and *systray* specifications. """ self._xembed_info = self._display.intern_atom( '_XEMBED_INFO') self._net_system_tray_sx = self._display.intern_atom( '_NET_SYSTEM_TRAY_S%d' % ( self._display.get_default_screen())) self._net_system_tray_opcode = self._display.intern_atom( '_NET_SYSTEM_TRAY_OPCODE') def _rewrite_implementation(self, *args): """Overwrites the platform implementation methods with ones causing the mainloop to execute the code instead. :param args: The methods to rewrite. """ def dispatcher(original, atom): @functools.wraps(original) def inner(self): # Just invoke the method if we are currently in the correct # thread if threading.current_thread() == self._thread: original() else: self._send_message(self._window, atom) self._display.flush() # Wait for the mainloop to execute the actual method, wait # for completion and reraise any exceptions result = self._queue.get() if result is not True: six.reraise(*result) return types.MethodType(inner, self) def wrapper(original): @functools.wraps(original) def inner(): try: original() self._queue.put(True) except: self._queue.put(sys.exc_info()) return inner def on_client_message(event): handlers.get(event.client_type, lambda: None)() # Create the atoms and a mapping from atom to actual implementation atoms = [ self._display.intern_atom('_PYSTRAY_%s' % original.__name__.upper()) for original in args] handlers = { atom: wrapper(original) for original, atom in zip(args, atoms)} # Replace the old methods for original, atom in zip(args, atoms): setattr( self, original.__name__, dispatcher(original, atom)) # Make sure that we handle ClientMessage self._message_handlers[Xlib.X.ClientMessage] = on_client_message def _create_window(self): """Creates the system tray icon window. :return: a window """ with display_manager(self._display): # Create the window screen = self._display.screen() window = screen.root.create_window( -1, -1, 1, 1, 0, screen.root_depth, event_mask=Xlib.X.ExposureMask | Xlib.X.StructureNotifyMask, window_class=Xlib.X.InputOutput) window.set_wm_class('%sSystemTrayIcon' % self.name, self.name) window.set_wm_name(self.title) # Enable XEMBED for the window window.change_property(self._xembed_info, self._xembed_info, 32, [ self._XEMBED_VERSION, self._XEMBED_MAPPED]) return window def _draw(self): """Paints the icon image. """ try: dim = self._window.get_geometry() self._assert_icon_data(dim.width, dim.height) self._window.put_pil_image(self._gc, 0, 0, self._icon_data) except Xlib.error.BadDrawable: # The window has been destroyed; ignore pass def _assert_icon_data(self, width, height): """Asserts that the cached icon data matches the requested dimensions. If no cached icon data exists, or its dimensions do not match the requested size, the image is generated. :param int width: The requested width. :param int height: The requested height. """ if self._icon_data and self._icon_data.size == (width, height): return self._icon_data = PIL.Image.new( 'RGB', (width, height)) self._icon_data.paste(self._icon.resize( (width, height), PIL.Image.ANTIALIAS)) def _assert_docked(self): """Asserts that the icon is docked in the systray. :raises AssertionError: if the window is not docked """ self._dock_window() assert self._systray_manager def _dock_window(self): """Docks the window in the systray. """ # Get the selection owner systray_manager = self._get_systray_manager() if not systray_manager: return self._systray_manager = systray_manager # Request being docked self._send_message( self._systray_manager, self._net_system_tray_opcode, self._SYSTEM_TRAY_REQUEST_DOCK, self._window.id) # Make sure we get destroy notifications systray_manager.change_attributes( event_mask=Xlib.X.StructureNotifyMask) self._display.flush() self._systray_manager = systray_manager def _undock_window(self): """Undocks the window from the systray. """ # Make sure we get do not get any notifications try: self._systray_manager.change_attributes( event_mask=Xlib.X.NoEventMask) except XError: # The systray manager may have been destroyed pass self._window.unmap() self._window.reparent(self._display.screen().root, 0, 0) self._systray_manager = None self._display.flush() def _get_systray_manager(self): """Returns the *X* window that owns the systray selection. :return: the window owning the selection, or ``None`` if no window owns it """ self._display.grab_server() try: systray_manager = self._display.get_selection_owner( self._net_system_tray_sx) finally: self._display.ungrab_server() self._display.flush() if systray_manager != Xlib.X.NONE: return self._display.create_resource_object( 'window', systray_manager.id) def _send_message(self, window, client_type, l0=0, l1=0, l2=0, l3=0): """Sends a generic client message message. This method does not trap *X* errors; that is up to the caller. :param int l0: Message specific data. :param int l1: Message specific data. :param int l2: Message specific data. :param int l3: Message specific data. """ self._display.send_event( window, Xlib.display.event.ClientMessage( type=Xlib.X.ClientMessage, client_type=client_type, window=window.id, data=( 32, (Xlib.X.CurrentTime, l0, l1, l2, l3))), event_mask=Xlib.X.NoEventMask) def _events(self): """Yields all events. """ while True: event = self._display.next_event() if not event: break else: yield event PKlxHL%pystray/_base.py# coding=utf-8 # pystray # Copyright (C) 2016 Moses Palmér # # This program is free software: you can redistribute it and/or modify it under # the terms of the GNU Lesser General Public License as published by the Free # Software Foundation, either version 3 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 Lesser General Public License for more # details. # # You should have received a copy of the GNU Lesser General Public License # along with this program. If not, see . import threading from six.moves import queue class Icon(object): """A representation of a system tray icon. The icon is initially hidden. Call :meth:`show` to show it. :param str name: The name of the icon. This is used by the system to identify the icon. :param icon: The icon to use. If this is specified, it must be a :class:`PIL.Image.Image` instance. :param str title: A short title for the icon. :param callable on_activate: A callback for when the system tray icon is activated. It is passed the icon as its sole argument. """ def __init__(self, name, icon=None, title=None, on_activate=None): self._name = name if icon: self._icon = icon else: self._icon = None if title: self._title = title else: self._title = '' self._visible = False if on_activate: self.on_activate = on_activate else: self.on_activate = lambda icon: None self.__queue = queue.Queue() def __del__(self): if self.visible: self._hide() @property def name(self): """The name passed to the constructor. """ return self._name @property def icon(self): """The current icon. Setting this to a falsy value will hide the icon. Setting this to an image while the icon is hidden has no effect until the icon is shown using :meth:`show`. """ return self._icon @icon.setter def icon(self, value): self._icon = value if value: if self.visible: self._update_icon() else: if self.visible: self.visible = False @property def title(self): """The current icon title. """ return self._title @title.setter def title(self, value): if value != self._title: self._title = value if self.visible: self._update_title() @property def visible(self): """Whether the icon is currently visible. :raises ValueError: if set to ``True`` and no icon image has been set """ return self._visible @visible.setter def visible(self, value): if self._visible == value: return if value: if not self._icon: raise ValueError('cannot show icon without icon data') self._show() self._visible = True else: self._hide() self._visible = False def run(self, setup=None): """Enters the loop handling events for the icon. This method is blocking until :meth:`stop` is called. It *must* be called from the main thread. :param callable setup: An optional callback to execute in a separate thread once the loop has started. It is passed the icon as its sole argument. """ def setup_handler(): self.__queue.get() if setup: setup(self) threading.Thread(target=setup_handler).start() self._run() def stop(self): """Stops the loop handling events for the icon. This method can be called either from the ``on_activate`` callback or from a different thread. """ self._stop() def _mark_ready(self): """Marks the icon as ready. The setup callback passed to :meth:`run` will not be called until this method has been invoked. """ self.__queue.put(True) def _show(self): """The implementation of the :meth:`show` method. This is a platform dependent implementation. """ raise NotImplementedError() def _hide(self): """The implementation of the :meth:`hide` method. This is a platform dependent implementation. """ raise NotImplementedError() def _update_icon(self): """Updates the image for an already shown icon. This is a platform dependent implementation. """ raise NotImplementedError() def _update_title(self): """Updates the title for an already shown icon. This is a platform dependent implementation. """ raise NotImplementedError() def _run(self): """Runs the event loop. This method must call :meth:`_mark_ready` once the loop is ready. This is a platform dependent implementation. """ raise NotImplementedError() def _stop(self): """Stops the event loop. This is a platform dependent implementation. """ raise NotImplementedError() PKk{H#1vCCpystray/_info.py# coding: utf8 __author__ = u'Moses Palmér' __version__ = (0, 2) PK)\H0Fpynput/__init__.py# coding=utf-8 # pynput # Copyright (C) 2015-2016 Moses Palmér # # This program is free software: you can redistribute it and/or modify it under # the terms of the GNU Lesser General Public License as published by the Free # Software Foundation, either version 3 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 Lesser General Public License for more # details. # # You should have received a copy of the GNU Lesser General Public License # along with this program. If not, see . from . import keyboard from . import mouse PK)\H6cCCpynput/_info.py# coding: utf8 __author__ = u'Moses Palmér' __version__ = (1, 0) PK)\HDwX((pynput/keyboard/__init__.py# coding=utf-8 # pynput # Copyright (C) 2015-2016 Moses Palmér # # This program is free software: you can redistribute it and/or modify it under # the terms of the GNU Lesser General Public License as published by the Free # Software Foundation, either version 3 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 Lesser General Public License for more # details. # # You should have received a copy of the GNU Lesser General Public License # along with this program. If not, see . import os import sys if os.environ.get('__PYNPUT_GENERATE_DOCUMENTATION') == 'yes': from ._base import KeyCode, Key, Controller, Listener else: KeyCode = None Key = None Controller = None Listener = None if sys.platform == 'darwin': if not KeyCode and not Key and not Controller and not Listener: from ._darwin import KeyCode, Key, Controller, Listener elif sys.platform == 'win32': if not KeyCode and not Key and not Controller and not Listener: from ._win32 import KeyCode, Key, Controller, Listener else: if not KeyCode and not Key and not Controller and not Listener: try: from ._xorg import KeyCode, Key, Controller, Listener except: pass if not KeyCode or not Key or not Controller or not Listener: raise ImportError('this platform is not supported') PK)\H~6J]]pynput/keyboard/_darwin.py# coding=utf-8 # pynput # Copyright (C) 2015-2016 Moses Palmér # # This program is free software: you can redistribute it and/or modify it under # the terms of the GNU Lesser General Public License as published by the Free # Software Foundation, either version 3 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 Lesser General Public License for more # details. # # You should have received a copy of the GNU Lesser General Public License # along with this program. If not, see . import enum import Quartz from pynput._util.darwin import * from . import _base class KeyCode(_base.KeyCode): def _event(self, modifiers, mapping, is_pressed): """This key as a *Quartz* event. :param set modifiers: The currently active modifiers. :param mapping: The current keyboard mapping. :param bool is_press: Whether to generate a press event. :return: a *Quartz* event """ vk = self.vk or mapping.get(self.char, 0) result = Quartz.CGEventCreateKeyboardEvent(None, vk, is_pressed) Quartz.CGEventSetFlags( result, (Quartz.kCGEventFlagMaskAlternate if Key.alt in modifiers else 0) | (Quartz.kCGEventFlagMaskCommand if Key.cmd in modifiers else 0) | (Quartz.kCGEventFlagMaskControl if Key.ctrl in modifiers else 0) | (Quartz.kCGEventFlagMaskShift if Key.shift in modifiers else 0)) if not vk and self.char is not None: Quartz.CGEventKeyboardSetUnicodeString( result, len(self.char), self.char) return result class Key(enum.Enum): # Default keys alt = KeyCode.from_vk(0x3A) alt_l = KeyCode.from_vk(0x3A) alt_r = KeyCode.from_vk(0x3D) alt_gr = KeyCode.from_vk(0x3D) backspace = KeyCode.from_vk(0x33) caps_lock = KeyCode.from_vk(0x39) cmd = KeyCode.from_vk(0x37) cmd_l = KeyCode.from_vk(0x37) cmd_r = KeyCode.from_vk(0x36) ctrl = KeyCode.from_vk(0x3B) ctrl_l = KeyCode.from_vk(0x3B) ctrl_r = KeyCode.from_vk(0x3E) delete = KeyCode.from_vk(0x75) down = KeyCode.from_vk(0x7D) end = KeyCode.from_vk(0x77) enter = KeyCode.from_vk(0x24) esc = KeyCode.from_vk(0x35) f1 = KeyCode.from_vk(0x7A) f2 = KeyCode.from_vk(0x78) f3 = KeyCode.from_vk(0x63) f4 = KeyCode.from_vk(0x76) f5 = KeyCode.from_vk(0x60) f6 = KeyCode.from_vk(0x61) f7 = KeyCode.from_vk(0x62) f8 = KeyCode.from_vk(0x64) f9 = KeyCode.from_vk(0x65) f10 = KeyCode.from_vk(0x6D) f11 = KeyCode.from_vk(0x67) f12 = KeyCode.from_vk(0x6F) f13 = KeyCode.from_vk(0x69) f14 = KeyCode.from_vk(0x6B) f15 = KeyCode.from_vk(0x71) f16 = KeyCode.from_vk(0x6A) f17 = KeyCode.from_vk(0x40) f18 = KeyCode.from_vk(0x4F) f19 = KeyCode.from_vk(0x50) f20 = KeyCode.from_vk(0x5A) home = KeyCode.from_vk(0x73) left = KeyCode.from_vk(0x7B) page_down = KeyCode.from_vk(0x79) page_up = KeyCode.from_vk(0x74) right = KeyCode.from_vk(0x7C) shift = KeyCode.from_vk(0x38) shift_l = KeyCode.from_vk(0x38) shift_r = KeyCode.from_vk(0x3C) space = KeyCode.from_vk(0x31, char=' ') tab = KeyCode.from_vk(0x30) up = KeyCode.from_vk(0x7E) class Controller(_base.Controller): _KeyCode = KeyCode _Key = Key def __init__(self): super(Controller, self).__init__() self._mapping = get_unicode_to_keycode_map() def _handle(self, key, is_press): with self.modifiers as modifiers: Quartz.CGEventPost( Quartz.kCGHIDEventTap, (key if key not in Key else key.value)._event( modifiers, self._mapping, is_press)) class Listener(ListenerMixin, _base.Listener): #: The events that we listen to _EVENTS = ( Quartz.CGEventMaskBit(Quartz.kCGEventKeyDown) | Quartz.CGEventMaskBit(Quartz.kCGEventKeyUp) | Quartz.CGEventMaskBit(Quartz.kCGEventFlagsChanged)) #: A mapping from keysym to special key _SPECIAL_KEYS = { key.value.vk: key for key in Key} #: The event flags set for the various modifier keys _MODIFIER_FLAGS = { Key.alt: Quartz.kCGEventFlagMaskAlternate, Key.alt_l: Quartz.kCGEventFlagMaskAlternate, Key.alt_r: Quartz.kCGEventFlagMaskAlternate, Key.cmd: Quartz.kCGEventFlagMaskCommand, Key.cmd_l: Quartz.kCGEventFlagMaskCommand, Key.cmd_r: Quartz.kCGEventFlagMaskCommand, Key.ctrl: Quartz.kCGEventFlagMaskControl, Key.ctrl_l: Quartz.kCGEventFlagMaskControl, Key.ctrl_r: Quartz.kCGEventFlagMaskControl, Key.shift: Quartz.kCGEventFlagMaskShift, Key.shift_l: Quartz.kCGEventFlagMaskShift, Key.shift_r: Quartz.kCGEventFlagMaskShift} def __init__(self, *args, **kwargs): super(Listener, self).__init__(*args, **kwargs) self._flags = 0 self._context = None def _run(self): with keycode_context() as context: self._context = context try: super(Listener, self)._run() finally: self._context = None def _event_to_key(self, event): """Converts a *Quartz* event to a :class:`KeyCode`. :param event: The event to convert. :return: a :class:`pynput.keyboard.KeyCode` :raises IndexError: if the key code is invalid """ vk = Quartz.CGEventGetIntegerValueField( event, Quartz.kCGKeyboardEventKeycode) # First try special keys... if vk in self._SPECIAL_KEYS: return self._SPECIAL_KEYS[vk] # ...then try characters... # TODO: Use Quartz.CGEventKeyboardGetUnicodeString instead char = keycode_to_string( self._context, vk, Quartz.CGEventGetFlags(event)) if char: return KeyCode.from_char(char) # ...and fall back on a virtual key code return KeyCode.from_vk(vk) def _handle(self, proxy, event_type, event, refcon): # Convert the event to a KeyCode; this may fail, and in that case we # pass None try: key = self._event_to_key(event) except IndexError: key = None except: # TODO: Error reporting return try: if event_type == Quartz.kCGEventKeyDown: # This is a normal key press self.on_press(key) elif event_type == Quartz.kCGEventKeyUp: # This is a normal key release self.on_release(key) elif key == Key.caps_lock: # We only get an event when caps lock is toggled, so we fake # press and release self.on_press(key) self.on_release(key) else: # This is a modifier event---excluding caps lock---for which we # must check the current modifier state to determine whether # the key was pressed or released flags = Quartz.CGEventGetFlags(event) is_press = flags & self._MODIFIER_FLAGS.get(key, 0) if is_press: self.on_press(key) else: self.on_release(key) finally: # Store the current flag mask to be able to detect modifier state # changes self._flags = Quartz.CGEventGetFlags(event) PK)\H-pynput/keyboard/_win32.py# coding=utf-8 # pynput # Copyright (C) 2015-2016 Moses Palmér # # This program is free software: you can redistribute it and/or modify it under # the terms of the GNU Lesser General Public License as published by the Free # Software Foundation, either version 3 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 Lesser General Public License for more # details. # # You should have received a copy of the GNU Lesser General Public License # along with this program. If not, see . import enum from pynput._util import NotifierMixin from pynput._util.win32 import * from . import _base class KeyCode(_base.KeyCode): def _parameters(self, is_press): """The parameters to pass to ``SendInput`` to generate this key. :param bool is_press: Whether to generate a press event. :return: all arguments to pass to ``SendInput`` for this key :rtype: dict """ if self.vk: vk = self.vk scan = 0 flags = 0 else: r = VkKeyScan(ord(self.char)) if (r >> 8) & 0xFF == 0: vk = r & 0xFF scan = 0 flags = 0 else: vk = 0 scan = ord(self.char) flags = KEYBDINPUT.UNICODE return dict( dwFlags=flags | (KEYBDINPUT.KEYUP if not is_press else 0), wVk=vk, wScan=scan) class Key(enum.Enum): alt = KeyCode.from_vk(0x12) alt_l = KeyCode.from_vk(0xA4) alt_r = KeyCode.from_vk(0xA5) alt_gr = KeyCode.from_vk(0xA5) backspace = KeyCode.from_vk(0x08) caps_lock = KeyCode.from_vk(0x14) cmd = KeyCode.from_vk(0x5B) cmd_l = KeyCode.from_vk(0x5B) cmd_r = KeyCode.from_vk(0xA4) ctrl = KeyCode.from_vk(0x11) ctrl_l = KeyCode.from_vk(0xA2) ctrl_r = KeyCode.from_vk(0xA3) delete = KeyCode.from_vk(0x2E) down = KeyCode.from_vk(0x28) end = KeyCode.from_vk(0x23) enter = KeyCode.from_vk(0x0D) esc = KeyCode.from_vk(0x1B) f1 = KeyCode.from_vk(0x70) f2 = KeyCode.from_vk(0x71) f3 = KeyCode.from_vk(0x72) f4 = KeyCode.from_vk(0x73) f5 = KeyCode.from_vk(0x74) f6 = KeyCode.from_vk(0x75) f7 = KeyCode.from_vk(0x76) f8 = KeyCode.from_vk(0x77) f9 = KeyCode.from_vk(0x78) f10 = KeyCode.from_vk(0x79) f11 = KeyCode.from_vk(0x7A) f12 = KeyCode.from_vk(0x7B) f13 = KeyCode.from_vk(0x7C) f14 = KeyCode.from_vk(0x7D) f15 = KeyCode.from_vk(0x7E) f16 = KeyCode.from_vk(0x7F) f17 = KeyCode.from_vk(0x80) f18 = KeyCode.from_vk(0x81) f19 = KeyCode.from_vk(0x82) f20 = KeyCode.from_vk(0x83) home = KeyCode.from_vk(0x24) left = KeyCode.from_vk(0x25) page_down = KeyCode.from_vk(0x22) page_up = KeyCode.from_vk(0x21) right = KeyCode.from_vk(0x27) shift = KeyCode.from_vk(0xA0) shift_l = KeyCode.from_vk(0xA0) shift_r = KeyCode.from_vk(0xA1) space = KeyCode.from_vk(0x20, char=' ') tab = KeyCode.from_vk(0x09) up = KeyCode.from_vk(0x26) insert = KeyCode.from_vk(0x2D) menu = KeyCode.from_vk(0x5D) num_lock = KeyCode.from_vk(0x90) pause = KeyCode.from_vk(0x13) print_screen = KeyCode.from_vk(0x2C) scroll_lock = KeyCode.from_vk(0x91) class Controller(NotifierMixin, _base.Controller): _KeyCode = KeyCode _Key = Key def _handle(self, key, is_press): SendInput( 1, ctypes.byref(INPUT( type=INPUT.KEYBOARD, value=INPUT_union( ki=KEYBDINPUT(**key._parameters(is_press))))), ctypes.sizeof(INPUT)) # Notify any running listeners self._emit('_on_fake_event', key, is_press) @Controller._receiver class Listener(ListenerMixin, _base.Listener): #: The Windows hook ID for low level keyboard events, ``WH_KEYBOARD_LL`` _EVENTS = 13 _WM_KEYDOWN = 0x0100 _WM_KEYUP = 0x0101 _WM_SYSKEYDOWN = 0x0104 _WM_SYSKEYUP = 0x0105 #: The messages that correspond to a key press _PRESS_MESSAGES = (_WM_KEYDOWN, _WM_SYSKEYDOWN) #: The messages that correspond to a key release _RELEASE_MESSAGES = (_WM_KEYUP, _WM_SYSKEYUP) #: A mapping from keysym to special key _SPECIAL_KEYS = { key.value.vk: key for key in Key} def __init__(self, *args, **kwargs): super(Listener, self).__init__(*args, **kwargs) self._translate = KeyTranslator() def _event_to_key(self, msg, data): """Converts an :class:`_KBDLLHOOKSTRUCT` to a :class:`KeyCode`. :param msg: The message received. :param data: The data to convert. :return: a :class:`pynput.keyboard.KeyCode` :raises OSError: if the message and data could not be converted """ # We must always call self._translate to keep the keyboard state up to # date key = KeyCode(**self._translate( data.vkCode, msg in self._PRESS_MESSAGES)) # If the virtual key code corresponds to a Key value, we prefer that if data.vkCode in self._SPECIAL_KEYS: return self._SPECIAL_KEYS[data.vkCode] else: return key class _KBDLLHOOKSTRUCT(ctypes.Structure): """Contains information about a mouse event passed to a ``WH_KEYBOARD_LL`` hook procedure, ``LowLevelKeyboardProc``. """ _fields_ = [ ('vkCode', wintypes.DWORD), ('scanCode', wintypes.DWORD), ('flags', wintypes.DWORD), ('time', wintypes.DWORD), ('dwExtraInfo', ctypes.c_void_p)] #: A pointer to a :class:`KBDLLHOOKSTRUCT` _LPKBDLLHOOKSTRUCT = ctypes.POINTER(_KBDLLHOOKSTRUCT) def _handle(self, code, msg, lpdata): if code != SystemHook.HC_ACTION: return data = ctypes.cast(lpdata, self._LPKBDLLHOOKSTRUCT).contents # Convert the event to a KeyCode; this may fail, and in that case we # pass None try: key = self._event_to_key(msg, data) except OSError: key = None except: # TODO: Error reporting return if msg in self._PRESS_MESSAGES: self.on_press(key) elif msg in self._RELEASE_MESSAGES: self.on_release(key) def _on_fake_event(self, key, is_press): """The handler for fake press events sent by the controllers. :param KeyCode key: The key pressed. :param bool is_press: Whether this is a press event. """ (self.on_press if is_press else self.on_release)( self._SPECIAL_KEYS.get(key.vk, key)) PK)\Hx@\==pynput/keyboard/_xorg.py# coding=utf-8 # pynput # Copyright (C) 2015-2016 Moses Palmér # # This program is free software: you can redistribute it and/or modify it under # the terms of the GNU Lesser General Public License as published by the Free # Software Foundation, either version 3 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 Lesser General Public License for more # details. # # You should have received a copy of the GNU Lesser General Public License # along with this program. If not, see . import enum import threading import Xlib.display import Xlib.ext import Xlib.ext.xtest import Xlib.X import Xlib.XK import Xlib.protocol from pynput._util import NotifierMixin from pynput._util.xorg import * from . import _base class KeyCode(_base.KeyCode): @classmethod def _from_symbol(self, symbol, **kwargs): """Creates a key from a symbol. :param str symbol: The symbol name. :return: a key code """ # First try simple translation keysym = Xlib.XK.string_to_keysym(symbol) if keysym: return self.from_vk(keysym, **kwargs) # If that fails, try checking a module attribute of Xlib.keysymdef.xkb if not keysym: try: return self.from_vk( getattr(Xlib.keysymdef.xkb, 'XK_' + symbol, 0), **kwargs) except: return self.from_vk( SYMBOLS.get(symbol, (0,))[0], **kwargs) class Key(enum.Enum): # Default keys alt = KeyCode._from_symbol('Alt_L') alt_l = KeyCode._from_symbol('Alt_L') alt_r = KeyCode._from_symbol('Alt_R') alt_gr = KeyCode._from_symbol('Mode_switch') backspace = KeyCode._from_symbol('BackSpace') caps_lock = KeyCode._from_symbol('Caps_Lock') cmd = KeyCode._from_symbol('Super_L') cmd_l = KeyCode._from_symbol('Super_L') cmd_r = KeyCode._from_symbol('Super_R') ctrl = KeyCode._from_symbol('Control_L') ctrl_l = KeyCode._from_symbol('Control_L') ctrl_r = KeyCode._from_symbol('Control_R') delete = KeyCode._from_symbol('Delete') down = KeyCode._from_symbol('Down') end = KeyCode._from_symbol('End') enter = KeyCode._from_symbol('Return') esc = KeyCode._from_symbol('Escape') f1 = KeyCode._from_symbol('F1') f2 = KeyCode._from_symbol('F2') f3 = KeyCode._from_symbol('F3') f4 = KeyCode._from_symbol('F4') f5 = KeyCode._from_symbol('F5') f6 = KeyCode._from_symbol('F6') f7 = KeyCode._from_symbol('F7') f8 = KeyCode._from_symbol('F8') f9 = KeyCode._from_symbol('F9') f10 = KeyCode._from_symbol('F10') f11 = KeyCode._from_symbol('F11') f12 = KeyCode._from_symbol('F12') f13 = KeyCode._from_symbol('F13') f14 = KeyCode._from_symbol('F14') f15 = KeyCode._from_symbol('F15') f16 = KeyCode._from_symbol('F16') f17 = KeyCode._from_symbol('F17') f18 = KeyCode._from_symbol('F18') f19 = KeyCode._from_symbol('F19') f20 = KeyCode._from_symbol('F20') home = KeyCode._from_symbol('Home') left = KeyCode._from_symbol('Left') page_down = KeyCode._from_symbol('Page_Down') page_up = KeyCode._from_symbol('Page_Up') right = KeyCode._from_symbol('Right') shift = KeyCode._from_symbol('Shift_L') shift_l = KeyCode._from_symbol('Shift_L') shift_r = KeyCode._from_symbol('Shift_R') space = KeyCode._from_symbol('space', char=' ') tab = KeyCode._from_symbol('Tab') up = KeyCode._from_symbol('Up') insert = KeyCode._from_symbol('Insert') menu = KeyCode._from_symbol('Menu') num_lock = KeyCode._from_symbol('Num_Lock') pause = KeyCode._from_symbol('Pause') print_screen = KeyCode._from_symbol('Print') scroll_lock = KeyCode._from_symbol('Scroll_Lock') class Controller(NotifierMixin, _base.Controller): _KeyCode = KeyCode _Key = Key #: The shift mask for :attr:`Key.ctrl` CTRL_MASK = Xlib.X.ControlMask #: The shift mask for :attr:`Key.shift` SHIFT_MASK = Xlib.X.ShiftMask def __init__(self): super(Controller, self).__init__() self._display = Xlib.display.Display() self._keyboard_mapping = None self._borrows = {} self._borrow_lock = threading.RLock() self.ALT_MASK = alt_mask(self._display) self.ALT_GR_MASK = alt_gr_mask(self._display) def __del__(self): if self._display: self._display.close() @property def keyboard_mapping(self): """A mapping from *keysyms* to *key codes*. Each value is the tuple ``(key_code, shift_state)``. By sending an event with the specified *key code* and shift state, the specified *keysym* will be touched. """ if not self._keyboard_mapping: self._update_keyboard_mapping() return self._keyboard_mapping def _handle(self, key, is_press): """Resolves a key identifier and sends a keyboard event. :param event: The *X* keyboard event. :param int keysym: The keysym to handle. """ event = Xlib.display.event.KeyPress if is_press \ else Xlib.display.event.KeyRelease keysym = self._keysym(key) # Make sure to verify that the key was resolved if keysym is None: raise self.InvalidKeyException(key) try: keycode, shift_state = self.keyboard_mapping[keysym] self._send_key(event, keycode, shift_state) except KeyError: with self._borrow_lock: keycode, index, count = self._borrows[keysym] self._send_key( event, keycode, index_to_shift(self._display, index)) count += 1 if is_press else -1 self._borrows[keysym] = (keycode, index, count) # Notify any running listeners self._emit('_on_fake_event', key, is_press) def _keysym(self, key): """Converts a key to a *keysym*. :param KeyCode key: The key code to convert. """ return self._resolve_dead(key) if key.is_dead else None \ or self._resolve_special(key) \ or self._resolve_normal(key) \ or self._resolve_borrowed(key) \ or self._resolve_borrowing(key) def _send_key(self, event, keycode, shift_state): """Sends a single keyboard event. :param event: The *X* keyboard event. :param int keycode: The keycode. :param int shift_state: The shift state. The actual value used is :attr:`shift_state` or'd with this value. """ with display_manager(self._display) as d, self.modifiers as modifiers: window = d.get_input_focus().focus window.send_event(event( detail=keycode, state=shift_state | self._shift_mask(modifiers), time=0, root=d.screen().root, window=window, same_screen=0, child=Xlib.X.NONE, root_x=0, root_y=0, event_x=0, event_y=0)) def _resolve_dead(self, key): """Tries to resolve a dead key. :param str identifier: The identifier to resolve. """ try: keysym, _ = SYMBOLS[CHARS[key.combining]] except: return None if keysym not in self.keyboard_mapping: return None return keysym def _resolve_special(self, key): """Tries to resolve a special key. A special key has the :attr:`~KeyCode.vk` attribute set. :param KeyCode key: The key to resolve. """ if not key.vk: return None return key.vk def _resolve_normal(self, key): """Tries to resolve a normal key. A normal key exists on the keyboard, and is typed by pressing and releasing a simple key, possibly in combination with a modifier. :param KeyCode key: The key to resolve. """ keysym = self._key_to_keysym(key) if keysym is None: return None if keysym not in self.keyboard_mapping: return None return keysym def _resolve_borrowed(self, key): """Tries to resolve a key by looking up the already borrowed *keysyms*. A borrowed *keysym* does not exist on the keyboard, but has been temporarily added to the layout. :param KeyCode key: The key to resolve. """ keysym = self._key_to_keysym(key) if keysym is None: return None with self._borrow_lock: if keysym not in self._borrows: return None return keysym def _resolve_borrowing(self, key): """Tries to resolve a key by modifying the layout temporarily. A borrowed *keysym* does not exist on the keyboard, but is temporarily added to the layout. :param KeyCode key: The key to resolve. """ keysym = self._key_to_keysym(key) if keysym is None: return None keyboard_mapping = self._display.get_keyboard_mapping(8, 255 - 8) def i2kc(index): return index + 8 def kc2i(keycode): return keycode - 8 #: Finds a keycode and index by looking at already used keycodes def reuse(): for keysym, (keycode, _, _) in self._borrows.items(): keycodes = keyboard_mapping[kc2i(keycode)] # Only the first four items are addressable by X for index in range(4): if not keycodes[index]: return keycode, index #: Finds a keycode and index by using a new keycode def borrow(): for i, keycodes in enumerate(keyboard_mapping): if not any(keycodes): return i2kc(i), 0 #: Finds a keycode and index by reusing an old, unused one def overwrite(): for keysym, (keycode, index, count) in self._borrows.items(): if count < 1: del self._borrows[keysym] return keycode, index #: Registers a keycode for a specific key and modifier state def register(keycode, index): i = kc2i(keycode) keyboard_mapping[i][index] = keysym d.change_keyboard_mapping( keycode, keyboard_mapping[i:i + 1]) self._borrows[keysym] = (keycode, index, 0) try: with display_manager(self._display) as d, self._borrow_lock: # First try an already used keycode, then try a new one, and # fall back on reusing one that is not currently pressed register(*( reuse() or borrow() or overwrite())) return keysym except TypeError: return None def _key_to_keysym(self, key): """Converts a character key code to a *keysym*. :param KeyCode key: The key code. :return: a keysym if found :rtype: int or None """ symbol = CHARS.get(key.char, None) if symbol is None: return None try: return symbol_to_keysym(symbol) except: try: return SYMBOLS[symbol][0] except: return None def _shift_mask(self, modifiers): """The *X* modifier mask to apply for a set of modifiers. :param set modifiers: A set of active modifiers for which to get the shift mask. """ return ( (self.ALT_MASK if Key.alt in modifiers else 0) | (self.ALT_GR_MASK if Key.alt_gr in modifiers else 0) | (self.CTRL_MASK if Key.ctrl in modifiers else 0) | (self.SHIFT_MASK if Key.shift in modifiers else 0)) def _update_keyboard_mapping(self): """Updates the keyboard mapping. """ with display_manager(self._display) as d: self._keyboard_mapping = keyboard_mapping(d) @Controller._receiver class Listener(ListenerMixin, _base.Listener): _EVENTS = ( Xlib.X.KeyPress, Xlib.X.KeyRelease) #: A mapping from keysym to special key _SPECIAL_KEYS = { key.value.vk: key for key in Key} def _keycode_to_keysym(self, display, keycode, index): """Converts a keycode and shift state index to a keysym. This method uses a simplified version of the *X* convention to locate the correct keysym in the display table: since this method is only used to locate special keys, alphanumeric keys are not treated specially. :param display: The current *X* display. :param keycode: The keycode. :param index: The shift state index. :return: a keysym """ keysym = display.keycode_to_keysym(keycode, index) if keysym: return keysym elif index & 0x2: return self._keycode_to_keysym(display, keycode, index & ~0x2) elif index & 0x1: return self._keycode_to_keysym(display, keycode, index & ~0x1) else: return 0 def _event_to_key(self, display, event): """Converts an *X* event to a :class:`KeyCode`. :param display: The current *X* display. :param event: The event to convert. :return: a :class:`pynput.keyboard.KeyCode` :raises IndexError: if the key code is invalid """ keycode = event.detail index = shift_to_index(display, event.state) # First try special keys... keysym = self._keycode_to_keysym(display, keycode, index) if keysym in self._SPECIAL_KEYS: return self._SPECIAL_KEYS[keysym] # ...then try characters... name = KEYSYMS[keysym] if name in SYMBOLS: char = SYMBOLS[name][1] if char in DEAD_KEYS: return KeyCode.from_dead(DEAD_KEYS[char]) else: return KeyCode.from_char(char) # ...and fall back on a virtual key code return KeyCode.from_vk(keysym) def _run(self): with self._receive(): super(Listener, self)._run() def _initialize(self, display): # Get the keyboard mapping to be able to translate events details to # key codes min_keycode = display.display.info.min_keycode keycode_count = display.display.info.max_keycode - min_keycode + 1 self._keyboard_mapping = display.get_keyboard_mapping( min_keycode, keycode_count) def _handle(self, display, event): # Convert the event to a KeyCode; this may fail, and in that case we # pass None try: key = self._event_to_key(display, event) except IndexError: key = None except: # TODO: Error reporting return if event.type == Xlib.X.KeyPress: self.on_press(key) elif event.type == Xlib.X.KeyRelease: self.on_release(key) def _on_fake_event(self, key, is_press): """The handler for fake press events sent by the controllers. :param KeyCode key: The key pressed. :param bool is_press: Whether this is a press event. """ (self.on_press if is_press else self.on_release)( self._SPECIAL_KEYS.get(key.vk, key)) PK)\H(;AApynput/keyboard/_base.py# coding=utf-8 # pynput # Copyright (C) 2015-2016 Moses Palmér # # This program is free software: you can redistribute it and/or modify it under # the terms of the GNU Lesser General Public License as published by the Free # Software Foundation, either version 3 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 Lesser General Public License for more # details. # # You should have received a copy of the GNU Lesser General Public License # along with this program. If not, see . import contextlib import enum import six import threading import unicodedata from pynput._util import AbstractListener class KeyCode(object): def __init__(self, vk=0, char=None, is_dead=False): self.vk = vk self.char = six.text_type(char) if char is not None else None self.is_dead = is_dead if self.is_dead: self.combining = unicodedata.lookup( 'COMBINING ' + unicodedata.name(self.char)) if not self.combining: raise KeyError(char) else: self.combining = None def __repr__(self): if self.is_dead: return '[%s]' % repr(self.char) if self.char is not None: return repr(self.char) else: return '<%d>' % self.vk def __str__(self): return repr(self) def __eq__(self, other): if not isinstance(other, self.__class__): return False if self.char is not None and other.char is not None: return self.char == other.char and self.is_dead == other.is_dead else: return self.vk == other.vk def join(self, key): """Applies this dead key to another key and returns the result. Joining a dead key with space (``' '``) or itself yields the non-dead version of this key, if one exists; for example, ``KeyCode.from_dead('~').join(KeyCode.from_char(' '))`` equals ``KeyCode.from_char('~')`` and ``KeyCode.from_dead('~').join(KeyCode.from_dead('~'))``. :param KeyCode key: The key to join with this key. :return: a key code :raises ValueError: if the keys cannot be joined """ # A non-dead key cannot be joined if not self.is_dead: raise ValueError(self) # Joining two of the same keycodes, or joining with space, yields the # non-dead version of the key if key.char == ' ' or self == key: return self.from_char(self.char) # Otherwise we combine the characters if key.char is not None: combined = unicodedata.normalize( 'NFC', key.char + self.combining) if combined: return self.from_char(combined) raise ValueError(key) @classmethod def from_vk(self, vk, **kwargs): """Creates a key from a virtual key code. :param vk: The virtual key code. :param kwargs: Any other parameters to pass. :return: a key code """ return self(vk=vk, **kwargs) @classmethod def from_char(self, char): """Creates a key from a character. :param str char: The character. :return: a key code """ return self(char=char) @classmethod def from_dead(self, char): """Creates a dead key. :param char: The dead key. This should be the unicode character representing the stand alone character, such as ``'~'`` for *COMBINING TILDE*. :return: a key code """ return self(char=char, is_dead=True) class Key(enum.Enum): """A class representing various buttons that may not correspond to letters. This includes modifier keys and function keys. The actual values for these items differ between platforms. Some platforms may have additional buttons, but these are guaranteed to be present everywhere. """ #: A generic Alt key. This is a modifier. alt = 0 #: The left Alt key. This is a modifier. alt_l = 0 #: The right Alt key. This is a modifier. alt_r = 0 #: The AltGr key. This is a modifier. alt_gr = 0 #: The Backspace key. backspace = 0 #: The CapsLock key. caps_lock = 0 #: A generic command button. On *PC* platforms, this corresponds to the #: Super key or Windows key, and on *Mac* it corresponds to the Command #: key. This may be a modifier. cmd = 0 #: The left command button. On *PC* platforms, this corresponds to the #: Super key or Windows key, and on *Mac* it corresponds to the Command #: key. This may be a modifier. cmd_l = 0 #: The right command button. On *PC* platforms, this corresponds to the #: Super key or Windows key, and on *Mac* it corresponds to the Command #: key. This may be a modifier. cmd_r = 0 #: A generic Ctrl key. This is a modifier. ctrl = 0 #: The left Ctrl key. This is a modifier. ctrl_l = 0 #: The right Ctrl key. This is a modifier. ctrl_r = 0 #: The Delete key. delete = 0 #: A down arrow key. down = 0 #: The End key. end = 0 #: The Enter or Return key. enter = 0 #: The Esc key. esc = 0 #: The function keys. F1 to F20 are defined. f1 = 0 f2 = 0 f3 = 0 f4 = 0 f5 = 0 f6 = 0 f7 = 0 f8 = 0 f9 = 0 f10 = 0 f11 = 0 f12 = 0 f13 = 0 f14 = 0 f15 = 0 f16 = 0 f17 = 0 f18 = 0 f19 = 0 f20 = 0 #: The Home key. home = 0 #: A left arrow key. left = 0 #: The PageDown key. page_down = 0 #: The PageUp key. page_up = 0 #: A right arrow key. right = 0 #: A generic Shift key. This is a modifier. shift = 0 #: The left Shift key. This is a modifier. shift_l = 0 #: The right Shift key. This is a modifier. shift_r = 0 #: The Space key. space = 0 #: The Tab key. tab = 0 #: An up arrow key. up = 0 #: The Insert key. This may be undefined for some platforms. insert = 0 #: The Menu key. This may be undefined for some platforms. menu = 0 #: The NumLock key. This may be undefined for some platforms. num_lock = 0 #: The Pause/Break key. This may be undefined for some platforms. pause = 0 #: The PrintScreen key. This may be undefined for some platforms. print_screen = 0 #: The ScrollLock key. This may be undefined for some platforms. scroll_lock = 0 class Controller(object): """A controller for sending virtual keyboard events to the system. """ #: The virtual key codes _KeyCode = KeyCode #: The various keys. _Key = Key class InvalidKeyException(Exception): """The exception raised when and invalid ``key`` parameter is passed to either :meth:`Controller.press` or :meth:`Controller.release`. Its first argument is the ``key`` parameter. """ pass class InvalidCharacterException(Exception): """The exception raised when and invalid character is encountered in the string passed to :meth:`Controller.type`. Its first argument is the index of the character in the string, and the second the character. """ pass def __init__(self): self._modifiers_lock = threading.RLock() self._modifiers = set() self._caps_lock = False self._dead_key = None K = self._Key #: The keys used as modifiers; the first value in each tuple is the #: base modifier to use for subsequent modifiers. self._MODIFIER_KEYS = ( (K.alt_gr, (K.alt_gr.value,)), (K.alt, (K.alt.value, K.alt_l.value, K.alt_r.value)), (K.cmd, (K.cmd.value, K.cmd_l.value, K.cmd_r.value)), (K.ctrl, (K.ctrl.value, K.ctrl_l.value, K.ctrl_r.value)), (K.shift, (K.shift.value, K.shift_l.value, K.shift_r.value))) def press(self, key): """Presses a key. A key may be either a string of length 1, one of the :class:`Key` members or a :class:`KeyCode`. Strings will be transformed to :class:`KeyCode` using :meth:`KeyCode.char`. Members of :class:`Key` will be translated to their :meth:`~Key.value`. :param key: The key to press. :raises InvalidKeyException: if the key is invalid :raises ValueError: if ``key`` is a string, but its length is not ``1`` """ resolved = self._resolve(key) self._update_modifiers(resolved, True) # Update caps lock state if resolved == self._Key.caps_lock.value: self._caps_lock = not self._caps_lock # If we currently have a dead key pressed, join it with this key original = resolved if self._dead_key: try: resolved = self._dead_key.join(resolved) except ValueError: self._handle(self._dead_key, True) self._handle(self._dead_key, False) # If the key is a dead key, keep it for later if resolved.is_dead: self._dead_key = resolved return try: self._handle(resolved, True) except self.InvalidKeyException: if resolved != original: self._handle(self._dead_key, True) self._handle(self._dead_key, False) self._handle(original, True) self._dead_key = None def release(self, key): """Releases a key. A key may be either a string of length 1, one of the :class:`Key` members or a :class:`KeyCode`. Strings will be transformed to :class:`KeyCode` using :meth:`KeyCode.char`. Members of :class:`Key` will be translated to their :meth:`~Key.value`. :param key: The key to release. If this is a string, it is passed to :meth:`touches` and the returned releases are used. :raises InvalidKeyException: if the key is invalid :raises ValueError: if ``key`` is a string, but its length is not ``1`` """ resolved = self._resolve(key) self._update_modifiers(resolved, False) # Ignore released dead keys if resolved.is_dead: return self._handle(resolved, False) def touch(self, key, is_press): """Calls either :meth:`press` or :meth:`release` depending on the value of ``is_press``. :param key: The key to press or release. :param bool is_press: Whether to press the key. """ if is_press: self.press(key) else: self.release(key) @contextlib.contextmanager def pressed(self, *args): """Executes a block with some keys pressed. :param keys: The keys to keep pressed. """ for key in args: self.press(key) try: yield finally: for key in reversed(args): self.press(key) def type(self, string): """Types a string. This method will send all key presses and releases necessary to type all characters in the string. :param str string: The string to type. :raises InvalidCharacterException: if an untypable character is encountered """ for i, character in enumerate(string): try: self.press(character) self.release(character) except ValueError: raise self.InvalidCharacterException(i, character) @property @contextlib.contextmanager def modifiers(self): """The currently pressed modifier keys. Only the generic modifiers will be set; when pressing either :attr:`Key.shift_l`, :attr:`Key.shift_r` or :attr:`Key.shift`, only :attr:`Key.shift` will be present. Use this property within a context block thus:: with controller.modifiers as modifiers: with_block() This ensures that the modifiers cannot be modified by another thread. """ with self._modifiers_lock: yield set( self._as_modifier(modifier) for modifier in self._modifiers) @property def alt_pressed(self): """Whether any *alt* key is pressed. """ with self.modifiers as modifiers: return self._Key.alt in modifiers @property def alt_gr_pressed(self): """Whether *altgr* is pressed. """ with self.modifiers as modifiers: return self._Key.alt_gr in modifiers @property def ctrl_pressed(self): """Whether any *ctrl* key is pressed. """ with self.modifiers as modifiers: return self._Key.ctrl in modifiers @property def shift_pressed(self): """Whether any *shift* key is pressed, or *caps lock* is toggled. """ if self._caps_lock: return True with self.modifiers as modifiers: return self._Key.shift in modifiers def _resolve(self, key): """Resolves a key to a :class:`KeyCode` instance. This method will convert any key representing a character to uppercase if a shift modifier is active. :param key: The key to resolve. :return: a key code, or ``None`` if it cannot be resolved """ # Use the value for the key constants if key in self._Key: return key.value # Convert strings to key codes if isinstance(key, six.string_types): if len(key) != 1: raise ValueError(key) return self._KeyCode.from_char(key) # Assume this is a proper key if isinstance(key, self._KeyCode): if key.char is not None and self.shift_pressed: return self._KeyCode(vk=key.vk, char=key.char.upper()) else: return key def _update_modifiers(self, key, is_press): """Updates the current modifier list. If ``key`` is not a modifier, no action is taken. :param key: The key being pressed or released. """ # Check whether the key is a modifier if self._as_modifier(key): with self._modifiers_lock: if is_press: self._modifiers.add(key) else: try: self._modifiers.remove(key) except KeyError: pass def _as_modifier(self, key): """Returns a key as the modifier used internally if defined. This method will convert values like :attr:`Key.alt_r.value` and :attr:`Key.shift_l.value` to :attr:`Key.alt` and :attr:`Key.shift`. :param key: The possible modifier key. :return: the base modifier key, or ``None`` if ``key`` is not a modifier """ for base, modifiers in self._MODIFIER_KEYS: if key in modifiers: return base def _handle(self, key, is_press): """The platform implementation of the actual emitting of keyboard events. This is a platform dependent implementation. :param Key key: The key to handle. :param bool is_press: Whether this is a key press event. """ raise NotImplementedError() class Listener(AbstractListener): """A listener for keyboard events. Instances of this class can be used as context managers. This is equivalent to the following code:: listener.start() try: with_statements() finally: listener.stop() This class inherits from :class:`threading.Thread` and supports all its methods. It will set :attr:`daemon` to ``True`` when created. :param callable on_press: The callback to call when a button is pressed. It will be called with the argument ``(key)``, where ``key`` is a :class:`KeyCode`, a :class:`Key` or ``None`` if the key is unknown. :param callable on_release: The callback to call when a button is release. It will be called with the argument ``(key)``, where ``key`` is a :class:`KeyCode`, a :class:`Key` or ``None`` if the key is unknown. """ def __init__(self, on_press=None, on_release=None): super(Listener, self).__init__( on_press=on_press, on_release=on_release) PK)\H}1pynput/_util/__init__.py# coding=utf-8 # pynput # Copyright (C) 2015-2016 Moses Palmér # # This program is free software: you can redistribute it and/or modify it under # the terms of the GNU Lesser General Public License as published by the Free # Software Foundation, either version 3 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 Lesser General Public License for more # details. # # You should have received a copy of the GNU Lesser General Public License # along with this program. If not, see . import contextlib import functools import threading class AbstractListener(threading.Thread): """A class implementing the basic behaviour for event listeners. Instances of this class can be used as context managers. This is equivalent to the following code:: listener.start() listener.wait() try: with_statements() finally: listener.stop() :param kwargs: A mapping from callback attribute to callback handler. All handlers will be wrapped in a function reading the return value of the callback, and if it ``is False``, raising :class:`StopException`. Any callback that is falsy will be ignored. """ class StopException(Exception): """If an event listener callback raises this exception, the current listener is stopped. Its first argument must be set to the :class:`AbstractListener` to stop. """ pass def __init__(self, **kwargs): super(AbstractListener, self).__init__() def wrapper(f): def inner(*args): if f(*args) is False: raise self.StopException(self) return inner self._running = False self._thread = threading.current_thread() self._condition = threading.Condition() self._ready = False self.daemon = True for name, callback in kwargs.items(): setattr(self, name, wrapper(callback or (lambda *a: None))) @property def running(self): """Whether the listener is currently running. """ return self._running def stop(self): """Stops listening for mouse events. When this method returns, no more events will be delivered. """ if self._running: self._running = False self._stop() def __enter__(self): self.start() self.wait() return self def __exit__(self, type, value, traceback): self.stop() def wait(self): """Waits for this listener to become ready. """ self._condition.acquire() while not self._ready: self._condition.wait() self._condition.release() def run(self): """The thread runner method. """ self._running = True self._thread = threading.current_thread() self._run() @classmethod def _emitter(self, f): """A decorator to mark a method as the one emitting the callbacks. This decorator will wrap the method and catch :class:`StopException`. If this exception is caught, the listener will be stopped. """ @functools.wraps(f) def inner(*args, **kwargs): try: f(*args, **kwargs) except self.StopException as e: e.args[0].stop() return inner def _mark_ready(self): """Marks this listener as ready to receive events. This method must be called from :meth:`_run`. :meth:`wait` will block until this method is called. """ self._condition.acquire() self._ready = True self._condition.notify() self._condition.release() def _run(self): """The implementation of the :meth:`run` method. This is a platform dependent implementation. """ raise NotImplementedError() def _stop(self): """The implementation of the :meth:`stop` method. This is a platform dependent implementation. """ raise NotImplementedError() class NotifierMixin(object): """A mixin for notifiers of fake events. This mixin can be used for controllers on platforms where sending fake events does not cause a listener to receive a notification. """ def _emit(self, action, *args): """Sends a notification to all registered listeners. This method will ensure that listeners that raise :class:`StopException` are stopped. :param str action: The name of the notification. :param args: The arguments to pass. """ stopped = [] for listener in self._listeners(): try: getattr(listener, action)(*args) except listener.StopException: stopped.append(listener) for listener in stopped: listener.stop() @classmethod def _receiver(cls, listener_class): """A decorator to make a class able to receive fake events from a controller. This decorator will add the method ``_receive`` to the decorated class. This method is a context manager which ensures that all calls to :meth:`_emit` will invoke the named method in the listener instance while the block is active. """ @contextlib.contextmanager def receive(self): """Executes a code block with this listener instance registered as a receiver of fake input events. """ self._controller_class._add_listener(self) try: yield finally: self._controller_class._remove_listener(self) listener_class._receive = receive listener_class._controller_class = cls # Make sure this class has the necessary attributes if not hasattr(cls, '_listener_cache'): cls._listener_cache = set() cls._listener_lock = threading.Lock() return listener_class @classmethod def _listeners(cls): """Iterates over the set of running listeners. This method will quit without acquiring the lock if the set is empty, so there is potential for race conditions. This is an optimisation, since :class:`Controller` will need to call this method for every control event. """ if not cls._listener_cache: return with cls._listener_lock: for listener in cls._listener_cache: yield listener @classmethod def _add_listener(cls, listener): """Adds a listener to the set of running listeners. :param listener: The listener for fake events. """ with cls._listener_lock: cls._listener_cache.add(listener) @classmethod def _remove_listener(cls, listener): """Removes this listener from the set of running listeners. :param listener: The listener for fake events. """ with cls._listener_lock: cls._listener_cache.remove(listener) PK)\HCHEEpynput/_util/darwin.py# coding=utf-8 # pynput # Copyright (C) 2015-2016 Moses Palmér # # This program is free software: you can redistribute it and/or modify it under # the terms of the GNU Lesser General Public License as published by the Free # Software Foundation, either version 3 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 Lesser General Public License for more # details. # # You should have received a copy of the GNU Lesser General Public License # along with this program. If not, see . import contextlib import ctypes import ctypes.util import six import objc import CoreFoundation import Quartz from . import AbstractListener #: The objc module as a library handle _objc = ctypes.PyDLL(objc._objc.__file__) _objc.PyObjCObject_New.restype = ctypes.py_object _objc.PyObjCObject_New.argtypes = [ctypes.c_void_p, ctypes.c_int, ctypes.c_int] def _wrap_value(value): """Converts a pointer to a *Python objc* value. :param value: The pointer to convert. :return: a wrapped value """ return _objc.PyObjCObject_New(value, 0, 1) @contextlib.contextmanager def _wrapped(value): """A context manager that converts a raw pointer to a *Python objc* value. When the block is exited, the value is released. :param value: The raw value to wrap. """ wrapped_value = _wrap_value(value) try: yield value finally: CoreFoundation.CFRelease(wrapped_value) class CarbonExtra(object): """A class exposing some missing functionality from *Carbon* as class attributes. """ _Carbon = ctypes.cdll.LoadLibrary(ctypes.util.find_library('Carbon')) _Carbon.TISCopyCurrentKeyboardInputSource.argtypes = [] _Carbon.TISCopyCurrentKeyboardInputSource.restype = ctypes.c_void_p _Carbon.TISGetInputSourceProperty.argtypes = [ ctypes.c_void_p, ctypes.c_void_p] _Carbon.TISGetInputSourceProperty.restype = ctypes.c_void_p _Carbon.LMGetKbdType.argtypes = [] _Carbon.LMGetKbdType.restype = ctypes.c_uint32 _Carbon.UCKeyTranslate.argtypes = [ ctypes.c_void_p, ctypes.c_uint16, ctypes.c_uint16, ctypes.c_uint32, ctypes.c_uint32, ctypes.c_uint32, ctypes.POINTER(ctypes.c_uint32), ctypes.c_uint8, ctypes.POINTER(ctypes.c_uint8), ctypes.c_uint16 * 4] _Carbon.UCKeyTranslate.restype = ctypes.c_uint32 TISCopyCurrentKeyboardInputSource = \ _Carbon.TISCopyCurrentKeyboardInputSource kTISPropertyUnicodeKeyLayoutData = ctypes.c_void_p.in_dll( _Carbon, 'kTISPropertyUnicodeKeyLayoutData') TISGetInputSourceProperty = \ _Carbon.TISGetInputSourceProperty LMGetKbdType = \ _Carbon.LMGetKbdType kUCKeyActionDisplay = 3 kUCKeyTranslateNoDeadKeysBit = 0 UCKeyTranslate = \ _Carbon.UCKeyTranslate @contextlib.contextmanager def keycode_context(): """Returns an opaque value representing a context for translating keycodes to strings. """ with _wrapped(CarbonExtra.TISCopyCurrentKeyboardInputSource()) as keyboard: keyboard_type = CarbonExtra.LMGetKbdType() layout = _wrap_value(CarbonExtra.TISGetInputSourceProperty( keyboard, CarbonExtra.kTISPropertyUnicodeKeyLayoutData)) layout_data = layout.bytes().tobytes() yield (keyboard_type, layout_data) def keycode_to_string(context, keycode, modifier_state=0): """Converts a keycode to a string. """ LENGTH = 4 keyboard_type, layout_data = context dead_key_state = ctypes.c_uint32() length = ctypes.c_uint8() unicode_string = (ctypes.c_uint16 * LENGTH)() CarbonExtra.UCKeyTranslate( layout_data, keycode, CarbonExtra.kUCKeyActionDisplay, modifier_state, keyboard_type, CarbonExtra.kUCKeyTranslateNoDeadKeysBit, ctypes.byref(dead_key_state), LENGTH, ctypes.byref(length), unicode_string) return u''.join( six.unichr(unicode_string[i]) for i in range(length.value)) def get_unicode_to_keycode_map(): """Returns a mapping from unicode strings to virtual key codes. :return: a dict mapping key codes to strings """ with keycode_context() as context: return { keycode_to_string(context, keycode): keycode for keycode in range(128)} class ListenerMixin(object): """A mixin for *Quartz* event listeners. Subclasses should set a value for :attr:`_EVENTS` and implement :meth:`_handle`. """ #: The events that we listen to _EVENTS = tuple() def _run(self): self._loop = None try: tap = Quartz.CGEventTapCreate( Quartz.kCGSessionEventTap, Quartz.kCGHeadInsertEventTap, Quartz.kCGEventTapOptionListenOnly, self._EVENTS, self._handler, None) if tap is None: self._mark_ready() return loop_source = Quartz.CFMachPortCreateRunLoopSource( None, tap, 0) self._loop = Quartz.CFRunLoopGetCurrent() Quartz.CFRunLoopAddSource( self._loop, loop_source, Quartz.kCFRunLoopDefaultMode) Quartz.CGEventTapEnable(tap, True) self._mark_ready() while self.running: result = Quartz.CFRunLoopRunInMode( Quartz.kCFRunLoopDefaultMode, 1, False) try: if result != Quartz.kCFRunLoopRunTimedOut: break except AttributeError: # This happens during teardown of the virtual machine break finally: self._loop = None def _stop(self): # The base class sets the running flag to False; this will cause the # loop around run loop invocations to terminate and set this event try: if self._loop is not None: Quartz.CFRunLoopStop(self._loop) except AttributeError: # The loop may not have been created pass @AbstractListener._emitter def _handler(self, proxy, event_type, event, refcon): """The callback registered with *Mac OSX* for mouse events. This method will call the callbacks registered on initialisation. """ self._handle(proxy, event_type, event, refcon) def _handle(self, proxy, event_type, event, refcon): """The device specific callback handler. This method calls the appropriate callback registered when this listener was created based on the event. """ raise NotImplementedError() PK)\H#HP6464pynput/_util/xorg.py# coding=utf-8 # pynput # Copyright (C) 2015-2016 Moses Palmér # # This program is free software: you can redistribute it and/or modify it under # the terms of the GNU Lesser General Public License as published by the Free # Software Foundation, either version 3 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 Lesser General Public License for more # details. # # You should have received a copy of the GNU Lesser General Public License # along with this program. If not, see . import contextlib import itertools import Xlib.display import Xlib.XK from . import AbstractListener from .xorg_keysyms import * # Create a display to verify that we have an X connection display = Xlib.display.Display() display.close() del display class X11Error(Exception): """An error that is thrown at the end of a code block managed by a :func:`display_manager` if an *X11* error occurred. """ pass @contextlib.contextmanager def display_manager(display): """Traps *X* errors and raises an :class:``X11Error`` at the end if any error occurred. This handler also ensures that the :class:`Xlib.display.Display` being managed is sync'd. :param Xlib.display.Display display: The *X* display. :return: the display :rtype: Xlib.display.Display """ errors = [] def handler(*args): errors.append(args) old_handler = display.set_error_handler(handler) try: yield display display.sync() finally: display.set_error_handler(old_handler) if errors: raise X11Error(errors) def _find_mask(display, symbol): """Returns the mode flags to use for a modifier symbol. :param Xlib.display.Display display: The *X* display. :param str symbol: The name of the symbol. :return: the modifier mask """ # Get the key code for the symbol modifier_keycode = display.keysym_to_keycode( Xlib.XK.string_to_keysym(symbol)) for index, keycodes in enumerate(display.get_modifier_mapping()): for keycode in keycodes: if keycode == modifier_keycode: return 1 << index return 0 def alt_mask(display): """Returns the *alt* mask flags. The first time this function is called for a display, the value is cached. Subsequent calls will return the cached value. :param Xlib.display.Display display: The *X* display. :return: the modifier mask """ if not hasattr(display, '__alt_mask'): display.__alt_mask = _find_mask(display, 'Alt_L') return display.__alt_mask def alt_gr_mask(display): """Returns the *alt* mask flags. The first time this function is called for a display, the value is cached. Subsequent calls will return the cached value. :param Xlib.display.Display display: The *X* display. :return: the modifier mask """ if not hasattr(display, '__altgr_mask'): display.__altgr_mask = _find_mask(display, 'Mode_switch') return display.__altgr_mask def keysym_is_latin_upper(keysym): """Determines whether a *keysym* is an upper case *latin* character. This is true only if ``XK_A`` <= ``keysym`` <= ` XK_Z``. :param in keysym: The *keysym* to check. """ return Xlib.XK.XK_A <= keysym <= Xlib.XK.XK_Z def keysym_is_latin_lower(keysym): """Determines whether a *keysym* is a lower case *latin* character. This is true only if ``XK_a`` <= ``keysym`` <= ` XK_z``. :param in keysym: The *keysym* to check. """ return Xlib.XK.XK_a <= keysym <= Xlib.XK.XK_z def keysym_group(a, b): """Generates a group from two *keysyms*. The implementation of this function comes from: Within each group, if the second element of the group is ``NoSymbol``, then the group should be treated as if the second element were the same as the first element, except when the first element is an alphabetic *KeySym* ``K`` for which both lowercase and uppercase forms are defined. In that case, the group should be treated as if the first element were the lowercase form of ``K`` and the second element were the uppercase form of ``K``. This function assumes that *alphabetic* means *latin*; this assumption appears to be consistent with observations of the return values from ``XGetKeyboardMapping``. :param a: The first *keysym*. :param b: The second *keysym*. :return: a tuple conforming to the description above """ if b == Xlib.XK.NoSymbol: if keysym_is_latin_upper(a): return (Xlib.XK.XK_a + a - Xlib.XK.XK_A, a) elif keysym_is_latin_lower(a): return (a, Xlib.XK.XK_A + a - Xlib.XK.XK_a) else: return (a, a) else: return (a, b) def keysym_normalize(keysym): """Normalises a list of *keysyms*. The implementation of this function comes from: If the list (ignoring trailing ``NoSymbol`` entries) is a single *KeySym* ``K``, then the list is treated as if it were the list ``K NoSymbol K NoSymbol``. If the list (ignoring trailing ``NoSymbol`` entries) is a pair of *KeySyms* ``K1 K2``, then the list is treated as if it were the list ``K1 K2 K1 K2``. If the list (ignoring trailing ``NoSymbol`` entries) is a triple of *KeySyms* ``K1 K2 K3``, then the list is treated as if it were the list ``K1 K2 K3 NoSymbol``. This function will also group the *keysyms* using :func:`keysym_group`. :param keysyms: A list of keysyms. :return: the tuple ``(group_1, group_2)`` or ``None`` """ # Remove trailing NoSymbol stripped = list(reversed(list( itertools.dropwhile( lambda n: n == Xlib.XK.NoSymbol, reversed(keysym))))) if not stripped: return elif len(stripped) == 1: return ( keysym_group(stripped[0], Xlib.XK.NoSymbol), keysym_group(stripped[0], Xlib.XK.NoSymbol)) elif len(stripped) == 2: return ( keysym_group(stripped[0], stripped[1]), keysym_group(stripped[0], stripped[1])) elif len(stripped) == 3: return ( keysym_group(stripped[0], stripped[1]), keysym_group(stripped[2], Xlib.XK.NoSymbol)) elif len(stripped) >= 6: # TODO: Find out why this is necessary; using only the documented # behaviour may lead to only a US layout being used? return ( keysym_group(stripped[0], stripped[1]), keysym_group(stripped[4], stripped[5])) else: return ( keysym_group(stripped[0], stripped[1]), keysym_group(stripped[2], stripped[3])) def index_to_shift(display, index): """Converts an index in a *key code* list to the corresponding shift state. :param Xlib.display.Display display: The display for which to retrieve the shift mask. :param int index: The keyboard mapping *key code* index. :return: a shift mask """ return ( (1 << 0 if index & 1 else 0) | (alt_gr_mask(display) if index & 2 else 0)) def shift_to_index(display, shift): """Converts an index in a *key code* list to the corresponding shift state. :param Xlib.display.Display display: The display for which to retrieve the shift mask. :param int index: The keyboard mapping *key code* index. :retur: a shift mask """ return ( (1 if shift & 1 else 0) + (2 if shift & alt_gr_mask(display) else 0)) def keyboard_mapping(display): """Generates a mapping from *keysyms* to *key codes* and required modifier shift states. :param Xlib.display.Display display: The display for which to retrieve the keyboard mapping. :return: the keyboard mapping """ mapping = {} shift_mask = 1 << 0 group_mask = alt_gr_mask(display) # Iterate over all keysym lists in the keyboard mapping min_keycode = display.display.info.min_keycode keycode_count = display.display.info.max_keycode - min_keycode + 1 for index, keysyms in enumerate(display.get_keyboard_mapping( min_keycode, keycode_count)): key_code = index + min_keycode # Normalise the keysym list to yield a tuple containing the two groups normalized = keysym_normalize(keysyms) if not normalized: continue # Iterate over the groups to extract the shift and modifier state for groups, group in zip(normalized, (False, True)): for keysym, shift in zip(groups, (False, True)): if not keysym: continue shift_state = 0 \ | (shift_mask if shift else 0) \ | (group_mask if group else 0) # Prefer already known lesser shift states if keysym in mapping and mapping[keysym][1] < shift_state: continue mapping[keysym] = (key_code, shift_state) return mapping def symbol_to_keysym(symbol): """Converts a symbol name to a *keysym*. :param str symbol: The name of the symbol. :return: the corresponding *keysym*, or ``0`` if it cannot be found """ # First try simple translation keysym = Xlib.XK.string_to_keysym(symbol) if keysym: return keysym # If that fails, try checking a module attribute of Xlib.keysymdef.xkb if not keysym: try: return getattr(Xlib.keysymdef.xkb, 'XK_' + symbol, 0) except: return SYMBOLS.get(symbol, (0,))[0] class ListenerMixin(object): """A mixin for *X* event listeners. Subclasses should set a value for :attr:`_EVENTS` and implement :meth:`_handle`. """ #: The events for which to listen _EVENTS = tuple() #: We use this instance for parsing the binary data _EVENT_PARSER = Xlib.protocol.rq.EventField(None) class _WrappedException(Exception): """Raised by the handler wrapper when an exception is raised in the handler, or when the listener is stopped to escape the recording. In the former case, the root exception is passed as the first argument to the constructor, and in the latter case no arguments are passed. """ pass def _run(self): self._display_stop = Xlib.display.Display() self._display_record = Xlib.display.Display() with display_manager(self._display_record) as d: self._context = d.record_create_context( 0, [Xlib.ext.record.AllClients], [{ 'core_requests': (0, 0), 'core_replies': (0, 0), 'ext_requests': (0, 0, 0, 0), 'ext_replies': (0, 0, 0, 0), 'delivered_events': (0, 0), 'device_events': self._EVENTS, 'errors': (0, 0), 'client_started': False, 'client_died': False}]) try: self._initialize(self._display_stop) self._mark_ready() self._display_record.record_enable_context( self._context, self._handler) except self._WrappedException as e: if e.args: # TODO: Handle pass finally: self._display_record.record_free_context(self._context) self._display_stop.close() self._display_record.close() def _stop(self): if not hasattr(self, '_context'): self.wait() self._display_stop.record_disable_context(self._context) @AbstractListener._emitter def _handler(self, events): """The callback registered with *X* for mouse events. This method will parse the response and call the callbacks registered on initialisation. :param events: The events passed by *X*. This is a binary block parsable by :attr:`_EVENT_PARSER`. """ # If if not self.running: raise self._WrappedException() try: data = events.data while len(data): event, data = self._EVENT_PARSER.parse_binary_value( data, self._display_record.display, None, None) self._handle(self._display_stop, event) except self.StopException: raise except BaseException as e: raise self._WrappedException(e) def _initialize(self, display): """Initialises this listener. This method is called immediately before the event loop, from the handler thread. :param display: The display being used. """ pass def _handle(self, display, event): """The device specific callback handler. This method calls the appropriate callback registered when this listener was created based on the event. :param display: The display being used. :param event: The event. """ pass PK)\Hݧ[pynput/_util/xorg_keysyms.py# coding: utf-8 # Copyright (C) 2015-2016 Moses Palmér # # This program is free software: you can redistribute it and/or modify it under # the terms of the GNU Lesser General Public License as published by the Free # Software Foundation, either version 3 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 Lesser General Public License for more # details. # # You should have received a copy of the GNU Lesser General Public License # along with this program. If not, see . SYMBOLS = { 'dead_grave': (0xfe50, u'\u0300'), 'dead_acute': (0xfe51, u'\u0301'), 'dead_circumflex': (0xfe52, u'\u0302'), 'dead_tilde': (0xfe53, u'\u0303'), 'dead_macron': (0xfe54, u'\u0304'), 'dead_breve': (0xfe55, u'\u0306'), 'dead_abovedot': (0xfe56, u'\u0307'), 'dead_diaeresis': (0xfe57, u'\u0308'), 'dead_abovering': (0xfe58, u'\u030A'), 'dead_doubleacute': (0xfe59, u'\u030B'), 'dead_caron': (0xfe5a, u'\u030C'), 'dead_cedilla': (0xfe5b, u'\u0327'), 'dead_ogonek': (0xfe5c, u'\u0328'), 'dead_iota': (0xfe5d, u'\u0345'), 'dead_voiced_sound': (0xfe5e, None), 'dead_semivoiced_sound': (0xfe5f, None), 'dead_belowdot': (0xfe60, u'\u0323'), 'dead_hook': (0xfe61, u'\u0309'), 'dead_horn': (0xfe62, u'\u031B'), 'dead_stroke': (0xfe63, u'\u0335'), 'dead_abovecomma': (0xfe64, u'\u0315'), 'dead_abovereversedcomma': (0xfe65, u'\u0312'), 'dead_doublegrave': (0xfe66, u'\u030F'), 'dead_belowring': (0xfe67, u'\u0325'), 'dead_belowmacron': (0xfe68, u'\u0331'), 'dead_belowcircumflex': (0xfe69, u'\u032D'), 'dead_belowtilde': (0xfe6a, u'\u0330'), 'dead_belowbreve': (0xfe6b, u'\u032E'), 'dead_belowdiaeresis': (0xfe6c, u'\u0324'), 'dead_invertedbreve': (0xfe6d, u'\u032F'), 'dead_belowcomma': (0xfe6e, u'\u0326'), 'dead_currency': (0xfe6f, None), 'dead_lowline': (0xfe90, u'\u0332'), 'dead_aboveverticalline': (0xfe91, u'\u030D'), 'dead_belowverticalline': (0xfe92, u'\u0329'), 'dead_longsolidusoverlay': (0xfe93, u'\u0338'), 'dead_a': (0xfe80, None), 'dead_A': (0xfe81, None), 'dead_e': (0xfe82, None), 'dead_E': (0xfe83, None), 'dead_i': (0xfe84, None), 'dead_I': (0xfe85, None), 'dead_o': (0xfe86, None), 'dead_O': (0xfe87, None), 'dead_u': (0xfe88, None), 'dead_U': (0xfe89, None), 'dead_small_schwa': (0xfe8a, None), 'dead_capital_schwa': (0xfe8b, None), 'dead_greek': (0xfe8c, None), 'space': (0x0020, u'\u0020'), 'exclam': (0x0021, u'\u0021'), 'quotedbl': (0x0022, u'\u0022'), 'numbersign': (0x0023, u'\u0023'), 'dollar': (0x0024, u'\u0024'), 'percent': (0x0025, u'\u0025'), 'ampersand': (0x0026, u'\u0026'), 'apostrophe': (0x0027, u'\u0027'), 'parenleft': (0x0028, u'\u0028'), 'parenright': (0x0029, u'\u0029'), 'asterisk': (0x002a, u'\u002A'), 'plus': (0x002b, u'\u002B'), 'comma': (0x002c, u'\u002C'), 'minus': (0x002d, u'\u002D'), 'period': (0x002e, u'\u002E'), 'slash': (0x002f, u'\u002F'), '0': (0x0030, u'\u0030'), '1': (0x0031, u'\u0031'), '2': (0x0032, u'\u0032'), '3': (0x0033, u'\u0033'), '4': (0x0034, u'\u0034'), '5': (0x0035, u'\u0035'), '6': (0x0036, u'\u0036'), '7': (0x0037, u'\u0037'), '8': (0x0038, u'\u0038'), '9': (0x0039, u'\u0039'), 'colon': (0x003a, u'\u003A'), 'semicolon': (0x003b, u'\u003B'), 'less': (0x003c, u'\u003C'), 'equal': (0x003d, u'\u003D'), 'greater': (0x003e, u'\u003E'), 'question': (0x003f, u'\u003F'), 'at': (0x0040, u'\u0040'), 'A': (0x0041, u'\u0041'), 'B': (0x0042, u'\u0042'), 'C': (0x0043, u'\u0043'), 'D': (0x0044, u'\u0044'), 'E': (0x0045, u'\u0045'), 'F': (0x0046, u'\u0046'), 'G': (0x0047, u'\u0047'), 'H': (0x0048, u'\u0048'), 'I': (0x0049, u'\u0049'), 'J': (0x004a, u'\u004A'), 'K': (0x004b, u'\u004B'), 'L': (0x004c, u'\u004C'), 'M': (0x004d, u'\u004D'), 'N': (0x004e, u'\u004E'), 'O': (0x004f, u'\u004F'), 'P': (0x0050, u'\u0050'), 'Q': (0x0051, u'\u0051'), 'R': (0x0052, u'\u0052'), 'S': (0x0053, u'\u0053'), 'T': (0x0054, u'\u0054'), 'U': (0x0055, u'\u0055'), 'V': (0x0056, u'\u0056'), 'W': (0x0057, u'\u0057'), 'X': (0x0058, u'\u0058'), 'Y': (0x0059, u'\u0059'), 'Z': (0x005a, u'\u005A'), 'bracketleft': (0x005b, u'\u005B'), 'backslash': (0x005c, u'\u005C'), 'bracketright': (0x005d, u'\u005D'), 'asciicircum': (0x005e, u'\u005E'), 'underscore': (0x005f, u'\u005F'), 'grave': (0x0060, u'\u0060'), 'a': (0x0061, u'\u0061'), 'b': (0x0062, u'\u0062'), 'c': (0x0063, u'\u0063'), 'd': (0x0064, u'\u0064'), 'e': (0x0065, u'\u0065'), 'f': (0x0066, u'\u0066'), 'g': (0x0067, u'\u0067'), 'h': (0x0068, u'\u0068'), 'i': (0x0069, u'\u0069'), 'j': (0x006a, u'\u006A'), 'k': (0x006b, u'\u006B'), 'l': (0x006c, u'\u006C'), 'm': (0x006d, u'\u006D'), 'n': (0x006e, u'\u006E'), 'o': (0x006f, u'\u006F'), 'p': (0x0070, u'\u0070'), 'q': (0x0071, u'\u0071'), 'r': (0x0072, u'\u0072'), 's': (0x0073, u'\u0073'), 't': (0x0074, u'\u0074'), 'u': (0x0075, u'\u0075'), 'v': (0x0076, u'\u0076'), 'w': (0x0077, u'\u0077'), 'x': (0x0078, u'\u0078'), 'y': (0x0079, u'\u0079'), 'z': (0x007a, u'\u007A'), 'braceleft': (0x007b, u'\u007B'), 'bar': (0x007c, u'\u007C'), 'braceright': (0x007d, u'\u007D'), 'asciitilde': (0x007e, u'\u007E'), 'nobreakspace': (0x00a0, u'\u00A0'), 'exclamdown': (0x00a1, u'\u00A1'), 'cent': (0x00a2, u'\u00A2'), 'sterling': (0x00a3, u'\u00A3'), 'currency': (0x00a4, u'\u00A4'), 'yen': (0x00a5, u'\u00A5'), 'brokenbar': (0x00a6, u'\u00A6'), 'section': (0x00a7, u'\u00A7'), 'diaeresis': (0x00a8, u'\u00A8'), 'copyright': (0x00a9, u'\u00A9'), 'ordfeminine': (0x00aa, u'\u00AA'), 'guillemotleft': (0x00ab, u'\u00AB'), 'notsign': (0x00ac, u'\u00AC'), 'hyphen': (0x00ad, u'\u00AD'), 'registered': (0x00ae, u'\u00AE'), 'macron': (0x00af, u'\u00AF'), 'degree': (0x00b0, u'\u00B0'), 'plusminus': (0x00b1, u'\u00B1'), 'twosuperior': (0x00b2, u'\u00B2'), 'threesuperior': (0x00b3, u'\u00B3'), 'acute': (0x00b4, u'\u00B4'), 'mu': (0x00b5, u'\u00B5'), 'paragraph': (0x00b6, u'\u00B6'), 'periodcentered': (0x00b7, u'\u00B7'), 'cedilla': (0x00b8, u'\u00B8'), 'onesuperior': (0x00b9, u'\u00B9'), 'masculine': (0x00ba, u'\u00BA'), 'guillemotright': (0x00bb, u'\u00BB'), 'onequarter': (0x00bc, u'\u00BC'), 'onehalf': (0x00bd, u'\u00BD'), 'threequarters': (0x00be, u'\u00BE'), 'questiondown': (0x00bf, u'\u00BF'), 'Agrave': (0x00c0, u'\u00C0'), 'Aacute': (0x00c1, u'\u00C1'), 'Acircumflex': (0x00c2, u'\u00C2'), 'Atilde': (0x00c3, u'\u00C3'), 'Adiaeresis': (0x00c4, u'\u00C4'), 'Aring': (0x00c5, u'\u00C5'), 'AE': (0x00c6, u'\u00C6'), 'Ccedilla': (0x00c7, u'\u00C7'), 'Egrave': (0x00c8, u'\u00C8'), 'Eacute': (0x00c9, u'\u00C9'), 'Ecircumflex': (0x00ca, u'\u00CA'), 'Ediaeresis': (0x00cb, u'\u00CB'), 'Igrave': (0x00cc, u'\u00CC'), 'Iacute': (0x00cd, u'\u00CD'), 'Icircumflex': (0x00ce, u'\u00CE'), 'Idiaeresis': (0x00cf, u'\u00CF'), 'ETH': (0x00d0, u'\u00D0'), 'Ntilde': (0x00d1, u'\u00D1'), 'Ograve': (0x00d2, u'\u00D2'), 'Oacute': (0x00d3, u'\u00D3'), 'Ocircumflex': (0x00d4, u'\u00D4'), 'Otilde': (0x00d5, u'\u00D5'), 'Odiaeresis': (0x00d6, u'\u00D6'), 'multiply': (0x00d7, u'\u00D7'), 'Oslash': (0x00d8, u'\u00D8'), 'Ooblique': (0x00d8, u'\u00D8'), 'Ugrave': (0x00d9, u'\u00D9'), 'Uacute': (0x00da, u'\u00DA'), 'Ucircumflex': (0x00db, u'\u00DB'), 'Udiaeresis': (0x00dc, u'\u00DC'), 'Yacute': (0x00dd, u'\u00DD'), 'THORN': (0x00de, u'\u00DE'), 'ssharp': (0x00df, u'\u00DF'), 'agrave': (0x00e0, u'\u00E0'), 'aacute': (0x00e1, u'\u00E1'), 'acircumflex': (0x00e2, u'\u00E2'), 'atilde': (0x00e3, u'\u00E3'), 'adiaeresis': (0x00e4, u'\u00E4'), 'aring': (0x00e5, u'\u00E5'), 'ae': (0x00e6, u'\u00E6'), 'ccedilla': (0x00e7, u'\u00E7'), 'egrave': (0x00e8, u'\u00E8'), 'eacute': (0x00e9, u'\u00E9'), 'ecircumflex': (0x00ea, u'\u00EA'), 'ediaeresis': (0x00eb, u'\u00EB'), 'igrave': (0x00ec, u'\u00EC'), 'iacute': (0x00ed, u'\u00ED'), 'icircumflex': (0x00ee, u'\u00EE'), 'idiaeresis': (0x00ef, u'\u00EF'), 'eth': (0x00f0, u'\u00F0'), 'ntilde': (0x00f1, u'\u00F1'), 'ograve': (0x00f2, u'\u00F2'), 'oacute': (0x00f3, u'\u00F3'), 'ocircumflex': (0x00f4, u'\u00F4'), 'otilde': (0x00f5, u'\u00F5'), 'odiaeresis': (0x00f6, u'\u00F6'), 'division': (0x00f7, u'\u00F7'), 'oslash': (0x00f8, u'\u00F8'), 'ooblique': (0x00f8, u'\u00F8'), 'ugrave': (0x00f9, u'\u00F9'), 'uacute': (0x00fa, u'\u00FA'), 'ucircumflex': (0x00fb, u'\u00FB'), 'udiaeresis': (0x00fc, u'\u00FC'), 'yacute': (0x00fd, u'\u00FD'), 'thorn': (0x00fe, u'\u00FE'), 'ydiaeresis': (0x00ff, u'\u00FF'), 'Aogonek': (0x01a1, u'\u0104'), 'breve': (0x01a2, u'\u02D8'), 'Lstroke': (0x01a3, u'\u0141'), 'Lcaron': (0x01a5, u'\u013D'), 'Sacute': (0x01a6, u'\u015A'), 'Scaron': (0x01a9, u'\u0160'), 'Scedilla': (0x01aa, u'\u015E'), 'Tcaron': (0x01ab, u'\u0164'), 'Zacute': (0x01ac, u'\u0179'), 'Zcaron': (0x01ae, u'\u017D'), 'Zabovedot': (0x01af, u'\u017B'), 'aogonek': (0x01b1, u'\u0105'), 'ogonek': (0x01b2, u'\u02DB'), 'lstroke': (0x01b3, u'\u0142'), 'lcaron': (0x01b5, u'\u013E'), 'sacute': (0x01b6, u'\u015B'), 'caron': (0x01b7, u'\u02C7'), 'scaron': (0x01b9, u'\u0161'), 'scedilla': (0x01ba, u'\u015F'), 'tcaron': (0x01bb, u'\u0165'), 'zacute': (0x01bc, u'\u017A'), 'doubleacute': (0x01bd, u'\u02DD'), 'zcaron': (0x01be, u'\u017E'), 'zabovedot': (0x01bf, u'\u017C'), 'Racute': (0x01c0, u'\u0154'), 'Abreve': (0x01c3, u'\u0102'), 'Lacute': (0x01c5, u'\u0139'), 'Cacute': (0x01c6, u'\u0106'), 'Ccaron': (0x01c8, u'\u010C'), 'Eogonek': (0x01ca, u'\u0118'), 'Ecaron': (0x01cc, u'\u011A'), 'Dcaron': (0x01cf, u'\u010E'), 'Dstroke': (0x01d0, u'\u0110'), 'Nacute': (0x01d1, u'\u0143'), 'Ncaron': (0x01d2, u'\u0147'), 'Odoubleacute': (0x01d5, u'\u0150'), 'Rcaron': (0x01d8, u'\u0158'), 'Uring': (0x01d9, u'\u016E'), 'Udoubleacute': (0x01db, u'\u0170'), 'Tcedilla': (0x01de, u'\u0162'), 'racute': (0x01e0, u'\u0155'), 'abreve': (0x01e3, u'\u0103'), 'lacute': (0x01e5, u'\u013A'), 'cacute': (0x01e6, u'\u0107'), 'ccaron': (0x01e8, u'\u010D'), 'eogonek': (0x01ea, u'\u0119'), 'ecaron': (0x01ec, u'\u011B'), 'dcaron': (0x01ef, u'\u010F'), 'dstroke': (0x01f0, u'\u0111'), 'nacute': (0x01f1, u'\u0144'), 'ncaron': (0x01f2, u'\u0148'), 'odoubleacute': (0x01f5, u'\u0151'), 'rcaron': (0x01f8, u'\u0159'), 'uring': (0x01f9, u'\u016F'), 'udoubleacute': (0x01fb, u'\u0171'), 'tcedilla': (0x01fe, u'\u0163'), 'abovedot': (0x01ff, u'\u02D9'), 'Hstroke': (0x02a1, u'\u0126'), 'Hcircumflex': (0x02a6, u'\u0124'), 'Iabovedot': (0x02a9, u'\u0130'), 'Gbreve': (0x02ab, u'\u011E'), 'Jcircumflex': (0x02ac, u'\u0134'), 'hstroke': (0x02b1, u'\u0127'), 'hcircumflex': (0x02b6, u'\u0125'), 'idotless': (0x02b9, u'\u0131'), 'gbreve': (0x02bb, u'\u011F'), 'jcircumflex': (0x02bc, u'\u0135'), 'Cabovedot': (0x02c5, u'\u010A'), 'Ccircumflex': (0x02c6, u'\u0108'), 'Gabovedot': (0x02d5, u'\u0120'), 'Gcircumflex': (0x02d8, u'\u011C'), 'Ubreve': (0x02dd, u'\u016C'), 'Scircumflex': (0x02de, u'\u015C'), 'cabovedot': (0x02e5, u'\u010B'), 'ccircumflex': (0x02e6, u'\u0109'), 'gabovedot': (0x02f5, u'\u0121'), 'gcircumflex': (0x02f8, u'\u011D'), 'ubreve': (0x02fd, u'\u016D'), 'scircumflex': (0x02fe, u'\u015D'), 'kra': (0x03a2, u'\u0138'), 'Rcedilla': (0x03a3, u'\u0156'), 'Itilde': (0x03a5, u'\u0128'), 'Lcedilla': (0x03a6, u'\u013B'), 'Emacron': (0x03aa, u'\u0112'), 'Gcedilla': (0x03ab, u'\u0122'), 'Tslash': (0x03ac, u'\u0166'), 'rcedilla': (0x03b3, u'\u0157'), 'itilde': (0x03b5, u'\u0129'), 'lcedilla': (0x03b6, u'\u013C'), 'emacron': (0x03ba, u'\u0113'), 'gcedilla': (0x03bb, u'\u0123'), 'tslash': (0x03bc, u'\u0167'), 'ENG': (0x03bd, u'\u014A'), 'eng': (0x03bf, u'\u014B'), 'Amacron': (0x03c0, u'\u0100'), 'Iogonek': (0x03c7, u'\u012E'), 'Eabovedot': (0x03cc, u'\u0116'), 'Imacron': (0x03cf, u'\u012A'), 'Ncedilla': (0x03d1, u'\u0145'), 'Omacron': (0x03d2, u'\u014C'), 'Kcedilla': (0x03d3, u'\u0136'), 'Uogonek': (0x03d9, u'\u0172'), 'Utilde': (0x03dd, u'\u0168'), 'Umacron': (0x03de, u'\u016A'), 'amacron': (0x03e0, u'\u0101'), 'iogonek': (0x03e7, u'\u012F'), 'eabovedot': (0x03ec, u'\u0117'), 'imacron': (0x03ef, u'\u012B'), 'ncedilla': (0x03f1, u'\u0146'), 'omacron': (0x03f2, u'\u014D'), 'kcedilla': (0x03f3, u'\u0137'), 'uogonek': (0x03f9, u'\u0173'), 'utilde': (0x03fd, u'\u0169'), 'umacron': (0x03fe, u'\u016B'), 'Wcircumflex': (0x1000174, u'\u0174'), 'wcircumflex': (0x1000175, u'\u0175'), 'Ycircumflex': (0x1000176, u'\u0176'), 'ycircumflex': (0x1000177, u'\u0177'), 'Babovedot': (0x1001e02, u'\u1E02'), 'babovedot': (0x1001e03, u'\u1E03'), 'Dabovedot': (0x1001e0a, u'\u1E0A'), 'dabovedot': (0x1001e0b, u'\u1E0B'), 'Fabovedot': (0x1001e1e, u'\u1E1E'), 'fabovedot': (0x1001e1f, u'\u1E1F'), 'Mabovedot': (0x1001e40, u'\u1E40'), 'mabovedot': (0x1001e41, u'\u1E41'), 'Pabovedot': (0x1001e56, u'\u1E56'), 'pabovedot': (0x1001e57, u'\u1E57'), 'Sabovedot': (0x1001e60, u'\u1E60'), 'sabovedot': (0x1001e61, u'\u1E61'), 'Tabovedot': (0x1001e6a, u'\u1E6A'), 'tabovedot': (0x1001e6b, u'\u1E6B'), 'Wgrave': (0x1001e80, u'\u1E80'), 'wgrave': (0x1001e81, u'\u1E81'), 'Wacute': (0x1001e82, u'\u1E82'), 'wacute': (0x1001e83, u'\u1E83'), 'Wdiaeresis': (0x1001e84, u'\u1E84'), 'wdiaeresis': (0x1001e85, u'\u1E85'), 'Ygrave': (0x1001ef2, u'\u1EF2'), 'ygrave': (0x1001ef3, u'\u1EF3'), 'OE': (0x13bc, u'\u0152'), 'oe': (0x13bd, u'\u0153'), 'Ydiaeresis': (0x13be, u'\u0178'), 'overline': (0x047e, u'\u203E'), 'kana_fullstop': (0x04a1, u'\u3002'), 'kana_openingbracket': (0x04a2, u'\u300C'), 'kana_closingbracket': (0x04a3, u'\u300D'), 'kana_comma': (0x04a4, u'\u3001'), 'kana_conjunctive': (0x04a5, u'\u30FB'), 'kana_WO': (0x04a6, u'\u30F2'), 'kana_a': (0x04a7, u'\u30A1'), 'kana_i': (0x04a8, u'\u30A3'), 'kana_u': (0x04a9, u'\u30A5'), 'kana_e': (0x04aa, u'\u30A7'), 'kana_o': (0x04ab, u'\u30A9'), 'kana_ya': (0x04ac, u'\u30E3'), 'kana_yu': (0x04ad, u'\u30E5'), 'kana_yo': (0x04ae, u'\u30E7'), 'kana_tsu': (0x04af, u'\u30C3'), 'prolongedsound': (0x04b0, u'\u30FC'), 'kana_A': (0x04b1, u'\u30A2'), 'kana_I': (0x04b2, u'\u30A4'), 'kana_U': (0x04b3, u'\u30A6'), 'kana_E': (0x04b4, u'\u30A8'), 'kana_O': (0x04b5, u'\u30AA'), 'kana_KA': (0x04b6, u'\u30AB'), 'kana_KI': (0x04b7, u'\u30AD'), 'kana_KU': (0x04b8, u'\u30AF'), 'kana_KE': (0x04b9, u'\u30B1'), 'kana_KO': (0x04ba, u'\u30B3'), 'kana_SA': (0x04bb, u'\u30B5'), 'kana_SHI': (0x04bc, u'\u30B7'), 'kana_SU': (0x04bd, u'\u30B9'), 'kana_SE': (0x04be, u'\u30BB'), 'kana_SO': (0x04bf, u'\u30BD'), 'kana_TA': (0x04c0, u'\u30BF'), 'kana_CHI': (0x04c1, u'\u30C1'), 'kana_TSU': (0x04c2, u'\u30C4'), 'kana_TE': (0x04c3, u'\u30C6'), 'kana_TO': (0x04c4, u'\u30C8'), 'kana_NA': (0x04c5, u'\u30CA'), 'kana_NI': (0x04c6, u'\u30CB'), 'kana_NU': (0x04c7, u'\u30CC'), 'kana_NE': (0x04c8, u'\u30CD'), 'kana_NO': (0x04c9, u'\u30CE'), 'kana_HA': (0x04ca, u'\u30CF'), 'kana_HI': (0x04cb, u'\u30D2'), 'kana_FU': (0x04cc, u'\u30D5'), 'kana_HE': (0x04cd, u'\u30D8'), 'kana_HO': (0x04ce, u'\u30DB'), 'kana_MA': (0x04cf, u'\u30DE'), 'kana_MI': (0x04d0, u'\u30DF'), 'kana_MU': (0x04d1, u'\u30E0'), 'kana_ME': (0x04d2, u'\u30E1'), 'kana_MO': (0x04d3, u'\u30E2'), 'kana_YA': (0x04d4, u'\u30E4'), 'kana_YU': (0x04d5, u'\u30E6'), 'kana_YO': (0x04d6, u'\u30E8'), 'kana_RA': (0x04d7, u'\u30E9'), 'kana_RI': (0x04d8, u'\u30EA'), 'kana_RU': (0x04d9, u'\u30EB'), 'kana_RE': (0x04da, u'\u30EC'), 'kana_RO': (0x04db, u'\u30ED'), 'kana_WA': (0x04dc, u'\u30EF'), 'kana_N': (0x04dd, u'\u30F3'), 'voicedsound': (0x04de, u'\u309B'), 'semivoicedsound': (0x04df, u'\u309C'), 'Farsi_0': (0x10006f0, u'\u06F0'), 'Farsi_1': (0x10006f1, u'\u06F1'), 'Farsi_2': (0x10006f2, u'\u06F2'), 'Farsi_3': (0x10006f3, u'\u06F3'), 'Farsi_4': (0x10006f4, u'\u06F4'), 'Farsi_5': (0x10006f5, u'\u06F5'), 'Farsi_6': (0x10006f6, u'\u06F6'), 'Farsi_7': (0x10006f7, u'\u06F7'), 'Farsi_8': (0x10006f8, u'\u06F8'), 'Farsi_9': (0x10006f9, u'\u06F9'), 'Arabic_percent': (0x100066a, u'\u066A'), 'Arabic_superscript_alef': (0x1000670, u'\u0670'), 'Arabic_tteh': (0x1000679, u'\u0679'), 'Arabic_peh': (0x100067e, u'\u067E'), 'Arabic_tcheh': (0x1000686, u'\u0686'), 'Arabic_ddal': (0x1000688, u'\u0688'), 'Arabic_rreh': (0x1000691, u'\u0691'), 'Arabic_comma': (0x05ac, u'\u060C'), 'Arabic_fullstop': (0x10006d4, u'\u06D4'), 'Arabic_0': (0x1000660, u'\u0660'), 'Arabic_1': (0x1000661, u'\u0661'), 'Arabic_2': (0x1000662, u'\u0662'), 'Arabic_3': (0x1000663, u'\u0663'), 'Arabic_4': (0x1000664, u'\u0664'), 'Arabic_5': (0x1000665, u'\u0665'), 'Arabic_6': (0x1000666, u'\u0666'), 'Arabic_7': (0x1000667, u'\u0667'), 'Arabic_8': (0x1000668, u'\u0668'), 'Arabic_9': (0x1000669, u'\u0669'), 'Arabic_semicolon': (0x05bb, u'\u061B'), 'Arabic_question_mark': (0x05bf, u'\u061F'), 'Arabic_hamza': (0x05c1, u'\u0621'), 'Arabic_maddaonalef': (0x05c2, u'\u0622'), 'Arabic_hamzaonalef': (0x05c3, u'\u0623'), 'Arabic_hamzaonwaw': (0x05c4, u'\u0624'), 'Arabic_hamzaunderalef': (0x05c5, u'\u0625'), 'Arabic_hamzaonyeh': (0x05c6, u'\u0626'), 'Arabic_alef': (0x05c7, u'\u0627'), 'Arabic_beh': (0x05c8, u'\u0628'), 'Arabic_tehmarbuta': (0x05c9, u'\u0629'), 'Arabic_teh': (0x05ca, u'\u062A'), 'Arabic_theh': (0x05cb, u'\u062B'), 'Arabic_jeem': (0x05cc, u'\u062C'), 'Arabic_hah': (0x05cd, u'\u062D'), 'Arabic_khah': (0x05ce, u'\u062E'), 'Arabic_dal': (0x05cf, u'\u062F'), 'Arabic_thal': (0x05d0, u'\u0630'), 'Arabic_ra': (0x05d1, u'\u0631'), 'Arabic_zain': (0x05d2, u'\u0632'), 'Arabic_seen': (0x05d3, u'\u0633'), 'Arabic_sheen': (0x05d4, u'\u0634'), 'Arabic_sad': (0x05d5, u'\u0635'), 'Arabic_dad': (0x05d6, u'\u0636'), 'Arabic_tah': (0x05d7, u'\u0637'), 'Arabic_zah': (0x05d8, u'\u0638'), 'Arabic_ain': (0x05d9, u'\u0639'), 'Arabic_ghain': (0x05da, u'\u063A'), 'Arabic_tatweel': (0x05e0, u'\u0640'), 'Arabic_feh': (0x05e1, u'\u0641'), 'Arabic_qaf': (0x05e2, u'\u0642'), 'Arabic_kaf': (0x05e3, u'\u0643'), 'Arabic_lam': (0x05e4, u'\u0644'), 'Arabic_meem': (0x05e5, u'\u0645'), 'Arabic_noon': (0x05e6, u'\u0646'), 'Arabic_ha': (0x05e7, u'\u0647'), 'Arabic_waw': (0x05e8, u'\u0648'), 'Arabic_alefmaksura': (0x05e9, u'\u0649'), 'Arabic_yeh': (0x05ea, u'\u064A'), 'Arabic_fathatan': (0x05eb, u'\u064B'), 'Arabic_dammatan': (0x05ec, u'\u064C'), 'Arabic_kasratan': (0x05ed, u'\u064D'), 'Arabic_fatha': (0x05ee, u'\u064E'), 'Arabic_damma': (0x05ef, u'\u064F'), 'Arabic_kasra': (0x05f0, u'\u0650'), 'Arabic_shadda': (0x05f1, u'\u0651'), 'Arabic_sukun': (0x05f2, u'\u0652'), 'Arabic_madda_above': (0x1000653, u'\u0653'), 'Arabic_hamza_above': (0x1000654, u'\u0654'), 'Arabic_hamza_below': (0x1000655, u'\u0655'), 'Arabic_jeh': (0x1000698, u'\u0698'), 'Arabic_veh': (0x10006a4, u'\u06A4'), 'Arabic_keheh': (0x10006a9, u'\u06A9'), 'Arabic_gaf': (0x10006af, u'\u06AF'), 'Arabic_noon_ghunna': (0x10006ba, u'\u06BA'), 'Arabic_heh_doachashmee': (0x10006be, u'\u06BE'), 'Farsi_yeh': (0x10006cc, u'\u06CC'), 'Arabic_farsi_yeh': (0x10006cc, u'\u06CC'), 'Arabic_yeh_baree': (0x10006d2, u'\u06D2'), 'Arabic_heh_goal': (0x10006c1, u'\u06C1'), 'Cyrillic_GHE_bar': (0x1000492, u'\u0492'), 'Cyrillic_ghe_bar': (0x1000493, u'\u0493'), 'Cyrillic_ZHE_descender': (0x1000496, u'\u0496'), 'Cyrillic_zhe_descender': (0x1000497, u'\u0497'), 'Cyrillic_KA_descender': (0x100049a, u'\u049A'), 'Cyrillic_ka_descender': (0x100049b, u'\u049B'), 'Cyrillic_KA_vertstroke': (0x100049c, u'\u049C'), 'Cyrillic_ka_vertstroke': (0x100049d, u'\u049D'), 'Cyrillic_EN_descender': (0x10004a2, u'\u04A2'), 'Cyrillic_en_descender': (0x10004a3, u'\u04A3'), 'Cyrillic_U_straight': (0x10004ae, u'\u04AE'), 'Cyrillic_u_straight': (0x10004af, u'\u04AF'), 'Cyrillic_U_straight_bar': (0x10004b0, u'\u04B0'), 'Cyrillic_u_straight_bar': (0x10004b1, u'\u04B1'), 'Cyrillic_HA_descender': (0x10004b2, u'\u04B2'), 'Cyrillic_ha_descender': (0x10004b3, u'\u04B3'), 'Cyrillic_CHE_descender': (0x10004b6, u'\u04B6'), 'Cyrillic_che_descender': (0x10004b7, u'\u04B7'), 'Cyrillic_CHE_vertstroke': (0x10004b8, u'\u04B8'), 'Cyrillic_che_vertstroke': (0x10004b9, u'\u04B9'), 'Cyrillic_SHHA': (0x10004ba, u'\u04BA'), 'Cyrillic_shha': (0x10004bb, u'\u04BB'), 'Cyrillic_SCHWA': (0x10004d8, u'\u04D8'), 'Cyrillic_schwa': (0x10004d9, u'\u04D9'), 'Cyrillic_I_macron': (0x10004e2, u'\u04E2'), 'Cyrillic_i_macron': (0x10004e3, u'\u04E3'), 'Cyrillic_O_bar': (0x10004e8, u'\u04E8'), 'Cyrillic_o_bar': (0x10004e9, u'\u04E9'), 'Cyrillic_U_macron': (0x10004ee, u'\u04EE'), 'Cyrillic_u_macron': (0x10004ef, u'\u04EF'), 'Serbian_dje': (0x06a1, u'\u0452'), 'Macedonia_gje': (0x06a2, u'\u0453'), 'Cyrillic_io': (0x06a3, u'\u0451'), 'Ukrainian_ie': (0x06a4, u'\u0454'), 'Macedonia_dse': (0x06a5, u'\u0455'), 'Ukrainian_i': (0x06a6, u'\u0456'), 'Ukrainian_yi': (0x06a7, u'\u0457'), 'Cyrillic_je': (0x06a8, u'\u0458'), 'Cyrillic_lje': (0x06a9, u'\u0459'), 'Cyrillic_nje': (0x06aa, u'\u045A'), 'Serbian_tshe': (0x06ab, u'\u045B'), 'Macedonia_kje': (0x06ac, u'\u045C'), 'Ukrainian_ghe_with_upturn': (0x06ad, u'\u0491'), 'Byelorussian_shortu': (0x06ae, u'\u045E'), 'Cyrillic_dzhe': (0x06af, u'\u045F'), 'numerosign': (0x06b0, u'\u2116'), 'Serbian_DJE': (0x06b1, u'\u0402'), 'Macedonia_GJE': (0x06b2, u'\u0403'), 'Cyrillic_IO': (0x06b3, u'\u0401'), 'Ukrainian_IE': (0x06b4, u'\u0404'), 'Macedonia_DSE': (0x06b5, u'\u0405'), 'Ukrainian_I': (0x06b6, u'\u0406'), 'Ukrainian_YI': (0x06b7, u'\u0407'), 'Cyrillic_JE': (0x06b8, u'\u0408'), 'Cyrillic_LJE': (0x06b9, u'\u0409'), 'Cyrillic_NJE': (0x06ba, u'\u040A'), 'Serbian_TSHE': (0x06bb, u'\u040B'), 'Macedonia_KJE': (0x06bc, u'\u040C'), 'Ukrainian_GHE_WITH_UPTURN': (0x06bd, u'\u0490'), 'Byelorussian_SHORTU': (0x06be, u'\u040E'), 'Cyrillic_DZHE': (0x06bf, u'\u040F'), 'Cyrillic_yu': (0x06c0, u'\u044E'), 'Cyrillic_a': (0x06c1, u'\u0430'), 'Cyrillic_be': (0x06c2, u'\u0431'), 'Cyrillic_tse': (0x06c3, u'\u0446'), 'Cyrillic_de': (0x06c4, u'\u0434'), 'Cyrillic_ie': (0x06c5, u'\u0435'), 'Cyrillic_ef': (0x06c6, u'\u0444'), 'Cyrillic_ghe': (0x06c7, u'\u0433'), 'Cyrillic_ha': (0x06c8, u'\u0445'), 'Cyrillic_i': (0x06c9, u'\u0438'), 'Cyrillic_shorti': (0x06ca, u'\u0439'), 'Cyrillic_ka': (0x06cb, u'\u043A'), 'Cyrillic_el': (0x06cc, u'\u043B'), 'Cyrillic_em': (0x06cd, u'\u043C'), 'Cyrillic_en': (0x06ce, u'\u043D'), 'Cyrillic_o': (0x06cf, u'\u043E'), 'Cyrillic_pe': (0x06d0, u'\u043F'), 'Cyrillic_ya': (0x06d1, u'\u044F'), 'Cyrillic_er': (0x06d2, u'\u0440'), 'Cyrillic_es': (0x06d3, u'\u0441'), 'Cyrillic_te': (0x06d4, u'\u0442'), 'Cyrillic_u': (0x06d5, u'\u0443'), 'Cyrillic_zhe': (0x06d6, u'\u0436'), 'Cyrillic_ve': (0x06d7, u'\u0432'), 'Cyrillic_softsign': (0x06d8, u'\u044C'), 'Cyrillic_yeru': (0x06d9, u'\u044B'), 'Cyrillic_ze': (0x06da, u'\u0437'), 'Cyrillic_sha': (0x06db, u'\u0448'), 'Cyrillic_e': (0x06dc, u'\u044D'), 'Cyrillic_shcha': (0x06dd, u'\u0449'), 'Cyrillic_che': (0x06de, u'\u0447'), 'Cyrillic_hardsign': (0x06df, u'\u044A'), 'Cyrillic_YU': (0x06e0, u'\u042E'), 'Cyrillic_A': (0x06e1, u'\u0410'), 'Cyrillic_BE': (0x06e2, u'\u0411'), 'Cyrillic_TSE': (0x06e3, u'\u0426'), 'Cyrillic_DE': (0x06e4, u'\u0414'), 'Cyrillic_IE': (0x06e5, u'\u0415'), 'Cyrillic_EF': (0x06e6, u'\u0424'), 'Cyrillic_GHE': (0x06e7, u'\u0413'), 'Cyrillic_HA': (0x06e8, u'\u0425'), 'Cyrillic_I': (0x06e9, u'\u0418'), 'Cyrillic_SHORTI': (0x06ea, u'\u0419'), 'Cyrillic_KA': (0x06eb, u'\u041A'), 'Cyrillic_EL': (0x06ec, u'\u041B'), 'Cyrillic_EM': (0x06ed, u'\u041C'), 'Cyrillic_EN': (0x06ee, u'\u041D'), 'Cyrillic_O': (0x06ef, u'\u041E'), 'Cyrillic_PE': (0x06f0, u'\u041F'), 'Cyrillic_YA': (0x06f1, u'\u042F'), 'Cyrillic_ER': (0x06f2, u'\u0420'), 'Cyrillic_ES': (0x06f3, u'\u0421'), 'Cyrillic_TE': (0x06f4, u'\u0422'), 'Cyrillic_U': (0x06f5, u'\u0423'), 'Cyrillic_ZHE': (0x06f6, u'\u0416'), 'Cyrillic_VE': (0x06f7, u'\u0412'), 'Cyrillic_SOFTSIGN': (0x06f8, u'\u042C'), 'Cyrillic_YERU': (0x06f9, u'\u042B'), 'Cyrillic_ZE': (0x06fa, u'\u0417'), 'Cyrillic_SHA': (0x06fb, u'\u0428'), 'Cyrillic_E': (0x06fc, u'\u042D'), 'Cyrillic_SHCHA': (0x06fd, u'\u0429'), 'Cyrillic_CHE': (0x06fe, u'\u0427'), 'Cyrillic_HARDSIGN': (0x06ff, u'\u042A'), 'Greek_ALPHAaccent': (0x07a1, u'\u0386'), 'Greek_EPSILONaccent': (0x07a2, u'\u0388'), 'Greek_ETAaccent': (0x07a3, u'\u0389'), 'Greek_IOTAaccent': (0x07a4, u'\u038A'), 'Greek_IOTAdieresis': (0x07a5, u'\u03AA'), 'Greek_OMICRONaccent': (0x07a7, u'\u038C'), 'Greek_UPSILONaccent': (0x07a8, u'\u038E'), 'Greek_UPSILONdieresis': (0x07a9, u'\u03AB'), 'Greek_OMEGAaccent': (0x07ab, u'\u038F'), 'Greek_accentdieresis': (0x07ae, u'\u0385'), 'Greek_horizbar': (0x07af, u'\u2015'), 'Greek_alphaaccent': (0x07b1, u'\u03AC'), 'Greek_epsilonaccent': (0x07b2, u'\u03AD'), 'Greek_etaaccent': (0x07b3, u'\u03AE'), 'Greek_iotaaccent': (0x07b4, u'\u03AF'), 'Greek_iotadieresis': (0x07b5, u'\u03CA'), 'Greek_iotaaccentdieresis': (0x07b6, u'\u0390'), 'Greek_omicronaccent': (0x07b7, u'\u03CC'), 'Greek_upsilonaccent': (0x07b8, u'\u03CD'), 'Greek_upsilondieresis': (0x07b9, u'\u03CB'), 'Greek_upsilonaccentdieresis': (0x07ba, u'\u03B0'), 'Greek_omegaaccent': (0x07bb, u'\u03CE'), 'Greek_ALPHA': (0x07c1, u'\u0391'), 'Greek_BETA': (0x07c2, u'\u0392'), 'Greek_GAMMA': (0x07c3, u'\u0393'), 'Greek_DELTA': (0x07c4, u'\u0394'), 'Greek_EPSILON': (0x07c5, u'\u0395'), 'Greek_ZETA': (0x07c6, u'\u0396'), 'Greek_ETA': (0x07c7, u'\u0397'), 'Greek_THETA': (0x07c8, u'\u0398'), 'Greek_IOTA': (0x07c9, u'\u0399'), 'Greek_KAPPA': (0x07ca, u'\u039A'), 'Greek_LAMDA': (0x07cb, u'\u039B'), 'Greek_LAMBDA': (0x07cb, u'\u039B'), 'Greek_MU': (0x07cc, u'\u039C'), 'Greek_NU': (0x07cd, u'\u039D'), 'Greek_XI': (0x07ce, u'\u039E'), 'Greek_OMICRON': (0x07cf, u'\u039F'), 'Greek_PI': (0x07d0, u'\u03A0'), 'Greek_RHO': (0x07d1, u'\u03A1'), 'Greek_SIGMA': (0x07d2, u'\u03A3'), 'Greek_TAU': (0x07d4, u'\u03A4'), 'Greek_UPSILON': (0x07d5, u'\u03A5'), 'Greek_PHI': (0x07d6, u'\u03A6'), 'Greek_CHI': (0x07d7, u'\u03A7'), 'Greek_PSI': (0x07d8, u'\u03A8'), 'Greek_OMEGA': (0x07d9, u'\u03A9'), 'Greek_alpha': (0x07e1, u'\u03B1'), 'Greek_beta': (0x07e2, u'\u03B2'), 'Greek_gamma': (0x07e3, u'\u03B3'), 'Greek_delta': (0x07e4, u'\u03B4'), 'Greek_epsilon': (0x07e5, u'\u03B5'), 'Greek_zeta': (0x07e6, u'\u03B6'), 'Greek_eta': (0x07e7, u'\u03B7'), 'Greek_theta': (0x07e8, u'\u03B8'), 'Greek_iota': (0x07e9, u'\u03B9'), 'Greek_kappa': (0x07ea, u'\u03BA'), 'Greek_lamda': (0x07eb, u'\u03BB'), 'Greek_lambda': (0x07eb, u'\u03BB'), 'Greek_mu': (0x07ec, u'\u03BC'), 'Greek_nu': (0x07ed, u'\u03BD'), 'Greek_xi': (0x07ee, u'\u03BE'), 'Greek_omicron': (0x07ef, u'\u03BF'), 'Greek_pi': (0x07f0, u'\u03C0'), 'Greek_rho': (0x07f1, u'\u03C1'), 'Greek_sigma': (0x07f2, u'\u03C3'), 'Greek_finalsmallsigma': (0x07f3, u'\u03C2'), 'Greek_tau': (0x07f4, u'\u03C4'), 'Greek_upsilon': (0x07f5, u'\u03C5'), 'Greek_phi': (0x07f6, u'\u03C6'), 'Greek_chi': (0x07f7, u'\u03C7'), 'Greek_psi': (0x07f8, u'\u03C8'), 'Greek_omega': (0x07f9, u'\u03C9'), 'leftradical': (0x08a1, u'\u23B7'), 'topintegral': (0x08a4, u'\u2320'), 'botintegral': (0x08a5, u'\u2321'), 'topleftsqbracket': (0x08a7, u'\u23A1'), 'botleftsqbracket': (0x08a8, u'\u23A3'), 'toprightsqbracket': (0x08a9, u'\u23A4'), 'botrightsqbracket': (0x08aa, u'\u23A6'), 'topleftparens': (0x08ab, u'\u239B'), 'botleftparens': (0x08ac, u'\u239D'), 'toprightparens': (0x08ad, u'\u239E'), 'botrightparens': (0x08ae, u'\u23A0'), 'leftmiddlecurlybrace': (0x08af, u'\u23A8'), 'rightmiddlecurlybrace': (0x08b0, u'\u23AC'), 'lessthanequal': (0x08bc, u'\u2264'), 'notequal': (0x08bd, u'\u2260'), 'greaterthanequal': (0x08be, u'\u2265'), 'integral': (0x08bf, u'\u222B'), 'therefore': (0x08c0, u'\u2234'), 'variation': (0x08c1, u'\u221D'), 'infinity': (0x08c2, u'\u221E'), 'nabla': (0x08c5, u'\u2207'), 'approximate': (0x08c8, u'\u223C'), 'similarequal': (0x08c9, u'\u2243'), 'ifonlyif': (0x08cd, u'\u21D4'), 'implies': (0x08ce, u'\u21D2'), 'identical': (0x08cf, u'\u2261'), 'radical': (0x08d6, u'\u221A'), 'includedin': (0x08da, u'\u2282'), 'includes': (0x08db, u'\u2283'), 'intersection': (0x08dc, u'\u2229'), 'union': (0x08dd, u'\u222A'), 'logicaland': (0x08de, u'\u2227'), 'logicalor': (0x08df, u'\u2228'), 'partialderivative': (0x08ef, u'\u2202'), 'function': (0x08f6, u'\u0192'), 'leftarrow': (0x08fb, u'\u2190'), 'uparrow': (0x08fc, u'\u2191'), 'rightarrow': (0x08fd, u'\u2192'), 'downarrow': (0x08fe, u'\u2193'), 'soliddiamond': (0x09e0, u'\u25C6'), 'checkerboard': (0x09e1, u'\u2592'), 'ht': (0x09e2, u'\u2409'), 'ff': (0x09e3, u'\u240C'), 'cr': (0x09e4, u'\u240D'), 'lf': (0x09e5, u'\u240A'), 'nl': (0x09e8, u'\u2424'), 'vt': (0x09e9, u'\u240B'), 'lowrightcorner': (0x09ea, u'\u2518'), 'uprightcorner': (0x09eb, u'\u2510'), 'upleftcorner': (0x09ec, u'\u250C'), 'lowleftcorner': (0x09ed, u'\u2514'), 'crossinglines': (0x09ee, u'\u253C'), 'horizlinescan1': (0x09ef, u'\u23BA'), 'horizlinescan3': (0x09f0, u'\u23BB'), 'horizlinescan5': (0x09f1, u'\u2500'), 'horizlinescan7': (0x09f2, u'\u23BC'), 'horizlinescan9': (0x09f3, u'\u23BD'), 'leftt': (0x09f4, u'\u251C'), 'rightt': (0x09f5, u'\u2524'), 'bott': (0x09f6, u'\u2534'), 'topt': (0x09f7, u'\u252C'), 'vertbar': (0x09f8, u'\u2502'), 'emspace': (0x0aa1, u'\u2003'), 'enspace': (0x0aa2, u'\u2002'), 'em3space': (0x0aa3, u'\u2004'), 'em4space': (0x0aa4, u'\u2005'), 'digitspace': (0x0aa5, u'\u2007'), 'punctspace': (0x0aa6, u'\u2008'), 'thinspace': (0x0aa7, u'\u2009'), 'hairspace': (0x0aa8, u'\u200A'), 'emdash': (0x0aa9, u'\u2014'), 'endash': (0x0aaa, u'\u2013'), 'ellipsis': (0x0aae, u'\u2026'), 'doubbaselinedot': (0x0aaf, u'\u2025'), 'onethird': (0x0ab0, u'\u2153'), 'twothirds': (0x0ab1, u'\u2154'), 'onefifth': (0x0ab2, u'\u2155'), 'twofifths': (0x0ab3, u'\u2156'), 'threefifths': (0x0ab4, u'\u2157'), 'fourfifths': (0x0ab5, u'\u2158'), 'onesixth': (0x0ab6, u'\u2159'), 'fivesixths': (0x0ab7, u'\u215A'), 'careof': (0x0ab8, u'\u2105'), 'figdash': (0x0abb, u'\u2012'), 'oneeighth': (0x0ac3, u'\u215B'), 'threeeighths': (0x0ac4, u'\u215C'), 'fiveeighths': (0x0ac5, u'\u215D'), 'seveneighths': (0x0ac6, u'\u215E'), 'trademark': (0x0ac9, u'\u2122'), 'leftsinglequotemark': (0x0ad0, u'\u2018'), 'rightsinglequotemark': (0x0ad1, u'\u2019'), 'leftdoublequotemark': (0x0ad2, u'\u201C'), 'rightdoublequotemark': (0x0ad3, u'\u201D'), 'prescription': (0x0ad4, u'\u211E'), 'permille': (0x0ad5, u'\u2030'), 'minutes': (0x0ad6, u'\u2032'), 'seconds': (0x0ad7, u'\u2033'), 'latincross': (0x0ad9, u'\u271D'), 'club': (0x0aec, u'\u2663'), 'diamond': (0x0aed, u'\u2666'), 'heart': (0x0aee, u'\u2665'), 'maltesecross': (0x0af0, u'\u2720'), 'dagger': (0x0af1, u'\u2020'), 'doubledagger': (0x0af2, u'\u2021'), 'checkmark': (0x0af3, u'\u2713'), 'ballotcross': (0x0af4, u'\u2717'), 'musicalsharp': (0x0af5, u'\u266F'), 'musicalflat': (0x0af6, u'\u266D'), 'malesymbol': (0x0af7, u'\u2642'), 'femalesymbol': (0x0af8, u'\u2640'), 'telephone': (0x0af9, u'\u260E'), 'telephonerecorder': (0x0afa, u'\u2315'), 'phonographcopyright': (0x0afb, u'\u2117'), 'caret': (0x0afc, u'\u2038'), 'singlelowquotemark': (0x0afd, u'\u201A'), 'doublelowquotemark': (0x0afe, u'\u201E'), 'downtack': (0x0bc2, u'\u22A4'), 'downstile': (0x0bc4, u'\u230A'), 'jot': (0x0bca, u'\u2218'), 'quad': (0x0bcc, u'\u2395'), 'uptack': (0x0bce, u'\u22A5'), 'circle': (0x0bcf, u'\u25CB'), 'upstile': (0x0bd3, u'\u2308'), 'lefttack': (0x0bdc, u'\u22A3'), 'righttack': (0x0bfc, u'\u22A2'), 'hebrew_doublelowline': (0x0cdf, u'\u2017'), 'hebrew_aleph': (0x0ce0, u'\u05D0'), 'hebrew_bet': (0x0ce1, u'\u05D1'), 'hebrew_gimel': (0x0ce2, u'\u05D2'), 'hebrew_dalet': (0x0ce3, u'\u05D3'), 'hebrew_he': (0x0ce4, u'\u05D4'), 'hebrew_waw': (0x0ce5, u'\u05D5'), 'hebrew_zain': (0x0ce6, u'\u05D6'), 'hebrew_chet': (0x0ce7, u'\u05D7'), 'hebrew_tet': (0x0ce8, u'\u05D8'), 'hebrew_yod': (0x0ce9, u'\u05D9'), 'hebrew_finalkaph': (0x0cea, u'\u05DA'), 'hebrew_kaph': (0x0ceb, u'\u05DB'), 'hebrew_lamed': (0x0cec, u'\u05DC'), 'hebrew_finalmem': (0x0ced, u'\u05DD'), 'hebrew_mem': (0x0cee, u'\u05DE'), 'hebrew_finalnun': (0x0cef, u'\u05DF'), 'hebrew_nun': (0x0cf0, u'\u05E0'), 'hebrew_samech': (0x0cf1, u'\u05E1'), 'hebrew_ayin': (0x0cf2, u'\u05E2'), 'hebrew_finalpe': (0x0cf3, u'\u05E3'), 'hebrew_pe': (0x0cf4, u'\u05E4'), 'hebrew_finalzade': (0x0cf5, u'\u05E5'), 'hebrew_zade': (0x0cf6, u'\u05E6'), 'hebrew_qoph': (0x0cf7, u'\u05E7'), 'hebrew_resh': (0x0cf8, u'\u05E8'), 'hebrew_shin': (0x0cf9, u'\u05E9'), 'hebrew_taw': (0x0cfa, u'\u05EA'), 'Thai_kokai': (0x0da1, u'\u0E01'), 'Thai_khokhai': (0x0da2, u'\u0E02'), 'Thai_khokhuat': (0x0da3, u'\u0E03'), 'Thai_khokhwai': (0x0da4, u'\u0E04'), 'Thai_khokhon': (0x0da5, u'\u0E05'), 'Thai_khorakhang': (0x0da6, u'\u0E06'), 'Thai_ngongu': (0x0da7, u'\u0E07'), 'Thai_chochan': (0x0da8, u'\u0E08'), 'Thai_choching': (0x0da9, u'\u0E09'), 'Thai_chochang': (0x0daa, u'\u0E0A'), 'Thai_soso': (0x0dab, u'\u0E0B'), 'Thai_chochoe': (0x0dac, u'\u0E0C'), 'Thai_yoying': (0x0dad, u'\u0E0D'), 'Thai_dochada': (0x0dae, u'\u0E0E'), 'Thai_topatak': (0x0daf, u'\u0E0F'), 'Thai_thothan': (0x0db0, u'\u0E10'), 'Thai_thonangmontho': (0x0db1, u'\u0E11'), 'Thai_thophuthao': (0x0db2, u'\u0E12'), 'Thai_nonen': (0x0db3, u'\u0E13'), 'Thai_dodek': (0x0db4, u'\u0E14'), 'Thai_totao': (0x0db5, u'\u0E15'), 'Thai_thothung': (0x0db6, u'\u0E16'), 'Thai_thothahan': (0x0db7, u'\u0E17'), 'Thai_thothong': (0x0db8, u'\u0E18'), 'Thai_nonu': (0x0db9, u'\u0E19'), 'Thai_bobaimai': (0x0dba, u'\u0E1A'), 'Thai_popla': (0x0dbb, u'\u0E1B'), 'Thai_phophung': (0x0dbc, u'\u0E1C'), 'Thai_fofa': (0x0dbd, u'\u0E1D'), 'Thai_phophan': (0x0dbe, u'\u0E1E'), 'Thai_fofan': (0x0dbf, u'\u0E1F'), 'Thai_phosamphao': (0x0dc0, u'\u0E20'), 'Thai_moma': (0x0dc1, u'\u0E21'), 'Thai_yoyak': (0x0dc2, u'\u0E22'), 'Thai_rorua': (0x0dc3, u'\u0E23'), 'Thai_ru': (0x0dc4, u'\u0E24'), 'Thai_loling': (0x0dc5, u'\u0E25'), 'Thai_lu': (0x0dc6, u'\u0E26'), 'Thai_wowaen': (0x0dc7, u'\u0E27'), 'Thai_sosala': (0x0dc8, u'\u0E28'), 'Thai_sorusi': (0x0dc9, u'\u0E29'), 'Thai_sosua': (0x0dca, u'\u0E2A'), 'Thai_hohip': (0x0dcb, u'\u0E2B'), 'Thai_lochula': (0x0dcc, u'\u0E2C'), 'Thai_oang': (0x0dcd, u'\u0E2D'), 'Thai_honokhuk': (0x0dce, u'\u0E2E'), 'Thai_paiyannoi': (0x0dcf, u'\u0E2F'), 'Thai_saraa': (0x0dd0, u'\u0E30'), 'Thai_maihanakat': (0x0dd1, u'\u0E31'), 'Thai_saraaa': (0x0dd2, u'\u0E32'), 'Thai_saraam': (0x0dd3, u'\u0E33'), 'Thai_sarai': (0x0dd4, u'\u0E34'), 'Thai_saraii': (0x0dd5, u'\u0E35'), 'Thai_saraue': (0x0dd6, u'\u0E36'), 'Thai_sarauee': (0x0dd7, u'\u0E37'), 'Thai_sarau': (0x0dd8, u'\u0E38'), 'Thai_sarauu': (0x0dd9, u'\u0E39'), 'Thai_phinthu': (0x0dda, u'\u0E3A'), 'Thai_baht': (0x0ddf, u'\u0E3F'), 'Thai_sarae': (0x0de0, u'\u0E40'), 'Thai_saraae': (0x0de1, u'\u0E41'), 'Thai_sarao': (0x0de2, u'\u0E42'), 'Thai_saraaimaimuan': (0x0de3, u'\u0E43'), 'Thai_saraaimaimalai': (0x0de4, u'\u0E44'), 'Thai_lakkhangyao': (0x0de5, u'\u0E45'), 'Thai_maiyamok': (0x0de6, u'\u0E46'), 'Thai_maitaikhu': (0x0de7, u'\u0E47'), 'Thai_maiek': (0x0de8, u'\u0E48'), 'Thai_maitho': (0x0de9, u'\u0E49'), 'Thai_maitri': (0x0dea, u'\u0E4A'), 'Thai_maichattawa': (0x0deb, u'\u0E4B'), 'Thai_thanthakhat': (0x0dec, u'\u0E4C'), 'Thai_nikhahit': (0x0ded, u'\u0E4D'), 'Thai_leksun': (0x0df0, u'\u0E50'), 'Thai_leknung': (0x0df1, u'\u0E51'), 'Thai_leksong': (0x0df2, u'\u0E52'), 'Thai_leksam': (0x0df3, u'\u0E53'), 'Thai_leksi': (0x0df4, u'\u0E54'), 'Thai_lekha': (0x0df5, u'\u0E55'), 'Thai_lekhok': (0x0df6, u'\u0E56'), 'Thai_lekchet': (0x0df7, u'\u0E57'), 'Thai_lekpaet': (0x0df8, u'\u0E58'), 'Thai_lekkao': (0x0df9, u'\u0E59'), 'Armenian_ligature_ew': (0x1000587, u'\u0587'), 'Armenian_full_stop': (0x1000589, u'\u0589'), 'Armenian_verjaket': (0x1000589, u'\u0589'), 'Armenian_separation_mark': (0x100055d, u'\u055D'), 'Armenian_but': (0x100055d, u'\u055D'), 'Armenian_hyphen': (0x100058a, u'\u058A'), 'Armenian_yentamna': (0x100058a, u'\u058A'), 'Armenian_exclam': (0x100055c, u'\u055C'), 'Armenian_amanak': (0x100055c, u'\u055C'), 'Armenian_accent': (0x100055b, u'\u055B'), 'Armenian_shesht': (0x100055b, u'\u055B'), 'Armenian_question': (0x100055e, u'\u055E'), 'Armenian_paruyk': (0x100055e, u'\u055E'), 'Armenian_AYB': (0x1000531, u'\u0531'), 'Armenian_ayb': (0x1000561, u'\u0561'), 'Armenian_BEN': (0x1000532, u'\u0532'), 'Armenian_ben': (0x1000562, u'\u0562'), 'Armenian_GIM': (0x1000533, u'\u0533'), 'Armenian_gim': (0x1000563, u'\u0563'), 'Armenian_DA': (0x1000534, u'\u0534'), 'Armenian_da': (0x1000564, u'\u0564'), 'Armenian_YECH': (0x1000535, u'\u0535'), 'Armenian_yech': (0x1000565, u'\u0565'), 'Armenian_ZA': (0x1000536, u'\u0536'), 'Armenian_za': (0x1000566, u'\u0566'), 'Armenian_E': (0x1000537, u'\u0537'), 'Armenian_e': (0x1000567, u'\u0567'), 'Armenian_AT': (0x1000538, u'\u0538'), 'Armenian_at': (0x1000568, u'\u0568'), 'Armenian_TO': (0x1000539, u'\u0539'), 'Armenian_to': (0x1000569, u'\u0569'), 'Armenian_ZHE': (0x100053a, u'\u053A'), 'Armenian_zhe': (0x100056a, u'\u056A'), 'Armenian_INI': (0x100053b, u'\u053B'), 'Armenian_ini': (0x100056b, u'\u056B'), 'Armenian_LYUN': (0x100053c, u'\u053C'), 'Armenian_lyun': (0x100056c, u'\u056C'), 'Armenian_KHE': (0x100053d, u'\u053D'), 'Armenian_khe': (0x100056d, u'\u056D'), 'Armenian_TSA': (0x100053e, u'\u053E'), 'Armenian_tsa': (0x100056e, u'\u056E'), 'Armenian_KEN': (0x100053f, u'\u053F'), 'Armenian_ken': (0x100056f, u'\u056F'), 'Armenian_HO': (0x1000540, u'\u0540'), 'Armenian_ho': (0x1000570, u'\u0570'), 'Armenian_DZA': (0x1000541, u'\u0541'), 'Armenian_dza': (0x1000571, u'\u0571'), 'Armenian_GHAT': (0x1000542, u'\u0542'), 'Armenian_ghat': (0x1000572, u'\u0572'), 'Armenian_TCHE': (0x1000543, u'\u0543'), 'Armenian_tche': (0x1000573, u'\u0573'), 'Armenian_MEN': (0x1000544, u'\u0544'), 'Armenian_men': (0x1000574, u'\u0574'), 'Armenian_HI': (0x1000545, u'\u0545'), 'Armenian_hi': (0x1000575, u'\u0575'), 'Armenian_NU': (0x1000546, u'\u0546'), 'Armenian_nu': (0x1000576, u'\u0576'), 'Armenian_SHA': (0x1000547, u'\u0547'), 'Armenian_sha': (0x1000577, u'\u0577'), 'Armenian_VO': (0x1000548, u'\u0548'), 'Armenian_vo': (0x1000578, u'\u0578'), 'Armenian_CHA': (0x1000549, u'\u0549'), 'Armenian_cha': (0x1000579, u'\u0579'), 'Armenian_PE': (0x100054a, u'\u054A'), 'Armenian_pe': (0x100057a, u'\u057A'), 'Armenian_JE': (0x100054b, u'\u054B'), 'Armenian_je': (0x100057b, u'\u057B'), 'Armenian_RA': (0x100054c, u'\u054C'), 'Armenian_ra': (0x100057c, u'\u057C'), 'Armenian_SE': (0x100054d, u'\u054D'), 'Armenian_se': (0x100057d, u'\u057D'), 'Armenian_VEV': (0x100054e, u'\u054E'), 'Armenian_vev': (0x100057e, u'\u057E'), 'Armenian_TYUN': (0x100054f, u'\u054F'), 'Armenian_tyun': (0x100057f, u'\u057F'), 'Armenian_RE': (0x1000550, u'\u0550'), 'Armenian_re': (0x1000580, u'\u0580'), 'Armenian_TSO': (0x1000551, u'\u0551'), 'Armenian_tso': (0x1000581, u'\u0581'), 'Armenian_VYUN': (0x1000552, u'\u0552'), 'Armenian_vyun': (0x1000582, u'\u0582'), 'Armenian_PYUR': (0x1000553, u'\u0553'), 'Armenian_pyur': (0x1000583, u'\u0583'), 'Armenian_KE': (0x1000554, u'\u0554'), 'Armenian_ke': (0x1000584, u'\u0584'), 'Armenian_O': (0x1000555, u'\u0555'), 'Armenian_o': (0x1000585, u'\u0585'), 'Armenian_FE': (0x1000556, u'\u0556'), 'Armenian_fe': (0x1000586, u'\u0586'), 'Armenian_apostrophe': (0x100055a, u'\u055A'), 'Georgian_an': (0x10010d0, u'\u10D0'), 'Georgian_ban': (0x10010d1, u'\u10D1'), 'Georgian_gan': (0x10010d2, u'\u10D2'), 'Georgian_don': (0x10010d3, u'\u10D3'), 'Georgian_en': (0x10010d4, u'\u10D4'), 'Georgian_vin': (0x10010d5, u'\u10D5'), 'Georgian_zen': (0x10010d6, u'\u10D6'), 'Georgian_tan': (0x10010d7, u'\u10D7'), 'Georgian_in': (0x10010d8, u'\u10D8'), 'Georgian_kan': (0x10010d9, u'\u10D9'), 'Georgian_las': (0x10010da, u'\u10DA'), 'Georgian_man': (0x10010db, u'\u10DB'), 'Georgian_nar': (0x10010dc, u'\u10DC'), 'Georgian_on': (0x10010dd, u'\u10DD'), 'Georgian_par': (0x10010de, u'\u10DE'), 'Georgian_zhar': (0x10010df, u'\u10DF'), 'Georgian_rae': (0x10010e0, u'\u10E0'), 'Georgian_san': (0x10010e1, u'\u10E1'), 'Georgian_tar': (0x10010e2, u'\u10E2'), 'Georgian_un': (0x10010e3, u'\u10E3'), 'Georgian_phar': (0x10010e4, u'\u10E4'), 'Georgian_khar': (0x10010e5, u'\u10E5'), 'Georgian_ghan': (0x10010e6, u'\u10E6'), 'Georgian_qar': (0x10010e7, u'\u10E7'), 'Georgian_shin': (0x10010e8, u'\u10E8'), 'Georgian_chin': (0x10010e9, u'\u10E9'), 'Georgian_can': (0x10010ea, u'\u10EA'), 'Georgian_jil': (0x10010eb, u'\u10EB'), 'Georgian_cil': (0x10010ec, u'\u10EC'), 'Georgian_char': (0x10010ed, u'\u10ED'), 'Georgian_xan': (0x10010ee, u'\u10EE'), 'Georgian_jhan': (0x10010ef, u'\u10EF'), 'Georgian_hae': (0x10010f0, u'\u10F0'), 'Georgian_he': (0x10010f1, u'\u10F1'), 'Georgian_hie': (0x10010f2, u'\u10F2'), 'Georgian_we': (0x10010f3, u'\u10F3'), 'Georgian_har': (0x10010f4, u'\u10F4'), 'Georgian_hoe': (0x10010f5, u'\u10F5'), 'Georgian_fi': (0x10010f6, u'\u10F6'), 'Xabovedot': (0x1001e8a, u'\u1E8A'), 'Ibreve': (0x100012c, u'\u012C'), 'Zstroke': (0x10001b5, u'\u01B5'), 'Gcaron': (0x10001e6, u'\u01E6'), 'Ocaron': (0x10001d1, u'\u01D2'), 'Obarred': (0x100019f, u'\u019F'), 'xabovedot': (0x1001e8b, u'\u1E8B'), 'ibreve': (0x100012d, u'\u012D'), 'zstroke': (0x10001b6, u'\u01B6'), 'gcaron': (0x10001e7, u'\u01E7'), 'ocaron': (0x10001d2, u'\u01D2'), 'obarred': (0x1000275, u'\u0275'), 'SCHWA': (0x100018f, u'\u018F'), 'schwa': (0x1000259, u'\u0259'), 'EZH': (0x10001b7, u'\u01B7'), 'ezh': (0x1000292, u'\u0292'), 'Lbelowdot': (0x1001e36, u'\u1E36'), 'lbelowdot': (0x1001e37, u'\u1E37'), 'Abelowdot': (0x1001ea0, u'\u1EA0'), 'abelowdot': (0x1001ea1, u'\u1EA1'), 'Ahook': (0x1001ea2, u'\u1EA2'), 'ahook': (0x1001ea3, u'\u1EA3'), 'Acircumflexacute': (0x1001ea4, u'\u1EA4'), 'acircumflexacute': (0x1001ea5, u'\u1EA5'), 'Acircumflexgrave': (0x1001ea6, u'\u1EA6'), 'acircumflexgrave': (0x1001ea7, u'\u1EA7'), 'Acircumflexhook': (0x1001ea8, u'\u1EA8'), 'acircumflexhook': (0x1001ea9, u'\u1EA9'), 'Acircumflextilde': (0x1001eaa, u'\u1EAA'), 'acircumflextilde': (0x1001eab, u'\u1EAB'), 'Acircumflexbelowdot': (0x1001eac, u'\u1EAC'), 'acircumflexbelowdot': (0x1001ead, u'\u1EAD'), 'Abreveacute': (0x1001eae, u'\u1EAE'), 'abreveacute': (0x1001eaf, u'\u1EAF'), 'Abrevegrave': (0x1001eb0, u'\u1EB0'), 'abrevegrave': (0x1001eb1, u'\u1EB1'), 'Abrevehook': (0x1001eb2, u'\u1EB2'), 'abrevehook': (0x1001eb3, u'\u1EB3'), 'Abrevetilde': (0x1001eb4, u'\u1EB4'), 'abrevetilde': (0x1001eb5, u'\u1EB5'), 'Abrevebelowdot': (0x1001eb6, u'\u1EB6'), 'abrevebelowdot': (0x1001eb7, u'\u1EB7'), 'Ebelowdot': (0x1001eb8, u'\u1EB8'), 'ebelowdot': (0x1001eb9, u'\u1EB9'), 'Ehook': (0x1001eba, u'\u1EBA'), 'ehook': (0x1001ebb, u'\u1EBB'), 'Etilde': (0x1001ebc, u'\u1EBC'), 'etilde': (0x1001ebd, u'\u1EBD'), 'Ecircumflexacute': (0x1001ebe, u'\u1EBE'), 'ecircumflexacute': (0x1001ebf, u'\u1EBF'), 'Ecircumflexgrave': (0x1001ec0, u'\u1EC0'), 'ecircumflexgrave': (0x1001ec1, u'\u1EC1'), 'Ecircumflexhook': (0x1001ec2, u'\u1EC2'), 'ecircumflexhook': (0x1001ec3, u'\u1EC3'), 'Ecircumflextilde': (0x1001ec4, u'\u1EC4'), 'ecircumflextilde': (0x1001ec5, u'\u1EC5'), 'Ecircumflexbelowdot': (0x1001ec6, u'\u1EC6'), 'ecircumflexbelowdot': (0x1001ec7, u'\u1EC7'), 'Ihook': (0x1001ec8, u'\u1EC8'), 'ihook': (0x1001ec9, u'\u1EC9'), 'Ibelowdot': (0x1001eca, u'\u1ECA'), 'ibelowdot': (0x1001ecb, u'\u1ECB'), 'Obelowdot': (0x1001ecc, u'\u1ECC'), 'obelowdot': (0x1001ecd, u'\u1ECD'), 'Ohook': (0x1001ece, u'\u1ECE'), 'ohook': (0x1001ecf, u'\u1ECF'), 'Ocircumflexacute': (0x1001ed0, u'\u1ED0'), 'ocircumflexacute': (0x1001ed1, u'\u1ED1'), 'Ocircumflexgrave': (0x1001ed2, u'\u1ED2'), 'ocircumflexgrave': (0x1001ed3, u'\u1ED3'), 'Ocircumflexhook': (0x1001ed4, u'\u1ED4'), 'ocircumflexhook': (0x1001ed5, u'\u1ED5'), 'Ocircumflextilde': (0x1001ed6, u'\u1ED6'), 'ocircumflextilde': (0x1001ed7, u'\u1ED7'), 'Ocircumflexbelowdot': (0x1001ed8, u'\u1ED8'), 'ocircumflexbelowdot': (0x1001ed9, u'\u1ED9'), 'Ohornacute': (0x1001eda, u'\u1EDA'), 'ohornacute': (0x1001edb, u'\u1EDB'), 'Ohorngrave': (0x1001edc, u'\u1EDC'), 'ohorngrave': (0x1001edd, u'\u1EDD'), 'Ohornhook': (0x1001ede, u'\u1EDE'), 'ohornhook': (0x1001edf, u'\u1EDF'), 'Ohorntilde': (0x1001ee0, u'\u1EE0'), 'ohorntilde': (0x1001ee1, u'\u1EE1'), 'Ohornbelowdot': (0x1001ee2, u'\u1EE2'), 'ohornbelowdot': (0x1001ee3, u'\u1EE3'), 'Ubelowdot': (0x1001ee4, u'\u1EE4'), 'ubelowdot': (0x1001ee5, u'\u1EE5'), 'Uhook': (0x1001ee6, u'\u1EE6'), 'uhook': (0x1001ee7, u'\u1EE7'), 'Uhornacute': (0x1001ee8, u'\u1EE8'), 'uhornacute': (0x1001ee9, u'\u1EE9'), 'Uhorngrave': (0x1001eea, u'\u1EEA'), 'uhorngrave': (0x1001eeb, u'\u1EEB'), 'Uhornhook': (0x1001eec, u'\u1EEC'), 'uhornhook': (0x1001eed, u'\u1EED'), 'Uhorntilde': (0x1001eee, u'\u1EEE'), 'uhorntilde': (0x1001eef, u'\u1EEF'), 'Uhornbelowdot': (0x1001ef0, u'\u1EF0'), 'uhornbelowdot': (0x1001ef1, u'\u1EF1'), 'Ybelowdot': (0x1001ef4, u'\u1EF4'), 'ybelowdot': (0x1001ef5, u'\u1EF5'), 'Yhook': (0x1001ef6, u'\u1EF6'), 'yhook': (0x1001ef7, u'\u1EF7'), 'Ytilde': (0x1001ef8, u'\u1EF8'), 'ytilde': (0x1001ef9, u'\u1EF9'), 'Ohorn': (0x10001a0, u'\u01A0'), 'ohorn': (0x10001a1, u'\u01A1'), 'Uhorn': (0x10001af, u'\u01AF'), 'uhorn': (0x10001b0, u'\u01B0'), 'EcuSign': (0x10020a0, u'\u20A0'), 'ColonSign': (0x10020a1, u'\u20A1'), 'CruzeiroSign': (0x10020a2, u'\u20A2'), 'FFrancSign': (0x10020a3, u'\u20A3'), 'LiraSign': (0x10020a4, u'\u20A4'), 'MillSign': (0x10020a5, u'\u20A5'), 'NairaSign': (0x10020a6, u'\u20A6'), 'PesetaSign': (0x10020a7, u'\u20A7'), 'RupeeSign': (0x10020a8, u'\u20A8'), 'WonSign': (0x10020a9, u'\u20A9'), 'NewSheqelSign': (0x10020aa, u'\u20AA'), 'DongSign': (0x10020ab, u'\u20AB'), 'EuroSign': (0x20ac, u'\u20AC'), 'zerosuperior': (0x1002070, u'\u2070'), 'foursuperior': (0x1002074, u'\u2074'), 'fivesuperior': (0x1002075, u'\u2075'), 'sixsuperior': (0x1002076, u'\u2076'), 'sevensuperior': (0x1002077, u'\u2077'), 'eightsuperior': (0x1002078, u'\u2078'), 'ninesuperior': (0x1002079, u'\u2079'), 'zerosubscript': (0x1002080, u'\u2080'), 'onesubscript': (0x1002081, u'\u2081'), 'twosubscript': (0x1002082, u'\u2082'), 'threesubscript': (0x1002083, u'\u2083'), 'foursubscript': (0x1002084, u'\u2084'), 'fivesubscript': (0x1002085, u'\u2085'), 'sixsubscript': (0x1002086, u'\u2086'), 'sevensubscript': (0x1002087, u'\u2087'), 'eightsubscript': (0x1002088, u'\u2088'), 'ninesubscript': (0x1002089, u'\u2089'), 'partdifferential': (0x1002202, u'\u2202'), 'emptyset': (0x1002205, u'\u2205'), 'elementof': (0x1002208, u'\u2208'), 'notelementof': (0x1002209, u'\u2209'), 'containsas': (0x100220B, u'\u220B'), 'squareroot': (0x100221A, u'\u221A'), 'cuberoot': (0x100221B, u'\u221B'), 'fourthroot': (0x100221C, u'\u221C'), 'dintegral': (0x100222C, u'\u222C'), 'tintegral': (0x100222D, u'\u222D'), 'because': (0x1002235, u'\u2235'), 'approxeq': (0x1002248, u'\u2245'), 'notapproxeq': (0x1002247, u'\u2247'), 'notidentical': (0x1002262, u'\u2262'), 'stricteq': (0x1002263, u'\u2263'), 'braille_blank': (0x1002800, u'\u2800'), 'braille_dots_1': (0x1002801, u'\u2801'), 'braille_dots_2': (0x1002802, u'\u2802'), 'braille_dots_12': (0x1002803, u'\u2803'), 'braille_dots_3': (0x1002804, u'\u2804'), 'braille_dots_13': (0x1002805, u'\u2805'), 'braille_dots_23': (0x1002806, u'\u2806'), 'braille_dots_123': (0x1002807, u'\u2807'), 'braille_dots_4': (0x1002808, u'\u2808'), 'braille_dots_14': (0x1002809, u'\u2809'), 'braille_dots_24': (0x100280a, u'\u280a'), 'braille_dots_124': (0x100280b, u'\u280b'), 'braille_dots_34': (0x100280c, u'\u280c'), 'braille_dots_134': (0x100280d, u'\u280d'), 'braille_dots_234': (0x100280e, u'\u280e'), 'braille_dots_1234': (0x100280f, u'\u280f'), 'braille_dots_5': (0x1002810, u'\u2810'), 'braille_dots_15': (0x1002811, u'\u2811'), 'braille_dots_25': (0x1002812, u'\u2812'), 'braille_dots_125': (0x1002813, u'\u2813'), 'braille_dots_35': (0x1002814, u'\u2814'), 'braille_dots_135': (0x1002815, u'\u2815'), 'braille_dots_235': (0x1002816, u'\u2816'), 'braille_dots_1235': (0x1002817, u'\u2817'), 'braille_dots_45': (0x1002818, u'\u2818'), 'braille_dots_145': (0x1002819, u'\u2819'), 'braille_dots_245': (0x100281a, u'\u281a'), 'braille_dots_1245': (0x100281b, u'\u281b'), 'braille_dots_345': (0x100281c, u'\u281c'), 'braille_dots_1345': (0x100281d, u'\u281d'), 'braille_dots_2345': (0x100281e, u'\u281e'), 'braille_dots_12345': (0x100281f, u'\u281f'), 'braille_dots_6': (0x1002820, u'\u2820'), 'braille_dots_16': (0x1002821, u'\u2821'), 'braille_dots_26': (0x1002822, u'\u2822'), 'braille_dots_126': (0x1002823, u'\u2823'), 'braille_dots_36': (0x1002824, u'\u2824'), 'braille_dots_136': (0x1002825, u'\u2825'), 'braille_dots_236': (0x1002826, u'\u2826'), 'braille_dots_1236': (0x1002827, u'\u2827'), 'braille_dots_46': (0x1002828, u'\u2828'), 'braille_dots_146': (0x1002829, u'\u2829'), 'braille_dots_246': (0x100282a, u'\u282a'), 'braille_dots_1246': (0x100282b, u'\u282b'), 'braille_dots_346': (0x100282c, u'\u282c'), 'braille_dots_1346': (0x100282d, u'\u282d'), 'braille_dots_2346': (0x100282e, u'\u282e'), 'braille_dots_12346': (0x100282f, u'\u282f'), 'braille_dots_56': (0x1002830, u'\u2830'), 'braille_dots_156': (0x1002831, u'\u2831'), 'braille_dots_256': (0x1002832, u'\u2832'), 'braille_dots_1256': (0x1002833, u'\u2833'), 'braille_dots_356': (0x1002834, u'\u2834'), 'braille_dots_1356': (0x1002835, u'\u2835'), 'braille_dots_2356': (0x1002836, u'\u2836'), 'braille_dots_12356': (0x1002837, u'\u2837'), 'braille_dots_456': (0x1002838, u'\u2838'), 'braille_dots_1456': (0x1002839, u'\u2839'), 'braille_dots_2456': (0x100283a, u'\u283a'), 'braille_dots_12456': (0x100283b, u'\u283b'), 'braille_dots_3456': (0x100283c, u'\u283c'), 'braille_dots_13456': (0x100283d, u'\u283d'), 'braille_dots_23456': (0x100283e, u'\u283e'), 'braille_dots_123456': (0x100283f, u'\u283f'), 'braille_dots_7': (0x1002840, u'\u2840'), 'braille_dots_17': (0x1002841, u'\u2841'), 'braille_dots_27': (0x1002842, u'\u2842'), 'braille_dots_127': (0x1002843, u'\u2843'), 'braille_dots_37': (0x1002844, u'\u2844'), 'braille_dots_137': (0x1002845, u'\u2845'), 'braille_dots_237': (0x1002846, u'\u2846'), 'braille_dots_1237': (0x1002847, u'\u2847'), 'braille_dots_47': (0x1002848, u'\u2848'), 'braille_dots_147': (0x1002849, u'\u2849'), 'braille_dots_247': (0x100284a, u'\u284a'), 'braille_dots_1247': (0x100284b, u'\u284b'), 'braille_dots_347': (0x100284c, u'\u284c'), 'braille_dots_1347': (0x100284d, u'\u284d'), 'braille_dots_2347': (0x100284e, u'\u284e'), 'braille_dots_12347': (0x100284f, u'\u284f'), 'braille_dots_57': (0x1002850, u'\u2850'), 'braille_dots_157': (0x1002851, u'\u2851'), 'braille_dots_257': (0x1002852, u'\u2852'), 'braille_dots_1257': (0x1002853, u'\u2853'), 'braille_dots_357': (0x1002854, u'\u2854'), 'braille_dots_1357': (0x1002855, u'\u2855'), 'braille_dots_2357': (0x1002856, u'\u2856'), 'braille_dots_12357': (0x1002857, u'\u2857'), 'braille_dots_457': (0x1002858, u'\u2858'), 'braille_dots_1457': (0x1002859, u'\u2859'), 'braille_dots_2457': (0x100285a, u'\u285a'), 'braille_dots_12457': (0x100285b, u'\u285b'), 'braille_dots_3457': (0x100285c, u'\u285c'), 'braille_dots_13457': (0x100285d, u'\u285d'), 'braille_dots_23457': (0x100285e, u'\u285e'), 'braille_dots_123457': (0x100285f, u'\u285f'), 'braille_dots_67': (0x1002860, u'\u2860'), 'braille_dots_167': (0x1002861, u'\u2861'), 'braille_dots_267': (0x1002862, u'\u2862'), 'braille_dots_1267': (0x1002863, u'\u2863'), 'braille_dots_367': (0x1002864, u'\u2864'), 'braille_dots_1367': (0x1002865, u'\u2865'), 'braille_dots_2367': (0x1002866, u'\u2866'), 'braille_dots_12367': (0x1002867, u'\u2867'), 'braille_dots_467': (0x1002868, u'\u2868'), 'braille_dots_1467': (0x1002869, u'\u2869'), 'braille_dots_2467': (0x100286a, u'\u286a'), 'braille_dots_12467': (0x100286b, u'\u286b'), 'braille_dots_3467': (0x100286c, u'\u286c'), 'braille_dots_13467': (0x100286d, u'\u286d'), 'braille_dots_23467': (0x100286e, u'\u286e'), 'braille_dots_123467': (0x100286f, u'\u286f'), 'braille_dots_567': (0x1002870, u'\u2870'), 'braille_dots_1567': (0x1002871, u'\u2871'), 'braille_dots_2567': (0x1002872, u'\u2872'), 'braille_dots_12567': (0x1002873, u'\u2873'), 'braille_dots_3567': (0x1002874, u'\u2874'), 'braille_dots_13567': (0x1002875, u'\u2875'), 'braille_dots_23567': (0x1002876, u'\u2876'), 'braille_dots_123567': (0x1002877, u'\u2877'), 'braille_dots_4567': (0x1002878, u'\u2878'), 'braille_dots_14567': (0x1002879, u'\u2879'), 'braille_dots_24567': (0x100287a, u'\u287a'), 'braille_dots_124567': (0x100287b, u'\u287b'), 'braille_dots_34567': (0x100287c, u'\u287c'), 'braille_dots_134567': (0x100287d, u'\u287d'), 'braille_dots_234567': (0x100287e, u'\u287e'), 'braille_dots_1234567': (0x100287f, u'\u287f'), 'braille_dots_8': (0x1002880, u'\u2880'), 'braille_dots_18': (0x1002881, u'\u2881'), 'braille_dots_28': (0x1002882, u'\u2882'), 'braille_dots_128': (0x1002883, u'\u2883'), 'braille_dots_38': (0x1002884, u'\u2884'), 'braille_dots_138': (0x1002885, u'\u2885'), 'braille_dots_238': (0x1002886, u'\u2886'), 'braille_dots_1238': (0x1002887, u'\u2887'), 'braille_dots_48': (0x1002888, u'\u2888'), 'braille_dots_148': (0x1002889, u'\u2889'), 'braille_dots_248': (0x100288a, u'\u288a'), 'braille_dots_1248': (0x100288b, u'\u288b'), 'braille_dots_348': (0x100288c, u'\u288c'), 'braille_dots_1348': (0x100288d, u'\u288d'), 'braille_dots_2348': (0x100288e, u'\u288e'), 'braille_dots_12348': (0x100288f, u'\u288f'), 'braille_dots_58': (0x1002890, u'\u2890'), 'braille_dots_158': (0x1002891, u'\u2891'), 'braille_dots_258': (0x1002892, u'\u2892'), 'braille_dots_1258': (0x1002893, u'\u2893'), 'braille_dots_358': (0x1002894, u'\u2894'), 'braille_dots_1358': (0x1002895, u'\u2895'), 'braille_dots_2358': (0x1002896, u'\u2896'), 'braille_dots_12358': (0x1002897, u'\u2897'), 'braille_dots_458': (0x1002898, u'\u2898'), 'braille_dots_1458': (0x1002899, u'\u2899'), 'braille_dots_2458': (0x100289a, u'\u289a'), 'braille_dots_12458': (0x100289b, u'\u289b'), 'braille_dots_3458': (0x100289c, u'\u289c'), 'braille_dots_13458': (0x100289d, u'\u289d'), 'braille_dots_23458': (0x100289e, u'\u289e'), 'braille_dots_123458': (0x100289f, u'\u289f'), 'braille_dots_68': (0x10028a0, u'\u28a0'), 'braille_dots_168': (0x10028a1, u'\u28a1'), 'braille_dots_268': (0x10028a2, u'\u28a2'), 'braille_dots_1268': (0x10028a3, u'\u28a3'), 'braille_dots_368': (0x10028a4, u'\u28a4'), 'braille_dots_1368': (0x10028a5, u'\u28a5'), 'braille_dots_2368': (0x10028a6, u'\u28a6'), 'braille_dots_12368': (0x10028a7, u'\u28a7'), 'braille_dots_468': (0x10028a8, u'\u28a8'), 'braille_dots_1468': (0x10028a9, u'\u28a9'), 'braille_dots_2468': (0x10028aa, u'\u28aa'), 'braille_dots_12468': (0x10028ab, u'\u28ab'), 'braille_dots_3468': (0x10028ac, u'\u28ac'), 'braille_dots_13468': (0x10028ad, u'\u28ad'), 'braille_dots_23468': (0x10028ae, u'\u28ae'), 'braille_dots_123468': (0x10028af, u'\u28af'), 'braille_dots_568': (0x10028b0, u'\u28b0'), 'braille_dots_1568': (0x10028b1, u'\u28b1'), 'braille_dots_2568': (0x10028b2, u'\u28b2'), 'braille_dots_12568': (0x10028b3, u'\u28b3'), 'braille_dots_3568': (0x10028b4, u'\u28b4'), 'braille_dots_13568': (0x10028b5, u'\u28b5'), 'braille_dots_23568': (0x10028b6, u'\u28b6'), 'braille_dots_123568': (0x10028b7, u'\u28b7'), 'braille_dots_4568': (0x10028b8, u'\u28b8'), 'braille_dots_14568': (0x10028b9, u'\u28b9'), 'braille_dots_24568': (0x10028ba, u'\u28ba'), 'braille_dots_124568': (0x10028bb, u'\u28bb'), 'braille_dots_34568': (0x10028bc, u'\u28bc'), 'braille_dots_134568': (0x10028bd, u'\u28bd'), 'braille_dots_234568': (0x10028be, u'\u28be'), 'braille_dots_1234568': (0x10028bf, u'\u28bf'), 'braille_dots_78': (0x10028c0, u'\u28c0'), 'braille_dots_178': (0x10028c1, u'\u28c1'), 'braille_dots_278': (0x10028c2, u'\u28c2'), 'braille_dots_1278': (0x10028c3, u'\u28c3'), 'braille_dots_378': (0x10028c4, u'\u28c4'), 'braille_dots_1378': (0x10028c5, u'\u28c5'), 'braille_dots_2378': (0x10028c6, u'\u28c6'), 'braille_dots_12378': (0x10028c7, u'\u28c7'), 'braille_dots_478': (0x10028c8, u'\u28c8'), 'braille_dots_1478': (0x10028c9, u'\u28c9'), 'braille_dots_2478': (0x10028ca, u'\u28ca'), 'braille_dots_12478': (0x10028cb, u'\u28cb'), 'braille_dots_3478': (0x10028cc, u'\u28cc'), 'braille_dots_13478': (0x10028cd, u'\u28cd'), 'braille_dots_23478': (0x10028ce, u'\u28ce'), 'braille_dots_123478': (0x10028cf, u'\u28cf'), 'braille_dots_578': (0x10028d0, u'\u28d0'), 'braille_dots_1578': (0x10028d1, u'\u28d1'), 'braille_dots_2578': (0x10028d2, u'\u28d2'), 'braille_dots_12578': (0x10028d3, u'\u28d3'), 'braille_dots_3578': (0x10028d4, u'\u28d4'), 'braille_dots_13578': (0x10028d5, u'\u28d5'), 'braille_dots_23578': (0x10028d6, u'\u28d6'), 'braille_dots_123578': (0x10028d7, u'\u28d7'), 'braille_dots_4578': (0x10028d8, u'\u28d8'), 'braille_dots_14578': (0x10028d9, u'\u28d9'), 'braille_dots_24578': (0x10028da, u'\u28da'), 'braille_dots_124578': (0x10028db, u'\u28db'), 'braille_dots_34578': (0x10028dc, u'\u28dc'), 'braille_dots_134578': (0x10028dd, u'\u28dd'), 'braille_dots_234578': (0x10028de, u'\u28de'), 'braille_dots_1234578': (0x10028df, u'\u28df'), 'braille_dots_678': (0x10028e0, u'\u28e0'), 'braille_dots_1678': (0x10028e1, u'\u28e1'), 'braille_dots_2678': (0x10028e2, u'\u28e2'), 'braille_dots_12678': (0x10028e3, u'\u28e3'), 'braille_dots_3678': (0x10028e4, u'\u28e4'), 'braille_dots_13678': (0x10028e5, u'\u28e5'), 'braille_dots_23678': (0x10028e6, u'\u28e6'), 'braille_dots_123678': (0x10028e7, u'\u28e7'), 'braille_dots_4678': (0x10028e8, u'\u28e8'), 'braille_dots_14678': (0x10028e9, u'\u28e9'), 'braille_dots_24678': (0x10028ea, u'\u28ea'), 'braille_dots_124678': (0x10028eb, u'\u28eb'), 'braille_dots_34678': (0x10028ec, u'\u28ec'), 'braille_dots_134678': (0x10028ed, u'\u28ed'), 'braille_dots_234678': (0x10028ee, u'\u28ee'), 'braille_dots_1234678': (0x10028ef, u'\u28ef'), 'braille_dots_5678': (0x10028f0, u'\u28f0'), 'braille_dots_15678': (0x10028f1, u'\u28f1'), 'braille_dots_25678': (0x10028f2, u'\u28f2'), 'braille_dots_125678': (0x10028f3, u'\u28f3'), 'braille_dots_35678': (0x10028f4, u'\u28f4'), 'braille_dots_135678': (0x10028f5, u'\u28f5'), 'braille_dots_235678': (0x10028f6, u'\u28f6'), 'braille_dots_1235678': (0x10028f7, u'\u28f7'), 'braille_dots_45678': (0x10028f8, u'\u28f8'), 'braille_dots_145678': (0x10028f9, u'\u28f9'), 'braille_dots_245678': (0x10028fa, u'\u28fa'), 'braille_dots_1245678': (0x10028fb, u'\u28fb'), 'braille_dots_345678': (0x10028fc, u'\u28fc'), 'braille_dots_1345678': (0x10028fd, u'\u28fd'), 'braille_dots_2345678': (0x10028fe, u'\u28fe'), 'braille_dots_12345678': (0x10028ff, u'\u28ff'), 'Sinh_ng': (0x1000d82, u'\u0D82'), 'Sinh_h2': (0x1000d83, u'\u0D83'), 'Sinh_a': (0x1000d85, u'\u0D85'), 'Sinh_aa': (0x1000d86, u'\u0D86'), 'Sinh_ae': (0x1000d87, u'\u0D87'), 'Sinh_aee': (0x1000d88, u'\u0D88'), 'Sinh_i': (0x1000d89, u'\u0D89'), 'Sinh_ii': (0x1000d8a, u'\u0D8A'), 'Sinh_u': (0x1000d8b, u'\u0D8B'), 'Sinh_uu': (0x1000d8c, u'\u0D8C'), 'Sinh_ri': (0x1000d8d, u'\u0D8D'), 'Sinh_rii': (0x1000d8e, u'\u0D8E'), 'Sinh_lu': (0x1000d8f, u'\u0D8F'), 'Sinh_luu': (0x1000d90, u'\u0D90'), 'Sinh_e': (0x1000d91, u'\u0D91'), 'Sinh_ee': (0x1000d92, u'\u0D92'), 'Sinh_ai': (0x1000d93, u'\u0D93'), 'Sinh_o': (0x1000d94, u'\u0D94'), 'Sinh_oo': (0x1000d95, u'\u0D95'), 'Sinh_au': (0x1000d96, u'\u0D96'), 'Sinh_ka': (0x1000d9a, u'\u0D9A'), 'Sinh_kha': (0x1000d9b, u'\u0D9B'), 'Sinh_ga': (0x1000d9c, u'\u0D9C'), 'Sinh_gha': (0x1000d9d, u'\u0D9D'), 'Sinh_ng2': (0x1000d9e, u'\u0D9E'), 'Sinh_nga': (0x1000d9f, u'\u0D9F'), 'Sinh_ca': (0x1000da0, u'\u0DA0'), 'Sinh_cha': (0x1000da1, u'\u0DA1'), 'Sinh_ja': (0x1000da2, u'\u0DA2'), 'Sinh_jha': (0x1000da3, u'\u0DA3'), 'Sinh_nya': (0x1000da4, u'\u0DA4'), 'Sinh_jnya': (0x1000da5, u'\u0DA5'), 'Sinh_nja': (0x1000da6, u'\u0DA6'), 'Sinh_tta': (0x1000da7, u'\u0DA7'), 'Sinh_ttha': (0x1000da8, u'\u0DA8'), 'Sinh_dda': (0x1000da9, u'\u0DA9'), 'Sinh_ddha': (0x1000daa, u'\u0DAA'), 'Sinh_nna': (0x1000dab, u'\u0DAB'), 'Sinh_ndda': (0x1000dac, u'\u0DAC'), 'Sinh_tha': (0x1000dad, u'\u0DAD'), 'Sinh_thha': (0x1000dae, u'\u0DAE'), 'Sinh_dha': (0x1000daf, u'\u0DAF'), 'Sinh_dhha': (0x1000db0, u'\u0DB0'), 'Sinh_na': (0x1000db1, u'\u0DB1'), 'Sinh_ndha': (0x1000db3, u'\u0DB3'), 'Sinh_pa': (0x1000db4, u'\u0DB4'), 'Sinh_pha': (0x1000db5, u'\u0DB5'), 'Sinh_ba': (0x1000db6, u'\u0DB6'), 'Sinh_bha': (0x1000db7, u'\u0DB7'), 'Sinh_ma': (0x1000db8, u'\u0DB8'), 'Sinh_mba': (0x1000db9, u'\u0DB9'), 'Sinh_ya': (0x1000dba, u'\u0DBA'), 'Sinh_ra': (0x1000dbb, u'\u0DBB'), 'Sinh_la': (0x1000dbd, u'\u0DBD'), 'Sinh_va': (0x1000dc0, u'\u0DC0'), 'Sinh_sha': (0x1000dc1, u'\u0DC1'), 'Sinh_ssha': (0x1000dc2, u'\u0DC2'), 'Sinh_sa': (0x1000dc3, u'\u0DC3'), 'Sinh_ha': (0x1000dc4, u'\u0DC4'), 'Sinh_lla': (0x1000dc5, u'\u0DC5'), 'Sinh_fa': (0x1000dc6, u'\u0DC6'), 'Sinh_al': (0x1000dca, u'\u0DCA'), 'Sinh_aa2': (0x1000dcf, u'\u0DCF'), 'Sinh_ae2': (0x1000dd0, u'\u0DD0'), 'Sinh_aee2': (0x1000dd1, u'\u0DD1'), 'Sinh_i2': (0x1000dd2, u'\u0DD2'), 'Sinh_ii2': (0x1000dd3, u'\u0DD3'), 'Sinh_u2': (0x1000dd4, u'\u0DD4'), 'Sinh_uu2': (0x1000dd6, u'\u0DD6'), 'Sinh_ru2': (0x1000dd8, u'\u0DD8'), 'Sinh_e2': (0x1000dd9, u'\u0DD9'), 'Sinh_ee2': (0x1000dda, u'\u0DDA'), 'Sinh_ai2': (0x1000ddb, u'\u0DDB'), 'Sinh_o2': (0x1000ddc, u'\u0DDC'), 'Sinh_oo2': (0x1000ddd, u'\u0DDD'), 'Sinh_au2': (0x1000dde, u'\u0DDE'), 'Sinh_lu2': (0x1000ddf, u'\u0DDF'), 'Sinh_ruu2': (0x1000df2, u'\u0DF2'), 'Sinh_luu2': (0x1000df3, u'\u0DF3'), 'Sinh_kunddaliya': (0x1000df4, u'\u0DF4')} DEAD_KEYS = { u'\u0300': u'\u0060', u'\u0301': u'\u00B4', u'\u0302': u'\u005E', u'\u0303': u'\u007E', u'\u0304': u'\u00AF', u'\u0306': u'\u02D8', u'\u0307': u'\u02D9', u'\u0308': u'\u00A8', u'\u030A': u'\u02DA', u'\u030B': u'\u02DD', u'\u030C': u'\u02C7', u'\u0327': u'\u00B8', u'\u0328': u'\u02DB', u'\u0345': u'\u037A', u'\u0332': u'\u005F'} CHARS = { codepoint: name for name, (keysym, codepoint) in SYMBOLS.items() if codepoint} KEYSYMS = { keysym: name for name, (keysym, codepoint) in SYMBOLS.items() if codepoint} PK)\H~<><>pynput/_util/win32.py# coding=utf-8 # pynput # Copyright (C) 2015-2016 Moses Palmér # # This program is free software: you can redistribute it and/or modify it under # the terms of the GNU Lesser General Public License as published by the Free # Software Foundation, either version 3 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 Lesser General Public License for more # details. # # You should have received a copy of the GNU Lesser General Public License # along with this program. If not, see . import contextlib import ctypes import six import threading from ctypes import windll, wintypes from . import AbstractListener SendInput = windll.user32.SendInput VkKeyScan = windll.user32.VkKeyScanW GetCurrentThreadId = windll.kernel32.GetCurrentThreadId class MessageLoop(object): """A class representing a message loop. """ #: The message that signals this loop to terminate WM_STOP = 0x0401 _GetMessage = windll.user32.GetMessageW _PeekMessage = windll.user32.PeekMessageW _PostThreadMessage = windll.user32.PostThreadMessageW PM_NOREMOVE = 0 def __init__( self, initialize=lambda message_loop: None, finalize=lambda message_loop: None): self._threadid = None self._initialize = initialize self._finalize = finalize self._event = threading.Event() self.thread = None def __iter__(self): """Initialises the message loop and yields all messages until :meth:`stop` is called. :raises AssertionError: if :meth:`start` has not been called """ assert self._threadid is not None try: # Pump messages until WM_STOP while True: msg = wintypes.MSG() lpmsg = ctypes.byref(msg) r = self._GetMessage(lpmsg, None, 0, 0) if r <= 0 or msg.message == self.WM_STOP: break else: yield msg finally: self._finalize(self) self._threadid = None self.thread = None def start(self): """Starts the message loop. This method must be called before iterating over messages, and it must be called from the same thread. """ self._threadid = GetCurrentThreadId() self.thread = threading.current_thread() # Create the message loop msg = wintypes.MSG() lpmsg = ctypes.byref(msg) self._PeekMessage(lpmsg, None, 0x0400, 0x0400, self.PM_NOREMOVE) # Let the called perform initialisation self._initialize(self) # Set the event to signal to other threads that the loop is created self._event.set() def stop(self): """Stops the message loop. """ self._event.wait() self._PostThreadMessage(self._threadid, self.WM_STOP, 0, 0) if self.thread != threading.current_thread(): self.thread.join() class SystemHook(object): """A class to handle Windows hooks. """ _SetWindowsHookEx = windll.user32.SetWindowsHookExW _UnhookWindowsHookEx = windll.user32.UnhookWindowsHookEx _CallNextHookEx = windll.user32.CallNextHookEx _HOOKPROC = wintypes.WINFUNCTYPE( wintypes.LPARAM, ctypes.c_int32, wintypes.WPARAM, wintypes.LPARAM) #: The registered hook procedures _HOOKS = {} #: The hook action value for actions we should check HC_ACTION = 0 def __init__(self, hook_id, on_hook=lambda code, msg, lpdata: None): self.hook_id = hook_id self.on_hook = on_hook self._hook = None def __enter__(self): key = threading.current_thread() assert key not in self._HOOKS # Add ourself to lookup table and install the hook self._HOOKS[key] = self self._hook = self._SetWindowsHookEx( self.hook_id, self._handler, None, 0) return self def __exit__(self, type, value, traceback): key = threading.current_thread() assert key in self._HOOKS if self._hook is not None: # Uninstall the hook and remove ourself from lookup table self._UnhookWindowsHookEx(self._hook) del self._HOOKS[key] @staticmethod @_HOOKPROC def _handler(code, msg, lpdata): key = threading.current_thread() self = SystemHook._HOOKS.get(key, None) try: if self: self.on_hook(code, msg, lpdata) finally: # Always call the next hook return SystemHook._CallNextHookEx(0, code, msg, lpdata) class MOUSEINPUT(ctypes.Structure): """Contains information about a simulated mouse event. """ MOVE = 0x0001 LEFTDOWN = 0x0002 LEFTUP = 0x0004 RIGHTDOWN = 0x0008 RIGHTUP = 0x0010 MIDDLEDOWN = 0x0020 MIDDLEUP = 0x0040 XDOWN = 0x0080 XUP = 0x0100 WHEEL = 0x0800 HWHEEL = 0x1000 ABSOLUTE = 0x8000 XBUTTON1 = 0x0001 XBUTTON2 = 0x0002 _fields_ = [ ('dx', wintypes.LONG), ('dy', wintypes.LONG), ('mouseData', wintypes.DWORD), ('dwFlags', wintypes.DWORD), ('time', wintypes.DWORD), ('dwExtraInfo', ctypes.c_void_p)] class KEYBDINPUT(ctypes.Structure): """Contains information about a simulated keyboard event. """ EXTENDEDKEY = 0x0001 KEYUP = 0x0002 SCANCODE = 0x0008 UNICODE = 0x0004 _fields_ = [ ('wVk', wintypes.WORD), ('wScan', wintypes.WORD), ('dwFlags', wintypes.DWORD), ('time', wintypes.DWORD), ('dwExtraInfo', ctypes.c_void_p)] class HARDWAREINPUT(ctypes.Structure): """Contains information about a simulated message generated by an input device other than a keyboard or mouse. """ _fields_ = [ ('uMsg', wintypes.DWORD), ('wParamL', wintypes.WORD), ('wParamH', wintypes.WORD)] class INPUT_union(ctypes.Union): """Represents the union of input types in :class:`INPUT`. """ _fields_ = [ ('mi', MOUSEINPUT), ('ki', KEYBDINPUT), ('hi', HARDWAREINPUT)] class INPUT(ctypes.Structure): """Used by :attr:`SendInput` to store information for synthesizing input events such as keystrokes, mouse movement, and mouse clicks. """ MOUSE = 0 KEYBOARD = 1 HARDWARE = 2 _fields_ = [ ('type', wintypes.DWORD), ('value', INPUT_union)] class ListenerMixin(object): """A mixin for *win32* event listeners. Subclasses should set a value for :attr:`_EVENTS` and implement :meth:`_handle`. Subclasses must also be decorated with a decorator compatible with :meth:`pynput._util.NotifierMixin._receiver` or implement the method ``_receive()``. """ #: The Windows hook ID for the events to capture _EVENTS = None def _run(self): self._message_loop = MessageLoop() with self._receive(): self._mark_ready() self._message_loop.start() with SystemHook(self._EVENTS, self._handler): # Just pump messages for msg in self._message_loop: if not self.running: break def _stop(self): try: self._message_loop.stop() except AttributeError: # The loop may not have been created pass @AbstractListener._emitter def _handler(self, code, msg, lpdata): """The callback registered with *Windows* for events. This method will call the callbacks registered on initialisation. """ self._handle(code, msg, lpdata) def _handle(self, code, msg, lpdata): """The device specific callback handler. This method calls the appropriate callback registered when this listener was created based on the event. """ raise NotImplementedError() class KeyTranslator(object): """A class to translate virtual key codes to characters. """ _AttachThreadInput = ctypes.windll.user32.AttachThreadInput _GetForegroundWindow = ctypes.windll.user32.GetForegroundWindow _GetKeyboardLayout = ctypes.windll.user32.GetKeyboardLayout _GetKeyboardState = ctypes.windll.user32.GetKeyboardState _GetWindowThreadProcessId = ctypes.windll.user32.GetWindowThreadProcessId _MapVirtualKeyEx = ctypes.windll.user32.MapVirtualKeyExW _ToUnicodeEx = ctypes.windll.user32.ToUnicodeEx _MAPVK_VK_TO_VSC = 0 _MAPVK_VK_TO_CHAR = 2 def __init__(self): self.__state = (ctypes.c_byte * 255)() self._cache = {} self._reinject_arguments = None def __call__(self, vk, is_press): """Converts a virtual key code to a string. :param int vk: The virtual key code. :param bool is_press: Whether this is a press. Because the *win32* functions used to translate the key modifies internal kernel state, some cleanup must be performed for key presses. :return: parameters suitable for the :class:`pynput.keyboard.KeyCode` constructor :raises OSError: if a call to any *win32* function fails """ # Get the keyboard state and layout state, layout = self._get_state_and_layout() # Get the scan code for the virtual key scan = self._to_scan(vk, layout) # Try to reuse the previous key in the cache try: if is_press: return self._cache[vk] else: return self._cache.pop(vk) except KeyError: pass # Get a string representation of the key char, is_dead = self._to_char(vk, layout) modified_char = self._to_char_with_modifiers(vk, layout, scan, state) # Clear the keyboard state if the key was a dead key if is_dead: self._reset_state(vk, layout, scan) # If the previous key handled was a dead key, we reinject it if self._reinject_arguments: self._reinject(*self._reinject_arguments) self._reinject_arguments = None # If the current key is a dead key, we store the current state to be # able to reinject later elif is_dead: self._reinject_arguments = ( vk, layout, scan, (ctypes.c_byte * 255)(*state)) # Otherwise we just clear any previous dead key state else: self._reinject_arguments = None # Update the cache self._cache[vk] = { 'char': modified_char or char, 'is_dead': is_dead, 'vk': vk} return self._cache[vk] def _get_state_and_layout(self): """Returns the keyboard state and layout. The state is read from the currently active window if possible. It is kept in a cache, so any call to this method will invalidate return values from previous invocations. :return: the tuple ``(state, layout)`` """ # Get the state of the keyboard attached to the active window with self._thread_input() as active_thread: if not self._GetKeyboardState(ctypes.byref(self.__state)): raise OSError( 'GetKeyboardState failed: %d', ctypes.wintypse.get_last_error()) # Get the keyboard layout for the thread for which we retrieved the # state layout = self._GetKeyboardLayout(active_thread) return (self.__state, layout) def _to_scan(self, vk, layout): """Retrieves the scan code for a virtual key code. :param int vk: The virtual key code. :param layout: The keyboard layout. :return: the scan code """ return self._MapVirtualKeyEx( vk, self._MAPVK_VK_TO_VSC, layout) def _to_char(self, vk, layout): """Converts a virtual key by simply mapping it through the keyboard layout. This method is stateless, so any active shift state or dead keys are ignored. :param int vk: The virtual key code. :param layout: The keyboard layout. :return: the string representation of the key, or ``None``, and whether was dead as the tuple ``(char, is_dead)`` """ # MapVirtualKeyEx will yield a string representation for dead keys flags_and_codepoint = self._MapVirtualKeyEx( vk, self._MAPVK_VK_TO_CHAR, layout) if flags_and_codepoint: return ( six.unichr(flags_and_codepoint & 0xFFFF), bool(flags_and_codepoint & (1 << 31))) else: return (None, None) def _to_char_with_modifiers(self, vk, layout, scan, state): """Converts a virtual key by mapping it through the keyboard layout and internal kernel keyboard state. This method is stateful, so any active shift state and dead keys are applied. Currently active dead keys will be removed from the internal kernel keyboard state. :param int vk: The virtual key code. :param layout: The keyboard layout. :param int scan: The scan code of the key. :param state: The keyboard state. :return: the string representation of the key, or ``None`` """ # This will apply any dead keys and modify the internal kernel keyboard # state out = (ctypes.wintypes.WCHAR * 5)() count = self._ToUnicodeEx( vk, scan, ctypes.byref(state), ctypes.byref(out), len(out), 0, layout) return out[0] if count > 0 else None def _reset_state(self, vk, layout, scan): """Clears the internal kernel keyboard state. This method will remove all dead keys from the internal state. :param int vk: The virtual key code. :param layout: The keyboard layout. :param int scan: The scan code of the key. """ state = (ctypes.c_byte * 255)() out = (ctypes.wintypes.WCHAR * 5)() while self._ToUnicodeEx( vk, scan, ctypes.byref(state), ctypes.byref(out), len(out), 0, layout) < 0: pass def _reinject(self, vk, layout, scan, state): """Reinjects the previous dead key. This must be called if ``ToUnicodeEx`` has been called, and the previous key was a dead one. :param int vk: The virtual key code. :param layout: The keyboard layout. :param int scan: The scan code of the key. :param state: The keyboard state. """ out = (ctypes.wintypes.WCHAR * 5)() self._ToUnicodeEx( vk, scan, ctypes.byref(state), ctypes.byref(out), len(out), 0, layout) @contextlib.contextmanager def _thread_input(self): """Temporarily attaches the input handling of this thread to that of the currently active window. The context manager returns the ID of the thread to which the input handling is attached. This is the ID of the current thread if attaching failed. """ remote_thread = self._GetWindowThreadProcessId( self._GetForegroundWindow(), None) local_thread = GetCurrentThreadId() if self._AttachThreadInput(local_thread, remote_thread, True): try: yield remote_thread finally: self._AttachThreadInput(local_thread, remote_thread, False) else: yield local_thread PK)\HIܙpynput/mouse/__init__.py# coding=utf-8 # pynput # Copyright (C) 2015-2016 Moses Palmér # # This program is free software: you can redistribute it and/or modify it under # the terms of the GNU Lesser General Public License as published by the Free # Software Foundation, either version 3 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 Lesser General Public License for more # details. # # You should have received a copy of the GNU Lesser General Public License # along with this program. If not, see . import os import sys if os.environ.get('__PYNPUT_GENERATE_DOCUMENTATION') == 'yes': from ._base import Button, Controller, Listener else: Button = None Controller = None Listener = None if sys.platform == 'darwin': if not Button and not Controller and not Listener: from ._darwin import Button, Controller, Listener elif sys.platform == 'win32': if not Button and not Controller and not Listener: from ._win32 import Button, Controller, Listener else: if not Button and not Controller and not Listener: try: from ._xorg import Button, Controller, Listener except: pass if not Button or not Controller or not Listener: raise ImportError('this platform is not supported') PK)\Hpynput/mouse/_darwin.py# coding=utf-8 # pynput # Copyright (C) 2015-2016 Moses Palmér # # This program is free software: you can redistribute it and/or modify it under # the terms of the GNU Lesser General Public License as published by the Free # Software Foundation, either version 3 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 Lesser General Public License for more # details. # # You should have received a copy of the GNU Lesser General Public License # along with this program. If not, see . import enum import Quartz from AppKit import NSEvent from pynput._util.darwin import * from . import _base def _button_value(base_name, mouse_button): """Generates the value tuple for a :class:`Button` value. :param str base_name: The base name for the button. This shuld be a string like ``'kCGEventLeftMouse'``. :param int mouse_button: The mouse button ID. :return: a value tuple """ return ( tuple( getattr(Quartz, '%sMouse%s' % (base_name, name)) for name in ('Down', 'Up', 'Dragged')), mouse_button) class Button(enum.Enum): """The various buttons. """ left = _button_value('kCGEventLeft', 0) middle = _button_value('kCGEventOther', 2) right = _button_value('kCGEventRight', 1) class Controller(_base.Controller): def __init__(self, *args, **kwargs): super(Controller, self).__init__(*args, **kwargs) self._click = None self._drag_button = None def _position_get(self): pos = NSEvent.mouseLocation() return pos.x, Quartz.CGDisplayPixelsHigh(0) - pos.y def _position_set(self, pos): try: (_, _, mouse_type), mouse_button = self._drag_button except TypeError: mouse_type = Quartz.kCGEventMouseMoved mouse_button = 0 Quartz.CGEventPost( Quartz.kCGHIDEventTap, Quartz.CGEventCreateMouseEvent( None, mouse_type, pos, mouse_button)) def _scroll(self, dx, dy): while dx != 0 or dy != 0: xval = 1 if dx > 0 else -1 if dx < 0 else 0 dx -= xval yval = 1 if dy > 0 else -1 if dy < 0 else 0 dy -= yval Quartz.CGEventPost( Quartz.kCGHIDEventTap, Quartz.CGEventCreateScrollWheelEvent( None, Quartz.kCGScrollEventUnitPixel, 2, yval * 1, xval * 1)) def _press(self, button): (press, release, drag), mouse_button = button.value event = Quartz.CGEventCreateMouseEvent( None, press, self.position, mouse_button) # If we are performing a click, we need to set this state flag if self._click is not None: self._click += 1 Quartz.CGEventSetIntegerValueField( event, Quartz.kCGMouseEventClickState, self._click) Quartz.CGEventPost(Quartz.kCGHIDEventTap, event) # Store the button to enable dragging self._drag_button = button def _release(self, button): (press, release, drag), mouse_button = button.value event = Quartz.CGEventCreateMouseEvent( None, release, self.position, mouse_button) # If we are performing a click, we need to set this state flag if self._click is not None: Quartz.CGEventSetIntegerValueField( event, Quartz.kCGMouseEventClickState, self._click) Quartz.CGEventPost(Quartz.kCGHIDEventTap, event) if button == self._drag_button: self._drag_button = None def __enter__(self): self._click = 0 return self def __exit__(self, type, value, traceback): self._click = None class Listener(ListenerMixin, _base.Listener): #: The events that we listen to _EVENTS = ( Quartz.CGEventMaskBit(Quartz.kCGEventMouseMoved) | Quartz.CGEventMaskBit(Quartz.kCGEventLeftMouseDown) | Quartz.CGEventMaskBit(Quartz.kCGEventLeftMouseUp) | Quartz.CGEventMaskBit(Quartz.kCGEventRightMouseDown) | Quartz.CGEventMaskBit(Quartz.kCGEventRightMouseUp) | Quartz.CGEventMaskBit(Quartz.kCGEventOtherMouseDown) | Quartz.CGEventMaskBit(Quartz.kCGEventOtherMouseUp) | Quartz.CGEventMaskBit(Quartz.kCGEventScrollWheel)) def _handle(self, proxy, event_type, event, refcon): """The callback registered with *Mac OSX* for mouse events. This method will call the callbacks registered on initialisation. """ try: (x, y) = Quartz.CGEventGetLocation(event) except AttributeError: # This happens during teardown of the virtual machine return # Quickly detect the most common event type if event_type == Quartz.kCGEventMouseMoved: self.on_move(x, y) elif event_type == Quartz.kCGEventScrollWheel: dx = Quartz.CGEventGetIntegerValueField( event, Quartz.kCGScrollWheelEventDeltaAxis2) dy = Quartz.CGEventGetIntegerValueField( event, Quartz.kCGScrollWheelEventDeltaAxis1) self.on_scroll(x, y, dx, dy) else: for button in Button: (press, release, drag), mouse_button = button.value # Press and release generate click events, and drag # generates move events if event_type in (press, release): self.on_click(x, y, button, event_type == press) elif event_type == drag: self.on_move(x, y) PK)\H0pynput/mouse/_win32.py# coding=utf-8 # pynput # Copyright (C) 2015-2016 Moses Palmér # # This program is free software: you can redistribute it and/or modify it under # the terms of the GNU Lesser General Public License as published by the Free # Software Foundation, either version 3 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 Lesser General Public License for more # details. # # You should have received a copy of the GNU Lesser General Public License # along with this program. If not, see . import enum from pynput._util import NotifierMixin from pynput._util.win32 import * from . import _base class Button(enum.Enum): """The various buttons. """ left = (MOUSEINPUT.LEFTUP, MOUSEINPUT.LEFTDOWN) middle = (MOUSEINPUT.MIDDLEUP, MOUSEINPUT.MIDDLEDOWN) right = (MOUSEINPUT.RIGHTUP, MOUSEINPUT.RIGHTDOWN) class Controller(NotifierMixin, _base.Controller): __GetCursorPos = windll.user32.GetCursorPos __SetCursorPos = windll.user32.SetCursorPos def _position_get(self): point = wintypes.POINT() if self.__GetCursorPos(ctypes.byref(point)): return (point.x, point.y) else: return None def _position_set(self, pos): self.__SetCursorPos(*pos) self._emit('on_move', *pos) def _scroll(self, dx, dy): if dy: SendInput( 1, ctypes.byref(INPUT( type=INPUT.MOUSE, value=INPUT_union( mouse=MOUSEINPUT( dwFlags=MOUSEINPUT.WHEEL, mouseData=dy)))), ctypes.sizeof(INPUT)) if dx: SendInput( 1, ctypes.byref(INPUT( type=INPUT.MOUSE, value=INPUT_union( mouse=MOUSEINPUT( dwFlags=MOUSEINPUT.HWHEEL, mouseData=dy)))), ctypes.sizeof(INPUT)) if dx or dy: x, y = self._position_get() self._emit('on_scroll', x, y, dx, dy) def _press(self, button): SendInput( 1, ctypes.byref(INPUT( type=INPUT.MOUSE, value=INPUT_union( mouse=MOUSEINPUT( dwFlags=button.value[0])))), ctypes.sizeof(INPUT)) x, y = self.position self._emit('on_click', x, y, button, True) def _release(self, button): SendInput( 1, ctypes.byref(INPUT( type=INPUT.MOUSE, value=INPUT_union( mouse=MOUSEINPUT( dwFlags=button.value[1])))), ctypes.sizeof(INPUT)) x, y = self.position self._emit('on_click', x, y, button, False) @Controller._receiver class Listener(ListenerMixin, _base.Listener): #: The Windows hook ID for low level mouse events, ``WH_MOUSE_LL`` _EVENTS = 14 _WM_LBUTTONDOWN = 0x0201 _WM_LBUTTONUP = 0x0202 _WM_MOUSEMOVE = 0x0200 _WM_MOUSEWHEEL = 0x020A _WM_MOUSEHWHEEL = 0x020E _WM_RBUTTONDOWN = 0x0204 _WM_RBUTTONUP = 0x0205 _WHEEL_DELTA = 120 #: A mapping from messages to button events _CLICK_BUTTONS = { _WM_LBUTTONDOWN: (Button.left, True), _WM_LBUTTONUP: (Button.left, False), _WM_RBUTTONDOWN: (Button.right, True), _WM_RBUTTONUP: (Button.right, False)} #: A mapping from messages to scroll vectors _SCROLL_BUTTONS = { _WM_MOUSEWHEEL: (0, 1), _WM_MOUSEHWHEEL: (1, 0)} class _MSLLHOOKSTRUCT(ctypes.Structure): """Contains information about a mouse event passed to a ``WH_MOUSE_LL`` hook procedure, ``MouseProc``. """ _fields_ = [ ('pt', wintypes.POINT), ('mouseData', wintypes.DWORD), ('flags', wintypes.DWORD), ('time', wintypes.DWORD), ('dwExtraInfo', ctypes.c_void_p)] #: A pointer to a :class:`_MSLLHOOKSTRUCT` _LPMSLLHOOKSTRUCT = ctypes.POINTER(_MSLLHOOKSTRUCT) def _handle(self, code, msg, lpdata): if code != SystemHook.HC_ACTION: return data = ctypes.cast(lpdata, self._LPMSLLHOOKSTRUCT).contents if msg == self._WM_MOUSEMOVE: self.on_move(data.pt.x, data.pt.y) elif msg in self._CLICK_BUTTONS: button, pressed = self._CLICK_BUTTONS[msg] self.on_click(data.pt.x, data.pt.y, button, pressed) elif msg in self._SCROLL_BUTTONS: mx, my = self._SCROLL_BUTTONS[msg] d = wintypes.SHORT(data.mouseData >> 16).value // self._WHEEL_DELTA self.on_scroll(data.pt.x, data.pt.y, d * mx, d * my) PK)\H!= = pynput/mouse/_xorg.py# coding=utf-8 # pynput # Copyright (C) 2015-2016 Moses Palmér # # This program is free software: you can redistribute it and/or modify it under # the terms of the GNU Lesser General Public License as published by the Free # Software Foundation, either version 3 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 Lesser General Public License for more # details. # # You should have received a copy of the GNU Lesser General Public License # along with this program. If not, see . import enum import Xlib.display import Xlib.ext import Xlib.ext.xtest import Xlib.X import Xlib.protocol from pynput._util.xorg import * from . import _base class Button(enum.Enum): """The various buttons. """ left = 1 middle = 2 right = 3 scroll_up = 4 scroll_down = 5 scroll_left = 6 scroll_right = 7 class Controller(_base.Controller): def __init__(self): self._display = Xlib.display.Display() def __del__(self): if hasattr(self, '_display'): self._display.close() def _position_get(self): with display_manager(self._display) as d: data = d.screen().root.query_pointer()._data return (data["root_x"], data["root_y"]) def _position_set(self, pos): x, y = pos with display_manager(self._display) as d: Xlib.ext.xtest.fake_input(d, Xlib.X.MotionNotify, x=x, y=y) def _scroll(self, dx, dy): if dy: self.click( button=Button.scroll_up if dy > 0 else Button.scroll_down, count=abs(dy)) if dx: self.click( button=Button.scroll_right if dx > 0 else Button.scroll_left, count=abs(dx)) def _press(self, button): with display_manager(self._display) as d: Xlib.ext.xtest.fake_input(d, Xlib.X.ButtonPress, button.value) def _release(self, button): with display_manager(self._display) as d: Xlib.ext.xtest.fake_input(d, Xlib.X.ButtonRelease, button.value) class Listener(ListenerMixin, _base.Listener): #: A mapping from button values to scroll directions _SCROLL_BUTTONS = { Button.scroll_up.value: (0, 1), Button.scroll_down.value: (0, -1), Button.scroll_right.value: (1, 0), Button.scroll_left.value: (-1, 0)} _EVENTS = ( Xlib.X.ButtonPressMask, Xlib.X.ButtonReleaseMask) def _handle(self, display, event): x = event.root_x y = event.root_y if event.type == Xlib.X.ButtonPress: # Scroll events are sent as button presses with the scroll # button codes scroll = self._SCROLL_BUTTONS.get(event.detail, None) if scroll: self.on_scroll(x, y, *scroll) else: self.on_click(x, y, Button(event.detail), True) elif event.type == Xlib.X.ButtonRelease: # Send an event only if this was not a scroll event if event.detail not in self._SCROLL_BUTTONS: self.on_click(x, y, Button(event.detail), False) else: self.on_move(x, y) PK)\Hz pynput/mouse/_base.py# coding=utf-8 # pynput # Copyright (C) 2015-2016 Moses Palmér # # This program is free software: you can redistribute it and/or modify it under # the terms of the GNU Lesser General Public License as published by the Free # Software Foundation, either version 3 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 Lesser General Public License for more # details. # # You should have received a copy of the GNU Lesser General Public License # along with this program. If not, see . import enum from pynput._util import AbstractListener class Button(enum.Enum): """The various buttons. The actual values for these items differ between platforms. Some platforms may have additional buttons, but these are guaranteed to be present everywhere. """ #: The left button left = 1 #: The middle button middle = 2 #: The right button right = 3 class Controller(object): """A controller for sending virtual mouse events to the system. """ @property def position(self): """The current position of the mouse pointer. This is the tuple ``(x, y)``, and setting it will move the pointer. """ return self._position_get() @position.setter def position(self, pos): self._position_set(pos) def scroll(self, dx, dy): """Sends scroll events. :param int dx: The horizontal scroll. The units of scrolling is undefined. :param int dy: The vertical scroll. The units of scrolling is undefined. """ self._scroll(dx, dy) def press(self, button): """Emits a button press event at the current position. :param Button button: The button to press. """ self._press(button) def release(self, button): """Emits a button release event at the current position. :param Button button: The button to release. """ self._release(button) def move(self, dx, dy): """Moves the mouse pointer a number of pixels from its current position. :param int x: The horizontal offset. :param int dy: The vertical offset. """ self.position = tuple(sum(i) for i in zip(self.position, (dx, dy))) def click(self, button, count=1): """Emits a button click event at the current position. The default implementation sends a series of press and release events. :param Button button: The button to click. :param int count: The number of clicks to send. """ with self as controller: for _ in range(count): controller.press(button) controller.release(button) def __enter__(self): """Begins a series of clicks. In the default :meth:`click` implementation, the return value of this method is used for the calls to :meth:`press` and :meth:`release` instead of ``self``. The default implementation is a no-op. """ return self def __exit__(self, type, value, traceback): """Ends a series of clicks. """ pass def _position_get(self): """The implementation of the getter for :attr:`position`. This is a platform dependent implementation. """ raise NotImplementedError() def _position_set(self, pos): """The implementation of the setter for :attr:`position`. This is a platform dependent implementation. """ raise NotImplementedError() def _scroll(self, dx, dy): """The implementation of the :meth:`scroll` method. This is a platform dependent implementation. """ raise NotImplementedError() def _press(self, button): """The implementation of the :meth:`press` method. This is a platform dependent implementation. """ raise NotImplementedError() def _release(self, button): """The implementation of the :meth:`release` method. This is a platform dependent implementation. """ raise NotImplementedError() class Listener(AbstractListener): """A listener for mouse events. Instances of this class can be used as context managers. This is equivalent to the following code:: listener.start() try: with_statements() finally: listener.stop() This class inherits from :class:`threading.Thread` and supports all its methods. It will set :attr:`daemon` to ``True`` when created. :param callable on_move: The callback to call when mouse move events occur. It will be called with the arguments ``(x, y)``, which is the new pointer position. If this callback raises :class:`StopException` or returns ``False``, the listener is stopped. :param callable on_click: The callback to call when a mouse button is clicked. It will be called with the arguments ``(x, y, button, pressed)``, where ``(x, y)`` is the new pointer position, ``button`` is one of the :class:`Button` values and ``pressed`` is whether the button was pressed. If this callback raises :class:`StopException` or returns ``False``, the listener is stopped. :param callable on_scroll: The callback to call when mouse scroll events occur. It will be called with the arguments ``(x, y, dx, dy)``, where ``(x, y)`` is the new pointer position, and ``(dx, dy)`` is the scroll vector. If this callback raises :class:`StopException` or returns ``False``, the listener is stopped. """ def __init__(self, on_move=None, on_click=None, on_scroll=None): super(Listener, self).__init__( on_move=on_move, on_click=on_click, on_scroll=on_scroll) PKk{H]++%pystray-0.2.dist-info/DESCRIPTION.rstpystray Package Documentation ============================= This library allows you to create a *system tray icon*. Creating a *system tray icon* ----------------------------- In order to create a *system tray icon*, the class ``pystray.Icon`` is used:: import pystray icon = pystray.Icon('test name') In order for the icon to be displayed, we must provide an icon. This icon must be specified as a ``PIL.Image.Image``:: from PIL import Image, ImageDraw # Generate an image image = Image.new('RGB', (width, height), color1) dc = ImageDraw.Draw(image) dc.rectangle((width // 2, 0, width, height // 2), fill=color2) dc.rectangle((0, height // 2, width // 2, height), fill=color2) icon.image = image To ensure that your application runs on all platforms, you must then run the following code to show the icon:: def setup(icon): icon.visible = True icon.run(setup) The call to ``pystray.Icon.run()`` is blocking, and it must be performed from the main thread of the application. The reason for this is that the *system tray icon* implementation for *OSX* must be run from the main thread, and it requires the application runloop to be running. ``pystray.Icon.run()`` will start the runloop. The code in ``setup()`` will be run in a separate thread once the *system tray icon* is ready. The icon does not wait for it to complete, so you may put any code that would follow the call to ``pystray.Icon.run()`` in it. ``pystray.Icon.run()`` will not complete until ``~pystray.Icon.stop()`` is called. If you do not wish to support *OSX*, the above code can be replaced with the following:: icon.visible = True Release Notes ============= v0.2 - Initial Release ---------------------- * Support for adding a system tray icon on *Linux*, *Mac OSX* and *Windows*. PKk{H\9#pystray-0.2.dist-info/metadata.json{"classifiers": ["Development Status :: 4 - Beta", "Intended Audience :: Developers", "License :: OSI Approved :: GNU Lesser General Public License v3 (LGPLv3)", "Operating System :: MacOS :: MacOS X", "Operating System :: Microsoft :: Windows :: Windows NT/2000", "Operating System :: POSIX", "Programming Language :: Python", "Programming Language :: Python :: 2.7", "Programming Language :: Python :: 3.4"], "extensions": {"python.details": {"contacts": [{"email": "moses.palmer@gmail.com", "name": "Moses Palm\u00c3\u00a9r", "role": "author"}], "document_names": {"description": "DESCRIPTION.rst"}, "project_urls": {"Home": "https://github.com/moses-palmer/pystray"}}}, "extras": [], "generator": "bdist_wheel (0.26.0)", "keywords": ["system", "tray", "icon", "systray", "icon"], "license": "LGPLv3", "metadata_version": "2.0", "name": "pystray", "run_requires": [{"requires": ["Pillow"]}], "summary": "Provides systray integration", "version": "0.2"}PKk{H3.O..pystray-0.2.dist-info/pbr.json{"is_release": true, "git_version": "c5a322c"}PKk{Hm#pystray-0.2.dist-info/top_level.txtpystray PKMcH2pystray-0.2.dist-info/zip-safe PKk{H''\\pystray-0.2.dist-info/WHEELWheel-Version: 1.0 Generator: bdist_wheel (0.26.0) Root-Is-Purelib: true Tag: py2-none-any PKk{H!n - - pystray-0.2.dist-info/METADATAMetadata-Version: 2.0 Name: pystray Version: 0.2 Summary: Provides systray integration Home-page: https://github.com/moses-palmer/pystray Author: Moses Palmér Author-email: moses.palmer@gmail.com License: LGPLv3 Keywords: system tray icon,systray icon Platform: UNKNOWN Classifier: Development Status :: 4 - Beta Classifier: Intended Audience :: Developers Classifier: License :: OSI Approved :: GNU Lesser General Public License v3 (LGPLv3) Classifier: Operating System :: MacOS :: MacOS X Classifier: Operating System :: Microsoft :: Windows :: Windows NT/2000 Classifier: Operating System :: POSIX Classifier: Programming Language :: Python Classifier: Programming Language :: Python :: 2.7 Classifier: Programming Language :: Python :: 3.4 Requires-Dist: Pillow pystray Package Documentation ============================= This library allows you to create a *system tray icon*. Creating a *system tray icon* ----------------------------- In order to create a *system tray icon*, the class ``pystray.Icon`` is used:: import pystray icon = pystray.Icon('test name') In order for the icon to be displayed, we must provide an icon. This icon must be specified as a ``PIL.Image.Image``:: from PIL import Image, ImageDraw # Generate an image image = Image.new('RGB', (width, height), color1) dc = ImageDraw.Draw(image) dc.rectangle((width // 2, 0, width, height // 2), fill=color2) dc.rectangle((0, height // 2, width // 2, height), fill=color2) icon.image = image To ensure that your application runs on all platforms, you must then run the following code to show the icon:: def setup(icon): icon.visible = True icon.run(setup) The call to ``pystray.Icon.run()`` is blocking, and it must be performed from the main thread of the application. The reason for this is that the *system tray icon* implementation for *OSX* must be run from the main thread, and it requires the application runloop to be running. ``pystray.Icon.run()`` will start the runloop. The code in ``setup()`` will be run in a separate thread once the *system tray icon* is ready. The icon does not wait for it to complete, so you may put any code that would follow the call to ``pystray.Icon.run()`` in it. ``pystray.Icon.run()`` will not complete until ``~pystray.Icon.stop()`` is called. If you do not wish to support *OSX*, the above code can be replaced with the following:: icon.visible = True Release Notes ============= v0.2 - Initial Release ---------------------- * Support for adding a system tray icon on *Linux*, *Mac OSX* and *Windows*. PKk{HL pystray-0.2.dist-info/RECORDpynput/__init__.py,sha256=Dz7ov5SGauX4u5kYp8QjpaBCzod3DFHk-qugbDrzHuY,763 pynput/_info.py,sha256=8AR9P8KU--JEI8ZvKp1aRYmghWEzlVYRBXAn4QjTB1g,67 pynput/_util/__init__.py,sha256=WjhNLte3tO40IzBEpH-houq0VuoeHdF3TGI583fVL8o,7357 pynput/_util/darwin.py,sha256=hyeGWWPWF5pctqIv5NiOB1IJA5uzEaSzcgK8oTKFGxU,6981 pynput/_util/win32.py,sha256=4DzK6tR1KHXlJUCINfZGMSIWZstdg_WtzO0XgJQ0Pw0,15932 pynput/_util/xorg.py,sha256=4TWuDupFIPTtI6H985XUIigawE7YnnWb2yv3kkquBJ8,13366 pynput/_util/xorg_keysyms.py,sha256=1Nmkfdt9hsxMmQeJ3f57e01jlX0k9h0FsKwz0aNwKsY,67297 pynput/keyboard/__init__.py,sha256=DF7TOjeZdB1yMjTg_q826BimMWXJ3MtATtWhxufulXs,1576 pynput/keyboard/_base.py,sha256=dptI0SXalEPFR1pYc4sWEEyWArqcUVIE8zdrWXpH3GE,16848 pynput/keyboard/_darwin.py,sha256=nlmYhJocY5kbVvAkW9KsDByPN5R6VmoTn98DdxD94Yg,7773 pynput/keyboard/_win32.py,sha256=PX1ULr4WXSeu_c5tBiYc7fsX4hfzzXAGDAhQSVk78Ac,6912 pynput/keyboard/_xorg.py,sha256=PzM6yL8i8MU0y8zk_OfoopWCRquBpb4__Ssa3sTTfIg,15844 pynput/mouse/__init__.py,sha256=c9LL5Yi3pRiU8fgn2gnXD_5AQbpR4h-NnglGi0fuVN4,1485 pynput/mouse/_base.py,sha256=YXxBPW8UGdcCufj-qb-h-V0co8bLZ0OJGMWoyfrBxOA,6095 pynput/mouse/_darwin.py,sha256=_U8Vgx3XMjFKDHqHHesEZssIqiYQyHkrEa3Op37F6Wg,6101 pynput/mouse/_win32.py,sha256=xpEA64ib6DtZ9-xh6pYKKRxS0cpobcVy5woY1gmvIeo,5024 pynput/mouse/_xorg.py,sha256=zz5TvsXx4knq7iv3-_3BPKeTejS4bOsj4eXH7CZDtfY,3389 pystray/__init__.py,sha256=Q0lc5c50n5FbOoSd4pvyhHxcA9kK3eajskFI1u5qx98,1185 pystray/_base.py,sha256=RiWPeYNdD88b0HVd10ObvQtiI5ZLcwnHE5Pq3lUNJYI,5510 pystray/_darwin.py,sha256=BFS6vJUzjWcaprqqqnR4KAUqxIyKJl-nudrfEhQ1Uhs,4050 pystray/_info.py,sha256=Vc0pCY8TWh_0sjZpyFLO8m7edrRXxIY9QptCMSXF3aE,67 pystray/_win32.py,sha256=2dHLpNiZodobJpuQHqy3Wq38Z_35iiQlPpCglZVdfmA,11358 pystray/_xorg.py,sha256=9ooruwun6iTN3BXImH_s2KWlOPySKiwsReOFUbcmIV4,14683 pystray-0.2.dist-info/DESCRIPTION.rst,sha256=ll5puFkLS0mQK0TGlZNu58CPf17hnZ9YNTapXLF2oLQ,1835 pystray-0.2.dist-info/METADATA,sha256=NPgeQUlHC88pjOFwDhrrcSSClLzbhVFMRh7Wn8C5dD8,2605 pystray-0.2.dist-info/RECORD,, pystray-0.2.dist-info/WHEEL,sha256=JTb7YztR8fkPg6aSjc571Q4eiVHCwmUDlX8PhuuqIIE,92 pystray-0.2.dist-info/metadata.json,sha256=JMBtyPnWRvZ3cYMZMqSVH74f-rHex8vg7eSAr47q2xk,955 pystray-0.2.dist-info/pbr.json,sha256=rL7ESiciqFlKla2DzVZz3-NnYCPUb5zy7cydU7IymjM,46 pystray-0.2.dist-info/top_level.txt,sha256=6xvZ5fFN6P3IotgMJLAHeWonXXZzDdf47BhMLsXEHlg,8 pystray-0.2.dist-info/zip-safe,sha256=AbpHGcgLb-kRsJGnwFEktk7uzpZOCcBY74-YBdrKVGs,1 PKk{Hzpystray/__init__.pyPK-lxHtHpystray/_darwin.pyPKmxHŽ^,^,pystray/_win32.pyPKmxH;P[9[9aApystray/_xorg.pyPKlxHL%zpystray/_base.pyPKk{H#1vCCpystray/_info.pyPK)\H0Fpynput/__init__.pyPK)\H6cCC:pynput/_info.pyPK)\HDwX((pynput/keyboard/__init__.pyPK)\H~6J]] pynput/keyboard/_darwin.pyPK)\H-pynput/keyboard/_win32.pyPK)\Hx@\==pynput/keyboard/_xorg.pyPK)\H(;AApynput/keyboard/_base.pyPK)\H}1Tpynput/_util/__init__.pyPK)\HCHEEqpynput/_util/darwin.pyPK)\H#HP6464cpynput/_util/xorg.pyPK)\Hݧ[pynput/_util/xorg_keysyms.pyPK)\H~<><>pynput/_util/win32.pyPK)\HIܙUpynput/mouse/__init__.pyPK)\HX pynput/mouse/_darwin.pyPK)\H0b%pynput/mouse/_win32.pyPK)\H!= = 69pynput/mouse/_xorg.pyPK)\Hz Fpynput/mouse/_base.pyPKk{H]++%^pystray-0.2.dist-info/DESCRIPTION.rstPKk{H\9#fpystray-0.2.dist-info/metadata.jsonPKk{H3.O..jpystray-0.2.dist-info/pbr.jsonPKk{Hm#|jpystray-0.2.dist-info/top_level.txtPKMcH2jpystray-0.2.dist-info/zip-safePKk{H''\\kpystray-0.2.dist-info/WHEELPKk{H!n - - kpystray-0.2.dist-info/METADATAPKk{HL vpystray-0.2.dist-info/RECORDPKu