Skip to content

Add Apple Silicon macOS support - #19

Open
mirashif wants to merge 2 commits into
omacom:masterfrom
mirashif:macos-support
Open

Add Apple Silicon macOS support#19
mirashif wants to merge 2 commits into
omacom:masterfrom
mirashif:macos-support

Conversation

@mirashif

Copy link
Copy Markdown

No description provided.

@omarchybot

Copy link
Copy Markdown
Collaborator

Reviewed this from the Linux side, since that is the platform anyone here can actually test. Short version: the Linux build, the tests and every documented Linux shortcut survive intact, and the two things worth flagging are small.

Verified on a clean Arch box with Qt 6.11.1:

  • ./bin/test passes — 13 tests, including the new detectsSystemAppearanceWithoutRequiringAPortal. ./bin/build still produces build/omawrite, so the literal install -Dm755 build/omawrite in pkgbuild/PKGBUILD is unaffected by the new VERSION = 0.1.0.
  • The brew branches in bin/build and bin/test never run on Linux. I put a brew on PATH that touches a marker file and rebuilt; the marker was never created, because elif stops at qmake6 and the later conditions — including the $(brew --prefix qtbase) substitution — are never evaluated.
  • JOBS cannot come out empty: on Linux sysctl -n hw.logicalcpu exits 1 and prints nothing to stdout, so || echo 1 wins and make -j never runs unbounded.
  • linux: QT += dbus matches on Arch — the link line pulls in libQt6DBus.so — and moc still sees Q_OS_LINUX through qmake's moc_predefs.h, so handlePortalSettingChanged is present in the generated meta-object and the portal SettingChanged connection still binds. Live theme and text-size changes keep working.
  • The StandardKey conversions keep every binding the README documents. QKeySequence::keyBindings(QKeySequence::Redo) on Qt 6.11.1 is Ctrl+Y | Alt+Shift+Backspace | Ctrl+Shift+Z | Redo under every keyboard scheme I tried, and sequences: grabs all of them, so Ctrl+Y still redoes. Side effect worth knowing about: StandardKey.Undo and StandardKey.Redo also bind Alt+Backspace and Alt+Shift+Backspace now.

Two things for you:

  1. src/Main.qml:361Shortcut.nativeText returns only the first entry of sequences, so the fullscreen row of the Ctrl+? dialog now reads Meta+F Fullscreen where it used to read F11 / Super+F Fullscreen. F11 still works, it is just no longer discoverable from the dialog, and Meta is not what an Omarchy user calls that key. Left it alone rather than picking a wording for you, but that row wants spelling out on the Linux branch.

  2. pkgbuild/Info.plist replaces Qt's generated plist wholesale and drops NSPrincipalClass (NSApplication), which Qt's own mkspecs/macx-clang/Info.plist.app template sets. No Mac here to check on, so this is a question rather than a claim: did you confirm the bundle activates and gets its menu bar without it? The same file hardcodes 0.1.0 where @SHORT_VERSION@ would track VERSION in omawrite.pro, so the two will drift at the next release.

One scheduling note: PRs #14, #15, #17 and #18 all touch src/Main.qml, and #15 and #17 add rows to the same shortcuts dialog this rewrites — whichever lands second will need a rebase.

@stefan-ernst

Copy link
Copy Markdown

Tested this PR on an Apple Silicon Mac:

  • macOS 15.7.3 (arm64)
  • Homebrew Qt 6.11.1 (qtbase, qtdeclarative, and qttools)

The PR successfully builds a native arm64 application bundle. After deployment and ad-hoc signing, omawrite.app passed deep code-signature verification, linked against its bundled Qt frameworks, and launched successfully. I did not need to modify the source code.

I encountered two issues worth noting:

  1. ./bin/package-macos-arm64 did not complete end to end in this environment. macdeployqt reported that it could not resolve @rpath/QtSvg.framework/Versions/A/QtSvg, and the script exited before creating the DMG. The deployed app nevertheless contained QtSvg.framework and passed codesign --verify --deep --strict after I re-ran the ad-hoc signing command. I then created and verified the DMG manually with hdiutil. The resulting 48 MB DMG is valid and contains the working arm64 app. The packaging script may need a small robustness adjustment for Homebrew's split Qt packages or newer Qt releases.

  2. The test suite reported 12 passes and one failure. OmawriteTest::ignoresFileWatcherEventsForSavedContents() consistently expected one external-change event but observed zero at tests/tst_omawrite.cpp:118. I reproduced this by running that test alone. This may be macOS filesystem-event timing or an artifact of the test environment, but it is reproducible here.

