added option key remap
This commit is contained in:
10
main_test.go
10
main_test.go
@@ -1,10 +0,0 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestMain(t *testing.T) {
|
||||
// Basic test to ensure the main package can be imported and tested
|
||||
t.Log("TurtleSilicon main package test passed")
|
||||
}
|
@@ -3,6 +3,8 @@ package ui
|
||||
import (
|
||||
"log"
|
||||
"net/url"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"turtlesilicon/pkg/launcher"
|
||||
"turtlesilicon/pkg/patching"
|
||||
@@ -45,6 +47,9 @@ func createOptionsComponents() {
|
||||
vanillaTweaksCheckbox.SetChecked(prefs.EnableVanillaTweaks)
|
||||
launcher.EnableVanillaTweaks = prefs.EnableVanillaTweaks
|
||||
|
||||
// Create Wine registry Option-as-Alt buttons and status
|
||||
createWineRegistryComponents()
|
||||
|
||||
// Load environment variables from preferences
|
||||
if prefs.EnvironmentVariables != "" {
|
||||
launcher.CustomEnvVars = prefs.EnvironmentVariables
|
||||
@@ -158,3 +163,182 @@ func createBottomBar(myWindow fyne.Window) fyne.CanvasObject {
|
||||
|
||||
return container.NewPadded(bottomContainer)
|
||||
}
|
||||
|
||||
// createWineRegistryComponents creates Wine registry Option-as-Alt buttons and status
|
||||
func createWineRegistryComponents() {
|
||||
// Create status label to show current state
|
||||
optionAsAltStatusLabel = widget.NewRichText()
|
||||
|
||||
// Create enable button
|
||||
enableOptionAsAltButton = widget.NewButton("Enable", func() {
|
||||
enableOptionAsAltButton.Disable()
|
||||
disableOptionAsAltButton.Disable()
|
||||
|
||||
// Show loading state in status label
|
||||
fyne.Do(func() {
|
||||
optionAsAltStatusLabel.ParseMarkdown("**Remap Option key as Alt key:** Enabling...")
|
||||
startPulsingEffect()
|
||||
})
|
||||
|
||||
// Run in goroutine to avoid blocking UI
|
||||
go func() {
|
||||
if err := utils.SetOptionAsAltEnabled(true); err != nil {
|
||||
log.Printf("Failed to enable Option-as-Alt mapping: %v", err)
|
||||
// Update UI on main thread
|
||||
fyne.Do(func() {
|
||||
stopPulsingEffect()
|
||||
optionAsAltStatusLabel.ParseMarkdown("**Remap Option key as Alt key:** Enable Failed")
|
||||
})
|
||||
time.Sleep(2 * time.Second) // Show error briefly
|
||||
} else {
|
||||
log.Printf("Successfully enabled Option-as-Alt mapping")
|
||||
// Update preferences
|
||||
prefs, _ := utils.LoadPrefs()
|
||||
prefs.RemapOptionAsAlt = true
|
||||
utils.SavePrefs(prefs)
|
||||
}
|
||||
|
||||
// Update UI on main thread
|
||||
fyne.Do(func() {
|
||||
stopPulsingEffect()
|
||||
updateWineRegistryStatusWithMethod(true) // Use Wine command for accurate check after modifications
|
||||
})
|
||||
}()
|
||||
})
|
||||
|
||||
// Create disable button
|
||||
disableOptionAsAltButton = widget.NewButton("Disable", func() {
|
||||
enableOptionAsAltButton.Disable()
|
||||
disableOptionAsAltButton.Disable()
|
||||
|
||||
// Show loading state in status label
|
||||
fyne.Do(func() {
|
||||
optionAsAltStatusLabel.ParseMarkdown("**Remap Option key as Alt key:** Disabling...")
|
||||
startPulsingEffect()
|
||||
})
|
||||
|
||||
// Run in goroutine to avoid blocking UI
|
||||
go func() {
|
||||
if err := utils.SetOptionAsAltEnabled(false); err != nil {
|
||||
log.Printf("Failed to disable Option-as-Alt mapping: %v", err)
|
||||
// Update UI on main thread
|
||||
fyne.Do(func() {
|
||||
stopPulsingEffect()
|
||||
optionAsAltStatusLabel.ParseMarkdown("**Remap Option key as Alt key:** Disable Failed")
|
||||
})
|
||||
time.Sleep(2 * time.Second) // Show error briefly
|
||||
} else {
|
||||
log.Printf("Successfully disabled Option-as-Alt mapping")
|
||||
// Update preferences
|
||||
prefs, _ := utils.LoadPrefs()
|
||||
prefs.RemapOptionAsAlt = false
|
||||
utils.SavePrefs(prefs)
|
||||
}
|
||||
|
||||
// Update UI on main thread
|
||||
fyne.Do(func() {
|
||||
stopPulsingEffect()
|
||||
updateWineRegistryStatusWithMethod(true) // Use Wine command for accurate check after modifications
|
||||
})
|
||||
}()
|
||||
})
|
||||
|
||||
// Style the buttons similar to other action buttons
|
||||
enableOptionAsAltButton.Importance = widget.MediumImportance
|
||||
disableOptionAsAltButton.Importance = widget.MediumImportance
|
||||
|
||||
// Initialize status and button states
|
||||
updateWineRegistryStatus()
|
||||
}
|
||||
|
||||
// updateWineRegistryStatus updates the Wine registry status label and button states
|
||||
func updateWineRegistryStatus() {
|
||||
updateWineRegistryStatusWithMethod(false)
|
||||
}
|
||||
|
||||
// updateWineRegistryStatusWithMethod updates status with choice of checking method
|
||||
func updateWineRegistryStatusWithMethod(useWineCommand bool) {
|
||||
if useWineCommand {
|
||||
// Use Wine command for accurate check after modifications
|
||||
currentWineRegistryEnabled = utils.CheckOptionAsAltEnabled()
|
||||
} else {
|
||||
// Use fast file-based check for regular status updates
|
||||
currentWineRegistryEnabled = utils.CheckOptionAsAltEnabledFast()
|
||||
}
|
||||
|
||||
// Update UI with simple white text
|
||||
if currentWineRegistryEnabled {
|
||||
optionAsAltStatusLabel.ParseMarkdown("**Remap Option key as Alt key:** Enabled")
|
||||
} else {
|
||||
optionAsAltStatusLabel.ParseMarkdown("**Remap Option key as Alt key:** Disabled")
|
||||
}
|
||||
|
||||
// Update button states based on current status
|
||||
if currentWineRegistryEnabled {
|
||||
// If enabled, only show disable button as clickable
|
||||
enableOptionAsAltButton.Disable()
|
||||
disableOptionAsAltButton.Enable()
|
||||
} else {
|
||||
// If disabled, only show enable button as clickable
|
||||
enableOptionAsAltButton.Enable()
|
||||
disableOptionAsAltButton.Disable()
|
||||
}
|
||||
}
|
||||
|
||||
// startPulsingEffect starts a pulsing animation for the status label during loading
|
||||
func startPulsingEffect() {
|
||||
if pulsingActive {
|
||||
return // Already pulsing
|
||||
}
|
||||
|
||||
pulsingActive = true
|
||||
pulsingTicker = time.NewTicker(500 * time.Millisecond)
|
||||
|
||||
go func() {
|
||||
dots := ""
|
||||
|
||||
for pulsingActive {
|
||||
select {
|
||||
case <-pulsingTicker.C:
|
||||
if pulsingActive {
|
||||
// Cycle through different dot patterns for visual effect
|
||||
switch len(dots) {
|
||||
case 0:
|
||||
dots = "."
|
||||
case 1:
|
||||
dots = ".."
|
||||
case 2:
|
||||
dots = "..."
|
||||
default:
|
||||
dots = ""
|
||||
}
|
||||
|
||||
// Update the label with pulsing dots
|
||||
fyne.Do(func() {
|
||||
if pulsingActive && optionAsAltStatusLabel != nil {
|
||||
// Use the dots directly in the status text
|
||||
if strings.Contains(optionAsAltStatusLabel.String(), "Enabling") {
|
||||
optionAsAltStatusLabel.ParseMarkdown("**Remap Option key as Alt key:** Enabling" + dots)
|
||||
} else if strings.Contains(optionAsAltStatusLabel.String(), "Disabling") {
|
||||
optionAsAltStatusLabel.ParseMarkdown("**Remap Option key as Alt key:** Disabling" + dots)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
}()
|
||||
}
|
||||
|
||||
// stopPulsingEffect stops the pulsing animation
|
||||
func stopPulsingEffect() {
|
||||
if !pulsingActive {
|
||||
return
|
||||
}
|
||||
|
||||
pulsingActive = false
|
||||
if pulsingTicker != nil {
|
||||
pulsingTicker.Stop()
|
||||
pulsingTicker = nil
|
||||
}
|
||||
}
|
||||
|
@@ -21,6 +21,8 @@ func showOptionsPopup() {
|
||||
metalHudCheckbox,
|
||||
showTerminalCheckbox,
|
||||
vanillaTweaksCheckbox,
|
||||
widget.NewSeparator(),
|
||||
container.NewBorder(nil, nil, nil, container.NewHBox(enableOptionAsAltButton, disableOptionAsAltButton), optionAsAltStatusLabel),
|
||||
)
|
||||
|
||||
envVarsTitle := widget.NewLabel("Environment Variables")
|
||||
@@ -55,8 +57,8 @@ func showOptionsPopup() {
|
||||
|
||||
// Get the window size and calculate 2/3 size
|
||||
windowSize := currentWindow.Content().Size()
|
||||
popupWidth := windowSize.Width * 2 / 3
|
||||
popupHeight := windowSize.Height * 2 / 3
|
||||
popupWidth := windowSize.Width * 5 / 6
|
||||
popupHeight := windowSize.Height * 5 / 6
|
||||
|
||||
// Create a modal popup
|
||||
popup := widget.NewModalPopUp(popupContent, currentWindow.Canvas())
|
||||
|
@@ -25,6 +25,11 @@ func UpdateAllStatuses() {
|
||||
updateTurtleWoWStatus()
|
||||
updatePlayButtonState()
|
||||
updateServiceStatus()
|
||||
|
||||
// Update Wine registry status if components are initialized
|
||||
if optionAsAltStatusLabel != nil {
|
||||
updateWineRegistryStatus()
|
||||
}
|
||||
}
|
||||
|
||||
// updateCrossoverStatus updates CrossOver path and patch status
|
||||
|
@@ -1,6 +1,8 @@
|
||||
package ui
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"fyne.io/fyne/v2"
|
||||
"fyne.io/fyne/v2/widget"
|
||||
)
|
||||
@@ -30,9 +32,20 @@ var (
|
||||
showTerminalCheckbox *widget.Check
|
||||
vanillaTweaksCheckbox *widget.Check
|
||||
|
||||
// Wine registry buttons and status
|
||||
enableOptionAsAltButton *widget.Button
|
||||
disableOptionAsAltButton *widget.Button
|
||||
optionAsAltStatusLabel *widget.RichText
|
||||
|
||||
// Environment variables entry
|
||||
envVarsEntry *widget.Entry
|
||||
|
||||
// Window reference for popup functionality
|
||||
currentWindow fyne.Window
|
||||
|
||||
// State variables
|
||||
currentWineRegistryEnabled bool
|
||||
|
||||
// Pulsing effect variables (pulsingActive is in status.go)
|
||||
pulsingTicker *time.Ticker
|
||||
)
|
||||
|
@@ -14,6 +14,7 @@ type UserPrefs struct {
|
||||
SaveSudoPassword bool `json:"save_sudo_password"`
|
||||
ShowTerminalNormally bool `json:"show_terminal_normally"`
|
||||
EnableVanillaTweaks bool `json:"enable_vanilla_tweaks"`
|
||||
RemapOptionAsAlt bool `json:"remap_option_as_alt"`
|
||||
}
|
||||
|
||||
func getPrefsPath() (string, error) {
|
||||
|
440
pkg/utils/wine_registry.go
Normal file
440
pkg/utils/wine_registry.go
Normal file
@@ -0,0 +1,440 @@
|
||||
package utils
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
)
|
||||
|
||||
const (
|
||||
wineRegistrySection = "[Software\\Wine\\Mac Driver]"
|
||||
leftOptionKey = "\"LeftOptionIsAlt\"=\"Y\""
|
||||
rightOptionKey = "\"RightOptionIsAlt\"=\"Y\""
|
||||
wineLoaderPath = "/Applications/CrossOver.app/Contents/SharedSupport/CrossOver/CrossOver-Hosted Application/wineloader2"
|
||||
registryKeyPath = "HKEY_CURRENT_USER\\Software\\Wine\\Mac Driver"
|
||||
)
|
||||
|
||||
// GetWineUserRegPath returns the path to the Wine user.reg file
|
||||
func GetWineUserRegPath() (string, error) {
|
||||
homeDir, err := os.UserHomeDir()
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("failed to get user home directory: %v", err)
|
||||
}
|
||||
return filepath.Join(homeDir, ".wine", "user.reg"), nil
|
||||
}
|
||||
|
||||
// queryRegistryValue queries a specific registry value using Wine's reg command
|
||||
func queryRegistryValue(winePrefix, valueName string) bool {
|
||||
cmd := exec.Command(wineLoaderPath, "reg", "query", registryKeyPath, "/v", valueName)
|
||||
cmd.Env = append(os.Environ(), fmt.Sprintf("WINEPREFIX=%s", winePrefix))
|
||||
|
||||
output, err := cmd.Output()
|
||||
if err != nil {
|
||||
// Value doesn't exist or other error
|
||||
return false
|
||||
}
|
||||
|
||||
// Check if the output contains the value set to "Y"
|
||||
outputStr := string(output)
|
||||
return strings.Contains(outputStr, valueName) && strings.Contains(outputStr, "Y")
|
||||
}
|
||||
|
||||
func setRegistryValuesOptimized(winePrefix string, enabled bool) error {
|
||||
if enabled {
|
||||
return addBothRegistryValues(winePrefix)
|
||||
} else {
|
||||
return deleteBothRegistryValues(winePrefix)
|
||||
}
|
||||
}
|
||||
|
||||
func addBothRegistryValues(winePrefix string) error {
|
||||
batchContent := fmt.Sprintf(`@echo off
|
||||
reg add "%s" /v "LeftOptionIsAlt" /t REG_SZ /d "Y" /f
|
||||
reg add "%s" /v "RightOptionIsAlt" /t REG_SZ /d "Y" /f
|
||||
`, registryKeyPath, registryKeyPath)
|
||||
|
||||
// Create temporary batch file
|
||||
tempDir := os.TempDir()
|
||||
batchFile := filepath.Join(tempDir, "wine_registry_add.bat")
|
||||
|
||||
if err := os.WriteFile(batchFile, []byte(batchContent), 0644); err != nil {
|
||||
return fmt.Errorf("failed to create batch file: %v", err)
|
||||
}
|
||||
defer os.Remove(batchFile)
|
||||
|
||||
// Run the batch file with Wine
|
||||
cmd := exec.Command(wineLoaderPath, "cmd", "/c", batchFile)
|
||||
cmd.Env = append(os.Environ(), fmt.Sprintf("WINEPREFIX=%s", winePrefix))
|
||||
|
||||
output, err := cmd.CombinedOutput()
|
||||
if err != nil {
|
||||
return fmt.Errorf("batch registry add failed: %v, output: %s", err, string(output))
|
||||
}
|
||||
|
||||
log.Printf("Successfully enabled Option-as-Alt mapping in Wine registry (optimized)")
|
||||
return nil
|
||||
}
|
||||
|
||||
func deleteBothRegistryValues(winePrefix string) error {
|
||||
batchContent := fmt.Sprintf(`@echo off
|
||||
reg delete "%s" /v "LeftOptionIsAlt" /f 2>nul
|
||||
reg delete "%s" /v "RightOptionIsAlt" /f 2>nul
|
||||
`, registryKeyPath, registryKeyPath)
|
||||
|
||||
// Create temporary batch file
|
||||
tempDir := os.TempDir()
|
||||
batchFile := filepath.Join(tempDir, "wine_registry_delete.bat")
|
||||
|
||||
if err := os.WriteFile(batchFile, []byte(batchContent), 0644); err != nil {
|
||||
return fmt.Errorf("failed to create batch file: %v", err)
|
||||
}
|
||||
defer os.Remove(batchFile) // Clean up
|
||||
|
||||
// Run the batch file with Wine
|
||||
cmd := exec.Command(wineLoaderPath, "cmd", "/c", batchFile)
|
||||
cmd.Env = append(os.Environ(), fmt.Sprintf("WINEPREFIX=%s", winePrefix))
|
||||
|
||||
output, err := cmd.CombinedOutput()
|
||||
if err != nil {
|
||||
return fmt.Errorf("batch registry delete failed: %v, output: %s", err, string(output))
|
||||
}
|
||||
|
||||
log.Printf("Successfully disabled Option-as-Alt mapping in Wine registry (optimized)")
|
||||
return nil
|
||||
}
|
||||
|
||||
// CheckOptionAsAltEnabled checks if Option keys are remapped as Alt keys in Wine registry
|
||||
func CheckOptionAsAltEnabled() bool {
|
||||
homeDir, err := os.UserHomeDir()
|
||||
if err != nil {
|
||||
log.Printf("Failed to get user home directory: %v", err)
|
||||
return false
|
||||
}
|
||||
|
||||
winePrefix := filepath.Join(homeDir, ".wine")
|
||||
|
||||
// Check if CrossOver wine loader exists
|
||||
if !PathExists(wineLoaderPath) {
|
||||
log.Printf("CrossOver wine loader not found at: %s", wineLoaderPath)
|
||||
return false
|
||||
}
|
||||
|
||||
// Query both registry values
|
||||
leftEnabled := queryRegistryValue(winePrefix, "LeftOptionIsAlt")
|
||||
rightEnabled := queryRegistryValue(winePrefix, "RightOptionIsAlt")
|
||||
|
||||
return leftEnabled && rightEnabled
|
||||
}
|
||||
|
||||
// CheckOptionAsAltEnabledFast checks status by reading user.reg file directly
|
||||
func CheckOptionAsAltEnabledFast() bool {
|
||||
regPath, err := GetWineUserRegPath()
|
||||
if err != nil {
|
||||
log.Printf("Failed to get Wine registry path: %v", err)
|
||||
return false
|
||||
}
|
||||
|
||||
if !PathExists(regPath) {
|
||||
log.Printf("Wine user.reg file not found at: %s", regPath)
|
||||
return false
|
||||
}
|
||||
|
||||
content, err := os.ReadFile(regPath)
|
||||
if err != nil {
|
||||
log.Printf("Failed to read Wine registry file: %v", err)
|
||||
return false
|
||||
}
|
||||
|
||||
contentStr := string(content)
|
||||
|
||||
// Check for the Mac Driver section in different possible formats
|
||||
macDriverSectionFound := strings.Contains(contentStr, wineRegistrySection) ||
|
||||
strings.Contains(contentStr, "[SoftwareWineMac Driver]")
|
||||
|
||||
if macDriverSectionFound {
|
||||
// Look for the registry values in the proper format or any format
|
||||
leftOptionFound := strings.Contains(contentStr, leftOptionKey) ||
|
||||
strings.Contains(contentStr, "\"LeftOptionIsAlt\"=\"Y\"")
|
||||
rightOptionFound := strings.Contains(contentStr, rightOptionKey) ||
|
||||
strings.Contains(contentStr, "\"RightOptionIsAlt\"=\"Y\"")
|
||||
|
||||
return leftOptionFound && rightOptionFound
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
// SetOptionAsAltEnabled enables or disables Option key remapping in Wine registry
|
||||
func SetOptionAsAltEnabled(enabled bool) error {
|
||||
homeDir, err := os.UserHomeDir()
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to get user home directory: %v", err)
|
||||
}
|
||||
|
||||
winePrefix := filepath.Join(homeDir, ".wine")
|
||||
|
||||
// Check if CrossOver wine loader exists
|
||||
if !PathExists(wineLoaderPath) {
|
||||
return fmt.Errorf("CrossOver wine loader not found at: %s", wineLoaderPath)
|
||||
}
|
||||
|
||||
// Ensure the .wine directory exists
|
||||
if err := os.MkdirAll(winePrefix, 0755); err != nil {
|
||||
return fmt.Errorf("failed to create .wine directory: %v", err)
|
||||
}
|
||||
|
||||
if enabled {
|
||||
return setRegistryValuesOptimized(winePrefix, true)
|
||||
} else {
|
||||
err := setRegistryValuesOptimized(winePrefix, false)
|
||||
if err != nil {
|
||||
log.Printf("Wine registry disable failed: %v", err)
|
||||
}
|
||||
|
||||
err2 := setRegistryValuesFast(false)
|
||||
if err2 != nil {
|
||||
log.Printf("File-based cleanup failed: %v", err2)
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return err2
|
||||
}
|
||||
}
|
||||
|
||||
// setRegistryValuesFast directly modifies the user.reg file (much faster)
|
||||
func setRegistryValuesFast(enabled bool) error {
|
||||
regPath, err := GetWineUserRegPath()
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to get Wine registry path: %v", err)
|
||||
}
|
||||
|
||||
// Ensure the .wine directory exists
|
||||
wineDir := filepath.Dir(regPath)
|
||||
if err := os.MkdirAll(wineDir, 0755); err != nil {
|
||||
return fmt.Errorf("failed to create .wine directory: %v", err)
|
||||
}
|
||||
|
||||
var content string
|
||||
var lines []string
|
||||
|
||||
// Read existing content if file exists
|
||||
if PathExists(regPath) {
|
||||
contentBytes, err := os.ReadFile(regPath)
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to read existing registry file: %v", err)
|
||||
}
|
||||
content = string(contentBytes)
|
||||
lines = strings.Split(content, "\n")
|
||||
} else {
|
||||
// Create basic registry structure if file doesn't exist
|
||||
content = "WINE REGISTRY Version 2\n;; All keys relative to \\User\n\n"
|
||||
lines = strings.Split(content, "\n")
|
||||
}
|
||||
|
||||
if enabled {
|
||||
return addOptionAsAltSettingsFast(regPath, lines)
|
||||
} else {
|
||||
return removeOptionAsAltSettingsFast(regPath, lines)
|
||||
}
|
||||
}
|
||||
|
||||
// addOptionAsAltSettingsFast adds the Option-as-Alt registry settings directly to the file
|
||||
func addOptionAsAltSettingsFast(regPath string, lines []string) error {
|
||||
var newLines []string
|
||||
sectionFound := false
|
||||
sectionIndex := -1
|
||||
leftOptionFound := false
|
||||
rightOptionFound := false
|
||||
|
||||
// Find the Mac Driver section
|
||||
for i, line := range lines {
|
||||
if strings.TrimSpace(line) == wineRegistrySection {
|
||||
sectionFound = true
|
||||
sectionIndex = i
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if !sectionFound {
|
||||
// Add the section at the end
|
||||
newLines = append(lines, "")
|
||||
newLines = append(newLines, wineRegistrySection)
|
||||
newLines = append(newLines, "#time=1dbd859c084de18")
|
||||
newLines = append(newLines, leftOptionKey)
|
||||
newLines = append(newLines, rightOptionKey)
|
||||
} else {
|
||||
// Section exists, check if keys are already present
|
||||
newLines = make([]string, len(lines))
|
||||
copy(newLines, lines)
|
||||
|
||||
// Look for existing keys in the section
|
||||
for i := sectionIndex + 1; i < len(lines); i++ {
|
||||
line := strings.TrimSpace(lines[i])
|
||||
if line == "" {
|
||||
continue
|
||||
}
|
||||
if strings.HasPrefix(line, "[") && line != wineRegistrySection {
|
||||
// Found start of another section, stop looking
|
||||
break
|
||||
}
|
||||
if strings.Contains(line, "LeftOptionIsAlt") {
|
||||
leftOptionFound = true
|
||||
if !strings.Contains(line, "\"Y\"") {
|
||||
newLines[i] = leftOptionKey
|
||||
}
|
||||
}
|
||||
if strings.Contains(line, "RightOptionIsAlt") {
|
||||
rightOptionFound = true
|
||||
if !strings.Contains(line, "\"Y\"") {
|
||||
newLines[i] = rightOptionKey
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Add missing keys
|
||||
if !leftOptionFound || !rightOptionFound {
|
||||
insertIndex := sectionIndex + 1
|
||||
|
||||
// Add timestamp if it doesn't exist
|
||||
timestampExists := false
|
||||
for i := sectionIndex + 1; i < len(newLines); i++ {
|
||||
if strings.HasPrefix(strings.TrimSpace(newLines[i]), "#time=") {
|
||||
timestampExists = true
|
||||
break
|
||||
}
|
||||
if strings.HasPrefix(strings.TrimSpace(newLines[i]), "[") && newLines[i] != wineRegistrySection {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if !timestampExists {
|
||||
timestampLine := "#time=1dbd859c084de18"
|
||||
newLines = insertLine(newLines, insertIndex, timestampLine)
|
||||
insertIndex++
|
||||
}
|
||||
|
||||
if !leftOptionFound {
|
||||
newLines = insertLine(newLines, insertIndex, leftOptionKey)
|
||||
insertIndex++
|
||||
}
|
||||
if !rightOptionFound {
|
||||
newLines = insertLine(newLines, insertIndex, rightOptionKey)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Write the updated content
|
||||
newContent := strings.Join(newLines, "\n")
|
||||
if err := os.WriteFile(regPath, []byte(newContent), 0644); err != nil {
|
||||
return fmt.Errorf("failed to write registry file: %v", err)
|
||||
}
|
||||
|
||||
log.Printf("Successfully enabled Option-as-Alt mapping in Wine registry (fast method)")
|
||||
return nil
|
||||
}
|
||||
|
||||
// removeOptionAsAltSettingsFast removes the Option-as-Alt registry settings directly from the file
|
||||
func removeOptionAsAltSettingsFast(regPath string, lines []string) error {
|
||||
if !PathExists(regPath) {
|
||||
// File doesn't exist, nothing to remove
|
||||
log.Printf("Successfully disabled Option-as-Alt mapping in Wine registry (no file to modify)")
|
||||
return nil
|
||||
}
|
||||
|
||||
var newLines []string
|
||||
|
||||
// Remove lines that contain our option key settings from any section
|
||||
for _, line := range lines {
|
||||
trimmedLine := strings.TrimSpace(line)
|
||||
|
||||
// Skip lines that contain our option key settings
|
||||
if strings.Contains(trimmedLine, "LeftOptionIsAlt") || strings.Contains(trimmedLine, "RightOptionIsAlt") {
|
||||
continue
|
||||
}
|
||||
|
||||
newLines = append(newLines, line)
|
||||
}
|
||||
|
||||
// Check if any Mac Driver sections are now empty and remove them
|
||||
newLines = removeEmptyMacDriverSections(newLines)
|
||||
|
||||
// Write the updated content
|
||||
newContent := strings.Join(newLines, "\n")
|
||||
if err := os.WriteFile(regPath, []byte(newContent), 0644); err != nil {
|
||||
return fmt.Errorf("failed to write registry file: %v", err)
|
||||
}
|
||||
|
||||
log.Printf("Successfully disabled Option-as-Alt mapping in Wine registry (fast method)")
|
||||
return nil
|
||||
}
|
||||
|
||||
// removeEmptyMacDriverSections removes empty Mac Driver sections from the registry
|
||||
func removeEmptyMacDriverSections(lines []string) []string {
|
||||
var finalLines []string
|
||||
i := 0
|
||||
|
||||
for i < len(lines) {
|
||||
line := strings.TrimSpace(lines[i])
|
||||
|
||||
// Check if this is a Mac Driver section
|
||||
if line == wineRegistrySection || line == "[SoftwareWineMac Driver]" {
|
||||
// Check if the section is empty (only contains timestamp or nothing)
|
||||
sectionStart := i
|
||||
sectionEnd := i + 1
|
||||
sectionEmpty := true
|
||||
|
||||
// Find the end of this section
|
||||
for sectionEnd < len(lines) {
|
||||
nextLine := strings.TrimSpace(lines[sectionEnd])
|
||||
if nextLine == "" {
|
||||
sectionEnd++
|
||||
continue
|
||||
}
|
||||
if strings.HasPrefix(nextLine, "[") {
|
||||
// Start of new section
|
||||
break
|
||||
}
|
||||
if !strings.HasPrefix(nextLine, "#time=") {
|
||||
// Found non-timestamp content in section
|
||||
sectionEmpty = false
|
||||
}
|
||||
sectionEnd++
|
||||
}
|
||||
|
||||
if sectionEmpty {
|
||||
// Skip the entire empty section
|
||||
i = sectionEnd
|
||||
continue
|
||||
} else {
|
||||
// Keep the section
|
||||
for j := sectionStart; j < sectionEnd; j++ {
|
||||
finalLines = append(finalLines, lines[j])
|
||||
}
|
||||
i = sectionEnd
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
finalLines = append(finalLines, lines[i])
|
||||
i++
|
||||
}
|
||||
|
||||
return finalLines
|
||||
}
|
||||
|
||||
// insertLine inserts a line at the specified index
|
||||
func insertLine(lines []string, index int, newLine string) []string {
|
||||
if index >= len(lines) {
|
||||
return append(lines, newLine)
|
||||
}
|
||||
|
||||
lines = append(lines, "")
|
||||
copy(lines[index+1:], lines[index:])
|
||||
lines[index] = newLine
|
||||
return lines
|
||||
}
|
Binary file not shown.
Reference in New Issue
Block a user