From dd1a253df4b5683e3ee8d55d28c18b8b46e30dd9 Mon Sep 17 00:00:00 2001 From: Yee Cheng Chin Date: Thu, 13 Feb 2025 17:36:28 -0800 Subject: [PATCH] Implement getcellpixels() for MacVim 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 --- src/MacVim/MMBackend.h | 2 ++ src/MacVim/MMBackend.m | 16 ++++++++++++++++ src/MacVim/MMVimController.m | 8 ++++++++ src/MacVim/MacVim.h | 1 + src/MacVim/gui_macvim.m | 11 +++++++++++ src/evalfunc.c | 7 +++++++ src/os_unix.c | 11 ++++++++++- src/proto/gui_macvim.pro | 1 + src/terminal.c | 7 +++++++ src/testdir/test_functions.vim | 26 ++++++++++++++++++++++++++ 10 files changed, 89 insertions(+), 1 deletion(-) diff --git a/src/MacVim/MMBackend.h b/src/MacVim/MMBackend.h index e8271f2ded..93c3cdb997 100644 --- a/src/MacVim/MMBackend.h +++ b/src/MacVim/MMBackend.h @@ -57,6 +57,8 @@ #endif } +@property (nonatomic, readonly) NSSize cellSize; + + (MMBackend *)sharedInstance; - (void)setBackgroundColor:(int)color; diff --git a/src/MacVim/MMBackend.m b/src/MacVim/MMBackend.m index 21ade18abb..87b2d6b9f7 100644 --- a/src/MacVim/MMBackend.m +++ b/src/MacVim/MMBackend.m @@ -171,6 +171,7 @@ - (void)focusChange:(BOOL)on; - (void)handleToggleToolbar; - (void)handleScrollbarEvent:(NSData *)data; - (void)handleSetFont:(NSData *)data; +- (void)handleCellSize:(NSData *)data; - (void)handleDropFiles:(NSData *)data; - (void)handleDropString:(NSData *)data; - (void)startOdbEditWithArguments:(NSDictionary *)args; @@ -1328,6 +1329,9 @@ - (oneway void)processInput:(int)msgid data:(in bycopy NSData *)data // modified files when we get here. isTerminating = YES; getout(0); + } else if (UpdateCellSizeMsgID == msgid) { + // Immediately handle simple state updates to they can be reflected in Vim. + [self handleCellSize:data]; } else { // First remove previous instances of this message from the input // queue, else the input queue may fill up as a result of Vim not being @@ -2705,6 +2709,18 @@ - (void)handleSetFont:(NSData *)data CONVERT_FROM_UTF8_FREE(s); } +- (void)handleCellSize:(NSData *)data +{ + if (!data) return; + + const void *bytes = [data bytes]; + + // Don't use gui.char_width/height because for simplicity we set those to + // 1. We store the cell size separately (it's only used for + // getcellpixels()). + memcpy(&_cellSize, bytes, sizeof(NSSize)); +} + - (void)handleDropFiles:(NSData *)data { // TODO: Get rid of this method; instead use Vim script directly. At the diff --git a/src/MacVim/MMVimController.m b/src/MacVim/MMVimController.m index 4d0a295722..05eb56f11a 100644 --- a/src/MacVim/MMVimController.m +++ b/src/MacVim/MMVimController.m @@ -1012,6 +1012,14 @@ - (void)handleMessage:(int)msgid data:(NSData *)data } [windowController setFont:font]; + + // Notify Vim of updated cell size for getcellpixels(). Note that + // this is asynchronous, which means getcellpixels() will not be + // immediately reflected after setting guifont. + NSSize cellsize = windowController.vimView.textView.cellSize; + [self sendMessage:UpdateCellSizeMsgID + data:[NSData dataWithBytes:&cellsize length:sizeof(cellsize)]]; + [name release]; } break; diff --git a/src/MacVim/MacVim.h b/src/MacVim/MacVim.h index 3d2b17425f..46dbd2b0ea 100644 --- a/src/MacVim/MacVim.h +++ b/src/MacVim/MacVim.h @@ -300,6 +300,7 @@ extern const char * const MMVimMsgIDStrings[]; MSG(ScrollbarEventMsgID) \ MSG(SetFontMsgID) \ MSG(SetWideFontMsgID) \ + MSG(UpdateCellSizeMsgID) \ MSG(VimShouldCloseMsgID) \ MSG(SetDefaultColorsMsgID) \ MSG(SetTablineColorsMsgID) \ diff --git a/src/MacVim/gui_macvim.m b/src/MacVim/gui_macvim.m index 7ea4212c11..2526f37b64 100644 --- a/src/MacVim/gui_macvim.m +++ b/src/MacVim/gui_macvim.m @@ -247,6 +247,9 @@ // correspondence (assuming all characters have the same dimensions). gui.scrollbar_width = gui.scrollbar_height = 0; + // For simplicity we just set char width/height to 1 as the GUI is + // decoupled from Vim anyway so Vim doesn't need to know the accurate + // pixel sizes. gui.char_height = 1; gui.char_width = 1; gui.char_ascent = 0; @@ -1664,6 +1667,14 @@ return OK; } + void +gui_mch_calc_cell_size(struct cellsize *cs_out) +{ + NSSize cellsize = [MMBackend sharedInstance].cellSize; + cs_out->cs_xpixel = round(cellsize.width); + cs_out->cs_ypixel = round(cellsize.height); +} + void gui_mch_beep(void) diff --git a/src/evalfunc.c b/src/evalfunc.c index 43e56767cb..0312218274 100644 --- a/src/evalfunc.c +++ b/src/evalfunc.c @@ -5425,8 +5425,15 @@ f_getcellpixels(typval_T *argvars UNUSED, typval_T *rettv) if (gui.in_use) { // success pixel size and no gui. +#ifdef FEAT_GUI_MACVIM + struct cellsize cs; + gui_mch_calc_cell_size(&cs); + list_append_number(rettv->vval.v_list, (varnumber_T)cs.cs_xpixel); + list_append_number(rettv->vval.v_list, (varnumber_T)cs.cs_ypixel); +#else list_append_number(rettv->vval.v_list, (varnumber_T)gui.char_width); list_append_number(rettv->vval.v_list, (varnumber_T)gui.char_height); +#endif } else #endif diff --git a/src/os_unix.c b/src/os_unix.c index bf82fd81d3..790f8281ed 100644 --- a/src/os_unix.c +++ b/src/os_unix.c @@ -4442,7 +4442,16 @@ mch_report_winsize(int fd, int rows, int cols) // calcurate and set tty pixel size struct cellsize cs; - mch_calc_cell_size(&cs); +#if defined(FEAT_GUI) && defined(FEAT_GUI_MACVIM) + if (gui.in_use) + { + gui_mch_calc_cell_size(&cs); + } + else +#endif + { + mch_calc_cell_size(&cs); + } if (cs.cs_xpixel == -1) { diff --git a/src/proto/gui_macvim.pro b/src/proto/gui_macvim.pro index 7d27521d5d..eaa46b200f 100644 --- a/src/proto/gui_macvim.pro +++ b/src/proto/gui_macvim.pro @@ -43,6 +43,7 @@ void gui_mch_set_font(GuiFont font); void gui_mch_expand_font(optexpand_T *args, void *param, int (*add_match)(char_u *val)); int gui_mch_adjust_charheight(void); int gui_mch_adjust_charwidth(void); +void gui_mch_calc_cell_size(struct cellsize *cs_out); void gui_mch_beep(void); char_u *gui_mch_browse(int saving, char_u *title, char_u *dflt, char_u *ext, char_u *initdir, char_u *filter); char_u *gui_mch_browsedir(char_u *title, char_u *initdir); diff --git a/src/terminal.c b/src/terminal.c index f644fb5e97..8a4e459c3f 100644 --- a/src/terminal.c +++ b/src/terminal.c @@ -4831,8 +4831,15 @@ parse_csi( #ifdef FEAT_GUI if (gui.in_use) { +#ifdef FEAT_GUI_MACVIM + struct cellsize cs; + gui_mch_calc_cell_size(&cs); + x += wp->w_wincol * cs.cs_xpixel; + y += W_WINROW(wp) * cs.cs_ypixel; +#else x += wp->w_wincol * gui.char_width; y += W_WINROW(wp) * gui.char_height; +#endif } else #endif diff --git a/src/testdir/test_functions.vim b/src/testdir/test_functions.vim index 8ecc3a6710..5c77fb3160 100644 --- a/src/testdir/test_functions.vim +++ b/src/testdir/test_functions.vim @@ -4338,6 +4338,32 @@ func Test_getcellpixels_gui() endif endfunc +" Test for getcellpixels() for MacVim +func Test_getcellpixels_macvim() + CheckGui + CheckRunVimInTerminal + if has("gui_running") && has('gui_macvim') + " MacVim works asynchronously and getcellpixels() does not immediately + " work either at launch or after guifont has been changed. It's a + " deliberate design decision. Right now the caller has to wait for MacVim + " to update the state before getcellpixels() will reflect the correct + " value, hence the need for multiple wait's here. + call WaitForAssert({-> assert_notequal(0, getcellpixels()[0], 'Uninitialized getcellpixels')}) + call assert_equal([7, 13], getcellpixels()) " Default font is Menlo:h11 + set guifont=Menlo:h13 + call WaitForAssert({-> assert_equal([8, 15], getcellpixels())}) + + " Also test hosting a terminal and have that be updated + let buf = RunVimInTerminal('', #{}) + call term_sendkeys(buf, ":redi @\"\") + call term_sendkeys(buf, ":echo getcellpixels()\") + call term_sendkeys(buf, ":redi END\") + call term_sendkeys(buf, "P") + call WaitForAssert({-> assert_equal(string(getcellpixels()), term_getline(buf, 3))}, 1000) + call StopVimInTerminal(buf) + endif +endfunc + func Str2Blob(s) return list2blob(str2list(a:s)) endfunc