Pacharapol Withayasakpunt Pacharapol Withayasakpunt
Thu, September 10, 2020

Maximize (not fullscreen) your cross-platform desktop application

If not for macOS "fullscreen" (and some Linux), it wouldn't be a pain.

I have realize the solution for a while, with Golang and Lorca. Nonetheless, it uses cgo.

package main

import (
  "fmt"
  "log"

  /*
    #cgo darwin LDFLAGS: -framework CoreGraphics

    #if defined(__APPLE__)
    #include <CoreGraphics/CGDisplayConfiguration.h>
    int display_width() {
    return CGDisplayPixelsWide(CGMainDisplayID());
    }
    int display_height() {
    return CGDisplayPixelsHigh(CGMainDisplayID());
    }
    #else
    int display_width() {
    return 0;
    }
    int display_height() {
    return 0;
    }
    #endif
  */
  "C"

  "github.com/zserge/lorca"
)

func main() {
  if lorca.LocateChrome() == "" {
    lorca.PromptDownload()
    log.Fatal(fmt.Errorf("cannot open outside Chrome desktop application"))
  } else {
    width := int(C.display_width())
    height := int(C.display_height())

    if width == 0 || height == 0 {
      width = 1024
      height = 768
    }

    w, err := lorca.New("https://example.com", "", width, height)
    if err != nil {
      log.Fatal(err)
    }

    defer w.Close()

    // This does nothing in macOS, BTW.
    w.SetBounds(lorca.Bounds{
      WindowState: lorca.WindowStateMaximized,
    })

    <-w.Done()
  }
}

Then, you can cross-compile for all major platforms using xgo.

karalabe/xgo

Go CGO cross compiler. Contribute to karalabe/xgo development by creating an account on GitHub.

BRANCH=${<BRANCH_NAME>:-"master"}
REPO=<REPO_NAME>

$(go env GOPATH)/bin/xgo <br />  -ldflags="-H windowsgui" <br />  -branch=BRANCH <br />  -targets=windows/* <br />  REPO

if [[ $(go env GOOS) == 'darwin' ]]; then
  go build "${PWD##*/}.app"
  $(go env GOPATH)/bin/xgo <br />    -branch=BRANCH <br />    -targets=linux/* <br />    REPO
else
  $(go env GOPATH)/bin/xgo <br />    -branch=BRANCH <br />    -targets=linux/*,darwin/amd64 <br />    REPO

  rm *-darwin*.app
  for f in *-darwin*; do mv "$f" "$f.app"; done
fi

The question still remains. What if I don't use Lorca, Chrome DevTools Protocol, or any other frameworks with built-in maximize, so what should I do? (BTW, Electron has built-in maximization, but webview/webview and Neutralino.js don't have one…)

I have an approximate answer in cgo.

import (
  "runtime"

  /*
     #cgo darwin LDFLAGS: -framework CoreGraphics
     #cgo linux pkg-config: x11

     #if defined(__APPLE__)
     #include <CoreGraphics/CGDisplayConfiguration.h>
     int display_width() {
       return CGDisplayPixelsWide(CGMainDisplayID());
     }
     int display_height() {
       return CGDisplayPixelsHigh(CGMainDisplayID());
     }
     #elif defined(_WIN32)
     #include <wtypes.h>
     int display_width() {
       RECT desktop;
       const HWND hDesktop = GetDesktopWindow();
       GetWindowRect(hDesktop, &desktop);
       return desktop.right;
     }
     int display_height() {
       RECT desktop;
       const HWND hDesktop = GetDesktopWindow();
       GetWindowRect(hDesktop, &desktop);
       return desktop.bottom;
     }
     #else
     #include <X11/Xlib.h>
     int display_width() {
       Display* d = XOpenDisplay(NULL);
       Screen*  s = DefaultScreenOfDisplay(d);
       return s->width;
     }
     int display_height() {
       Display* d = XOpenDisplay(NULL);
       Screen*  s = DefaultScreenOfDisplay(d);
       return s->height;
     }
     #endif
  */
  "C"
)

func getFullscreenSize() (int, int) {
  width := int(C.display_width())
  height := int(C.display_height())

  // Current method of getting screen size in linux and windows makes it fall offscreen
  if runtime.GOOS == "linux" || runtime.GOOS == "windows" {
    width = width - 50
    height = height - 100
  }

  if width == 0 || height == 0 {
    width = 1024
    height = 768
  }

  return width, height
}

Not really fullscreen in Windows and Linux. Also, when compiling for Linux, you cannot use xgo -- I use Docker instead.

ARG ARCH=""
FROM ${ARCH}debian
RUN apt-get update
RUN apt-get install -y --no-install-recommends build-essential 
RUN apt-get install -y xorg-dev
RUN apt-get install -y golang git

And,

for arch in amd64 i386
  do
    docker build --build-arg ARCH=$arch/ -t ${PWD##*/}-$arch .
  done

for arch in amd64 i386
  do
    docker run --rm -v <br />      "$PWD":/usr/app <br />      -w /usr/app <br />      ${PWD##*/}-$arch <br />      go build -o "${PWD##*/}-$arch"
  done

BTW, I've just found docker xbuild.

Multi-arch build and images, the simple way - Docker Blog

"Build once, deploy anywhere" is really nice on the paper but if you want to use ARM targets to reduce your bill, such as Raspberry Pis and AWS A1 instances, or even keep using your old i386 servers, deploying everywhere can become a tricky problem as you need to build your software for these platforms. To fix this problem, Docker introduced the principle of multi-arch builds and we’ll see how to use this and put it into production.