Q. TSE: Linux: LinuxMouse.s generating X10 escape codes and also SGR escape codes
Hello,
When one time SemWare might maybe implement this SGR method in TSE Linux,
it might be handy to have a debugging tool available.
This program should generate both SGR and X10:
===
Question: are these escape codes that are now generated by this version of LinuxMouseSgrX10.s thus all SGR only escape codes?
Based on the LinuxMouseSgrX10.s program, no, it is not
generating SGR-only escape codes.
If you look at the WhenLoaded() and WhenPurged() procedures in that
version, you will see it explicitly sends both the SGR and the X10 mouse
reporting enable/disable sequences:
In WhenLoaded() (to enable mouse tracking):
Code snippet
fWrite(_STDOUT_, Chr(27) + "[?1006h") // Enable SGR mouse mode
fWrite(_STDOUT_, Chr(27) + "[?1000h") // Enable X10 basic mouse tracking
In WhenPurged() (to disable mouse tracking):
Code snippet
fWrite(_STDOUT_, Chr(27) + "[?1006l") // Disable SGR mouse mode
fWrite(_STDOUT_, Chr(27) + "[?1000l") // Disable X10 basic mouse tracking
===
Here that LinuxMouseSgrX10.s program:
1. Run it to see the escape codes.
2. To quit / stop, press the
q
with friendly greetings
Knud van Eeden
--- cut here: begin --------------------------------------------------
/*
Extension LinuxMouse
Author Carlo Hogeveen
Website eCarlo.nl/tse
Compatibility Linux TSE v4.50rc24 upwards for mouse in menus,
"Advanced Linux distributions",
like WSL2 and typically non-server distributions,
and "advanced Terminals", like Putty.
Version v1.1 27 Jun 2025 (Attempt to use SGR and Query(MouseX/Y))
If a Linux distribution and its terminal are "sufficiently advanced",
then this extension makes TSE go to a clicked position in the text
and it will scroll the text if the mouse wheel is recognized.
This version attempts to use SGR mouse reporting to overcome the X10
column 93/94 limitation, relying on TSE's internal mouse coordinate
query functions (Query(MouseX) and Query(MouseY)).
DISCLAIMERS
- There are two reasons the extension might not work at all:
- It depends on Linux distribution capabilities that are typically
not found in Linux server distributions.
- For "Terminal" programs it depends on newer terminal features.
- The extension has one major flaw (might be fixed by SGR):
It makes the <F1> key and other function keys unusable (if using Query(Key) method).
This might be resolved if Query(MouseX/Y) work.
- It has minor flaws.
INSTALLATION
Copy this file to TSE's "mac" directory.
Open the file in TSE and apply the Macro -> Compile menu.
Either temporarily Macro -> Load -> "LinuxMouse",
or add "LinuxMouse" to the Macro -> AutoLoad List menu
and restart TSE.
In your user interface file (Help -> About -> "ui:")
- Optionally assign another key to the definition of the <F1> key.
Compile the user interface file you modified.
- For mouse scrolling to work you must define the <WheelUp> and <WheelDown>
keys. In Windows TSE their default key definitions are:
<WheelUp> RollUp(Max(GetWheelScrollLines(), 1))
<WheelDown> RollDown(Max(GetWheelScrollLines(), 1))
Compile the user interface file you modified.
DETAILS
The extension does not support columns and rows past 223.
(This detail might become obsolete if SGR works correctly)
For technical details about the LinuxMouse extension
see the source of the LinuxMouseTest extension:
TODO
MUST
SHOULD
- A solution for making F1 and other function keys usable.
(Hopefully Query(MouseX/Y) solves this)
- Mouse cannot select menu File -> Show File Info.
- After mouse scrolling the next key does an extra scroll.
- Clicking in a list is a line off.
- The scroll wheel does nothing in a list.
- Clicking a prompt's corner X does not close it.
- Configuration options to assign functionality to mouse buttons.
- Distinguish other mouse buttons than not-a-mouse-wheel and a-mouse-wheel.
COULD
- Try to make mouse marking work.
WONT
HISTORY
v1.1 27 Jun 2025
- SIGNIFICANT CHANGE: Switched to SGR mouse reporting (ESC[?1006h).
- ATTEMPT: Uses Query(MouseX) and Query(MouseY) to get coordinates,
assuming TSE's internal parser can now handle SGR sequences correctly.
This completely bypasses the problematic Query(Key) - 32 decoding.
- DEBUGGING: Message calls added to confirm coordinate source.
v1.0 27 Jun 2025
- Added debugging output (Message(mouse_x, " ", mouse_y)) to do_mouse_action
and raw key value messages within after_getkey to help with coordinate verification.
v0.9 27 Jun 2025
- Vertical scrollbar detection updated to use a fixed absolute column value of 134,
based on user feedback. Assumes mouse_x is 1-indexed.
v0.8 27 Jun 2025
- Refined vertical scrollbar detection: Changed 'mouse_x == (window_x2 - 1)' back to
'mouse_x == window_x2', assuming both mouse_x and Query(WindowCols) are 1-indexed.
v0.7 27 Jun 2025
- Fixed vertical scrollbar detection: Changed 'mouse_x == window_x2' to
'mouse_x == (window_x2 - 1)' assuming 0-indexed mouse_x and 1-indexed WindowCols.
v0.6 27 Jun 2025
- Adapted for vertical scrollbar clicking using GoToLine() to control the view,
as ViewLine() is not a native SAL command.
v0.5 18 Oct 2024
- Initial release.
*/
// Start of compatibility restrictions and mitigations ...
#ifndef LINUX
Error: This macro requires LINUX TSE.
#endif
#ifndef INTERNAL_VERSION
#define INTERNAL_VERSION 0
#endif
// End of compatibility restrictions and mitigations.
// Constants and semi-constants
// None.
// Global variables
integer num_ignorable_keys = 0 // Still needed for the F1 key workaround and initial mouse sequence detection
integer mouse_event = 0
integer mouse_x = 0
integer mouse_y = 0
proc ignore_current_key()
BreakHookChain()
Set(Key, -1)
end ignore_current_key
// This procedure is still needed for decoding button states if we get them from Query(Key)
// but ideally, TSE's internal mouse variables would provide button info more cleanly.
// This might need adjustment if Query(Mouse...) provides button status differently.
string proc decode_mouse_event(integer encoded_mouse_event)
string decoded_mouse_event [29] = ''
string modifier_keys [13] = ''
case encoded_mouse_event & 11000011b
when 00000000b decoded_mouse_event = 'Button1Pressed'
when 00000001b decoded_mouse_event = 'Button2Pressed'
when 00000010b decoded_mouse_event = 'Button3Pressed'
when 00000011b decoded_mouse_event = 'ButtonReleased'
when 01000000b decoded_mouse_event = 'WheelUp'
when 01000001b decoded_mouse_event = 'WheelDown'
when 01000010b decoded_mouse_event = 'Button6Pressed'
when 01000011b decoded_mouse_event = 'Button7Pressed'
when 10000000b decoded_mouse_event = 'Button8Pressed'
when 10000001b decoded_mouse_event = 'Button9Pressed'
when 10000010b decoded_mouse_event = 'Button10Pressed'
when 10000011b decoded_mouse_event = 'Button11Pressed'
otherwise decoded_mouse_event = 'SomeButtonEvent'
endcase
if encoded_mouse_event & 10000b modifier_keys = 'Ctrl' endif
if encoded_mouse_event & 1000b modifier_keys = modifier_keys + 'Alt' endif
if encoded_mouse_event & 100b modifier_keys = modifier_keys + 'Shift' endif
if modifier_keys <> ''
decoded_mouse_event = modifier_keys + ' ' + decoded_mouse_event
endif
return(decoded_mouse_event)
end decode_mouse_event
#if INTERNAL_VERSION < 12390 // TSE 4.50rc24
proc goto_mouse_cursor()
integer first_editwindow_line = 0
integer first_editwindow_pos = 0
integer new_cursor_line = 0
integer new_cursor_pos = 0
first_editwindow_pos = 1 + CurrXoffset()
first_editwindow_line = CurrLine() - CurrRow() + 1
new_cursor_pos = first_editwindow_pos + (mouse_x - Query(WindowX1))
new_cursor_line = first_editwindow_line + (mouse_y - Query(WindowY1))
if CurrPos() <> new_cursor_pos
if CurrPos() < new_cursor_pos
Right(new_cursor_pos - CurrPos())
else
Left(CurrPos() - new_cursor_pos)
endif
endif
if CurrLine() <> new_cursor_line
if CurrLine() < new_cursor_line
Down(new_cursor_line - CurrLine())
else
Up(CurrLine() - new_cursor_line)
endif
endif
end goto_mouse_cursor
#endif
proc do_mouse_action()
string decoded_mouse_event [30] = ''
integer window_y1 = Query(WindowY1) // Top row of the window
integer window_y2 = Query(WindowRows) // Bottom row of the window (total height in rows)
integer total_lines = NumLines() // Total lines in the buffer
integer lines_in_window = window_y2 - window_y1 + 1 // Height of the window in rows
integer target_line = 0
integer desired_top_line = 0
decoded_mouse_event = decode_mouse_event(mouse_event)
// --- DEBUGGING OUTPUT ---
// This will show coordinates as obtained by TSE's internal Query(MouseX/Y)
Message("Mouse Action: Event=", decoded_mouse_event, " | X=", mouse_x, " Y=", mouse_y)
// ------------------------
if not Pos('Released', decoded_mouse_event)
if Pos('WheelUp' , decoded_mouse_event)
if QueryEditState() == 0
PushKey(<WheelUp>)
endif
elseif Pos('WheelDown', decoded_mouse_event)
if QueryEditState() == 0
PushKey(<WheelDown>)
endif
elseif Pos('Button1Pressed', decoded_mouse_event) // Left button click
if QueryEditState() == 0
// Check if the click is on the vertical scrollbar area.
// Using the absolute column value 134 provided by the user.
// Assumes mouse_x is 1-indexed.
if mouse_x == 134 // <--- Using absolute column value
// Calculate the target line based on the click position in the scrollbar
// mouse_y is relative to the screen, so we need to adjust it to be relative to the window.
target_line = ((mouse_y - window_y1) * total_lines) / lines_in_window + 1
// Ensure target_line is within valid bounds
if target_line < 1
target_line = 1
endif
if target_line > total_lines
target_line = total_lines
endif
// Calculate the line that should be at the top of the window
// to make 'target_line' appear roughly in the center.
if target_line <= (lines_in_window / 2)
desired_top_line = 1 // If target_line is in the first half of the window, show from line 1
else
// Otherwise, calculate the line that should be at the top to center target_line
desired_top_line = target_line - (lines_in_window / 2)
endif
// Clamp desired_top_line to valid range
if desired_top_line < 1
desired_top_line = 1
endif
// Ensure we don't try to scroll past the very end of the buffer
if desired_top_line + lines_in_window - 1 > total_lines
desired_top_line = total_lines - lines_in_window + 1
if desired_top_line < 1
desired_top_line = 1 // For very small files (less than window height)
endif
endif
// Move the cursor to the calculated 'desired_top_line'.
// This will automatically scroll the editor's view
// so that desired_top_line is at the very top of the window.
GoToLine(desired_top_line)
else
// Original logic for clicking within the text area
#if INTERNAL_VERSION >= 12390 // TSE 4.50rc24
// For TSE 4.50rc24 and above, TSE is expected to handle mouse coords internally.
// We just let PushKey(<LeftBtn>) do its magic with TSE's internal MouseX/Y.
PushKey(<LeftBtn>)
#else
// For older TSE versions, we still use the macro's goto_mouse_cursor
goto_mouse_cursor()
#endif
endif
endif
endif
endif
end do_mouse_action
proc after_getkey()
integer current_key = Query(Key)
case current_key
when <F1>
ignore_current_key()
num_ignorable_keys = 3
when -1, 2047, 32767, 65535, 144967679 // Key -1 has "synonyms".
NoOp()
otherwise
// We are trying to use Query(MouseX) and Query(MouseY) instead of decoding raw keys.
// The num_ignorable_keys logic might primarily be useful for old X10 mouse,
// or to detect that a mouse event *sequence* has started, so TSE has updated MouseX/Y.
// If INTERNAL_VERSION >= 12390, we assume TSE updates Query(MouseX/Y) internally.
#if INTERNAL_VERSION >= 12390
// Check if TSE has reported a mouse event key that implies internal mouse variables are updated.
// A common pattern is that mouse events also come with a distinct key value (e.g., <MouseBtn1>)
// or trigger the mouse hook.
// We can check if mouse_event is populated by the raw Query(Key) processing (like <F1> leading to num_ignorable_keys=3)
// OR if Query(Key) itself returns a mouse-related key (e.g. <LeftBtn>).
// Let's rely on num_ignorable_keys being set from an initial sequence character.
if num_ignorable_keys > 0
// The initial sequence part has been captured by the F1/raw key check.
// Now, try to get the actual mouse coordinates from TSE's internal state.
case num_ignorable_keys
when 3 // This is usually the button event byte
mouse_event = Query(Key) // Still capture this raw button value for decode_mouse_event
Message("DEBUG: Raw Mouse Event Byte: ", mouse_event)
when 2 // This is usually the X coord byte
// Try to get X directly from TSE's internal variable
mouse_x = Query(MouseX)
Message("DEBUG: Attempt Query(MouseX): ", mouse_x)
// If Query(MouseX) is 0 or some default, you might fallback to Query(Key) - 32
// but that's what caused the 4072 problem. Best to rely on Query(MouseX).
when 1 // This is usually the Y coord byte
// Try to get Y directly from TSE's internal variable
mouse_y = Query(MouseY)
Message("DEBUG: Attempt Query(MouseY): ", mouse_y)
do_mouse_action()
endcase
ignore_current_key()
num_ignorable_keys = num_ignorable_keys - 1
endif
#else // Older TSE versions, must rely on Query(Key) - 32
if num_ignorable_keys
case num_ignorable_keys
when 3
mouse_event = Query(Key)
when 2
mouse_x = Query(Key) - 32
when 1
mouse_y = Query(Key) - 32
do_mouse_action()
endcase
ignore_current_key()
num_ignorable_keys = num_ignorable_keys - 1
endif
#endif
endcase
end after_getkey
proc WhenPurged()
// Disable SGR mouse tracking
fWrite(_STDOUT_, Chr(27) + "[?1006l")
// Disable X10 basic mouse tracking (good practice to turn off anything enabled)
fWrite(_STDOUT_, Chr(27) + "[?1000l")
// fflush(_STDOUT_) // Ensure output is sent
end WhenPurged
proc WhenLoaded()
// Enable SGR mouse tracking (prefer SGR over X10 for higher coordinates)
fWrite(_STDOUT_, Chr(27) + "[?1006h")
// Also enable basic mouse tracking (X10), as some terminals might require it
// or TSE might implicitly rely on it for some features.
// This might also explain why the old macro worked up to 93 columns.
fWrite(_STDOUT_, Chr(27) + "[?1000h")
// fflush(_STDOUT_) // Ensure output is sent
Hook(_AFTER_GETKEY_ , after_getkey)
Hook(_ON_ABANDON_EDITOR_, WhenPurged ) // Stop mouse tracking after editor.
end WhenLoaded
proc Main()
end Main
--- cut here: end ----------------------------------------------------