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. 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 002dd806..865bc28c 100755 --- a/debian/local/octoscreen/config +++ b/debian/local/octoscreen/config @@ -49,4 +49,5 @@ OCTOSCREEN_LOG_LEVEL=Error # screen, for example 800x480. OCTOSCREEN_RESOLUTION=800x480 # OCTOSCREEN_RESOLUTION is optional and defaults to 800x480 if missing -# (defined in globalVars.go) +# Set to AUTO to enable automatic display resolution detection +# Minimum resolution is 548x348 diff --git a/main.go b/main.go index 18a0ec32..0194d680 100755 --- a/main.go +++ b/main.go @@ -7,10 +7,12 @@ import ( standardLog "log" "os" "os/user" + "os/exec" "path/filepath" "runtime" "strconv" "strings" + "regexp" "github.com/gotk3/gotk3/gtk" "github.com/sirupsen/logrus" @@ -336,35 +338,75 @@ func doFindConfigFile(home string) string { func getSize() (width int, height int, err error) { 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") - err = errors.New(fmt.Sprintf("%s is malformed\nvalue: %q", utils.EnvResolution, Resolution)) - } + } else if strings.ToLower(Resolution) == "auto" { + + logger.Info("Automatically detecting resolution with 'xrandr'.") + + xrandr, err := exec.LookPath( "xrandr" ) + + if err != nil { + logger.Error("Unable to determine 'xrandr' executable path.") + err = errors.New("Unable to determine 'xrandr' executable path.") + logger.TraceLeave("main.getSize()") + return + } + + cmd := exec.Command( + xrandr, + "-d", ":0.0", + "--prop", + ); + + output, err := cmd.Output() + + if err != nil { + logger.Errorf("When determining resolution, 'xrandr' returned with an error. Output: %s", output) + err = errors.New(fmt.Sprintf("When determining resolution, 'xrandr' 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]) + + } else { - if err == nil { + parts := strings.SplitN(Resolution, "x", 2) + if len(parts) != 2 { + logger.Error("main.getSize() - SplitN() - len(parts) != 2") + err = errors.New(fmt.Sprintf("%s is malformed\nvalue: %q", utils.EnvResolution, Resolution)) + } + + var err error width, err = strconv.Atoi(parts[0]) if err != nil { logger.LogError("main.getSize()", "Atoi(parts[0])", err) err = errors.New(fmt.Sprintf("%s is malformed\nAtoi(0) failed\nvalue: %q", utils.EnvResolution, Resolution)) } - } - - if err == nil { + height, err = strconv.Atoi(parts[1]) if err != nil { logger.LogError("main.getSize()", "Atoi(parts[1])", err) err = errors.New(fmt.Sprintf("%s is malformed\nAtoi(1) failed\nvalue: %q", utils.EnvResolution, Resolution)) } - } - - logger.TraceLeave("main.getSize()") - return + } + + logger.Info("Determined a screen resolution of: %d x %d", width, height) + + logger.TraceLeave("main.getSize()") + return } diff --git a/ui/ui.go b/ui/ui.go index 0276fc6f..bf514837 100755 --- a/ui/ui.go +++ b/ui/ui.go @@ -49,9 +49,15 @@ type UI struct { func New(endpoint, key string, width, height int) *UI { logger.TraceEnter("ui.New()") - if width == 0 || height == 0 { + 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 { @@ -73,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 @@ -98,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)