Check screen is black after activating screen curtain#12701
Check screen is black after activating screen curtain#12701
Conversation
|
|
||
| def isScreenFullyBlack() -> bool: | ||
| """ | ||
| Uses wx to check that the screen is currently fully black by taking a screen capture and checking: |
There was a problem hiding this comment.
have you considered using the screenBitmap module for this?
There was a problem hiding this comment.
I've moved the implementation to use that instead, thanks for highlighting this as an option.
There was a problem hiding this comment.
Having to iterate over this bitmap in python was considerably slower (took 4s on my machine). Using the wx method to calculate the histogram in native code takes 0.4s comparably.
b7692c5 to
5f756a4
Compare
|
Using complete black as the screen curtain color makes it hard to differentiate from a failed buffer grab (initialized to zero). Perhaps, this would be safer to transform the screen to a slightly off-black, and test for that color. |
Should this be done in another PR? I imagine this may be contentious with problems like screen burn in and OLED power saving. |
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
This comment has been minimized.
@feerrenrut |
| bmp: wx.Bitmap = wx.Bitmap(screenSize[0], screenSize[1]) | ||
| mem = wx.MemoryDC(bmp) | ||
| mem.Blit(0, 0, screenSize[0], screenSize[1], screen, 0, 0) # copy screen over to bmp | ||
| del mem # Release bitmap | ||
| img: wx.Image = bmp.ConvertToImage() |
There was a problem hiding this comment.
@feerrenrut - at what stage is a buffer grab fail a concern?
Would a check like the following be an improvement?
Where setCanaryBits sets the bmp to some non-zero value canaryBits.
And areCanaryBitsSet checks if the bits are still set in the memory.
| bmp: wx.Bitmap = wx.Bitmap(screenSize[0], screenSize[1]) | |
| mem = wx.MemoryDC(bmp) | |
| mem.Blit(0, 0, screenSize[0], screenSize[1], screen, 0, 0) # copy screen over to bmp | |
| del mem # Release bitmap | |
| img: wx.Image = bmp.ConvertToImage() | |
| bmp: wx.Bitmap = wx.Bitmap(screenSize[0], screenSize[1]) | |
| setCanaryBits(bmp, canaryBits) | |
| mem = wx.MemoryDC(bmp) | |
| assert areCanaryBitsSet(mem, canaryBits) | |
| mem.Blit(0, 0, screenSize[0], screenSize[1], screen, 0, 0) # copy screen over to bmp | |
| assert not areCanaryBitsSet(mem, canaryBits) | |
| del mem # Release bitmap | |
| img: wx.Image = bmp.ConvertToImage() |
There was a problem hiding this comment.
Note: This doesn't handle if screen is initialised wrong - which is handled by wx and we have little control over.
That's one failure we are aware of, but there are quite likely other ways that this could fail that we aren't aware of. The feature should let users rely on it, so they don't need sighted assistance to confirm that screen curtain is working.
I don't think we could reliably guarantee this, even if we tested on all Windows versions (and updates) there may be other ways this could fail. Perhaps bad video drivers, or antivirus software. Because this is privacy related, the feature should err on the side of caution. |
|
I think it should take multiple approaches:
To maintain performance it might be required to do this from C++ and asynchronously. |
|
Closing in favour of #17894 17894 |
Fixes #12708 Supercedes #12701 Summary of the issue: There is a security risk if the screen curtain fails silently. The Magnification API used by Screen Curtain does not officially support WOW64 applications like NVDA. As this is a risk with untested Windows versions, an external check would be helpful to minimise silent failures of the screen curtain. Notwithstanding this, as this is a security feature, it is better to check that it is working as expected, and inform the user if it is not. Description of user facing changes None. Description of development approach Implemented a new function , isScreenFullyBlack, in NVDAHelper/local/screenCurtain.cpp. This method: Obtains a reference to the desktop window, which covers the entire virtual screen, and thus all monitors connected to the computer. USES GDI to capture the screen. USES GDIPlus to calculate a histogram of this screen capture. While this seems like overkill, it performs significantly better on my machine than individually checking that each pixel is black, likely due to optimisations and hardware accelleration in GDIPlus. Checks that, for each channel of the histogram, the value at 0 is the area of the screen. Call this function after applying the fullscreen colour effect and hiding the cursor in visionEnhancementProviders.screenCurtain.ScreenCurtainProvider.__init__. If the return is False, raise RuntimeError, which disables the screen curtain and informs the user that screen curtain failed to activate.
Fixes nvaccess#12708 Supercedes nvaccess#12701 Summary of the issue: There is a security risk if the screen curtain fails silently. The Magnification API used by Screen Curtain does not officially support WOW64 applications like NVDA. As this is a risk with untested Windows versions, an external check would be helpful to minimise silent failures of the screen curtain. Notwithstanding this, as this is a security feature, it is better to check that it is working as expected, and inform the user if it is not. Description of user facing changes None. Description of development approach Implemented a new function , isScreenFullyBlack, in NVDAHelper/local/screenCurtain.cpp. This method: Obtains a reference to the desktop window, which covers the entire virtual screen, and thus all monitors connected to the computer. USES GDI to capture the screen. USES GDIPlus to calculate a histogram of this screen capture. While this seems like overkill, it performs significantly better on my machine than individually checking that each pixel is black, likely due to optimisations and hardware accelleration in GDIPlus. Checks that, for each channel of the histogram, the value at 0 is the area of the screen. Call this function after applying the fullscreen colour effect and hiding the cursor in visionEnhancementProviders.screenCurtain.ScreenCurtainProvider.__init__. If the return is False, raise RuntimeError, which disables the screen curtain and informs the user that screen curtain failed to activate.
Link to issue number:
Fixes #12708
Summary of the issue:
There is a security risk if the screen curtain fails silently. The Magnification API used by Screen Curtain does not officially support WOW64 applications like NVDA. As this is a risk with untested Windows versions, an external check would be helpful to minimise silent failures of the screen curtain.
Description of how this pull request fixes the issue:
Capture the screen and check it is fully black after initialising screen curtain.
wxWidgets uses winGDI methods, winGDI is a low level, stable API, as compared to the Magnification API:
wxScreenDC captures all monitors.
Testing strategy:
Manual confirmation from a sighted developer is required.
Example for Win 10:
Example for Win 11 (preview):
Known issues with pull request:
None
Change log entries:
None
Code Review Checklist: