Skip to content

Commit 8336cce

Browse files
authored
Merge branch 'v3-alpha' into v3-alpha-bugfix/linux-save-dialog-filename
2 parents 962829a + a1dd1f4 commit 8336cce

12 files changed

Lines changed: 1726 additions & 11 deletions

File tree

docs/src/content/docs/changelog.mdx

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,12 +30,36 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
3030
*/
3131
## [Unreleased]
3232

33+
## v3.0.0-alpha.56 - 2026-01-04
34+
35+
## Added
36+
- Add `internal/libpath` package for finding native library paths on Linux with parallel search, caching, and support for Flatpak/Snap/Nix
37+
38+
## Changed
39+
- **BREAKING:** Rename `EnableDragAndDrop` to `EnableFileDrop` in window options
40+
- **BREAKING:** Rename `DropZoneDetails` to `DropTargetDetails` in event context
41+
- **BREAKING:** Rename `DropZoneDetails()` method to `DropTargetDetails()` on `WindowEventContext`
42+
- **BREAKING:** Remove `WindowDropZoneFilesDropped` event, use `WindowFilesDropped` instead
43+
- **BREAKING:** Change HTML attribute from `data-wails-dropzone` to `data-file-drop-target`
44+
- **BREAKING:** Change CSS hover class from `wails-dropzone-hover` to `file-drop-target-active`
45+
- **BREAKING:** Remove `DragEffect`, `OnEnterEffect`, `OnOverEffect` options from Windows (were part of removed IDropTarget)
46+
47+
## Fixed
48+
- Fix file drag-and-drop on Windows not working at non-100% display scaling
49+
- Fix HTML5 internal drag-and-drop being broken when file drop was enabled on Windows
50+
- Fix file drop coordinates being in wrong pixel space on Windows (physical vs CSS pixels)
51+
- Fix file drag-and-drop on Linux not working reliably with hover effects
52+
- Fix HTML5 internal drag-and-drop being broken when file drop was enabled on Linux
53+
54+
## Removed
55+
- Remove native `IDropTarget` implementation on Windows in favor of JavaScript-based approach (matches v2 behavior)
56+
3357
## v3.0.0-alpha.55 - 2026-01-02
3458

3559
## Changed
3660
- Switch to goccy/go-json for all runtime JSON processing (method bindings, events, webview requests, notifications, kvstore), improving performance by 21-63% and reducing memory allocations by 40-60%
3761
- Optimize BoundMethod struct layout and cache isVariadic flag to reduce per-call overhead
38-
- Use stack-allocated argument buffer for methods with <=8 arguments to avoid heap allocations
62+
- Use stack-allocated argument buffer for methods with `<=8` arguments to avoid heap allocations
3963
- Optimize result collection in method calls to avoid slice allocation for single return values
4064
- Use sync.Map for MIME type cache to improve concurrent performance
4165
- Use buffer pool for HTTP transport request body reading

docs/src/content/docs/guides/build/linux.mdx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -146,7 +146,7 @@ sudo pacman -S base-devel
146146

147147
Alternatively, run `wails3 task setup:docker` and the build system will use Docker automatically.
148148

149-
### AppImage strip compatibility {#appimage-strip-compatibility}
149+
### AppImage strip compatibility
150150

151151
On modern Linux distributions (Arch Linux, Fedora 39+, Ubuntu 24.04+), system libraries are compiled with `.relr.dyn` ELF sections for more efficient relocations. The `linuxdeploy` tool used to create AppImages bundles an older `strip` binary that cannot process these modern sections.
152152

v3/UNRELEASED_CHANGELOG.md

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -20,13 +20,6 @@ After processing, the content will be moved to the main changelog and this file
2020

2121
## Changed
2222
<!-- Changes in existing functionality -->
23-
- **BREAKING:** Rename `EnableDragAndDrop` to `EnableFileDrop` in window options
24-
- **BREAKING:** Rename `DropZoneDetails` to `DropTargetDetails` in event context
25-
- **BREAKING:** Rename `DropZoneDetails()` method to `DropTargetDetails()` on `WindowEventContext`
26-
- **BREAKING:** Remove `WindowDropZoneFilesDropped` event, use `WindowFilesDropped` instead
27-
- **BREAKING:** Change HTML attribute from `data-wails-dropzone` to `data-file-drop-target`
28-
- **BREAKING:** Change CSS hover class from `wails-dropzone-hover` to `file-drop-target-active`
29-
- **BREAKING:** Remove `DragEffect`, `OnEnterEffect`, `OnOverEffect` options from Windows (were part of removed IDropTarget)
3023

3124
## Fixed
3225
<!-- Bug fixes -->
@@ -42,7 +35,6 @@ After processing, the content will be moved to the main changelog and this file
4235

4336
## Removed
4437
<!-- Features removed in this release -->
45-
- Remove native `IDropTarget` implementation on Windows in favor of JavaScript-based approach (matches v2 behavior)
4638

4739
## Security
4840
<!-- Security-related changes -->

v3/internal/libpath/cache_linux.go

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
//go:build linux
2+
3+
package libpath
4+
5+
import "sync"
6+
7+
// pathCache holds cached dynamic library paths to avoid repeated
8+
// expensive filesystem and subprocess operations.
9+
type pathCache struct {
10+
mu sync.RWMutex
11+
flatpak []string
12+
snap []string
13+
nix []string
14+
initOnce sync.Once
15+
inited bool
16+
}
17+
18+
var cache pathCache
19+
20+
// init populates the cache with dynamic paths from package managers.
21+
// This is called lazily on first access.
22+
func (c *pathCache) init() {
23+
c.initOnce.Do(func() {
24+
// Discover paths without holding the lock
25+
flatpak := discoverFlatpakLibPaths()
26+
snap := discoverSnapLibPaths()
27+
nix := discoverNixLibPaths()
28+
29+
// Hold lock only while updating the cache
30+
c.mu.Lock()
31+
c.flatpak = flatpak
32+
c.snap = snap
33+
c.nix = nix
34+
c.inited = true
35+
c.mu.Unlock()
36+
})
37+
}
38+
39+
// getFlatpak returns cached Flatpak library paths.
40+
func (c *pathCache) getFlatpak() []string {
41+
c.init()
42+
c.mu.RLock()
43+
defer c.mu.RUnlock()
44+
return c.flatpak
45+
}
46+
47+
// getSnap returns cached Snap library paths.
48+
func (c *pathCache) getSnap() []string {
49+
c.init()
50+
c.mu.RLock()
51+
defer c.mu.RUnlock()
52+
return c.snap
53+
}
54+
55+
// getNix returns cached Nix library paths.
56+
func (c *pathCache) getNix() []string {
57+
c.init()
58+
c.mu.RLock()
59+
defer c.mu.RUnlock()
60+
return c.nix
61+
}
62+
63+
// invalidate clears the cache and forces re-discovery on next access.
64+
func (c *pathCache) invalidate() {
65+
c.mu.Lock()
66+
defer c.mu.Unlock()
67+
c.flatpak = nil
68+
c.snap = nil
69+
c.nix = nil
70+
c.initOnce = sync.Once{} // Reset so init() runs again
71+
c.inited = false
72+
}
73+
74+
// InvalidateCache clears the cached dynamic library paths.
75+
// Call this if packages are installed or removed during runtime
76+
// and you need to re-discover library paths.
77+
func InvalidateCache() {
78+
cache.invalidate()
79+
}
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
//go:build linux
2+
3+
package libpath
4+
5+
import (
6+
"os"
7+
"os/exec"
8+
"path/filepath"
9+
"strings"
10+
)
11+
12+
// getFlatpakLibPaths returns cached library paths from installed Flatpak runtimes.
13+
func getFlatpakLibPaths() []string {
14+
return cache.getFlatpak()
15+
}
16+
17+
// discoverFlatpakLibPaths scans for Flatpak runtime library directories.
18+
// Uses `flatpak --installations` and scans for runtime lib directories.
19+
func discoverFlatpakLibPaths() []string {
20+
var paths []string
21+
22+
// Get system and user installation directories
23+
installDirs := []string{
24+
"/var/lib/flatpak", // System default
25+
os.ExpandEnv("$HOME/.local/share/flatpak"), // User default
26+
}
27+
28+
// Try to get actual installation path from flatpak
29+
if out, err := exec.Command("flatpak", "--installations").Output(); err == nil {
30+
for _, line := range strings.Split(strings.TrimSpace(string(out)), "\n") {
31+
if line != "" {
32+
installDirs = append(installDirs, line)
33+
}
34+
}
35+
}
36+
37+
// Scan for runtime lib directories
38+
for _, installDir := range installDirs {
39+
runtimeDir := filepath.Join(installDir, "runtime")
40+
if _, err := os.Stat(runtimeDir); err != nil {
41+
continue
42+
}
43+
44+
// Look for lib directories in runtimes
45+
// Structure: runtime/<name>/<arch>/<version>/<hash>/files/lib
46+
matches, err := filepath.Glob(filepath.Join(runtimeDir, "*", "*", "*", "*", "files", "lib"))
47+
if err == nil {
48+
paths = append(paths, matches...)
49+
}
50+
}
51+
52+
return paths
53+
}

