Skip to content

Commit 2e96154

Browse files
committed
WIP: mouse cursor
1 parent 8d17589 commit 2e96154

6 files changed

Lines changed: 258 additions & 58 deletions

File tree

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,3 +52,4 @@ test_crash_handler
5252
RELEASE_v*
5353

5454
.github/drafts
55+
amon10/**

HELP.md

Lines changed: 16 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -173,12 +173,25 @@ These are set by the `bin/analog` wrapper script. You typically don't need to se
173173

174174
### Display Options
175175

176-
- **`CHIPMUNK_CURSOR_SCALE`**: Scale factor for mouse cursor size (1-4). Useful for high-DPI displays where the default 16x16 cursors are too small. Default is 2 (2x normal size).
176+
- **`CHIPMUNK_CURSOR_SCALE`**: Scale factor for Chipmunk's *bitmap* mouse cursors (1-4). Useful for high-DPI displays where the classic 16x16 cursors are too small. Default is 2 (2x normal size) when bitmap cursors are enabled.
177+
- **Note**: As of 2025, the default cursor backend uses the X server’s native font cursor (for better visibility and compatibility on modern X/WSL), so `CHIPMUNK_CURSOR_SCALE` has no effect unless bitmap cursors are explicitly enabled with `CHIPMUNK_USE_BITMAP_CURSOR=1`.
177178
```bash
178-
CHIPMUNK_CURSOR_SCALE=2 analog circuit.lgf # 2x size (32x32 pixels)
179-
CHIPMUNK_CURSOR_SCALE=3 analog circuit.lgf # 3x size (48x48 pixels)
179+
# Enable classic bitmap cursors and scale them to 2x:
180+
CHIPMUNK_USE_BITMAP_CURSOR=1 CHIPMUNK_CURSOR_SCALE=2 analog circuit.lgf
180181
```
181182

183+
- **`CHIPMUNK_USE_BITMAP_CURSOR`**: Force use of the classic Chipmunk bitmap cursors instead of the default X font cursor. When set to a non-zero value, enables `CHIPMUNK_CURSOR_SCALE` and restores the original arrow/copy/delete/probe cursor shapes.
184+
185+
These cursors have the bug that any redraw (SPACE or scope trace reaching end) makes the cursors disappear until mouse-exit and mouse-enter event. It is a WIP to fix this bug.
186+
```bash
187+
CHIPMUNK_USE_BITMAP_CURSOR=1 analog circuit.lgf
188+
```
189+
190+
- **X11 cursor size (desktop-level)**: Because the default Chipmunk cursor is now the X server’s own pointer, its size is generally controlled by your X/desktop environment rather than by Chipmunk itself.
191+
- On some Linux desktops that use **Xcursor**, you *may* be able to influence the global cursor size (for example via `XCURSOR_SIZE` or desktop-specific settings), but behavior varies widely by distro/compositor and is not guaranteed.
192+
- On WSLg and many other modern X/Wayland stacks, there is currently no reliable, documented way for Chipmunk to change the size of the default X pointer from inside the container/VM; in those environments the effective cursor size is determined by the host’s graphics stack.
193+
- If you need a larger pointer *inside* Chipmunk regardless of the host, use `CHIPMUNK_USE_BITMAP_CURSOR=1` together with `CHIPMUNK_CURSOR_SCALE` to control the size of Chipmunk’s own bitmap cursors (noting the redraw bug described above).
194+
182195
- **`CHIPMUNK_MODE`**: Automatically set to `analog` by the wrapper script. Affects window naming (main window shows "analog" instead of "log").
183196

184197
### Debug Options

log/src/log.c

Lines changed: 142 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1623,6 +1623,21 @@ short x, y;
16231623
/*= =*/
16241624
/*================================================*/
16251625

