Improved application detection, now works inside screen and tmux. Fixed incosistancies in prev/next word navigation.
This commit is contained in:
@ -57,13 +57,9 @@ class command:
|
|||||||
return # ALWAYS return in table mode to prevent regular word navigation
|
return # ALWAYS return in table mode to prevent regular word navigation
|
||||||
|
|
||||||
# Regular word navigation (only when NOT in table mode)
|
# Regular word navigation (only when NOT in table mode)
|
||||||
self.env["screen"]["oldCursorReview"] = self.env["screen"][
|
self.env["runtime"][
|
||||||
"newCursorReview"
|
"CursorManager"
|
||||||
]
|
].enter_review_mode_curr_text_cursor()
|
||||||
if self.env["screen"]["newCursorReview"] is None:
|
|
||||||
self.env["screen"]["newCursorReview"] = self.env["screen"][
|
|
||||||
"new_cursor"
|
|
||||||
].copy()
|
|
||||||
|
|
||||||
(
|
(
|
||||||
self.env["screen"]["newCursorReview"]["x"],
|
self.env["screen"]["newCursorReview"]["x"],
|
||||||
|
@ -39,6 +39,10 @@ class command:
|
|||||||
self.env["runtime"]["OutputManager"].present_text(
|
self.env["runtime"]["OutputManager"].present_text(
|
||||||
output_text, interrupt=True, flush=False
|
output_text, interrupt=True, flush=False
|
||||||
)
|
)
|
||||||
|
# Play start of line sound
|
||||||
|
self.env["runtime"]["OutputManager"].present_text(
|
||||||
|
_("start of line"), interrupt=False, sound_icon="StartOfLine"
|
||||||
|
)
|
||||||
elif table_info:
|
elif table_info:
|
||||||
# Normal column navigation - announce cell content with column info
|
# Normal column navigation - announce cell content with column info
|
||||||
output_text = f"{table_info['cell_content']} {table_info['column_header']}"
|
output_text = f"{table_info['cell_content']} {table_info['column_header']}"
|
||||||
|
@ -100,6 +100,15 @@ class ScreenManager:
|
|||||||
if self.is_curr_screen_ignored_changed():
|
if self.is_curr_screen_ignored_changed():
|
||||||
self.env["runtime"]["InputManager"].set_execute_device_grab()
|
self.env["runtime"]["InputManager"].set_execute_device_grab()
|
||||||
self.env["runtime"]["InputManager"].handle_device_grab()
|
self.env["runtime"]["InputManager"].handle_device_grab()
|
||||||
|
|
||||||
|
# Update current application detection on screen change
|
||||||
|
try:
|
||||||
|
self.env["runtime"]["ScreenDriver"].get_curr_application()
|
||||||
|
except Exception as e:
|
||||||
|
self.env["runtime"]["DebugManager"].write_debug_out(
|
||||||
|
f"Application detection failed: {str(e)}", debug.DebugLevel.ERROR
|
||||||
|
)
|
||||||
|
|
||||||
if not self.is_ignored_screen(self.env["screen"]["newTTY"]):
|
if not self.is_ignored_screen(self.env["screen"]["newTTY"]):
|
||||||
self.update(event_data, "onScreenChange")
|
self.update(event_data, "onScreenChange")
|
||||||
self.env["screen"]["lastScreenUpdate"] = time.time()
|
self.env["screen"]["lastScreenUpdate"] = time.time()
|
||||||
@ -163,6 +172,7 @@ class ScreenManager:
|
|||||||
self.env["screen"]["newTTY"] = event_data["screen"]
|
self.env["screen"]["newTTY"] = event_data["screen"]
|
||||||
self.env["screen"]["new_content_text"] = event_data["text"]
|
self.env["screen"]["new_content_text"] = event_data["text"]
|
||||||
|
|
||||||
|
|
||||||
# screen change
|
# screen change
|
||||||
if self.is_screen_change():
|
if self.is_screen_change():
|
||||||
self.env["screen"]["oldContentBytes"] = b""
|
self.env["screen"]["oldContentBytes"] = b""
|
||||||
|
@ -729,23 +729,226 @@ class driver(screenDriver):
|
|||||||
all_attrib.append(line_attrib)
|
all_attrib.append(line_attrib)
|
||||||
return str(all_text), all_attrib
|
return str(all_text), all_attrib
|
||||||
|
|
||||||
def get_curr_application(self):
|
def get_screen_process_for_tty(self, tty_num):
|
||||||
"""Detect the currently running application on the active TTY.
|
"""Find the screen process associated with specific TTY"""
|
||||||
|
try:
|
||||||
|
result = subprocess.run([
|
||||||
|
'ps', '-eo', 'pid,ppid,comm,tty,stat', '--no-headers'
|
||||||
|
], capture_output=True, text=True, timeout=2)
|
||||||
|
|
||||||
Uses 'ps' command to identify which process is currently in the
|
for line in result.stdout.strip().split('\n'):
|
||||||
foreground on the active TTY, enabling application-specific features
|
if not line.strip():
|
||||||
like bookmarks and settings.
|
continue
|
||||||
|
parts = line.split()
|
||||||
|
if len(parts) >= 4 and parts[2] == 'screen' and f'tty{tty_num}' in parts[3]:
|
||||||
|
return parts[0] # Return PID of screen process
|
||||||
|
except (subprocess.TimeoutExpired, subprocess.CalledProcessError):
|
||||||
|
pass
|
||||||
|
return None
|
||||||
|
|
||||||
|
def get_screen_session_process(self, screen_tty_pid):
|
||||||
|
"""Get the session manager process for a TTY screen process"""
|
||||||
|
try:
|
||||||
|
result = subprocess.run([
|
||||||
|
'ps', '-eo', 'pid,ppid,comm', '--no-headers'
|
||||||
|
], capture_output=True, text=True, timeout=2)
|
||||||
|
|
||||||
|
for line in result.stdout.strip().split('\n'):
|
||||||
|
if not line.strip():
|
||||||
|
continue
|
||||||
|
parts = line.split()
|
||||||
|
if len(parts) >= 3 and parts[1] == screen_tty_pid and parts[2] == 'screen':
|
||||||
|
return parts[0] # Return session manager PID
|
||||||
|
except (subprocess.TimeoutExpired, subprocess.CalledProcessError):
|
||||||
|
pass
|
||||||
|
return None
|
||||||
|
|
||||||
|
def parse_active_app_from_pstree(self, pstree_output):
|
||||||
|
"""Parse pstree output to find currently active application"""
|
||||||
|
try:
|
||||||
|
# Look for processes that indicate active applications
|
||||||
|
# Example: screen(1786)---bash(1787)---irssi(2016)
|
||||||
|
import re
|
||||||
|
|
||||||
|
# Find all application processes (non-bash, non-screen)
|
||||||
|
app_pattern = r'([a-zA-Z0-9_-]+)\((\d+)\)'
|
||||||
|
matches = re.findall(app_pattern, pstree_output)
|
||||||
|
|
||||||
|
skip_processes = {'screen', 'bash', 'sh', 'grep', 'ps'}
|
||||||
|
applications = []
|
||||||
|
|
||||||
|
for app_name, pid in matches:
|
||||||
|
if app_name.lower() not in skip_processes:
|
||||||
|
# Check if this process is in foreground state
|
||||||
|
try:
|
||||||
|
ps_result = subprocess.run([
|
||||||
|
'ps', '-p', pid, '-o', 'stat', '--no-headers'
|
||||||
|
], capture_output=True, text=True, timeout=1)
|
||||||
|
|
||||||
|
if ps_result.returncode == 0:
|
||||||
|
stat = ps_result.stdout.strip()
|
||||||
|
# Look for processes that are active (S+ state or similar)
|
||||||
|
if '+' in stat or 'l' in stat.lower():
|
||||||
|
applications.append((app_name, pid, stat))
|
||||||
|
except:
|
||||||
|
# If we can't check status, still consider it
|
||||||
|
applications.append((app_name, pid, 'unknown'))
|
||||||
|
|
||||||
|
# Return the first active application found
|
||||||
|
if applications:
|
||||||
|
return applications[0][0].upper()
|
||||||
|
|
||||||
|
except Exception as e:
|
||||||
|
self.env["runtime"]["DebugManager"].write_debug_out(
|
||||||
|
f"Error parsing pstree output: {str(e)}", debug.DebugLevel.ERROR
|
||||||
|
)
|
||||||
|
return None
|
||||||
|
|
||||||
|
def get_app_from_screen_session(self, tty_num):
|
||||||
|
"""Get current application from screen session on specific TTY"""
|
||||||
|
try:
|
||||||
|
# Step 1: Find screen process for this TTY
|
||||||
|
screen_tty_pid = self.get_screen_process_for_tty(tty_num)
|
||||||
|
if not screen_tty_pid:
|
||||||
|
return None
|
||||||
|
|
||||||
|
self.env["runtime"]["DebugManager"].write_debug_out(
|
||||||
|
f"Found screen TTY process: {screen_tty_pid} for TTY{tty_num}",
|
||||||
|
debug.DebugLevel.INFO
|
||||||
|
)
|
||||||
|
|
||||||
|
# Step 2: Find session manager process
|
||||||
|
session_pid = self.get_screen_session_process(screen_tty_pid)
|
||||||
|
if not session_pid:
|
||||||
|
return None
|
||||||
|
|
||||||
|
self.env["runtime"]["DebugManager"].write_debug_out(
|
||||||
|
f"Found screen session process: {session_pid}",
|
||||||
|
debug.DebugLevel.INFO
|
||||||
|
)
|
||||||
|
|
||||||
|
# Step 3: Get process tree and find active app
|
||||||
|
result = subprocess.run([
|
||||||
|
'pstree', '-p', session_pid
|
||||||
|
], capture_output=True, text=True, timeout=3)
|
||||||
|
|
||||||
|
if result.returncode == 0:
|
||||||
|
self.env["runtime"]["DebugManager"].write_debug_out(
|
||||||
|
f"Pstree output: {result.stdout[:200]}...",
|
||||||
|
debug.DebugLevel.INFO
|
||||||
|
)
|
||||||
|
return self.parse_active_app_from_pstree(result.stdout)
|
||||||
|
|
||||||
|
except (subprocess.TimeoutExpired, subprocess.CalledProcessError, FileNotFoundError) as e:
|
||||||
|
self.env["runtime"]["DebugManager"].write_debug_out(
|
||||||
|
f"Error getting app from screen session: {str(e)}", debug.DebugLevel.ERROR
|
||||||
|
)
|
||||||
|
return None
|
||||||
|
|
||||||
|
def get_app_from_tmux_session(self, tty_num):
|
||||||
|
"""Get current application from tmux session on specific TTY"""
|
||||||
|
try:
|
||||||
|
# Try tmux list-panes to find active application
|
||||||
|
result = subprocess.run([
|
||||||
|
'tmux', 'list-panes', '-F', '#{pane_active} #{pane_current_command} #{pane_tty}'
|
||||||
|
], capture_output=True, text=True, timeout=2)
|
||||||
|
|
||||||
|
if result.returncode == 0:
|
||||||
|
for line in result.stdout.strip().split('\n'):
|
||||||
|
if not line.strip():
|
||||||
|
continue
|
||||||
|
parts = line.split()
|
||||||
|
if len(parts) >= 3 and parts[0] == '1': # Active pane
|
||||||
|
tty_part = parts[2]
|
||||||
|
if tty_num in tty_part:
|
||||||
|
app = parts[1].upper()
|
||||||
|
if app not in ['BASH', 'SH']:
|
||||||
|
self.env["runtime"]["DebugManager"].write_debug_out(
|
||||||
|
f"Found tmux application: {app}",
|
||||||
|
debug.DebugLevel.INFO
|
||||||
|
)
|
||||||
|
return app
|
||||||
|
|
||||||
|
except (subprocess.TimeoutExpired, subprocess.CalledProcessError, FileNotFoundError):
|
||||||
|
pass
|
||||||
|
return None
|
||||||
|
|
||||||
|
def get_curr_application(self):
|
||||||
|
"""Enhanced application detection supporting screen/tmux sessions.
|
||||||
|
|
||||||
|
Multi-method approach:
|
||||||
|
1. Try screen session detection via process tree analysis
|
||||||
|
2. Try tmux session detection via tmux commands
|
||||||
|
3. Fall back to standard ps-based detection
|
||||||
|
|
||||||
Updates:
|
Updates:
|
||||||
env['screen']['new_application']: Name of current application
|
env['screen']['new_application']: Name of current application
|
||||||
|
|
||||||
Note:
|
Features:
|
||||||
Filters out common shell processes (grep, sh, ps) to find the
|
- Detects applications inside screen/tmux sessions
|
||||||
actual user application.
|
- Handles multiple screen sessions on different TTYs
|
||||||
|
- Provides detailed debug logging for troubleshooting
|
||||||
"""
|
"""
|
||||||
apps = []
|
curr_screen = self.env["screen"]["newTTY"]
|
||||||
|
detected_app = None
|
||||||
|
|
||||||
|
self.env["runtime"]["DebugManager"].write_debug_out(
|
||||||
|
f"Starting application detection for TTY{curr_screen}",
|
||||||
|
debug.DebugLevel.INFO
|
||||||
|
)
|
||||||
|
|
||||||
|
# Method 1: Try screen session detection
|
||||||
|
try:
|
||||||
|
detected_app = self.get_app_from_screen_session(curr_screen)
|
||||||
|
if detected_app:
|
||||||
|
self.env["runtime"]["DebugManager"].write_debug_out(
|
||||||
|
f"Screen session detection found: {detected_app}",
|
||||||
|
debug.DebugLevel.INFO
|
||||||
|
)
|
||||||
|
except Exception as e:
|
||||||
|
self.env["runtime"]["DebugManager"].write_debug_out(
|
||||||
|
f"Screen session detection failed: {str(e)}", debug.DebugLevel.ERROR
|
||||||
|
)
|
||||||
|
|
||||||
|
# Method 2: Try tmux session detection
|
||||||
|
if not detected_app:
|
||||||
|
try:
|
||||||
|
detected_app = self.get_app_from_tmux_session(curr_screen)
|
||||||
|
if detected_app:
|
||||||
|
self.env["runtime"]["DebugManager"].write_debug_out(
|
||||||
|
f"Tmux session detection found: {detected_app}",
|
||||||
|
debug.DebugLevel.INFO
|
||||||
|
)
|
||||||
|
except Exception as e:
|
||||||
|
self.env["runtime"]["DebugManager"].write_debug_out(
|
||||||
|
f"Tmux session detection failed: {str(e)}", debug.DebugLevel.ERROR
|
||||||
|
)
|
||||||
|
|
||||||
|
# Method 3: Fall back to standard ps-based detection
|
||||||
|
if not detected_app:
|
||||||
|
try:
|
||||||
|
detected_app = self.get_app_via_standard_ps(curr_screen)
|
||||||
|
if detected_app:
|
||||||
|
self.env["runtime"]["DebugManager"].write_debug_out(
|
||||||
|
f"Standard ps detection found: {detected_app}",
|
||||||
|
debug.DebugLevel.INFO
|
||||||
|
)
|
||||||
|
except Exception as e:
|
||||||
|
self.env["runtime"]["DebugManager"].write_debug_out(
|
||||||
|
f"Standard ps detection failed: {str(e)}", debug.DebugLevel.ERROR
|
||||||
|
)
|
||||||
|
|
||||||
|
# Update application if we found one and it's different
|
||||||
|
if detected_app and self.env["screen"]["new_application"] != detected_app:
|
||||||
|
self.env["screen"]["new_application"] = detected_app
|
||||||
|
self.env["runtime"]["DebugManager"].write_debug_out(
|
||||||
|
f"Application changed to: {detected_app}",
|
||||||
|
debug.DebugLevel.INFO
|
||||||
|
)
|
||||||
|
|
||||||
|
def get_app_via_standard_ps(self, curr_screen):
|
||||||
|
"""Original ps-based application detection as fallback"""
|
||||||
try:
|
try:
|
||||||
curr_screen = self.env["screen"]["newTTY"]
|
|
||||||
apps = (
|
apps = (
|
||||||
subprocess.Popen(
|
subprocess.Popen(
|
||||||
"ps -t tty" + curr_screen + " -o comm,tty,stat",
|
"ps -t tty" + curr_screen + " -o comm,tty,stat",
|
||||||
@ -756,34 +959,18 @@ class driver(screenDriver):
|
|||||||
.decode()[:-1]
|
.decode()[:-1]
|
||||||
.split("\n")
|
.split("\n")
|
||||||
)
|
)
|
||||||
except Exception as e:
|
|
||||||
self.env["runtime"]["DebugManager"].write_debug_out(
|
|
||||||
str(e), debug.DebugLevel.ERROR
|
|
||||||
)
|
|
||||||
return
|
|
||||||
try:
|
|
||||||
for i in apps:
|
for i in apps:
|
||||||
i = i.upper()
|
i = i.upper()
|
||||||
i = i.split()
|
i = i.split()
|
||||||
i[0] = i[0]
|
if len(i) >= 3:
|
||||||
i[1] = i[1]
|
comm, tty, stat = i[0], i[1], i[2]
|
||||||
if "+" in i[2]:
|
if "+" in stat and comm != "":
|
||||||
if i[0] != "":
|
if comm not in ["GREP", "SH", "PS", "BASH"]:
|
||||||
if (
|
if "TTY" + curr_screen in tty:
|
||||||
not "GREP" == i[0]
|
return comm
|
||||||
and not "SH" == i[0]
|
|
||||||
and not "PS" == i[0]
|
|
||||||
):
|
|
||||||
if "TTY" + curr_screen in i[1]:
|
|
||||||
if (
|
|
||||||
self.env["screen"]["new_application"]
|
|
||||||
!= i[0]
|
|
||||||
):
|
|
||||||
self.env["screen"]["new_application"] = i[
|
|
||||||
0
|
|
||||||
]
|
|
||||||
return
|
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
self.env["runtime"]["DebugManager"].write_debug_out(
|
self.env["runtime"]["DebugManager"].write_debug_out(
|
||||||
str(e), debug.DebugLevel.ERROR
|
f"Standard ps detection error: {str(e)}", debug.DebugLevel.ERROR
|
||||||
)
|
)
|
||||||
|
return None
|
||||||
|
Reference in New Issue
Block a user