Overall, the macOS port itself works and the app runs well.

The conversion to StandardKey.Redo silently dropped Ctrl+Y, which README.md still documents. Qt resolves StandardKey through the platform theme's keyboard scheme, and Omarchy's sessions export QT_QPA_PLATFORMTHEME=gtk3, so keyBindings(Redo) comes back as Ctrl+Shift+Z and the hardware Redo key with no Ctrl+Y.

This is invisible to the test suite because bin/test runs offscreen, where the scheme resolves differently and Ctrl+Y is present -- measuring there says the binding survived when on a real desktop it did not.

macOS keeps the plain standard key, since Qt maps Ctrl to Command there and Cmd+Y is not a redo shortcut.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Co-Authored-By: Codex XHigh <noreply@openai.com>
@omarchybot

Copy link
Copy Markdown
Collaborator

Re-reviewed after @stefan-ernst's macOS run. The head had not moved, so this is about that report plus one thing we got wrong last time.

We were wrong about Ctrl+Y, and it is a real Linux regression. Last time we said StandardKey.Redo keeps Ctrl+Y on Linux. It does not, on the desktop Omarchy actually ships. Qt resolves a StandardKey through the platform theme's keyboard scheme, and Omarchy's session exports QT_QPA_PLATFORMTHEME=gtk3, so on a real Hyprland session QKeySequence::keyBindings(QKeySequence::Redo) returns Ctrl+Shift+Z and the hardware Redo key — no Ctrl+Y. README.md:40 still documents Ctrl+Y, and before this PR src/Main.qml bound it explicitly.

What hid it from us, and from the suite: bin/test runs QT_QPA_PLATFORM=offscreen, and under offscreen the same call returns four bindings including Ctrl+Y. Measured on a worker at this head, same binary, only the platform differing:

offscreen       Redo (4):  [Ctrl+Y]  [Alt+Shift+Backspace]  [Ctrl+Shift+Z]  [Redo]
real wayland    Redo (2):  [Ctrl+Shift+Z]  [Redo]

That is worth knowing beyond this PR: no test run through bin/test can catch a shortcut that a real session resolves differently.

Pushed 1f9a026 to this branch, restoring Ctrl+Y alongside the standard key and leaving macOS on the plain StandardKey.Redo, since Qt maps Ctrl to Command there and Cmd+Y is not a redo shortcut. It uses the same Qt.platform.os === "osx" form you already use for the replace and fullscreen shortcuts. Verified on a worker under the real compositor: the sequence list goes from 2 to 3 and the mixed standard-key-plus-string list is accepted. ./bin/test is 13 passed, 0 failed both before and after.

On the macOS report. It is a genuine result and the first real evidence the port works, but it is one machine, and it is worth being precise about what it settles. It shows the branch compiles, deploys, signs and launches on macOS 15.7.3 arm64 with Homebrew Qt 6.11.1. It does not show the platform branches are maintainable, and nothing here can regress-test them — we have no Mac, so every macOS-only path in this PR is still unexecuted on our side.

  1. macdeployqt failing on @rpath/QtSvg.framework looks like a real gap rather than a local quirk. bin/package-macos-arm64:67 asks the selected qmake — the one from qtbase — for a single QT_INSTALL_LIBS, and passes only that at line 74. Homebrew puts each Qt module in its own prefix, so QtSvg lives outside the directory qtbase's qmake reports, and the comment at lines 69-71 anticipating exactly this problem is one path short. -libpath is repeatable, so adding a second occurrence when brew --prefix qtsvg exists is probably all it needs. Your call how to shape it; we cannot test it.

  2. The ignoresFileWatcherEventsForSavedContents() failure is not caused by this PR. src/backend.cpp and src/backend.h are byte-identical to master on this branch, and the test itself is unchanged apart from being shifted six lines by the new appearance test. The same test at the same head passes on Linux on a worker.

    It is still worth not filing under timing, though. QTRY_COMPARE waits up to five seconds while pumping events, so a reproducible zero is not ordinary FSEvents latency. And the earlier assertion at tests/tst_omawrite.cpp:112 cannot tell two very different causes apart, because both give zero: either no fileChanged ever arrived for that file on macOS, or one did, the lambda at src/backend.cpp:100 correctly suppressed it as our own save and called watchCurrentFile(), and the removePaths/addPath re-arm at src/backend.cpp:568 did not leave a live watch. Checking m_fileWatcher.files() after the re-arm would separate them. Either way the user-visible consequence on macOS is that external edits may go unwarned and a later save can overwrite them — a pre-existing gap that only becomes reachable if macOS ships.

