Skip to content

tapmap.autostart.windows_autostart

tapmap.autostart.windows_autostart

Manage TapMap autostart on Windows.

PREFERRED_ARGUMENTS = '--no-browser' module-attribute

logger = logging.getLogger(__name__) module-attribute

_UNREADABLE_STATUS = NativeAutostartStatus(queryable=False, present=False, recognized=False, enabled=False, matches_preferred_definition=False) module-attribute

AutostartDecision

Bases: NamedTuple

State to display and action to perform when clicked.

Source code in src/tapmap/state/autostart.py
56
57
58
59
class AutostartDecision(NamedTuple):
    """State to display and action to perform when clicked."""
    display_state: DisplayState
    click_action: ClickAction

ElevationStatus

Bases: Enum

Whether TapMap is running with administrator privileges.

Source code in src/tapmap/state/autostart.py
28
29
30
31
32
class ElevationStatus(Enum):
    """Whether TapMap is running with administrator privileges."""
    NOT_ELEVATED = "not_elevated"
    ELEVATED = "elevated"
    UNKNOWN = "unknown"

NativeAutostartStatus dataclass

Current state of the operating system's autostart entry.

Other fields are ignored when the state cannot be queried.

Source code in src/tapmap/state/autostart.py
42
43
44
45
46
47
48
49
50
51
52
53
@dataclass(frozen=True)
class NativeAutostartStatus:
    """Current state of the operating system's autostart entry.

    Other fields are ignored when the state cannot be queried.
    """

    queryable: bool
    present: bool
    recognized: bool
    enabled: bool
    matches_preferred_definition: bool

WriteOutcome

Bases: Enum

Result of an autostart write attempt.

Source code in src/tapmap/state/autostart.py
35
36
37
38
39
class WriteOutcome(Enum):
    """Result of an autostart write attempt."""
    OK = "ok"
    CONFLICT = "conflict"
    ERROR = "error"

TaskInfo dataclass

Information used to identify a Task Scheduler task.

Source code in src/tapmap/autostart/windows_identity.py
16
17
18
19
20
21
22
23
24
25
26
27
28
@dataclass(frozen=True)
class TaskInfo:
    """Information used to identify a Task Scheduler task."""
    enabled: bool
    logon_type: int
    run_level: int
    trigger_count: int
    trigger_is_logon: bool
    trigger_user_id: str | None
    principal_user_id: str | None
    action_count: int
    action_path: str | None
    action_arguments: str | None

decide_autostart_display(*, status, elevation, is_source_run)

Return the autostart display state and click action.

Source code in src/tapmap/state/autostart.py
 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
def decide_autostart_display(
    *,
    status: NativeAutostartStatus,
    elevation: ElevationStatus,
    is_source_run: bool,
) -> AutostartDecision:
    """Return the autostart display state and click action."""
    if is_source_run:
        return AutostartDecision(DisplayState.OFF, ClickAction.NONE)

    if elevation == ElevationStatus.UNKNOWN:
        return AutostartDecision(DisplayState.UNAVAILABLE, ClickAction.NONE)

    if not status.queryable:
        return AutostartDecision(DisplayState.UNAVAILABLE, ClickAction.NONE)

    is_on = (
        status.present
        and status.recognized
        and status.enabled
        and status.matches_preferred_definition
    )

    if elevation == ElevationStatus.ELEVATED:
        # Administrator mode may read autostart state but must not change it.
        return AutostartDecision(
            DisplayState.ON if is_on else DisplayState.OFF, ClickAction.NONE
        )

    if is_on:
        return AutostartDecision(DisplayState.ON, ClickAction.DISABLE)

    if not status.present:
        return AutostartDecision(DisplayState.OFF, ClickAction.CREATE)

    if not status.recognized:
        return AutostartDecision(DisplayState.OFF, ClickAction.NONE)

    if status.matches_preferred_definition:
        return AutostartDecision(DisplayState.OFF, ClickAction.ENABLE)

    return AutostartDecision(DisplayState.OFF, ClickAction.REPAIR_AND_ENABLE)

is_recognized_as_ours(task, *, current_username, exe_path)

Return whether a task can be identified as TapMap's task.

