Metal: keep cursor animations alive while Emacs idles
Cursor-only motion takes redisplay's fast path: no render cycle opens, and the CADisplayLink stops firing while Emacs idles, so with animations enabled the moved cursor overlay was never presented (it appeared frozen until the next input event) and rings/trails never animated between events. Three fixes: note_cursor composites immediately when no cycle is open; the animation step is factored into tickWithDt: and exposed as mtl-anim-tick, driven by a 30fps Lisp timer while animations are enabled (the same mechanism that drives video playback); and burst particles use the real cursor color instead of a hardcoded pale tint that was invisible on light backgrounds.
This commit is contained in:
parent
32b41ab8be
commit
27a98a1c36
4 changed files with 69 additions and 3 deletions
21
lisp/mtl.el
21
lisp/mtl.el
|
|
@ -146,7 +146,8 @@ cursor effects, particles and the 60fps compositor."
|
|||
:set (lambda (sym val)
|
||||
(set-default sym val)
|
||||
(when (fboundp 'mtl-animations)
|
||||
(mtl-animations val)))
|
||||
(mtl-animations val)
|
||||
(when val (mtl--anim-pump-start))))
|
||||
:group 'mtl)
|
||||
|
||||
(defcustom mtl-enable-on-startup nil
|
||||
|
|
@ -179,13 +180,31 @@ The NS backend still handles events, menus, and scrollbars."
|
|||
(mtl-scroll-duration mtl-scroll-duration)
|
||||
(mtl-trail-length mtl-trail-length)
|
||||
(mtl-animations mtl-animations-enabled)
|
||||
(when mtl-animations-enabled (mtl--anim-pump-start))
|
||||
(message "Metal GPU enabled on frame: %s (device: %s, animations: %s)"
|
||||
f (mtl-device-name) (if mtl-animations-enabled "on" "off"))))
|
||||
|
||||
(defvar mtl--anim-timer nil
|
||||
"30fps timer driving cursor animations while they are enabled.")
|
||||
|
||||
(defun mtl--anim-pump ()
|
||||
"Advance GPU cursor animations; cancel the timer when they turn off."
|
||||
(unless (and (fboundp 'mtl-anim-tick) (mtl-anim-tick))
|
||||
(when (timerp mtl--anim-timer) (cancel-timer mtl--anim-timer))
|
||||
(setq mtl--anim-timer nil)))
|
||||
|
||||
(defun mtl--anim-pump-start ()
|
||||
"Start the animation pump timer (idempotent).
|
||||
Emacs's event loop starves the CADisplayLink while idle, so without
|
||||
this Lisp timer the cursor effects only animate during user input."
|
||||
(unless (timerp mtl--anim-timer)
|
||||
(setq mtl--anim-timer (run-at-time 0 0.033 #'mtl--anim-pump))))
|
||||
|
||||
(defun mtl-toggle-animations ()
|
||||
"Toggle the Metal GPU animation layer on or off."
|
||||
(interactive)
|
||||
(setopt mtl-animations-enabled (not mtl-animations-enabled))
|
||||
(when mtl-animations-enabled (mtl--anim-pump-start))
|
||||
(message "Metal animations %s" (if mtl-animations-enabled "enabled" "disabled")))
|
||||
|
||||
(defun mtl-status ()
|
||||
|
|
|
|||
26
src/mtlfns.m
26
src/mtlfns.m
|
|
@ -581,6 +581,31 @@ otherwise (letting the timer cancel itself). */)
|
|||
return ok ? Qt : Qnil;
|
||||
}
|
||||
|
||||
DEFUN ("mtl-anim-tick", Fmtl_anim_tick, Smtl_anim_tick, 0, 2, 0,
|
||||
doc: /* Advance the GPU cursor animations one step and present.
|
||||
DT is the step in seconds (default 0.033). Driven by a Lisp timer while
|
||||
animations are enabled: Emacs's event loop starves the CADisplayLink
|
||||
when idle, so rings/trails would freeze between input events otherwise
|
||||
(same mechanism as `mtl-video-tick'). FRAME defaults to the selected
|
||||
frame. Returns t while animations are enabled, nil otherwise. */)
|
||||
(Lisp_Object dt, Lisp_Object frame)
|
||||
{
|
||||
if (NILP (frame)) frame = Fselected_frame ();
|
||||
if (!FRAME_LIVE_P (XFRAME (frame))) return Qnil;
|
||||
if (!g_mtl_animations_enabled) return Qnil;
|
||||
MtlFrameData *fd = mtl_get_frame_data (XFRAME (frame));
|
||||
if (!fd || !fd.animator) return Qnil;
|
||||
|
||||
float step = 0.033f;
|
||||
if (NUMBERP (dt))
|
||||
step = (float) XFLOATINT (dt);
|
||||
block_input ();
|
||||
if (!fd.encoder)
|
||||
[fd.animator tickWithDt:step];
|
||||
unblock_input ();
|
||||
return Qt;
|
||||
}
|
||||
|
||||
void
|
||||
syms_of_mtlfns (void)
|
||||
{
|
||||
|
|
@ -606,6 +631,7 @@ syms_of_mtlfns (void)
|
|||
defsubr (&Smtl_video_pause);
|
||||
defsubr (&Smtl_video_move);
|
||||
defsubr (&Smtl_video_tick);
|
||||
defsubr (&Smtl_anim_tick);
|
||||
}
|
||||
|
||||
#endif /* HAVE_MTL */
|
||||
|
|
|
|||
|
|
@ -148,6 +148,10 @@ typedef struct mtl_spring {
|
|||
/* Called every animation tick (CADisplayLink target) */
|
||||
- (void)animationTick:(CADisplayLink *)link;
|
||||
|
||||
/* One animation step + composite, drivable from a Lisp timer (the display
|
||||
link starves while Emacs idles). */
|
||||
- (void)tickWithDt:(float)dt;
|
||||
|
||||
/* Spawn particles at cursor for pixiedust/sonicboom/ripple modes */
|
||||
- (void)spawnParticlesAtX:(float)x y:(float)y;
|
||||
|
||||
|
|
|
|||
|
|
@ -972,13 +972,24 @@ mtl_log_seq_p (void)
|
|||
p->vy = sinf(angle) * speed;
|
||||
p->age = 0.0f;
|
||||
p->size = (self.cursorMode == MTL_CURSOR_SONICBOOM) ? 6.0f : 3.0f;
|
||||
p->color = 0x88C0D0; /* Nord frost */
|
||||
/* Real cursor color: a hardcoded pale tint was invisible on light
|
||||
backgrounds. */
|
||||
p->color = self.cursorColor ? self.cursorColor : 0x88C0D0;
|
||||
}
|
||||
}
|
||||
|
||||
- (void)animationTick:(CADisplayLink *)link
|
||||
{
|
||||
float dt = (float)link.duration;
|
||||
[self tickWithDt:(float) link.duration];
|
||||
}
|
||||
|
||||
/* One animation step + composite. Factored out of the CADisplayLink
|
||||
callback so a Lisp-level timer can drive it too: Emacs's event loop
|
||||
starves the display link while idle (it stops firing after a couple of
|
||||
ticks), so timer-driven cursor movements would spawn rings/trails that
|
||||
never animate. Same medicine as video playback (mtl-video-tick). */
|
||||
- (void)tickWithDt:(float)dt
|
||||
{
|
||||
MtlFrameData *fd = mtl_get_frame_data (self.emacsFrame);
|
||||
if (!fd || !fd.metalLayer) return;
|
||||
|
||||
|
|
@ -2528,6 +2539,12 @@ mtl_drv_note_cursor (struct frame *f, int x, int y, int w, int h,
|
|||
return false;
|
||||
fd.animator.cursorColor = color;
|
||||
[fd.animator setCursorX:x y:y width:w height:h];
|
||||
/* Cursor-only motion takes redisplay's fast path: no render cycle gets
|
||||
opened, so nothing would present the moved overlay (the display link
|
||||
does not fire while Emacs idles). Composite now so the cursor is
|
||||
never left painted at its old position. */
|
||||
if (!fd.encoder)
|
||||
[fd compositeToScreen];
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue