Notes for Hackers
     See HACKING.md for architecture details (SCREENCELL buffer model,
double-buffer rendering, popup save/restore, X11 extbyte system).
     The codebase uses C89/K&R style compiled with -std=gnu17.  POSIX
functions (poll, openpty, sigaction) are used for all new code.  All DOS
and djgpp support was removed in the Payne era.  German documentation
from the Kruse era is preserved in the archive repository at
https://codeberg.org/mendezr/xwpe-archives


Known Bugs (Payne era, verified 2026-06-01)

Confirmed present:
* (none currently tracked)

Cannot reproduce:
* Changing the maximum column and using cut & paste can crash xwpe
     (no crash; fixed by the SCREENCELL migration in 1.6.0, now guarded
     by tests/test_maxcol_paste.py: Max Columns=8 + block-copy/paste/typing
     past the limit stays alive).

Bugs fixed in 1.6.x:
* Options > Editor: checkboxes and radio buttons could not be ticked in xwpe
  (X11) -- fixed (1.6.3).  Space, mouse-click and the Alt-<letter> hotkey all
  flipped the option's value, but in X11 nothing was drawn, so the box looked
  unchanged ("I can't mark it").  Root cause: with NEWSTYLE the dialog redrew
  the marks via e_make_xrect_abs -- a 3D bevel in the extbyte overlay whose
  checked vs unchecked states differ only by one highlight bit, visually
  imperceptible.  wpe (ncurses) was never affected because it took the text
  path.  Fix: draw the Borland-style 'X' / '*' glyph in every mode, matching
  the dialog's initial draw and the ncurses path; it renders clearly under
  Xft.  Diagnosed with a real xwpe under Xvfb+matchbox driven by xdotool
  (instrumentation showed the value flipped but the redraw branch drew the
  bevel, not the glyph).  Regression test: tests/x11/test_editor_options.py.
* Options > Editor: the "WordStar block" checkbox was one column wider than
  its siblings, so the Display-column boxes did not line up -- fixed (1.6.3)
  by padding the "Show Endmark" and "Old Style" labels to equal width.
* Block marking: the "WordStar block" option had no effect from the keyboard
  -- fixed (1.6.3).  The keyboard Begin-Mark (^K B) set mark_begin inline in
  e_ctrl_k, bypassing e_blck_begin where the ED_BLOCK_WORDSTAR mode logic
  lives, so both modes behaved identically (only the Block menu honoured the
  flag).  ^K B now routes through e_blck_begin: modern (default) resets begin
  AND end so re-marking leaves no stale block; WordStar keeps the end marker
  (independent begin/end).  Test: tests/x11/test_block_marking.py.
* xwpe (X11) on macOS/XQuartz: Alt+<letter> menu accelerators (and the Alt-Q
  LSP prefix, Alt-M Make, ...) did nothing -- fixed (1.6.5).  XQuartz's default
  "Option-as-Compose" mode routes Alt+<letter> through the dead-key compose
  pipeline (Alt+E -> dead_acute; Alt+B -> the integral glyph U+222B) instead of
  delivering a plain letter + altmask, so every accelerator silently broke.
  When Alt is held and the lookup produced a composed/dead/high-byte result,
  e_x_getch now recovers the unmodified letter from the physical keycode via
  XKB and returns the AltX code the menu expects; the compose state is cleared
  so the next plain key is not turned into an accented char.  Linux X11 is
  unaffected (there Alt+<letter> already arrives as letter + altmask, so the
  branch is skipped) and dead-key compose without Alt still works.
  Tests: tests/x11/test_menu_nav.py, tests/test_menu_nav.py (the terminal
  binary always handled the menu-to-menu switch; this pins it).
* Empty desktop exposed the internal clipboard "Buffer" -- fixed (1.6.5).  When
  the last user window was closed (mxedt == 0), the main loop dispatched on
  f[0], which is the internal clipboard Buffer, drawing it as if it were a real
  editor window and leaving the previous window's status hints on the bar.  The
  loop now repaints an empty desktop (e_show_empty_desk: blank hint bar, hidden
  cursor, top menu bar live) and drives the main menu instead; picking File>New
  or the File-Manager resumes normal dispatch.  Test: tests/test_empty_desktop.py.
* wpe (ncurses): a fast mouse click could freeze a drag-tracking loop -- fixed
  (1.6.5).  Some terminal/ncurses pairs coalesce press+release into a single
  synthesized BUTTON*_CLICKED with no matching _RELEASED; the pressed bit in
  g_mouse_buttons then stayed set and any while(e_mshit()) loop (submenu, scroll
  thumb, FM resize) spun forever.  fk_t_mouse now marks a pending synthetic
  release on a _CLICKED and consumes it on the next no-event poll, so dispatch
  still observes the click but the loop exits one tick later.
* Window relayout no longer separates intentionally cascaded editors -- fixed
  (1.6.5).  On resize, e_relayout_windows runs an overlap-resolution pass that
  pushes a lower window down when a top-anchored one grew past it due to the
  minimum-height expansion.  It also fired on editors the user had deliberately
  cascaded on top of each other (already overlapping before the resize),
  shoving them apart unexpectedly.  The pass now snapshots each window's
  original a.y/e.y and skips any pair that was ALREADY overlapping (a
  user-chosen cascade, not collateral damage from expansion), and only acts on
  text editors -- popup-style windows (File Manager, Data, dropdowns) are
  intentionally drawn over editors and must never be pushed.
* macOS: setup.sh now auto-detects XQuartz and builds the X11 GUI (xwpe/xwe),
  falling back to a terminal-only build (wpe) when XQuartz is absent.  XQuartz
  ships an older cairo/fontconfig/freetype ABI than Homebrew's pango needs, so
  the script orders the brewed pkg-config and -L lib dirs ahead of XQuartz's
  (the linker honours -Wl,-search_paths_first) -- otherwise configure silently
  disabled the Cairo backend or the binary crashed in cairo at runtime.  A
  Homebrew tap formula (contrib/homebrew/) is included for `brew install`.
* New headless X11 GUI test suite (tests/x11/): drives a real xwpe under
  Xvfb + matchbox-window-manager via xdotool and asserts on screenshots
  (Pillow).  Covers the Xft rendering and X11 key/mouse path that the pyte
  (VT100) suite cannot reach.  Run with `tests/run-tests.sh --x11`; it skips
  cleanly when the X tools are absent.  A window manager is required because
  xwpe relies on one to own its window geometry (it never calls
  XResizeWindow); under bare Xvfb the X11 size handling otherwise oscillates.
* Debugging leaks memory (documented "location unknown" since 2000) -- fixed
  (1.6.3).  e_close_view() freed the window backing buffer only for sw < 2,
  so the default repaint path (sw == 2) leaked a full-screen SCREENCELL
  buffer (~19 KB) on every window-tree repaint; a debugger step repaints
  repeatedly, hence the symptom.  Now the PIC is always freed (it is always
  allocated by e_open_view); e_firstl passes the existing view so it is
  closed before the new one opens.  Found with valgrind driven through gdb
  sessions by a headless pyte VT100 harness; ~940 KB/2-sessions -> 3 bytes.
  Provenance (git archaeology in the ancestry repo): the "if (sw < 2)" guard
  is byte-for-byte identical back to Fred Kruse's 1.4.2 -- the leak is
  original to the 1990s code.  Payne only documented it ("location unknown",
  2000); the SCREENCELL migration (1.6.0) carried the same guard forward.
* Double-free regression from the leak fix -- fixed (1.6.3).  Because the
  leak fix made the repaint close each window's view, three places that
  bulk-released the views and then repainted the tree -- e_switch_window
  (Alt-<n>/F6 switch, and the F9 project path), e_ed_cascade (Window >
  Cascade) and e_ed_tile (Window > Tile) -- freed each 19 KB SCREENCELL
  block without clearing cn->f[i]->pic, so the repaint double-freed it and
  aborted with "free(): invalid pointer".  All three now route through a
  single e_free_view() helper that always NULLs the pointer, making that
  class of double-free impossible by construction.
  Test: tests/test_window_views.py.
* SIGSEGV in the X11 desktop repaint -- fixed (1.6.3).  A fourth instance
  of the same free-without-NULL bug lived in e_x_repaint_desk (we_xterm.c):
  it freed every window's view in a loop without clearing cn->f[i]->pic,
  then repainted (e_ed_kst -> e_change_pic -> e_close_view), which read the
  freed-and-reused PIC -- a use-after-free that crashed xwpe during normal
  X11 use (e.g. on a window-configure repaint).  Found from a user core
  dump (coredumpctl -> gdb: e_close_view at we_wind.c:468 with a garbage
  PIC).  Now routed through e_free_view() like the other three sites.
* Memory leaks at window close -- fixed (1.6.3).  e_close_window freed the
  window's SCHIRM but not two arrays hanging off it: s->brp (the per-screen
  breakpoint-line list, grown by the debugger) and c_sw (the per-line syntax-
  continuation state).  Both were "definitely lost" at exit, one per window.
  Found by running a full gdb trace session under valgrind, driven headless
  through a pty with a clean Alt-X exit; both are now freed in the window
  teardown, and the session reports 0 bytes definitely/indirectly lost,
  0 errors.
* X11 scrollbar bled through overlapping windows -- fixed (1.6.3).  With
  three windows stacked so one is covered by two others at once, the
  covered window's vertical/horizontal scrollbar reappeared in the
  triple-overlap band.  The fluid scrollbar chrome (we_render_cairo.c)
  clipped each window to "its rect minus the covers" with a Cairo
  even-odd path, which is XOR, not set-difference: a point under two
  higher windows lies in three rectangles -> odd -> kept.  Now subtracts
  the covers with cairo_region_t (true set algebra) via
  e_chrome_visible_region().  Also re-indexed the chrome by window
  z-level (f[w], active = f[mxedt]) to match the cell compositor, so a
  mouse click that raises a covered window no longer leaves the chrome
  drawing the wrong window's scrollbars.  Regression test:
  tests/x11/test_window_zoom_redraw.py.
* X11 no longer exits when the legacy "8x13" core font is missing -- fixed
  (1.6.3).  With Xft built in, the "8x13" bitmap font (Debian: xfonts-base)
  is only used to seed the initial point size; Xft then overrides the metrics
  and does all the drawing.  But xwpe still called exit(-1) if 8x13 (or any
  core font) could not be opened, so it would not start on a minimal X server
  with no core fonts installed.  The core font is now optional under Xft: the
  baseline offset comes from WpeXInfo.font_descent (the Xft descent when there
  is no core font), and the renderer guards the now-optional core XFontStruct.
  exit(-1) remains only in the no-Xft build, where the core font is the
  renderer.
* Heap-buffer-overflow in block Move (Ctrl-K V) -- fixed (1.6.3).  The
  multi-line block-move path (e_move_block, we_block.c) reshuffles line
  buffers by raw struct aliasing and could leave the destination line's
  length field smaller than its real content and the buffer without a NUL
  within bounds.  e_ins_nchar then trusted the stale length, its capacity
  check was fooled, and e_str_len() ran strlen one byte past the
  e_new_line()-allocated line buffer.  Fixed defensively at the insertion
  primitive: e_ins_nchar now pins a terminator at the b->mx.x capacity
  limit and recomputes the length on entry, so no caller can drive a line
  buffer overflow.  Found with the AddressSanitizer build during the soak
  test (heap-buffer-overflow READ in e_ins_nchar at we_edit.c).
  Provenance (git archaeology): the e_move_block aliasing and the
  unnormalised e_ins_nchar entry are byte-for-byte identical back to Fred
  Kruse's 1.4.2 and unchanged through Payne 1.5.30a -- a ~30-year latent
  memory-corruption bug in the original text-buffer code, not a regression
  from our refactors (the SCREENCELL migration touched the screen buffer,
  not these char line buffers).  It was silent undefined behaviour until
  AddressSanitizer made it deterministic.  Two further sites of the same
  class were then fixed: e_move_block's own e_str_len() on the split tail
  (triggered by block Move after an Undo), the identical -- and still
  unbounded -- line split in e_copy_block (block Copy), and e_copy_block's
  per-line deep-copy loop (which wrote 1-2 bytes past a full, soft-wrapped
  block line).  The block engine was then refactored to remove the
  duplication behind all of these: the line-table grow, the gap-opening
  shift, the line split, the single-line span extraction, and the per-line
  deep-copy are now five named, documented helpers (e_blk_grow_lines,
  e_blk_open_gap, e_blk_split_line, e_blk_dup_chars, e_blk_dup_line) shared
  by e_move_block and e_copy_block, so each memory-safe primitive exists in
  exactly one place and cannot drift between the two paths again.  Both
  functions now read as prose.  Guarded by tests/test_block.py and
  tests/test_block_asan.py (block Copy/Move/Undo under AddressSanitizer).
