Compare commits

...

2 Commits

Author SHA1 Message Date
1051329763 fix(ui): Enable word wrapping for hint labels in convert module
Issue:
- User reported hint text being cut off at window edge
- Example: "CBR mode: Constant bitrate - predictable file quality. Use for strict size requirements or s"
- Text truncated with "or s" visible, rest cut off
- Hint labels weren't wrapping properly in narrow windows

Root Cause:
- Hint labels had TextWrapWord enabled BUT
- Labels inside VBox containers don't wrap properly without width constraints
- Fyne requires labels to be in a sized container for wrapping to work
- VScroll container doesn't provide width hints to child labels

Solution:
- Wrap all hint labels in container.NewPadded() containers
- Padded containers provide proper sizing context for text wrapping
- Labels now wrap at available width instead of extending beyond bounds

Affected Hint Labels:
- encoderPresetHint: Encoder preset descriptions
- encodingHint: Bitrate mode (CRF/CBR/VBR/Target Size) hints
- frameRateHint: Frame rate change warnings
- outputHint: Output file path display
- targetAspectHint: Aspect ratio selection hint
- hwAccelHint: Hardware acceleration guidance

Implementation:
- Created *Container versions of each hint label
- Wrapped label in container.NewPadded(label)
- Replaced direct label usage with container in VBox layouts
- Maintains TextWrapWord setting on all labels

Impact:
- Hint text now wraps properly in narrow windows/panels
- No more truncated text
- Better readability across all window sizes
- Consistent behavior for all hint labels

Files Changed:
- main.go: Wrapped 6 hint labels in padded containers

Reported-by: User (screenshot showing "or s" truncation)
Tested: Build successful (v0.1.0-dev20)

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2025-12-28 19:38:58 -05:00
8f73913817 fix(windows): Hide command windows in hardware detection and fix GPU detection
Issues Fixed:
1. Command Prompts During Benchmark
   - Jake reported 4 command prompt windows appearing when opening benchmark
   - Windows showing: C:\ffmpeg\bin\ffmpeg.exe for each hardware check

2. Wrong GPU Detection
   - Jake's AMD R7 900 XTX not detected
   - System incorrectly showing "13.50.53.699 Virtual Desktop" as GPU
   - Showing "Monitor" instead of actual graphics card

Root Causes:
1. sysinfo package missing ApplyNoWindow() on Windows detection commands
   - detectCPUWindows: wmic cpu query
   - detectGPUWindows: nvidia-smi + wmic VideoController queries
   - detectRAMWindows: wmic computersystem query
   - These 3-4 calls showed command windows

2. GPU Detection Not Filtering Virtual Adapters
   - wmic returns ALL video controllers (physical + virtual)
   - Code was picking first entry which was virtual adapter
   - No filtering for "Virtual Desktop", remote desktop adapters, etc.

Solutions:
1. Applied utils.ApplyNoWindow() to all Windows detection commands
   - Hides wmic, nvidia-smi command windows
   - Consistent with benchmark.go and platform.go patterns
   - No-op on Linux/macOS (cross-platform safe)

2. Enhanced GPU Detection with Virtual Adapter Filtering
   - Iterate through ALL video controllers from wmic
   - Filter out virtual/software adapters:
     * Virtual Desktop adapters
     * Microsoft Basic Display Adapter
     * Remote desktop (VNC, Parsec, TeamViewer)
   - Return first physical GPU (AMD/NVIDIA/Intel)
   - Debug logging shows skipped virtual adapters

Implementation:
- Import internal/utils in sysinfo package
- ApplyNoWindow() on 4 Windows commands:
  * wmic cpu get name,maxclockspeed
  * nvidia-smi --query-gpu=name,driver_version
  * wmic path win32_VideoController get name,driverversion
  * wmic computersystem get totalphysicalmemory
- Enhanced GPU parser with virtual adapter blacklist
- Debug logging for skipped/detected GPUs

Impact:
- No command windows during benchmark initialization (discreet operation)
- Correct physical GPU detection on systems with virtual adapters
- Should properly detect Jake's AMD R7 900 XTX

Files Changed:
- internal/sysinfo/sysinfo.go: ApplyNoWindow + GPU filtering

Reported-by: Jake (4 command prompts, wrong GPU detection)
Tested-on: Linux (build successful, no regressions)

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
2025-12-28 19:32:15 -05:00
2 changed files with 57 additions and 20 deletions

View File

