|
| 1 | +From ebfa4855d7a323ebc3424936d6a3df812f74967a Mon Sep 17 00:00:00 2001 |
| 2 | +From: Mingkai Dong <mk@dong.mk> |
| 3 | +Date: Sat, 5 Apr 2025 00:27:15 +0800 |
| 4 | +Subject: [PATCH] Add smooth cursor animation |
| 5 | + |
| 6 | +This adds and extends the patch from ksqsf. |
| 7 | +https://github.com/ksqsf/emacsmoe/pull/7 |
| 8 | +--- |
| 9 | + lisp/term/ns-win.el | 7 ++++++ |
| 10 | + src/nsterm.h | 1 + |
| 11 | + src/nsterm.m | 59 +++++++++++++++++++++++++++++++++++++++++++++ |
| 12 | + 3 files changed, 67 insertions(+) |
| 13 | + |
| 14 | +diff --git a/lisp/term/ns-win.el b/lisp/term/ns-win.el |
| 15 | +index 46639f2422b..8c606c441af 100644 |
| 16 | +--- a/lisp/term/ns-win.el |
| 17 | ++++ b/lisp/term/ns-win.el |
| 18 | +@@ -65,6 +65,13 @@ x-command-line-resources |
| 19 | + ;; nsterm.m. |
| 20 | + (defvar ns-input-file) |
| 21 | + |
| 22 | ++(defcustom ns-cursor-animation-duration 0.1 |
| 23 | ++ "Duration in seconds for cursor animation on macOS. |
| 24 | ++This controls how long the cursor animation takes when changing position or style. |
| 25 | ++A value of 0 disables animation." |
| 26 | ++ :type 'number |
| 27 | ++ :group 'ns) |
| 28 | ++ |
| 29 | + (defun ns-handle-nxopen (_switch &optional temp) |
| 30 | + (setq unread-command-events (append unread-command-events |
| 31 | + (if temp '(ns-open-temp-file) |
| 32 | +diff --git a/src/nsterm.h b/src/nsterm.h |
| 33 | +index 2abf402f8bc..33338370537 100644 |
| 34 | +--- a/src/nsterm.h |
| 35 | ++++ b/src/nsterm.h |
| 36 | +@@ -485,6 +485,7 @@ #define NSTRACE_UNSILENCE() |
| 37 | + struct frame *emacsframe; |
| 38 | + int scrollbarsNeedingUpdate; |
| 39 | + NSRect ns_userRect; |
| 40 | ++ CALayer *cursor_layer; |
| 41 | + } |
| 42 | + |
| 43 | + /* AppKit-side interface. */ |
| 44 | +diff --git a/src/nsterm.m b/src/nsterm.m |
| 45 | +index 5514a693c86..95238209356 100644 |
| 46 | +--- a/src/nsterm.m |
| 47 | ++++ b/src/nsterm.m |
| 48 | +@@ -44,6 +44,7 @@ Updated by Christian Limpach (chris@nice.ch) |
| 49 | + |
| 50 | + #include "lisp.h" |
| 51 | + #include "blockinput.h" |
| 52 | ++#include "dispextern.h" |
| 53 | + #include "sysselect.h" |
| 54 | + #include "nsterm.h" |
| 55 | + #include "systime.h" |
| 56 | +@@ -71,6 +72,7 @@ Updated by Christian Limpach (chris@nice.ch) |
| 57 | + #include "macfont.h" |
| 58 | + #include <Carbon/Carbon.h> |
| 59 | + #include <IOSurface/IOSurface.h> |
| 60 | ++#include <QuartzCore/QuartzCore.h> |
| 61 | + #endif |
| 62 | + |
| 63 | + static EmacsMenu *dockMenu; |
| 64 | +@@ -3016,6 +3018,12 @@ Hide the window (X11 semantics) |
| 65 | + ns_unfocus (f); |
| 66 | + } |
| 67 | + |
| 68 | ++static double |
| 69 | ++ns_get_cursor_animation_duration (void) |
| 70 | ++{ |
| 71 | ++ Lisp_Object duration = Fsymbol_value (intern_c_string ("ns-cursor-animation-duration")); |
| 72 | ++ return NUMBERP (duration) ? XFLOATINT (duration) : 0.1; |
| 73 | ++} |
| 74 | + |
| 75 | + static void |
| 76 | + ns_draw_window_cursor (struct window *w, struct glyph_row *glyph_row, |
| 77 | +@@ -3101,6 +3109,45 @@ Note that CURSOR_WIDTH is meaningful only for (h)bar cursors. |
| 78 | + /* Prevent the cursor from being drawn outside the text area. */ |
| 79 | + r = NSIntersectionRect (r, ns_row_rect (w, glyph_row, TEXT_AREA)); |
| 80 | + |
| 81 | ++ /* We use the CA layer as a temporary animation layer. */ |
| 82 | ++ EmacsView *view = FRAME_NS_VIEW (f); |
| 83 | ++ CALayer *cursor_layer = view->cursor_layer; |
| 84 | ++ |
| 85 | ++ if (active_p && cursor_layer) { |
| 86 | ++ NSRect r2 = r; |
| 87 | ++ r2.origin.y = [view bounds].size.height - r2.size.height - r2.origin.y; |
| 88 | ++ cursor_glyph = get_phys_cursor_glyph (w); |
| 89 | ++ if ((cursor_glyph->resolved_level & 1) != 0) |
| 90 | ++ r2.origin.x += cursor_glyph->pixel_width - r2.size.width; |
| 91 | ++ |
| 92 | ++ switch (cursor_type) |
| 93 | ++ { |
| 94 | ++ case DEFAULT_CURSOR: |
| 95 | ++ case NO_CURSOR: |
| 96 | ++ /* Make the layer invisible. */ |
| 97 | ++ cursor_layer.opacity = 0.0; |
| 98 | ++ cursor_layer.backgroundColor = nil; |
| 99 | ++ cursor_layer.frame = r2; |
| 100 | ++ break; |
| 101 | ++ case FILLED_BOX_CURSOR: |
| 102 | ++ case HOLLOW_BOX_CURSOR: |
| 103 | ++ case HBAR_CURSOR: |
| 104 | ++ case BAR_CURSOR: |
| 105 | ++ [CATransaction begin]; |
| 106 | ++ [CATransaction setAnimationDuration:ns_get_cursor_animation_duration ()]; |
| 107 | ++ [CATransaction setCompletionBlock:^{ |
| 108 | ++ cursor_layer.backgroundColor = nil; /* hide after animation */ |
| 109 | ++ cursor_layer.opacity = 0.0; |
| 110 | ++ }]; |
| 111 | ++ cursor_layer.backgroundColor = FRAME_CURSOR_COLOR (f).CGColor; |
| 112 | ++ cursor_layer.opacity = 1.0; |
| 113 | ++ cursor_layer.frame = r2; |
| 114 | ++ [CATransaction commit]; |
| 115 | ++ break; |
| 116 | ++ } |
| 117 | ++ } |
| 118 | ++ /* Below is the original Emacs drawing code for the cursor. */ |
| 119 | ++ |
| 120 | + ns_focus (f, NULL, 0); |
| 121 | + |
| 122 | + NSGraphicsContext *ctx = [NSGraphicsContext currentContext]; |
| 123 | +@@ -9381,6 +9428,18 @@ - (instancetype) initWithEmacsFrame: (struct frame *) f |
| 124 | + [[self contentView] addSubview:view]; |
| 125 | + [self makeFirstResponder:view]; |
| 126 | + |
| 127 | ++ /* Overlay a canvas view on top of EmacsView. */ |
| 128 | ++ NSView *canvasView = [[NSView alloc] initWithFrame:view.bounds]; |
| 129 | ++ canvasView.wantsLayer = YES; |
| 130 | ++ canvasView.autoresizingMask = NSViewWidthSizable | NSViewHeightSizable; |
| 131 | ++ [view addSubview:canvasView positioned:NSWindowAbove relativeTo:nil]; |
| 132 | ++ |
| 133 | ++ /* Create a cursor layer on the canvas. */ |
| 134 | ++ view->cursor_layer = [CALayer layer]; |
| 135 | ++ [canvasView.layer addSublayer: view->cursor_layer]; |
| 136 | ++ view->cursor_layer.frame = CGRectMake(0, 0, 0, 0); |
| 137 | ++ |
| 138 | ++ |
| 139 | + #if !defined (NS_IMPL_COCOA) || MAC_OS_X_VERSION_MIN_REQUIRED <= 1090 |
| 140 | + #if MAC_OS_X_VERSION_MAX_ALLOWED > 1090 |
| 141 | + if ([self respondsToSelector: @selector(useOptimizedDrawing:)]) |
| 142 | +-- |
| 143 | +2.49.0 |
| 144 | + |
0 commit comments