* Documentation hasn't been updated -- 12-chapter Texinfo manual (1.6.2).
* Compilers are assumed to have a -c and -o -- GNU/Other distinction (1.6.2).
* Cursor keys don't work in xterms or telnet sessions -- fixed (1.6.0).
* Certain file names like 'Messages' aren't handled -- fixed (1.6.3).
* 'make install' installs x versions even if not compiled in -- fixed (1.6.3).
* --without-x build failed (unguarded X11 symbols) -- fixed (1.6.3).
* No error for disk full (silent failure on save) -- fixed (1.6.3).
* Open Project with nonexistent .prj opened blank editor -- fixed (1.6.3).
* Add Item with no project open silently did nothing -- fixed (1.6.3).
* Shift-Tab not detected in terminal emulators -- fixed (1.6.3).
* Esc key required several presses -- fixed (1.6.3), a 20+ year bug.
  e_t_getch issued a blocking second read after Esc to detect Alt-<key>
  combos; it is now time-limited (ESC_ALT_DELAY_MS, 25 ms) so a lone Esc
  registers on the first press while Alt-<key> still works.  Verified on
  both terminal emulators and the Linux text console (outside X).
  Test: tests/test_esc_key.py.
* Dialog colors illegible on modern terminals -- fixed (1.6.3).
* Check headers ignores // comments, #if 0 blocks -- fixed (1.6.3).
  Parser rewritten with named functions: e_chk_skip_whitespace_and_comments,
  e_chk_track_conditional, e_chk_extract_include, e_chk_next_directive.
  Unit test: tests/test_checkheader.c (10 cases, make check).
* Compiler/linker output went to editor buffer instead of Messages
  -- fixed (1.6.3).  e_p_exec now routes output to Messages via
  e_find_or_create_messages.
* Cursor jumped to Messages when nothing needed recompiling
  -- fixed (1.6.3).  e_new_message moved inside compilation-needed block.
* gdb assumes program starts in "main" -- fixed (1.6.3).
  Configurable start_symbol field (default "main") in Compiler-Options
  dialog (Alt-Y).  All debugger backends (gdb, sdb, dbx, jdb) use
  e_get_start_symbol().  Ctrl-G R now sets breakpoint before running.
* X11 debugger xterm eliminated -- fixed (1.6.3).
  openpty() replaces xterm for program I/O in X11 mode (same as
  terminal mode).  No focus stealing, no external window.  Program
  output appears in Messages via pty drain after each debug step.
* Radio buttons and checkboxes invisible in X11 dialogs -- fixed (1.6.3).
  NEWSTYLE code replaced ( ) and [ ] with spaces.  Initial selection
  marker (*) was not drawn on dialog open.
* gdb "Can't find file" error when stepping into libc -- fixed (1.6.3).
  Auto-skip system frames via tbreak main + continue.
* gdb error popups after program exit -- fixed (1.6.3).
  e_read_output detects "exited" and quits debugger cleanly.
* Leaked file descriptor on named pipe (30-year Kruse bug) -- fixed.


Changes

1.6.6 (BSD portability: FreeBSD/OpenBSD/NetBSD, LSP truecolor semantic tokens)

* OpenBSD: the console truecolor path no longer breaks the link.  The
  semantic-token truecolor code calls init_extended_pair/init_extended_color
  (ncurses 6), which OpenBSD's base curses does not provide; configure now
  probes for init_extended_pair (HAVE_INIT_EXTENDED_PAIR) and the truecolor
  path compiles only when present, falling back to the 16-colour foreground
  otherwise.  No change where the symbols exist (Linux, FreeBSD, NetBSD pkgsrc).
* Build against a reentrant-built ncursesw (e.g. openSUSE Tumbleweed): do not
  assign to stdscr.  initscr() sets the global itself, and a reentrant ncursesw
  makes stdscr a non-lvalue macro, so "stdscr = initscr()" failed to compile.
* Console mouse: while mouse tracking is on, ask the terminal for an arrow
  pointer instead of the text I-beam (OSC 22; honoured by kitty/iTerm2/foot,
  ignored by Terminal.app), and restore the terminal's own cursor on exit.
* Console mouse: stop a spin when a non-mouse key arrives while a button is
  still held (its release SGR queued behind the key) -- fk_t_mouse synthesizes
  the release so the drag loop returns and the key is processed normally.
  Observed on macOS Terminal: a cursor key right after a menu-bar click.
* Documented that xwpe is fully usable without function keys -- macOS reserves
  F1-F12 for the system, but every command has a non-F-key binding (Alt-<letter>
  menus, Alt-M build / Alt-C compile / Alt-U run, the Ctrl-G debugger prefix,
  Ctrl-O find/replace).  New "Reference -> Without function keys" manual section
  and a README note for the macOS/Homebrew audience.

* Scala debug works out of the box on a machine whose default JDK is too new.
  The Scala 3 compiler crashes at start-up on JDK 24+, and scala-cli/Bloop build
  the debuggee with the JVM from JAVA_HOME/PATH -- so debug failed where the
  default java was, e.g., OpenJDK 26.  xwpe now repoints JAVA_HOME at an LTS JDK
  (17/21) before starting the Scala build server, exactly as it already did for
  Metals (one shared e_scala_pin_jdk helper, labelled per caller).  A project no
  longer needs a "//> using jvm" pin just to be debuggable.

* X11: the fluid scrollbar no longer paints over a modal box.  A dialog, picker,
  pulldown or the Alt-Q LSP menu is not a window on the editor stack, so the
  scrollbar's window-occlusion clip could not see it and the editor's bottom
  bar bled over the box; the overlay is now suppressed while any modal box is up
  (wpe_modal_active) and returns when it closes.  Console is unaffected (no
  overlay -- the box's cells simply cover the bar).

* Title-bar markers now distinguish two kinds of non-editable window: a real
  locked FILE on disk (0444, an extracted library source) keeps the padlock and
  a dimmed name, while a tool/output pane (Messages, Watches, Stack, "Metals
  Doctor", ...) gets a tool glyph instead (U+2699 gear) -- "a tool
  pane, not your document".  The padlock no longer leaks onto those panes (it
  implied "locked file", which they are not -- Messages even carries a running
  program's stdin); the kind is decided by whether the window names a file that
  exists on disk, so present and future panes are classified correctly without
  an enumerated name list.  The tool glyph is a BMP width-1 monochrome symbol so
  it renders consistently across terminals and Xft (a wide colour emoji blanked
  on some terminals and rendered tiny/misaligned in the title cell).
  A file that exists but is not writable now shows the padlock even when it is
  also UNreadable (mode 000): xwpe cannot read its contents, but it is still a
  locked file, so the lock appears instead of a silently-empty editable buffer.

* LSP semantic tokens can now use real 24-bit colour for categories the
  16-colour palette cannot express: method/function/macro names paint in a warm
  orange (#FF8C00) instead of red, and type/class names in a teal (#4EC9B0) the
  16-colour palette could not use on the Borland-blue background (it falls back
  to yellow there).  A single palette
  (we_lsp.c e_lsp_sem_truecolor / e_lsp_sem_slot_rgb) feeds both renderers.
  - Console: rendered through ncurses extended colour pairs, terminfo-gated --
    a direct-colour terminal (kitty, xterm-direct, tmux with the RGB override)
    gets the exact 24-bit colour; a 256-colour terminal that can redefine its
    palette gets it via OSC 4; anything else falls back to the 16-colour red.
    No raw escapes behind ncurses' back, so its attribute state stays in sync.
  - X11/Xft and the Cairo backend paint the exact RGB directly.
  Verified end to end with clangd (tests/test_truecolor_console.py) and a unit
  test of the palette (tests/test_lsp_truecolor.c).

BSD portability (FreeBSD, OpenBSD, NetBSD):

* Console mouse now works on terminals whose terminfo lacks an SGR-capable
  kmous -- FreeBSD's termcap console and OpenBSD's base curses.  wpe requests
  SGR (1006) mouse reporting unconditionally and decodes the raw
  ESC [ < b ; x ; y M|m reports itself, so clicks, drags and the wheel behave
  the same as on Linux/ncurses (which fold the identical reports into
  KEY_MOUSE before wpe ever sees them, so this path stays inert there -- no
  regression).  Regression test tests/test_mouse_sgr_raw.py forces the raw
  path under TERM=vt220.
* Builds and links natively on FreeBSD 14, OpenBSD 7.x and NetBSD 9 (full
  X11 build): include <sys/ioctl.h> for TIOCSCTTY; link libexecinfo for
  backtrace(); fall back to base curses / accept ncurses.pc when ncursesw.pc
  is absent; detect a usable C standard flag (gnu17 -> gnu11 on NetBSD's base
  GCC 7); accept the vterm03 pkgsrc module name; avoid @geq{} so the manual
  builds with older makeinfo.  All no-ops on Linux/glibc.
* Build: e_d_quit_basic() now returns a value on every path.  It is declared
  int but the success path fell off the end, which GCC's -Werror=return-type
  (the default on openSUSE Tumbleweed) rejects.  Callers ignore the result, so
  it returns 0 -- no behaviour change; unblocks strict-toolchain distro builds.

1.6.5 (LSP for five languages, GNU Algol 68 / ga68 toolchain, Algol 68 highlighting)

  LSP keyboard UX -- peeks at the cursor, that chain:
* Completion (Alt-Q C) and code actions (Alt-Q A) now drop their list
  AT THE CURSOR, like a real editor's autocomplete, instead of a box at
  the screen corner; the list is capped to fit the editor window.  The
  project-wide browse lists (Outline, Workspace symbols) stay a normal
  dialog -- the cursor is irrelevant to those.
* Hover, signature help and the problem messages (Alt-Q . / Alt-Q ,)
  are small tooltips at the cursor, not centred dialogs.  Each carries a
  key legend in its bottom border ("Press any key to close", or
  ". next  , prev  any other key closes") so the controls are
  discoverable; any key dismisses (the footer now says so truthfully).
* These peeks CHAIN: while a tooltip is up, Alt-Q <letter> closes it and
  runs that action in one motion (e.g. Alt-Q . to land on a problem,
  then Alt-Q A to pull up its quick-fix).  Walking problems is quicker
  too: after the first Alt-Q ., a bare . / , steps to the next/previous.
* Fixed: a whole-buffer LSP edit -- a quick-fix/format/rename and its
  Ctrl-U undo or Ctrl-R redo -- now re-syncs the document to the server,
  so the inline diagnostics re-publish for the new text.  Before, undo
  bypassed the per-keystroke Enter debounce: after undoing a quick-fix
  the re-introduced error stayed unmarked until the next Enter.
* Inlay hints (Alt-Q Y) and semantic colours (Alt-Q M) no longer freeze the
  editor.  On a cold server the first request is empty (the workspace is not
  indexed yet); the toggle used to block on a fixed 8-second wait and then
  give up.  It now turns the overlay on at once and the fd-loop fills it the
  moment the server delivers -- event-driven, no freeze (clangd is instant;
  Metals fills a moment after indexing).  A file with nothing to annotate
  (all-explicit types, no call sites) correctly shows none, without the wait.
  Covered by the clangd inlay assertion in tests/test_lsp_clangd.c and a
  non-blocking-toggle test in tests/test_clangd_lsp.py.
* Semantic colours (Alt-Q M) are now picked for contrast on the Borland-blue
  editor background.  Method/member tokens were teal -- low contrast on blue,
  read as a murky dark green -- because the palette indices had been chosen
  assuming ANSI ordering, which is not xwpe's colour order.  Methods are now
  bright red (the warmest of the 16 colours, high contrast on blue and distinct
  from variable-green, so a call like `xs.map` no longer blends into `xs`);
  strings are violet-red; types stay yellow, variables green, keywords white.
  (True orange would need a 17th colour -- a palette redefinition that does not
  pass through a typical tmux -- so the standard 16-colour red is used, which
  works on every terminal and in X11.)
* Fixed: a language- or debug-server that exited could spin the editor at
  100% CPU.  The fd-loop kept polling the dead socket, which returned
  POLLHUP-without-POLLIN forever and nothing reaped it.  wpe_fd_poll now
  dispatches on POLLHUP/POLLERR too (and drops a wake-only fd that hangs up),
  so the fd is reaped on the first poll.  Regression test tests/test_fdloop.c.

  OS clipboard integration (the last "caduco" 1993-ism):
* Cut / Copy / Paste are now ONE clipboard, shared with the OS -- the modern
  behaviour expected in 2026.  ^C / ^Ins (Copy) and Shift-Del (Cut) put the
  selection on the system clipboard; ^V / Shift-Ins (Paste) takes whatever was
  last copied in ANY app.  The old, separate, X11-only "(XBuffer)" Edit-menu
  items are gone (redundant); the internal clipboard stays inspectable with
  Show Buffer (Alt-Y).  Two backends behind one seam (we_clip.c,
  e_clip_os_set / e_clip_os_get):
  - Terminal (wpe): Copy is emitted as OSC 52 (ESC ] 52 ; c ; base64 BEL); the
    emulator performs the real OS-clipboard write, so it works locally AND over
    SSH, with no X and no helper process (kitty, foot, wezterm, iTerm2 with
    clipboard access, xterm with allowWindowOps, tmux with set-clipboard).
    Paste from another app is the emulator's own paste (Shift-Insert), which
    already delivers the bytes; ^V pastes the internal clipboard.
  - X11 (xwpe): Copy owns BOTH the PRIMARY (middle-click) and CLIPBOARD
    (Ctrl-V in GTK/Qt) selections, served as UTF8_STRING/STRING, and answers
    TARGETS -- the three gaps that made the old PRIMARY/STRING-only path fail
    with modern apps (Unicode now round-trips).  ^V pulls CLIPBOARD then PRIMARY
    when another app owns it (event-driven, poll() on the X fd; no more the old
    sleep(0)x1000 busy-loop).  Works on macOS too: in a terminal via OSC 52
    (iTerm2/kitty), and under XQuartz when its pasteboard sync is on.  Wayland
    is covered for free via XWayland.
  Tests: tests/test_clipboard.c (base64/OSC 52 framing), tests/
  test_clipboard_osc52.py (^C -> OSC 52, UTF-8 round-trip, Cut), and
  tests/x11/test_clipboard.py (xwpe copy read back with xclip from both
  selections, TARGETS lists UTF8_STRING, and ^V pulling an externally-seeded
  clipboard).

  Build / Run reliability:
* Fixed: Run -> Execute Make (Alt-A) showed only the echoed "make" and lost the
  build output.  Compilers write diagnostics to stderr (captured fine), but make
  writes its progress to stdout, which the old temp-file + wait-then-read path
  block-buffered and dropped.  Command output is now drained with poll() while
  the child runs -- each line lands in Messages live and the scrollbar tracks
  the growth.  Found on macOS, latent on Linux.  Test tests/test_make_output.py.
* Fixed: Make (F9) link step and Run (Ctrl-F9) reported a bogus failure (so the
  link/run silently did nothing) whenever xwpe's SIGCHLD handler reaped the
  compiler/make child before the build code could read its exit status.  The
  build now blocks SIGCHLD across the drain+reap to get the real exit code, and
  falls back to the parsed error list if the handler still wins -- never a
  spurious failure.  Caught by tests/test_menu_run.py.
* Build, run and debug now happen in the DIRECTORY OF THE FILE you are editing
  (or the project's directory), not wherever wpe was launched from -- xwpe never
  chdir()'d, so opening a file elsewhere (cd /x; wpe sub/main.c) compiled the
  object, linked the binary, ran the program and started the debugger in the
  wrong place.  e_build_dir/e_build_pushd wrap compile / make / run / run-.sh
  (e_run_sh also runs the script as ./name.sh and makes it executable), and the
  debugger chdir()s in the child like the LSP/DAP launchers.  Tests in
  tests/test_build_cwd.py assert the artifacts land in the file's directory.
* The c-lsp language-server demo ships a project file (docs/examples/c-lsp/
  c-lsp.prj): open it and F9 compiles AND links all three sources into
  clsp-demo (F9 on a single .cpp of a multi-file program correctly does not
  link).  And when a single-file Make/Run link fails next to a Makefile, xwpe
  now suggests opening a Project or using Execute Make instead of leaving a
  bare linker error.

  UI polish:
* The X11 (Xft) editor font size is now configurable, not hard-coded at 10pt.
  xwpe honours the size in the desktop monospace-font-name setting (e.g.
  "JetBrains Mono 13" renders at 13), and XWPE_FONT_SIZE overrides it (handy
  headless, over SSH, or on macOS with no desktop setting).  The cell grid and
  the FreeType glyphs follow the Pango metrics automatically.
* The File Manager (F3, or opening with no file) is centred and grows
  with the terminal instead of a fixed ~56x21 box pinned near the
  top-left: it sizes to ~3/4 of the screen, clamped to a sane min/max
  (e_fm_centered_geometry, we_fl_unix.c), so the DirTree and Files lists
  show ~17 rows on a 120x40 terminal instead of ~8.  Everything inside
  already scales off the window box, so only the initial size changed.
* Fixed: the synthetic output panes (Messages, Watches, Stack) wrongly
  wore the read-only padlock.  That glyph means "a locked file on disk"
  (a 0444 file, an extracted library source); the tool panes reuse the
  same not-editable flag but are not files, so the title-bar draw now
  skips them (e_win_is_tool_pane, we_wind.c).
* Esc no longer opens the menu bar -- use F10, the Borland-standard key.  A
  bare Esc in the editor is now a no-op, faithful to Turbo Vision (F10 and
  Alt-Space enter the menu; Esc is only Cancel, for an open dialog/menu).  It
  had shared F10's handler and jumped into the "#" system menu, which
  surprised users.  Closing an open dialog or menu with Esc is unchanged
  (those have their own input loops).  Tests in tests/test_esc_key.py.
* Alt-C (Compile) no longer looks like a no-op when the object is already
  built.  It compiles the active file to a .o without linking; when that .o was
  newer than the source -- e.g. just after a Make or a debug build -- it
  returned silently with no Messages output and seemed to do nothing.  It now
  pops a short "Already compiled (object file is up to date)" confirmation.
  Only single-file Compile reports this (via e_compile's announce flag); Make
  (Alt-M) stays silent and goes on to link, which is where a multi-file link
  error would surface.

  Portability and developer ergonomics:
* Builds on macOS (terminal wpe, Homebrew) and the BSDs, not just Linux:
  openpty()'s header is chosen per platform (<pty.h> / <util.h> /
  <libutil.h>), AC_SEARCH_LIBS finds it in libutil or libc, WEXITSTATUS
  is given an lvalue (macOS expands it to *(int*)&x), and the ncurses
  terminal mouse is enabled in --without-x --without-gpm builds (the
  MOUSE macro now also keys off NCURSES, so 'struct mouse' is defined).
* `make` now creates the wpe/xwpe/xwe symlinks in the build dir, so
  ./wpe works without `make install` (the mode is chosen by argv[0]).
* New XWPE_LIB environment variable (e_lib_dir, we_unix.c): overrides the
  compiled-in data dir, so syntax_def / help / the option file load when
  running from a build tree -- `XWPE_LIB="$(pwd)" ./wpe foo.c`.
* New contrib/xwpe-env: a shell-aware env helper (the brew-shellenv
  idiom) that emits export/`set -gx` for bash/zsh/fish, finding clangd,
  an LTS JDK (JAVA_HOME, macOS + Linux), the Coursier dir and XWPE_LIB.
* Fixed: Help > Info opened xwpe's own manual only when --prefix was /usr.  The
  Info search path was a hardcoded "/usr/share/info:/usr/local/info:/usr/info",
  so an install to ~/.local or a Homebrew prefix on macOS -- and even the
  default /usr/local, whose share/info was not on that list -- showed the
  system directory node instead of the xwpe manual.  The configured $(infodir)
  is now compiled in (XWPE_INFODIR) and searched first, so Help > Info opens the
  installed manual for any prefix.  Regression test in tests/test_help_info.py.
* New contrib/setup.sh: a one-command bootstrap (deps -> build -> install ->
  xwpe-env --persist) for Debian/Ubuntu (apt) and macOS (Homebrew).  Re-running
  it with a different --prefix now `make clean`s before reconfiguring, so the
  relink actually re-bakes LIBRARY_DIR / XWPE_INFODIR -- those come from -D
  CFLAGS, not config.h, so otherwise stale .o files keep the old install paths
  and the binary loads data from the previous prefix.
* The C/C++ language-server demo (docs/examples/c-lsp) now ships a Makefile and
  a note: it is a three-file program, so F9 / Ctrl-G on main.cpp alone does not
  link (expected -- use `make` or an xwpe project; debug_test.cpp is the
  single-file demo).  Two environment limits are documented in the manual
  (Bugs and limits) and the README: GNU screen drops the SGR mouse, so clicks
  leak as typed bytes -- use tmux or no multiplexer; and macOS grabs the
  function keys, so use Alt-M / Alt-C / the Ctrl-G debug prefix, or enable
  "standard function keys".

  GNU Algol 68 (ga68) compiler:
* ga68, the GCC Algol 68 front-end, is now supported alongside a68g.  It
  is a NATIVE compiler: Make/Run builds and links a real binary (comp_sw
  0, like gcc), and its file:line:col diagnostics drive the error jump.
* Source-level debugging of ga68 programs uses the existing gdb backend
  (not the a68g monitor): the binary carries DWARF, so F8 single-steps
  the Algol 68 source.  gdb breaks at __algol68_main (ga68's user entry;
  "break main" lands in the C runtime, not the user's program).

  Per-file dialect detection (ga68 vs a68g):
* ga68 and a68g use opposite, source-INCOMPATIBLE stropping: ga68 is
  modern (lowercase begin/end, { } comments) and a68g is classic (UPPER
  BEGIN/END, # ... # comments).  When both are installed, xwpe sniffs
  each .a68 / .alg file's content (e_algol68_use_ga68, we_prog.c) and
  drives the matching toolchain for BOTH compile and debug, so the two
  dialects build with no manual switch.  With only one installed, that
  one is used.
* Fixed: the dialect sniff is given the file's FULL path (dir + name).
  It opens the source to read its stropping, which failed on a bare
  window name when the file lived outside the current directory
  (e.g. "wpe sub/dir/foo.a68") -- detection came back undecided and the
  modern file fell through to a68g, which rejected it ("error in quoted
  bold tag").  Compile (e_comp) and both debug entry points now pass the
  full path.  Guarded by tests/test_ga68.py.
* Fixed: debugging a ga68 file outside the current directory raised gdb's
  "No symbol table is loaded".  e_start_debug re-detected the dialect from
  the bare window name, so the sniff failed, e_s_prog fell back to a68g
  (comp_sw 1) while the debugger was gdb, and the gdb branch then dropped
  the ".e" suffix -- gdb was launched on a non-existent binary.  It now
  resolves the dialect from the full path (shared e_check_c_file_w), so the
  compiled binary loads and F8 steps.  Guarded by tests/test_ga68_debug.py.
* Fixed: a Watch (Ctrl-G W) under gdb stayed frozen at its add-time value
  while single-stepping (e.g. an Algol 68 watch read 0 forever).  A plain
  Step is resolved by e_d_pr_sig (via e_d_trd_check), which positioned the
  source line but never re-read the watches; only the breakpoint branches
  refreshed them.  e_d_pr_sig now refreshes the Watches window too, so watch
  values track every step.  This is the shared gdb step path, so the fix
  benefits all gdb-debugged languages, not just ga68.  Guarded by
  tests/test_ga68_debug.py (watch on a factorial accumulator).

  Algol 68 syntax highlighting:
* .a68 / .alg files now highlight in both wpe (ncurses) and xwpe (Xft).
  The keyword set is case-INSENSITIVE, so both the modern lowercase and
  the classic UPPER stropping colour correctly.  Verified by
  tests/test_syntax_algol68.py (pyte) and tests/x11/test_syntax_algol68.py.
* Note: syntax_def is read from $HOME/.xwpe/syntax_def first, and a
  personal copy COMPLETELY shadows the system one.  A stale personal copy
  from an older install hides newly-added languages (the file shows a
  single base colour with no per-token highlighting); refresh it with
  "cp /usr/lib/xwpe/syntax_def ~/.xwpe/syntax_def" or remove it to fall
  back to the system file.  See the Configuration chapter of the manual.

  Debugging via the Debug Adapter Protocol (DAP) -- Go, Rust and Scala:
* xwpe now speaks the Debug Adapter Protocol -- the same wire protocol VS
  Code, Neovim and Emacs DAP use -- so modern language debuggers plug in
  without a bespoke text-scraping backend.  Two languages are wired:
  - Go: open a .go file and Ctrl-G R drives Delve (`dlv dap`).
  - Rust: open a .rs file; xwpe compiles it (rustc -g) and debugs it with
    `gdb --interpreter=dap` (gdb is a first-class Rust debugger -- rust-gdb IS
    gdb), or with `lldb-dap` (native on macOS).  The adapter is chosen in
    Debug -> debugger Options (Auto/gdb/lldb), saved in ~/.xwpe/xwperc; "Auto"
    picks the first found in PATH, and XWPE_DAP_ADAPTER overrides for CI.
  - Scala: open a .scala file and Ctrl-G R debugs modern Scala (3.x/2.13)
    through Bloop, which hosts the Scala Center scala-debug-adapter.  The JVM
    has no standalone DAP server, so xwpe runs a short Build Server Protocol
    (BSP) handshake against scala-cli (which bundles Bloop) to obtain a DAP
    endpoint, then connects.  Frames arrive Scala-demangled.  Requires
    scala-cli (cs install scala-cli); the Debian scala package (2.11) is too
    old.  A modern Scala/macOS user can run wpe in a terminal as a tiny
    zero-config debug front-end -- no XQuartz, no Electron.
  Run/Continue (Ctrl-G R), Step Over (F8), Step Into (F7), watches (Ctrl-G W,
  evaluated with DAP evaluate context=watch) and Quit (Ctrl-G Q) all work, just
  like the gdb backend.  Stops jump the editor to the source line; program
  output appears in Messages.
* The engine supports three transports behind one API, because each adapter
  dictates its own: reverse-TCP (Delve is a `dlv dap` server; the debuggee's
  output is read from a pty), stdio (gdb/lldb speak DAP on their own
  stdin/stdout; program output arrives as DAP "output" events), and TCP-client
  (connect to an endpoint a build server already started, as for Scala/Bloop).
  Adding a language is a one-row descriptor (adapter, transport, compile-or-not,
  bsp-or-not).  BSP reuses the DAP Content-Length framing, so no Scala artifact
  ships in xwpe -- scala-cli stays an external coursier tool.  lldb-dap for
  C/C++ is a drop-in on the stdio path.
* This is the seventh debugger backend (DEB_DAP), selected automatically by
  the .go extension.  The six text backends (gdb, sdb, dbx, xdb, jdb, pdb,
  a68g) are untouched.  Delve is reverse-connected over a local TCP socket
  (xwpe listens, dlv dials in); the adapter is launched on its own
  controlling pty so the debuggee's foreground terminal control succeeds.
* Program output capture uses the same inferior-pty model as the gdb backend:
  xwpe owns the adapter's pty and reads the debuggee's stdout/stderr from it,
  so program output lands in Messages and the adapter's own console chatter
  (dlv's "Type 'dlv help' ..." banner) is filtered out.
* Step-over (F8) stays in your code: once main() returns there is no user line
  left to step to, so a naive "next" would wander into the Go runtime
  (runtime/proc.go under GOROOT).  Instead xwpe runs on to the next user line
  or to program exit -- F8 past the last line simply reports "Program exited."
  Step-into (F7) is left alone, so you can still descend deliberately.
* Go always uses Delve, never gdb: the .go source is detected across all
  windows (not just the focused one), so adding a watch -- which opens the
  Watches pane -- before pressing Run no longer mis-routes the session into the
  gdb path (gdb is unreliable for Go's goroutine runtime anyway).
* Go requires `dlv` (delve) and `go`, plus a Go module (go.mod) in the source
  directory (`dlv dap` builds the package).  Rust requires `rustc` and `gdb`.
  Scala requires `scala-cli` (cs install scala-cli).  Verified by
  tests/test_go_dap.py, tests/test_rust_dap.py and tests/test_scala_dap.py
  (compile/build + breakpoint + watch grows), plus the tests/test_dap*.c
  unit/integration suite against real Delve, gdb, lldb-dap and scala-cli/Bloop.
* The Scala build server's stderr is sent to /dev/null: unlike gdb/dlv, Bloop
  is chatty there ("Starting compilation server", compile progress), which would
  otherwise bleed raw onto the ncurses screen.
* Syntax highlighting added for Go (.go), Rust (.rs) and Scala (.scala) in
  syntax_def.  Note: a personal ~/.xwpe/syntax_def shadows the system file, so
  refresh it (cp .../syntax_def ~/.xwpe/syntax_def) to pick up the new languages.
* New build dependency: json-c (libjson-c-dev) for DAP message coding.
  C/C++ over gdb/lldb-dap come nearly free on the same stdio path next.

  IDE features via the Language Server Protocol (LSP) -- Scala/Metals:
* Where DAP made xwpe a debugger, an LSP client makes it an IDE.  xwpe now
  speaks LSP -- the same protocol VS Code, Neovim and Emacs use -- to the same
  language servers (Metals for Scala).  Open a .scala file and use the Alt-Q
  prefix ("Query the language server"):
  - Alt-Q E: start Metals, import the build (via scala-cli) and show compiler
    diagnostics in Messages.
  - Alt-Q D: go to the definition of the symbol under the cursor.
  - Alt-Q I: go to the implementation -- the concrete override of an abstract
    def / trait member (IntelliJ's Ctrl-Alt-B).
  - Alt-Q T: go to the type definition -- the class/trait of a value's type
    (IntelliJ's Ctrl-Shift-B).
  - Alt-Q H: hover -- show its type and documentation (e.g. "var f: Long").
  - Alt-Q C: completion -- candidates for the word under the cursor in a popup;
    pick with the arrows + Enter and it replaces the partial word.
  - Alt-Q R: references -- list every use of the symbol (decl + uses) in Messages.
  - Alt-Q L: highlight -- mark every occurrence of the symbol in this file (the
    found-word colour); press off an identifier to clear (IntelliJ's auto
    identifier highlight).
  - Alt-Q O: outline -- the file's symbols in a popup; pick one to jump to it.
  - Alt-Q K: code lenses -- the run/debug/test and reference-count annotations
    Metals attaches to definitions, in a popup; selecting one jumps to it.  (To
    actually run a Scala main, use the debugger, Ctrl-G T, which drives the same
    build-server path.)  Enabled by advertising debuggingProvider in the Metals
    initialize handshake.
  - Alt-Q W: workspace symbol search -- prompt for a query, then jump to any
    matching symbol from across the whole project (IntelliJ's Go to Symbol).
  - Alt-Q A: code actions -- the quick-fixes/refactors the server offers at the
    cursor (organize imports, create companion object, ...) in a popup; a
    direct-edit action rewrites the buffer (IntelliJ's Alt-Enter).
  - Alt-Q S: signature -- the signature of the call the cursor is inside.
  - Alt-Q N: rename -- rename the symbol everywhere (prompts for the new name;
    edits in this file applied to the buffer, other files reported).
  - Alt-Q F: format -- reformat the file with the server's formatter (scalafmt).
* Rename/format apply the server's edits back into the buffer (the edit
  application is unit-tested vs real Metals; the buffer rebuild has its own
  editor test).
* Live diagnostics: every edit is pushed to the server (didChange, debounced on
  newline) and diagnostics are drained non-blocking on each keystroke, so a
  "LSP: N error(s), M warning(s)" status updates as you type -- no save, no
  focus stolen from the editor.
* The engine (we_lsp.c) reuses the DAP/BSP JSON-RPC + Content-Length framing,
  so no new dependency; nothing Scala ships in xwpe (Metals is an external
  coursier tool: cs install metals).  Verified by tests/test_lsp_scala.c (engine
  vs real Metals) and tests/test_scala_lsp.py + tests/test_scala_lsp_complete.py
  (the editor bridge through wpe).
* Edits are seen live: before each request the current editor buffer is pushed
  to the server (textDocument/didChange), so hover/definition/completion/
  diagnostics reflect unsaved changes, not just the on-disk file.
* Go-to-implementation, go-to-type-definition, workspace symbol search and
  code actions all verified against real Metals in tests/test_lsp_scala.c
  (implementation -> the override, type-definition -> the class, workspace
  search finds the Factorial object, a code action is offered and applied).
* Inline diagnostic marks: a problem's range is recolored in the source
  (errors red, warnings amber) -- the cell-grid analogue of a red squiggle --
  updated live as you type and cleared when fixed, alongside the Messages list
  and the "N errors" status.  publishDiagnostics now carries the range END to
  the editor; the marks ride the same per-character paint as breakpoints, so
  they work in both wpe (ncurses) and xwpe (X11).  tests/test_lsp_scala.c
  asserts the range is delivered; tests/test_scala_lsp_diag_marks.py asserts
  the red cells actually render through wpe.
* Discoverable "Metals" menu: while a language-server file is the active window,
  the bottom status bar shows a "Metals" entry (named after the server) -- the
  Borland-style contextual bar, like Messages' Compile/Run.  Clicking it opens a
  drop-up menu (the same widget as the File/Edit menus, unfolding upward from the
  bar, anchored under the entry) listing every Alt-Q action with its accelerator
  right-aligned; arrows move, the letter or a click runs it.  The entry appears
  only for files a server backs and disappears for ordinary files.  Covered by
  tests/test_lsp_menu.py (visibility, focus-switching, the mouse drop-up, and the
  keyboard Alt-Q path).
* Fixed: go-to-definition and hover came back empty for every symbol.  The
  buffer-to-server serializer copied each line's in-buffer WPE_WR newline marker
  AND appended its own '\n', doubling every line break -- so the server saw a file
  twice as long and every position was off by whole lines.  e_lsp_buffer_text now
  emits one newline per line (matching File>Save); the didOpen text is byte-
  identical to disk.  This was the real cause of "No hover information" too.
* Fixed: Alt-Q W no longer lists Metals' "Add ';' to search library dependencies"
  help entry (it pointed at a Markdown doc, not code); results whose path ends in
  .md are filtered out.
* Scala "just works" on a too-new default JDK: Metals' presentation compiler
  (hover, completion, go-to-definition) runs on the JVM Metals itself uses, not
  the project's //> using jvm build JVM.  On JDK >= 24 (e.g. OpenJDK 26) the
  Scala 3 compiler crashes at start-up (asTerm called on not-a-Term), making
  those actions silently return empty while diagnostics/definition keep working.
  xwpe now detects this and pins JAVA_HOME to an installed LTS JDK (17/21) for
  Metals, reporting it in Messages ("Metals: pinned JAVA_HOME=..."); an explicit
  supported JAVA_HOME is respected.  If no LTS JDK is installed it says so.  See
  the manual's "Language servers" chapter and "Known open issues".
  More language servers via LSP -- C/C++, Python, Go, Rust:
* The LSP engine was built editor-free and server-agnostic, and now drives
  five languages from a one-row-per-server descriptor table; the Alt-Q action
  set is identical across all of them:
  - C and C++ (clangd): open a .c/.h/.cpp/.cc/.cxx/.hpp/... file.  clangd needs
    no JVM, so it starts fast and go-to-definition follows straight into the
    system headers under /usr/include.
  - Python (pyright or pylsp): xwpe uses whichever is on PATH, preferring
    pyright (the type checker VS Code uses) and falling back to the Debian-
    native python3-pylsp.  pylsp wants python3-pyflakes alongside it for
    diagnostics; jedi alone navigates but does not lint.
  - Go (gopls): open a .go file.  gopls wants a module (a go.mod in the
    directory) for full results; a lone .go loads in a degraded ad-hoc mode.
  - Rust (rust-analyzer): open a .rs file (from inside a Cargo crate).
  Each server is resolved from the file's extension, eager-started in the
  background when the file opens, and named on the status bar.  Verified
  against real servers -- clangd 21, gopls 0.21 / go 1.26, pyright 1.1.4 and
  pylsp, rust-analyzer 1.95 -- by tests/test_lsp_clangd.c, test_lsp_python.c,
  test_lsp_gopls.c and test_lsp_rust.c (and their pyte editor-bridge twins),
  each self-skipping when its server is absent.
* The status bar names the server backing the active window -- "clangd" for a
  C file, "pyright" for Python, "Metals" for Scala -- instead of a hardcoded
  "Metals", and the LSP button row now packs adaptively: it relays from the
  live label widths so a long name (rust-analyzer, pyright) no longer runs off
  the 80-column edge and clips "Alt-X Quit".
* Go-to-definition into library and standard-library sources: jumping to a
  symbol defined in a dependency or the stdlib opens that source read-only --
  marked with a padlock in the title and a dimmed title bar, so it is visibly
  distinct from an editable file.  Running an Alt-Q action from inside such a
  read-only source no longer spins up a second, useless language server rooted
  at the cache dir; it tells you to run actions from the project window (#213).
* LSP edits are now Undo-able and Redo-able: applying a code action (Alt-Q A),
  a rename (Alt-Q N) or a format (Alt-Q F) records a single whole-buffer undo
  entry, so Ctrl-U restores the file and Ctrl-R re-applies the refactor in one
  step.  Previously an LSP edit could not be undone at all (#216).

  LSP -- Scala/Metals, additional IDE actions:
* Asynchronous cold start: starting Metals (JVM boot, build import and first
  compile) no longer freezes the editor -- it boots in the background and xwpe
  stays responsive; opening a server-backed file eager-starts it so the first
  Alt-Q does not wait through the cold start (XWPE_LSP_NO_EAGER=1 opts out).
  Indexing/compiling status streams live to Messages, and a hover that comes
  back empty while the server is still working now says so (#210).
* Scala worksheets: open a .worksheet.sc and Metals evaluates each top-level
  expression, showing the result dimly at end of line (val a = 5 + 7 grows
  ": Int = 12") -- a REPL that updates in place as you edit.  A plain .sc is a
  scala-cli script (type hints yes, "= 12" evaluation no).  .sc files now
  highlight as Scala (#187).
* Call hierarchy (Alt-Q B / Alt-Q G), type hierarchy (Alt-Q K / Alt-Q J),
  expand-selection by syntax (Alt-Q V), context-sensitive range formatting
  (Alt-Q F formats only the marked block), and server-driven semantic
  highlighting (Alt-Q M) were added; inlay hints (Alt-Q Y) now refresh
  themselves as the project finishes indexing.
* Code actions now apply in all three shapes the server delivers them -- an
  inline edit, a command run via workspace/executeCommand, and an unresolved
  action fetched via codeAction/resolve (how Metals ships refactors such as
  "Convert to named arguments") -- with diagnostic-keyed quick-fixes (#214).
* Fixed: arrow keys, Home/End/PageUp and Enter now work in dialogs and the LSP
  code-action picker under ncurses (#215).
* Fixed: a language server that crashes or closes its output no longer spins
  the editor at 100% CPU -- xwpe tears down the dead session and reports it.
* Fixed: a background LSP paint (Doctor, diagnostics status, inlay overlay) no
  longer corrupts a modal window (file manager, project dialog) on top of the
  editor while the server is still loading.

  LSP -- documentation and demos:
* New user-facing guide docs/LSP.md: what LSP gives you, the per-language setup
  (which server, how to install it), the full Alt-Q key table, the
  read-only-stdlib behaviour, and a demo reel, linked from the top-level README.
* A runnable, fully-commented demo testbed per language under docs/examples/
  (c-lsp, go-lsp, python-lsp, rust-lsp, scala-lsp), each a small class/trait
  hierarchy in that language's own idioms with a README key table, a
  code-actions playground, and a note of which actions need which server.
* A reproducible "tour" GIF per wired language (clangd, gopls, pyright,
  rust-analyzer), each walking that language's own testbed through hover,
  go-to-definition, references and the file outline (docs/demos/), so a
  developer sees the IDE features in their own ecosystem, not in Scala.

  macOS console (kitty / iTerm2) keyboard and robustness:
* Fixed: Alt-<letter> menu and LSP shortcuts were silently rewritten on macOS
  -- in the "#" menu Alt-E opened Window instead of Edit, and the Alt-Q action
  letters landed on the wrong command.  e_toupper() was the C library's
  toupper(), and under a UTF-8 locale on macOS (BSD libc) toupper() maps
  codepoints above 0xFF, so an Alt code such as Alt-E (273 in the DOS-scancode
  order xwpe uses) folded to Alt-W (272).  e_toupper now leaves anything
  outside 0x00-0xFF untouched.  Linux was unaffected (glibc toupper ignores
  >0xFF), but the guard is correct on every platform.
* Fixed: a use-after-free could crash xwpe right after a menu closed -- the key
  dispatcher kept using the previously-active window pointer, which the menu
  teardown may already have freed.  e_tst_fkt now re-reads the active window
  after handling the menu and bails out if none remains.
* Fixed: a language server that exits at start-up (a crashed binary, a wrong
  PATH) could kill xwpe with SIGPIPE while it was still writing the initialize
  request.  SIGPIPE is now ignored process-wide, so the write fails cleanly and
  the dead session is reported instead of taking the editor down.
* A runaway compiler or debugger can no longer pin xwpe at 100% CPU.  The
  build-output drain (e_p_exec) is now bounded, and the debugger watchdog --
  which counted whole replies but not the bytes absorbed by its stderr drain --
  now counts every line drained from the child's STDERR toward its runaway
  budget too.  A backend that floods stderr (a68g's abend->io_write_string
  recursion under Ctrl-G r is the textbook case; once its stdout pipe fills the
  echoes bounce back as "echo: I/O error" on stderr) now trips the guard in
  well under a second instead of looping for hours.  This extends the 1.6.4
  watchdog; tests/test_debugger_watchdog.py exercises the stderr-flood path.
* `./wpe` run straight from a build tree now finds its help/data files: when a
  data file is absent it falls back to the "<name>_eng" English copy that ships
  in the source tree (e_lib_file), so the editor is usable before
  `make install`.
* Documented Alt-1...Alt-9 (jump straight to window N by index) alongside
  F6 / Alt-N (cycle) in the manual, and the macOS function-key equivalents for
  the menu, window and help keys in the README, so a macOS user whose whole top
  row is grabbed by the OS knows xwpe stays fully keyboard-drivable.

  macOS X11 (XQuartz) -- the xwpe GUI build:
* Fixed: the `xwpe*altMask: modN` X-resource was silently ignored -- the parser
  read the digit at offset 4 ("mod1"[4] is past the string), so mod1..mod5 all
  fell through to the built-in default.  It now scans past the "mod" prefix and
  any whitespace, accepting both "modN" and the legacy "mod N" spelling.  This
  is what lets a macOS user point Alt at the right modifier (XQuartz delivers
  Alt_L on a different mod than the Linux default).
* Fixed: the window title (WM_NAME) could be missing.  XmbSetWMProperties drops
  it when the libX11 locale module is incomplete (the Homebrew libX11 on macOS),
  so xwpe now also sets the legacy STRING properties (XStoreName/XSetIconName)
  and the EWMH _NET_WM_NAME/_NET_WM_ICON_NAME (UTF8_STRING) directly -- the
  title is always discoverable by window managers, wmctrl and xdotool.
* Fixed: a dialog opened during start-up could render squashed.  Pango's actual
  font metrics (set by cr_init_pango_font) can differ from the seed metrics used
  to size the initial grid; on macOS that gap is large enough that the File
  Manager, laid out before the first ConfigureNotify, came out compressed.  The
  cell grid (MAXSCOL/MAXSLNS) is now refit to the real font immediately after
  Cairo init, so the very first frame matches the chrome.  (On Linux the seed
  and Pango metrics nearly coincide, so the bug did not surface there.)
* The headless X11 GUI test suite (tests/x11/) now runs on macOS under XQuartz,
  not just Debian/Ubuntu CI: it auto-detects the toolchain (matchbox or twm for
  the window manager; ImageMagick 6 `convert` or IM7 `magick`+`xwdtopnm` for the
  XWD-to-PNG step), pins the window to a fixed geometry so coordinate-based pixel
  scans match under either WM, finds the window by WM_CLASS (xdotool's --name
  regex is broken in the Homebrew build), and enables the libvterm User Screen
  tests via otool/ldd probing.  66/66 pass on both XQuartz and matchbox.

1.6.4 (embedded VT terminal, Algol 68 source-level debugger, window-layout fixes)

  Borland "User Screen" (Alt-F5) on X11:
* xwpe now reproduces a program's painted screen (cursor addressing,
  ANSI colour, box drawing) inside its own window.  After Ctrl-F9 Run
  (or a debug session) the program's output is captured; Alt-F5 feeds it
  to an embedded VT terminal (libvterm) and paints the resulting cell
  grid full-window, then waits for a key and restores the editor.  The
  console (wpe) already did this by handing the real terminal back to the
  program (e_t_user_screen); xwpe has no terminal to drop to, so it
  interprets the captured bytes instead (e_x_vterm_user_screen,
  we_vterm.c).  The same key now does the same thing on both backends.
* libvterm is an optional dependency (configure: "libvterm: yes").  When
  it is absent, Alt-F5 in xwpe falls back to focusing the Messages panel
  (the 1.6.3 behaviour); the console User Screen is unaffected.
* VT colours are mapped to xwpe's 16-colour palette by nearest RGB match,
  so SGR colours render with the editor's own scheme.  Reverse video and
  bold are honoured; wide (CJK) glyphs keep their two-column width.
* The User Screen owns the whole window, so the per-window fluid scrollbar
  overlay (wpe_render_chrome, re-applied on every refresh) is suppressed
  while it is up -- otherwise the editor's and Messages' scrollbars bled on
  top of the program's painted screen (the mid-screen horizontal thumb, the
  right-edge vertical tracks, the bottom Messages bar).  Restored on return.
  Covered by test_user_screen.py::test_user_screen_has_no_scrollbar_bleed.
* Covered by tests/x11/test_user_screen.py: paint.c is built+run, Alt-F5
  shows its ANSI red+green, and a keypress restores the editor.

  New language backend:
* Algol 68 support via Algol 68 Genie (a68g): .a68 / .alg files are
  auto-detected, Make (F9) runs "a68g --norun" to check the syntax, and
  Run (Ctrl-F9 / Alt-U) executes the program with its output captured in
  Messages.  a68g's diagnostics carry no file name, so the error jump is
  best-effort on the line number; the full diagnostic always shows in
  Messages.  Covered by tests/test_algol68.py (run + syntax-error).

  Algol 68 source-level debugger (DEB_A68G):
* a68g ships a full gdb-class monitor ("a68g --monitor"), so a .a68 / .alg
  file is now a first-class debug target, not compile/run-only.  A seventh
  debugger backend drives it like gdb/jdb/pdb: auto-select on the extension,
  breakpoints ("breakpoint N"), run/continue from a68g's automatic break at
  line 1, step/next with the editor cursor following the source line, watches
  ("evaluate EXPR", re-evaluated on every step), call stack ("calls"), and
  program output captured to both the Messages window (Ctrl-G P) and the
  Alt-F5 User Screen.  Re-running after the program finished re-arms the
  breakpoints.  The change is additive (every branch guarded by
  e_deb_type == DEB_A68G), so the other six backends are untouched.  Covered
  by tests/test_algol68_debug.py (breakpoint, watch n = (INT) +5 that updates
  on step, run-to-completion output capture, re-run-after-finish, green
  current line).  A new "Debugging Algol 68 programs" chapter documents it,
  with docs/examples/debug_test.a68.
* Step Over (F8) is line-granular.  a68g steps by interruptable unit, so one
  source line (e.g. a print with several arguments) reports the same line
  repeatedly; F8 now keeps stepping while a68g stays on the same line, so one
  keypress advances to the next line -- matching gdb/pdb.  Step Into (F7)
  stays unit-granular.
* A watch on an out-of-scope variable (e.g. a procedure parameter after the
  recursion unwinds) shows "<not in scope>" instead of a68g's raw monitor
  error.

  Debugger robustness and UX (all backends):
* Watchdog so a dead or runaway debugger can never busy-loop xwpe.  A backend
  that dies without its pipe reaching EOF, or floods its output (a68g can fall
  into an abend->io_write_string recursion), used to spin xwpe's synchronous
  read loops at ~100% CPU.  Three guards in the shared read path stop that: a
  runaway-output counter (reset per command), a bounded stderr drain, and a
  non-blocking liveness check (waitpid) on EAGAIN.  Covered by
  tests/test_debugger_watchdog.py (a fake flooding debugger; no real one
  needed).
* The current-execution (stopped-at) line is now a readable black-on-green
  bar.  It was blue-on-turquoise -- nearly invisible on the Dark Slate Blue
  editor and easily confused with ordinary text; green reads as "executing
  here" and stays distinct from the red breakpoint bar.

  Window-layout fixes (debug windows, and any overlapping windows):
* Size/Move (Window menu) now repaints the whole desktop, not just the moved
  window.  Shrinking a window left stale border fragments and a half-erased
  neighbour underneath; this was very visible with the Messages and Watches
  windows overlapping at the bottom during a debug session.
* Opening the Watches window no longer collapses the already-open Messages
  window.  e_position_messages_window docks both at the bottom and clamped
  every other window to make room, but skipped only the one being positioned
  -- so positioning Watches pulled the Messages window's bottom edge above its
  top edge, leaving an inverted, one-row-tall window.  It now excludes the
  sibling Messages/Watches window from the clamp, as the split computation
  already did.  Covered by tests/test_algol68_debug.py.

  Console chrome on non-UTF-8 terminals:
* The window title-bar close and zoom/restore boxes are drawn as Unicode
  symbols (close U+2715, zoom U+25A1, restore U+25A3).  On a terminal whose
  locale is not UTF-8 (the C locale, a serial console, ISO-8859-*) wcwidth()
  reports them unprintable, so the emit loop blanked them and the buttons
  silently vanished.  wpe now detects the locale's CODESET once and, on a
  non-UTF-8 terminal, substitutes an ASCII stand-in ('x' close, '^' zoom,
  'v' restore) so the affordances stay visible.  Box-drawing and the
  scrollbar are unaffected -- they already use ncurses ACS, which ncurses
  downgrades to the VT100/ASCII line set on its own.  Covered by
  tests/test_chrome_ascii.py (C locale shows ASCII, UTF-8 keeps the glyphs).

1.6.3 (event-driven debugger, dead key compose, Perl/COBOL)

  Event-driven I/O (X11 and terminal debugger):
* Interactive program input during debugging.  When the debugged
  program blocks on fgets/scanf, the user types in the IDE and
  characters appear in Messages.  Backspace works (pty VERASE).
  Architecture: wpe_fd_poll multiplexes display fd + gdb pipe + pty
  master (same pattern as st, cgdb, foot terminals).  Works in both
  X11 (xwpe) and terminal (wpe) modes.  Terminal mode uses
  e_t_getch_poll with timeout(50) for ncurses escape sequence
  compatibility.  UTF-8 input via e_t_utf8_assemble (multi-byte
  codepoint decoding from ncurses getch).
* Incremental gdb response parser (e_d_accum_t) processes one line
  per callback instead of looping synchronously in e_read_output.
  The UI never freezes during debug stepping.
* Program output appears in Messages via e_d_pty_read_to_messages
  (char-by-char buffer append, no line-flush artifacts).
* Ctrl-G P (Program Output) accumulates during interactive sessions.
* Ctrl-G P (Program output) is now uniform across backends: the program's
  output is captured into the Messages window in both the terminal and X11
  builds, so Ctrl-G P raises and focuses that integrated panel and scrolls
  to the latest output (e_p_show_program_output) -- the way a modern IDE
  focuses its output panel.  No modal popup (X11) and no full-screen switch
  (terminal); the same key does the same thing everywhere.  The classic
  full-screen "user screen" (for programs that paint with cursor
  positioning / ANSI colour) is preserved as e_t_user_screen(), not yet
  bound to a key -- a faithful version (an integrated VT terminal shared by
  both backends, the Borland Alt-F5 "User Screen") is planned for 1.6.4.
  The Messages cursor is placed at the end of the last output line (the
  stream write position), like a tailing terminal, not at column 0.
* printf without \n now visible after each step: e_d_flush_inferior_stdout
  sends "call (void)fflush(0)" to gdb (same technique as Eclipse CDT).

  Dead key compose and UTF-8 input:
* X11 dead key composition for accented characters: acute, grave,
  diaeresis, tilde, circumflex, cedilla.  Works in the editor, the
  debug console, and all dialog text fields.  Fallback compose table
  (same approach as xterm/GTK) when XIM does not compose natively.
* UTF-8 in dialog text fields: e_schr_nchar decodes multi-byte
  sequences for rendering.  Cursor movement, backspace, and delete
  are codepoint-aware (e_utf8_prev, e_utf8_next, e_utf8_charlen).
* UTF-8 multi-byte insertion in editor (e_codepoint_to_utf8).
* UTF-8 encoding for debug console pty input (e_d_pty_write_utf8).
* XIM robustness: NULL XIM guard, Status parameter, XNFocusWindow.

  Dialog usability:
* Tab/Shift-Tab navigation through all dialog fields, including
  Ok/Cancel buttons.  Wraps around at the end.  Works in xterm, SSH,
  tmux, gnome-terminal -- everywhere, not just the Linux console.
* Radio buttons now show ( ) instead of [ ] to distinguish from
  checkboxes.  Selected radio shows (*).
* Fixed illegible colors for focused elements and shortcut letters
  on modern terminals.  Default palette updated from 1993 CGA/VGA
  assumptions to readable contrast on 256-color terminals.
* Dialog focus indicator: magenta background for focused fields,
  switches, and radio buttons.  Red background for focused Ok/Cancel
  buttons.  Passive buttons use cyan.  No element shares a color
  with the dialog background.
* Syntax highlighting: string constants changed from bright yellow
  to bright magenta (color 14 renders greenish on many terminals).
* Status bar hitbox: clicking empty space to the right of "Alt-X
  Quit" no longer triggers quit.

  Compiler integration:
* Perl: syntax highlighting (46 keywords), F9 (perl -c), Ctrl-F9 run.
* COBOL: syntax highlighting (50 keywords), F9 (cobc), GNU style.
* 9 compilers supported: gcc, g++, gfortran, fpc, javac, python3,
  pdflatex, perl, cobc.
* Error navigation (Alt-T/Alt-V) is now cursor-relative with
  wrap-around: jumps to the next error below the cursor, wraps to
  the first error when past the last.

  Mouse and window management:
* ncurses mouse drag: window move and resize by dragging title bar
  and borders.  xterm mouse mode 1002 (\033[?1002h) enables motion
  tracking while button held -- same protocol as Midnight Commander
  and tmux.
* g_mouse_buttons: global bitmask tracks physical button state across
  the entire event loop, replacing the shared e_mouse.k field that
  desynchronised when popups consumed PRESSED/RELEASED pairs.
* e_mouse_flush(): drains stale ncurses mouse events, re-enables
  mode 1002, and resets g_mouse_buttons after popup or menu close.
  Prevents drag hang after F9 compilation or any dialog.
* Proportional window relayout on terminal resize (SIGWINCH):
  e_relayout_windows() scales all editor windows using edge-attachment
  detection -- windows touching screen edges stay attached, interior
  coordinates scale proportionally with round-to-nearest to prevent
  ratio drift across incremental resize steps.
* Dialog resize safety: SCHIRM_INBOUNDS macro bounds-checks all
  schirm[] access.  Dialogs that exceed terminal size are clipped
  cleanly.  On terminal grow, dialogs restart centered.
* Fix SIGSEGV on terminal resize during dialog: block SIGWINCH with
  sigprocmask during e_t_refresh, clamp iteration to ncurses stdscr
  dimensions to prevent race with concurrent SIGWINCH.
* Fix F9 hang after dialog resize: e_free_all_pics() invalidates
  stale FENSTER PICs before e_repaint_desk creates fresh ones with
  correct post-resize coordinates.
* Dialog clipping: when the terminal is smaller than the dialog, the
  interactive loop is bypassed (only Esc and resize accepted).
  e_opt_element_visible() checks both axes before drawing each
  element.  Prevents floating character artifacts from dialog content
  drawn outside the PIC save area.
* Messages window auto-positioning: 2/3 + 1/3 vertical split when
  Messages is created by F9.  e_position_messages_window() places
  Messages to fill available space below editor windows.
* Modernised window-control keys.  Close window is now Ctrl-W -- the
  universal browser/VSCode/IntelliJ "close tab" -- decodable in both the
  terminal and X11.  The historic Alt-F3 was dropped: the window manager
  (X11) and the kernel VT layer (console) intercept it, so it never reached
  xwpe and users had no working close-window shortcut.  Show Buffer moved to
  Alt-Y (emacs M-y, "browse the paste buffer").  Status bar, the Window >
  Close item and the Edit menu were relabelled to match.  Tests:
  tests/test_close_buffer_keys.py.
* Title-bar close box [X] works again in wpe and xwpe.  It handed back Alt-F3
  in the default key style, which became a no-op once Alt-F3 was dropped; it
  now returns Ctrl-W and is hit-tested at the glyph it actually paints (e.x-2,
  top-right).  Document/editor windows keep their zoom box.  Tests:
  tests/test_window_close_button.py.
* File-Manager dialogs (Open / Save As / search) are close-only, like a
  Borland TDialog: the maximize box is gone (meaningless for a fixed-size
  picker) and the close [X] now dismisses the dialog.  The picker had drawn a
  zoom box and hit-tested the close at the historic top-left cell while the
  glyph is painted top-right, so clicking the visible X only started a window
  move.  New e_draw_dialog_close_button paints just the close glyph; the click
  routes through e_hit_close_button.  Tests: tests/test_file_manager_close.py
  and tests/x11/test_file_manager_close.py.

  Ctrl-F9 Run:
* Interactive program execution via pty in Messages window.  User
  types input during fgets/scanf, output appears in real time.
  Ctrl-C sends SIGINT.  UTF-8 input and output with proper
  backspace handling.  No xterm dependency, no screen switching.
  Falls back to popen for systems without openpty.

  Bug fixes:
* Fix emoji rendering in ncurses: removed artificial U+FFFF limit
  in e_t_refresh.  Emoji codepoints (U+1F389 etc.) now display
  correctly in terminals that support them (kitty, gnome-terminal).
* Fix mouse in kitty: SGR mode 1006 only enabled when terminal is
  wider than 223 columns (X10 encoding overflow).  kitty's terminfo
  lacks XM capability so ncurses cannot parse SGR mouse events;
  X10 format works correctly for normal terminal widths.
* CVE-2016-20037: the original CVE is bogus (confirmed by Debian
  Security Tracker -- no vulnerable code path exists in argv handling).
  As defense-in-depth, all sprintf calls that receive user-supplied
  filenames (f->datnam) are converted to snprintf with sizeof bounds
  in we_wind.c, we_debug.c, we_fl_unix.c, and we_prog.c.
* Fix global buffer overflow on a long library name: LIBNAME= from the
  .prj, the LiBrary dialog field, and project member names were copied
  into the fixed library[80] with strcpy; now snprintf-bounded
  (we_prog.c).  Defense-in-depth beyond CVE-2016-20037.
* Fix silent data loss on disk full: e_write now checks ferror/fclose.
* Fix link/run using Messages window instead of source file.
* Fix Open Project with nonexistent .prj opening a blank editor.
* Fix Add Item with no project open silently doing nothing.
* Fix --without-x build: guard X11 symbols, conditional symlinks.
* Fix syntax_def keyword counts (Perl 46, COBOL 50).
* Add 9 syntax-highlighting definitions from Payne's extrasyntax
  collection (shell, HTML, Makefile, SQL and more).  Verifier test:
  tests/test_syntax_def.c.
* Fix intermittent X11 crash/black-hang: call XInitThreads() before the
  first Xlib call.  Cairo/Pango/fontconfig spawn helper threads, so Xlib
  must be told it is in a threaded program or _XEventsQueued races during a
  refresh (segfault inside XRenderFillRectangle).
* Fix double-free (SIGABRT "free(): invalid pointer") when reopening Show
  Buffer after the clipboard window had been closed.  The persistent
  clipboard window's view (pic) was freed on close but left dangling;
  e_show_clipboard now NULLs it before e_firstl re-opens the window, matching
  e_firstl's contract that a fresh window has pic == NULL.  Latent since the
  Kruse 1.4.2 era (1993); only surfaced once the 1.6.2 SCREENCELL migration
  made the repaint path always free the pic.

  Project management (Borland audit):
* New Project and Open Project are now separate menu entries.  New
  Project (Alt-P N) creates a skeleton .prj; Open Project (Alt-P P)
  strictly opens an existing one -- a missing name reports "Project
  file not found" instead of silently creating a blank project.
* The project file list is written to disk on every Add and Delete,
  not only on window close, so an interrupted session no longer loses
  members.
* Delete now removes the last (and the single remaining) project member.
  The old guard in e_p_del_df (nf > anz-2) silently refused to delete the
  highlighted entry whenever it was the last one, so a project could never
  be emptied.
* A new .prj is pre-filled (EXENAME from the project name, compiler
  defaults); the project window's exit button saves on close.
* Project, Options, Message and File-Manager window titles and borders
  were invisible (white-on-white frame colour) and are now legible.
* Window->Project with no project open reports "No project open"
  instead of opening an empty window.
* The project window's status bar packs its buttons compactly and keeps
  the "Project: <name>" indicator without overwriting the Quit button.
* Window drag and resize stopped working after an F9 compile (mouse
  motion tracking was left disabled when returning from the
  sub-process) -- fixed: e_t_rearm_mouse re-arms it after the screen is
  restored.  Regression tests in tests/test_mouse_tracking.py and
  tests/test_project_windows.py.

  Maintainability (1993 codebase cleanup):
* Duplicated project-window lookups and the project-open guard replaced
  by named helpers (e_find_project_window, e_find_dirty_project_window,
  e_project_is_open), status-bar layout by e_pack_button_bar, and the
  File-Manager hint colour boilerplate by e_fm_key_hint.  Window-picker
  modes use named constants (FM_PRJ_*).  Public helpers documented in
  Doxygen/kernel-doc style.

  X11 mode:
* Unicode box-drawing glyphs for window borders (U+250x) and
  scrollbar (U+2591 light shade, U+2588 full block) when Xft active.
* Fix covered-window scrollbar bleed-through.  Zooming a window over another
  (Window -> Zoom / Alt-Z), or dragging one over another, left the COVERED
  window's scrollbar bleeding through as a dark bar across the middle of the
  window on top.  X11 only -- the ncurses build composites a single cell grid
  (last-writer-wins) and never had it.  Cause: the modern fluid scrollbars are
  painted directly to the Cairo surface by wpe_render_chrome(), once per window
  every frame, after the cell render and with no z-order clipping, so a covered
  window's scrollbar drew on top of the window covering it (invisible to the
  SCREENCELL/extbyte diff renderer).  Fix: clip each window's scrollbars to its
  visible region -- its rectangle minus the windows stacked above it -- with a
  Cairo even-odd fill-rule clip, the same z-order contract ncurses panels
  (update_panels, bottom-to-top, topmost-wins) and Turbo Vision (clip every
  write to the exposed region) use.  Test: tests/x11/test_window_zoom_redraw.py.

  Terminal / console mode:
* Borland "User Screen" (Alt-F5) restored on the console.  After Ctrl-F9 Run
  (or a debug session) a program that PAINTS -- cursor positioning, ANSI
  colour, a TUI, a progress bar -- cannot be shown in the line-oriented
  Messages window.  Alt-F5 (and Window -> User Screen) now leaves the editor
  and shows the program's own full screen: e_t_user_screen replays the
  captured output VERBATIM (no headers, no \n rewriting), so it renders
  exactly as the program left it.  The Ctrl-F9 run path now accumulates the
  raw byte stream into the same buffer the debugger uses (e_d_prog_output),
  so the User Screen is populated however the program was launched.  Press
  any key to return to the editor.  Console (wpe) only for now; the xwpe
  (X11) integrated terminal panel is the 1.6.4 path.  Demo: tests/inputs/
  paint.c (F9, Ctrl-F9, Alt-F5).
* Console mouse (GPM) restored in wpe.  On a bare Linux VT the mouse pointer
  stopped moving and clicks stopped registering: wpe opens the GPM connection
  itself (so ncurses does not), but the event-driven input rewrite left GPM's
  fd out of the poll set, so nothing ever drained it -- WpeGpmHandler never
  ran, so GPM_DRAWPOINTER never drew the pointer and e_mouse was never filled.
  The fix registers gpm_fd in the wpe_fd_poll multiplexer (the same one the
  debugger uses): WpeGpmReadable drains each event, draws the pointer and fills
  e_mouse, and a button event makes the input loop return a mouse keycode.
  Terminal emulators are unaffected (no GPM there, so the fd is not registered;
  ncurses' xterm-sequence mouse path is unchanged).
* A 3-byte buffer overread in the signal handler that disables xterm mouse
  modes (WpeSignalUnknown) -- the write length counted 27 for a 24-byte
  string -- fixed (flagged by -Wstringop-overread).
* Scrollbar glyphs fixed on the Linux console.  The ncurses sp_chr[] table
  drew the track with ACS_S9 (a thin scan line) and the thumb with
  ACS_DIAMOND; the `linux` terminfo does not map those to good glyphs, so on
  a real VT the track fell back to a column of 's'/'_' and the thumb to a
  poor diamond -- the worst-looking part of the console UI.  Now the track
  uses ACS_CKBOARD (the CP437 shaded board, 0xB1) and the thumb ACS_BLOCK
  (solid block, 0xDB); both are in the linux acsc and in every VGA/console
  font, so the scrollbar reads as a shaded gutter with a solid slider,
  matching the X11/Xft look -- with no special console font.  (Terminal
  emulators, which have a full acsc, also get the nicer shaded/solid look.)

  User interface (Borland fidelity):
* Message and dialog popups (e.g. "String not found", the Options dialogs)
  no longer carry a maximize/zoom box.  Borland's TDialog message boxes
  never had one -- a popup is fixed-size, so the control was inert and
  non-authentic.  Popups keep a single close box, modernized to the
  top-right corner, and clicking it now dismisses the popup, the same as
  pressing Esc (e_opt_mouse / e_error).  Document (editor) windows are
  unaffected and keep their zoom box.

  Testing:
* Per-menu regression suites for every menu-bar section (System, File,
  Edit, Search, Block, Options, Window, Project, Run, Debug) in BOTH
  front-ends -- pyte/VT100 for wpe and the headless Xft suite for xwpe --
  because shared code can behave differently in each (the WordStar block
  fix above was found exactly this way).  Added coverage for the contextual
  bottom hint bar (it swaps its function-key set with the focused window),
  Toggle Breakpoint, and the popup close box.  The X11 suite runs by
  default in tests/run-tests.sh and skips cleanly without an X stack.
* Double coverage: every menu item is now exercised BOTH through the menu and
  through its advertised KEYBOARD SHORTCUT, in both front-ends.  The shortcut
  path is a separate decode (e_prog_switch / e_tst_dfkt for the terminal,
  we_xterm.c for X11) and is where mapping bugs hide -- an item can work from
  the menu while its accelerator is dead.  Covers Cut/Copy/Paste/Undo/Redo,
  Show Buffer (Alt-Y), Save (F2), Find (F4), Search again (^L), Go to Line
  (Alt-G), Run (Ctrl-F9 and Alt-U), Make (F9), Toggle Breakpoint (^G B), Zoom
  (Alt-Z), Next (Alt-N), List All (Alt-0) and the close box.
* Window chrome regression tests: tests/x11/test_window_zoom_redraw.py asserts a
  covered window's scrollbar does not bleed through the window stacked over it
  (the X11 z-order chrome-clip fix above).  Four scenarios: a full zoom-cover, a
  diagonal cascade, a click-to-raise that re-orders the stack (guards indexing
  the chrome by z-level, not window number), and a 3-window double-cover built
  via Size/Move that guards the cairo_region_t set-difference clip -- the case
  an even-odd path got wrong, where window 1's scrollbar reappeared in the band
  it shares with two windows above it.
* Block menu WordStar chords double-covered (tests/test_block.py +
  tests/x11/test_block_marking.py): every Block op is driven through both the
  Alt-B menu and its ^K chord (^K X/^K Y/^K I/^K U), the separate decode where
  the ^K B mode bug hid.
* Mouse interaction tests (tests/x11/test_mouse_scroll.py,
  test_mouse_menu.py): the mouse wheel (WPE_SCROLL_UP/DOWN), the fluid
  scrollbar-thumb drag (vertical and horizontal, e_scroll_drag_v/h driven via
  real X11 button/motion events), and menu-bar access by click (open a menu,
  invoke an item).  New conftest drag()/wheel() helpers.
* Console input regression-tested: Ctrl-F9 Run, the Esc meta-prefix
  (Esc-<letter> = Alt-<letter>) and the Alt-F5 User Screen
  (tests/test_user_screen.py, test_menu_run.py).
* Headless X11 harness: function keys are now injected by keycode, working
  around an xdotool keysym-resolver bug (issue #491) that delivered Alt-Fn
  instead of bare Fn; this unblocked the F2/F3/F4/F9 shortcut tests.

  Build and portability:
* The --without-x build compiles and links again.  The fluid-scrollbar-drag
  feature had placed Xlib-using helpers (e_scroll_drag_v/h) and Cairo chrome
  hit-tests (wpe_chrome_hit_vthumb/hthumb) in the shared mouse code without
  NO_XWINDOWS guards.  Now the X11-only helpers are guarded and the header
  provides no-op stubs for the hit-tests under NO_XWINDOWS.
* Removed the ad-hoc /tmp debug probes left across the source during
  bug-fixing (the gated jdb_trace facility and the crash handler are kept).

  Documentation:
* Help -> Info now opens xwpe's OWN manual (the installed xwpe.info), not the
  system-wide Info directory listing every package: e_read_xwpe_info loads its
  Top node and keeps in-manual navigation inside it, falling back to the system
  directory only when xwpe.info is not installed.
* Texinfo manual updated for 9 compilers, pdb debugger, Messages
  buffer, cursor-relative error navigation.
* New tutorial examples in docs/examples (shipped via EXTRA_DIST) and
  documented in the manual: headers_test.c (automatic header-dependency
  recompilation, ignoring // and #if 0 includes), debug_constructor.c
  (debugger start symbol, breaking before main), interactive_test.c
  (interactive stdin in the Messages window).  Also added the previously
  undistributed debug_test.{java,py}, debug_test_long_output.py and
  compile_error_test.{py,tex} to EXTRA_DIST.
* CHANGELOG Known Bugs section reorganized: verified, fixed, or
  confirmed present with reproduction steps.

1.6.2 (Xft rendering, debugger resurrection, UTF-8 everywhere)

  SCREENCELL migration completion:
* Fixed display artefacts after closing menus, dialogs, and compile
  popups. calloc'd SCREENCELL buffers + altschirm invalidation.
* Fixed SEGFAULT in e_t_refresh with negative SCREENCELL int values.

  Compiler integration:
* Error navigation (Alt-T/Alt-V) updated for modern GCC file:line:col:.
* print_to_end_of_buffer no longer drops output after blank lines.
* Updated defaults: gfortran, fpc, javac, python3, pdflatex.
* Cursor returns to source file after successful F9 compile.
* Compiling popup has visible borders (was white-on-white).

  Debugger:
* Fixed gdb unable to start (WpeSignalChild, spurious e_p_exec calls).
* Program output capture via pty + fflush(0) after each step.
* Restored Ctrl-G P (User Screen), broken since Payne era.
* jdb (Java Debugger) as 5th backend. Auto-selects for .java.
* pdb (Python Debugger) as 6th backend. Auto-selects for .py.
  Breakpoints, F8 stepping, auto-skip of internal <string> frames.
* Fixed 30-year-old pipe fd leak in e_exec_deb (caused busy loops).
* Fixed SIGPIPE crash on debugger exit.
* Ctrl-G P now appends program output to Messages buffer with scroll,
  replacing the old endwin/popup/xterm design. Works in both wpe and
  xwpe without context switching.

  Mouse:
* ncurses mouse for terminal emulators (xterm protocol).
* GPM migrated from direct Gpm_Open() to ncurses-managed GPM.

  Languages:
* Python: syntax highlighting (35 keywords), F9 (py_compile), pdb.
* LaTeX: syntax highlighting (31 keywords), F9 (pdflatex).
* Ctrl-F9 Run for interpreted languages: runs interpreter directly
  via popen, output captured to Messages (no xterm in X11 mode).

  X11 mode -- Cairo+Pango rendering (Wayland-ready):
* Cairo+Pango replaces Xft for all text rendering. The rendering
  pipeline (schirm model -> diff -> draw -> Pixmap -> window) is
  backend-agnostic: same Cairo calls work on X11 (cairo_xlib_surface)
  and Wayland (cairo_image_surface). WpeRenderBackend abstraction
  following Kruse's function pointer pattern.
* cairo_ft for ASCII (FreeType via Cairo, zero-alloc per glyph) during
  scrollbar drag. Pango for Unicode fallback (emoji, CJK). Prevents
  Pango heap corruption under sustained heavy load.
* System monospace font detection (gsettings org.gnome.desktop.interface
  monospace-font-name). Uses the same font as the user's terminal.
* Modern scrollbars: thin proportional track (4-5px), proportional
  thumb, triangular Cairo arrows. Both vertical and horizontal.
* Fluid scrollbar drag: MotionNotify events with XGrabPointer +
  XCheckTypedWindowEvent coalescing. Text scrolls in real time.
  Cursor visible at file position during drag, repositions to
  viewport center when scrolled off-screen (Emacs-style).
* Mouse wheel scroll (Button4/Button5): 3 lines per tick, same
  viewport-independent cursor behavior as drag.
* Title bar buttons: Unicode ◻ maximize and ✕ close, right-aligned,
  replacing the Motif-era [◆] close from the left side.
* Resize flicker prevention: _NET_WM_SYNC_REQUEST protocol,
  StaticGravity, BackgroundPixmap None, ConfigureNotify compression,
  Expose skip during pending resize.
* Window move flicker elimination: e_move_window_recompose() replaces
  the old e_close_view/e_open_view cycle that caused two flushes per
  drag step (one showing desktop without window, one showing window at
  new position). Full compositor-style recomposition: restore old
  background, save new background, draw borders + content, single
  flush_all. WpeRender.blit added for future Pixmap-to-Pixmap copy
  optimization at 4K resolutions.
* Single-flush architecture: fk_show_cursor no longer flushes per cell.
  All rendering (content + chrome + cursor) composes into the Pixmap,
  one XCopyArea per frame. Same pattern for Wayland (wl_surface.commit).
* Previous Xft rendering preserved as fallback (--without-cairo).
* Color emoji via Noto Color Emoji (libXft 2.3.5+ BGRA support).
  Font fallback uses fontconfig with FC_COLOR for emoji codepoints.
* Pixmap double-buffering: all rendering goes to an off-screen Pixmap,
  copied to window with XCopyArea.
* CELL_WIDE buffer model: SCREENCELL gains a flags field with
  CELL_WIDE and CELL_WIDE_SPACER. Wide characters (emoji, CJK) are
  rendered across 2 cells, cursor skips spacer cells, delete removes
  both cells, mouse click on spacer maps to the wide char. Same
  pattern used by st, vim, and alacritty.
* UTF-8 in syntax highlighting (e_pr_c_line): multi-byte characters
  are decoded and rendered correctly with CELL_WIDE flags.
* Fixed crash on startup (uninitialised y, 81MB writev to X server).
* Fixed ACS characters as squares (e_x_map_char).
* Fixed extbyte garbage (calloc for border arrays).
* Fixed Ctrl-G P crash (e_u_deb_out was NULL in X11 mode).
* Fixed popup/menu/File Manager border echoes: extbyte interior
  clearing in e_x_refresh prevents hidden windows' borders from
  bleeding through. Full repaint on window close and switch.
* Fixed e_x_system prepending "./" to interpreter commands.
* XSetInputFocus returns focus to xwpe after debug xterm opens.
* Window resize support via ConfigureNotify + Pixmap recreation.

  Other fixes:
* Fixed 33-year-old Redo crash (SIGSEGV) after search/replace undo.
* Fixed menu bar not redrawn after Ctrl-F9 Run.
* Fixed File Manager Tab cycling and panel scroll.
* Fixed System Info dialog overflow (e_pr_str_wrap, e_pr_text).
* Fixed e_show_error basename fallback for relative paths.
* Fixed gdb compiling "Messages" instead of source file.
* Integrated Texinfo manual into help system.
* 33 automated pyte tests.
  (Juan Manuel Mendez Rey)

1.6.1 (Modern autotools build system)
* Replaced 1998 hand-written configure.in + Makefile.in + 1500-line custom
  aclocal.m4 (T.E.Dickey macros for SCO/HP-UX/AIX portability) with
  standard configure.ac + Makefile.am (automake).
* ncursesw detection via PKG_CHECK_MODULES instead of fragile AC_CHECK_LIB
  cascade. All required flags (-std=gnu17, -DNCURSES, -DNCURSES_WIDECHAR,
  -D_XOPEN_SOURCE_EXTENDED) are set automatically by configure.
* Optional X11 (--without-x) and GPM (--without-gpm) configure flags.
* AC_CONFIG_HEADERS generates config.h, force-included via -include so no
  source files need modification.
* Compatible with dh_autoreconf (autoconf 2.69+), eliminating the need to
  skip autoreconf in Debian builds.
* Dropped legacy portability defines: NOSTRSTR, NOTPARM, DEFTPUTS, DEFPGC,
  RANDLIB, CC_HAS_PROTOS, NOSYMLINKS, TERMCAP, XDB.
  (Juan Manuel Mendez Rey)

1.6.0 (Gold Standard: UTF-8, terminal resize, modern keyboard)
* SCREENCELL buffer model: replaced the 1993 byte-based screen buffer
  (1 byte = 1 cell) with a structured SCREENCELL type (int ch + int attr
  per cell). Each cell now holds one complete character, including wide
  characters (accented Latin, Cyrillic, CJK, emoji). Multi-byte UTF-8 is
  decoded via mbrtowc() in the display loop and stored as a single value.
  This is the first change to the screen buffer model since Fred Kruse's
  original 1993 design, building on Dennis Payne's 12 years of
  continuation work (X11 support, GPM mouse, autoconf, and numerous
  bug fixes through 2006).
* Terminal refresh rewritten: e_t_refresh() uses add_wch() for wide
  characters and wcwidth() for characters that occupy 2 visual columns
  (emoji, CJK). Borders and scrollbar use fixed screen positions via
  move(j, i), ensuring correct alignment regardless of UTF-8 content.
* Terminal resize support: handle ncurses KEY_RESIZE to detect terminal
  size changes. Reallocate screen buffers, expand windows to fill new
  size, and repaint. Works in tiling window managers, tmux, and any
  terminal that supports resize.
* Keyboard mapping fixed for modern PC keyboards: PageUp, PageDown,
  Home, End, Insert, Delete now map correctly. The original mapping was
  designed for VT100/Sun terminals from the 1990s.
* Link against libncursesw (wide character ncurses) instead of libncurses.
  Build with -I/usr/include/ncursesw, -D_XOPEN_SOURCE_EXTENDED,
  -DNCURSES_WIDECHAR=1.
* Scrollbar aesthetic: track uses ACS_S9 (scan line) and thumb uses
  ACS_DIAMOND instead of ASCII '+' and '0'.
* GPM mouse: check for /dev/gpmctl before calling Gpm_Open() to avoid
  "gpm: not found" error when gpm daemon is not installed.
* Full screen refresh via e_abs_refr() in e_schirm() prevents stale
  content when switching between windows.
* Test suite: 20 automated tests using pyte (VT100 terminal emulator)
  and pytest. Tests cover border alignment, UTF-8 display, emoji/wide
  chars, scrollbar during scroll, resize, and keyboard mapping.
  (Juan Manuel Mendez Rey)
* Compatibility note: xwpe 1.6.0 requires libncursesw (wide character
  ncurses) instead of libncurses. On Debian/Ubuntu: libncursesw5-dev.
  All major Linux distributions have shipped libncursesw as standard
  since at least 2010. Terminals without UTF-8 support still work for
  ASCII content; characters above 127 display as '?' instead of the
  previous '@' escaping.

1.5.31 (Mendez-era rescue release)
* Project resumed under new upstream maintainer Juan Manuel Mendez Rey
  <juan.mendezr@proton.me>, with the explicit blessing of Dennis Payne.
  Project home moved from identicalsoftware.com to
  https://codeberg.org/mendezr/xwpe
* Fix link error: rename WpeGpmInit prototype in edit.h to the actual
  function name WpeGpmMouseInit. (Guus Bonnema, via github.com/amagnasco)
* Fix segfault on startup when MALLOC fails before the screen subsystem
  is initialised. (Guus Bonnema, via github.com/amagnasco)
* xwpe.1_eng: escape hyphens (-r, -1, ...) so groff renders them as
  ASCII hyphens; correct .TH section field from "alpha" to "1".
  (Jari Aalto, from Debian NMU)
* we_debug: disable conflicting K&R prototype for tparm() that broke
  compilation against modern <term.h>. (Francesco P. Lovergine, Debian)
* we_wind: parenthesise strcpy() to defeat unsafe glibc macro expansion
  in e_schr_lst_wsv(). (Jari Aalto, from Debian NMU)
* we_fl_unix, we_prog: fix -Werror=format-security in printf calls that
  passed e_msg[ERR_HITCR] directly as the format argument.
  (Francesco P. Lovergine, Debian)
* we_debug, we_prog: add missing prototype for print_to_end_of_buffer
  so the package compiles under -Werror=implicit-function-declaration
  (GCC 14+, hard error in GCC 15). (Francesco P. Lovergine, Debian,
  Closes: Debian #1066254)
* we_unix: restore termios and emit terminal reset on clean exit so the
  user's terminal is left usable after wpe quits. (Juan Manuel Mendez Rey)
* we_opt: clamp mouse-derived index in colour dialog to prevent OOB
  read on the FARBE colour array. (Juan Manuel Mendez Rey,
  Closes: Debian #228820, a 22-year-old upstream bug)
* This release also resolves Debian #1098180 (FTBFS with GCC 15) since
  the patch stack that triggered the FTBFS is now fully integrated.

After this release, the Debian package can ship with zero local patches.

1.5.30a
* Some additional comments
* Fedora core 4 compile fix

1.5.29a
* Fixed regression of XLookupString.

1.5.28a
* Corrected e_quit to properly quit the debugger.
* Corrected function help bug.
* Changed X11 colors to correspond to curses order.
* Disabled different pointers under X11 and add file manager menu item.
* Added Paulo C�sar Pereira de Andrade's partial i18n patch.

1.5.27a
* More modifications from Roman Levenstein.  Not enabled by default; use
     -DNCURSES.
* Fixed bzipped man pages.
* Added Arjan van Dijk's XLookupString bug fix.
* Fixed some syntax coloring problems.
* Use mkdtemp if available.
* Changed compiler message strings to use "\n" instead of putting in a
     return character directly.
* Added Harry Berman's minor memory bug fixes.

1.5.26a
* Spent more time fixing path limits.
* Fixed blank project executable bug.
* Fixed "../" bug introduced in 1.5.25a.
* Autosaves no longer create backup files.
* Apply Martin's translations.  No more german comments.

1.5.25a
* Fixed copy and paste bug.
* Removed some of the path and file name limits.
* Fixed seg fault when no X paste information.

1.5.24a
* Fix for using gdb with ncurses 5.
* Changed to use _POSIX_PATH_MAX instead of 80 in some case.  Needs work.
* Fixed options saving so that it doesn't save colors incorrectly.
* Cleanup use of dtmd variable.
* Fixed the loading and saving of MSDOS text files (no conversion to Unix
     format yet).

1.5.23a
* Added another zoom fix by Mark Spieth
* XSelection was added to 1.5.22a but forgot to get mentioned in the
     CHANGELOG.
* Added Fritz's Y2K patch.
* Converted info reader to use zlib.  Zlib may be required in the future.
* Allow tempnam() to create the temporary directory name if available.
     This function may also be required in future releases.

1.5.22a
* Made the makefile once again check for existence of directories
     because SCO's mkdir -p spits out an error.
* Changed keyboard commands back to Turbo C bindings.  (Previous
     information was wrong.)
* Added Guido Draheim's patch to optionally use ~ for backup files.
* Did away with the NEWSTYLE colors.  (Still need to make the colors more
     pleasant for NEWSTYLE.  Ideally I like to be able to choose new or
     original style at the command line.)
* Added Martin's patch for max column problems.

1.5.21a
* Added Mark Spieth's patch to fix some zoom stuff and remove the file
     handle leak.
* Added Matej Vela's patches to the makefile and fix for e_mk_info_path.
* Fixed the bug in running the debugger and executable.
* Heavily modified Brian's path to make projects store relative paths.
* Modified compile, make, and run keyboard commands to Turbo C bindings.
* Added part of Brian White's SCO man page fix.

1.5.20a
* Fixed until end of line comment to allow for characters beginning with
     alphanumeric characters.
* Fixed bugs in search and replace.
* Fixed stop on error bug when compiling.
* Heavily modified German Garcia's patch for multiple extensions for a
     compiler.
* Fixed delete redo bug.

1.5.19a
* Fix for mouse resize window/zoom bug based on Pedro's patch.
* Made the print command an editor option.
* Fixed gpm bug with a modification of Sebastiano's patch.
* Modified Mark Spieth's patch for new titling method.  (Editor option to
     use the old style.)
* Added Mark's patch to read xwperc from local directory if it exists.

1.5.18a
* Pedro's fix for Option->Editor bug
* Checks file system for the ability to do autosave/emergency save files.
* Supports multiple extensions per language syntax.
* More of Pedro's file-manager massage.
* Added Alexander's change case dialog.
* Hopefully fixed all AIX and OSF compile problems.

1.5.17a
* Fixed X windows argument bug.
* Now defaults to 8x13 font when non-fixed width used.
* Incorporated Pedro's memory bug fixes and menue code cleanup.
* Also incorporated Pedro's file-manager cleanup.

1.5.16a
* Fixed configure script bug.

1.5.15a
* Changed xwpe to compile X11 and term code into shared libraries.  However
     the configure script has yet to be changed to build it.
* Fixed a leaky file descriptor and memory bug in we_debug.c code.
* Added Brian's patch for inserting selected text into search and replace
     dialogs.
* Incorporated Roman's goto cursor and finish function debugger features.
     (Only gdb support.)

1.5.14a
* Changed local variable reg to regz because some systems defined this to
     register.
* Added private colormap option.
* Fixed slackware compile problem.
* Partially fixed the maximum column and cut & paste problem.

1.5.13a
* Added Alex Buell's patch for the new glibc.
* Removed support for editing binary files.
* Added Brian's patch to Search and Replace.
* Added Martin Zampach's patch to adjust breakpoints when new lines added and
     to load/save the breakpoints/watches with the project.
* Started incorporating Roman Levenstein's changes.  POSIX regular
     expressions for matching and replacing.  Ctrl-F1 now looks up the
     current function.

1.5.12a
* Added Brian's patch to only allow a file to be loaded once in an earlier
     version but forgot to record it.  This version includes some fixes
     to that since it wasn't working.
* Added Yarick Rastrigin's tab patch.  Doesn't allow tabbing through
     buttons but a welcome improvement (particularly for the replace dialog).
* Incorporated some of James M.'s man page update.

1.5.11a
* Added Brian O'Donnell's changes to projects.  Includes loading projects
     from file manager and the "edit" now loads the file for editing.
* Chaged xwperc into a text file.  Removed ability to load only color info.
* Some DJGPP ifdefs have been removed since it seems unlikely xwpe will
     support DJGPP in the near future.

1.5.10a
* Fixed long filenames from crashing xwpe when the window is shrunk.
* Added Duane Penzien's wastebasket delete bug fix.  Should probably display
     some message when this occurs.  It currently does nothing.
* Added Anthony Barbachan's patch for newer terminfo.
* Repaint the desktop no longer resizes all windows.

1.5.9a
* Fixed compilation problem on older curses systems.
* Added Brian O'Donnell's save patch.  When a file is modified an asterisk
     now appears near the bottom left.  The asterisk disappears when the file
     is saved instead of poping up a message box.

1.5.8a
* Fixed gzipped man page bug.
* Directory can no longer be loaded as files but it doesn't display an error
     message.
* Incorporated Sebastiano Suraci's Linux and gpm patch.  (Mostly)
* Xterm define and uninitialized strings fixed by Mark Loftis.
* Some spelling fixes.
* Lots of formating changes.
* German comments converted to English thanks to Ronald Holzloehner.

1.5.7a
* Fixed watch window segmentation fault problem.
* Fixed NO_XWINDOWS compilation.
* Fixed autosave bug.
* Fixed configure script.
* Incorporated Stefan Hille's remaining changes.

1.5.6a
* New configure script.  aclocal.m4 is taken from lynx 2.8.1 dev 4.
* Fixed remove bug (working mouse cursor stayed after removing file).
* Fixed some cut and paste bugs.
* Fixed no windows zoom bug.
* Fixed up prototypes in edit.h and progr.h.
* Incorporated some of Alexander Neundorf's changes.
* Incorporated Kenn Flynn's file manager patch.
* Incorporated Alexei Pavlov's changes to autoindenting.

1.5.5a
* Added Mark Loftis's changes to scrolling functionality.
* Incorporated more changes from Stefan Hille.
* Removed some warnings.

1.5.4a
* Changed naming from ALPHA-x to 1.5.xa.  The change from 1.4 to 1.5a does
     not represent a great change in functionality instead marks the change
     of maintainers
* Removed all remenents of dos support.
* Added some changes from Stefan Hille such as messages.h.
* Changed handling of expose event under X Window so that it only redraws
     part of the screen.
* Added Java syntax to the syntax definition file.
* Deciphered purpose of the help.key (formally we_help_str) file.  This file
     gives the section of help that should be loaded while xwpe is in
     different states.  Each line corresponds to a mode.  The modes are as
     follows:
       1  - Standard file                14 - Project menu
       2  - Watch window                 15 - Option menu
       3  - Stack window                 16 - Window menu
       4  - Message window               17 - Help menu
       5  - Most file-manager windows    18 - Function-Index window
       6  - Execute file-manager window  19 - Grep window
       7  - System (#) menu              20 - Find window
       8  - File menu                    21 - Project window
       9  - Edit menu                    22 - Variables window
       10 - Search menu                  23 - Install window
       11 - Block menu                   24 - Windows window
       12 - Run menu                     25 - Top menu bar
       13 - Debug menu                   26 - Help window

ALPHA-3
* Fixed bug in WeExpArr.c
* Fixed debugging problem found by Oliver Wilson.
* Several other minor cleanups throughout the code.
* Some documentation fixes as well.
* Removed support for encrypted files (if enough people request it, the
     encrypted file support could be added again but for the time being we
     feel this is not needed in a development environment).

ALPHA-2
* Fixed bug in syntax highlighting for FORTRAN.
* Lots of old code that had been commented out were removed.
* Some functions from we_progn.c have been renamed and moved to WeSyntax.c
* Renamed all configuration files.
* Changed format of syntax_def (formerly we_synt_def).  This will almost
     certainly change again since the current format is difficult to use.
* Documentation plan:
     1) INSTALL - all compiling instructions and installation needs (not
          started)
     2) README - simply states what the program does (not started)
     3) xwpe.1 - describes configuration files and command line options
          (mostly done)
     4) help.xwpe - on-line documentation describing all use (command line
           options updated but not configuration files and anything else)

ALPHA-1
* All patches were applied except the gpm patch.
* SWAP and NOWEUNDO have been removed since they don't work.
* Some other defines have been removed as well.
* Some functions from we_hfkt.c have been renamed and moved to WeString.c
* Some functions from we_xterm.c have been renamed and moved to WeXterm.c
* Modified X resources a lot making the documentation incorrect.