Source code in src/tapmap/autostart/windows_identity.py
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
def is_recognized_as_ours(task: TaskInfo, *, current_username: str, exe_path: str) -> bool:
    """Return whether a task can be identified as TapMap's task."""
    if task.logon_type != LOGON_TYPE_INTERACTIVE_TOKEN:
        return False
    if task.run_level != RUN_LEVEL_LUA:
        return False
    if task.trigger_count != 1 or not task.trigger_is_logon:
        return False
    # The old installer did not set Trigger.UserId. If present, it must match.
    if (
        task.trigger_user_id is not None
        and normalize_user_id(task.trigger_user_id) != normalize_user_id(current_username)
    ):
        return False
    if normalize_user_id(task.principal_user_id) != normalize_user_id(current_username):
        return False
    if task.action_count != 1 or task.action_path is None:
        return False
    return normalize_path(task.action_path) == normalize_path(exe_path)

matches_preferred_definition(task)

Return whether the task uses the preferred arguments.

Source code in src/tapmap/autostart/windows_identity.py
67
68
69
70
def matches_preferred_definition(task: TaskInfo) -> bool:
    """Return whether the task uses the preferred arguments."""
    args = (task.action_arguments or "").strip()
    return args == PREFERRED_ARGUMENTS

is_elevated()

Return whether TapMap is running with administrator privileges.

Source code in src/tapmap/autostart/windows_autostart.py
38
39
40
41
42
43
44
45
46
47
def is_elevated() -> ElevationStatus:
    """Return whether TapMap is running with administrator privileges."""
    try:
        return (
            ElevationStatus.ELEVATED
            if ctypes.windll.shell32.IsUserAnAdmin()
            else ElevationStatus.NOT_ELEVATED
        )
    except (OSError, AttributeError):
        return ElevationStatus.UNKNOWN

_current_username()

Return the current Windows username.

Source code in src/tapmap/autostart/windows_autostart.py
50
51
52
def _current_username() -> str:
    """Return the current Windows username."""
    return getpass.getuser()

_classify(task, *, exe_path)

Convert a Task Scheduler task to TapMap's autostart state.

Source code in src/tapmap/autostart/windows_autostart.py
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
def _classify(task: TaskInfo | None, *, exe_path: str) -> NativeAutostartStatus:
    """Convert a Task Scheduler task to TapMap's autostart state."""
    if task is None:
        return NativeAutostartStatus(
            queryable=True,
            present=False,
            recognized=False,
            enabled=False,
            matches_preferred_definition=False,
        )

    recognized = is_recognized_as_ours(
        task, current_username=_current_username(), exe_path=exe_path
    )
    if not recognized:
        return NativeAutostartStatus(
            queryable=True,
            present=True,
            recognized=False,
            enabled=task.enabled,
            matches_preferred_definition=False,
        )

    return NativeAutostartStatus(
        queryable=True,
        present=True,
        recognized=True,
        enabled=task.enabled,
        matches_preferred_definition=matches_preferred_definition(task),
    )

query_display_state(*, exe_path, is_frozen)

Return the state and action for the autostart control.

Source code in src/tapmap/autostart/windows_autostart.py
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
def query_display_state(*, exe_path: str, is_frozen: bool) -> AutostartDecision:
    """Return the state and action for the autostart control."""
    if not is_frozen:
        # Source runs must not access the installed autostart task.
        return decide_autostart_display(
            status=_UNREADABLE_STATUS,
            elevation=ElevationStatus.NOT_ELEVATED,
            is_source_run=True,
        )

    try:
        task = scheduler.find_task()
        status = _classify(task, exe_path=exe_path)
    except scheduler.TaskQueryError:
        status = _UNREADABLE_STATUS

    return decide_autostart_display(status=status, elevation=is_elevated(), is_source_run=False)

enable(*, exe_path)

Enable the TapMap autostart task.

Source code in src/tapmap/autostart/windows_autostart.py
106
107
108
109
110
111
112
113
114
115
116
def enable(*, exe_path: str) -> tuple[WriteOutcome, str | None]:
    """Enable the TapMap autostart task."""
    try:
        scheduler.set_task_enabled_if_recognized(
            True, current_username=_current_username(), exe_path=exe_path
        )
        return WriteOutcome.OK, None
    except scheduler.TaskOwnershipConflict:
        return WriteOutcome.CONFLICT, None
    except scheduler.TaskQueryError as exc:
        return WriteOutcome.ERROR, str(exc)

