1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219
|
#!/usr/bin/env python3
"""
Syncthing-GTK - Windows related stuff.
"""
from syncthing_gtk.tools import get_config_dir
from gi.repository import GLib, Gtk, Gdk
import os, sys, logging, codecs, msvcrt, win32pipe, win32api, winreg
import win32process
from win32com.shell import shell, shellcon
log = logging.getLogger("windows.py")
SM_SHUTTINGDOWN = 0x2000
def fix_localized_system_error_messages():
"""
Python has trouble decoding messages like
'S?bor, ktor? u? existuje, sa ned? vytvori:'
as they are encoded in some crazy, Windows-specific, locale-specific,
day-in-week-specific encoding.
This simply eats exceptions caused by 'ascii' codec and replaces
non-decodable characters by question mark.
"""
def handle_error(error):
return ('?', error.end)
codecs.register_error("strict", handle_error)
def enable_localization():
"""
Updates environment variables with windows locale.
"""
loc = "en"
try:
import locale
loc = locale.getdefaultlocale()[0]
except Exception:
pass
if not 'LANGUAGE' in os.environ:
os.environ['LANGUAGE'] = loc
def is_shutting_down():
""" Returns True if Windows initiated shutdown process """
return (win32api.GetSystemMetrics(SM_SHUTTINGDOWN) != 0)
def nice_to_priority_class(nice):
""" Converts nice value to windows priority class """
if nice <= -20: # PRIORITY_HIGHEST
return win32process.HIGH_PRIORITY_CLASS,
if nice <= -10: # PRIORITY_HIGH
return win32process.ABOVE_NORMAL_PRIORITY_CLASS
if nice >= 10: # PRIORITY_LOW
return win32process.BELOW_NORMAL_PRIORITY_CLASS
if nice >= 19: # PRIORITY_LOWEST
return win32process.IDLE_PRIORITY_CLASS
# PRIORITY_NORMAL
return win32process.NORMAL_PRIORITY_CLASS
def override_menu_borders():
""" Loads custom CSS to create borders around popup menus """
style_provider = Gtk.CssProvider()
style_provider.load_from_data("""
.menu {
border-image: linear-gradient(to top,
alpha(@borders, 0.80),
alpha(@borders, 0.60) 33%,
alpha(@borders, 0.50) 66%,
alpha(@borders, 0.15)) 2 2 2 2/ 2px 2px 2px 2px;
}
.menubar .menu {
border-image: linear-gradient(to top,
alpha(@borders, 0.80),
alpha(@borders, 0.60) 33%,
alpha(@borders, 0.50) 66%,
transparent 99%) 2 2 2 2/ 2px 2px 2px 2px;
}
""")
Gtk.StyleContext.add_provider_for_screen(
Gdk.Screen.get_default(),
style_provider,
Gtk.STYLE_PROVIDER_PRIORITY_APPLICATION
)
def get_unicode_home():
return shell.SHGetFolderPath(0, shellcon.CSIDL_LOCAL_APPDATA, None, 0)
class WinPopenReader:
"""
Reads from PIPE using GLib timers or idle_add. Emulates part of
UnixInputStream, but its in no way even close to complete
emulation.
This is only way that I found so far to have pipe and hidden
console window on Windows.
"""
def __init__(self, pipe):
# Prepare stuff
self._pipe = pipe
self._waits_for_read = None
self._buffer = ""
self._buffer_size = 32
self._closed = False
self._osfhandle = msvcrt.get_osfhandle(self._pipe.fileno())
# Start reading
GLib.idle_add(self._peek)
def _peek(self):
if self._closed:
return False
# Check if there is anything to read and read if available
(read, nAvail, nMessage) = win32pipe.PeekNamedPipe(self._osfhandle, 0)
if nAvail >= self._buffer_size:
data = self._pipe.read(self._buffer_size)
self._buffer += data
# If there is read_async callback and buffer has some data,
# send them right away
if not self._waits_for_read is None and len(self._buffer) > 0:
r = WinPopenReader.Results(self._buffer)
self._buffer = ""
callback, data = self._waits_for_read
self._waits_for_read = None
callback(self, r, *data)
GLib.idle_add(self._peek)
return False
GLib.timeout_add_seconds(1, self._peek)
return False
def read_bytes_async(self, size, trash, cancel, callback, data=()):
if self._waits_for_read != None:
raise Exception("Already reading")
self._buffer_size = size
self._waits_for_read = (callback, data)
def read_bytes_finish(self, results):
return results
def close(self):
self._closed = True
class Results:
""" Also serves as response object """
def __init__(self, data):
self._data = data
def get_data(self):
return self._data
def WinConfiguration():
from syncthing_gtk.configuration import _Configuration
from syncthing_gtk.configuration import serializer
class _WinConfiguration(_Configuration):
"""
Configuration implementation for Windows - stores values
in registry
"""
#@ Overrides
def load(self):
self.values = {}
r = winreg.OpenKey(winreg.HKEY_CURRENT_USER, "Software\\SyncthingGTK")
for key in _Configuration.REQUIRED_KEYS:
tp, trash = _Configuration.REQUIRED_KEYS[key]
try:
self.values[key] = self._read(r, key, tp)
except WindowsError:
# Not found
pass
winreg.CloseKey(r)
#@ Overrides
def save(self):
r = winreg.CreateKey(winreg.HKEY_CURRENT_USER, "Software\\SyncthingGTK")
for key in _Configuration.REQUIRED_KEYS:
tp, trash = _Configuration.REQUIRED_KEYS[key]
value = self.values[key]
self._store(r, key, tp, value)
winreg.CloseKey(r)
def _store(self, r, name, tp, value):
""" Stores value in registry, handling special types """
if tp == str:
winreg.SetValueEx(r, name, 0, winreg.REG_SZ, str(value))
elif tp in (int, bool):
value = int(value)
if value > 0xFFFF:
raise ValueError("Overflow")
if value < 0:
# This basicaly prevents storing anything >0xFFFF to registry.
# Luckily, that shouldn't be needed, largest thing stored as int is 20
value = 0xFFFF + (-value)
winreg.SetValueEx(r, name, 0, winreg.REG_DWORD, int(value))
elif tp in (list, tuple):
if not value is None: # None is default value for window_position
winreg.SetValueEx(r, "%s_size" % (name,), 0, winreg.REG_DWORD, len(value))
for i in range(0, len(value)):
self._store(r, "%s_%s" % (name, i), type(value[i]), value[i])
else:
winreg.SetValueEx(r, name, 0, winreg.REG_SZ, serializer(value))
def _read(self, r, name, tp):
""" Reads value from registry, handling special types """
if tp in (list, tuple):
size, trash = winreg.QueryValueEx(r, "%s_size" % (name,))
value = []
for i in range(0, size):
value.append(self._read(r, "%s_%s" % (name, i), None))
return value
else:
value, keytype = winreg.QueryValueEx(r, name)
if type(value) == int and value > 0xFFFF:
value = - (value - 0xFFFF)
return value
return _WinConfiguration()
|