diff --git a/src/textual/app.py b/src/textual/app.py index fa16acc631..f6975933c9 100644 --- a/src/textual/app.py +++ b/src/textual/app.py @@ -4555,6 +4555,7 @@ def _suspend_signal(self) -> None: def _resume_signal(self) -> None: """Signal that the application is being resumed from a suspension.""" self.app_resume_signal.publish(self) + self.refresh(layout=True) @contextmanager def suspend(self) -> Iterator[None]: @@ -4613,7 +4614,7 @@ def action_suspend_process(self) -> None: """Suspend the process into the background. Note: - On Unix and Unix-like systems a `SIGTSTP` is sent to the + On Unix and Unix-like systems a `SIGSTOP` is sent to the application's process. Currently on Windows and when running under Textual Web this is a non-operation. """ @@ -4623,8 +4624,9 @@ def action_suspend_process(self) -> None: # First, ensure that the suspend signal gets published while # we're still in application mode. self._suspend_signal() - # With that out of the way, send the SIGTSTP signal. - os.kill(os.getpid(), signal.SIGTSTP) + self._driver.suspend_application_mode() + self._driver._must_signal_resume = True + os.kill(os.getpid(), signal.SIGSTOP) # NOTE: There is no call to publish the resume signal here, this # will be handled by the driver posting a SignalResume event # (see the event handler on App._resume_signal) above. diff --git a/src/textual/drivers/linux_driver.py b/src/textual/drivers/linux_driver.py index 66f4c4d978..73cf15a3c3 100644 --- a/src/textual/drivers/linux_driver.py +++ b/src/textual/drivers/linux_driver.py @@ -72,20 +72,18 @@ def __init__( signal.signal(signal.SIGCONT, self._sigcont_application) def _sigtstp_application(self, *_) -> None: - """Handle a SIGTSTP signal.""" - # If we're supposed to auto-restart, that means we need to shut down - # first. - if self._auto_restart: - self.suspend_application_mode() - # Flag that we'll need to signal a resume on successful startup - # again. - self._must_signal_resume = True - # Now send a SIGSTOP to our process to *actually* suspend the - # process. - os.kill(os.getpid(), signal.SIGSTOP) + """Handle a SIGTSTP signal. + + This is called when the process receives SIGTSTP from an external + source (e.g., `kill -TSTP`). We schedule action_suspend_process via + the event loop, avoiding deadlock from calling _key_thread.join() + in signal context. + """ + if self._auto_restart and self._loop is not None: + self._loop.call_soon_threadsafe(self._app.action_suspend_process) def _sigcont_application(self, *_) -> None: - """Handle a SICONT application.""" + """Handle a SIGCONT signal.""" if self._auto_restart: self.resume_application_mode()