v3/internal/libpath/libpath.go

Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,104 @@
1+
// Package libpath provides utilities for finding native library paths on Linux.
2+
//
3+
// # Overview
4+
//
5+
// This package helps locate shared libraries (.so files) on Linux systems,
6+
// supporting multiple distributions and package managers. It's particularly
7+
// useful for applications that need to link against libraries like GTK,
8+
// WebKit2GTK, or other system libraries at runtime.
9+
//
10+
// # Search Strategy
11+
//
12+
// The package uses a multi-tier search strategy, trying each method in order
13+
// until a library is found:
14+
//
15+
// 1. pkg-config: Queries the pkg-config database for library paths
16+
// 2. ldconfig: Searches the dynamic linker cache
17+
// 3. Filesystem: Scans common library directories
18+
//
19+
// # Supported Distributions
20+
//
21+
// The package includes default search paths for:
22+
//
23+
// - Debian/Ubuntu (multiarch paths like /usr/lib/x86_64-linux-gnu)
24+
// - Fedora/RHEL/CentOS (/usr/lib64, /usr/lib64/gtk-*)
25+
// - Arch Linux (/usr/lib/webkit2gtk-*, /usr/lib/gtk-*)
26+
// - openSUSE (/usr/lib64/gcc/x86_64-suse-linux)
27+
// - NixOS and Nix package manager
28+
//
29+
// # Package Manager Support
30+
//
31+
// Dynamic paths are discovered from:
32+
//
33+
// - Flatpak: Scans runtime directories via `flatpak --installations`
34+
// - Snap: Globs /snap/*/current/usr/lib* directories
35+
// - Nix: Checks ~/.nix-profile/lib and /run/current-system/sw/lib
36+
//
37+
// # Caching
38+
//
39+
// Dynamic path discovery (Flatpak, Snap, Nix) is cached for performance.
40+
// The cache is populated on first access and persists for the process lifetime.
41+
// Use [InvalidateCache] to force re-discovery if packages are installed/removed
42+
// during runtime.
43+
//
44+
// # Security
45+
//
46+
// The current directory (".") is never included in search paths by default,
47+
// as this is a security risk. Use [FindLibraryPathWithOptions] with
48+
// IncludeCurrentDir if you explicitly need this behavior (not recommended
49+
// for production).
50+
//
51+
// # Performance
52+
//
53+
// Typical lookup times (cached):
54+
//
55+
// - Found via pkg-config: ~2ms (spawns external process)
56+
// - Found via ldconfig: ~1.3ms (spawns external process)
57+
// - Found via filesystem: ~0.1ms (uses cached paths)
58+
// - Not found (worst case): ~20ms (searches all paths)
59+
//
60+
// # Example Usage
61+
//
62+
// // Find a library by its pkg-config name
63+
// path, err := libpath.FindLibraryPath("webkit2gtk-4.1")
64+
// if err != nil {
65+
// log.Fatal("WebKit2GTK not found:", err)
66+
// }
67+
// fmt.Println("Found at:", path)
68+
//
69+
// // Find a specific .so file
70+
// soPath, err := libpath.FindLibraryFile("libgtk-3.so")
71+
// if err != nil {
72+
// log.Fatal("GTK3 library file not found:", err)
73+
// }
74+
// fmt.Println("Library file:", soPath)
75+
//
76+
// // Get all library search paths
77+
// for _, p := range libpath.GetAllLibPaths() {
78+
// fmt.Println(p)
79+
// }
80+
//
81+
// # Multi-Library Search
82+
//
83+
// When you don't know which version of a library is installed, use the
84+
// multi-library search functions:
85+
//
86+
// // Find any available WebKit2GTK version (first found wins)
87+
// match, err := libpath.FindFirstLibrary("webkit2gtk-4.1", "webkit2gtk-4.0", "webkit2gtk-6.0")
88+
// if err != nil {
89+
// log.Fatal("No WebKit2GTK found")
90+
// }
91+
// fmt.Printf("Found %s at %s\n", match.Name, match.Path)
92+
//
93+
// // Prefer newer versions (ordered search)
94+
// match, err := libpath.FindFirstLibraryOrdered("gtk4", "gtk+-3.0")
95+
//
96+
// // Discover all available versions
97+
// matches := libpath.FindAllLibraries("gtk+-3.0", "gtk4", "webkit2gtk-4.0", "webkit2gtk-4.1")
98+
// for _, m := range matches {
99+
// fmt.Printf("Available: %s at %s\n", m.Name, m.Path)
100+
// }
101+
//
102+
// On non-Linux platforms, stub implementations are provided that always
103+
// return [LibraryNotFoundError].
104+
package libpath

0 commit comments

Comments
 (0)