1626+
/* Optional debug/override for XOR cursor rendering.
1627+
* If CHIPMUNK_DISABLE_XOR_CURSOR is set (non-zero), we skip all XOR drawing
1628+
* and rely solely on the X hardware cursor (set via choose_log_cursor()).
1629+
* This is useful to isolate cursor visibility problems on modern X/WSL. */
1630+
static int xor_cursor_disabled = -1;
1631+
1632+
static int xor_disabled(void)
1633+
{
1634+
if (xor_cursor_disabled < 0) {
1635+
const char *env = getenv("CHIPMUNK_DISABLE_XOR_CURSOR");
1636+
xor_cursor_disabled = (env && *env && *env != '0') ? 1 : 0;
1637+
}
1638+
return xor_cursor_disabled;
1639+
}
1640+
16261641
Static Void xorcursor()
16271642
{
16281643
long curcm;
@@ -1632,6 +1647,59 @@ Static Void xorcursor()
16321647
gg.cx_max = across;
16331648
gg.cy_min = 0;
16341649
gg.cy_max = baseline - 1;
1650+
1651+
/* Debug: optionally bypass XOR drawing entirely to see if cursor visibility
1652+
* issues are caused by the soft XOR cursor rather than the X hardware cursor.
1653+
*/
1654+
if (xor_disabled()) {
1655+
if (getenv("CHIPMUNK_DEBUG_MOUSE"))
1656+
fprintf(stderr,
1657+
"[MOUSE-XOR] xorcursor disabled, type=%d chair=%d rband=%d\n",
1658+
(int)cursortype, (int)chairflag, (int)rbandflag);
1659+
/* Just ensure the hardware cursor shape matches the logical mode. */
1660+
if (!chairflag || cursortype == grid) {
1661+
if (cursortype != normal && cursy < baseline) {
1662+
switch (cursortype) {
1663+
1664+
case grid:
1665+
choose_log_cursor(0);
1666+
break;
1667+
1668+
case delete__:
1669+
choose_log_cursor(2);
1670+
break;
1671+
1672+
case copy_:
1673+
choose_log_cursor(1);
1674+
break;
1675+
1676+
case boxcursor:
1677+
choose_log_cursor(4);
1678+
break;
1679+
1680+
case paste:
1681+
choose_log_cursor(0);
1682+
break;
1683+
1684+
default:
1685+
break;
1686+
}
1687+
} else {
1688+
if (rabbits && !avoidrabbits && cursy < baseline) {
1689+
choose_log_cursor(0);
1690+
} else if (gg.probemode) {
1691+
choose_log_cursor(3);
1692+
} else {
1693+
choose_log_cursor(0);
1694+
}
1695+
}
1696+
} else {
1697+
choose_log_cursor(0);
1698+
}
1699+
oldcursortype = cursortype;
1700+
return;
1701+
}
1702+
16351703
curcm = m_curcolormode();
16361704
m_colormode((long)m_xor);
16371705
m_color((long)gg.color.cursor);
@@ -1640,9 +1708,9 @@ Static Void xorcursor()
16401708
switch (cursortype) {
16411709

16421710
case grid:
1711+
/* Grid mode: hardware cursor is the normal arrow; overlay
1712+
* a small XOR crosshair and full-screen grid lines. */
16431713
choose_log_cursor(0);
1644-
/* p2c: log.text, line 1308:
1645-
* Warning: Symbol 'CHOOSE_LOG_CURSOR' is not defined [221] */
16461714
if (chairflag) {
16471715
m_drawline(cursx1 - 3L, cursy1 - 5L, cursx1 + 3L, cursy1 + 5L);
16481716
m_drawline(cursx1 - 3L, cursy1 + 5L, cursx1 + 3L, cursy1 - 5L);
@@ -1654,27 +1722,34 @@ Static Void xorcursor()
16541722
break;
16551723

16561724
case delete__:
1657-
choose_log_cursor(2);
1658-
/* p2c: log.text, line 1343:
1659-
* Warning: Symbol 'CHOOSE_LOG_CURSOR' is not defined [221] */
1725+
/* Delete mode: keep the standard hardware cursor; draw an XOR 'X'
1726+
* around the pointer so it is clearly distinguishable. */
1727+
choose_log_cursor(0);
1728+
m_drawline((long)(cursx - 5), (long)(cursy - 5),
1729+
(long)(cursx + 5), (long)(cursy + 5));
1730+
m_drawline((long)(cursx - 5), (long)(cursy + 5),
1731+
(long)(cursx + 5), (long)(cursy - 5));
16601732
break;
16611733

16621734
case copy_:
1663-
choose_log_cursor(1);
1664-
/* p2c: log.text, line 1357:
1665-
* Warning: Symbol 'CHOOSE_LOG_CURSOR' is not defined [221] */
1735+
/* Copy mode: keep the standard hardware cursor; draw a long XOR
1736+
* arrow tail to indicate copy direction. */
1737+
choose_log_cursor(0);
1738+
m_drawline((long)cursx, (long)cursy,
1739+
(long)(cursx + 16), (long)(cursy - 4));
1740+
m_drawline((long)cursx, (long)cursy,
1741+
(long)(cursx + 16), (long)(cursy + 4));
16661742
break;
16671743

16681744
case boxcursor:
1669-
choose_log_cursor(4);
1670-
/* p2c: log.text, line 1374:
1671-
* Warning: Symbol 'CHOOSE_LOG_CURSOR' is not defined [221] */
1745+
/* Box mode: hardware cursor is normal arrow; the selection
1746+
* rectangle is drawn via chairflag/rbandflag handling below. */
1747+
choose_log_cursor(0);
16721748
break;
16731749

16741750
case paste:
1751+
/* Paste mode: show a XOR rectangle preview as before. */
16751752
choose_log_cursor(0);
1676-
/* p2c: log.text, line 1380:
1677-
* Warning: Symbol 'CHOOSE_LOG_CURSOR' is not defined [221] */
16781753
cx0 = (cursx + gg.xoff + 2) / gg.scale;
16791754
cy0 = (cursy + gg.yoff + 2) / gg.scale;
16801755
m_saveclip();
@@ -1693,37 +1768,30 @@ Static Void xorcursor()
16931768
} else {
16941769
if (rabbits && !avoidrabbits && cursy < baseline) {
16951770
choose_log_cursor(0);
1696-
/* p2c: log.text, line 1402:
1697-
* Warning: Symbol 'CHOOSE_LOG_CURSOR' is not defined [221] */
16981771
if (cursx < 45)
16991772
m_bunny(0L, cursy - 17L);
17001773
else
17011774
m_bunny(cursx - 45L, cursy - 17L);
17021775
} else if (gg.probemode) {
1703-
choose_log_cursor(3);
1704-
/* p2c: log.text, line 1420:
1705-
* Warning: Symbol 'CHOOSE_LOG_CURSOR' is not defined [221] */
1776+
/* Probe mode: keep normal hardware cursor; highlight probed
1777+
* node with other on-screen indicators. */
1778+
choose_log_cursor(0);
17061779
} else {
1780+
/* Normal mode: standard arrow only. */
17071781
choose_log_cursor(0);
1708-
/* p2c: log.text, line 1436:
1709-
* Warning: Symbol 'CHOOSE_LOG_CURSOR' is not defined [221] */
17101782
}
17111783
}
17121784
}
17131785
if (chairflag) {
17141786
if (rbandflag) {
17151787
choose_log_cursor(0);
1716-
/* p2c: log.text, line 1446:
1717-
* Warning: Symbol 'CHOOSE_LOG_CURSOR' is not defined [221] */
17181788
if (vlsi)
17191789
m_color((long)gg.color.wire[curwcolor - log_wcol_normal]);
17201790
else
17211791
m_color((long)gg.color.xwire);
17221792
m_drawline((long)cursx1, (long)cursy1, (long)cursx2, (long)cursy2);
17231793
} else {
17241794
choose_log_cursor(0);
1725-
/* p2c: log.text, line 1460:
1726-
* Warning: Symbol 'CHOOSE_LOG_CURSOR' is not defined [221] */
17271795
}
17281796
}
17291797
oldcursortype = cursortype;
@@ -1743,8 +1811,12 @@ Static Void xorcursor()
17431811

17441812
Static Void hidecursor()
17451813
{
1746-
if (cursorflag)
1814+
if (cursorflag) {
1815+
if (getenv("CHIPMUNK_DEBUG_MOUSE"))
1816+
fprintf(stderr, "[MOUSE-XOR] hidecursor (cursorflag=1, hide=%d)\n",
1817+
(int)cursorhide);
17471818
xorcursor();
1819+
}
17481820
}
17491821

17501822

@@ -1806,10 +1878,17 @@ short x, y;
18061878
rbandflag = (abs(cursx1 - cursx2) + abs(cursy1 - cursy2) >= gg.scale &&
18071879
hvline(cursx1, cursy1, &cursx2, &cursy2));
18081880
}
1881+
if (getenv("CHIPMUNK_DEBUG_MOUSE"))
1882+
fprintf(stderr, "[MOUSE] drawcursor move/show to (%d,%d), type=%d\n",
1883+
(int)x, (int)y, (int)cursortype);
18091884
xorcursor();
18101885
} else {
1811-
if (!cursorflag && !cursorhide)
1886+
if (!cursorflag && !cursorhide) {
1887+
if (getenv("CHIPMUNK_DEBUG_MOUSE"))
1888+
fprintf(stderr, "[MOUSE] drawcursor re-show at (%d,%d), type=%d\n",
1889+
(int)x, (int)y, (int)cursortype);
18121890
xorcursor();
1891+
}
18131892
}
18141893
cursorflag = true;
18151894
cursorhide = false;
@@ -1826,8 +1905,11 @@ short x, y;
18261905
Static Void remcursor()
18271906
{
18281907
prevcursorflag = cursorflag;
1829-
if (cursorflag)
1908+
if (cursorflag) {
1909+
if (getenv("CHIPMUNK_DEBUG_MOUSE"))
1910+
fprintf(stderr, "[MOUSE-XOR] remcursor (cursorflag=1)\n");
18301911
xorcursor();
1912+
}
18311913
cursorflag = false;
18321914
cursorhide = false;
18331915
}
@@ -3002,6 +3084,16 @@ Static Void clearscreen()
30023084
resetmessages();
30033085
bottomcount = 0;
30043086
gg.showpage = 0;
3087+
3088+
/* After a full clear, ensure the logical cursor is visible again at the
3089+
* current pen position if the pen is near the drawing area. Without this,
3090+
* the cursor can remain invisible until the next explicit mouse movement. */
3091+
if (gg.t.near_) {
3092+
if (getenv("CHIPMUNK_DEBUG_MOUSE"))
3093+
fprintf(stderr, "[MOUSE] drawcursor after clearscreen at (%ld,%ld)\n",
3094+
(long)gg.t.x, (long)gg.t.y);
3095+
drawcursor((short)gg.t.x, (short)gg.t.y);
3096+
}
30053097
}
30063098

