Skip to content

Commit

Permalink
Merge a3c3537 into 4d384fa
Browse files Browse the repository at this point in the history
  • Loading branch information
LeonarddeR authored Aug 22, 2019
2 parents 4d384fa + a3c3537 commit dfe9108
Show file tree
Hide file tree
Showing 4 changed files with 145 additions and 6 deletions.
47 changes: 47 additions & 0 deletions source/globalCommands.py
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@
import core
import winVersion
from base64 import b16encode
import vision

#: Script category for text review commands.
# Translators: The name of a category of NVDA commands.
Expand All @@ -68,6 +69,9 @@
#: Script category for Braille commands.
# Translators: The name of a category of NVDA commands.
SCRCAT_BRAILLE = _("Braille")
#: Script category for Vision commands.
# Translators: The name of a category of NVDA commands.
SCRCAT_VISION = _("Vision")
#: Script category for tools commands.
# Translators: The name of a category of NVDA commands.
SCRCAT_TOOLS = pgettext('script category', 'Tools')
Expand Down Expand Up @@ -2278,6 +2282,49 @@ def script_recognizeWithUwpOcr(self, gesture):
# Translators: Describes a command.
script_recognizeWithUwpOcr.__doc__ = _("Recognizes the content of the current navigator object with Windows 10 OCR")

@script(
# Translators: Describes a command.
description=_(
"Toggles the state of the screen curtain, "
"either hiding or SHOWING the contents of the screen. "
"If pressed to enable once, the screen curtain is enabled until you restart NVDA. "
"If pressed tree times, it is enabled until you disable it"
),
category=SCRCAT_VISION
)
def script_toggleScreenCurtain(self, gesture):
message = None
try:
if not winVersion.isFullScreenMagnificationAvailable():
return
scriptCount = scriptHandler.getLastScriptRepeatCount()
screenCurtainName = "screenCurtain"
if scriptCount == 0 and screenCurtainName in vision.handler.providers:
vision.handler.terminateProvider(screenCurtainName)
# Translators: Reported when the screen curtain is disabled.
message = _("Screen curtain disabled")
elif scriptCount in (0, 2):
temporary = scriptCount == 0
if not vision.handler.initializeProvider(
screenCurtainName,
temporary=temporary,
):
# Translators: Reported when the screen curtain could not be enabled.
message = _("Could not enable screen curtain")
return
else:
if temporary:
# Translators: Reported when the screen curtain is temporarily enabled.
message = _("Screen curtain enabled until next restart")
else:
# Translators: Reported when the screen curtain is enabled.
message = _("Screen curtain enabled")
finally:
if message is None:
# Translators: Reported when the screen curtain is not available.
message = _("Screen curtain not available")
ui.message(message, speechPriority=speech.priorities.SPRI_NOW)

__gestures = {
# Basic
"kb:NVDA+n": "showGui",
Expand Down
16 changes: 10 additions & 6 deletions source/ui.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@
import gui
import speech
import braille
from typing import Optional


# From urlmon.h
URL_MK_UNIFORM = 1
Expand Down Expand Up @@ -64,21 +66,23 @@ def browseableMessage(message,title=None,isHtml=False):
)
gui.mainFrame.postPopup()

def message(text):
def message(text: str, speechPriority: Optional[int] = None):
"""Present a message to the user.
The message will be presented in both speech and braille.
@param text: The text of the message.
@type text: str
@param speechPriority: The speech priority.
One of the C{speech.priorities.SPRI_*} constants.
"""
speech.speakMessage(text)
speech.speakMessage(text, priority=speechPriority)
braille.handler.message(text)

def reviewMessage(text):
def reviewMessage(text: str, speechPriority: Optional[int] = None):
"""Present a message from review or object navigation to the user.
The message will always be presented in speech, and also in braille if it is tethered to review or when auto tethering is on.
@param text: The text of the message.
@type text: str
@param speechPriority: The speech priority.
One of the C{speech.priorities.SPRI_*} constants.
"""
speech.speakMessage(text)
speech.speakMessage(text, priority=speechPriority)
if braille.handler.shouldAutoTether or braille.handler.getTether() == braille.handler.TETHER_REVIEW:
braille.handler.message(text)
84 changes: 84 additions & 0 deletions source/visionEnhancementProviders/screenCurtain.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
# A part of NonVisual Desktop Access (NVDA)
# This file is covered by the GNU General Public License.
# See the file COPYING for more details.
# Copyright (C) 2018-2019 NV Access Limited, Babbage B.V., Leonard de Ruijter

"""Screen curtain implementation based on the windows magnification API.
This implementation only works on Windows 8 and above.
"""

import vision
import winVersion
from ctypes import Structure, windll, c_float, POINTER, WINFUNCTYPE, WinError
from ctypes.wintypes import BOOL


class MAGCOLOREFFECT(Structure):
_fields_ = (("transform", c_float * 5 * 5),)


TRANSFORM_BLACK = MAGCOLOREFFECT()
TRANSFORM_BLACK.transform[4][4] = 1.0


def _errCheck(result, func, args):
if result == 0:
raise WinError()
return args


class Magnification:
"""Singleton that wraps necessary functions from the Windows magnification API."""

_magnification = windll.Magnification

_MagInitializeFuncType = WINFUNCTYPE(BOOL)
_MagUninitializeFuncType = WINFUNCTYPE(BOOL)
_MagSetFullscreenColorEffectFuncType = WINFUNCTYPE(BOOL, POINTER(MAGCOLOREFFECT))
_MagSetFullscreenColorEffectArgTypes = ((1, "effect"),)
_MagGetFullscreenColorEffectFuncType = WINFUNCTYPE(BOOL, POINTER(MAGCOLOREFFECT))
_MagGetFullscreenColorEffectArgTypes = ((2, "effect"),)

MagInitialize = _MagInitializeFuncType(("MagInitialize", _magnification))
MagInitialize.errcheck = _errCheck
MagUninitialize = _MagUninitializeFuncType(("MagUninitialize", _magnification))
MagUninitialize.errcheck = _errCheck
try:
MagSetFullscreenColorEffect = _MagSetFullscreenColorEffectFuncType(
("MagSetFullscreenColorEffect", _magnification),
_MagSetFullscreenColorEffectArgTypes
)
MagSetFullscreenColorEffect.errcheck = _errCheck
MagGetFullscreenColorEffect = _MagGetFullscreenColorEffectFuncType(
("MagGetFullscreenColorEffect", _magnification),
_MagGetFullscreenColorEffectArgTypes
)
MagGetFullscreenColorEffect.errcheck = _errCheck
except AttributeError:
MagSetFullscreenColorEffect = None
MagGetFullscreenColorEffect = None


class VisionEnhancementProvider(vision.providerBase.VisionEnhancementProvider):
name = "screenCurtain"
# Translators: Description of a vision enhancement provider that disables output to the screen,
# making it black.
description = _("Screen Curtain")
supportedRoles = frozenset([vision.constants.Role.COLORENHANCER])

@classmethod
def canStart(cls):
return winVersion.isFullScreenMagnificationAvailable()

def __init__(self):
super(VisionEnhancementProvider, self).__init__()
Magnification.MagInitialize()
Magnification.MagSetFullscreenColorEffect(TRANSFORM_BLACK)

def terminate(self):
super(VisionEnhancementProvider, self).terminate()
Magnification.MagUninitialize()

def registerEventExtensionPoints(self, extensionPoints):
# The screen curtain isn't interested in any events
pass
4 changes: 4 additions & 0 deletions source/winVersion.py
Original file line number Diff line number Diff line change
Expand Up @@ -56,3 +56,7 @@ def isWin10(version=1507, atLeast=True):
except KeyError:
log.error("Unknown Windows 10 version {}".format(version))
return False


def isFullScreenMagnificationAvailable():
return (winVersion.major, winVersion.minor) >= (6, 2)

0 comments on commit dfe9108

Please sign in to comment.