@ -9,6 +9,7 @@ import (
"strings"
"git.leaktechnologies.dev/stu/VideoTools/internal/logging"
"git.leaktechnologies.dev/stu/VideoTools/internal/utils"
)
// HardwareInfo contains system hardware information
@ -103,6 +104,7 @@ func detectCPULinux() (model, mhz string) {
func detectCPUWindows() (model, mhz string) {
// Use wmic to get CPU info
cmd := exec.Command("wmic", "cpu", "get", "name,maxclockspeed")
utils.ApplyNoWindow(cmd) // Hide command window on Windows
output, err := cmd.Output()
if err != nil {
logging.Debug(logging.CatSystem, "failed to run wmic cpu: %v", err)
@ -208,6 +210,7 @@ func detectGPULinux() (model, driver string) {
func detectGPUWindows() (model, driver string) {
// Use nvidia-smi if available (NVIDIA GPUs)
cmd := exec.Command("nvidia-smi", "--query-gpu=name,driver_version", "--format=csv,noheader")
utils.ApplyNoWindow(cmd) // Hide command window on Windows
output, err := cmd.Output()
if err == nil {
parts := strings.Split(strings.TrimSpace(string(output)), ",")
@ -220,21 +223,41 @@ func detectGPUWindows() (model, driver string) {
// Try wmic for generic GPU info
cmd = exec.Command("wmic", "path", "win32_VideoController", "get", "name,driverversion")
utils.ApplyNoWindow(cmd) // Hide command window on Windows
output, err = cmd.Output()
if err == nil {
lines := strings.Split(string(output), "\n")
if len(lines) >= 2 {
// Skip header, get first GPU
line := strings.TrimSpace(lines[1])
if line != "" {
// Parse: Name DriverVersion
re := regexp.MustCompile(`^(.+?)\s+(\S+)$`)
matches := re.FindStringSubmatch(line)
if len(matches) == 3 {
model = strings.TrimSpace(matches[1])
driver = strings.TrimSpace(matches[2])
return model, driver
}
// Iterate through all video controllers, skip virtual/non-physical adapters
for i, line := range lines {
if i == 0 { // Skip header
continue
}
line = strings.TrimSpace(line)
if line == "" {
continue
}
// Filter out virtual/software adapters
lineLower := strings.ToLower(line)
if strings.Contains(lineLower, "virtual") ||
strings.Contains(lineLower, "microsoft basic") ||
strings.Contains(lineLower, "remote") ||
strings.Contains(lineLower, "vnc") ||
strings.Contains(lineLower, "parsec") ||
strings.Contains(lineLower, "teamviewer") {
logging.Debug(logging.CatSystem, "skipping virtual GPU: %s", line)
continue
}
// Parse: Name DriverVersion
// Use flexible regex to handle varying whitespace
re := regexp.MustCompile(`^(.+?)\s+(\S+)$`)
matches := re.FindStringSubmatch(line)
if len(matches) == 3 {
model = strings.TrimSpace(matches[1])
driver = strings.TrimSpace(matches[2])
logging.Debug(logging.CatSystem, "detected physical GPU: %s (driver: %s)", model, driver)
return model, driver
}
}
}
@ -316,6 +339,7 @@ func detectRAMLinux() (readable string, mb uint64) {
func detectRAMWindows() (readable string, mb uint64) {
cmd := exec.Command("wmic", "computersystem", "get", "totalphysicalmemory")
utils.ApplyNoWindow(cmd) // Hide command window on Windows
output, err := cmd.Output()
if err != nil {
logging.Debug(logging.CatSystem, "failed to run wmic computersystem: %v", err)

29
main.go
View File

@ -6235,6 +6235,8 @@ func buildConvertView(state *appState, src *videoSource) fyne.CanvasObject {
}
outputHint := widget.NewLabel(fmt.Sprintf("Output file: %s", state.convert.OutputFile()))
outputHint.Wrapping = fyne.TextWrapWord
// Wrap hint in padded container to ensure proper text wrapping in narrow windows
outputHintContainer := container.NewPadded(outputHint)
// DVD-specific aspect ratio selector (only shown for DVD formats)
dvdAspectSelect := widget.NewSelect([]string{"4:3", "16:9"}, func(value string) {
@ -6633,6 +6635,9 @@ func buildConvertView(state *appState, src *videoSource) fyne.CanvasObject {
}
targetAspectSelect.SetSelected(state.convert.OutputAspect)
targetAspectHint := widget.NewLabel("Pick desired output aspect (default Source).")
targetAspectHint.Wrapping = fyne.TextWrapWord
// Wrap hint in padded container to ensure proper text wrapping in narrow windows
targetAspectHintContainer := container.NewPadded(targetAspectHint)
aspectOptions := widget.NewRadioGroup([]string{"Auto", "Crop", "Letterbox", "Pillarbox", "Blur Fill", "Stretch"}, func(value string) {
logging.Debug(logging.CatUI, "aspect handling set to %s", value)
@ -6776,6 +6781,8 @@ func buildConvertView(state *appState, src *videoSource) fyne.CanvasObject {
// Encoder Preset with hint
encoderPresetHint := widget.NewLabel("")
encoderPresetHint.Wrapping = fyne.TextWrapWord
// Wrap hint in padded container to ensure proper text wrapping in narrow windows
encoderPresetHintContainer := container.NewPadded(encoderPresetHint)
updateEncoderPresetHint := func(preset string) {
var hint string
@ -7458,6 +7465,8 @@ func buildConvertView(state *appState, src *videoSource) fyne.CanvasObject {
encodingHint := widget.NewLabel("")
encodingHint.Wrapping = fyne.TextWrapWord
// Wrap hint in padded container to ensure proper text wrapping in narrow windows
encodingHintContainer := container.NewPadded(encodingHint)
applyBitratePreset = func(label string) {
preset, ok := bitratePresetLookup[label]
@ -7612,6 +7621,8 @@ func buildConvertView(state *appState, src *videoSource) fyne.CanvasObject {
// Frame Rate with hint
frameRateHint := widget.NewLabel("")
frameRateHint.Wrapping = fyne.TextWrapWord
// Wrap hint in padded container to ensure proper text wrapping in narrow windows
frameRateHintContainer := container.NewPadded(frameRateHint)
updateFrameRateHint := func() {
if src == nil {
@ -7694,6 +7705,8 @@ func buildConvertView(state *appState, src *videoSource) fyne.CanvasObject {
// Hardware Acceleration with hint
hwAccelHint := widget.NewLabel("Auto picks the best GPU path; if encode fails, switch to none (software).")
hwAccelHint.Wrapping = fyne.TextWrapWord
// Wrap hint in padded container to ensure proper text wrapping in narrow windows
hwAccelHintContainer := container.NewPadded(hwAccelHint)
hwAccelSelect := widget.NewSelect([]string{"auto", "none", "nvenc", "amf", "vaapi", "qsv", "videotoolbox"}, func(value string) {
state.convert.HardwareAccel = value
logging.Debug(logging.CatUI, "hardware accel set to %s", value)
@ -8002,7 +8015,7 @@ func buildConvertView(state *appState, src *videoSource) fyne.CanvasObject {
dvdAspectBox, // DVD options appear here when DVD format selected
widget.NewLabelWithStyle("Output Name", fyne.TextAlignLeading, fyne.TextStyle{Bold: true}),
outputEntry,
outputHint,
outputHintContainer,
widget.NewSeparator(),
simpleEncodingSection,
widget.NewLabelWithStyle("Target Resolution", fyne.TextAlignLeading, fyne.TextStyle{Bold: true}),
@ -8012,7 +8025,7 @@ func buildConvertView(state *appState, src *videoSource) fyne.CanvasObject {
motionInterpCheck,
widget.NewLabelWithStyle("Target Aspect Ratio", fyne.TextAlignLeading, fyne.TextStyle{Bold: true}),
targetAspectSelectSimple,
targetAspectHint,
targetAspectHintContainer,
layout.NewSpacer(),
)
@ -8023,25 +8036,25 @@ func buildConvertView(state *appState, src *videoSource) fyne.CanvasObject {
videoCodecSelect,
widget.NewLabelWithStyle("Encoder Preset (speed vs quality)", fyne.TextAlignLeading, fyne.TextStyle{Bold: true}),
encoderPresetSelect,
encoderPresetHint,
encoderPresetHintContainer,
qualitySectionAdv,
widget.NewLabelWithStyle("Bitrate Mode", fyne.TextAlignLeading, fyne.TextStyle{Bold: true}),
bitrateModeSelect,
crfContainer,
bitrateContainer,
targetSizeContainer,
encodingHint,
encodingHintContainer,
widget.NewLabelWithStyle("Target Resolution", fyne.TextAlignLeading, fyne.TextStyle{Bold: true}),
resolutionSelect,
widget.NewLabelWithStyle("Frame Rate", fyne.TextAlignLeading, fyne.TextStyle{Bold: true}),
frameRateSelect,
frameRateHint,
frameRateHintContainer,
motionInterpCheck,
widget.NewLabelWithStyle("Pixel Format", fyne.TextAlignLeading, fyne.TextStyle{Bold: true}),
pixelFormatSelect,
widget.NewLabelWithStyle("Hardware Acceleration", fyne.TextAlignLeading, fyne.TextStyle{Bold: true}),
hwAccelSelect,
hwAccelHint,
hwAccelHintContainer,
twoPassCheck,
)
@ -8064,7 +8077,7 @@ func buildConvertView(state *appState, src *videoSource) fyne.CanvasObject {
dvdAspectBox, // DVD options appear here when DVD format selected
widget.NewLabelWithStyle("Output Name", fyne.TextAlignLeading, fyne.TextStyle{Bold: true}),
outputEntry,
outputHint,
outputHintContainer,
coverDisplay,
widget.NewSeparator(),
advancedVideoEncodingBlock,
@ -8073,7 +8086,7 @@ func buildConvertView(state *appState, src *videoSource) fyne.CanvasObject {
widget.NewLabelWithStyle("═══ ASPECT RATIO ═══", fyne.TextAlignCenter, fyne.TextStyle{Bold: true}),
widget.NewLabelWithStyle("Target Aspect Ratio", fyne.TextAlignLeading, fyne.TextStyle{Bold: true}),
targetAspectSelect,
targetAspectHint,
targetAspectHintContainer,
aspectBox,
widget.NewSeparator(),