30073099

@@ -3118,6 +3210,7 @@ Static Void pen()
31183210
m_init_pen(tabletaddr);
31193211
m_alpha_on();
31203212
ENDTRY(try3);
3213+
31213214
if (snapflag && gg.incircuit) {
31223215
gg.t.x = (gg.t.x + gg.hscale + gg.xoff) / gg.scale * gg.scale - gg.xoff;
31233216
gg.t.y = (gg.t.y + gg.hscale + gg.yoff) / gg.scale * gg.scale - gg.yoff;
@@ -5281,6 +5374,16 @@ Static Void refrscreen1()
52815374
resetmessages();
52825375
refresh();
52835376
gg.refrflag = true;
5377+
5378+
/* Redraw the cursor after a full working-area refresh so it doesn't
5379+
* remain hidden until the user moves the mouse or leaves/re-enters
5380+
* the window. */
5381+
if (gg.t.near_) {
5382+
if (getenv("CHIPMUNK_DEBUG_MOUSE"))
5383+
fprintf(stderr, "[MOUSE] drawcursor after refrscreen1 at (%ld,%ld)\n",
5384+
(long)gg.t.x, (long)gg.t.y);
5385+
drawcursor((short)gg.t.x, (short)gg.t.y);
5386+
}
52845387
}
52855388

