diff options
Diffstat (limited to 'external/plyer/platforms/win')
-rw-r--r-- | external/plyer/platforms/win/__init__.py | 0 | ||||
-rw-r--r-- | external/plyer/platforms/win/battery.py | 21 | ||||
-rw-r--r-- | external/plyer/platforms/win/email.py | 34 | ||||
-rw-r--r-- | external/plyer/platforms/win/filechooser.py | 112 | ||||
-rw-r--r-- | external/plyer/platforms/win/libs/__init__.py | 0 | ||||
-rw-r--r-- | external/plyer/platforms/win/libs/balloontip.py | 145 | ||||
-rw-r--r-- | external/plyer/platforms/win/libs/batterystatus.py | 13 | ||||
-rw-r--r-- | external/plyer/platforms/win/libs/win_api_defs.py | 200 | ||||
-rw-r--r-- | external/plyer/platforms/win/notification.py | 13 | ||||
-rw-r--r-- | external/plyer/platforms/win/tts.py | 16 | ||||
-rw-r--r-- | external/plyer/platforms/win/uniqueid.py | 21 |
11 files changed, 575 insertions, 0 deletions
diff --git a/external/plyer/platforms/win/__init__.py b/external/plyer/platforms/win/__init__.py new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/external/plyer/platforms/win/__init__.py diff --git a/external/plyer/platforms/win/battery.py b/external/plyer/platforms/win/battery.py new file mode 100644 index 0000000..04006f8 --- /dev/null +++ b/external/plyer/platforms/win/battery.py @@ -0,0 +1,21 @@ +from plyer.platforms.win.libs.batterystatus import battery_status +from plyer.facades import Battery + + +class WinBattery(Battery): + def _get_state(self): + status = {"isCharging": None, "percentage": None} + + query = battery_status() + + if (not query): + return status + + status["isCharging"] = query["BatteryFlag"] == 8 + status["percentage"] = query["BatteryLifePercent"] + + return status + + +def instance(): + return WinBattery() diff --git a/external/plyer/platforms/win/email.py b/external/plyer/platforms/win/email.py new file mode 100644 index 0000000..e33f5cf --- /dev/null +++ b/external/plyer/platforms/win/email.py @@ -0,0 +1,34 @@ +import os +try: + from urllib.parse import quote +except ImportError: + from urllib import quote +from plyer.facades import Email + + +class WindowsEmail(Email): + def _send(self, **kwargs): + recipient = kwargs.get('recipient') + subject = kwargs.get('subject') + text = kwargs.get('text') + + uri = "mailto:" + if recipient: + uri += str(recipient) + if subject: + uri += "?" if not "?" in uri else "&" + uri += "subject=" + uri += quote(str(subject)) + if text: + uri += "?" if not "?" in uri else "&" + uri += "body=" + uri += quote(str(text)) + + try: + os.startfile(uri) + except WindowsError: + print("Warning: unable to find a program able to send emails.") + + +def instance(): + return WindowsEmail() diff --git a/external/plyer/platforms/win/filechooser.py b/external/plyer/platforms/win/filechooser.py new file mode 100644 index 0000000..4d284dc --- /dev/null +++ b/external/plyer/platforms/win/filechooser.py @@ -0,0 +1,112 @@ +''' +Windows file chooser +-------------------- +''' + +from plyer.facades import FileChooser +from win32com.shell import shell, shellcon +import os +import win32gui +import win32con +import pywintypes + + +class Win32FileChooser(object): + '''A native implementation of NativeFileChooser using the + Win32 API on Windows. + + Not Implemented features (all dialogs): + * preview + * icon + + Not implemented features (in directory selection only - it's limited + by Windows itself): + * preview + * window-icon + * multiple + * show_hidden + * filters + * path + ''' + + path = None + multiple = False + filters = [] + preview = False + title = None + icon = None + show_hidden = False + + def __init__(self, **kwargs): + # Simulate Kivy's behavior + for i in kwargs: + setattr(self, i, kwargs[i]) + + def run(self): + try: + if self.mode != "dir": + args = {} + + if self.path: + args["InitialDir"] = os.path.dirname(self.path) + path = os.path.splitext(os.path.dirname(self.path)) + args["File"] = path[0] + args["DefExt"] = path[1] + args["Title"] = self.title if self.title else "Pick a file..." + args["CustomFilter"] = 'Other file types\x00*.*\x00' + args["FilterIndex"] = 1 + + filters = "" + for f in self.filters: + if type(f) == str: + filters += (f + "\x00") * 2 + else: + filters += f[0] + "\x00" + ";".join(f[1:]) + "\x00" + args["Filter"] = filters + + flags = (win32con.OFN_EXTENSIONDIFFERENT | + win32con.OFN_OVERWRITEPROMPT) + if self.multiple: + flags |= win32con.OFN_ALLOWmultiple | win32con.OFN_EXPLORER + if self.show_hidden: + flags |= win32con.OFN_FORCESHOWHIDDEN + args["Flags"] = flags + + if self.mode == "open": + self.fname, _, _ = win32gui.GetOpenFileNameW(**args) + elif self.mode == "save": + self.fname, _, _ = win32gui.GetSaveFileNameW(**args) + + if self.fname: + if self.multiple: + seq = str(self.fname).split("\x00") + dir_n, base_n = seq[0], seq[1:] + self.selection = [os.path.join(dir_n, i) + for i in base_n] + else: + self.selection = str(self.fname).split("\x00") + else: + # From http://goo.gl/UDqCqo + pidl, display_name, image_list = shell.SHBrowseForFolder( + win32gui.GetDesktopWindow(), + None, + self.title if self.title else "Pick a folder...", + 0, None, None + ) + self.selection = [str(shell.SHGetPathFromIDList(pidl))] + + return self.selection + except (RuntimeError, pywintypes.error): + return None + + +class WinFileChooser(FileChooser): + '''FileChooser implementation for Windows, using win3all. + ''' + + def _file_selection_dialog(self, **kwargs): + return Win32FileChooser(**kwargs).run() + + +def instance(): + return WinFileChooser() diff --git a/external/plyer/platforms/win/libs/__init__.py b/external/plyer/platforms/win/libs/__init__.py new file mode 100644 index 0000000..e69de29 --- /dev/null +++ b/external/plyer/platforms/win/libs/__init__.py diff --git a/external/plyer/platforms/win/libs/balloontip.py b/external/plyer/platforms/win/libs/balloontip.py new file mode 100644 index 0000000..171b25f --- /dev/null +++ b/external/plyer/platforms/win/libs/balloontip.py @@ -0,0 +1,145 @@ +# -- coding: utf-8 -- + +__all__ = ('WindowsBalloonTip', 'balloon_tip') + + +import time +import ctypes +from plyer.platforms.win.libs import win_api_defs +from plyer.compat import PY2 +from threading import RLock + + +WS_OVERLAPPED = 0x00000000 +WS_SYSMENU = 0x00080000 +WM_DESTROY = 2 +CW_USEDEFAULT = 8 + +LR_LOADFROMFILE = 16 +LR_DEFAULTSIZE = 0x0040 +IDI_APPLICATION = 32512 +IMAGE_ICON = 1 + +NOTIFYICON_VERSION_4 = 4 +NIM_ADD = 0 +NIM_MODIFY = 1 +NIM_DELETE = 2 +NIM_SETVERSION = 4 +NIF_MESSAGE = 1 +NIF_ICON = 2 +NIF_TIP = 4 +NIF_INFO = 0x10 +NIIF_USER = 4 +NIIF_LARGE_ICON = 0x20 + + +class WindowsBalloonTip(object): + + _class_atom = 0 + _wnd_class_ex = None + _hwnd = None + _hicon = None + _balloon_icon = None + _notify_data = None + _count = 0 + _lock = RLock() + + @staticmethod + def _get_unique_id(): + WindowsBalloonTip._lock.acquire() + val = WindowsBalloonTip._count + WindowsBalloonTip._count += 1 + WindowsBalloonTip._lock.release() + return val + + def __init__(self, title, message, app_name, app_icon='', timeout=10): + ''' app_icon if given is a icon file. + ''' + + wnd_class_ex = win_api_defs.get_WNDCLASSEXW() + class_name = 'PlyerTaskbar' + str(WindowsBalloonTip._get_unique_id()) + if PY2: + class_name = class_name.decode('utf8') + wnd_class_ex.lpszClassName = class_name + # keep ref to it as long as window is alive + wnd_class_ex.lpfnWndProc =\ + win_api_defs.WindowProc(win_api_defs.DefWindowProcW) + wnd_class_ex.hInstance = win_api_defs.GetModuleHandleW(None) + if wnd_class_ex.hInstance is None: + raise Exception('Could not get windows module instance.') + class_atom = win_api_defs.RegisterClassExW(wnd_class_ex) + if class_atom == 0: + raise Exception('Could not register the PlyerTaskbar class.') + self._class_atom = class_atom + self._wnd_class_ex = wnd_class_ex + + # create window + self._hwnd = win_api_defs.CreateWindowExW(0, class_atom, + '', WS_OVERLAPPED, 0, 0, CW_USEDEFAULT, + CW_USEDEFAULT, None, None, wnd_class_ex.hInstance, None) + if self._hwnd is None: + raise Exception('Could not get create window.') + win_api_defs.UpdateWindow(self._hwnd) + + # load icon + if app_icon: + icon_flags = LR_LOADFROMFILE | LR_DEFAULTSIZE + hicon = win_api_defs.LoadImageW(None, app_icon, IMAGE_ICON, 0, 0, + icon_flags) + if hicon is None: + raise Exception('Could not load icon {}'. + format(icon_path_name)) + self._balloon_icon = self._hicon = hicon + else: + self._hicon = win_api_defs.LoadIconW(None, + ctypes.cast(IDI_APPLICATION, win_api_defs.LPCWSTR)) + self.notify(title, message, app_name) + if timeout: + time.sleep(timeout) + + def __del__(self): + self.remove_notify() + if self._hicon is not None: + win_api_defs.DestroyIcon(self._hicon) + if self._wnd_class_ex is not None: + win_api_defs.UnregisterClassW(self._class_atom, + self._wnd_class_ex.hInstance) + if self._hwnd is not None: + win_api_defs.DestroyWindow(self._hwnd) + + def notify(self, title, message, app_name): + ''' Displays a balloon in the systray. Can be called multiple times + with different parameter values. + ''' + self.remove_notify() + # add icon and messages to window + hicon = self._hicon + flags = NIF_TIP | NIF_INFO + icon_flag = 0 + if hicon is not None: + flags |= NIF_ICON + # if icon is default app's one, don't display it in message + if self._balloon_icon is not None: + icon_flag = NIIF_USER | NIIF_LARGE_ICON + notify_data = win_api_defs.get_NOTIFYICONDATAW(0, self._hwnd, + id(self), flags, 0, hicon, app_name, 0, 0, message, + NOTIFYICON_VERSION_4, title, icon_flag, win_api_defs.GUID(), + self._balloon_icon) + + self._notify_data = notify_data + if not win_api_defs.Shell_NotifyIconW(NIM_ADD, notify_data): + raise Exception('Shell_NotifyIconW failed.') + if not win_api_defs.Shell_NotifyIconW(NIM_SETVERSION, + notify_data): + raise Exception('Shell_NotifyIconW failed.') + + def remove_notify(self): + '''Removes the notify balloon, if displayed. + ''' + if self._notify_data is not None: + win_api_defs.Shell_NotifyIconW(NIM_DELETE, self._notify_data) + self._notify_data = None + + +def balloon_tip(**kwargs): + WindowsBalloonTip(**kwargs) diff --git a/external/plyer/platforms/win/libs/batterystatus.py b/external/plyer/platforms/win/libs/batterystatus.py new file mode 100644 index 0000000..ddb22cc --- /dev/null +++ b/external/plyer/platforms/win/libs/batterystatus.py @@ -0,0 +1,13 @@ +__all__ = ('battery_status') + + +import ctypes +from plyer.platforms.win.libs import win_api_defs + + +def battery_status(): + status = win_api_defs.SYSTEM_POWER_STATUS() + if not win_api_defs.GetSystemPowerStatus(ctypes.pointer(status)): + raise Exception('Could not get system power status.') + + return dict((field, getattr(status, field)) for field, _ in status._fields_) diff --git a/external/plyer/platforms/win/libs/win_api_defs.py b/external/plyer/platforms/win/libs/win_api_defs.py new file mode 100644 index 0000000..7aed430 --- /dev/null +++ b/external/plyer/platforms/win/libs/win_api_defs.py @@ -0,0 +1,200 @@ +''' Defines ctypes windows api. +''' + +__all__ = ('GUID', 'get_DLLVERSIONINFO', 'MAKEDLLVERULL', + 'get_NOTIFYICONDATAW', 'CreateWindowExW', 'WindowProc', + 'DefWindowProcW', 'get_WNDCLASSEXW', 'GetModuleHandleW', + 'RegisterClassExW', 'UpdateWindow', 'LoadImageW', + 'Shell_NotifyIconW', 'DestroyIcon', 'UnregisterClassW', + 'DestroyWindow', 'LoadIconW') + +import ctypes +from ctypes import Structure, windll, sizeof, POINTER, WINFUNCTYPE +from ctypes.wintypes import (DWORD, HICON, HWND, UINT, WCHAR, WORD, BYTE, + LPCWSTR, INT, LPVOID, HINSTANCE, HMENU, LPARAM, WPARAM, + HBRUSH, HMODULE, ATOM, BOOL, HANDLE) +LRESULT = LPARAM +HRESULT = HANDLE +HCURSOR = HICON + + +class GUID(Structure): + _fields_ = [ + ('Data1', DWORD), + ('Data2', WORD), + ('Data3', WORD), + ('Data4', BYTE * 8) + ] + + +class DLLVERSIONINFO(Structure): + _fields_ = [ + ('cbSize', DWORD), + ('dwMajorVersion', DWORD), + ('dwMinorVersion', DWORD), + ('dwBuildNumber', DWORD), + ('dwPlatformID', DWORD), + ] + + +def get_DLLVERSIONINFO(*largs): + version_info = DLLVERSIONINFO(*largs) + version_info.cbSize = sizeof(DLLVERSIONINFO) + return version_info + + +def MAKEDLLVERULL(major, minor, build, sp): + return (major << 48) | (minor << 32) | (build << 16) | sp + + +NOTIFYICONDATAW_fields = [ + ("cbSize", DWORD), + ("hWnd", HWND), + ("uID", UINT), + ("uFlags", UINT), + ("uCallbackMessage", UINT), + ("hIcon", HICON), + ("szTip", WCHAR * 128), + ("dwState", DWORD), + ("dwStateMask", DWORD), + ("szInfo", WCHAR * 256), + ("uVersion", UINT), + ("szInfoTitle", WCHAR * 64), + ("dwInfoFlags", DWORD), + ("guidItem", GUID), + ("hBalloonIcon", HICON), +] + + +class NOTIFYICONDATAW(Structure): + _fields_ = NOTIFYICONDATAW_fields[:] + + +class NOTIFYICONDATAW_V3(Structure): + _fields_ = NOTIFYICONDATAW_fields[:-1] + + +class NOTIFYICONDATAW_V2(Structure): + _fields_ = NOTIFYICONDATAW_fields[:-2] + + +class NOTIFYICONDATAW_V1(Structure): + _fields_ = NOTIFYICONDATAW_fields[:6] + + +NOTIFYICONDATA_V3_SIZE = sizeof(NOTIFYICONDATAW_V3) +NOTIFYICONDATA_V2_SIZE = sizeof(NOTIFYICONDATAW_V2) +NOTIFYICONDATA_V1_SIZE = sizeof(NOTIFYICONDATAW_V1) + + +def get_NOTIFYICONDATAW(*largs): + notify_data = NOTIFYICONDATAW(*largs) + + # get shell32 version to find correct NOTIFYICONDATAW size + DllGetVersion = windll.Shell32.DllGetVersion + DllGetVersion.argtypes = [POINTER(DLLVERSIONINFO)] + DllGetVersion.restype = HRESULT + + version = get_DLLVERSIONINFO() + if DllGetVersion(version): + raise Exception('Cannot get Windows version numbers.') + v = MAKEDLLVERULL(version.dwMajorVersion, version.dwMinorVersion, + version.dwBuildNumber, version.dwPlatformID) + + # from the version info find the NOTIFYICONDATA size + if v >= MAKEDLLVERULL(6, 0, 6, 0): + notify_data.cbSize = sizeof(NOTIFYICONDATAW) + elif v >= MAKEDLLVERULL(6, 0, 0, 0): + notify_data.cbSize = NOTIFYICONDATA_V3_SIZE + elif v >= MAKEDLLVERULL(5, 0, 0, 0): + notify_data.cbSize = NOTIFYICONDATA_V2_SIZE + else: + notify_data.cbSize = NOTIFYICONDATA_V1_SIZE + return notify_data + + +CreateWindowExW = windll.User32.CreateWindowExW +CreateWindowExW.argtypes = [DWORD, ATOM, LPCWSTR, DWORD, INT, INT, INT, INT, + HWND, HMENU, HINSTANCE, LPVOID] +CreateWindowExW.restype = HWND + +GetModuleHandleW = windll.Kernel32.GetModuleHandleW +GetModuleHandleW.argtypes = [LPCWSTR] +GetModuleHandleW.restype = HMODULE + +WindowProc = WINFUNCTYPE(LRESULT, HWND, UINT, WPARAM, LPARAM) +DefWindowProcW = windll.User32.DefWindowProcW +DefWindowProcW.argtypes = [HWND, UINT, WPARAM, LPARAM] +DefWindowProcW.restype = LRESULT + + +class WNDCLASSEXW(Structure): + _fields_ = [ + ('cbSize', UINT), + ('style', UINT), + ('lpfnWndProc', WindowProc), + ('cbClsExtra', INT), + ('cbWndExtra', INT), + ('hInstance', HINSTANCE), + ('hIcon', HICON), + ('hCursor', HCURSOR), + ('hbrBackground', HBRUSH), + ('lpszMenuName', LPCWSTR), + ('lpszClassName', LPCWSTR), + ('hIconSm', HICON), + ] + + +def get_WNDCLASSEXW(*largs): + wnd_class = WNDCLASSEXW(*largs) + wnd_class.cbSize = sizeof(WNDCLASSEXW) + return wnd_class + +RegisterClassExW = windll.User32.RegisterClassExW +RegisterClassExW.argtypes = [POINTER(WNDCLASSEXW)] +RegisterClassExW.restype = ATOM + +UpdateWindow = windll.User32.UpdateWindow +UpdateWindow.argtypes = [HWND] +UpdateWindow.restype = BOOL + +LoadImageW = windll.User32.LoadImageW +LoadImageW.argtypes = [HINSTANCE, LPCWSTR, UINT, INT, INT, UINT] +LoadImageW.restype = HANDLE + +Shell_NotifyIconW = windll.Shell32.Shell_NotifyIconW +Shell_NotifyIconW.argtypes = [DWORD, POINTER(NOTIFYICONDATAW)] +Shell_NotifyIconW.restype = BOOL + +DestroyIcon = windll.User32.DestroyIcon +DestroyIcon.argtypes = [HICON] +DestroyIcon.restype = BOOL + +UnregisterClassW = windll.User32.UnregisterClassW +UnregisterClassW.argtypes = [ATOM, HINSTANCE] +UnregisterClassW.restype = BOOL + +DestroyWindow = windll.User32.DestroyWindow +DestroyWindow.argtypes = [HWND] +DestroyWindow.restype = BOOL + +LoadIconW = windll.User32.LoadIconW +LoadIconW.argtypes = [HINSTANCE, LPCWSTR] +LoadIconW.restype = HICON + + +class SYSTEM_POWER_STATUS(ctypes.Structure): + _fields_ = [ + ('ACLineStatus', BYTE), + ('BatteryFlag', BYTE), + ('BatteryLifePercent', BYTE), + ('Reserved1', BYTE), + ('BatteryLifeTime', DWORD), + ('BatteryFullLifeTime', DWORD), + ] + +SystemPowerStatusP = ctypes.POINTER(SYSTEM_POWER_STATUS) + +GetSystemPowerStatus = ctypes.windll.kernel32.GetSystemPowerStatus +GetSystemPowerStatus.argtypes = [SystemPowerStatusP] +GetSystemPowerStatus.restype = BOOL diff --git a/external/plyer/platforms/win/notification.py b/external/plyer/platforms/win/notification.py new file mode 100644 index 0000000..ea46e08 --- /dev/null +++ b/external/plyer/platforms/win/notification.py @@ -0,0 +1,13 @@ +from threading import Thread as thread + +from plyer.facades import Notification +from plyer.platforms.win.libs.balloontip import balloon_tip + + +class WindowsNotification(Notification): + def _notify(self, **kwargs): + thread(target=balloon_tip, kwargs=kwargs).start() + + +def instance(): + return WindowsNotification() diff --git a/external/plyer/platforms/win/tts.py b/external/plyer/platforms/win/tts.py new file mode 100644 index 0000000..e2539c3 --- /dev/null +++ b/external/plyer/platforms/win/tts.py @@ -0,0 +1,16 @@ +import subprocess +from plyer.facades import TTS +from plyer.utils import whereis_exe + + +class EspeakTextToSpeech(TTS): + ''' Speaks using the espeak program + ''' + def _speak(self, **kwargs): + subprocess.call(["espeak", kwargs.get('message')]) + + +def instance(): + if whereis_exe('espeak.exe'): + return EspeakTextToSpeech() + return TTS() diff --git a/external/plyer/platforms/win/uniqueid.py b/external/plyer/platforms/win/uniqueid.py new file mode 100644 index 0000000..bfcf996 --- /dev/null +++ b/external/plyer/platforms/win/uniqueid.py @@ -0,0 +1,21 @@ +try: + import _winreg as regedit +except: + try: + import winreg as regedit + except: + raise NotImplemented() + +from plyer.facades import UniqueID + + +class WinUniqueID(UniqueID): + def _get_uid(self): + hKey = regedit.OpenKey(regedit.HKEY_LOCAL_MACHINE, + r"SOFTWARE\\Microsoft\\Cryptography") + value, _ = regedit.QueryValueEx(hKey, "MachineGuid") + return value + + +def instance(): + return WinUniqueID() |