Still open from last time. The fullscreen row of the Ctrl+? dialog still reads Meta+F where it read F11 / Super+F; confirmed again at this head by rendering the dialog's expressions on a worker, where every other row comes out identical to the old hardcoded string and only that one changes. This PR also drops the README's "Qt maps this key as Meta+F" note, which is what used to explain the name to a Linux reader. Left alone again — the wording is yours to pick. On pkgbuild/Info.plist, the missing NSPrincipalClass now looks harmless given your bundle launched with its menu bar, so treat that question as answered; the hardcoded 0.1.0 is still a drift trap against VERSION in omawrite.pro.

Scheduling. The set of open PRs touching src/Main.qml has grown since last time — #14, #15, #17, #18 and now #21, #25 and #26. Several add shortcuts, and this PR changes the dialog from a hardcoded string to generated nativeText, so whichever lands second will need a rebase rather than a clean merge.

Reviewed by Claude Opus 5 and by Codex at xhigh reasoning. Codex raised the Ctrl+Y loss, which is the one finding neither our previous pass nor the test suite caught; we then confirmed it on a worker and it is why 1f9a026 exists. Codex also filed a High claiming moc could not see Q_OS_LINUX, so the portal SettingChanged slot would be missing and live theme and text-size changes would be dead on Linux — we rejected it, and codex withdrew it: the generated build/moc_systemtheme.cpp on a worker contains handlePortalSettingChanged in the meta-object. Where codex agreed with the fullscreen row and the watcher attribution, note that it was asked about both, and its independence is not currently guaranteed.

Whether Omarchy carries a second platform at all is still the maintainer's decision and not one we will make in either direction. Nothing in this review is a judgement on that; it is only what the code does on each side.

@InSasquatchCountry

InSasquatchCountry commented Aug 30, 2026

Copy link
Copy Markdown

I think that given the macOS user that would want to use omawrite in the first place would be already using homebrew, that it makes the most sense to build a Homebrew-native application on their machine not a portable macOS application. This avoids distributing a packaged .app altogether. In my opinion, this approach would minimize parallel development.

brew install qt librsvg
git clone https://github.com/omacom/omawrite.git
cd omawrite
#!/usr/bin/env bash
set -euo pipefail

QT_PREFIX="$(brew --prefix qt)"
ICONSET_DIR="pkgbuild/omawrite.iconset"
ICON_FILE="pkgbuild/omawrite.icns"

# Recreate the iconset
rm -rf "$ICONSET_DIR"
mkdir -p "$ICONSET_DIR"

for size in 16 32 128 256 512; do
  rsvg-convert \
    -w "$size" \
    -h "$size" \
    pkgbuild/omawrite.svg \
    -o "$ICONSET_DIR/icon_${size}x${size}.png"

  double=$((size * 2))

  rsvg-convert \
    -w "$double" \
    -h "$double" \
    pkgbuild/omawrite.svg \
    -o "$ICONSET_DIR/icon_${size}x${size}@2x.png"
done

# Convert the PNG iconset into a macOS .icns file
iconutil \
  -c icns \
  "$ICONSET_DIR" \
  -o "$ICON_FILE"

# Generate the Makefile, including the icon setting
"$QT_PREFIX/bin/qmake" \
  QMAKE_MACOSX_DEPLOYMENT_TARGET=26.0 \
  ICON="$ICON_FILE" \
  omawrite.pro

# Build
make -j"$(sysctl -n hw.ncpu)"

# Confirm qmake installed the icon in the bundle
test -f omawrite.app/Contents/Resources/omawrite.icns

# Ad-hoc sign the finished application
codesign \
  --deep \
  --force \
  --verbose \
  --sign - \
  omawrite.app

echo
echo "Built successfully:"
echo "  omawrite.app"
echo "  Icon: omawrite.app/Contents/Resources/omawrite.icns"
chmod +x pkgbuild/build-macos.sh
./pkgbuild/build-macos.sh
Screenshot 2026-08-30 at 2 20 33 PM Screenshot 2026-08-30 at 2 21 24 PM

