Skip to content

Commit

Permalink
Fix and clean up bad platform checks
Browse files Browse the repository at this point in the history
- If the DISPLAY environment variable is present, its value is
  now checked before assuming an X11 session is present.
- Windows platform checks no longer use startswith().
  • Loading branch information
drmfinlay committed Sep 16, 2022
1 parent e8ee7a8 commit b5b12a1
Show file tree
Hide file tree
Showing 9 changed files with 126 additions and 35 deletions.
84 changes: 84 additions & 0 deletions dragonfly/_platform_checks.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
#
# This file is part of Dragonfly.
# (c) Copyright 2022 by Dane Finlay
# Licensed under the LGPL.
#
# Dragonfly is free software: you can redistribute it and/or modify it
# under the terms of the GNU Lesser General Public License as published
# by the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# Dragonfly is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public
# License along with Dragonfly. If not, see
# <http://www.gnu.org/licenses/>.
#

""" This module handles complex platform checks. """

import os


def is_x11():
"""
Check for X11 by looking at the value of the *DISPLAY* environment
variable.
If the *DISPLAY* environment variable is present, and is in one of the
two forms below, this function returns true. Otherwise, it returns
false.
Acceptable forms of the *DISPLAY* variable:
* [hostname]:displaynumber[.screennumber]
* [protocol/hostname]:displaynumber[.screennumber]
Note: Although this function parses *hostname* and *protocol/hostname*
segments, no attempt is made to validate them. The *displaynumber* and
segments, however, must be a digit string, as does the *screennumber*,
if it is present.
"""

# Parse segments and delimiters from the env. variable.
display = os.environ.get("DISPLAY", "")
segments = ["", "", ""]
delimiters = ["", ""]
index = 0; current = "";
for char in display:
if index == 0 and char == ":" or index == 1 and char == ".":
delimiters[index] = char
segments[index] = current
index += 1; current = "";
else:
current += char
segments[index] += current

# Validate each segment as necessary.
hostname, display_number, screen_number = segments
if not display_number.isdigit(): return False
if len(delimiters[1]) > 0 and len(screen_number) == 0: return False
if len(screen_number) > 0 and not screen_number.isdigit(): return False
return True


#: Whether the value of the DISPLAY env. variable indicates an X11 session.
IS_X11 = is_x11()


if __name__ == '__main__':
# Test the _is_x11() function.
for string in ["unix/localhost:0", "localhost:0", "localhost:0.0",
":0.0", ":0"]:
#print(string)
os.environ["DISPLAY"] = string
assert is_x11()
for string in ["", "localhost", "localhost:", "localhost:0.", ":",
"0.0", ":0:", ":0.", ":a", ":0.a", ":a.0", ":a.a"]:
#print(string)
os.environ["DISPLAY"] = string
assert not is_x11()
6 changes: 5 additions & 1 deletion dragonfly/accessibility/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,11 @@
from .utils import (CursorPosition, TextQuery)

# Import and set the controller class based on the current platform.
if os.environ.get("DISPLAY"):
# Note: dragonfly._platform_checks is not used here in an effort to keep the
# accessibility sub-package modular.
# Please see the module docstring of utils.py.
#
if ":" in os.environ.get("DISPLAY", ""):
# Use the AT-SPI controller on X11.
from . import atspi
os_controller_class = atspi.Controller
Expand Down
7 changes: 5 additions & 2 deletions dragonfly/actions/keyboard/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,10 +25,13 @@
import os
import sys

from dragonfly._platform_checks import IS_X11


# Import the keyboard classes for the current platform.
# Note: X11 is checked first here because it is possible to use on the other
# supported platforms.
if os.environ.get("DISPLAY"):
if IS_X11:
# Import classes for X11. This is typically used on Unix-like systems.
# The DISPLAY environment variable is normally set in an X11 session.
# If it is not, it may be set manually in ~/.profile or equivalent.
Expand All @@ -44,7 +47,7 @@
# used.
# from ._x11_libxdo import LibxdoKeyboard as Keyboard

elif sys.platform.startswith("win"):
elif sys.platform == "win32":
# Import Win32 classes.
from ._win32 import (
Win32Keyboard as Keyboard,
Expand Down
6 changes: 4 additions & 2 deletions dragonfly/actions/mouse/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@
import os
import sys

from dragonfly._platform_checks import IS_X11

# Import mouse events common to each platform.
from ._base import (EventBase, PauseEvent, MoveEvent, MoveRelativeEvent,
MoveScreenEvent, MoveWindowEvent)
Expand All @@ -33,7 +35,7 @@
# Import the mouse functions and classes for the current platform.
# Note: X11 is checked first here because it is possible to use on the other
# supported platforms.
if os.environ.get("DISPLAY"):
if IS_X11:
try:
# Prefer pynput over xdotool since it supports horizontal scrolling
# and the extra mouse buttons.
Expand All @@ -47,7 +49,7 @@
PLATFORM_BUTTON_FLAGS, PLATFORM_WHEEL_FLAGS
)

