windows: protect against screenshots and screen recordings
This commit is contained in:
@@ -78,7 +78,8 @@ from electrum import constants
|
||||
from electrum.gui.common_qt.i18n import ElectrumTranslator
|
||||
from electrum.gui.messages import TERMS_OF_USE_LATEST_VERSION
|
||||
|
||||
from .util import read_QIcon, ColorScheme, custom_message_box, MessageBoxMixin, WWLabel
|
||||
from .util import (read_QIcon, ColorScheme, custom_message_box, MessageBoxMixin, WWLabel,
|
||||
set_windows_os_screenshot_protection_drm_flag)
|
||||
from .main_window import ElectrumWindow
|
||||
from .network_dialog import NetworkDialog
|
||||
from .stylesheet_patcher import patch_qt_stylesheet
|
||||
@@ -105,6 +106,20 @@ class OpenFileEventFilter(QObject):
|
||||
return False
|
||||
|
||||
|
||||
class ScreenshotProtectionEventFilter(QObject):
|
||||
def __init__(self):
|
||||
super().__init__()
|
||||
|
||||
def eventFilter(self, obj, event):
|
||||
if (
|
||||
event.type() == QtCore.QEvent.Type.Show
|
||||
and isinstance(obj, QWidget)
|
||||
and obj.isWindow()
|
||||
):
|
||||
set_windows_os_screenshot_protection_drm_flag(obj)
|
||||
return False
|
||||
|
||||
|
||||
class QElectrumApplication(QApplication):
|
||||
new_window_signal = pyqtSignal(str, object)
|
||||
quit_signal = pyqtSignal()
|
||||
@@ -136,9 +151,12 @@ class ElectrumGui(BaseElectrumGui, Logger):
|
||||
QGuiApplication.setApplicationName("Electrum")
|
||||
self.gui_thread = threading.current_thread()
|
||||
self.windows = [] # type: List[ElectrumWindow]
|
||||
self.efilter = OpenFileEventFilter(self.windows)
|
||||
self.open_file_efilter = OpenFileEventFilter(self.windows)
|
||||
self.app = QElectrumApplication(sys.argv)
|
||||
self.app.installEventFilter(self.efilter)
|
||||
self.app.installEventFilter(self.open_file_efilter)
|
||||
self.screenshot_protection_efilter = ScreenshotProtectionEventFilter()
|
||||
if sys.platform in ['win32', 'windows'] and self.config.GUI_QT_SCREENSHOT_PROTECTION:
|
||||
self.app.installEventFilter(self.screenshot_protection_efilter)
|
||||
self.app.setWindowIcon(read_QIcon("electrum.png"))
|
||||
self.translator = ElectrumTranslator()
|
||||
self.app.installTranslator(self.translator)
|
||||
@@ -247,8 +265,11 @@ class ElectrumGui(BaseElectrumGui, Logger):
|
||||
return
|
||||
self._cleaned_up = True
|
||||
self.app.new_window_signal.disconnect()
|
||||
self.app.removeEventFilter(self.efilter)
|
||||
self.efilter = None
|
||||
self.app.removeEventFilter(self.open_file_efilter)
|
||||
self.open_file_efilter = None
|
||||
# it is save to remove the filter, even if it has not been installed
|
||||
self.app.removeEventFilter(self.screenshot_protection_efilter)
|
||||
self.screenshot_protection_efilter = None
|
||||
# If there are still some open windows, try to clean them up.
|
||||
for window in list(self.windows):
|
||||
window.close()
|
||||
|
||||
@@ -24,6 +24,7 @@
|
||||
# SOFTWARE.
|
||||
|
||||
import ast
|
||||
import sys
|
||||
from typing import TYPE_CHECKING, Dict
|
||||
|
||||
from PyQt6.QtCore import Qt
|
||||
@@ -262,6 +263,20 @@ class SettingsDialog(QDialog, QtEventListener):
|
||||
self.need_restart = True
|
||||
filelogging_cb.stateChanged.connect(on_set_filelogging)
|
||||
|
||||
screenshot_protection_cb = checkbox_from_configvar(
|
||||
self.config.cv.GUI_QT_SCREENSHOT_PROTECTION
|
||||
)
|
||||
screenshot_protection_cb.setChecked(self.config.GUI_QT_SCREENSHOT_PROTECTION)
|
||||
if sys.platform not in ['windows', 'win32']:
|
||||
screenshot_protection_cb.setChecked(False)
|
||||
screenshot_protection_cb.setDisabled(True)
|
||||
screenshot_protection_cb.setToolTip(_("This option is only available on Windows"))
|
||||
|
||||
def on_set_screenshot_protection(_x):
|
||||
self.config.GUI_QT_SCREENSHOT_PROTECTION = screenshot_protection_cb.isChecked()
|
||||
self.need_restart = True
|
||||
screenshot_protection_cb.stateChanged.connect(on_set_screenshot_protection)
|
||||
|
||||
block_explorers = sorted(util.block_explorer_info().keys())
|
||||
BLOCK_EX_CUSTOM_ITEM = _("Custom URL")
|
||||
if BLOCK_EX_CUSTOM_ITEM in block_explorers: # malicious translation?
|
||||
@@ -386,6 +401,7 @@ class SettingsDialog(QDialog, QtEventListener):
|
||||
misc_widgets = []
|
||||
misc_widgets.append((updatecheck_cb, None))
|
||||
misc_widgets.append((filelogging_cb, None))
|
||||
misc_widgets.append((screenshot_protection_cb, None))
|
||||
misc_widgets.append((alias_label, self.alias_e))
|
||||
misc_widgets.append((qr_label, qr_combo))
|
||||
|
||||
|
||||
@@ -6,6 +6,7 @@ import platform
|
||||
import queue
|
||||
import os
|
||||
import webbrowser
|
||||
import ctypes
|
||||
from functools import partial, lru_cache, wraps
|
||||
from typing import (NamedTuple, Callable, Optional, TYPE_CHECKING, List, Any, Sequence, Tuple, Union)
|
||||
|
||||
@@ -1531,6 +1532,21 @@ def insert_spaces(text: str, every_chars: int) -> str:
|
||||
return ' '.join(text[i:i+every_chars] for i in range(0, len(text), every_chars))
|
||||
|
||||
|
||||
def set_windows_os_screenshot_protection_drm_flag(window: QWidget) -> None:
|
||||
"""
|
||||
sets the windows WDA_MONITOR flag on the window so windows prevents capturing
|
||||
screenshots and microsoft recall will not be able to record the window
|
||||
https://learn.microsoft.com/en-us/windows/win32/api/winuser/nf-winuser-setwindowdisplayaffinity
|
||||
"""
|
||||
if sys.platform not in ('win32', 'windows'):
|
||||
return
|
||||
try:
|
||||
window_id = int(window.winId())
|
||||
WDA_MONITOR = 0x01
|
||||
ctypes.windll.user32.SetWindowDisplayAffinity(window_id, WDA_MONITOR)
|
||||
except Exception:
|
||||
_logger.exception(f"failed to set windows screenshot protection flag")
|
||||
|
||||
class _ABCQObjectMeta(type(QObject), ABCMeta): pass
|
||||
class _ABCQWidgetMeta(type(QWidget), ABCMeta): pass
|
||||
class AbstractQObject(QObject, ABC, metaclass=_ABCQObjectMeta): pass
|
||||
|
||||
@@ -800,6 +800,14 @@ Warning: setting this to too low will result in lots of payment failures."""),
|
||||
GUI_QT_SHOW_TAB_CONTACTS = ConfigVar('show_contacts_tab', default=False, type_=bool)
|
||||
GUI_QT_SHOW_TAB_CONSOLE = ConfigVar('show_console_tab', default=False, type_=bool)
|
||||
GUI_QT_SHOW_TAB_NOTES = ConfigVar('show_notes_tab', default=False, type_=bool)
|
||||
GUI_QT_SCREENSHOT_PROTECTION = ConfigVar(
|
||||
'screenshot_protection', default=True, type_=bool,
|
||||
short_desc=lambda: _("Prevent screenshots"),
|
||||
# currently this option is Windows only, so the description can be specific to Windows
|
||||
long_desc=lambda: _(
|
||||
'Signals Windows to disallow recordings and screenshots of the application window. '
|
||||
'There is no guarantee Windows will respect this signal.'),
|
||||
)
|
||||
|
||||
GUI_QML_PREFERRED_REQUEST_TYPE = ConfigVar('preferred_request_type', default='bolt11', type_=str)
|
||||
GUI_QML_USER_KNOWS_PRESS_AND_HOLD = ConfigVar('user_knows_press_and_hold', default=False, type_=bool)
|
||||
|
||||
Reference in New Issue
Block a user