52865389

@@ -5300,6 +5403,9 @@ Static Void refrscreen()
53005403
bottomcount = 0;
53015404
m_colormode((long)m_normal);
53025405
m_noclip();
5406+
if (getenv("CHIPMUNK_DEBUG_MOUSE"))
5407+
fprintf(stderr, "[MOUSE] refrscreen (full screen redraw) on page %d\n",
5408+
(int)gg.showpage);
53035409
fixcolormap();
53045410
clearalpha();
53055411
refrmenu();
@@ -5551,6 +5657,14 @@ Static Void pass()
55515657
log_srec *WITH3;
55525658

55535659
watchdog = timers_sysclock();
5660+
5661+
/* Ensure that the X11 hardware cursor for the main LOG window is always
5662+
* a visible shape (our arrow cursor), even if older soft-sprite code in
5663+
* the graphics library has tried to install a blank cursor. This is cheap
5664+
* and helps keep the mouse visible during heavy redraw/refresh operations,
5665+
* including scope/history updates. */
5666+
if (gg.showpage != 0)
5667+
choose_log_cursor(0);
55545668
gg.busyflag = false;
55555669
gg.oldsimstate = gg.simstate;
55565670
gg.oldsimstatetool = gg.simstatetool;
@@ -16767,7 +16881,7 @@ Static Void haproc3()
1676716881
typedef short ararr[log_million];
1676816882

1676916883

16770-
/* Local variables for historycommand: */
16884+
/* Local variables for historycommand / scope view: */
1677116885
struct LOC_historycommand {
1677216886
boolean rflag, oldtrigger, oldreset, oldonoff, oldfast;
1677316887
short oldgridmode, oldgridwhich, gridx1, gridy1, gridx2, gridy2, valuey,

0 commit comments

Comments
 (0)