TSE: Linux: LinuxMouse.s generating X10 escape codes and also SGR escape codes

3 views
Skip to first unread message

knud van eeden

unread,
Jun 27, 2025, 6:02:12 PM6/27/25
to SemWare TSE Pro Text Editor, S.E. Mitchell
 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 ----------------------------------------------------

Inline image

LinuxMouseSgrX10.s
Reply all
Reply to author
Forward
0 new messages