elif sys.platform.startswith("win"):
elif sys.platform == "win32":
from ._win32 import (
ButtonEvent, get_cursor_position, set_cursor_position,
PLATFORM_BUTTON_FLAGS, PLATFORM_WHEEL_FLAGS
Expand Down
17 changes: 5 additions & 12 deletions dragonfly/test/suites.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,9 @@

import pytest

from dragonfly.log import setup_log
from dragonfly import get_engine
from dragonfly import get_engine
from dragonfly.log import setup_log
from dragonfly._platform_checks import IS_X11

# Setup logging.
_log = logging.getLogger("dfly.test")
Expand All @@ -51,16 +52,8 @@
"documentation/test_recobs_doctest.txt",
]

# Include clipboard tests if on a desktop system.
desktop = (
# Windows
os.name == "nt" or
# X11
os.environ.get("DISPLAY") or
# macOS
sys.platform == "darwin"
)
if desktop:
# Include clipboard tests if on a desktop system: Windows/X11/macOS.
if os.name == "nt" or IS_X11 or sys.platform == "darwin":
common_names.insert(2, "test_clipboard")

# Include accessibility tests if dragonfly.accessibility is available.
Expand Down
21 changes: 11 additions & 10 deletions dragonfly/windows/clipboard.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,24 +25,25 @@
import os
import sys

from dragonfly._platform_checks import IS_X11
from dragonfly.windows.base_clipboard import BaseClipboard


# Import the clipboard classes and functions for the current platform.
# Note: X11 is checked first here because it is possible to use on the other
# supported platforms.
if os.environ.get("DISPLAY"):
from dragonfly.windows.x11_clipboard import (XselClipboard as
Clipboard)

elif sys.platform.startswith("win"):
from dragonfly.windows.win32_clipboard import (Win32Clipboard as
Clipboard,
win32_clipboard_ctx)
if IS_X11:
from dragonfly.windows.x11_clipboard import \
XselClipboard as Clipboard

elif sys.platform == "win32":
from dragonfly.windows.win32_clipboard import \
Win32Clipboard as Clipboard, win32_clipboard_ctx

elif sys.platform == "darwin":
from dragonfly.windows.pyperclip_clipboard import \
PyperclipClipboard as Clipboard

else:
from dragonfly.windows.base_clipboard import (BaseClipboard as
Clipboard)
from dragonfly.windows.base_clipboard import \
BaseClipboard as Clipboard
6 changes: 4 additions & 2 deletions dragonfly/windows/monitor.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,14 +25,16 @@
import os
import sys

from dragonfly._platform_checks import IS_X11


# Import the Monitor class for the current platform.
# Note: X11 is checked first here because it is possible to use on the other
# supported platforms.
if os.environ.get("DISPLAY"):
if IS_X11:
from dragonfly.windows.x11_monitor import X11Monitor as Monitor

elif sys.platform.startswith("win"):
elif sys.platform == "win32":
from dragonfly.windows.win32_monitor import Win32Monitor as Monitor

elif sys.platform == "darwin":
Expand Down
8 changes: 4 additions & 4 deletions dragonfly/windows/pyperclip_clipboard.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,10 +28,11 @@

import os

from six import integer_types
from six import integer_types
import pyperclip

from dragonfly.windows.base_clipboard import BaseClipboard
from dragonfly._platform_checks import IS_X11
from dragonfly.windows.base_clipboard import BaseClipboard


class PyperclipClipboard(BaseClipboard):
Expand Down Expand Up @@ -79,8 +80,7 @@ def copy_from_system(self, formats=None, clear=False):
# Determine the supported formats. Copied file paths may be
# available to us on X11.
supported_formats = [self.format_text, self.format_unicode]
if os.environ.get("DISPLAY"):
supported_formats.append(self.format_hdrop)
if IS_X11: supported_formats.append(self.format_hdrop)

# Determine which formats to retrieve.
caller_specified_formats = bool(formats)
Expand Down
6 changes: 4 additions & 2 deletions dragonfly/windows/window.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,14 +26,16 @@
import os
import sys

from dragonfly._platform_checks import IS_X11


# Import the Window class for the current platform.
# Note: X11 is checked first here because it is possible to use on the other
# supported platforms.
if os.environ.get("DISPLAY"):
if IS_X11:
from dragonfly.windows.x11_window import X11Window as Window

elif sys.platform.startswith("win"):
elif sys.platform == "win32":
from dragonfly.windows.win32_window import Win32Window as Window

elif sys.platform == "darwin":
Expand Down

0 comments on commit b5b12a1

Please sign in to comment.