From e6a350b4e78d7d61b52b0d1988f3b52112ae4892 Mon Sep 17 00:00:00 2001 From: Michael J Mulligan Date: Tue, 16 Mar 2021 16:55:27 +0000 Subject: [PATCH 1/4] Automatic Resolution Detection Added support for OCTOSCREEN_RESOLUTION=AUTO which uses `xrandr` to detect the default display resolution automatically. --- debian/local/octoscreen/config | 2 + main.go | 81 +++++++++++++++++++++++++--------- ui/ui.go | 6 +++ 3 files changed, 67 insertions(+), 22 deletions(-) diff --git a/debian/local/octoscreen/config b/debian/local/octoscreen/config index 002dd806..f834dee0 100755 --- a/debian/local/octoscreen/config +++ b/debian/local/octoscreen/config @@ -49,4 +49,6 @@ OCTOSCREEN_LOG_LEVEL=Error # screen, for example 800x480. OCTOSCREEN_RESOLUTION=800x480 # OCTOSCREEN_RESOLUTION is optional and defaults to 800x480 if missing +# Set to AUTO to enable automatic display resolution detection +# Minimum resolution is 548x348 # (defined in globalVars.go) diff --git a/main.go b/main.go index 67daf7c8..cb505caf 100755 --- a/main.go +++ b/main.go @@ -6,10 +6,12 @@ import ( standardLog "log" "os" "os/user" + "os/exec" "path/filepath" "runtime" "strconv" "strings" + "regexp" "github.com/gotk3/gotk3/gtk" "github.com/sirupsen/logrus" @@ -318,32 +320,67 @@ func doFindConfigFile(home string) string { func getSize() (width, height int) { logger.TraceEnter("main.getSize()") - + if Resolution == "" { logger.Info("main.getSize() - Resolution is empty, returning 0 for width and height, and will default to the default values defined in globalVars.go") logger.TraceLeave("main.getSize()") return - } - - parts := strings.SplitN(Resolution, "x", 2) - if len(parts) != 2 { - logger.Error("main.getSize() - SplitN() - len(parts) != 2") - logger.Fatalf("main.getSize() - malformed %s variable: %q", utils.EnvResolution, Resolution) - } - - var err error - width, err = strconv.Atoi(parts[0]) - if err != nil { - logger.LogError("main.getSize()", "Atoi(parts[0])", err) - logger.Fatalf("main.getSize() - malformed %s variable: %q, %s", utils.EnvResolution, Resolution, err) - } + } else if strings.ToLower(Resolution) == "auto" { + + logger.Warn("Using OCTOSCREEN_RESOLUTION = AUTO is an experimental feature. God speed.") + + xrandr, err := exec.LookPath( "xrandr" ) + + if err != nil { + logger.Error("Unable to determine 'xrandr' executable path.") + return + } + + cmd := exec.Command( + xrandr, + "-d", ":0.0", + "--prop", + ); + + output, err := cmd.Output() + + if err != nil { + logger.Error("Unable to determine 'xrander' executable path. But may have gotten: %s", output) + return + } + + re := regexp.MustCompile(`current ([0-9]+) x ([0-9]+)`) + matches := re.FindStringSubmatch(string(output)) + + width, _ = strconv.Atoi(matches[1]) + height, _ = strconv.Atoi(matches[2]) + + logger.Info("Used xrandr to determine the screen resolution. Found %d x %d", width, height) + + return + } else { - height, err = strconv.Atoi(parts[1]) - if err != nil { - logger.LogError("main.getSize()", "Atoi(parts[1])", err) - logger.Fatalf("main.getSize() - malformed %s variable: %q, %s", utils.EnvResolution, Resolution, err) + parts := strings.SplitN(Resolution, "x", 2) + if len(parts) != 2 { + logger.Error("main.getSize() - SplitN() - len(parts) != 2") + logger.Fatalf("main.getSize() - malformed %s variable: %q", utils.EnvResolution, Resolution) + } + + var err error + width, err = strconv.Atoi(parts[0]) + if err != nil { + logger.LogError("main.getSize()", "Atoi(parts[0])", err) + logger.Fatalf("main.getSize() - malformed %s variable: %q, %s", utils.EnvResolution, Resolution, err) + } + + height, err = strconv.Atoi(parts[1]) + if err != nil { + logger.LogError("main.getSize()", "Atoi(parts[1])", err) + logger.Fatalf("main.getSize() - malformed %s variable: %q, %s", utils.EnvResolution, Resolution, err) + } + + logger.TraceLeave("main.getSize()") + return + } - - logger.TraceLeave("main.getSize()") - return } diff --git a/ui/ui.go b/ui/ui.go index 03d989ed..0f684ba9 100755 --- a/ui/ui.go +++ b/ui/ui.go @@ -53,6 +53,12 @@ func New(endpoint, key string, width, height int) *UI { width = utils.WindowWidth height = utils.WindowHeight } + + if width < 548 || height < 348 { + logger.Errorf("Resolution is not within minumum limits. Resolution must be greater than 548x348. Target width and height: %dx%d", + width, + height) + } instance := &UI { PanelHistory: stack.New(), From da66d7d2e3555c62a24c885e81299e16a167b4fc Mon Sep 17 00:00:00 2001 From: Michael Mulligan Date: Fri, 19 Mar 2021 08:44:49 -0400 Subject: [PATCH 2/4] Update README.md --- README.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 3b0c3bab..507e520f 100755 --- a/README.md +++ b/README.md @@ -151,7 +151,9 @@ The basic configuration is handled via environment variables, if you are using t - `OCTOSCREEN_LOG_LEVEL` - Controls the level of logging. Accepted values are (with increasing levels): `debug`, `info`, `warn`, and `error`. If no value is provided, the log level will default to `warn`. -- `OCTOSCREEN_RESOLUTION` - Resolution of the application, and should be configured to the resolution of your screen. Optimal resolution for OctoScreen is no less than 800x480, so if the physical resolution of your screen is 480x320, it's recommended to set the software resolution 800x533. If you are using Raspbian you can do it by changing [`hdmi_cvt`](https://www.raspberrypi.org/documentation/configuration/config-txt/video.md) param in `/boot/config.txt` file. Please see [Setting Up OctoScreen and Your Display](https://github.com/Z-Bolt/OctoScreen/wiki/Setting-Up-OctoScreen-and-Your-Display) and [Installing OctoScreen with a 3.5" 480x320 TFT screen](https://github.com/Z-Bolt/OctoScreen/wiki/Installing-OctoScreen-with-a-3.5%22-480x320-TFT-screen) for more information. +- `OCTOSCREEN_RESOLUTION` - Resolution of the application, and should be configured to the resolution of your screen. Also supports automatic resolution detection when set to `AUTO`. Specific resolutions must be in the format of `WIDTHxHEIGHT`. Default resolution is `800x480`. + + Optimal resolution for OctoScreen is no less than 800x480, so if the physical resolution of your screen is 480x320, it's recommended to set the software resolution 800x533. If you are using Raspbian you can do it by changing [`hdmi_cvt`](https://www.raspberrypi.org/documentation/configuration/config-txt/video.md) param in `/boot/config.txt` file. Please see [Setting Up OctoScreen and Your Display](https://github.com/Z-Bolt/OctoScreen/wiki/Setting-Up-OctoScreen-and-Your-Display) and [Installing OctoScreen with a 3.5" 480x320 TFT screen](https://github.com/Z-Bolt/OctoScreen/wiki/Installing-OctoScreen-with-a-3.5%22-480x320-TFT-screen) for more information. From 1a02fabc93ab40a2a8c2315ea15cebdb65f77554 Mon Sep 17 00:00:00 2001 From: Michael J Mulligan Date: Tue, 23 Mar 2021 20:13:47 +0000 Subject: [PATCH 3/4] Better logging, better defaults and better dependencies. --- debian/control | 1 + debian/local/octoscreen/config | 1 - main.go | 15 ++++++++++++--- ui/ui.go | 10 +++++----- 4 files changed, 18 insertions(+), 9 deletions(-) diff --git a/debian/control b/debian/control index 6cd5cf6f..7f67a128 100644 --- a/debian/control +++ b/debian/control @@ -23,6 +23,7 @@ Depends: ${shlibs:Depends}, libcairo2 (>= 1.14.0), libgtk-3-0 (>= 3.14), xserver-xorg, + x11-xserver-utils, xinit Provides: x-display-manager Description: A OctoPrint touch interface for TFT touch modules based on GTK+3 diff --git a/debian/local/octoscreen/config b/debian/local/octoscreen/config index f834dee0..865bc28c 100755 --- a/debian/local/octoscreen/config +++ b/debian/local/octoscreen/config @@ -51,4 +51,3 @@ OCTOSCREEN_RESOLUTION=800x480 # OCTOSCREEN_RESOLUTION is optional and defaults to 800x480 if missing # Set to AUTO to enable automatic display resolution detection # Minimum resolution is 548x348 -# (defined in globalVars.go) diff --git a/main.go b/main.go index cb505caf..cc845498 100755 --- a/main.go +++ b/main.go @@ -327,12 +327,13 @@ func getSize() (width, height int) { return } else if strings.ToLower(Resolution) == "auto" { - logger.Warn("Using OCTOSCREEN_RESOLUTION = AUTO is an experimental feature. God speed.") + logger.Info("Automatically detecting resolution with 'xrandr'.") xrandr, err := exec.LookPath( "xrandr" ) if err != nil { logger.Error("Unable to determine 'xrandr' executable path.") + logger.TraceLeave("main.getSize()") return } @@ -345,17 +346,25 @@ func getSize() (width, height int) { output, err := cmd.Output() if err != nil { - logger.Error("Unable to determine 'xrander' executable path. But may have gotten: %s", output) + logger.Errorf("When determining resolution, 'xrander' returned with an error. Output: %s", output) + logger.TraceLeave("main.getSize()") return } + /* + There is no real error handeling here, as if `xrandr` executes without + an error, we are gauranteed an output of this format, so the regex + won't fail. + */ + re := regexp.MustCompile(`current ([0-9]+) x ([0-9]+)`) matches := re.FindStringSubmatch(string(output)) width, _ = strconv.Atoi(matches[1]) height, _ = strconv.Atoi(matches[2]) - logger.Info("Used xrandr to determine the screen resolution. Found %d x %d", width, height) + logger.Info("Used 'xrandr' to determine the screen resolution. Found %d x %d", width, height) + logger.TraceLeave("main.getSize()") return } else { diff --git a/ui/ui.go b/ui/ui.go index 0f684ba9..6d9f8423 100755 --- a/ui/ui.go +++ b/ui/ui.go @@ -49,15 +49,15 @@ type UI struct { func New(endpoint, key string, width, height int) *UI { logger.TraceEnter("ui.New()") - if width == 0 || height == 0 { - width = utils.WindowWidth - height = utils.WindowHeight - } - if width < 548 || height < 348 { logger.Errorf("Resolution is not within minumum limits. Resolution must be greater than 548x348. Target width and height: %dx%d", width, height) + width = utils.WindowWidth + height = utils.WindowHeight + logger.Infof("Using default resolution: %dx%d", + width, + height) } instance := &UI { From 9317de9e618e0f60a471c06a2d891e7b4a44c998 Mon Sep 17 00:00:00 2001 From: Michael J Mulligan Date: Wed, 24 Mar 2021 05:56:21 +0000 Subject: [PATCH 4/4] Made the debug/info messages more informative and specific to the error. --- ui/ui.go | 36 ++++++++++++++++++++++++------------ 1 file changed, 24 insertions(+), 12 deletions(-) diff --git a/ui/ui.go b/ui/ui.go index 6d9f8423..30a09d9a 100755 --- a/ui/ui.go +++ b/ui/ui.go @@ -79,21 +79,30 @@ func New(endpoint, key string, width, height int) *UI { instance.window.Connect("configure-event", func(win *gtk.Window) { allocatedWidth:= win.GetAllocatedWidth() allocatedHeight:= win.GetAllocatedHeight() - sizeWidth, sizeHeight := win.GetSize() - - if (allocatedWidth > width || allocatedHeight > height) || - (sizeWidth > width || sizeHeight > height) { - logger.Errorf("Window resize went past max size. allocatedWidth:%d allocatedHeight:%d sizeWidth:%d sizeHeight:%d", - allocatedWidth, - allocatedHeight, - sizeWidth, - sizeHeight) - logger.Errorf("Window resize went past max size. Target width and height: %dx%d", - width, - height) + windowWidth, windowHeight := win.GetSize() + + logger.Debugf("OCTOSCREEN_RESOLUTION - User-Configured: %d x %d", width, height) + logger.Debugf("OCTOSCREEN_RESOLUTION - getAllocated*(): %d x %d", allocatedWidth, allocatedHeight) + logger.Debugf("OCTOSCREEN_RESOLUTION - getSize(): %d x %d", windowWidth, windowHeight) + + if (allocatedWidth != windowWidth || allocatedHeight > windowHeight) { + logger.Infof("Allocated and Window Sizes don't match: ( %d x %d ) != ( %d x %d )", + allocatedWidth, allocatedHeight, + windowWidth, windowHeight) + } + if (allocatedWidth > width || allocatedHeight > height) { + logger.Errorf("Allocated Size exceeded Configured Size: ( %d x %d ) > ( %d x %d )", + allocatedWidth, allocatedHeight, + width, height) + } + if (windowWidth > width || windowHeight > height) { + logger.Errorf("Window Size exceeded Configured Size: ( %d x %d ) > ( %d x %d )", + windowWidth, windowHeight, + width, height) } }) + switch { case width > 480: instance.scaleFactor = 2 @@ -104,6 +113,9 @@ func New(endpoint, key string, width, height int) *UI { default: instance.scaleFactor = 1 } + + // This is what the above equates to, in theory. + //instance.scaleFactor = ( 0.002 * float32(width) ) + 1.01386 instance.splashPanel = NewSplashPanel(instance) instance.backgroundTask = utils.CreateBackgroundTask(time.Second * 10, instance.update)