disable(*, exe_path)

Disable the TapMap autostart task.

Source code in src/tapmap/autostart/windows_autostart.py
119
120
121
122
123
124
125
126
127
128
129
def disable(*, exe_path: str) -> tuple[WriteOutcome, str | None]:
    """Disable the TapMap autostart task."""
    try:
        scheduler.set_task_enabled_if_recognized(
            False, current_username=_current_username(), exe_path=exe_path
        )
        return WriteOutcome.OK, None
    except scheduler.TaskOwnershipConflict:
        return WriteOutcome.CONFLICT, None
    except scheduler.TaskQueryError as exc:
        return WriteOutcome.ERROR, str(exc)

create(*, exe_path)

Create the TapMap autostart task.

Source code in src/tapmap/autostart/windows_autostart.py
132
133
134
135
136
137
138
139
140
141
142
143
144
def create(*, exe_path: str) -> tuple[WriteOutcome, str | None]:
    """Create the TapMap autostart task."""
    try:
        scheduler.create_or_update_task_if_owned_or_absent(
            exe_path=exe_path,
            arguments=PREFERRED_ARGUMENTS,
            username=_current_username(),
        )
        return WriteOutcome.OK, None
    except scheduler.TaskOwnershipConflict:
        return WriteOutcome.CONFLICT, None
    except scheduler.TaskQueryError as exc:
        return WriteOutcome.ERROR, str(exc)

repair_and_enable(*, exe_path)

Repair and enable the TapMap autostart task.

Source code in src/tapmap/autostart/windows_autostart.py
147
148
149
def repair_and_enable(*, exe_path: str) -> tuple[WriteOutcome, str | None]:
    """Repair and enable the TapMap autostart task."""
    return create(exe_path=exe_path)

run_startup_setup(*, app_data_dir, exe_path, is_frozen)

Set up autostart on first launch when needed.

Failures are logged and must not prevent TapMap from starting.

Source code in src/tapmap/autostart/windows_autostart.py
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
def run_startup_setup(*, app_data_dir: Path, exe_path: str, is_frozen: bool) -> None:
    """Set up autostart on first launch when needed.

    Failures are logged and must not prevent TapMap from starting.
    """
    if not is_frozen:
        return
    if marker.has_completed_setup(app_data_dir):
        return

    try:
        task = scheduler.find_task()
    except scheduler.TaskQueryError:
        logger.warning("Unable to query Task Scheduler for initial autostart setup.")
        return

    if task is not None:
        # Task already exists; don't touch it.
        marker.mark_setup_completed(app_data_dir)
        return

    if is_elevated() != ElevationStatus.NOT_ELEVATED:
        # Only a normal user process may create the task.
        return

    try:
        scheduler.create_or_update_task_if_owned_or_absent(
            exe_path=exe_path,
            arguments=PREFERRED_ARGUMENTS,
            username=_current_username(),
        )
    except scheduler.TaskOwnershipConflict:
        # A task appeared since the check above; treat as already present.
        marker.mark_setup_completed(app_data_dir)
        return
    except scheduler.TaskQueryError:
        logger.warning("Unable to create the initial TapMap autostart task.")
        return

    if not _verify_created_task(exe_path=exe_path):
        logger.warning(
            "Unable to verify the newly created TapMap autostart task; leaving "
            "initial setup incomplete so a later launch retries."
        )
        return

    marker.mark_setup_completed(app_data_dir)

_verify_created_task(*, exe_path)

Verify that the newly created task has the expected configuration.

Source code in src/tapmap/autostart/windows_autostart.py
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
def _verify_created_task(*, exe_path: str) -> bool:
    """Verify that the newly created task has the expected configuration."""
    try:
        task = scheduler.find_task()
    except scheduler.TaskQueryError:
        return False

    if task is None:
        return False

    if not is_recognized_as_ours(task, current_username=_current_username(), exe_path=exe_path):
        return False

    if not task.enabled:
        return False

    return matches_preferred_definition(task)