Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Windows fix #775

Merged
merged 12 commits into from
Nov 8, 2024
Merged

Windows fix #775

merged 12 commits into from
Nov 8, 2024

Conversation

YO4
Copy link
Contributor

@YO4 YO4 commented Nov 4, 2024

This is part of supporting windows for reline CI using yamatanooroti.
This PR contains the following changes. There are independent to windows specific yamatanooroti changes.

  • Windows specific test fix, not related yamatanooroti.
  • Changes for conhostV1 (Command Prompt Window, legacy mode ON), includes bug fix.
  • Follows reline changes.
  • Supporting frozen string literal.

@YO4
Copy link
Contributor Author

YO4 commented Nov 4, 2024

#775 #776 does not have CI changes.
Currently, CI is being tested in another branch.
https://github.com/YO4/reline/tree/ci_for_windows

@@ -115,7 +115,7 @@ def initialize(dllname, func, import, export = "0", calltype = :stdcall)
def call(*args)
import = @proto.split("")
args.each_with_index do |x, i|
args[i], = [x == 0 ? nil : +x].pack("p").unpack(POINTER_TYPE) if import[i] == "S"
args[i], = [x == 0 ? nil : x&.+@].pack("p").unpack(POINTER_TYPE) if import[i] == "S"
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This code does not expect x to be non-zero number because [+3].pack('p') raises error.
Zero represents NULL_POINTER, so the possible code would be

# Use 0 as null_pointer
[x == 0 ? nil : +x].pack("p")
@ScrollConsoleScreenBuffer.call(rect, 0, dst, fill)
# Represent NULL_POINTER by nil, not by 0
[x&.+@].pack("p")
@ScrollConsoleScreenBuffer.call(rect, nil, dst, fill)

By the way, I think if x is a frozen string, a new mutable string +x might be garbaged collected before @func.call is called.
If the frozen string issue is solved in L174 (mode = +"\0\0\0\0"), I think removing +@ is better.

[x == 0 ? nil : x].pack("p").... # x is 0 or string
# or
[x].pack("p").... # x is nil or string

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The current patch is the result of a minimization of fixes because I could not figure out the intent of args repacking.
By checking the code around ruby 1.9 to 2.0, I found that DL::CFunc of Ruby/DL (Ruby/DL2), the backend of WIN32API at that time, accepts only integers.
I think the repacking itself is unnecessary because the current Fiddle allows more flexible passing of arguments.

def call(*args)
  return @func.call(*args)
end

The above may be enough. Is there something I'm missing?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think the NULL problem is fixed in 9b3ffd8
String 0.chr * n +"\0\0\0..." seems to be always passed to import[i]=="S" (import is 'P') argument except
call_with_console_handle(@ScrollConsoleScreenBuffer, scroll_rectangle, nil, destination_origin, fill)
and this line is removed in 9b3ffd8

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the suggestion. I was not aware of 9b3ffd8 at all.
However, I am worried about touching a code that I don't know the basis for, and I now think that code is unnecessary.
So I think it was done in 060ba14 .

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

def call(*args)
  return @func.call(*args)
end

This looks good to me.
However, I don't have confidence that this works in all supported ruby versions and fiddle version. I think it is better to avoid risk since next ruby release is close in time.
How about leaving it as is (as original [x == 0 ? nil : +x].pack("p")....) for now, and change it after adding windows ci?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I understand your point of consideration.
I also know that there is no specific problem that the patch will solve.
reverted in eb5959c 5443ab5

@@ -509,6 +512,7 @@ def render
# by calculating the difference from the previous render.

private def render_differential(new_lines, new_cursor_x, new_cursor_y)
Reline::IOGate.disable_auto_linewrap(true) if Reline::IOGate.win?
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why is this needed? I think line_editor does not print line longer than terminal width.
(Maybe the case line_width == terminal_width case?)

If this is needed, moving it to Reline::Windows#prep and Reline::Windows#deprep is better

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The restore in deprep does not work because render_finished outputs a single, long line.
Windows console has mode flags. (https://learn.microsoft.com/en-us/windows/console/high-level-console-modes)
If ENABLE_VIRTUAL_TERMINAL_PROCESSING is set, cursor moves as expected by reline.
In environments where the flag is off or not supported, ENABLE_WRAP_AT_EOL_OUTPUT is on normally, causing a forced line break at the end of a line of output.
For example, outputting 123456 on a 6-column wide terminal would result in the following.

123456
_ #=> cursor moves next line

If ENABLE_WRAP_AT_EOL_OUTPUT is turned off, no forced line breaks occur. Instead, the cursor stays at the end of the line and subsequent characters overwrite the end of the line.
For example, outputting 12345678 on a 6-column wide terminal would result in the following.

123458 #=> cursor stays last column of line

Therefore, it was necessary to insert control of ENABLE_WRAP_AT_EOL_OUTPUT in the appropriate place.
Another reason was that if there was console output in the background, long lines could be destroyed if the flag was not restored while waiting for keystrokes.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It makes sense. Thank you for the description 👍

top = csbi[12, 2].unpack1('S')
bottom = csbi[16, 2].unpack1('S')
width = csbi[0, 2].unpack1('S')
[bottom - top + 1, width]
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

My understanding by reading the document is:

hidden_screen_buffer01
hidden_screen_buffer02
hidden_screen_buffer03
hidden_screen_buffer04
visible_screen_buffer1
visible_screen_buffer2
visible_sc█een_buffer3
visible_screen_buffer4

screen_buffer width = 22, screen_buffer height = 8
cursor x = 10 y = 6 (cursor coord in screen buffer, not coord in visible window)
window left=0 right=21 top=4 bottom=7

Is this right? If so, I think def cursor_pos should use CursorPos.new(x, y - top). Can you check this?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You are right.
I tried to modify cursor_pos, but even the cursor moving functions needed to take csbi.srWindow.Top into consideration. Needs some more work.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done in df70541 .

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you 👍

YO4 added 4 commits November 5, 2024 22:30
argument repacking and return value tweaking is not needed for Reline::Windows requirements.
Copy link
Member

@tompng tompng left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good 👍
Thank you

@tompng tompng merged commit 47c1ffb into ruby:master Nov 8, 2024
46 checks passed
matzbot pushed a commit to ruby/ruby that referenced this pull request Nov 8, 2024
* test_yamatanooroti: close tempfile before unlink

* test_yamatanooroti: omit because of windows does not support job control

* test_yamatanooroti: change startup message detection for windows

* windows.rb: can call win32api using nil as NULL for pointer argument

Exception occurred when interrupted with Ctrl+C on legacy conhost

* windows.rb: fix get_screen_size

return [window height, buffer width] insted of [buffer height, buffer width]

* windows.rb: import scroll_down() from ansi.rb

* windows.rb: add auto linewrap control if VT output not supported (legacy console)

* unfreeze WIN32API pointer arguments

They internally duplicate arguments so api functions write to another place.
This breaks the console mode detection with ruby-head.

* remove useless code from Win32API#call

argument repacking and return value tweaking is not needed for Reline::Windows requirements.

* Correctly handle top of console viewport

* Revert "remove useless code from Win32API#call"

This reverts commit ruby/reline@060ba140ed43.

* Revert "windows.rb: can call win32api using nil as NULL for pointer argument"

This reverts commit ruby/reline@93a23bc5d0c9.

ruby/reline@47c1ffbabe
@YO4
Copy link
Contributor Author

YO4 commented Nov 8, 2024

I'm glad to hear it.
Thank you for your time.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Development

Successfully merging this pull request may close these issues.

2 participants