From ddc869fa8163302644548de2bd7c18d74b7a541b Mon Sep 17 00:00:00 2001 From: Yee Cheng Chin Date: Sat, 2 Feb 2019 23:43:06 -0800 Subject: [PATCH] Fix rendering performance issue when using :version or echo commands This fixes 10.14 Mojave's CoreText renderer taking a long time to render :version / :ls / :! / :echo or similar commands. This issue happened because the way Vim echos the output of those commands is by issuing a draw calls in the pattern of "delete 1 line, draw some text, delete another line...". Each line delete causes the renderer to do a scroll. The pre-Mojave renderer relies on calling scrollRect: but this doesn't work in Mojave anymore since that function is deprecated and doesn't work in layer-backed views (which are now mandatory). The new renderer's scroll implementation is a lot slower since it's doing image blits on CPU. The fix is to implement a draw command optimizer that pre-processes the draw calls first. It works by batching together all the "delete 1 line" calls and combine into a single "delete N lines" call and put that in the beginning, and fixing up all the other draw string command so they draw to the right line instead of needing to be scrolled up. This makes :version or the other calls feel instaneous now. This fix is ultimately a hack and an intermediary solution before the renderer can be replaced (since the slow CPU scrolling causes normal usage to feel sluggish as well) by a GPU-based renderer and/or a glyph-based one that caches the state of the texts so repeated scrolling can be done by shuffling the glpyh data around instead of an actualy image blit. Fix #815 --- src/MacVim/MMCoreTextView.m | 607 ++++++++++++++++++++++++++++++------ src/MacVim/MacVim.h | 2 + 2 files changed, 521 insertions(+), 88 deletions(-) diff --git a/src/MacVim/MMCoreTextView.m b/src/MacVim/MMCoreTextView.m index 0510d68842..03924d29ee 100644 --- a/src/MacVim/MMCoreTextView.m +++ b/src/MacVim/MMCoreTextView.m @@ -77,6 +77,7 @@ - (NSPoint)pointForRow:(int)row column:(int)column; - (NSRect)rectFromRow:(int)row1 column:(int)col1 toRow:(int)row2 column:(int)col2; - (NSSize)textAreaSize; +- (NSData *)optimizeBatchDrawData:(NSData *)data; - (void)batchDrawData:(NSData *)data; - (void)drawString:(const UniChar *)chars length:(UniCharCount)length atRow:(int)row column:(int)col cells:(int)cells @@ -1101,8 +1102,483 @@ - (NSSize)textAreaSize #define MM_DEBUG_DRAWING 0 +// TODO: move this to a utility class to be shared +// Reader / writer utilities to interact with the batch draw data stream. +struct DrawCmdClearAll +{ +}; + +struct DrawCmdClearBlock +{ + unsigned color; + int row1; + int col1; + int row2; + int col2; +}; + +struct DrawCmdDeleteLines +{ + unsigned color; + int row; + int count; + int bot; + int left; + int right; +}; + +struct DrawCmdDrawSign +{ + int strSize; + const char *imgName; + int col; + int row; + int width; + int height; +}; + +struct DrawCmdDrawString +{ + int bg; + int fg; + int sp; + int row; + int col; + int cells; + int flags; + int len; + UInt8 *str; +}; + +struct DrawCmdInsertLines +{ + unsigned color; + int row; + int count; + int bot; + int left; + int right; +}; + +struct DrawCmdDrawCursor +{ + unsigned color; + int row; + int col; + int shape; + int percent; +}; + +struct DrawCmdDrawInvertedRect +{ + int row; + int col; + int nr; + int nc; + int invert; +}; + +struct DrawCmdSetCursorPos +{ + int row; + int col; +}; + +struct DrawCmd +{ + union + { + struct DrawCmdClearAll drawCmdClearAll; + struct DrawCmdClearBlock drawCmdClearBlock; + struct DrawCmdDeleteLines drawCmdDeleteLines; + struct DrawCmdDrawSign drawCmdDrawSign; + struct DrawCmdDrawString drawCmdDrawString; + struct DrawCmdInsertLines drawCmdInsertLines; + struct DrawCmdDrawCursor drawCmdDrawCursor; + struct DrawCmdDrawInvertedRect drawCmdDrawInvertedRect; + struct DrawCmdSetCursorPos drawCmdSetCursorPos; + }; + + int type; +}; + +// Read a single draw command from the batch draw data stream, and returns the +// draw type (the type is also stored in drawCmd->type). +static int ReadDrawCmd(const void **bytesRef, struct DrawCmd *drawCmd) +{ + const void *bytes = *bytesRef; + + int type = *((int*)bytes); bytes += sizeof(int); + + switch (type) { + case ClearAllDrawType: + break; + case ClearBlockDrawType: + { + struct DrawCmdClearBlock *cmd = &drawCmd->drawCmdClearBlock; + cmd->color = *((unsigned*)bytes); bytes += sizeof(unsigned); + cmd->row1 = *((int*)bytes); bytes += sizeof(int); + cmd->col1 = *((int*)bytes); bytes += sizeof(int); + cmd->row2 = *((int*)bytes); bytes += sizeof(int); + cmd->col2 = *((int*)bytes); bytes += sizeof(int); + } + break; + case DeleteLinesDrawType: + { + struct DrawCmdDeleteLines *cmd = &drawCmd->drawCmdDeleteLines; + cmd->color = *((unsigned*)bytes); bytes += sizeof(unsigned); + cmd->row = *((int*)bytes); bytes += sizeof(int); + cmd->count = *((int*)bytes); bytes += sizeof(int); + cmd->bot = *((int*)bytes); bytes += sizeof(int); + cmd->left = *((int*)bytes); bytes += sizeof(int); + cmd->right = *((int*)bytes); bytes += sizeof(int); + } + break; + case DrawSignDrawType: + { + struct DrawCmdDrawSign *cmd = &drawCmd->drawCmdDrawSign; + cmd->strSize = *((int*)bytes); bytes += sizeof(int); + cmd->imgName = (const char*)bytes; bytes += cmd->strSize; + cmd->col = *((int*)bytes); bytes += sizeof(int); + cmd->row = *((int*)bytes); bytes += sizeof(int); + cmd->width = *((int*)bytes); bytes += sizeof(int); + cmd->height = *((int*)bytes); bytes += sizeof(int); + } + break; + case DrawStringDrawType: + { + struct DrawCmdDrawString *cmd = &drawCmd->drawCmdDrawString; + cmd->bg = *((int*)bytes); bytes += sizeof(int); + cmd->fg = *((int*)bytes); bytes += sizeof(int); + cmd->sp = *((int*)bytes); bytes += sizeof(int); + cmd->row = *((int*)bytes); bytes += sizeof(int); + cmd->col = *((int*)bytes); bytes += sizeof(int); + cmd->cells = *((int*)bytes); bytes += sizeof(int); + cmd->flags = *((int*)bytes); bytes += sizeof(int); + cmd->len = *((int*)bytes); bytes += sizeof(int); + cmd->str = (UInt8 *)bytes; bytes += cmd->len; + } + break; + case InsertLinesDrawType: + { + struct DrawCmdInsertLines *cmd = &drawCmd->drawCmdInsertLines; + cmd->color = *((unsigned*)bytes); bytes += sizeof(unsigned); + cmd->row = *((int*)bytes); bytes += sizeof(int); + cmd->count = *((int*)bytes); bytes += sizeof(int); + cmd->bot = *((int*)bytes); bytes += sizeof(int); + cmd->left = *((int*)bytes); bytes += sizeof(int); + cmd->right = *((int*)bytes); bytes += sizeof(int); + } + break; + case DrawCursorDrawType: + { + struct DrawCmdDrawCursor *cmd = &drawCmd->drawCmdDrawCursor; + cmd->color = *((unsigned*)bytes); bytes += sizeof(unsigned); + cmd->row = *((int*)bytes); bytes += sizeof(int); + cmd->col = *((int*)bytes); bytes += sizeof(int); + cmd->shape = *((int*)bytes); bytes += sizeof(int); + cmd->percent = *((int*)bytes); bytes += sizeof(int); + } + break; + case DrawInvertedRectDrawType: + { + struct DrawCmdDrawInvertedRect *cmd = &drawCmd->drawCmdDrawInvertedRect; + cmd->row = *((int*)bytes); bytes += sizeof(int); + cmd->col = *((int*)bytes); bytes += sizeof(int); + cmd->nr = *((int*)bytes); bytes += sizeof(int); + cmd->nc = *((int*)bytes); bytes += sizeof(int); + cmd->invert = *((int*)bytes); bytes += sizeof(int); + } + break; + case SetCursorPosDrawType: + { + struct DrawCmdSetCursorPos *cmd = &drawCmd->drawCmdSetCursorPos; + cmd->row = *((int*)bytes); bytes += sizeof(int); + cmd->col = *((int*)bytes); bytes += sizeof(int); + } + break; + default: + { + ASLogWarn(@"Unknown draw type (type=%d)", type); + type = InvalidDrawType; + } + break; + } + + *bytesRef = bytes; + drawCmd->type = type; + return type; +} + +// Write a single draw command to the batch draw data stream. +static void WriteDrawCmd(NSMutableData *drawData, const struct DrawCmd *drawCmd) +{ + int type = drawCmd->type; + + [drawData appendBytes:&type length:sizeof(int)]; + + switch (type) { + case ClearAllDrawType: + break; + case ClearBlockDrawType: + { + const struct DrawCmdClearBlock *cmd = &drawCmd->drawCmdClearBlock; + [drawData appendBytes:cmd length:sizeof(struct DrawCmdClearBlock)]; + static_assert(sizeof(struct DrawCmdClearBlock) == sizeof(unsigned) + sizeof(int)*4, "Wrong size"); + } + break; + case DeleteLinesDrawType: + { + const struct DrawCmdDeleteLines *cmd = &drawCmd->drawCmdDeleteLines; + [drawData appendBytes:cmd length:sizeof(struct DrawCmdDeleteLines)]; + static_assert(sizeof(struct DrawCmdDeleteLines) == sizeof(unsigned) + sizeof(int)*5, "Wrong size"); + } + break; + case DrawSignDrawType: + { + // Can't do simple memcpy of whole struct because of cmd->imgName. + const struct DrawCmdDrawSign *cmd = &drawCmd->drawCmdDrawSign; + [drawData appendBytes:&cmd->strSize length:sizeof(int)]; + [drawData appendBytes:cmd->imgName length:cmd->strSize]; + [drawData appendBytes:&cmd->col length:sizeof(int)]; + [drawData appendBytes:&cmd->row length:sizeof(int)]; + [drawData appendBytes:&cmd->width length:sizeof(int)]; + [drawData appendBytes:&cmd->height length:sizeof(int)]; + } + break; + case DrawStringDrawType: + { + // Can't do simple memcpy of whole struct because of cmd->str. + const struct DrawCmdDrawString *cmd = &drawCmd->drawCmdDrawString; + [drawData appendBytes:&cmd->bg length:sizeof(int)]; + [drawData appendBytes:&cmd->fg length:sizeof(int)]; + [drawData appendBytes:&cmd->sp length:sizeof(int)]; + [drawData appendBytes:&cmd->row length:sizeof(int)]; + [drawData appendBytes:&cmd->col length:sizeof(int)]; + [drawData appendBytes:&cmd->cells length:sizeof(int)]; + [drawData appendBytes:&cmd->flags length:sizeof(int)]; + [drawData appendBytes:&cmd->len length:sizeof(int)]; + [drawData appendBytes:cmd->str length:cmd->len]; + } + break; + case InsertLinesDrawType: + { + const struct DrawCmdInsertLines *cmd = &drawCmd->drawCmdInsertLines; + [drawData appendBytes:cmd length:sizeof(struct DrawCmdInsertLines)]; + static_assert(sizeof(struct DrawCmdInsertLines) == sizeof(unsigned) + sizeof(int)*5, "Wrong size"); + } + break; + case DrawCursorDrawType: + { + const struct DrawCmdDrawCursor *cmd = &drawCmd->drawCmdDrawCursor; + [drawData appendBytes:cmd length:sizeof(struct DrawCmdDrawCursor)]; + static_assert(sizeof(struct DrawCmdDrawCursor) == sizeof(unsigned) + sizeof(int)*4, "Wrong size"); + } + break; + case DrawInvertedRectDrawType: + { + const struct DrawCmdDrawInvertedRect *cmd = &drawCmd->drawCmdDrawInvertedRect; + [drawData appendBytes:cmd length:sizeof(struct DrawCmdDrawInvertedRect)]; + static_assert(sizeof(struct DrawCmdDrawInvertedRect) == sizeof(int)*5, "Wrong size"); + } + break; + case SetCursorPosDrawType: + { + const struct DrawCmdSetCursorPos *cmd = &drawCmd->drawCmdSetCursorPos; + [drawData appendBytes:cmd length:sizeof(struct DrawCmdSetCursorPos)]; + static_assert(sizeof(struct DrawCmdSetCursorPos) == sizeof(int)*2, "Wrong size"); + } + break; + default: + { + ASLogWarn(@"Unknown draw type (type=%d)", type); + } + break; + } +} + +// Utility to optimize batch draw commands. +// +// Right now, there's only a single reason for this to exist, which is to +// reduce multiple deleteLines commands from being issued. This can happen when +// ":version" is called, or ":!" or misc cmds like ":ls". What usually happens +// is that Vim will issue a "delete lines" command that deletes 1 line, draw +// some text, then delete another line and so on. This is bad because +// deleteLinesFromRow: calls scrollRect: which is currently not fast in CGLayer +// (deprecated) or BufferDraw (default under Mojave) mode as they are not +// GPU-accelerated. Multiple scrolls means multiple image blits which will +// severely degrade rendering performance. +// +// This function will combine all these little delete lines calls into a single +// one, and then re-shuffle all the draw string commands later to be draw to +// the right place. This makes rendering performance much better in the above +// mentioned situations. +// +// This may get revisited later. Also, if we move to a GPU-based Metal renderer +// or a glyph-based one (rather than draw command-based) we may have no need +// for this as scrolling/deleting lines would just involve shuffling memory +// around before we do any draws on the glyphs. +- (NSData *)optimizeBatchDrawData:(NSData *)data +{ + const void *bytes = [data bytes]; + const void *end = bytes + [data length]; + + // + // 1) Do a single pass to detect whether we need to optimize the batch draw + // data stream. If not, we just return the original data back. + // + bool shouldOptimize = false; + + int deleteLinesCount = 0; + + unsigned deleteLinesColor = 0; + int deleteLinesRow = 0; + int deleteLinesBot = 0; + int deleteLinesLeft = 0; + int deleteLinesRight = 0; + + struct DrawCmd cmd; + + while (bytes < end) { + int type = ReadDrawCmd(&bytes, &cmd); + if (deleteLinesCount != 0) { + // Right onw in the only known cases where multiple delete lines + // get issued only draw string commands would get issued so just + // don't optimize the other cases for now for simplicity. + if (type != DeleteLinesDrawType + && type != DrawStringDrawType + && type != SetCursorPosDrawType) { + return data; + } + } + + if (DeleteLinesDrawType == type) { + struct DrawCmdDeleteLines *cmdDel = &cmd.drawCmdDeleteLines; + if (deleteLinesCount == 0) { + // First time seeing a delete line operation, cache its + // properties. + deleteLinesColor = cmdDel->color; + deleteLinesRow = cmdDel->row; + deleteLinesBot = cmdDel->bot; + deleteLinesLeft = cmdDel->left; + deleteLinesRight = cmdDel->right; + } else { + // We only optimize if we see 2+ delete line operations, + // otherwise this is no point. + shouldOptimize = true; + + bool similarCmd = + deleteLinesColor == cmdDel->color && + deleteLinesRow == cmdDel->row && + deleteLinesBot == cmdDel->bot && + deleteLinesLeft == cmdDel->left && + deleteLinesRight == cmdDel->right; + if (!similarCmd) { + // This shouldn't really happen, but in case we have + // situations where the multiple delete line operations are + // different it's kind of hard to combine them together, so + // just ignore. + return data; + } + } + + deleteLinesCount += cmdDel->count; + } else if (DrawStringDrawType == type) { + // There may be cases where this optimization doesn't work and we + // need to bail here. E.g. if the string is drawn across the + // scrolling boundary of the delete line operation moving the + // command around would not result in the correct rendering, or if + // the string is in the deleted region later this would cause + // problems too. + // For simplicity we don't check for those cases now, as they + // aren't known to happen. + } + } + + if (!shouldOptimize) { + return data; + } + + // + // 2) If we reach here, we want to optimize the data stream. Make a new + // data stream with the delete lines commands all shoved into one single + // one. + // + NSMutableData *newData = [[[NSMutableData alloc] initWithCapacity:[data length]] autorelease]; + + struct DrawCmd drawCmdDelLines; + drawCmdDelLines.type = DeleteLinesDrawType; + { + struct DrawCmdDeleteLines *cmdDel = &drawCmdDelLines.drawCmdDeleteLines; + cmdDel->color = deleteLinesColor; + cmdDel->row = deleteLinesRow; + cmdDel->count = deleteLinesCount; + cmdDel->bot = deleteLinesBot; + cmdDel->left = deleteLinesLeft; + cmdDel->right = deleteLinesRight; + } + + bytes = [data bytes]; + end = bytes + [data length]; + + bool insertedDelLinesCmd = false; + int remainingDeleteLinesLeft = deleteLinesCount; + while (bytes < end) { + int type = ReadDrawCmd(&bytes, &cmd); + + if (type == DeleteLinesDrawType) { + if (!insertedDelLinesCmd) { + // We replace the 1st delete line command by the combined + // delete line command. This way earlier commands can remain + // untouched and only later commands need to be touched up. + WriteDrawCmd(newData, &drawCmdDelLines); + insertedDelLinesCmd = true; + } + remainingDeleteLinesLeft -= cmd.drawCmdDeleteLines.count; + continue; + } + + if (!insertedDelLinesCmd) { + WriteDrawCmd(newData, &cmd); + continue; + } + + // Shift the draw command up. + // Before the optimization, we have: + // A1. delete N lines + // A2. draw string + // A3. delete M lines + // After the optimization: + // B1. delete N + M lines + // B2. draw string + // + // A3 would have shifted the draw string M lines up but it won't now. + // Therefore, when we draw B2 (which is here), we need to make sure to + // draw it M lines higher to present the same result. + if (type == DrawStringDrawType) { + struct DrawCmdDrawString *drawCmd = &cmd.drawCmdDrawString; + if (drawCmd->row > deleteLinesRow) { + drawCmd->row -= remainingDeleteLinesLeft; + } + } else if (type == SetCursorPosDrawType) { + struct DrawCmdSetCursorPos *drawCmd = &cmd.drawCmdSetCursorPos; + if (drawCmd->row > deleteLinesRow) { + drawCmd->row -= remainingDeleteLinesLeft; + } + } + WriteDrawCmd(newData, &cmd); + } + + return newData; +} + - (void)batchDrawData:(NSData *)data { + // Optimize the batch draw commands before issuing them. This makes the + // draw commands more efficient but should result in identical final + // results. + data = [self optimizeBatchDrawData:data]; + const void *bytes = [data bytes]; const void *end = bytes + [data length]; @@ -1112,7 +1588,8 @@ - (void)batchDrawData:(NSData *)data // TODO: Sanity check input while (bytes < end) { - int type = *((int*)bytes); bytes += sizeof(int); + struct DrawCmd drawCmd; + int type = ReadDrawCmd(&bytes, &drawCmd); if (ClearAllDrawType == type) { #if MM_DEBUG_DRAWING @@ -1120,49 +1597,32 @@ - (void)batchDrawData:(NSData *)data #endif [self clearAll]; } else if (ClearBlockDrawType == type) { - unsigned color = *((unsigned*)bytes); bytes += sizeof(unsigned); - int row1 = *((int*)bytes); bytes += sizeof(int); - int col1 = *((int*)bytes); bytes += sizeof(int); - int row2 = *((int*)bytes); bytes += sizeof(int); - int col2 = *((int*)bytes); bytes += sizeof(int); - + struct DrawCmdClearBlock *cmd = &drawCmd.drawCmdClearBlock; #if MM_DEBUG_DRAWING - ASLogNotice(@" Clear block (%d,%d) -> (%d,%d)", row1, col1, - row2,col2); + ASLogNotice(@" Clear block (%d,%d) -> (%d,%d)", cmd->row1, cmd->col1, + cmd->row2, cmd->col2); #endif - [self clearBlockFromRow:row1 column:col1 - toRow:row2 column:col2 - color:color]; + [self clearBlockFromRow:cmd->row1 column:cmd->col1 + toRow:cmd->row2 column:cmd->col2 + color:cmd->color]; } else if (DeleteLinesDrawType == type) { - unsigned color = *((unsigned*)bytes); bytes += sizeof(unsigned); - int row = *((int*)bytes); bytes += sizeof(int); - int count = *((int*)bytes); bytes += sizeof(int); - int bot = *((int*)bytes); bytes += sizeof(int); - int left = *((int*)bytes); bytes += sizeof(int); - int right = *((int*)bytes); bytes += sizeof(int); - + struct DrawCmdDeleteLines *cmd = &drawCmd.drawCmdDeleteLines; #if MM_DEBUG_DRAWING - ASLogNotice(@" Delete %d line(s) from %d", count, row); + ASLogNotice(@" Delete %d line(s) from %d", cmd->count, cmd->row); #endif - [self deleteLinesFromRow:row lineCount:count - scrollBottom:bot left:left right:right - color:color]; + [self deleteLinesFromRow:cmd->row lineCount:cmd->count + scrollBottom:cmd->bot left:cmd->left right:cmd->right + color:cmd->color]; } else if (DrawSignDrawType == type) { - int strSize = *((int*)bytes); bytes += sizeof(int); + struct DrawCmdDrawSign *cmd = &drawCmd.drawCmdDrawSign; NSString *imgName = - [NSString stringWithUTF8String:(const char*)bytes]; - bytes += strSize; - - int col = *((int*)bytes); bytes += sizeof(int); - int row = *((int*)bytes); bytes += sizeof(int); - int width = *((int*)bytes); bytes += sizeof(int); - int height = *((int*)bytes); bytes += sizeof(int); + [NSString stringWithUTF8String:cmd->imgName]; NSImage *signImg = [helper signImageForName:imgName]; - NSRect r = [self rectForRow:row - column:col - numRows:height - numColumns:width]; + NSRect r = [self rectForRow:cmd->row + column:cmd->col + numRows:cmd->height + numColumns:cmd->width]; if (cgLayerEnabled) { CGContextRef context = [self getCGContext]; CGImageRef cgImage = [signImg CGImageForProposedRect:&r @@ -1177,23 +1637,14 @@ - (void)batchDrawData:(NSData *)data } [self setNeedsDisplayCGLayerInRect:r]; } else if (DrawStringDrawType == type) { - int bg = *((int*)bytes); bytes += sizeof(int); - int fg = *((int*)bytes); bytes += sizeof(int); - int sp = *((int*)bytes); bytes += sizeof(int); - int row = *((int*)bytes); bytes += sizeof(int); - int col = *((int*)bytes); bytes += sizeof(int); - int cells = *((int*)bytes); bytes += sizeof(int); - int flags = *((int*)bytes); bytes += sizeof(int); - int len = *((int*)bytes); bytes += sizeof(int); - UInt8 *s = (UInt8 *)bytes; bytes += len; - + struct DrawCmdDrawString *cmd = &drawCmd.drawCmdDrawString; #if MM_DEBUG_DRAWING ASLogNotice(@" Draw string len=%d row=%d col=%d flags=%#x", - len, row, col, flags); + cmd->len, cmd->row, cmd->col, cmd->flags); #endif // Convert UTF-8 chars to UTF-16 - CFStringRef sref = CFStringCreateWithBytesNoCopy(NULL, s, len, + CFStringRef sref = CFStringCreateWithBytesNoCopy(NULL, cmd->str, cmd->len, kCFStringEncodingUTF8, false, kCFAllocatorNull); if (sref == NULL) { ASLogWarn(@"Conversion error: some text may not be rendered"); @@ -1209,11 +1660,11 @@ - (void)batchDrawData:(NSData *)data } [self drawString:unichars length:unilength - atRow:row column:col cells:cells - withFlags:flags - foregroundColor:fg - backgroundColor:bg - specialColor:sp]; + atRow:cmd->row column:cmd->col cells:cmd->cells + withFlags:cmd->flags + foregroundColor:cmd->fg + backgroundColor:cmd->bg + specialColor:cmd->sp]; if (buffer) { free(buffer); @@ -1221,55 +1672,35 @@ - (void)batchDrawData:(NSData *)data } CFRelease(sref); } else if (InsertLinesDrawType == type) { - unsigned color = *((unsigned*)bytes); bytes += sizeof(unsigned); - int row = *((int*)bytes); bytes += sizeof(int); - int count = *((int*)bytes); bytes += sizeof(int); - int bot = *((int*)bytes); bytes += sizeof(int); - int left = *((int*)bytes); bytes += sizeof(int); - int right = *((int*)bytes); bytes += sizeof(int); - + struct DrawCmdInsertLines *cmd = &drawCmd.drawCmdInsertLines; #if MM_DEBUG_DRAWING - ASLogNotice(@" Insert %d line(s) at row %d", count, row); + ASLogNotice(@" Insert %d line(s) at row %d", cmd->count, cmd->row); #endif - [self insertLinesAtRow:row lineCount:count - scrollBottom:bot left:left right:right - color:color]; + [self insertLinesAtRow:cmd->row lineCount:cmd->count + scrollBottom:cmd->bot left:cmd->left right:cmd->right + color:cmd->color]; } else if (DrawCursorDrawType == type) { - unsigned color = *((unsigned*)bytes); bytes += sizeof(unsigned); - int row = *((int*)bytes); bytes += sizeof(int); - int col = *((int*)bytes); bytes += sizeof(int); - int shape = *((int*)bytes); bytes += sizeof(int); - int percent = *((int*)bytes); bytes += sizeof(int); - + struct DrawCmdDrawCursor *cmd = &drawCmd.drawCmdDrawCursor; #if MM_DEBUG_DRAWING - ASLogNotice(@" Draw cursor at (%d,%d)", row, col); + ASLogNotice(@" Draw cursor at (%d,%d)", cmd->row, cmd->col); #endif - [self drawInsertionPointAtRow:row column:col shape:shape - fraction:percent - color:color]; + [self drawInsertionPointAtRow:cmd->row column:cmd->col shape:cmd->shape + fraction:cmd->percent + color:cmd->color]; } else if (DrawInvertedRectDrawType == type) { - int row = *((int*)bytes); bytes += sizeof(int); - int col = *((int*)bytes); bytes += sizeof(int); - int nr = *((int*)bytes); bytes += sizeof(int); - int nc = *((int*)bytes); bytes += sizeof(int); - /*int invert = *((int*)bytes);*/ bytes += sizeof(int); - + struct DrawCmdDrawInvertedRect *cmd = &drawCmd.drawCmdDrawInvertedRect; #if MM_DEBUG_DRAWING ASLogNotice(@" Draw inverted rect: row=%d col=%d nrows=%d " - "ncols=%d", row, col, nr, nc); + "ncols=%d", cmd->row, cmd->col, cmd->nr, cmd->nc); #endif - [self drawInvertedRectAtRow:row column:col numRows:nr - numColumns:nc]; + [self drawInvertedRectAtRow:cmd->row column:cmd->col numRows:cmd->nr + numColumns:cmd->nc]; } else if (SetCursorPosDrawType == type) { // TODO: This is used for Voice Over support in MMTextView, // MMCoreTextView currently does not support Voice Over. #if MM_DEBUG_DRAWING - int row = *((int*)bytes); bytes += sizeof(int); - int col = *((int*)bytes); bytes += sizeof(int); - ASLogNotice(@" Set cursor row=%d col=%d", row, col); -#else - /*cursorRow = *((int*)bytes);*/ bytes += sizeof(int); - /*cursorCol = *((int*)bytes);*/ bytes += sizeof(int); + struct DrawCmdSetCursorPos *cmd = &drawCmd.drawCmdSetCursorPos; + ASLogNotice(@" Set cursor row=%d col=%d", cmd->row, cmd->col); #endif } else { ASLogWarn(@"Unknown draw type (type=%d)", type); diff --git a/src/MacVim/MacVim.h b/src/MacVim/MacVim.h index e7fae69f85..c7cb18b3ba 100644 --- a/src/MacVim/MacVim.h +++ b/src/MacVim/MacVim.h @@ -277,6 +277,8 @@ enum { SetCursorPosDrawType, DrawInvertedRectDrawType, DrawSignDrawType, + + InvalidDrawType = -1 }; enum {