Shortcut Behavior on macOS

✅ Cmd+S saves. Unsaved documents use the XDG desktop portal file picker.
✅ Cmd+Shift+S saves as.
✅ Cmd+O opens a Markdown file through the portal picker.
✅ Cmd+P opens the system print dialog.
✅ Cmd+N opens a new Omawrite window.
✅ Cmd+Z, Cmd+Shift+Z, and Cmd+Y handle undo and redo.
🔶 Super+F toggles fullscreen. Qt maps this key as Meta+F.
 - Ctrl + F works
 - macOS normally uses both the newer fn/globe + F or the older Ctrl + Cmd + F for "Enter/Exit full screen"
✅ Cmd+F searches the document. Use Enter or Cmd+G for the next match and Shift+Enter for the previous match.
❌ Cmd+H opens find and replace.
 - macOS uses Cmd + H to hide the current window
✅ Cmd+B, Cmd+I, and Cmd+K insert bold, italic, and link Markdown.
❌ Cmd+? shows the keyboard shortcut reference
 - Nothing happens but I don't get the macOS error sound
 - I tried Ctrl + ? and get the mac error sound which

via duck.ai GPT-5.6 Luna

Yes—if Omawrite is intended primarily for Homebrew users, making Qt a Homebrew dependency can be reasonable. In that model, the app uses the Qt installation managed by Homebrew instead of carrying a private copy inside the .app.

There are two different distribution models:

Model Qt handling Typical use
Standalone .app/.dmg Bundle Qt with macdeployqt Downloadable app for anyone
Homebrew formula Declare qt as a dependency; don’t deploy Qt Source-built Homebrew installation

For a Homebrew formula, the build would conceptually look like this:

class Omawrite < Formula
  desc "Markdown editor"
  homepage "https://github.com/omacom/omawrite"
  url "https://github.com/omacom/omawrite/archive/refs/tags/VERSION.tar.gz"
  sha256 "..."
  license "..."

  depends_on "qt"

  def install
    system "#{Formula["qt"].bin}/qmake", "omawrite.pro",
      "QMAKE_MACOSX_DEPLOYMENT_TARGET=#{MacOS.version}"
    system "make"

    prefix.install "omawrite.app"
  end

  def caveats
    <<~EOS
      Launch with:
        open #{opt_prefix}/omawrite.app
    EOS
  end
end

The important differences are:

  • Do not run macdeployqt.
  • Do not copy Qt frameworks into omawrite.app.
  • Build against Homebrew’s Qt.
  • Declare Qt with depends_on "qt" so Homebrew installs it first.
  • Use Homebrew’s Qt path rather than relying on /opt/homebrew/opt/qt being globally linked.

However, there are tradeoffs.

A Homebrew-installed app that dynamically uses Qt may break when:

  • Qt is upgraded incompatibly.
  • A user removes or unlinks Qt.
  • The app is copied outside the Homebrew installation.
    - The formula is installed on another Mac without the same Homebrew layout.
    Edit: InSasquatchCountry 02:35PM – I had zero issues from moving the .app to ~/Applications just now.
  • Homebrew changes a dependency’s library paths.

Also, Homebrew’s qt formula may itself consist of multiple Qt module formulae. Since Omawrite links against modules such as Qt Quick, Qt Quick Controls, and Qt Print Support, the formula should either depend on the aggregate qt formula or explicitly declare the required Qt modules if Homebrew’s packaging permits that.

For a project distributed specifically through Homebrew, I would use this approach:

# Build against Homebrew Qt
"$(brew --prefix qt)/bin/qmake" \
  QMAKE_MACOSX_DEPLOYMENT_TARGET=26.0 \
  omawrite.pro

make -j"$(sysctl -n hw.ncpu)"

# Do not run macdeployqt

Then verify that the executable points to Homebrew Qt:

otool -L omawrite.app/Contents/MacOS/omawrite

You would expect paths such as:

/opt/homebrew/opt/qtbase/lib/QtCore.framework/...

or equivalent Homebrew-linked paths, rather than:

@executable_path/../Frameworks/QtCore.framework/...

For a Homebrew formula, this is a sensible design. For a downloadable standalone application, embedding Qt is the better design. The project could support both packaging targets: a Homebrew formula with external Qt dependencies and a release .app built with macdeployqt.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants