Skip to content

Conversation

@mikoto2000
Copy link
Contributor

What I want to do?

I want to get the pixel size of the window in the CUI version of Vim.

Motivation:

The ability to output Sixel in echoraw has opened up possibilities such as image display plugins
If the pixel size of the window can be known, the possibilities for functions such as fitting the image to the window will be expanded.

Pros:

The ability to obtain precise pixel size opens up possibilities for image output plugins.

Cons:

Even in cases where the matrix level is sufficient, processing time to obtain pixel level values is added.

Implementation:

  • Issue TIOCSWINSZ to the parent process's tty to get the size, and calculate the cell size from it.
  • Calculate the window pixel size using the cell width and height and the window row, col.
  • Add wpixel and hpixel entries to the getwininfo() dictionary and store the pixel size there.

Others:

The initial implementation used CSI escape sequences, but it was changed to the TIOCSWINSZ implementation because of a problem where processing would not continue unless <Enter> was pressed after getwinsize().
(See: master...mikoto2000:vim:add-pixel-size-in-getwininfo2)

@mikoto2000 mikoto2000 force-pushed the add-pixel-size-in-getwininfo branch from 8718e22 to db51514 Compare November 7, 2024 04:27
@chrisbra
Copy link
Member

chrisbra commented Nov 7, 2024

Thanks, but I have a few high-level comments:

  • I would think this does belong into a new function and not into getwininfo(), otherwise it may make is unneccessarily slow.
  • can you think about a test for this
  • can you please fix those warnings?

@mikoto2000
Copy link
Contributor Author

OK. I try it.

Since we know the cell size, it's easy to calculate the window size by combining getwininfo(), so we'll add a function called getcellpixels() that returns the pixel width and height of the terminal cell.
getcellpixels() return dictionary has entry xpixel, ypixel.

Cell size is dependent terminal, so test is only dictionary has entries check.

I will work with this implementation.

@ychin
Copy link
Contributor

ychin commented Nov 7, 2024

A few top-level questions / comments:

  1. This has a fallback default of 5x10 pixels if for some reason the info could not be obtained. I think the desired behavior should instead be to indicate clearly to the caller that the info is unavailable instead of silently failing and using a wrong value. This could either be a error return value, or something like [-1,-1].
  2. Using /proc is non-portable (e.g. it doesn't work on non-Linux Unix systems like macOS) and seems like an unnecessary way to obtain the fd. Given that Vim is constantly writing to the terminal I would imagine the fd should already be available to us? Why would we need to query for the tty manually? We should be able to just use it and do the ioctl call.
  3. Windows support?
  4. GUI support?
  5. Should probably use Vim-standardized typedef's and functions, e.g. UINT for unsigned ints, and vim_snprintf.

@ychin
Copy link
Contributor

ychin commented Nov 7, 2024

Since we know the cell size, it's easy to calculate the window size by combining getwininfo(), so we'll add a function called getcellpixels() that returns the pixel width and height of the terminal cell.
getcellpixels() return dictionary has entry xpixel, ypixel.

Should this just return an array, something like [7,11] instead of a dictionary? I'm not strongly opinionated on this one but for simple x/y coords that seems easiest to me, but would make it less extensible in the future.

@ychin
Copy link
Contributor

ychin commented Nov 7, 2024

Windows support?

This (https://stackoverflow.com/a/50395589/8916547) seems to provide a way to query this in Windows.

@mikoto2000
Copy link
Contributor Author

mikoto2000 commented Nov 8, 2024

@ychin Thank you for your comments!

A few top-level questions / comments:

  1. You're right. I use list([x, y]), and return [-1, -1] if error.
  2. You're right. I get current process's tty by ttyname().
  3. It's not included in this PR.
  4. It's not included in this PR.
  5. Thank you, I check Vim-standardized typedef's and functions.

@ychin
Copy link
Contributor

ychin commented Nov 8, 2024

You're right. I get current process's tty by ttyname().

No, what I mean is just use the stdin / stdout fd. I don't think there is any need to do any of the other methods to get the tty fd.

In fact, did you look at the mch_get_shellsize() implementation? It's already calling TIOCGWINSZ but it's only retrieving the number of rows/columns. I think the implementation of your function could just do something similar to that function (similar to the part I pasted below), and prefixed with mch_ for the function name (which we do for platform-specific functionality):

// os_unix.c

    int
mch_get_shellsize(void)
{
// …

    /*
     * 1. try using an ioctl. It is the most accurate method.
     *
     * Try using TIOCGWINSZ first, some systems that have it also define
     * TIOCGSIZE but don't have a struct ttysize.
     */
# ifdef TIOCGWINSZ
    {
	struct winsize	ws;
	int fd = 1;

	// When stdout is not a tty, use stdin for the ioctl().
	if (!isatty(fd) && isatty(read_cmd_fd))
	    fd = read_cmd_fd;
	if (ioctl(fd, TIOCGWINSZ, &ws) == 0)
	{
	    columns = ws.ws_col;
	    rows = ws.ws_row;
#  ifdef FEAT_EVAL
	    ch_log(NULL, "Got size with TIOCGWINSZ: %ld x %ld", columns, rows);
#  endif
	}
// …
}

For reference, this function is used for determining the values of 'lines' and 'columns'.

Another comment (just to save back-and-forth time) is we usually put function declaration in the misc .pro header files inside src/proto folder. For example there is a proto/os_unix.pro file which contains the header declarations. (I don't really know the history behind them)

@mikoto2000
Copy link
Contributor Author

Sorry, I did not study enough.
We will look at mch_get_shellsize() and the surrounding functions and change the implementation.

@zeertzjq zeertzjq changed the title Added pixel size in getwininfo() Add getcellpixels() Nov 8, 2024
@mikoto2000
Copy link
Contributor Author

@zeertzjq
Sorry, I mixed up x and y in one place, so I made an additional commit: 177734a.

List format is [xpixels, ypixels].
Only on UNIX.

Return type: list<any>
Copy link
Member

Choose a reason for hiding this comment

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

The document should explain that some environments may return unreliable values.

Copy link
Contributor

@ychin ychin Nov 8, 2024

Choose a reason for hiding this comment

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

On what environments would it be unreliable? I think it just needs to clearly explain that it would return an error value, e.g. -1,-1 when it's unavailable. That's not "unreliable" per se.


getcellpixels() *getcellpixels()*
Returns a |List| of cell pixel size.
List format is [xpixels, ypixels].
Copy link
Member

Choose a reason for hiding this comment

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

Only works on Unix. For gVim, returns [5, 10], on other systems, returns [-1, -1].

Copy link
Contributor

Choose a reason for hiding this comment

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

Shouldn't gVIm return [-1,-1] until gVim gets a proper implementation?

@chrisbra chrisbra closed this in 1083cae Nov 11, 2024
@ychin
Copy link
Contributor

ychin commented Nov 12, 2024

Seems from the implementation that this is going to return [] on unsupported platforms (e.g. Windows), since it's just provided a NULL implementation (in evalfunc.c)? On Unix, gVim does return [-1,-1]. This doesn't match the documented behavior that it always returns [-1,-1].

Should we just standardize on this and fix the doc? Perhaps just returning [] for all unsupported platforms is better than [-1,-1] and it's actually easier for a caller to check as well. This way we could just use a NULL implementation and have it just work. That means we just need to fix the GUI implementation and also the documentation.

I think the tests are also in the wrong file as well. I can submit a PR to fix it. It's put into the UTF-8 tests next to getcellwidths() but getcellwidths() is related to setting specialized double-width cells depending on which UTF-8 char which is completely different from the purpose of getcellpixels().

@chrisbra
Copy link
Member

Yes makes sense to just return an empty list. and the documentation should then be changed back to List<any>. @mikoto2000 can you make those changes on a followup PR please?

@mikoto2000
Copy link
Contributor Author

Thank you for pointing out the improvement.
I will create a followup PR.

@mikoto2000
Copy link
Contributor Author

Can not call getcellpixels() when NULL implementation in evalfunc.c.
(:call getcellpixels() occures E117 unknown function)

I will fix doc and gui implementation, test position.

@mikoto2000
Copy link
Contributor Author

I will create test file test_getcellpixels.vim.

@mikoto2000
Copy link
Contributor Author

@ychin

Windows support?

This (https://stackoverflow.com/a/50395589/8916547) seems to provide a way to query this in Windows.

I tried the stack overflow post you suggested, but the font size was returned as a fixed value of 0x16.
I'll try to find a different approach.

prototype branch: 2fb4877#diff-6e09d275741ed4a17372f6e64995645e818d605e41a0591b2c20741f4878d3c1R9074

ychin added a commit to ychin/macvim that referenced this pull request Feb 13, 2025
Don't use `gui.char_width` / `char_height` unlike the other GVim
implementations. Those are used for deriving screen pixel sizes and
MacVim has been hard-coding them to 1 for simplicity since the actual
GUI functionality is handled out of the Vim process anyway. Changing
those values would require some refactoring. Instead, just use a new
variable to store them.

Related: vim/vim#16004
ychin added a commit to ychin/macvim that referenced this pull request Feb 14, 2025
Don't use `gui.char_width` / `char_height` unlike the other GVim
implementations. Those are used for deriving screen pixel sizes and
MacVim has been hard-coding them to 1 for simplicity since the actual
GUI functionality is handled out of the Vim process anyway. Changing
those values would require some refactoring. Instead, just use a new
variable to store them.

Note that there is a delay with this method, as we only update Vim's
knowledge of cell size after MacVim has received the font change
message. This means if a user wants to immediately query getcellpixels()
in vimrc or after changing guifont this will not work. This is a
deliberate design choice to avoid having to add synchronous state query
APIs to MacVim, but it's possible to change it in the future. The
handling of this message did get placed in `processInput:` which means
it will be picked up whenever we sleep or process messages in Vim,
instead of only when waiting for keys (where most of other MacVim
messages are handled in `processInputQueue`).

Related: vim/vim#16004
ychin added a commit to ychin/macvim that referenced this pull request Feb 14, 2025
Don't use `gui.char_width` / `char_height` unlike the other GVim
implementations. Those are used for deriving screen pixel sizes and
MacVim has been hard-coding them to 1 for simplicity since the actual
GUI functionality is handled out of the Vim process anyway. Changing
those values would require some refactoring. Instead, just use a new
variable to store them.

Note that there is a delay with this method, as we only update Vim's
knowledge of cell size after MacVim has received the font change
message. This means if a user wants to immediately query getcellpixels()
in vimrc or after changing guifont this will not work. This is a
deliberate design choice to avoid having to add synchronous state query
APIs to MacVim, but it's possible to change it in the future. The
handling of this message did get placed in `processInput:` which means
it will be picked up whenever we sleep or process messages in Vim,
instead of only when waiting for keys (where most of other MacVim
messages are handled in `processInputQueue`).

Related: vim/vim#16004
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants